From efaf9311b332e49dc643de5237b73b1613e627cc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 30 Apr 2025 17:38:16 +0100 Subject: [PATCH 001/140] Extract population business logic from PopulationTask --- src/world/generator/PopulationTask.php | 40 +++---------- src/world/generator/PopulationUtils.php | 74 +++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 src/world/generator/PopulationUtils.php diff --git a/src/world/generator/PopulationTask.php b/src/world/generator/PopulationTask.php index bad134324..a8366a306 100644 --- a/src/world/generator/PopulationTask.php +++ b/src/world/generator/PopulationTask.php @@ -27,8 +27,6 @@ use pocketmine\scheduler\AsyncTask; use pocketmine\utils\AssumptionFailedError; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; -use pocketmine\world\SimpleChunkManager; -use pocketmine\world\World; use function array_map; use function igbinary_serialize; use function igbinary_unserialize; @@ -71,8 +69,6 @@ class PopulationTask extends AsyncTask{ if($context === null){ throw new AssumptionFailedError("Generator context should have been initialized before any PopulationTask execution"); } - $generator = $context->getGenerator(); - $manager = new SimpleChunkManager($context->getWorldMinY(), $context->getWorldMaxY()); $chunk = $this->chunk !== null ? FastChunkSerializer::deserializeTerrain($this->chunk) : null; @@ -93,21 +89,15 @@ class PopulationTask extends AsyncTask{ $serialChunks ); - self::setOrGenerateChunk($manager, $generator, $this->chunkX, $this->chunkZ, $chunk); - - $resultChunks = []; //this is just to keep phpstan's type inference happy - foreach($chunks as $relativeChunkHash => $c){ - World::getXZ($relativeChunkHash, $relativeX, $relativeZ); - $resultChunks[$relativeChunkHash] = self::setOrGenerateChunk($manager, $generator, $this->chunkX + $relativeX, $this->chunkZ + $relativeZ, $c); - } - $chunks = $resultChunks; - - $generator->populateChunk($manager, $this->chunkX, $this->chunkZ); - $chunk = $manager->getChunk($this->chunkX, $this->chunkZ); - if($chunk === null){ - throw new AssumptionFailedError("We just generated this chunk, so it must exist"); - } - $chunk->setPopulated(); + [$chunk, $chunks] = PopulationUtils::populateChunkWithAdjacents( + $context->getWorldMinY(), + $context->getWorldMaxY(), + $context->getGenerator(), + $this->chunkX, + $this->chunkZ, + $chunk, + $chunks + ); $this->chunk = FastChunkSerializer::serializeTerrain($chunk); @@ -118,18 +108,6 @@ class PopulationTask extends AsyncTask{ $this->adjacentChunks = igbinary_serialize($serialChunks) ?? throw new AssumptionFailedError("igbinary_serialize() returned null"); } - private static function setOrGenerateChunk(SimpleChunkManager $manager, Generator $generator, int $chunkX, int $chunkZ, ?Chunk $chunk) : Chunk{ - $manager->setChunk($chunkX, $chunkZ, $chunk ?? new Chunk([], false)); - if($chunk === null){ - $generator->generateChunk($manager, $chunkX, $chunkZ); - $chunk = $manager->getChunk($chunkX, $chunkZ); - if($chunk === null){ - throw new AssumptionFailedError("We just set this chunk, so it must exist"); - } - } - return $chunk; - } - public function onCompletion() : void{ /** * @var \Closure $onCompletion diff --git a/src/world/generator/PopulationUtils.php b/src/world/generator/PopulationUtils.php new file mode 100644 index 000000000..84840ee3e --- /dev/null +++ b/src/world/generator/PopulationUtils.php @@ -0,0 +1,74 @@ +setChunk($chunkX, $chunkZ, $chunk ?? new Chunk([], false)); + if($chunk === null){ + $generator->generateChunk($manager, $chunkX, $chunkZ); + $chunk = $manager->getChunk($chunkX, $chunkZ); + if($chunk === null){ + throw new AssumptionFailedError("We just set this chunk, so it must exist"); + } + } + return $chunk; + } + + /** + * @param Chunk[]|null[] $adjacentChunks + * @phpstan-param array $adjacentChunks + * + * @return Chunk[]|Chunk[][] + * @phpstan-return array{Chunk, array} + */ + public static function populateChunkWithAdjacents(int $minY, int $maxY, Generator $generator, int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks) : array{ + $manager = new SimpleChunkManager($minY, $maxY); + self::setOrGenerateChunk($manager, $generator, $chunkX, $chunkZ, $centerChunk); + + $resultChunks = []; //this is just to keep phpstan's type inference happy + foreach($adjacentChunks as $relativeChunkHash => $c){ + World::getXZ($relativeChunkHash, $relativeX, $relativeZ); + $resultChunks[$relativeChunkHash] = self::setOrGenerateChunk($manager, $generator, $chunkX + $relativeX, $chunkZ + $relativeZ, $c); + } + $adjacentChunks = $resultChunks; + + $generator->populateChunk($manager, $chunkX, $chunkZ); + $centerChunk = $manager->getChunk($chunkX, $chunkZ); + if($centerChunk === null){ + throw new AssumptionFailedError("We just generated this chunk, so it must exist"); + } + $centerChunk->setPopulated(); + return [$centerChunk, $adjacentChunks]; + } +} From 6f3506360e8777dbe7b0e0eb05f717678e755658 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 08:30:26 +0000 Subject: [PATCH 002/140] Bump the github-actions group with 3 updates (#6683) --- .github/workflows/build-docker-image.yml | 8 ++++---- .github/workflows/discord-release-notify.yml | 2 +- .github/workflows/draft-release-pr-check.yml | 2 +- .github/workflows/draft-release.yml | 4 ++-- .github/workflows/main.yml | 2 +- .github/workflows/team-pr-auto-approve.yml | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 83d568878..dc282ab71 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.15.0 + uses: docker/build-push-action@v6.16.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.15.0 + uses: docker/build-push-action@v6.16.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.15.0 + uses: docker/build-push-action@v6.16.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.15.0 + uses: docker/build-push-action@v6.16.0 with: push: true context: ./pocketmine-mp diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index fde5e3099..93b2978aa 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.32.0 + uses: shivammathur/setup-php@2.33.0 with: php-version: 8.2 diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml index 303f61ccf..20b2200e6 100644 --- a/.github/workflows/draft-release-pr-check.yml +++ b/.github/workflows/draft-release-pr-check.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP - uses: shivammathur/setup-php@2.32.0 + uses: shivammathur/setup-php@2.33.0 with: php-version: 8.2 diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 02cdeec6f..fa20d1912 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -59,7 +59,7 @@ jobs: steps: - name: Generate access token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }} private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }} @@ -87,7 +87,7 @@ jobs: submodules: true - name: Setup PHP - uses: shivammathur/setup-php@2.32.0 + uses: shivammathur/setup-php@2.33.0 with: php-version: ${{ env.PHP_VERSION }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 051a3a790..cfe97aa7e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.32.0 + uses: shivammathur/setup-php@2.33.0 with: php-version: 8.2 tools: php-cs-fixer:3.49 diff --git a/.github/workflows/team-pr-auto-approve.yml b/.github/workflows/team-pr-auto-approve.yml index 0c2fdd81c..cc5c47139 100644 --- a/.github/workflows/team-pr-auto-approve.yml +++ b/.github/workflows/team-pr-auto-approve.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Generate access token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }} private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }} From 6bf9a305de7d722d15736f379cac944a5310ebee Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 3 May 2025 19:23:50 +0100 Subject: [PATCH 003/140] Rename confusing PHPStan rule name it never occurred to me that this was misleading until I read some Devin documentation, noticed that Devin misunderstood was the class was for, and then realized actually Devin understood correctly, and it was the name of the class that was wrong. Funny how that happens... --- phpstan.neon.dist | 2 +- tests/phpstan/configs/phpstan-bugs.neon | 2 +- ...fStringRule.php => UnsafeForeachArrayWithStringKeysRule.php} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/phpstan/rules/{UnsafeForeachArrayOfStringRule.php => UnsafeForeachArrayWithStringKeysRule.php} (98%) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 13f35c121..391f0f54c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -15,7 +15,7 @@ rules: - pocketmine\phpstan\rules\DisallowEnumComparisonRule - pocketmine\phpstan\rules\DisallowForeachByReferenceRule - pocketmine\phpstan\rules\ExplodeLimitRule - - pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule + - pocketmine\phpstan\rules\UnsafeForeachArrayWithStringKeysRule # - pocketmine\phpstan\rules\ThreadedSupportedTypesRule parameters: diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index aeb3fae29..cb92bf968 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -256,5 +256,5 @@ parameters: message: '#^Strict comparison using \=\=\= between 0 and 0 will always evaluate to true\.$#' identifier: identical.alwaysTrue count: 1 - path: ../rules/UnsafeForeachArrayOfStringRule.php + path: ../rules/UnsafeForeachArrayWithStringKeysRule.php diff --git a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php b/tests/phpstan/rules/UnsafeForeachArrayWithStringKeysRule.php similarity index 98% rename from tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php rename to tests/phpstan/rules/UnsafeForeachArrayWithStringKeysRule.php index 34056131b..83f47f092 100644 --- a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php +++ b/tests/phpstan/rules/UnsafeForeachArrayWithStringKeysRule.php @@ -41,7 +41,7 @@ use function sprintf; /** * @implements Rule */ -final class UnsafeForeachArrayOfStringRule implements Rule{ +final class UnsafeForeachArrayWithStringKeysRule implements Rule{ public function getNodeType() : string{ return Foreach_::class; From 2a42e2c75d1c1b92d17559833899ce35940da269 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 4 May 2025 16:41:40 +0100 Subject: [PATCH 004/140] Drop PluginLoader from Plugin, expose path instead we already had this anyway, and it's already being reflected into. Instead of DevTools checking for FolderPluginLoader instances, it could just check if the file is a directory instead. --- src/crash/CrashDump.php | 4 +--- src/plugin/Plugin.php | 4 ++-- src/plugin/PluginBase.php | 7 +------ src/plugin/PluginManager.php | 2 +- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php index 49a587c34..df7c9199d 100644 --- a/src/crash/CrashDump.php +++ b/src/crash/CrashDump.php @@ -259,10 +259,8 @@ class CrashDump{ } if(file_exists($filePath)){ - $reflection = new \ReflectionClass(PluginBase::class); - $file = $reflection->getProperty("file"); foreach($this->server->getPluginManager()->getPlugins() as $plugin){ - $filePath = Filesystem::cleanPath($file->getValue($plugin)); + $filePath = Filesystem::cleanPath($plugin->getFile()); if(str_starts_with($frameCleanPath, $filePath)){ $this->data->plugin = $plugin->getName(); break; diff --git a/src/plugin/Plugin.php b/src/plugin/Plugin.php index ae64d443b..b71e3f5fb 100644 --- a/src/plugin/Plugin.php +++ b/src/plugin/Plugin.php @@ -34,7 +34,7 @@ use pocketmine\Server; */ interface Plugin{ - public function __construct(PluginLoader $loader, Server $server, PluginDescription $description, string $dataFolder, string $file, string $resourceFolder); + public function __construct(Server $server, PluginDescription $description, string $dataFolder, string $file, string $resourceFolder); public function isEnabled() : bool; @@ -59,7 +59,7 @@ interface Plugin{ public function getLogger() : \AttachableLogger; - public function getPluginLoader() : PluginLoader; + public function getFile() : string; public function getScheduler() : TaskScheduler; diff --git a/src/plugin/PluginBase.php b/src/plugin/PluginBase.php index a32339e84..727e93591 100644 --- a/src/plugin/PluginBase.php +++ b/src/plugin/PluginBase.php @@ -59,7 +59,6 @@ abstract class PluginBase implements Plugin, CommandExecutor{ private TaskScheduler $scheduler; public function __construct( - private PluginLoader $loader, private Server $server, private PluginDescription $description, private string $dataFolder, @@ -311,14 +310,10 @@ abstract class PluginBase implements Plugin, CommandExecutor{ return $this->description->getFullName(); } - protected function getFile() : string{ + public function getFile() : string{ return $this->file; } - public function getPluginLoader() : PluginLoader{ - return $this->loader; - } - public function getScheduler() : TaskScheduler{ return $this->scheduler; } diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 1a74b64e7..3750af3ef 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -220,7 +220,7 @@ class PluginManager{ * @var Plugin $plugin * @see Plugin::__construct() */ - $plugin = new $mainClass($loader, $this->server, $description, $dataFolder, $prefixed, $prefixed . "/resources/"); + $plugin = new $mainClass($this->server, $description, $dataFolder, $prefixed, $prefixed . "/resources/"); $this->plugins[$plugin->getDescription()->getName()] = $plugin; return $plugin; From 912a5d6ad0cc8a0cbe407557bd483234b17af7a6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 4 May 2025 16:45:25 +0100 Subject: [PATCH 005/140] Remove TODO --- src/plugin/PluginBase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugin/PluginBase.php b/src/plugin/PluginBase.php index 727e93591..3b401d44a 100644 --- a/src/plugin/PluginBase.php +++ b/src/plugin/PluginBase.php @@ -66,7 +66,6 @@ abstract class PluginBase implements Plugin, CommandExecutor{ private string $resourceFolder, ){ $this->dataFolder = rtrim($dataFolder, "/" . DIRECTORY_SEPARATOR) . "/"; - //TODO: this is accessed externally via reflection, not unused $this->file = rtrim($file, "/" . DIRECTORY_SEPARATOR) . "/"; $this->resourceFolder = rtrim(str_replace(DIRECTORY_SEPARATOR, "/", $resourceFolder), "/") . "/"; From eb3922fc7e9e847ef59427d3aa833ff15781d8a2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 4 May 2025 17:10:01 +0100 Subject: [PATCH 006/140] shut --- src/crash/CrashDump.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php index df7c9199d..15c48c7a7 100644 --- a/src/crash/CrashDump.php +++ b/src/crash/CrashDump.php @@ -26,7 +26,6 @@ namespace pocketmine\crash; use Composer\InstalledVersions; use pocketmine\errorhandler\ErrorTypeToStringMap; use pocketmine\network\mcpe\protocol\ProtocolInfo; -use pocketmine\plugin\PluginBase; use pocketmine\plugin\PluginManager; use pocketmine\Server; use pocketmine\thread\ThreadCrashInfoFrame; From f2e74736296eca93f3732cc64041535e824dab00 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 4 May 2025 17:18:58 +0100 Subject: [PATCH 007/140] Update PHP-CS-Fixer --- .github/workflows/main.yml | 4 ++-- .php-cs-fixer.php | 6 ++++++ build/dump-version-info.php | 4 ++-- src/block/tile/Spawnable.php | 4 ++-- src/world/World.php | 2 +- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cfe97aa7e..cabda54be 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,8 +30,8 @@ jobs: - name: Setup PHP and tools uses: shivammathur/setup-php@2.33.0 with: - php-version: 8.2 - tools: php-cs-fixer:3.49 + php-version: 8.3 + tools: php-cs-fixer:3.75 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 32af1ef48..5a14b1d35 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -6,6 +6,12 @@ $finder = PhpCsFixer\Finder::create() ->in(__DIR__ . '/tests') ->in(__DIR__ . '/tools') ->notPath('plugins/DevTools') + //JsonMapper will break if the FQNs in the doc comments for these are shortened :( + ->notPath('crafting/json') + ->notPath('inventory/json') + ->notPath('data/bedrock/block/upgrade/model') + ->notPath('data/bedrock/item/upgrade/model') + ->notName('PocketMine.php'); return (new PhpCsFixer\Config) diff --git a/build/dump-version-info.php b/build/dump-version-info.php index e13696f3d..3181acba6 100644 --- a/build/dump-version-info.php +++ b/build/dump-version-info.php @@ -31,8 +31,8 @@ require dirname(__DIR__) . '/vendor/autoload.php'; */ /** - * @var string[]|\Closure[] $options - * @phpstan-var array $options + * @var string[]|Closure[] $options + * @phpstan-var array $options */ $options = [ "base_version" => VersionInfo::BASE_VERSION, diff --git a/src/block/tile/Spawnable.php b/src/block/tile/Spawnable.php index 67bc72fd9..0c41713f2 100644 --- a/src/block/tile/Spawnable.php +++ b/src/block/tile/Spawnable.php @@ -32,7 +32,7 @@ use pocketmine\network\mcpe\protocol\types\CacheableNbt; use function get_class; abstract class Spawnable extends Tile{ - /** @phpstan-var CacheableNbt<\pocketmine\nbt\tag\CompoundTag>|null */ + /** @phpstan-var CacheableNbt|null */ private ?CacheableNbt $spawnCompoundCache = null; /** @@ -73,7 +73,7 @@ abstract class Spawnable extends Tile{ * Returns encoded NBT (varint, little-endian) used to spawn this tile to clients. Uses cache where possible, * populates cache if it is null. * - * @phpstan-return CacheableNbt<\pocketmine\nbt\tag\CompoundTag> + * @phpstan-return CacheableNbt */ final public function getSerializedSpawnCompound() : CacheableNbt{ if($this->spawnCompoundCache === null){ diff --git a/src/world/World.php b/src/world/World.php index 3a7d0c538..63a617126 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -360,7 +360,7 @@ class World implements ChunkManager{ private bool $doingTick = false; - /** @phpstan-var class-string<\pocketmine\world\generator\Generator> */ + /** @phpstan-var class-string */ private string $generator; private bool $unloaded = false; From d789c75c0084cacac09baa33d8ec5462d1f9c089 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 8 May 2025 02:21:39 +0100 Subject: [PATCH 008/140] Improve PHPStan error reporting for unsafe foreaches these are actually two separate concerns: one for dodgy PHPStan type suppression on implicit keys, and the other for arrays being casted to strings by PHP. --- phpstan.neon.dist | 2 +- ...OfStringRule.php => UnsafeForeachRule.php} | 43 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) rename tests/phpstan/rules/{UnsafeForeachArrayOfStringRule.php => UnsafeForeachRule.php} (69%) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 13f35c121..12c739f2f 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -15,7 +15,7 @@ rules: - pocketmine\phpstan\rules\DisallowEnumComparisonRule - pocketmine\phpstan\rules\DisallowForeachByReferenceRule - pocketmine\phpstan\rules\ExplodeLimitRule - - pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule + - pocketmine\phpstan\rules\UnsafeForeachRule # - pocketmine\phpstan\rules\ThreadedSupportedTypesRule parameters: diff --git a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php b/tests/phpstan/rules/UnsafeForeachRule.php similarity index 69% rename from tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php rename to tests/phpstan/rules/UnsafeForeachRule.php index 34056131b..cb463c61d 100644 --- a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php +++ b/tests/phpstan/rules/UnsafeForeachRule.php @@ -41,7 +41,7 @@ use function sprintf; /** * @implements Rule */ -final class UnsafeForeachArrayOfStringRule implements Rule{ +final class UnsafeForeachRule implements Rule{ public function getNodeType() : string{ return Foreach_::class; @@ -73,7 +73,7 @@ final class UnsafeForeachArrayOfStringRule implements Rule{ $benevolentUnionDepth--; return $result; } - if($type instanceof IntegerType && $benevolentUnionDepth === 0){ + if($type instanceof IntegerType){ $expectsIntKeyTypes = true; return $type; } @@ -87,24 +87,31 @@ final class UnsafeForeachArrayOfStringRule implements Rule{ $hasCastableKeyTypes = true; return $type; }); - if($hasCastableKeyTypes && !$expectsIntKeyTypes){ - $tip = $implicitType ? - sprintf( - "Declare a key type using @phpstan-var or @phpstan-param, or use %s() to promote the key type to get proper error reporting", + $errors = []; + if($implicitType){ + $errors[] = RuleErrorBuilder::message("Possible unreported errors in foreach on array with unspecified key type.") + ->tip(sprintf( + <<getIterableKeyType()->describe(VerbosityLevel::value()) - ))->tip($tip)->identifier('pocketmine.foreach.stringKeys')->build() - ]; + ))->identifier('pocketmine.foreach.implicitKeys')->build(); } - return []; + if($hasCastableKeyTypes && !$expectsIntKeyTypes){ + $errors[] = RuleErrorBuilder::message(sprintf( + "Unsafe foreach on array with key type %s.", + $iterableType->getIterableKeyType()->describe(VerbosityLevel::value()) + )) + ->tip(sprintf( + <<identifier('pocketmine.foreach.stringKeys')->build(); + } + return $errors; } } From 5e830c732075d067887eaac9fb605d6a347e8115 Mon Sep 17 00:00:00 2001 From: Dries C Date: Fri, 9 May 2025 16:29:05 +0200 Subject: [PATCH 009/140] Protocol changes for 1.21.80 (#6687) * Bedrock 1.21.80 support * Update bedrock-data * Add required tags to models * Fixed biome data loading * Support newest world format apparently I messed up the blockstate data version last time around... it hasn't changed since 1.21.60 * always CS has to complain... * Sync with release versions * Ready 5.28.0 release * this might help... --------- Co-authored-by: Dylan T. --- changelogs/5.28.md | 21 ++++++ composer.json | 4 +- composer.lock | 30 ++++---- src/VersionInfo.php | 4 +- src/data/bedrock/BedrockDataFiles.php | 3 +- src/data/bedrock/block/BlockStateData.php | 4 +- src/data/bedrock/block/BlockStateNames.php | 1 + src/data/bedrock/block/BlockTypeNames.php | 1 + src/data/bedrock/item/ItemTypeNames.php | 17 +++++ src/network/mcpe/cache/StaticPacketCache.php | 63 ++++++++++++++++- .../mcpe/handler/InGamePacketHandler.php | 5 -- .../biome/model/BiomeDefinitionEntryData.php | 69 +++++++++++++++++++ src/world/biome/model/ColorData.php | 41 +++++++++++ src/world/format/io/data/BedrockWorldData.php | 4 +- tools/generate-bedrock-data-from-packets.php | 54 ++++++++------- 15 files changed, 264 insertions(+), 57 deletions(-) create mode 100644 changelogs/5.28.md create mode 100644 src/world/biome/model/BiomeDefinitionEntryData.php create mode 100644 src/world/biome/model/ColorData.php diff --git a/changelogs/5.28.md b/changelogs/5.28.md new file mode 100644 index 000000000..f368e819e --- /dev/null +++ b/changelogs/5.28.md @@ -0,0 +1,21 @@ +# 5.28.0 +Released 9th May 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.80. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Aded support for Minecraft: Bedrock Edition 1.21.70. +- Removed support for earlier versions. + +## Fixes +- `AvailableEnchantmentRegistry` now requires provided tags to always be `string`. Previously, this wasn't enforced, leading to random crashes in core code related to enchanting. +- `Entity->setFireTicks()` and `Entity->setOnFire()` now truncate the fire time to the max value instead of throwing exceptions. + +## Internals +- Improved PHPStan error reporting for unsafe foreaches. Foreach on an array with implicit keys now generates different errors than foreach on an array with string keys. diff --git a/composer.json b/composer.json index 87086f456..7545806b4 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", - "pocketmine/bedrock-data": "~4.1.0+bedrock-1.21.70", + "pocketmine/bedrock-data": "5.0.0+bedrock-1.21.80", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~37.0.0+bedrock-1.21.70", + "pocketmine/bedrock-protocol": "38.0.0+bedrock-1.21.80", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 23f312317..d45311018 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": "818c679a25da8e6b466bc454ad48dec3", + "content-hash": "4dfc7b8c912d8d5fa194ddc0e97903fb", "packages": [ { "name": "adhocore/json-comment", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "4.1.0+bedrock-1.21.70", + "version": "5.0.0+bedrock-1.21.80", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "d53fe98cb3b596ac016e275df5bd5e89b04a4817" + "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d53fe98cb3b596ac016e275df5bd5e89b04a4817", - "reference": "d53fe98cb3b596ac016e275df5bd5e89b04a4817", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e38d5ea19f794ec5216e5f96742237e8c4e7f080", + "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080", "shasum": "" }, "type": "library", @@ -224,9 +224,9 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.70" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.80" }, - "time": "2025-03-25T19:43:31+00:00" + "time": "2025-05-09T14:15:18+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "37.0.0+bedrock-1.21.70", + "version": "38.0.0+bedrock-1.21.80", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "7091dad2c12ed4a4106432df21fc698960c6be9e" + "reference": "a626561eaefeb6333c0d2726e2789ceb0aac0724" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/7091dad2c12ed4a4106432df21fc698960c6be9e", - "reference": "7091dad2c12ed4a4106432df21fc698960c6be9e", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a626561eaefeb6333c0d2726e2789ceb0aac0724", + "reference": "a626561eaefeb6333c0d2726e2789ceb0aac0724", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/37.0.0+bedrock-1.21.70" + "source": "https://github.com/pmmp/BedrockProtocol/tree/38.0.0+bedrock-1.21.80" }, - "time": "2025-03-27T15:19:36+00:00" + "time": "2025-05-09T14:17:07+00:00" }, { "name": "pocketmine/binaryutils", @@ -2921,7 +2921,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -2952,7 +2952,7 @@ "ext-zlib": ">=1.2.11", "composer-runtime-api": "^2.0" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.1.0" }, diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 44238dba3..c5b38f072 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.27.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.28.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** diff --git a/src/data/bedrock/BedrockDataFiles.php b/src/data/bedrock/BedrockDataFiles.php index 1ecb707cc..53bd9b11e 100644 --- a/src/data/bedrock/BedrockDataFiles.php +++ b/src/data/bedrock/BedrockDataFiles.php @@ -31,8 +31,7 @@ final class BedrockDataFiles{ } public const BANNER_PATTERNS_JSON = BEDROCK_DATA_PATH . '/banner_patterns.json'; - public const BIOME_DEFINITIONS_NBT = BEDROCK_DATA_PATH . '/biome_definitions.nbt'; - public const BIOME_DEFINITIONS_FULL_NBT = BEDROCK_DATA_PATH . '/biome_definitions_full.nbt'; + public const BIOME_DEFINITIONS_JSON = BEDROCK_DATA_PATH . '/biome_definitions.json'; public const BIOME_ID_MAP_JSON = BEDROCK_DATA_PATH . '/biome_id_map.json'; public const BLOCK_ID_TO_ITEM_ID_MAP_JSON = BEDROCK_DATA_PATH . '/block_id_to_item_id_map.json'; public const BLOCK_PROPERTIES_TABLE_JSON = BEDROCK_DATA_PATH . '/block_properties_table.json'; diff --git a/src/data/bedrock/block/BlockStateData.php b/src/data/bedrock/block/BlockStateData.php index 600eba938..e90410ac7 100644 --- a/src/data/bedrock/block/BlockStateData.php +++ b/src/data/bedrock/block/BlockStateData.php @@ -45,8 +45,8 @@ final class BlockStateData{ public const CURRENT_VERSION = (1 << 24) | //major (21 << 16) | //minor - (70 << 8) | //patch - (1); //revision + (60 << 8) | //patch + (33); //revision public const TAG_NAME = "name"; public const TAG_STATES = "states"; diff --git a/src/data/bedrock/block/BlockStateNames.php b/src/data/bedrock/block/BlockStateNames.php index 9fed77e4a..704798d1d 100644 --- a/src/data/bedrock/block/BlockStateNames.php +++ b/src/data/bedrock/block/BlockStateNames.php @@ -113,6 +113,7 @@ final class BlockStateNames{ public const RAIL_DATA_BIT = "rail_data_bit"; public const RAIL_DIRECTION = "rail_direction"; public const REDSTONE_SIGNAL = "redstone_signal"; + public const REHYDRATION_LEVEL = "rehydration_level"; public const REPEATER_DELAY = "repeater_delay"; public const RESPAWN_ANCHOR_CHARGE = "respawn_anchor_charge"; public const ROTATION = "rotation"; diff --git a/src/data/bedrock/block/BlockTypeNames.php b/src/data/bedrock/block/BlockTypeNames.php index bc30800fc..527a01345 100644 --- a/src/data/bedrock/block/BlockTypeNames.php +++ b/src/data/bedrock/block/BlockTypeNames.php @@ -392,6 +392,7 @@ final class BlockTypeNames{ public const DOUBLE_CUT_COPPER_SLAB = "minecraft:double_cut_copper_slab"; public const DRAGON_EGG = "minecraft:dragon_egg"; public const DRAGON_HEAD = "minecraft:dragon_head"; + public const DRIED_GHAST = "minecraft:dried_ghast"; public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block"; public const DRIPSTONE_BLOCK = "minecraft:dripstone_block"; public const DROPPER = "minecraft:dropper"; diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index ea95d57f0..5f86cde96 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -68,6 +68,7 @@ final class ItemTypeNames{ public const BIRCH_SIGN = "minecraft:birch_sign"; public const BLACK_BUNDLE = "minecraft:black_bundle"; public const BLACK_DYE = "minecraft:black_dye"; + public const BLACK_HARNESS = "minecraft:black_harness"; public const BLADE_POTTERY_SHERD = "minecraft:blade_pottery_sherd"; public const BLAZE_POWDER = "minecraft:blaze_powder"; public const BLAZE_ROD = "minecraft:blaze_rod"; @@ -76,6 +77,7 @@ final class ItemTypeNames{ public const BLUE_BUNDLE = "minecraft:blue_bundle"; public const BLUE_DYE = "minecraft:blue_dye"; public const BLUE_EGG = "minecraft:blue_egg"; + public const BLUE_HARNESS = "minecraft:blue_harness"; public const BOARD = "minecraft:board"; public const BOAT = "minecraft:boat"; public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg"; @@ -95,6 +97,7 @@ final class ItemTypeNames{ public const BROWN_BUNDLE = "minecraft:brown_bundle"; public const BROWN_DYE = "minecraft:brown_dye"; public const BROWN_EGG = "minecraft:brown_egg"; + public const BROWN_HARNESS = "minecraft:brown_harness"; public const BRUSH = "minecraft:brush"; public const BUCKET = "minecraft:bucket"; public const BUNDLE = "minecraft:bundle"; @@ -166,6 +169,7 @@ final class ItemTypeNames{ public const CROSSBOW = "minecraft:crossbow"; public const CYAN_BUNDLE = "minecraft:cyan_bundle"; public const CYAN_DYE = "minecraft:cyan_dye"; + public const CYAN_HARNESS = "minecraft:cyan_harness"; public const DANGER_POTTERY_SHERD = "minecraft:danger_pottery_sherd"; public const DARK_OAK_BOAT = "minecraft:dark_oak_boat"; public const DARK_OAK_CHEST_BOAT = "minecraft:dark_oak_chest_boat"; @@ -265,12 +269,15 @@ final class ItemTypeNames{ public const GOLDEN_SWORD = "minecraft:golden_sword"; public const GRAY_BUNDLE = "minecraft:gray_bundle"; public const GRAY_DYE = "minecraft:gray_dye"; + public const GRAY_HARNESS = "minecraft:gray_harness"; public const GREEN_BUNDLE = "minecraft:green_bundle"; public const GREEN_DYE = "minecraft:green_dye"; + public const GREEN_HARNESS = "minecraft:green_harness"; public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg"; public const GUNPOWDER = "minecraft:gunpowder"; public const GUSTER_BANNER_PATTERN = "minecraft:guster_banner_pattern"; public const GUSTER_POTTERY_SHERD = "minecraft:guster_pottery_sherd"; + public const HAPPY_GHAST_SPAWN_EGG = "minecraft:happy_ghast_spawn_egg"; public const HARD_STAINED_GLASS = "minecraft:hard_stained_glass"; public const HARD_STAINED_GLASS_PANE = "minecraft:hard_stained_glass_pane"; public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea"; @@ -321,10 +328,13 @@ final class ItemTypeNames{ public const LIGHT_BLOCK = "minecraft:light_block"; public const LIGHT_BLUE_BUNDLE = "minecraft:light_blue_bundle"; public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye"; + public const LIGHT_BLUE_HARNESS = "minecraft:light_blue_harness"; public const LIGHT_GRAY_BUNDLE = "minecraft:light_gray_bundle"; public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye"; + public const LIGHT_GRAY_HARNESS = "minecraft:light_gray_harness"; public const LIME_BUNDLE = "minecraft:lime_bundle"; public const LIME_DYE = "minecraft:lime_dye"; + public const LIME_HARNESS = "minecraft:lime_harness"; public const LINGERING_POTION = "minecraft:lingering_potion"; public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg"; public const LODESTONE_COMPASS = "minecraft:lodestone_compass"; @@ -333,6 +343,7 @@ final class ItemTypeNames{ public const MACE = "minecraft:mace"; public const MAGENTA_BUNDLE = "minecraft:magenta_bundle"; public const MAGENTA_DYE = "minecraft:magenta_dye"; + public const MAGENTA_HARNESS = "minecraft:magenta_harness"; public const MAGMA_CREAM = "minecraft:magma_cream"; public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg"; public const MANGROVE_BOAT = "minecraft:mangrove_boat"; @@ -400,6 +411,7 @@ final class ItemTypeNames{ public const OMINOUS_TRIAL_KEY = "minecraft:ominous_trial_key"; public const ORANGE_BUNDLE = "minecraft:orange_bundle"; public const ORANGE_DYE = "minecraft:orange_dye"; + public const ORANGE_HARNESS = "minecraft:orange_harness"; public const OXIDIZED_COPPER_DOOR = "minecraft:oxidized_copper_door"; public const PAINTING = "minecraft:painting"; public const PALE_OAK_BOAT = "minecraft:pale_oak_boat"; @@ -419,6 +431,7 @@ final class ItemTypeNames{ public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg"; public const PINK_BUNDLE = "minecraft:pink_bundle"; public const PINK_DYE = "minecraft:pink_dye"; + public const PINK_HARNESS = "minecraft:pink_harness"; public const PITCHER_POD = "minecraft:pitcher_pod"; public const PLANKS = "minecraft:planks"; public const PLENTY_POTTERY_SHERD = "minecraft:plenty_pottery_sherd"; @@ -439,6 +452,7 @@ final class ItemTypeNames{ public const PUMPKIN_SEEDS = "minecraft:pumpkin_seeds"; public const PURPLE_BUNDLE = "minecraft:purple_bundle"; public const PURPLE_DYE = "minecraft:purple_dye"; + public const PURPLE_HARNESS = "minecraft:purple_harness"; public const QUARTZ = "minecraft:quartz"; public const RABBIT = "minecraft:rabbit"; public const RABBIT_FOOT = "minecraft:rabbit_foot"; @@ -455,6 +469,7 @@ final class ItemTypeNames{ public const RED_BUNDLE = "minecraft:red_bundle"; public const RED_DYE = "minecraft:red_dye"; public const RED_FLOWER = "minecraft:red_flower"; + public const RED_HARNESS = "minecraft:red_harness"; public const REDSTONE = "minecraft:redstone"; public const REPEATER = "minecraft:repeater"; public const RESIN_BRICK = "minecraft:resin_brick"; @@ -563,6 +578,7 @@ final class ItemTypeNames{ public const WHEAT_SEEDS = "minecraft:wheat_seeds"; public const WHITE_BUNDLE = "minecraft:white_bundle"; public const WHITE_DYE = "minecraft:white_dye"; + public const WHITE_HARNESS = "minecraft:white_harness"; public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wild_armor_trim_smithing_template"; public const WIND_CHARGE = "minecraft:wind_charge"; public const WITCH_SPAWN_EGG = "minecraft:witch_spawn_egg"; @@ -583,6 +599,7 @@ final class ItemTypeNames{ public const WRITTEN_BOOK = "minecraft:written_book"; public const YELLOW_BUNDLE = "minecraft:yellow_bundle"; public const YELLOW_DYE = "minecraft:yellow_dye"; + public const YELLOW_HARNESS = "minecraft:yellow_harness"; public const ZOGLIN_SPAWN_EGG = "minecraft:zoglin_spawn_egg"; public const ZOMBIE_HORSE_SPAWN_EGG = "minecraft:zombie_horse_spawn_egg"; public const ZOMBIE_PIGMAN_SPAWN_EGG = "minecraft:zombie_pigman_spawn_egg"; diff --git a/src/network/mcpe/cache/StaticPacketCache.php b/src/network/mcpe/cache/StaticPacketCache.php index 88a522600..861881437 100644 --- a/src/network/mcpe/cache/StaticPacketCache.php +++ b/src/network/mcpe/cache/StaticPacketCache.php @@ -23,13 +23,22 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\cache; +use pocketmine\color\Color; use pocketmine\data\bedrock\BedrockDataFiles; +use pocketmine\data\SavedDataLoadingException; use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket; use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; +use pocketmine\network\mcpe\protocol\types\biome\BiomeDefinitionEntry; use pocketmine\network\mcpe\protocol\types\CacheableNbt; use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; +use pocketmine\utils\Utils; +use pocketmine\world\biome\model\BiomeDefinitionEntryData; +use function count; +use function get_debug_type; +use function is_array; +use function json_decode; class StaticPacketCache{ use SingletonTrait; @@ -41,9 +50,61 @@ class StaticPacketCache{ return new CacheableNbt((new NetworkNbtSerializer())->read(Filesystem::fileGetContents($filePath))->mustGetCompoundTag()); } + /** + * @return list + */ + private static function loadBiomeDefinitionModel(string $filePath) : array{ + $biomeEntries = json_decode(Filesystem::fileGetContents($filePath), associative: true); + if(!is_array($biomeEntries)){ + throw new SavedDataLoadingException("$filePath root should be an array, got " . get_debug_type($biomeEntries)); + } + + $jsonMapper = new \JsonMapper(); + $jsonMapper->bExceptionOnMissingData = true; + $jsonMapper->bStrictObjectTypeChecking = true; + $jsonMapper->bEnforceMapType = false; + + $entries = []; + foreach(Utils::promoteKeys($biomeEntries) as $biomeName => $entry){ + if(!is_array($entry)){ + throw new SavedDataLoadingException("$filePath should be an array of objects, got " . get_debug_type($entry)); + } + + try{ + $biomeDefinition = $jsonMapper->map($entry, new BiomeDefinitionEntryData()); + + $mapWaterColour = $biomeDefinition->mapWaterColour; + $entries[] = new BiomeDefinitionEntry( + (string) $biomeName, + $biomeDefinition->id, + $biomeDefinition->temperature, + $biomeDefinition->downfall, + $biomeDefinition->redSporeDensity, + $biomeDefinition->blueSporeDensity, + $biomeDefinition->ashDensity, + $biomeDefinition->whiteAshDensity, + $biomeDefinition->depth, + $biomeDefinition->scale, + new Color( + $mapWaterColour->r, + $mapWaterColour->g, + $mapWaterColour->b, + $mapWaterColour->a + ), + $biomeDefinition->rain, + count($biomeDefinition->tags) > 0 ? $biomeDefinition->tags : null, + ); + }catch(\JsonMapper_Exception $e){ + throw new \RuntimeException($e->getMessage(), 0, $e); + } + } + + return $entries; + } + private static function make() : self{ return new self( - BiomeDefinitionListPacket::create(self::loadCompoundFromFile(BedrockDataFiles::BIOME_DEFINITIONS_NBT)), + BiomeDefinitionListPacket::fromDefinitions(self::loadBiomeDefinitionModel(BedrockDataFiles::BIOME_DEFINITIONS_JSON)), AvailableActorIdentifiersPacket::create(self::loadCompoundFromFile(BedrockDataFiles::ENTITY_IDENTIFIERS_NBT)) ); } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 93a01fdcc..eec200e4b 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -73,7 +73,6 @@ use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket; use pocketmine\network\mcpe\protocol\PlayerActionPacket; use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket; use pocketmine\network\mcpe\protocol\PlayerHotbarPacket; -use pocketmine\network\mcpe\protocol\PlayerInputPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket; use pocketmine\network\mcpe\protocol\serializer\BitSet; @@ -781,10 +780,6 @@ class InGamePacketHandler extends PacketHandler{ return false; } - public function handlePlayerInput(PlayerInputPacket $packet) : bool{ - return false; //TODO - } - public function handleSetPlayerGameType(SetPlayerGameTypePacket $packet) : bool{ $gameMode = $this->session->getTypeConverter()->protocolGameModeToCore($packet->gamemode); if($gameMode !== $this->player->getGamemode()){ diff --git a/src/world/biome/model/BiomeDefinitionEntryData.php b/src/world/biome/model/BiomeDefinitionEntryData.php new file mode 100644 index 000000000..8a5c3d354 --- /dev/null +++ b/src/world/biome/model/BiomeDefinitionEntryData.php @@ -0,0 +1,69 @@ + + */ + public array $tags; +} diff --git a/src/world/biome/model/ColorData.php b/src/world/biome/model/ColorData.php new file mode 100644 index 000000000..f70a77d15 --- /dev/null +++ b/src/world/biome/model/ColorData.php @@ -0,0 +1,41 @@ +bedrockDataPath . '/biome_definitions_full.nbt', $packet->definitions->getEncodedNbt()); + $definitions = []; + foreach($packet->buildDefinitionsFromData() as $entry){ + $mapWaterColor = new ColorData(); + $mapWaterColor->r = $entry->getMapWaterColor()->getR(); + $mapWaterColor->g = $entry->getMapWaterColor()->getG(); + $mapWaterColor->b = $entry->getMapWaterColor()->getB(); + $mapWaterColor->a = $entry->getMapWaterColor()->getA(); - $nbt = $packet->definitions->getRoot(); - if(!$nbt instanceof CompoundTag){ - throw new AssumptionFailedError(); - } - $strippedNbt = clone $nbt; - foreach($strippedNbt as $compound){ - if($compound instanceof CompoundTag){ - foreach([ - "minecraft:capped_surface", - "minecraft:consolidated_features", - "minecraft:frozen_ocean_surface", - "minecraft:legacy_world_generation_rules", - "minecraft:mesa_surface", - "minecraft:mountain_parameters", - "minecraft:multinoise_generation_rules", - "minecraft:overworld_generation_rules", - "minecraft:surface_material_adjustments", - "minecraft:surface_parameters", - "minecraft:swamp_surface", - ] as $remove){ - $compound->removeTag($remove); - } - } + $data = new BiomeDefinitionEntryData(); + $data->id = $entry->getId(); + $data->temperature = round($entry->getTemperature(), 3); + $data->downfall = round($entry->getDownfall(), 3); + $data->redSporeDensity = round($entry->getRedSporeDensity(), 3); + $data->blueSporeDensity = round($entry->getBlueSporeDensity(), 3); + $data->ashDensity = round($entry->getAshDensity(), 3); + $data->whiteAshDensity = round($entry->getWhiteAshDensity(), 3); + $data->depth = round($entry->getDepth(), 3); + $data->scale = round($entry->getScale(), 3); + $data->mapWaterColour = $mapWaterColor; + $data->rain = $entry->hasRain(); + $data->tags = $entry->getTags() ?? []; + + $definitions[$entry->getBiomeName()] = self::objectToOrderedArray($data); } - file_put_contents($this->bedrockDataPath . '/biome_definitions.nbt', (new CacheableNbt($strippedNbt))->getEncodedNbt()); + ksort($definitions, SORT_STRING); + + file_put_contents($this->bedrockDataPath . '/biome_definitions.json', json_encode($definitions, JSON_PRETTY_PRINT) . "\n"); return true; } From 134c7309c5985cd6df0a323a8465f44e9099510a Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 14:30:04 +0000 Subject: [PATCH 010/140] 5.28.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/14931216524 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index c5b38f072..acc7db91c 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.28.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.28.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From d90fc3415c611a976cdde80bbcd79574fadb38bf Mon Sep 17 00:00:00 2001 From: ItzxDwi <107537435+ItzxDwi@users.noreply.github.com> Date: Fri, 9 May 2025 23:33:55 +0800 Subject: [PATCH 011/140] fixed wrong version info (#6689) --- changelogs/5.28.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.28.md b/changelogs/5.28.md index f368e819e..8a69b97e0 100644 --- a/changelogs/5.28.md +++ b/changelogs/5.28.md @@ -10,7 +10,7 @@ Do not update plugin minimum API versions unless you need new features added in Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. ## General -- Aded support for Minecraft: Bedrock Edition 1.21.70. +- Aded support for Minecraft: Bedrock Edition 1.21.80. - Removed support for earlier versions. ## Fixes From 04de72e85ec8c8da36e1d527db3cbe4ee855a124 Mon Sep 17 00:00:00 2001 From: Sergi del Olmo Date: Sat, 10 May 2025 15:34:37 +0200 Subject: [PATCH 012/140] Fix changelog typo (#6690) --- changelogs/5.28.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.28.md b/changelogs/5.28.md index 8a69b97e0..a2ede942f 100644 --- a/changelogs/5.28.md +++ b/changelogs/5.28.md @@ -10,7 +10,7 @@ Do not update plugin minimum API versions unless you need new features added in Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. ## General -- Aded support for Minecraft: Bedrock Edition 1.21.80. +- Added support for Minecraft: Bedrock Edition 1.21.80. - Removed support for earlier versions. ## Fixes From bb7bfee0cd94bdae7026026e9eaf01d53157eb19 Mon Sep 17 00:00:00 2001 From: zSALLAZAR <59490940+zSALLAZAR@users.noreply.github.com> Date: Wed, 14 May 2025 08:06:22 +0200 Subject: [PATCH 013/140] Remove `ServerEvent` class (#6695) --- src/event/server/CommandEvent.php | 3 +- src/event/server/DataPacketDecodeEvent.php | 3 +- src/event/server/DataPacketReceiveEvent.php | 3 +- src/event/server/DataPacketSendEvent.php | 3 +- src/event/server/LowMemoryEvent.php | 3 +- src/event/server/NetworkInterfaceEvent.php | 3 +- src/event/server/QueryRegenerateEvent.php | 3 +- src/event/server/ServerEvent.php | 33 --------------------- src/event/server/UpdateNotifyEvent.php | 3 +- 9 files changed, 16 insertions(+), 41 deletions(-) delete mode 100644 src/event/server/ServerEvent.php diff --git a/src/event/server/CommandEvent.php b/src/event/server/CommandEvent.php index 1881ad496..f8feb6808 100644 --- a/src/event/server/CommandEvent.php +++ b/src/event/server/CommandEvent.php @@ -26,6 +26,7 @@ namespace pocketmine\event\server; use pocketmine\command\CommandSender; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\event\Event; /** * Called when any CommandSender runs a command, before it is parsed. @@ -41,7 +42,7 @@ use pocketmine\event\CancellableTrait; * * The message DOES NOT begin with a slash. */ -class CommandEvent extends ServerEvent implements Cancellable{ +class CommandEvent extends Event implements Cancellable{ use CancellableTrait; public function __construct( diff --git a/src/event/server/DataPacketDecodeEvent.php b/src/event/server/DataPacketDecodeEvent.php index 44aefbb91..9a3b9be21 100644 --- a/src/event/server/DataPacketDecodeEvent.php +++ b/src/event/server/DataPacketDecodeEvent.php @@ -25,13 +25,14 @@ namespace pocketmine\event\server; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\event\Event; use pocketmine\network\mcpe\NetworkSession; /** * Called before a packet is decoded and handled by the network session. * Cancelling this event will drop the packet without decoding it, minimizing wasted CPU time. */ -class DataPacketDecodeEvent extends ServerEvent implements Cancellable{ +class DataPacketDecodeEvent extends Event implements Cancellable{ use CancellableTrait; public function __construct( diff --git a/src/event/server/DataPacketReceiveEvent.php b/src/event/server/DataPacketReceiveEvent.php index 17224003d..41d2a2445 100644 --- a/src/event/server/DataPacketReceiveEvent.php +++ b/src/event/server/DataPacketReceiveEvent.php @@ -25,10 +25,11 @@ namespace pocketmine\event\server; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\event\Event; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\ServerboundPacket; -class DataPacketReceiveEvent extends ServerEvent implements Cancellable{ +class DataPacketReceiveEvent extends Event implements Cancellable{ use CancellableTrait; public function __construct( diff --git a/src/event/server/DataPacketSendEvent.php b/src/event/server/DataPacketSendEvent.php index 147d99db3..d46990015 100644 --- a/src/event/server/DataPacketSendEvent.php +++ b/src/event/server/DataPacketSendEvent.php @@ -25,6 +25,7 @@ namespace pocketmine\event\server; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\event\Event; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\utils\Utils; @@ -32,7 +33,7 @@ use pocketmine\utils\Utils; /** * Called when packets are sent to network sessions. */ -class DataPacketSendEvent extends ServerEvent implements Cancellable{ +class DataPacketSendEvent extends Event implements Cancellable{ use CancellableTrait; /** diff --git a/src/event/server/LowMemoryEvent.php b/src/event/server/LowMemoryEvent.php index aea9afa80..a30e747e5 100644 --- a/src/event/server/LowMemoryEvent.php +++ b/src/event/server/LowMemoryEvent.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\event\server; +use pocketmine\event\Event; use pocketmine\utils\Process; /** * Called when the server is in a low-memory state as defined by the properties * Plugins should free caches or other non-essential data. */ -class LowMemoryEvent extends ServerEvent{ +class LowMemoryEvent extends Event{ public function __construct( private int $memory, private int $memoryLimit, diff --git a/src/event/server/NetworkInterfaceEvent.php b/src/event/server/NetworkInterfaceEvent.php index 5c1675f19..2278c1bc2 100644 --- a/src/event/server/NetworkInterfaceEvent.php +++ b/src/event/server/NetworkInterfaceEvent.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\event\server; +use pocketmine\event\Event; use pocketmine\network\NetworkInterface; -class NetworkInterfaceEvent extends ServerEvent{ +class NetworkInterfaceEvent extends Event{ public function __construct( protected NetworkInterface $interface ){} diff --git a/src/event/server/QueryRegenerateEvent.php b/src/event/server/QueryRegenerateEvent.php index c51bc5054..02146621a 100644 --- a/src/event/server/QueryRegenerateEvent.php +++ b/src/event/server/QueryRegenerateEvent.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\event\server; +use pocketmine\event\Event; use pocketmine\network\query\QueryInfo; -class QueryRegenerateEvent extends ServerEvent{ +class QueryRegenerateEvent extends Event{ public function __construct(private QueryInfo $queryInfo){} public function getQueryInfo() : QueryInfo{ diff --git a/src/event/server/ServerEvent.php b/src/event/server/ServerEvent.php deleted file mode 100644 index 97e79279d..000000000 --- a/src/event/server/ServerEvent.php +++ /dev/null @@ -1,33 +0,0 @@ - Date: Sat, 17 May 2025 13:33:42 +0100 Subject: [PATCH 014/140] Consolidate Bedrock data version info this ensures we don't have to go into a bunch of randomly scattered files to update version numbers. --- src/data/bedrock/WorldDataVersions.php | 67 +++++++++++++++++++ src/data/bedrock/block/BlockStateData.php | 13 +--- src/world/format/io/data/BedrockWorldData.php | 13 ++-- src/world/format/io/leveldb/LevelDB.php | 5 +- 4 files changed, 76 insertions(+), 22 deletions(-) create mode 100644 src/data/bedrock/WorldDataVersions.php diff --git a/src/data/bedrock/WorldDataVersions.php b/src/data/bedrock/WorldDataVersions.php new file mode 100644 index 000000000..498dac9da --- /dev/null +++ b/src/data/bedrock/WorldDataVersions.php @@ -0,0 +1,67 @@ + Date: Sat, 17 May 2025 13:35:52 +0100 Subject: [PATCH 015/140] always the CS... --- src/data/bedrock/WorldDataVersions.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/bedrock/WorldDataVersions.php b/src/data/bedrock/WorldDataVersions.php index 498dac9da..e79478b11 100644 --- a/src/data/bedrock/WorldDataVersions.php +++ b/src/data/bedrock/WorldDataVersions.php @@ -43,7 +43,6 @@ final class WorldDataVersions{ (60 << 8) | //patch (33); //revision - public const CHUNK = ChunkVersion::v1_21_40; public const SUBCHUNK = SubChunkVersion::PALETTED_MULTI; From 67f3bb9c5242dfab49ccb25d65edb4e00ac3f7e2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 17 May 2025 13:46:33 +0100 Subject: [PATCH 016/140] Update composer dependencies and fix an error found by new PHPStan update --- composer.json | 2 +- composer.lock | 63 +++++++++++++++++++--------------- src/thread/ThreadCrashInfo.php | 2 +- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/composer.json b/composer.json index 7545806b4..8900eea51 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.11", + "phpstan/phpstan": "2.1.16", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index d45311018..94eebda8c 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": "4dfc7b8c912d8d5fa194ddc0e97903fb", + "content-hash": "e3fffa76c2ce9dd0f5c2cd66a5aa097c", "packages": [ { "name": "adhocore/json-comment", @@ -976,7 +976,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1035,7 +1035,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -1055,19 +1055,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -1115,7 +1116,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -1131,22 +1132,22 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" } ], "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.13.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "shasum": "" }, "require": { @@ -1185,7 +1186,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" }, "funding": [ { @@ -1193,7 +1194,7 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { "name": "nikic/php-parser", @@ -1373,16 +1374,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.11", + "version": "2.1.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30" + "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30", - "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", + "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", "shasum": "" }, "require": { @@ -1427,7 +1428,7 @@ "type": "github" } ], - "time": "2025-03-24T13:45:00+00:00" + "time": "2025-05-16T09:40:10+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -1853,16 +1854,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.45", + "version": "10.5.46", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" + "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", + "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", "shasum": "" }, "require": { @@ -1872,7 +1873,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -1934,7 +1935,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46" }, "funding": [ { @@ -1945,12 +1946,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2025-02-06T16:08:12+00:00" + "time": "2025-05-02T06:46:24+00:00" }, { "name": "sebastian/cli-parser", diff --git a/src/thread/ThreadCrashInfo.php b/src/thread/ThreadCrashInfo.php index 66aae927a..6fffdc83b 100644 --- a/src/thread/ThreadCrashInfo.php +++ b/src/thread/ThreadCrashInfo.php @@ -84,6 +84,6 @@ final class ThreadCrashInfo extends ThreadSafe{ public function getThreadName() : string{ return $this->threadName; } public function makePrettyMessage() : string{ - return sprintf("%s: \"%s\" in \"%s\" on line %d", $this->type ?? "Fatal error", $this->message, Filesystem::cleanPath($this->file), $this->line); + return sprintf("%s: \"%s\" in \"%s\" on line %d", $this->type, $this->message, Filesystem::cleanPath($this->file), $this->line); } } From dca37d5842a6405004aff5eeb3fca264f2b260df Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 17 May 2025 14:11:57 +0100 Subject: [PATCH 017/140] Hack: forcibly remove symfony/polyfill-mbstring we don't need this dependency anyway because mbstring is already provided. --- composer.json | 3 ++ composer.lock | 83 +-------------------------------------------------- 2 files changed, 4 insertions(+), 82 deletions(-) diff --git a/composer.json b/composer.json index 8900eea51..f24ddc7e5 100644 --- a/composer.json +++ b/composer.json @@ -57,6 +57,9 @@ "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" }, + "provide": { + "symfony/polyfill-mbstring": "*" + }, "autoload": { "psr-4": { "pocketmine\\": "src/" diff --git a/composer.lock b/composer.lock index 94eebda8c..2350246ed 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": "e3fffa76c2ce9dd0f5c2cd66a5aa097c", + "content-hash": "c2f2a1e28028894c1b12484f115732f0", "packages": [ { "name": "adhocore/json-comment", @@ -1052,87 +1052,6 @@ } ], "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" } ], "packages-dev": [ From e0864e7ee82d4295e2b1d80d0d057016b5e94956 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 17 May 2025 14:54:26 +0100 Subject: [PATCH 018/140] composer: also axe unnecessary ctype polyfill --- composer.json | 1 + composer.lock | 81 +-------------------------------------------------- 2 files changed, 2 insertions(+), 80 deletions(-) diff --git a/composer.json b/composer.json index f24ddc7e5..1482aa4cb 100644 --- a/composer.json +++ b/composer.json @@ -58,6 +58,7 @@ "phpunit/phpunit": "^10.5.24" }, "provide": { + "symfony/polyfill-ctype": "*", "symfony/polyfill-mbstring": "*" }, "autoload": { diff --git a/composer.lock b/composer.lock index 2350246ed..1326fbc9a 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": "c2f2a1e28028894c1b12484f115732f0", + "content-hash": "b25d87be51beaaad7285a6b2e771ab4e", "packages": [ { "name": "adhocore/json-comment", @@ -973,85 +973,6 @@ } ], "time": "2024-10-25T15:07:50+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.32.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" } ], "packages-dev": [ From abb004fbc5d03ff58a5f68f069b70d7e682fc70a Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 17 May 2025 15:00:53 +0100 Subject: [PATCH 019/140] Ready 5.28.1 (#6696) --- changelogs/5.28.md | 6 ++++++ src/VersionInfo.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelogs/5.28.md b/changelogs/5.28.md index a2ede942f..74906ecc7 100644 --- a/changelogs/5.28.md +++ b/changelogs/5.28.md @@ -19,3 +19,9 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if ## Internals - Improved PHPStan error reporting for unsafe foreaches. Foreach on an array with implicit keys now generates different errors than foreach on an array with string keys. + +# 5.28.1 +Released 17th May 2025. + +## Fixes +- Fixed errors when PlayStation players attempt to join due to null `TitleID`. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index acc7db91c..7344085cf 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.28.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 280911ec59103128b60e40a94c388fbd5907ea59 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Sat, 17 May 2025 14:01:49 +0000 Subject: [PATCH 020/140] 5.28.2 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/15085916916 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 7344085cf..885099701 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.28.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.28.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From a37353c0605d6b6424ffa66dd150c2b691c6a651 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 17 May 2025 16:37:05 +0100 Subject: [PATCH 021/140] composer: fixed borked version constraints bruhhhhhhhhhhhh --- composer.json | 4 ++-- composer.lock | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 1482aa4cb..c744c320e 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", - "pocketmine/bedrock-data": "5.0.0+bedrock-1.21.80", + "pocketmine/bedrock-data": "~5.0.0+bedrock-1.21.80", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "38.0.0+bedrock-1.21.80", + "pocketmine/bedrock-protocol": "~38.0.0+bedrock-1.21.80", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 1326fbc9a..b82a014da 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": "b25d87be51beaaad7285a6b2e771ab4e", + "content-hash": "d8fa42f33a3bcb26014e6f862366dbd6", "packages": [ { "name": "adhocore/json-comment", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "38.0.0+bedrock-1.21.80", + "version": "38.0.1+bedrock-1.21.80", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "a626561eaefeb6333c0d2726e2789ceb0aac0724" + "reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a626561eaefeb6333c0d2726e2789ceb0aac0724", - "reference": "a626561eaefeb6333c0d2726e2789ceb0aac0724", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f", + "reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/38.0.0+bedrock-1.21.80" + "source": "https://github.com/pmmp/BedrockProtocol/tree/38.0.1+bedrock-1.21.80" }, - "time": "2025-05-09T14:17:07+00:00" + "time": "2025-05-17T11:56:33+00:00" }, { "name": "pocketmine/binaryutils", From 81d3017ad5e15e8f6ca846733826b47a5e90eba2 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 17 May 2025 16:44:19 +0100 Subject: [PATCH 022/140] Murphy's Law (#6698) --- changelogs/5.28.md | 7 +++++++ src/VersionInfo.php | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changelogs/5.28.md b/changelogs/5.28.md index 74906ecc7..f378031f7 100644 --- a/changelogs/5.28.md +++ b/changelogs/5.28.md @@ -25,3 +25,10 @@ Released 17th May 2025. ## Fixes - Fixed errors when PlayStation players attempt to join due to null `TitleID`. + +# 5.28.2 +Released 17th May 2025. + +## Fixes +- Fixed version constraints which were incorrectly updated during the 1.21.80 update. This led to an unnoticed failure to update BedrockProtocol in the previous patch release. +- Actually fixed PlayStation issues this time diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 885099701..aa42e2e03 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.28.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 647c2587a8c40e641cebca331d06105e92edee8c Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Sat, 17 May 2025 15:45:22 +0000 Subject: [PATCH 023/140] 5.28.3 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/15086729525 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index aa42e2e03..615024656 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.28.2"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.28.3"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 657e6c8130154629c21e4ed9c148386ca6d3af89 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 17 May 2025 17:24:53 +0100 Subject: [PATCH 024/140] Added trigger cron workflow for RestrictedActions branch sync we're having problems with the restricted action getting disabled due to repo inactivity, so it's best we trigger it from here, since this repo's activity is what it's interested in anyway. --- .../workflows/branch-sync-cron-trigger.yml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/branch-sync-cron-trigger.yml diff --git a/.github/workflows/branch-sync-cron-trigger.yml b/.github/workflows/branch-sync-cron-trigger.yml new file mode 100644 index 000000000..145fcd222 --- /dev/null +++ b/.github/workflows/branch-sync-cron-trigger.yml @@ -0,0 +1,32 @@ +#Since GitHub automatically disables cron actions after 60 days of repo inactivity, we need the active repo (PM) +#to trigger the branch merge workflow explicitly. This avoids the need for TOS-violating actions which we previously +#used to keep the restricted action active, as the workflow depends on the activity of this repo anyway. + +name: Trigger branch sync + +on: + schedule: + - cron: "0 0 * * *" #once per day so we don't spam merge commits on busy days + workflow_dispatch: #for testing + +jobs: + trigger: + name: Trigger branch sync RestrictedActions workflow + runs-on: ubuntu-22.04 + + steps: + - name: Generate access token + id: generate-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }} + private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }} + owner: ${{ github.repository_owner }} + repositories: RestrictedActions + + - name: Dispatch branch sync restricted action + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ steps.generate-token.outputs.token }} + repository: ${{ github.repository_owner }}/RestrictedActions + event-type: pocketmine_mp_branch_sync From b5f236c019f360f0ac57aa4355810354f9ae80ac Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 17 May 2025 18:09:14 +0100 Subject: [PATCH 025/140] Apparently we're supposed to use replace for this, not provide --- composer.json | 2 +- composer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index c744c320e..979973893 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" }, - "provide": { + "replace": { "symfony/polyfill-ctype": "*", "symfony/polyfill-mbstring": "*" }, diff --git a/composer.lock b/composer.lock index b82a014da..9cb0721fc 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": "d8fa42f33a3bcb26014e6f862366dbd6", + "content-hash": "ceb98091ac3f61f1a4b87708c48dc75a", "packages": [ { "name": "adhocore/json-comment", From 94fb5d95b92604840dabb719f04327efa559cf94 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 17 May 2025 19:09:54 +0100 Subject: [PATCH 026/140] CommonThreadPartsTrait: fixed thread crashes sometimes missing cause info closes #6669 this happens because isTerminated returns true before the thread's shutdown handler runs, so we join with the thread to make sure that shutdown handlers are done before returning. ... hopefully we don't get servers randomly deadlocking in shutdown handlers ??? --- src/thread/CommonThreadPartsTrait.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index e1c9d7c6b..de606a7b2 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -94,7 +94,17 @@ trait CommonThreadPartsTrait{ } } - public function getCrashInfo() : ?ThreadCrashInfo{ return $this->crashInfo; } + public function getCrashInfo() : ?ThreadCrashInfo{ + //TODO: Joining a crashed worker might be a bit sus, but we need to make sure the thread's shutdown + //handler has run before we try to collect the crash info. As of 6.1.1, pmmpthread sets isTerminated=true + //*before* the shutdown handler is invoked, so we might land here before the crash info has been set. + //In the future this should probably be fixed by running the shutdown handlers before setting isTerminated, + //but this workaround should be good enough for now. + if($this->isTerminated() && !$this->isJoined()){ + $this->join(); + } + return $this->crashInfo; + } public function start(int $options = NativeThread::INHERIT_NONE) : bool{ ThreadManager::getInstance()->add($this); From 9606c0e0bbe054061714e48503d993a9aa8ca7b5 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 23 May 2025 22:16:57 +0100 Subject: [PATCH 027/140] Remove stale labels as well as Waiting on Author labels actions/stale is far too slow to do this itself since it processes lots of irrelevant crap on every run --- .github/workflows/pr-remove-waiting-label.yml | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/.github/workflows/pr-remove-waiting-label.yml b/.github/workflows/pr-remove-waiting-label.yml index eb46043bd..da14e36ba 100644 --- a/.github/workflows/pr-remove-waiting-label.yml +++ b/.github/workflows/pr-remove-waiting-label.yml @@ -15,19 +15,23 @@ jobs: with: github-token: ${{ github.token }} script: | - const [owner, repo] = context.payload.repository.full_name.split('/'); - try { - await github.rest.issues.removeLabel({ - owner: owner, - repo: repo, - issue_number: context.payload.number, - name: "Status: Waiting on Author", - }); - } catch (error) { - if (error.status === 404) { - //probably label wasn't set on the issue - console.log('Failed to remove label (probably label isn\'t on the PR): ' + error.message); - } else { - throw error; + function removeLabel(owner, repo, issue_number, name) { + try { + await github.rest.issues.removeLabel({ + owner: owner, + repo: repo, + issue_number: issue_number, + name: name, + }); + } catch (error) { + if (error.status === 404) { + //probably label wasn't set on the issue + console.log('Failed to remove label ' + name + ' (probably label isn\'t on the PR): ' + error.message); + } else { + throw error; + } } } + const [owner, repo] = context.payload.repository.full_name.split('/'); + removeLabel(owner, repo, context.payload.number, "Status: Waiting on Author"); + removeLabel(owner, repo, context.payload.number, "Stale"); From 3636173d75d7b97414c86d7c6f32bade005185e9 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 23 May 2025 23:28:15 +0100 Subject: [PATCH 028/140] ... --- .github/workflows/pr-remove-waiting-label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-remove-waiting-label.yml b/.github/workflows/pr-remove-waiting-label.yml index da14e36ba..b7cd85acd 100644 --- a/.github/workflows/pr-remove-waiting-label.yml +++ b/.github/workflows/pr-remove-waiting-label.yml @@ -15,7 +15,7 @@ jobs: with: github-token: ${{ github.token }} script: | - function removeLabel(owner, repo, issue_number, name) { + async function removeLabel(owner, repo, issue_number, name) { try { await github.rest.issues.removeLabel({ owner: owner, From e1af2a4af11abf148eb38c4b5d5e05c257fb124b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 24 May 2025 16:19:48 +0100 Subject: [PATCH 029/140] Update language dependency --- composer.json | 2 +- composer.lock | 14 +++++++------- src/lang/KnownTranslationFactory.php | 8 ++++++++ src/lang/KnownTranslationKeys.php | 2 ++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 979973893..a3b2fb6bd 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.7.0", - "pocketmine/locale-data": "~2.24.0", + "pocketmine/locale-data": "~2.25.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.1.0", diff --git a/composer.lock b/composer.lock index 9cb0721fc..ef41f04cd 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": "ceb98091ac3f61f1a4b87708c48dc75a", + "content-hash": "b106b34fbd6c8abdfd45931bcb18bb69", "packages": [ { "name": "adhocore/json-comment", @@ -471,16 +471,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.24.2", + "version": "2.25.1", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "2a00c44c52bce98e7a43aa31517df78cbb2ba23b" + "reference": "8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/2a00c44c52bce98e7a43aa31517df78cbb2ba23b", - "reference": "2a00c44c52bce98e7a43aa31517df78cbb2ba23b", + "url": "https://api.github.com/repos/pmmp/Language/zipball/8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd", + "reference": "8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd", "shasum": "" }, "type": "library", @@ -488,9 +488,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.24.2" + "source": "https://github.com/pmmp/Language/tree/2.25.1" }, - "time": "2025-04-03T01:23:27+00:00" + "time": "2025-04-16T11:15:32+00:00" }, { "name": "pocketmine/log", diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index 4e42419ea..eadd74f32 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -3035,6 +3035,14 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::TILE_BED_TOOFAR, []); } + public static function tile_respawn_anchor_notValid() : Translatable{ + return new Translatable(KnownTranslationKeys::TILE_RESPAWN_ANCHOR_NOTVALID, []); + } + + public static function tile_respawn_anchor_respawnSet() : Translatable{ + return new Translatable(KnownTranslationKeys::TILE_RESPAWN_ANCHOR_RESPAWNSET, []); + } + public static function view_distance() : Translatable{ return new Translatable(KnownTranslationKeys::VIEW_DISTANCE, []); } diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index 6fbb32ecb..44a64c489 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -658,6 +658,8 @@ final class KnownTranslationKeys{ public const TILE_BED_NOSLEEP = "tile.bed.noSleep"; public const TILE_BED_OCCUPIED = "tile.bed.occupied"; public const TILE_BED_TOOFAR = "tile.bed.tooFar"; + public const TILE_RESPAWN_ANCHOR_NOTVALID = "tile.respawn_anchor.notValid"; + public const TILE_RESPAWN_ANCHOR_RESPAWNSET = "tile.respawn_anchor.respawnSet"; public const VIEW_DISTANCE = "view_distance"; public const WELCOME_TO_POCKETMINE = "welcome_to_pocketmine"; public const WHITELIST_ENABLE = "whitelist_enable"; From 4d5c27a7343fe78bfff901f2b911fc877581862e Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Sat, 24 May 2025 23:01:36 +0300 Subject: [PATCH 030/140] Unit test block hardness & blast resistance values (#6629) --- src/block/BlockBreakInfo.php | 2 +- src/block/VanillaBlocks.php | 171 +++++++++++++++--------------- tests/phpunit/block/BlockTest.php | 55 ++++++++++ 3 files changed, 144 insertions(+), 84 deletions(-) diff --git a/src/block/BlockBreakInfo.php b/src/block/BlockBreakInfo.php index e77e06cfd..3d45caf3c 100644 --- a/src/block/BlockBreakInfo.php +++ b/src/block/BlockBreakInfo.php @@ -73,7 +73,7 @@ class BlockBreakInfo{ return new self(0.0, $toolType, $toolHarvestLevel, 0.0); } - public static function indestructible(float $blastResistance = 18000000.0) : self{ + public static function indestructible(float $blastResistance = 18000003.75) : self{ return new self(-1.0, BlockToolType::NONE, 0, $blastResistance); } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 231004dfa..0a6d4b31c 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -859,7 +859,7 @@ final class VanillaBlocks{ $railBreakInfo = new Info(new BreakInfo(0.7)); self::register("activator_rail", fn(BID $id) => new ActivatorRail($id, "Activator Rail", $railBreakInfo)); self::register("anvil", fn(BID $id) => new Anvil($id, "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); - self::register("bamboo", fn(BID $id) => new Bamboo($id, "Bamboo", new Info(new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{ + self::register("bamboo", fn(BID $id) => new Bamboo($id, "Bamboo", new Info(new class(1.0, ToolType::AXE) extends BreakInfo{ public function getBreakTime(Item $item) : float{ if($item->getBlockToolType() === ToolType::SWORD){ return 0.0; @@ -867,7 +867,7 @@ final class VanillaBlocks{ return parent::getBreakTime($item); } }, [Tags::POTTABLE_PLANTS]))); - self::register("bamboo_sapling", fn(BID $id) => new BambooSapling($id, "Bamboo Sapling", new Info(BreakInfo::instant()))); + self::register("bamboo_sapling", fn(BID $id) => new BambooSapling($id, "Bamboo Sapling", new Info(new BreakInfo(1.0)))); $bannerBreakInfo = new Info(BreakInfo::axe(1.0)); self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class); @@ -876,7 +876,7 @@ final class VanillaBlocks{ self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible()))); self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class); self::register("bed", fn(BID $id) => new Bed($id, "Bed Block", new Info(new BreakInfo(0.2))), TileBed::class); - self::register("bedrock", fn(BID $id) => new Bedrock($id, "Bedrock", new Info(BreakInfo::indestructible()))); + self::register("bedrock", fn(BID $id) => new Bedrock($id, "Bedrock", new Info(BreakInfo::indestructible(18000000.0)))); self::register("beetroots", fn(BID $id) => new Beetroot($id, "Beetroot Block", new Info(BreakInfo::instant()))); self::register("bell", fn(BID $id) => new Bell($id, "Bell", new Info(BreakInfo::pickaxe(5.0))), TileBell::class); @@ -913,7 +913,7 @@ final class VanillaBlocks{ self::register("cobweb", fn(BID $id) => new Cobweb($id, "Cobweb", new Info(new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)))); self::register("cocoa_pod", fn(BID $id) => new CocoaBlock($id, "Cocoa Block", new Info(BreakInfo::axe(0.2, null, 15.0)))); - self::register("coral_block", fn(BID $id) => new CoralBlock($id, "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD)))); + self::register("coral_block", fn(BID $id) => new CoralBlock($id, "Coral Block", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)))); self::register("daylight_sensor", fn(BID $id) => new DaylightSensor($id, "Daylight Sensor", new Info(BreakInfo::axe(0.2))), TileDaylightSensor::class); self::register("dead_bush", fn(BID $id) => new DeadBush($id, "Dead Bush", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); self::register("detector_rail", fn(BID $id) => new DetectorRail($id, "Detector Rail", $railBreakInfo)); @@ -930,15 +930,15 @@ final class VanillaBlocks{ self::register("pitcher_plant", fn(BID $id) => new DoublePlant($id, "Pitcher Plant", new Info(BreakInfo::instant()))); self::register("pitcher_crop", fn(BID $id) => new PitcherCrop($id, "Pitcher Crop", new Info(BreakInfo::instant()))); self::register("double_pitcher_crop", fn(BID $id) => new DoublePitcherCrop($id, "Double Pitcher Crop", new Info(BreakInfo::instant()))); - self::register("dragon_egg", fn(BID $id) => new DragonEgg($id, "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); + self::register("dragon_egg", fn(BID $id) => new DragonEgg($id, "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, blastResistance: 45.0)))); self::register("dried_kelp", fn(BID $id) => new DriedKelp($id, "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5)))); self::register("emerald", fn(BID $id) => new Opaque($id, "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); self::register("enchanting_table", fn(BID $id) => new EnchantingTable($id, "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))), TileEnchantingTable::class); - self::register("end_portal_frame", fn(BID $id) => new EndPortalFrame($id, "End Portal Frame", new Info(BreakInfo::indestructible()))); + self::register("end_portal_frame", fn(BID $id) => new EndPortalFrame($id, "End Portal Frame", new Info(BreakInfo::indestructible(18000000.0)))); self::register("end_rod", fn(BID $id) => new EndRod($id, "End Rod", new Info(BreakInfo::instant()))); self::register("end_stone", fn(BID $id) => new Opaque($id, "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); - $endBrickBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD, 4.0)); + $endBrickBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)); self::register("end_stone_bricks", fn(BID $id) => new Opaque($id, "End Stone Bricks", $endBrickBreakInfo)); self::register("end_stone_brick_stairs", fn(BID $id) => new Stair($id, "End Stone Brick Stairs", $endBrickBreakInfo)); @@ -962,7 +962,7 @@ final class VanillaBlocks{ self::register("torchflower", fn(BID $id) => new Flower($id, "Torchflower", $flowerTypeInfo)); self::register("torchflower_crop", fn(BID $id) => new TorchflowerCrop($id, "Torchflower Crop", new Info(BreakInfo::instant()))); self::register("flower_pot", fn(BID $id) => new FlowerPot($id, "Flower Pot", new Info(BreakInfo::instant())), TileFlowerPot::class); - self::register("frosted_ice", fn(BID $id) => new FrostedIce($id, "Frosted Ice", new Info(BreakInfo::pickaxe(2.5)))); + self::register("frosted_ice", fn(BID $id) => new FrostedIce($id, "Frosted Ice", new Info(BreakInfo::pickaxe(0.5)))); self::register("furnace", fn(BID $id) => new Furnace($id, "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE), TileNormalFurnace::class); self::register("blast_furnace", fn(BID $id) => new Furnace($id, "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::BLAST_FURNACE), TileBlastFurnace::class); self::register("smoker", fn(BID $id) => new Furnace($id, "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::SMOKER), TileSmoker::class); @@ -970,30 +970,28 @@ final class VanillaBlocks{ $glassBreakInfo = new Info(new BreakInfo(0.3)); self::register("glass", fn(BID $id) => new Glass($id, "Glass", $glassBreakInfo)); self::register("glass_pane", fn(BID $id) => new GlassPane($id, "Glass Pane", $glassBreakInfo)); - self::register("glowing_obsidian", fn(BID $id) => new GlowingObsidian($id, "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND, 50.0)))); + self::register("glowing_obsidian", fn(BID $id) => new GlowingObsidian($id, "Glowing Obsidian", new Info(BreakInfo::pickaxe(35.0, ToolTier::DIAMOND, 6000.0)))); self::register("glowstone", fn(BID $id) => new Glowstone($id, "Glowstone", new Info(BreakInfo::pickaxe(0.3)))); - self::register("glow_lichen", fn(BID $id) => new GlowLichen($id, "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2)))); + self::register("glow_lichen", fn(BID $id) => new GlowLichen($id, "Glow Lichen", new Info(BreakInfo::axe(0.2)))); self::register("gold", fn(BID $id) => new Opaque($id, "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON, 30.0)))); - $grassBreakInfo = BreakInfo::shovel(0.6); - self::register("grass", fn(BID $id) => new Grass($id, "Grass", new Info($grassBreakInfo, [Tags::DIRT]))); - self::register("grass_path", fn(BID $id) => new GrassPath($id, "Grass Path", new Info($grassBreakInfo))); + self::register("grass", fn(BID $id) => new Grass($id, "Grass", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); + self::register("grass_path", fn(BID $id) => new GrassPath($id, "Grass Path", new Info(BreakInfo::shovel(0.65)))); self::register("gravel", fn(BID $id) => new Gravel($id, "Gravel", new Info(BreakInfo::shovel(0.6)))); - $hardenedClayBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)); - self::register("hardened_clay", fn(BID $id) => new HardenedClay($id, "Hardened Clay", $hardenedClayBreakInfo)); + self::register("hardened_clay", fn(BID $id) => new HardenedClay($id, "Hardened Clay", new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)))); $hardenedGlassBreakInfo = new Info(new BreakInfo(10.0)); self::register("hardened_glass", fn(BID $id) => new HardenedGlass($id, "Hardened Glass", $hardenedGlassBreakInfo)); self::register("hardened_glass_pane", fn(BID $id) => new HardenedGlassPane($id, "Hardened Glass Pane", $hardenedGlassBreakInfo)); self::register("hay_bale", fn(BID $id) => new HayBale($id, "Hay Bale", new Info(new BreakInfo(0.5)))); - self::register("hopper", fn(BID $id) => new Hopper($id, "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 15.0))), TileHopper::class); + self::register("hopper", fn(BID $id) => new Hopper($id, "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 24.0))), TileHopper::class); self::register("ice", fn(BID $id) => new Ice($id, "Ice", new Info(BreakInfo::pickaxe(0.5)))); $updateBlockBreakInfo = new Info(new BreakInfo(1.0)); self::register("info_update", fn(BID $id) => new Opaque($id, "update!", $updateBlockBreakInfo)); self::register("info_update2", fn(BID $id) => new Opaque($id, "ate!upd", $updateBlockBreakInfo)); - self::register("invisible_bedrock", fn(BID $id) => new Transparent($id, "Invisible Bedrock", new Info(BreakInfo::indestructible()))); + self::register("invisible_bedrock", fn(BID $id) => new Transparent($id, "Invisible Bedrock", new Info(BreakInfo::indestructible(18000000.0)))); $ironBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::STONE, 30.0)); self::register("iron", fn(BID $id) => new Opaque($id, "Iron Block", $ironBreakInfo)); @@ -1006,16 +1004,16 @@ final class VanillaBlocks{ self::register("item_frame", fn(BID $id) => new ItemFrame($id, "Item Frame", $itemFrameInfo), TileItemFrame::class); self::register("glowing_item_frame", fn(BID $id) => new ItemFrame($id, "Glow Item Frame", $itemFrameInfo), TileGlowingItemFrame::class); - self::register("jukebox", fn(BID $id) => new Jukebox($id, "Jukebox", new Info(BreakInfo::axe(0.8))), TileJukebox::class); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not + self::register("jukebox", fn(BID $id) => new Jukebox($id, "Jukebox", new Info(BreakInfo::axe(2.0, blastResistance: 30.0))), TileJukebox::class); self::register("ladder", fn(BID $id) => new Ladder($id, "Ladder", new Info(BreakInfo::axe(0.4)))); - $lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0)); + $lanternBreakInfo = new Info(BreakInfo::pickaxe(3.5)); self::register("lantern", fn(BID $id) => new Lantern($id, "Lantern", $lanternBreakInfo, 15)); self::register("soul_lantern", fn(BID $id) => new Lantern($id, "Soul Lantern", $lanternBreakInfo, 10)); self::register("lapis_lazuli", fn(BID $id) => new Opaque($id, "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE)))); self::register("lava", fn(BID $id) => new Lava($id, "Lava", new Info(BreakInfo::indestructible(500.0)))); - self::register("lectern", fn(BID $id) => new Lectern($id, "Lectern", new Info(BreakInfo::axe(2.0))), TileLectern::class); + self::register("lectern", fn(BID $id) => new Lectern($id, "Lectern", new Info(BreakInfo::axe(2.5))), TileLectern::class); self::register("lever", fn(BID $id) => new Lever($id, "Lever", new Info(new BreakInfo(0.5)))); self::register("magma", fn(BID $id) => new Magma($id, "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); self::register("melon", fn(BID $id) => new Melon($id, "Melon Block", new Info(BreakInfo::axe(1.0)))); @@ -1065,14 +1063,15 @@ final class VanillaBlocks{ self::register("purpur_stairs", fn(BID $id) => new Stair($id, "Purpur Stairs", $purpurBreakInfo)); $quartzBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); + $smoothQuartzBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); self::register("quartz", fn(BID $id) => new Opaque($id, "Quartz Block", $quartzBreakInfo)); self::register("chiseled_quartz", fn(BID $id) => new SimplePillar($id, "Chiseled Quartz Block", $quartzBreakInfo)); self::register("quartz_pillar", fn(BID $id) => new SimplePillar($id, "Quartz Pillar", $quartzBreakInfo)); - self::register("smooth_quartz", fn(BID $id) => new Opaque($id, "Smooth Quartz Block", $quartzBreakInfo)); + self::register("smooth_quartz", fn(BID $id) => new Opaque($id, "Smooth Quartz Block", $smoothQuartzBreakInfo)); self::register("quartz_bricks", fn(BID $id) => new Opaque($id, "Quartz Bricks", $quartzBreakInfo)); self::register("quartz_stairs", fn(BID $id) => new Stair($id, "Quartz Stairs", $quartzBreakInfo)); - self::register("smooth_quartz_stairs", fn(BID $id) => new Stair($id, "Smooth Quartz Stairs", $quartzBreakInfo)); + self::register("smooth_quartz_stairs", fn(BID $id) => new Stair($id, "Smooth Quartz Stairs", $smoothQuartzBreakInfo)); self::register("rail", fn(BID $id) => new Rail($id, "Rail", $railBreakInfo)); self::register("red_mushroom", fn(BID $id) => new RedMushroom($id, "Red Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); @@ -1127,13 +1126,13 @@ final class VanillaBlocks{ $infestedStoneBreakInfo = new Info(BreakInfo::pickaxe(0.75)); self::register("infested_stone", fn(BID $id) => new InfestedStone($id, "Infested Stone", $infestedStoneBreakInfo, $stone)); self::register("infested_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick)); - self::register("infested_cobblestone", fn(BID $id) => new InfestedStone($id, "Infested Cobblestone", $infestedStoneBreakInfo, $cobblestone)); + self::register("infested_cobblestone", fn(BID $id) => new InfestedStone($id, "Infested Cobblestone", new Info(BreakInfo::pickaxe(1.0, blastResistance: 3.75)), $cobblestone)); self::register("infested_mossy_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Mossy Stone Brick", $infestedStoneBreakInfo, $mossyStoneBrick)); self::register("infested_cracked_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Cracked Stone Brick", $infestedStoneBreakInfo, $crackedStoneBrick)); self::register("infested_chiseled_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Chiseled Stone Brick", $infestedStoneBreakInfo, $chiseledStoneBrick)); self::register("stone_stairs", fn(BID $id) => new Stair($id, "Stone Stairs", $stoneBreakInfo)); - self::register("smooth_stone", fn(BID $id) => new Opaque($id, "Smooth Stone", $stoneBreakInfo)); + self::register("smooth_stone", fn(BID $id) => new Opaque($id, "Smooth Stone", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)))); self::register("andesite_stairs", fn(BID $id) => new Stair($id, "Andesite Stairs", $stoneBreakInfo)); self::register("diorite_stairs", fn(BID $id) => new Stair($id, "Diorite Stairs", $stoneBreakInfo)); self::register("granite_stairs", fn(BID $id) => new Stair($id, "Granite Stairs", $stoneBreakInfo)); @@ -1146,7 +1145,6 @@ final class VanillaBlocks{ self::register("stonecutter", fn(BID $id) => new Stonecutter($id, "Stonecutter", new Info(BreakInfo::pickaxe(3.5)))); self::register("stone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5)))); - //TODO: in the future this won't be the same for all the types $stoneSlabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); self::register("brick_slab", fn(BID $id) => new Slab($id, "Brick", $stoneSlabBreakInfo)); @@ -1157,28 +1155,31 @@ final class VanillaBlocks{ self::register("sandstone_slab", fn(BID $id) => new Slab($id, "Sandstone", $stoneSlabBreakInfo)); self::register("smooth_stone_slab", fn(BID $id) => new Slab($id, "Smooth Stone", $stoneSlabBreakInfo)); self::register("stone_brick_slab", fn(BID $id) => new Slab($id, "Stone Brick", $stoneSlabBreakInfo)); - self::register("dark_prismarine_slab", fn(BID $id) => new Slab($id, "Dark Prismarine", $stoneSlabBreakInfo)); - self::register("mossy_cobblestone_slab", fn(BID $id) => new Slab($id, "Mossy Cobblestone", $stoneSlabBreakInfo)); - self::register("prismarine_slab", fn(BID $id) => new Slab($id, "Prismarine", $stoneSlabBreakInfo)); - self::register("prismarine_bricks_slab", fn(BID $id) => new Slab($id, "Prismarine Bricks", $stoneSlabBreakInfo)); - self::register("purpur_slab", fn(BID $id) => new Slab($id, "Purpur", $stoneSlabBreakInfo)); self::register("red_nether_brick_slab", fn(BID $id) => new Slab($id, "Red Nether Brick", $stoneSlabBreakInfo)); self::register("red_sandstone_slab", fn(BID $id) => new Slab($id, "Red Sandstone", $stoneSlabBreakInfo)); self::register("smooth_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Sandstone", $stoneSlabBreakInfo)); - self::register("andesite_slab", fn(BID $id) => new Slab($id, "Andesite", $stoneSlabBreakInfo)); - self::register("diorite_slab", fn(BID $id) => new Slab($id, "Diorite", $stoneSlabBreakInfo)); - self::register("end_stone_brick_slab", fn(BID $id) => new Slab($id, "End Stone Brick", $stoneSlabBreakInfo)); - self::register("granite_slab", fn(BID $id) => new Slab($id, "Granite", $stoneSlabBreakInfo)); - self::register("polished_andesite_slab", fn(BID $id) => new Slab($id, "Polished Andesite", $stoneSlabBreakInfo)); - self::register("polished_diorite_slab", fn(BID $id) => new Slab($id, "Polished Diorite", $stoneSlabBreakInfo)); - self::register("polished_granite_slab", fn(BID $id) => new Slab($id, "Polished Granite", $stoneSlabBreakInfo)); - self::register("smooth_red_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Red Sandstone", $stoneSlabBreakInfo)); self::register("cut_red_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Red Sandstone", $stoneSlabBreakInfo)); self::register("cut_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Sandstone", $stoneSlabBreakInfo)); - self::register("mossy_stone_brick_slab", fn(BID $id) => new Slab($id, "Mossy Stone Brick", $stoneSlabBreakInfo)); + self::register("mossy_cobblestone_slab", fn(BID $id) => new Slab($id, "Mossy Cobblestone", $stoneSlabBreakInfo)); + self::register("purpur_slab", fn(BID $id) => new Slab($id, "Purpur", $stoneSlabBreakInfo)); + self::register("smooth_red_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Red Sandstone", $stoneSlabBreakInfo)); self::register("smooth_quartz_slab", fn(BID $id) => new Slab($id, "Smooth Quartz", $stoneSlabBreakInfo)); self::register("stone_slab", fn(BID $id) => new Slab($id, "Stone", $stoneSlabBreakInfo)); + self::register("end_stone_brick_slab", fn(BID $id) => new Slab($id, "End Stone Brick", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 30.0)))); + + $lightStoneSlabBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); + self::register("dark_prismarine_slab", fn(BID $id) => new Slab($id, "Dark Prismarine", $lightStoneSlabBreakInfo)); + self::register("prismarine_slab", fn(BID $id) => new Slab($id, "Prismarine", $lightStoneSlabBreakInfo)); + self::register("prismarine_bricks_slab", fn(BID $id) => new Slab($id, "Prismarine Bricks", $lightStoneSlabBreakInfo)); + self::register("andesite_slab", fn(BID $id) => new Slab($id, "Andesite", $lightStoneSlabBreakInfo)); + self::register("diorite_slab", fn(BID $id) => new Slab($id, "Diorite", $lightStoneSlabBreakInfo)); + self::register("granite_slab", fn(BID $id) => new Slab($id, "Granite", $lightStoneSlabBreakInfo)); + self::register("polished_andesite_slab", fn(BID $id) => new Slab($id, "Polished Andesite", $lightStoneSlabBreakInfo)); + self::register("polished_diorite_slab", fn(BID $id) => new Slab($id, "Polished Diorite", $lightStoneSlabBreakInfo)); + self::register("polished_granite_slab", fn(BID $id) => new Slab($id, "Polished Granite", $lightStoneSlabBreakInfo)); + self::register("mossy_stone_brick_slab", fn(BID $id) => new Slab($id, "Mossy Stone Brick", $lightStoneSlabBreakInfo)); + self::register("legacy_stonecutter", fn(BID $id) => new Opaque($id, "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)))); self::register("sugarcane", fn(BID $id) => new Sugarcane($id, "Sugarcane", new Info(BreakInfo::instant()))); self::register("sweet_berry_bush", fn(BID $id) => new SweetBerryBush($id, "Sweet Berry Bush", new Info(BreakInfo::instant()))); @@ -1237,25 +1238,26 @@ final class VanillaBlocks{ } $sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); + $smoothSandstoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); self::register("red_sandstone_stairs", fn(BID $id) => new Stair($id, "Red Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Red Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Red Sandstone Stairs", $smoothSandstoneBreakInfo)); self::register("red_sandstone", fn(BID $id) => new Opaque($id, "Red Sandstone", $sandstoneBreakInfo)); self::register("chiseled_red_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Red Sandstone", $sandstoneBreakInfo)); self::register("cut_red_sandstone", fn(BID $id) => new Opaque($id, "Cut Red Sandstone", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone", fn(BID $id) => new Opaque($id, "Smooth Red Sandstone", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone", fn(BID $id) => new Opaque($id, "Smooth Red Sandstone", $smoothSandstoneBreakInfo)); self::register("sandstone_stairs", fn(BID $id) => new Stair($id, "Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Sandstone Stairs", $smoothSandstoneBreakInfo)); self::register("sandstone", fn(BID $id) => new Opaque($id, "Sandstone", $sandstoneBreakInfo)); self::register("chiseled_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Sandstone", $sandstoneBreakInfo)); self::register("cut_sandstone", fn(BID $id) => new Opaque($id, "Cut Sandstone", $sandstoneBreakInfo)); - self::register("smooth_sandstone", fn(BID $id) => new Opaque($id, "Smooth Sandstone", $sandstoneBreakInfo)); + self::register("smooth_sandstone", fn(BID $id) => new Opaque($id, "Smooth Sandstone", $smoothSandstoneBreakInfo)); self::register("glazed_terracotta", fn(BID $id) => new GlazedTerracotta($id, "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD)))); self::register("dyed_shulker_box", fn(BID $id) => new DyedShulkerBox($id, "Dyed Shulker Box", $shulkerBoxBreakInfo), TileShulkerBox::class); self::register("stained_glass", fn(BID $id) => new StainedGlass($id, "Stained Glass", $glassBreakInfo)); self::register("stained_glass_pane", fn(BID $id) => new StainedGlassPane($id, "Stained Glass Pane", $glassBreakInfo)); - self::register("stained_clay", fn(BID $id) => new StainedHardenedClay($id, "Stained Clay", $hardenedClayBreakInfo)); + self::register("stained_clay", fn(BID $id) => new StainedHardenedClay($id, "Stained Clay", new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 6.25)))); self::register("stained_hardened_glass", fn(BID $id) => new StainedHardenedGlass($id, "Stained Hardened Glass", $hardenedGlassBreakInfo)); self::register("stained_hardened_glass_pane", fn(BID $id) => new StainedHardenedGlassPane($id, "Stained Hardened Glass Pane", $hardenedGlassBreakInfo)); self::register("carpet", fn(BID $id) => new Carpet($id, "Carpet", new Info(new BreakInfo(0.1)))); @@ -1272,22 +1274,26 @@ final class VanillaBlocks{ } }))); - //TODO: in the future these won't all have the same hardness; they only do now because of the old metadata crap - $wallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("cobblestone_wall", fn(BID $id) => new Wall($id, "Cobblestone Wall", $wallBreakInfo)); - self::register("andesite_wall", fn(BID $id) => new Wall($id, "Andesite Wall", $wallBreakInfo)); - self::register("brick_wall", fn(BID $id) => new Wall($id, "Brick Wall", $wallBreakInfo)); - self::register("diorite_wall", fn(BID $id) => new Wall($id, "Diorite Wall", $wallBreakInfo)); - self::register("end_stone_brick_wall", fn(BID $id) => new Wall($id, "End Stone Brick Wall", $wallBreakInfo)); - self::register("granite_wall", fn(BID $id) => new Wall($id, "Granite Wall", $wallBreakInfo)); - self::register("mossy_stone_brick_wall", fn(BID $id) => new Wall($id, "Mossy Stone Brick Wall", $wallBreakInfo)); - self::register("mossy_cobblestone_wall", fn(BID $id) => new Wall($id, "Mossy Cobblestone Wall", $wallBreakInfo)); - self::register("nether_brick_wall", fn(BID $id) => new Wall($id, "Nether Brick Wall", $wallBreakInfo)); - self::register("prismarine_wall", fn(BID $id) => new Wall($id, "Prismarine Wall", $wallBreakInfo)); - self::register("red_nether_brick_wall", fn(BID $id) => new Wall($id, "Red Nether Brick Wall", $wallBreakInfo)); - self::register("red_sandstone_wall", fn(BID $id) => new Wall($id, "Red Sandstone Wall", $wallBreakInfo)); - self::register("sandstone_wall", fn(BID $id) => new Wall($id, "Sandstone Wall", $wallBreakInfo)); - self::register("stone_brick_wall", fn(BID $id) => new Wall($id, "Stone Brick Wall", $wallBreakInfo)); + self::register("end_stone_brick_wall", fn(BID $id) => new Wall($id, "End Stone Brick Wall", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); + + $brickWallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); + self::register("cobblestone_wall", fn(BID $id) => new Wall($id, "Cobblestone Wall", $brickWallBreakInfo)); + self::register("brick_wall", fn(BID $id) => new Wall($id, "Brick Wall", $brickWallBreakInfo)); + self::register("mossy_cobblestone_wall", fn(BID $id) => new Wall($id, "Mossy Cobblestone Wall", $brickWallBreakInfo)); + self::register("nether_brick_wall", fn(BID $id) => new Wall($id, "Nether Brick Wall", $brickWallBreakInfo)); + self::register("red_nether_brick_wall", fn(BID $id) => new Wall($id, "Red Nether Brick Wall", $brickWallBreakInfo)); + + $stoneWallBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); + self::register("stone_brick_wall", fn(BID $id) => new Wall($id, "Stone Brick Wall", $stoneWallBreakInfo)); + self::register("mossy_stone_brick_wall", fn(BID $id) => new Wall($id, "Mossy Stone Brick Wall", $stoneWallBreakInfo)); + self::register("granite_wall", fn(BID $id) => new Wall($id, "Granite Wall", $stoneWallBreakInfo)); + self::register("diorite_wall", fn(BID $id) => new Wall($id, "Diorite Wall", $stoneWallBreakInfo)); + self::register("andesite_wall", fn(BID $id) => new Wall($id, "Andesite Wall", $stoneWallBreakInfo)); + self::register("prismarine_wall", fn(BID $id) => new Wall($id, "Prismarine Wall", $stoneWallBreakInfo)); + + $sandstoneWallBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD, 4.0)); + self::register("red_sandstone_wall", fn(BID $id) => new Wall($id, "Red Sandstone Wall", $sandstoneWallBreakInfo)); + self::register("sandstone_wall", fn(BID $id) => new Wall($id, "Sandstone Wall", $sandstoneWallBreakInfo)); self::registerElements(); @@ -1320,8 +1326,8 @@ final class VanillaBlocks{ self::register("mangrove_roots", fn(BID $id) => new MangroveRoots($id, "Mangrove Roots", new Info(BreakInfo::axe(0.7)))); self::register("muddy_mangrove_roots", fn(BID $id) => new SimplePillar($id, "Muddy Mangrove Roots", new Info(BreakInfo::shovel(0.7), [Tags::MUD]))); self::register("froglight", fn(BID $id) => new Froglight($id, "Froglight", new Info(new BreakInfo(0.3)))); - self::register("sculk", fn(BID $id) => new Sculk($id, "Sculk", new Info(new BreakInfo(0.6, ToolType::HOE)))); - self::register("reinforced_deepslate", fn(BID $id) => new class($id, "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 3600.0))) extends Opaque{ + self::register("sculk", fn(BID $id) => new Sculk($id, "Sculk", new Info(new BreakInfo(0.2, ToolType::HOE)))); + self::register("reinforced_deepslate", fn(BID $id) => new class($id, "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 6000.0))) extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ return []; } @@ -1537,7 +1543,7 @@ final class VanillaBlocks{ self::register("lapis_lazuli_ore", fn(BID $id) => new LapisOre($id, "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE))); self::register("redstone_ore", fn(BID $id) => new RedstoneOre($id, "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON))); - $deepslateOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(4.5, $toolTier)); + $deepslateOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(4.5, $toolTier, 15.0)); self::register("deepslate_coal_ore", fn(BID $id) => new CoalOre($id, "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD))); self::register("deepslate_copper_ore", fn(BID $id) => new CopperOre($id, "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE))); self::register("deepslate_diamond_ore", fn(BID $id) => new DiamondOre($id, "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON))); @@ -1581,10 +1587,10 @@ 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", fn(BID $id) => new class($id, "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))) extends Opaque{ + self::register("ancient_debris", fn(BID $id) => new class($id, "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 6000.0))) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); - $netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 3600.0)); + $netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 6000.0)); self::register("netherite", fn(BID $id) => new class($id, "Netherite Block", $netheriteBreakInfo) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); @@ -1602,14 +1608,14 @@ final class VanillaBlocks{ self::register("gilded_blackstone", fn(BID $id) => new GildedBlackstone($id, "Gilded Blackstone", $blackstoneBreakInfo)); - //TODO: polished blackstone ought to have 2.0 hardness (as per java) but it's 1.5 in Bedrock (probably parity bug) + $polishedBlackstoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); $prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : ""); - self::register("polished_blackstone", fn(BID $id) => new Opaque($id, $prefix(""), $blackstoneBreakInfo)); + self::register("polished_blackstone", fn(BID $id) => new Opaque($id, $prefix(""), $polishedBlackstoneBreakInfo)); self::register("polished_blackstone_button", fn(BID $id) => new StoneButton($id, $prefix("Button"), new Info(BreakInfo::pickaxe(0.5)))); self::register("polished_blackstone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5)), 20)); self::register("polished_blackstone_slab", fn(BID $id) => new Slab($id, $prefix(""), $slabBreakInfo)); - self::register("polished_blackstone_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $blackstoneBreakInfo)); - self::register("polished_blackstone_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $blackstoneBreakInfo)); + self::register("polished_blackstone_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $polishedBlackstoneBreakInfo)); + self::register("polished_blackstone_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $polishedBlackstoneBreakInfo)); self::register("chiseled_polished_blackstone", fn(BID $id) => new Opaque($id, "Chiseled Polished Blackstone", $blackstoneBreakInfo)); $prefix = fn(string $thing) => "Polished Blackstone Brick" . ($thing !== "" ? " $thing" : ""); @@ -1622,8 +1628,7 @@ final class VanillaBlocks{ self::register("soul_torch", fn(BID $id) => new Torch($id, "Soul Torch", new Info(BreakInfo::instant()))); self::register("soul_fire", fn(BID $id) => new SoulFire($id, "Soul Fire", new Info(BreakInfo::instant(), [Tags::FIRE]))); - //TODO: soul soul ought to have 0.5 hardness (as per java) but it's 1.0 in Bedrock (probably parity bug) - self::register("soul_soil", fn(BID $id) => new Opaque($id, "Soul Soil", new Info(BreakInfo::shovel(1.0)))); + self::register("soul_soil", fn(BID $id) => new Opaque($id, "Soul Soil", new Info(BreakInfo::shovel(0.5)))); self::register("shroomlight", fn(BID $id) => new class($id, "Shroomlight", new Info(new BreakInfo(1.0, ToolType::HOE))) extends Opaque{ public function getLightLevel() : int{ return 15; } @@ -1641,7 +1646,7 @@ final class VanillaBlocks{ self::register("crimson_roots", fn(BID $id) => new NetherRoots($id, "Crimson Roots", $netherRootsInfo)); self::register("warped_roots", fn(BID $id) => new NetherRoots($id, "Warped Roots", $netherRootsInfo)); - self::register("chain", fn(BID $id) => new Chain($id, "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); + self::register("chain", fn(BID $id) => new Chain($id, "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); } private static function registerBlocksR17() : void{ @@ -1659,7 +1664,7 @@ final class VanillaBlocks{ self::register("raw_gold", fn(BID $id) => new Opaque($id, "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0)))); self::register("raw_iron", fn(BID $id) => new Opaque($id, "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); - $deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD, 18.0)); + $deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD, 30.0)); self::register("deepslate", fn(BID $id) => new class($id, "Deepslate", $deepslateBreakInfo) extends SimplePillar{ public function getDropsForCompatibleTool(Item $item) : array{ return [VanillaBlocks::COBBLED_DEEPSLATE()->asItem()]; @@ -1671,29 +1676,29 @@ final class VanillaBlocks{ }); //TODO: parity issue here - in Java this has a hardness of 3.0, but in bedrock it's 3.5 - self::register("chiseled_deepslate", fn(BID $id) => new Opaque($id, "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)))); + self::register("chiseled_deepslate", fn(BID $id) => new Opaque($id, "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)))); - $deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("deepslate_bricks", fn(BID $id) => new Opaque($id, "Deepslate Bricks", $deepslateBrickBreakInfo)); self::register("deepslate_brick_slab", fn(BID $id) => new Slab($id, "Deepslate Brick", $deepslateBrickBreakInfo)); self::register("deepslate_brick_stairs", fn(BID $id) => new Stair($id, "Deepslate Brick Stairs", $deepslateBrickBreakInfo)); self::register("deepslate_brick_wall", fn(BID $id) => new Wall($id, "Deepslate Brick Wall", $deepslateBrickBreakInfo)); self::register("cracked_deepslate_bricks", fn(BID $id) => new Opaque($id, "Cracked Deepslate Bricks", $deepslateBrickBreakInfo)); - $deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("deepslate_tiles", fn(BID $id) => new Opaque($id, "Deepslate Tiles", $deepslateTilesBreakInfo)); self::register("deepslate_tile_slab", fn(BID $id) => new Slab($id, "Deepslate Tile", $deepslateTilesBreakInfo)); self::register("deepslate_tile_stairs", fn(BID $id) => new Stair($id, "Deepslate Tile Stairs", $deepslateTilesBreakInfo)); self::register("deepslate_tile_wall", fn(BID $id) => new Wall($id, "Deepslate Tile Wall", $deepslateTilesBreakInfo)); self::register("cracked_deepslate_tiles", fn(BID $id) => new Opaque($id, "Cracked Deepslate Tiles", $deepslateTilesBreakInfo)); - $cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("cobbled_deepslate", fn(BID $id) => new Opaque($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); self::register("cobbled_deepslate_slab", fn(BID $id) => new Slab($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); self::register("cobbled_deepslate_stairs", fn(BID $id) => new Stair($id, "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo)); self::register("cobbled_deepslate_wall", fn(BID $id) => new Wall($id, "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo)); - $polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("polished_deepslate", fn(BID $id) => new Opaque($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); self::register("polished_deepslate_slab", fn(BID $id) => new Slab($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); self::register("polished_deepslate_stairs", fn(BID $id) => new Stair($id, "Polished Deepslate Stairs", $polishedDeepslateBreakInfo)); @@ -1702,7 +1707,7 @@ final class VanillaBlocks{ self::register("tinted_glass", fn(BID $id) => new TintedGlass($id, "Tinted Glass", new Info(new BreakInfo(0.3)))); //blast resistance should be 30 if we were matched with java :( - $copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 18.0)); + $copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0)); self::register("lightning_rod", fn(BID $id) => new LightningRod($id, "Lightning Rod", $copperBreakInfo)); self::register("copper", fn(BID $id) => new Copper($id, "Copper Block", $copperBreakInfo)); @@ -1730,8 +1735,8 @@ final class VanillaBlocks{ self::register("cave_vines", fn(BID $id) => new CaveVines($id, "Cave Vines", new Info(BreakInfo::instant()))); self::register("small_dripleaf", fn(BID $id) => new SmallDripleaf($id, "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1)))); - self::register("big_dripleaf_head", fn(BID $id) => new BigDripleafHead($id, "Big Dripleaf", new Info(BreakInfo::instant()))); - self::register("big_dripleaf_stem", fn(BID $id) => new BigDripleafStem($id, "Big Dripleaf Stem", new Info(BreakInfo::instant()))); + self::register("big_dripleaf_head", fn(BID $id) => new BigDripleafHead($id, "Big Dripleaf", new Info(new BreakInfo(0.1)))); + self::register("big_dripleaf_stem", fn(BID $id) => new BigDripleafStem($id, "Big Dripleaf Stem", new Info(new BreakInfo(0.1)))); } private static function registerBlocksR18() : void{ @@ -1742,7 +1747,7 @@ final class VanillaBlocks{ self::register("mud", fn(BID $id) => new Opaque($id, "Mud", new Info(BreakInfo::shovel(0.5), [Tags::MUD]))); self::register("packed_mud", fn(BID $id) => new Opaque($id, "Packed Mud", new Info(BreakInfo::pickaxe(1.0, null, 15.0)))); - $mudBricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); + $mudBricksBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 15.0)); self::register("mud_bricks", fn(BID $id) => new Opaque($id, "Mud Bricks", $mudBricksBreakInfo)); self::register("mud_brick_slab", fn(BID $id) => new Slab($id, "Mud Brick", $mudBricksBreakInfo)); @@ -1754,7 +1759,7 @@ final class VanillaBlocks{ self::register("resin", fn(BID $id) => new Opaque($id, "Block of Resin", new Info(BreakInfo::instant()))); self::register("resin_clump", fn(BID $id) => new ResinClump($id, "Resin Clump", new Info(BreakInfo::instant()))); - $resinBricksInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD)); + $resinBricksInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); self::register("resin_brick_slab", fn(BID $id) => new Slab($id, "Resin Brick", $resinBricksInfo)); self::register("resin_brick_stairs", fn(BID $id) => new Stair($id, "Resin Brick Stairs", $resinBricksInfo)); self::register("resin_brick_wall", fn(BID $id) => new Wall($id, "Resin Brick Wall", $resinBricksInfo)); diff --git a/tests/phpunit/block/BlockTest.php b/tests/phpunit/block/BlockTest.php index 971564720..138a3e4e8 100644 --- a/tests/phpunit/block/BlockTest.php +++ b/tests/phpunit/block/BlockTest.php @@ -24,16 +24,22 @@ declare(strict_types=1); namespace pocketmine\block; use PHPUnit\Framework\TestCase; +use pocketmine\data\bedrock\BedrockDataFiles; +use pocketmine\data\bedrock\block\BlockTypeNames; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; +use pocketmine\world\format\io\GlobalBlockStateHandlers; +use function array_fill_keys; use function get_debug_type; use function implode; use function is_array; +use function is_float; use function is_int; use function is_string; use function json_decode; use function log; +use function round; use const JSON_THROW_ON_ERROR; class BlockTest extends TestCase{ @@ -95,6 +101,55 @@ class BlockTest extends TestCase{ } } + public function testBlockBreakInfo() : void{ + $propertiesTable = json_decode(Filesystem::fileGetContents(BedrockDataFiles::BLOCK_PROPERTIES_TABLE_JSON), true, 3, JSON_THROW_ON_ERROR); + if(!is_array($propertiesTable)){ + throw new AssumptionFailedError("Block properties table must be an array"); + } + $exceptions = array_fill_keys([ + BlockTypeNames::AIR, + BlockTypeNames::WATER, + BlockTypeNames::FLOWING_WATER, + BlockTypeNames::LAVA, + BlockTypeNames::FLOWING_LAVA, + BlockTypeNames::MANGROVE_LOG, //For some reason ONLY this wood block has blast resistance 2 instead of 10... + ], true); + + $serializer = GlobalBlockStateHandlers::getSerializer(); + $testedBlocks = []; + $hardnessErrors = []; + $blastResistanceErrors = []; + foreach($this->blockFactory->getAllKnownStates() as $block){ + $vanillaId = $serializer->serializeBlock($block)->getName(); + if(isset($exceptions[$vanillaId]) || isset($testedBlocks[$vanillaId])){ + continue; + } + if(!isset($propertiesTable[$vanillaId]) || !is_array($propertiesTable[$vanillaId])){ + throw new AssumptionFailedError("$vanillaId does not exist in the vanilla block properties table or is not an array"); + } + if(!isset($propertiesTable[$vanillaId]["hardness"]) || !is_float($propertiesTable[$vanillaId]["hardness"])){ + throw new AssumptionFailedError("Hardness property is missing for $vanillaId or is not a float value"); + } + if(!isset($propertiesTable[$vanillaId]["blastResistance"]) || !is_float($propertiesTable[$vanillaId]["blastResistance"])){ + throw new AssumptionFailedError("Blast resistance property is missing for $vanillaId or is not a float value"); + } + $testedBlocks[$vanillaId] = true; + + $vanillaHardness = round($propertiesTable[$vanillaId]["hardness"], 5); + $vanillaBlastResistance = round($propertiesTable[$vanillaId]["blastResistance"], 5) * 5; + + $breakInfo = $block->getBreakInfo(); + if($breakInfo->getHardness() !== $vanillaHardness){ + $hardnessErrors[] = "Hardness mismatch for $vanillaId (expected: $vanillaHardness, got " . $breakInfo->getHardness() . ")"; + } + if($breakInfo->getBlastResistance() !== $vanillaBlastResistance){ + $blastResistanceErrors[] = "Blast resistance mismatch for $vanillaId (expected: $vanillaBlastResistance, got " . $breakInfo->getBlastResistance() . ")"; + } + } + self::assertEmpty($hardnessErrors, "Block hardness test failed:\n" . implode("\n", $hardnessErrors)); + self::assertEmpty($blastResistanceErrors, "Block blast resistance test failed:\n" . implode("\n", $blastResistanceErrors)); + } + /** * @return int[][]|string[][] * @phpstan-return array{array, array} From eee2e62d81b6aeec02e668b2f36680bffdf39af7 Mon Sep 17 00:00:00 2001 From: zSALLAZAR <59490940+zSALLAZAR@users.noreply.github.com> Date: Sun, 25 May 2025 10:01:46 +0200 Subject: [PATCH 031/140] Add EntityFrostWalkerEvent (#6673) --- src/entity/Living.php | 20 ++++- src/event/entity/EntityFrostWalkerEvent.php | 84 +++++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 src/event/entity/EntityFrostWalkerEvent.php diff --git a/src/entity/Living.php b/src/entity/Living.php index 852344784..6d62c85d2 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -38,6 +38,7 @@ use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDeathEvent; +use pocketmine\event\entity\EntityFrostWalkerEvent; use pocketmine\inventory\ArmorInventory; use pocketmine\inventory\CallbackInventoryListener; use pocketmine\inventory\Inventory; @@ -721,19 +722,30 @@ abstract class Living extends Entity{ $y = $this->location->getFloorY() - 1; $baseZ = $this->location->getFloorZ(); - $frostedIce = VanillaBlocks::FROSTED_ICE(); + $liquid = VanillaBlocks::WATER(); + $targetBlock = VanillaBlocks::FROSTED_ICE(); + if(EntityFrostWalkerEvent::hasHandlers()){ + $ev = new EntityFrostWalkerEvent($this, $radius, $liquid, $targetBlock); + $ev->call(); + if($ev->isCancelled()){ + return; + } + $radius = $ev->getRadius(); + $liquid = $ev->getLiquid(); + $targetBlock = $ev->getTargetBlock(); + } + for($x = $baseX - $radius; $x <= $baseX + $radius; $x++){ for($z = $baseZ - $radius; $z <= $baseZ + $radius; $z++){ $block = $world->getBlockAt($x, $y, $z); if( - !$block instanceof Water || - !$block->isSource() || + !$block->isSameState($liquid) || $world->getBlockAt($x, $y + 1, $z)->getTypeId() !== BlockTypeIds::AIR || count($world->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z))) !== 0 ){ continue; } - $world->setBlockAt($x, $y, $z, $frostedIce); + $world->setBlockAt($x, $y, $z, $targetBlock); } } } diff --git a/src/event/entity/EntityFrostWalkerEvent.php b/src/event/entity/EntityFrostWalkerEvent.php new file mode 100644 index 000000000..15ba28268 --- /dev/null +++ b/src/event/entity/EntityFrostWalkerEvent.php @@ -0,0 +1,84 @@ + + */ +class EntityFrostWalkerEvent extends EntityEvent implements Cancellable{ + use CancellableTrait; + + public function __construct( + Living $entity, + private int $radius, + private Liquid $liquid, + private Block $targetBlock + ){ + $this->entity = $entity; + } + + public function getRadius() : int{ + return $this->radius; + } + + public function setRadius(int $radius) : void{ + $this->radius = $radius; + } + + /** + * Returns the liquid that gets frozen + */ + public function getLiquid() : Liquid{ + return $this->liquid; + } + + /** + * Sets the liquid that gets frozen + */ + public function setLiquid(Liquid $liquid) : void{ + $this->liquid = $liquid; + } + + /** + * Returns the block that replaces the liquid + */ + public function getTargetBlock() : Block{ + return $this->targetBlock; + } + + /** + * Sets the block that replaces the liquid + */ + public function setTargetBlock(Block $targetBlock) : void{ + $this->targetBlock = $targetBlock; + } +} From 18b6b1742cd39e3a23fa590d8dc64d2dd94bacc0 Mon Sep 17 00:00:00 2001 From: zSALLAZAR <59490940+zSALLAZAR@users.noreply.github.com> Date: Sun, 25 May 2025 10:04:33 +0200 Subject: [PATCH 032/140] Rename `PlayerExhaustEvent` to `EntityExhaustEvent` (#6674) Removed the `getPlayer` function --- src/entity/Human.php | 6 +++--- src/entity/HungerManager.php | 10 +++++----- src/entity/effect/HungerEffect.php | 4 ++-- .../EntityExhaustEvent.php} | 20 ++++++------------- src/player/Player.php | 12 +++++------ 5 files changed, 22 insertions(+), 30 deletions(-) rename src/event/{player/PlayerExhaustEvent.php => entity/EntityExhaustEvent.php} (82%) diff --git a/src/entity/Human.php b/src/entity/Human.php index 1bed6d0a1..fd3287cdf 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -30,7 +30,7 @@ use pocketmine\entity\effect\EffectInstance; use pocketmine\entity\effect\VanillaEffects; use pocketmine\entity\projectile\ProjectileSource; use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\player\PlayerExhaustEvent; +use pocketmine\event\entity\EntityExhaustEvent; use pocketmine\inventory\CallbackInventoryListener; use pocketmine\inventory\Inventory; use pocketmine\inventory\InventoryHolder; @@ -173,9 +173,9 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ public function jump() : void{ parent::jump(); if($this->isSprinting()){ - $this->hungerManager->exhaust(0.2, PlayerExhaustEvent::CAUSE_SPRINT_JUMPING); + $this->hungerManager->exhaust(0.2, EntityExhaustEvent::CAUSE_SPRINT_JUMPING); }else{ - $this->hungerManager->exhaust(0.05, PlayerExhaustEvent::CAUSE_JUMPING); + $this->hungerManager->exhaust(0.05, EntityExhaustEvent::CAUSE_JUMPING); } } diff --git a/src/entity/HungerManager.php b/src/entity/HungerManager.php index 7e3b40e74..3167eaec0 100644 --- a/src/entity/HungerManager.php +++ b/src/entity/HungerManager.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace pocketmine\entity; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\event\entity\EntityExhaustEvent; use pocketmine\event\entity\EntityRegainHealthEvent; -use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\world\World; use function max; use function min; @@ -130,13 +130,13 @@ class HungerManager{ * * @return float the amount of exhaustion level increased */ - public function exhaust(float $amount, int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{ + public function exhaust(float $amount, int $cause = EntityExhaustEvent::CAUSE_CUSTOM) : float{ if(!$this->enabled){ return 0; } $evAmount = $amount; - if(PlayerExhaustEvent::hasHandlers()){ - $ev = new PlayerExhaustEvent($this->entity, $amount, $cause); + if(EntityExhaustEvent::hasHandlers()){ + $ev = new EntityExhaustEvent($this->entity, $amount, $cause); $ev->call(); if($ev->isCancelled()){ return 0.0; @@ -205,7 +205,7 @@ class HungerManager{ if($food >= 18){ if($health < $this->entity->getMaxHealth()){ $this->entity->heal(new EntityRegainHealthEvent($this->entity, 1, EntityRegainHealthEvent::CAUSE_SATURATION)); - $this->exhaust(6.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN); + $this->exhaust(6.0, EntityExhaustEvent::CAUSE_HEALTH_REGEN); } }elseif($food <= 0){ if(($difficulty === World::DIFFICULTY_EASY && $health > 10) || ($difficulty === World::DIFFICULTY_NORMAL && $health > 1) || $difficulty === World::DIFFICULTY_HARD){ diff --git a/src/entity/effect/HungerEffect.php b/src/entity/effect/HungerEffect.php index 949b148bc..5ce85dfe8 100644 --- a/src/entity/effect/HungerEffect.php +++ b/src/entity/effect/HungerEffect.php @@ -26,7 +26,7 @@ namespace pocketmine\entity\effect; use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\entity\Living; -use pocketmine\event\player\PlayerExhaustEvent; +use pocketmine\event\entity\EntityExhaustEvent; class HungerEffect extends Effect{ @@ -36,7 +36,7 @@ class HungerEffect extends Effect{ public function applyEffect(Living $entity, EffectInstance $instance, float $potency = 1.0, ?Entity $source = null) : void{ if($entity instanceof Human){ - $entity->getHungerManager()->exhaust(0.1 * $instance->getEffectLevel(), PlayerExhaustEvent::CAUSE_POTION); + $entity->getHungerManager()->exhaust(0.1 * $instance->getEffectLevel(), EntityExhaustEvent::CAUSE_POTION); } } } diff --git a/src/event/player/PlayerExhaustEvent.php b/src/event/entity/EntityExhaustEvent.php similarity index 82% rename from src/event/player/PlayerExhaustEvent.php rename to src/event/entity/EntityExhaustEvent.php index 9a13ff900..6a4954a21 100644 --- a/src/event/player/PlayerExhaustEvent.php +++ b/src/event/entity/EntityExhaustEvent.php @@ -21,17 +21,16 @@ declare(strict_types=1); -namespace pocketmine\event\player; +namespace pocketmine\event\entity; -use pocketmine\entity\Human; +use pocketmine\entity\Entity; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; -use pocketmine\event\entity\EntityEvent; /** - * @phpstan-extends EntityEvent + * @phpstan-extends EntityEvent */ -class PlayerExhaustEvent extends EntityEvent implements Cancellable{ +class EntityExhaustEvent extends EntityEvent implements Cancellable{ use CancellableTrait; public const CAUSE_ATTACK = 1; @@ -47,18 +46,11 @@ class PlayerExhaustEvent extends EntityEvent implements Cancellable{ public const CAUSE_CUSTOM = 11; public function __construct( - protected Human $human, + Entity $entity, private float $amount, private int $cause ){ - $this->entity = $human; - } - - /** - * @return Human - */ - public function getPlayer(){ - return $this->human; + $this->entity = $entity; } public function getAmount() : float{ diff --git a/src/player/Player.php b/src/player/Player.php index ff8a67c94..056a628e3 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -46,6 +46,7 @@ use pocketmine\entity\projectile\Arrow; use pocketmine\entity\Skin; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\event\entity\EntityExhaustEvent; use pocketmine\event\entity\EntityExtinguishEvent; use pocketmine\event\inventory\InventoryCloseEvent; use pocketmine\event\inventory\InventoryOpenEvent; @@ -60,7 +61,6 @@ use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerEmoteEvent; use pocketmine\event\player\PlayerEntityInteractEvent; use pocketmine\event\player\PlayerEntityPickEvent; -use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\event\player\PlayerGameModeChangeEvent; use pocketmine\event\player\PlayerInteractEvent; use pocketmine\event\player\PlayerItemConsumeEvent; @@ -1442,9 +1442,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($horizontalDistanceTravelled > 0){ //TODO: check for swimming if($this->isSprinting()){ - $this->hungerManager->exhaust(0.01 * $horizontalDistanceTravelled, PlayerExhaustEvent::CAUSE_SPRINTING); + $this->hungerManager->exhaust(0.01 * $horizontalDistanceTravelled, EntityExhaustEvent::CAUSE_SPRINTING); }else{ - $this->hungerManager->exhaust(0.0, PlayerExhaustEvent::CAUSE_WALKING); + $this->hungerManager->exhaust(0.0, EntityExhaustEvent::CAUSE_WALKING); } if($this->nextChunkOrderRun > 20){ @@ -1910,7 +1910,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $returnedItems = []; if($this->getWorld()->useBreakOn($pos, $item, $this, true, $returnedItems)){ $this->returnItemsFromAction($oldItem, $item, $returnedItems); - $this->hungerManager->exhaust(0.005, PlayerExhaustEvent::CAUSE_MINING); + $this->hungerManager->exhaust(0.005, EntityExhaustEvent::CAUSE_MINING); return true; } }else{ @@ -2013,7 +2013,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $heldItem->onAttackEntity($entity, $returnedItems); $this->returnItemsFromAction($oldItem, $heldItem, $returnedItems); - $this->hungerManager->exhaust(0.1, PlayerExhaustEvent::CAUSE_ATTACK); + $this->hungerManager->exhaust(0.1, EntityExhaustEvent::CAUSE_ATTACK); } return true; @@ -2584,7 +2584,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ protected function applyPostDamageEffects(EntityDamageEvent $source) : void{ parent::applyPostDamageEffects($source); - $this->hungerManager->exhaust(0.1, PlayerExhaustEvent::CAUSE_DAMAGE); + $this->hungerManager->exhaust(0.1, EntityExhaustEvent::CAUSE_DAMAGE); } public function attack(EntityDamageEvent $source) : void{ From 5527a0c6bf4343b39cd6ed4526f75539ac6ddf19 Mon Sep 17 00:00:00 2001 From: ItzxDwi <107537435+ItzxDwi@users.noreply.github.com> Date: Sun, 25 May 2025 16:07:41 +0800 Subject: [PATCH 033/140] Entity: make stepHeight accessable (#6702) --- src/entity/Entity.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 6681558ad..eb7098f1e 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -1187,12 +1187,14 @@ abstract class Entity{ $moveBB->offset(0, 0, $dz); - if($this->stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ + $stepHeight = $this->getStepHeight(); + + if($stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ $cx = $dx; $cy = $dy; $cz = $dz; $dx = $wantedX; - $dy = $this->stepHeight; + $dy = $stepHeight; $dz = $wantedZ; $stepBB = clone $this->boundingBox; @@ -1262,6 +1264,14 @@ abstract class Entity{ Timings::$entityMove->stopTiming(); } + public function setStepHeight(float $stepHeight) : void{ + $this->stepHeight = $stepHeight; + } + + public function getStepHeight() : float{ + return $this->stepHeight; + } + protected function checkGroundState(float $wantedX, float $wantedY, float $wantedZ, float $dx, float $dy, float $dz) : void{ $this->isCollidedVertically = $wantedY !== $dy; $this->isCollidedHorizontally = ($wantedX !== $dx || $wantedZ !== $dz); From 05eda887b11313b1db42224e8386504338da703f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= <57811430+SmallkingDev@users.noreply.github.com> Date: Sun, 25 May 2025 09:35:26 +0100 Subject: [PATCH 034/140] Item: make setter methods fluent (#6678) - `Item::clearCustomBlockData` previously, the instance was returned, but the return type has been changed - `Item::setCanPlaceOn`, `Item::setCanDestroy` and `Item::setKeepOnDeath` now return `$this` --- src/item/Item.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/item/Item.php b/src/item/Item.php index 205f15e13..8f8623c79 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -125,7 +125,7 @@ class Item implements \JsonSerializable{ /** * @return $this */ - public function clearCustomBlockData(){ + public function clearCustomBlockData() : Item{ $this->blockEntityTag = null; return $this; } @@ -202,11 +202,12 @@ class Item implements \JsonSerializable{ /** * @param string[] $canPlaceOn */ - public function setCanPlaceOn(array $canPlaceOn) : void{ + public function setCanPlaceOn(array $canPlaceOn) : Item{ $this->canPlaceOn = []; foreach($canPlaceOn as $value){ $this->canPlaceOn[$value] = $value; } + return $this; } /** @@ -220,11 +221,12 @@ class Item implements \JsonSerializable{ /** * @param string[] $canDestroy */ - public function setCanDestroy(array $canDestroy) : void{ + public function setCanDestroy(array $canDestroy) : Item{ $this->canDestroy = []; foreach($canDestroy as $value){ $this->canDestroy[$value] = $value; } + return $this; } /** @@ -234,8 +236,9 @@ class Item implements \JsonSerializable{ return $this->keepOnDeath; } - public function setKeepOnDeath(bool $keepOnDeath) : void{ + public function setKeepOnDeath(bool $keepOnDeath) : Item{ $this->keepOnDeath = $keepOnDeath; + return $this; } /** From a554d2acf5db00f577493517e36bb3ad07a6bdd0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 25 May 2025 11:32:20 +0100 Subject: [PATCH 035/140] Revert change that can't go on stable API additions need to wait for the next minor release Revert "Entity: make stepHeight accessable (#6702)" This reverts commit 5527a0c6bf4343b39cd6ed4526f75539ac6ddf19. --- src/entity/Entity.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/entity/Entity.php b/src/entity/Entity.php index eb7098f1e..6681558ad 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -1187,14 +1187,12 @@ abstract class Entity{ $moveBB->offset(0, 0, $dz); - $stepHeight = $this->getStepHeight(); - - if($stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ + if($this->stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ $cx = $dx; $cy = $dy; $cz = $dz; $dx = $wantedX; - $dy = $stepHeight; + $dy = $this->stepHeight; $dz = $wantedZ; $stepBB = clone $this->boundingBox; @@ -1264,14 +1262,6 @@ abstract class Entity{ Timings::$entityMove->stopTiming(); } - public function setStepHeight(float $stepHeight) : void{ - $this->stepHeight = $stepHeight; - } - - public function getStepHeight() : float{ - return $this->stepHeight; - } - protected function checkGroundState(float $wantedX, float $wantedY, float $wantedZ, float $dx, float $dy, float $dz) : void{ $this->isCollidedVertically = $wantedY !== $dy; $this->isCollidedHorizontally = ($wantedX !== $dx || $wantedZ !== $dz); From dd17adeaaf96745ff78dd39107adc9f9f9a43c6d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 25 May 2025 11:33:47 +0100 Subject: [PATCH 036/140] Reintroduce step height additions for minor-next Revert "Revert change that can't go on stable" This reverts commit a554d2acf5db00f577493517e36bb3ad07a6bdd0. --- src/entity/Entity.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/entity/Entity.php b/src/entity/Entity.php index d73356df8..73a0b3a9c 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -1191,12 +1191,14 @@ abstract class Entity{ $moveBB->offset(0, 0, $dz); - if($this->stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ + $stepHeight = $this->getStepHeight(); + + if($stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ $cx = $dx; $cy = $dy; $cz = $dz; $dx = $wantedX; - $dy = $this->stepHeight; + $dy = $stepHeight; $dz = $wantedZ; $stepBB = clone $this->boundingBox; @@ -1266,6 +1268,14 @@ abstract class Entity{ Timings::$entityMove->stopTiming(); } + public function setStepHeight(float $stepHeight) : void{ + $this->stepHeight = $stepHeight; + } + + public function getStepHeight() : float{ + return $this->stepHeight; + } + protected function checkGroundState(float $wantedX, float $wantedY, float $wantedZ, float $dx, float $dy, float $dz) : void{ $this->isCollidedVertically = $wantedY !== $dy; $this->isCollidedHorizontally = ($wantedX !== $dx || $wantedZ !== $dz); From 059f4ee7bfb2b892dadce97fa3c54a0f680f60f9 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 27 May 2025 21:51:10 +0100 Subject: [PATCH 037/140] Extract GeneratorExecutor system from World, v2 (#6682) - `AsyncGeneratorExecutor` class added that encapsulates the logic of generating chunks using async tasks as previously - `GeneratorExecutor` interface added that can be implemented to provide chunks in other ways - `SyncGeneratorExecutor` which invokes the generator directly on the main thread, useful for simple generators like `Flat` where async tasks are not needed - Some redundant APIs were removed from `World` (these will probably come back as deprecated stubs for the remainder of 5.x, but I was having too much fun deleting code) - Removed internal `World->registerGeneratorToWorker()` (no longer useful) - `World` now invokes generator executor instead of posting AsyncTasks directly - Some internal classes moved to `pocketmine\world\generator\executor` (PopulationTask excluded because plugins use it in lieu of being able to regenerate chunks - Generators can opt into main-thread execution by setting the `$fast` parameter to `true` in `GeneratorManager::register()` --- src/world/World.php | 74 +++++------- src/world/generator/GeneratorManager.php | 7 +- src/world/generator/GeneratorManagerEntry.php | 5 +- src/world/generator/PopulationTask.php | 7 ++ .../executor/AsyncGeneratorExecutor.php | 106 ++++++++++++++++++ .../AsyncGeneratorRegisterTask.php} | 33 ++---- .../AsyncGeneratorUnregisterTask.php} | 15 +-- .../generator/executor/GeneratorExecutor.php | 38 +++++++ .../GeneratorExecutorSetupParameters.php | 50 +++++++++ .../executor/SyncGeneratorExecutor.php | 61 ++++++++++ .../ThreadLocalGeneratorContext.php | 4 +- tests/phpstan/configs/actual-problems.neon | 12 +- 12 files changed, 318 insertions(+), 94 deletions(-) create mode 100644 src/world/generator/executor/AsyncGeneratorExecutor.php rename src/world/generator/{GeneratorRegisterTask.php => executor/AsyncGeneratorRegisterTask.php} (54%) rename src/world/generator/{GeneratorUnregisterTask.php => executor/AsyncGeneratorUnregisterTask.php} (74%) create mode 100644 src/world/generator/executor/GeneratorExecutor.php create mode 100644 src/world/generator/executor/GeneratorExecutorSetupParameters.php create mode 100644 src/world/generator/executor/SyncGeneratorExecutor.php rename src/world/generator/{ => executor}/ThreadLocalGeneratorContext.php (94%) diff --git a/src/world/World.php b/src/world/World.php index c4d8f8671..792681a89 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -93,9 +93,11 @@ use pocketmine\world\format\io\GlobalBlockStateHandlers; use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\format\LightArray; use pocketmine\world\format\SubChunk; +use pocketmine\world\generator\executor\AsyncGeneratorExecutor; +use pocketmine\world\generator\executor\GeneratorExecutor; +use pocketmine\world\generator\executor\GeneratorExecutorSetupParameters; +use pocketmine\world\generator\executor\SyncGeneratorExecutor; use pocketmine\world\generator\GeneratorManager; -use pocketmine\world\generator\GeneratorRegisterTask; -use pocketmine\world\generator\GeneratorUnregisterTask; use pocketmine\world\generator\PopulationTask; use pocketmine\world\light\BlockLightUpdate; use pocketmine\world\light\LightPopulationTask; @@ -336,11 +338,7 @@ class World implements ChunkManager{ */ private array $chunkPopulationRequestQueueIndex = []; - /** - * @var true[] - * @phpstan-var array - */ - private array $generatorRegisteredWorkers = []; + private readonly GeneratorExecutor $generatorExecutor; private bool $autoSave = true; @@ -360,9 +358,6 @@ class World implements ChunkManager{ private bool $doingTick = false; - /** @phpstan-var class-string */ - private string $generator; - private bool $unloaded = false; /** * @var \Closure[] @@ -498,7 +493,23 @@ class World implements ChunkManager{ $generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator()) ?? throw new AssumptionFailedError("WorldManager should already have checked that the generator exists"); $generator->validateGeneratorOptions($this->provider->getWorldData()->getGeneratorOptions()); - $this->generator = $generator->getGeneratorClass(); + + $executorSetupParameters = new GeneratorExecutorSetupParameters( + worldMinY: $this->minY, + worldMaxY: $this->maxY, + generatorSeed: $this->getSeed(), + generatorClass: $generator->getGeneratorClass(), + generatorSettings: $this->provider->getWorldData()->getGeneratorOptions() + ); + $this->generatorExecutor = $generator->isFast() ? + new SyncGeneratorExecutor($executorSetupParameters) : + new AsyncGeneratorExecutor( + $this->logger, + $this->workerPool, + $executorSetupParameters, + $this->worldId + ); + $this->chunkPopulationRequestQueue = new \SplQueue(); $this->addOnUnloadCallback(function() : void{ $this->logger->debug("Cancelling unfulfilled generation requests"); @@ -534,17 +545,6 @@ class World implements ChunkManager{ $this->initRandomTickBlocksFromConfig($cfg); $this->timings = new WorldTimings($this); - - $this->workerPool->addWorkerStartHook($workerStartHook = function(int $workerId) : void{ - if(array_key_exists($workerId, $this->generatorRegisteredWorkers)){ - $this->logger->debug("Worker $workerId with previously registered generator restarted, flagging as unregistered"); - unset($this->generatorRegisteredWorkers[$workerId]); - } - }); - $workerPool = $this->workerPool; - $this->addOnUnloadCallback(static function() use ($workerPool, $workerStartHook) : void{ - $workerPool->removeWorkerStartHook($workerStartHook); - }); } private function initRandomTickBlocksFromConfig(ServerConfigGroup $cfg) : void{ @@ -585,21 +585,6 @@ class World implements ChunkManager{ return $this->tickRateTime; } - public function registerGeneratorToWorker(int $worker) : void{ - $this->logger->debug("Registering generator on worker $worker"); - $this->workerPool->submitTaskToWorker(new GeneratorRegisterTask($this, $this->generator, $this->provider->getWorldData()->getGeneratorOptions()), $worker); - $this->generatorRegisteredWorkers[$worker] = true; - } - - public function unregisterGenerator() : void{ - foreach($this->workerPool->getRunningWorkers() as $i){ - if(isset($this->generatorRegisteredWorkers[$i])){ - $this->workerPool->submitTaskToWorker(new GeneratorUnregisterTask($this), $i); - } - } - $this->generatorRegisteredWorkers = []; - } - public function getServer() : Server{ return $this->server; } @@ -657,7 +642,7 @@ class World implements ChunkManager{ $this->save(); - $this->unregisterGenerator(); + $this->generatorExecutor->shutdown(); $this->provider->close(); $this->blockCache = []; @@ -3486,8 +3471,8 @@ class World implements ChunkManager{ $centerChunk = $this->loadChunk($chunkX, $chunkZ); $adjacentChunks = $this->getAdjacentChunks($chunkX, $chunkZ); - $task = new PopulationTask( - $this->worldId, + + $this->generatorExecutor->populate( $chunkX, $chunkZ, $centerChunk, @@ -3500,15 +3485,6 @@ class World implements ChunkManager{ $this->generateChunkCallback($chunkPopulationLockId, $chunkX, $chunkZ, $centerChunk, $adjacentChunks, $temporaryChunkLoader); } ); - $workerId = $this->workerPool->selectWorker(); - if(!isset($this->workerPool->getRunningWorkers()[$workerId]) && isset($this->generatorRegisteredWorkers[$workerId])){ - $this->logger->debug("Selected worker $workerId previously had generator registered, but is now offline"); - unset($this->generatorRegisteredWorkers[$workerId]); - } - if(!isset($this->generatorRegisteredWorkers[$workerId])){ - $this->registerGeneratorToWorker($workerId); - } - $this->workerPool->submitTaskToWorker($task, $workerId); return $resolver->getPromise(); }finally{ diff --git a/src/world/generator/GeneratorManager.php b/src/world/generator/GeneratorManager.php index 291ea91de..a1b00480e 100644 --- a/src/world/generator/GeneratorManager.php +++ b/src/world/generator/GeneratorManager.php @@ -50,7 +50,7 @@ final class GeneratorManager{ }catch(InvalidGeneratorOptionsException $e){ return $e; } - }); + }, fast: true); $this->addGenerator(Normal::class, "normal", fn() => null); $this->addAlias("normal", "default"); $this->addGenerator(Nether::class, "nether", fn() => null); @@ -62,6 +62,7 @@ final class GeneratorManager{ * @param string $name Alias for this generator type that can be written in configs * @param \Closure $presetValidator Callback to validate generator options for new worlds * @param bool $overwrite Whether to force overwriting any existing registered generator with the same name + * @param bool $fast Whether this generator is fast enough to run without async tasks * * @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator * @@ -69,7 +70,7 @@ final class GeneratorManager{ * * @throws \InvalidArgumentException */ - public function addGenerator(string $class, string $name, \Closure $presetValidator, bool $overwrite = false) : void{ + public function addGenerator(string $class, string $name, \Closure $presetValidator, bool $overwrite = false, bool $fast = false) : void{ Utils::testValidInstance($class, Generator::class); $name = strtolower($name); @@ -77,7 +78,7 @@ final class GeneratorManager{ throw new \InvalidArgumentException("Alias \"$name\" is already assigned"); } - $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator); + $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator, $fast); } /** diff --git a/src/world/generator/GeneratorManagerEntry.php b/src/world/generator/GeneratorManagerEntry.php index 256ed27d5..942f6ee79 100644 --- a/src/world/generator/GeneratorManagerEntry.php +++ b/src/world/generator/GeneratorManagerEntry.php @@ -31,12 +31,15 @@ final class GeneratorManagerEntry{ */ public function __construct( private string $generatorClass, - private \Closure $presetValidator + private \Closure $presetValidator, + private readonly bool $fast ){} /** @phpstan-return class-string */ public function getGeneratorClass() : string{ return $this->generatorClass; } + public function isFast() : bool{ return $this->fast; } + /** * @throws InvalidGeneratorOptionsException */ diff --git a/src/world/generator/PopulationTask.php b/src/world/generator/PopulationTask.php index a8366a306..971349a5b 100644 --- a/src/world/generator/PopulationTask.php +++ b/src/world/generator/PopulationTask.php @@ -27,11 +27,18 @@ use pocketmine\scheduler\AsyncTask; use pocketmine\utils\AssumptionFailedError; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; +use pocketmine\world\generator\executor\ThreadLocalGeneratorContext; use function array_map; use function igbinary_serialize; use function igbinary_unserialize; /** + * @internal + * + * TODO: this should be moved to the executor namespace, but plugins have unfortunately used it directly due to the + * difficulty of regenerating chunks. This should be addressed in the future. + * For the remainder of PM5, we can't relocate this class. + * * @phpstan-type OnCompletion \Closure(Chunk $centerChunk, array $adjacentChunks) : void */ class PopulationTask extends AsyncTask{ diff --git a/src/world/generator/executor/AsyncGeneratorExecutor.php b/src/world/generator/executor/AsyncGeneratorExecutor.php new file mode 100644 index 000000000..d19b6e661 --- /dev/null +++ b/src/world/generator/executor/AsyncGeneratorExecutor.php @@ -0,0 +1,106 @@ + + */ + private array $generatorRegisteredWorkers = []; + + public function __construct( + \Logger $logger, + private readonly AsyncPool $workerPool, + private readonly GeneratorExecutorSetupParameters $setupParameters, + int $asyncContextId = null + ){ + $this->logger = new \PrefixedLogger($logger, "AsyncGeneratorExecutor"); + + //TODO: we only allow setting this for PM5 because of PopulationTask uses in plugins + $this->asyncContextId = $asyncContextId ?? self::$nextAsyncContextId++; + + $this->workerStartHook = function(int $workerId) : void{ + if(array_key_exists($workerId, $this->generatorRegisteredWorkers)){ + $this->logger->debug("Worker $workerId with previously registered generator restarted, flagging as unregistered"); + unset($this->generatorRegisteredWorkers[$workerId]); + } + }; + $this->workerPool->addWorkerStartHook($this->workerStartHook); + } + + private function registerGeneratorToWorker(int $worker) : void{ + $this->logger->debug("Registering generator on worker $worker"); + $this->workerPool->submitTaskToWorker(new AsyncGeneratorRegisterTask($this->setupParameters, $this->asyncContextId), $worker); + $this->generatorRegisteredWorkers[$worker] = true; + } + + private function unregisterGenerator() : void{ + foreach($this->workerPool->getRunningWorkers() as $i){ + if(isset($this->generatorRegisteredWorkers[$i])){ + $this->workerPool->submitTaskToWorker(new AsyncGeneratorUnregisterTask($this->asyncContextId), $i); + } + } + $this->generatorRegisteredWorkers = []; + } + + public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void{ + $task = new PopulationTask( + $this->asyncContextId, + $chunkX, + $chunkZ, + $centerChunk, + $adjacentChunks, + $onCompletion + ); + $workerId = $this->workerPool->selectWorker(); + if(!isset($this->workerPool->getRunningWorkers()[$workerId]) && isset($this->generatorRegisteredWorkers[$workerId])){ + $this->logger->debug("Selected worker $workerId previously had generator registered, but is now offline"); + unset($this->generatorRegisteredWorkers[$workerId]); + } + if(!isset($this->generatorRegisteredWorkers[$workerId])){ + $this->registerGeneratorToWorker($workerId); + } + $this->workerPool->submitTaskToWorker($task, $workerId); + } + + public function shutdown() : void{ + $this->unregisterGenerator(); + $this->workerPool->removeWorkerStartHook($this->workerStartHook); + } +} diff --git a/src/world/generator/GeneratorRegisterTask.php b/src/world/generator/executor/AsyncGeneratorRegisterTask.php similarity index 54% rename from src/world/generator/GeneratorRegisterTask.php rename to src/world/generator/executor/AsyncGeneratorRegisterTask.php index e2e773a35..5bc67834d 100644 --- a/src/world/generator/GeneratorRegisterTask.php +++ b/src/world/generator/executor/AsyncGeneratorRegisterTask.php @@ -21,37 +21,20 @@ declare(strict_types=1); -namespace pocketmine\world\generator; +namespace pocketmine\world\generator\executor; use pocketmine\scheduler\AsyncTask; -use pocketmine\world\World; -class GeneratorRegisterTask extends AsyncTask{ - public int $seed; - public int $worldId; - public int $worldMinY; - public int $worldMaxY; +class AsyncGeneratorRegisterTask extends AsyncTask{ - /** - * @phpstan-param class-string $generatorClass - */ public function __construct( - World $world, - public string $generatorClass, - public string $generatorSettings - ){ - $this->seed = $world->getSeed(); - $this->worldId = $world->getId(); - $this->worldMinY = $world->getMinY(); - $this->worldMaxY = $world->getMaxY(); - } + private readonly GeneratorExecutorSetupParameters $setupParameters, + private readonly int $contextId + ){} public function onRun() : void{ - /** - * @var Generator $generator - * @see Generator::__construct() - */ - $generator = new $this->generatorClass($this->seed, $this->generatorSettings); - ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldMinY, $this->worldMaxY), $this->worldId); + $setupParameters = $this->setupParameters; + $generator = $setupParameters->createGenerator(); + ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $setupParameters->worldMinY, $setupParameters->worldMaxY), $this->contextId); } } diff --git a/src/world/generator/GeneratorUnregisterTask.php b/src/world/generator/executor/AsyncGeneratorUnregisterTask.php similarity index 74% rename from src/world/generator/GeneratorUnregisterTask.php rename to src/world/generator/executor/AsyncGeneratorUnregisterTask.php index 41b4cd808..c771903f5 100644 --- a/src/world/generator/GeneratorUnregisterTask.php +++ b/src/world/generator/executor/AsyncGeneratorUnregisterTask.php @@ -21,19 +21,16 @@ declare(strict_types=1); -namespace pocketmine\world\generator; +namespace pocketmine\world\generator\executor; use pocketmine\scheduler\AsyncTask; -use pocketmine\world\World; -class GeneratorUnregisterTask extends AsyncTask{ - public int $worldId; - - public function __construct(World $world){ - $this->worldId = $world->getId(); - } +class AsyncGeneratorUnregisterTask extends AsyncTask{ + public function __construct( + private readonly int $contextId + ){} public function onRun() : void{ - ThreadLocalGeneratorContext::unregister($this->worldId); + ThreadLocalGeneratorContext::unregister($this->contextId); } } diff --git a/src/world/generator/executor/GeneratorExecutor.php b/src/world/generator/executor/GeneratorExecutor.php new file mode 100644 index 000000000..d3f62d410 --- /dev/null +++ b/src/world/generator/executor/GeneratorExecutor.php @@ -0,0 +1,38 @@ + $adjacentChunks + * @phpstan-param \Closure(Chunk $centerChunk, array $adjacentChunks) : void $onCompletion + */ + public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void; + + public function shutdown() : void; + +} diff --git a/src/world/generator/executor/GeneratorExecutorSetupParameters.php b/src/world/generator/executor/GeneratorExecutorSetupParameters.php new file mode 100644 index 000000000..b5fdb7bf9 --- /dev/null +++ b/src/world/generator/executor/GeneratorExecutorSetupParameters.php @@ -0,0 +1,50 @@ + $generatorClass + */ + public function __construct( + public readonly int $worldMinY, + public readonly int $worldMaxY, + public readonly int $generatorSeed, + public readonly string $generatorClass, + public readonly string $generatorSettings, + ){} + + public function createGenerator() : Generator{ + /** + * @var Generator $generator + * @see Generator::__construct() + */ + $generator = new $this->generatorClass($this->generatorSeed, $this->generatorSettings); + return $generator; + } +} diff --git a/src/world/generator/executor/SyncGeneratorExecutor.php b/src/world/generator/executor/SyncGeneratorExecutor.php new file mode 100644 index 000000000..79b5fdd00 --- /dev/null +++ b/src/world/generator/executor/SyncGeneratorExecutor.php @@ -0,0 +1,61 @@ +generator = $setupParameters->createGenerator(); + $this->worldMinY = $setupParameters->worldMinY; + $this->worldMaxY = $setupParameters->worldMaxY; + } + + public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void{ + [$centerChunk, $adjacentChunks] = PopulationUtils::populateChunkWithAdjacents( + $this->worldMinY, + $this->worldMaxY, + $this->generator, + $chunkX, + $chunkZ, + $centerChunk, + $adjacentChunks + ); + + $onCompletion($centerChunk, $adjacentChunks); + } + + public function shutdown() : void{ + //NOOP + } +} diff --git a/src/world/generator/ThreadLocalGeneratorContext.php b/src/world/generator/executor/ThreadLocalGeneratorContext.php similarity index 94% rename from src/world/generator/ThreadLocalGeneratorContext.php rename to src/world/generator/executor/ThreadLocalGeneratorContext.php index bcf99882b..bea8bb032 100644 --- a/src/world/generator/ThreadLocalGeneratorContext.php +++ b/src/world/generator/executor/ThreadLocalGeneratorContext.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\world\generator; +namespace pocketmine\world\generator\executor; + +use pocketmine\world\generator\Generator; /** * Manages thread-local caches for generators and the things needed to support them diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index d3adde422..2030a0dad 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -1272,18 +1272,18 @@ parameters: count: 1 path: ../../../src/world/format/io/region/RegionLoader.php - - - message: '#^Dynamic new is not allowed\.$#' - identifier: pocketmine.new.dynamic - count: 1 - path: ../../../src/world/generator/GeneratorRegisterTask.php - - message: '#^Method pocketmine\\world\\generator\\biome\\BiomeSelector\:\:pickBiome\(\) should return pocketmine\\world\\biome\\Biome but returns pocketmine\\world\\biome\\Biome\|null\.$#' identifier: return.type count: 1 path: ../../../src/world/generator/biome/BiomeSelector.php + - + message: '#^Dynamic new is not allowed\.$#' + identifier: pocketmine.new.dynamic + count: 1 + path: ../../../src/world/generator/executor/GeneratorExecutorSetupParameters.php + - message: '#^Cannot call method getBiomeId\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' identifier: method.nonObject From bf33a625c93469b4f158e292c5868a161603daf0 Mon Sep 17 00:00:00 2001 From: Adam <116978956+b1zeyofficial@users.noreply.github.com> Date: Wed, 28 May 2025 02:57:28 +0600 Subject: [PATCH 038/140] Implemented Respawn Anchor (#6646) PlayerRespawnAnchorUseEvent is also added with options SET_SPAWN and EXPLODE, which allows plugins to customise the outcome of using the anchor in PM, which currently doesn't support dimensions. The event is also cancellable. --- src/block/BlockTypeIds.php | 3 +- src/block/RespawnAnchor.php | 123 +++++++++++++++++ src/block/VanillaBlocks.php | 3 + .../convert/BlockObjectToStateSerializer.php | 5 + .../BlockStateToObjectDeserializer.php | 4 + src/entity/object/EndCrystal.php | 2 +- src/entity/object/PrimedTNT.php | 2 +- src/event/block/BlockExplodeEvent.php | 122 +++++++++++++++++ src/event/block/BlockPreExplodeEvent.php | 129 ++++++++++++++++++ src/event/entity/EntityExplodeEvent.php | 25 +++- src/event/entity/EntityPreExplodeEvent.php | 50 ++++++- .../player/PlayerRespawnAnchorUseEvent.php | 56 ++++++++ src/item/StringToItemParser.php | 1 + src/player/Player.php | 17 +++ src/world/Explosion.php | 77 +++++++++-- src/world/sound/RespawnAnchorChargeSound.php | 35 +++++ src/world/sound/RespawnAnchorDepleteSound.php | 35 +++++ .../sound/RespawnAnchorSetSpawnSound.php | 35 +++++ .../block_factory_consistency_check.json | 1 + 19 files changed, 711 insertions(+), 14 deletions(-) create mode 100644 src/block/RespawnAnchor.php create mode 100644 src/event/block/BlockExplodeEvent.php create mode 100644 src/event/block/BlockPreExplodeEvent.php create mode 100644 src/event/player/PlayerRespawnAnchorUseEvent.php create mode 100644 src/world/sound/RespawnAnchorChargeSound.php create mode 100644 src/world/sound/RespawnAnchorDepleteSound.php create mode 100644 src/world/sound/RespawnAnchorSetSpawnSound.php diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index c440cefdc..4af1894bd 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -786,8 +786,9 @@ final class BlockTypeIds{ public const RESIN_BRICKS = 10756; public const RESIN_CLUMP = 10757; public const CHISELED_RESIN_BRICKS = 10758; + public const RESPAWN_ANCHOR = 10759; - public const FIRST_UNUSED_BLOCK_ID = 10759; + public const FIRST_UNUSED_BLOCK_ID = 10760; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/RespawnAnchor.php b/src/block/RespawnAnchor.php new file mode 100644 index 000000000..f702d240d --- /dev/null +++ b/src/block/RespawnAnchor.php @@ -0,0 +1,123 @@ +boundedIntAuto(self::MIN_CHARGES, self::MAX_CHARGES, $this->charges); + } + + public function getCharges() : int{ + return $this->charges; + } + + /** @return $this */ + public function setCharges(int $charges) : self{ + if($charges < self::MIN_CHARGES || $charges > self::MAX_CHARGES){ + throw new \InvalidArgumentException("Charges must be between " . self::MIN_CHARGES . " and " . self::MAX_CHARGES . ", given: $charges"); + } + $this->charges = $charges; + return $this; + } + + public function getLightLevel() : int{ + return $this->charges > 0 ? ($this->charges * 4) - 1 : 0; + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($item->getTypeId() === ItemTypeIds::fromBlockTypeId(BlockTypeIds::GLOWSTONE) && $this->charges < self::MAX_CHARGES){ + $this->position->getWorld()->setBlock($this->position, $this->setCharges($this->charges + 1)); + $this->position->getWorld()->addSound($this->position, new RespawnAnchorChargeSound()); + return true; + } + + if($this->charges > self::MIN_CHARGES){ + if($player === null){ + return false; + } + + $ev = new PlayerRespawnAnchorUseEvent($player, $this, PlayerRespawnAnchorUseEvent::ACTION_EXPLODE); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + + switch($ev->getAction()){ + case PlayerRespawnAnchorUseEvent::ACTION_EXPLODE: + $this->explode($player); + return false; + + case PlayerRespawnAnchorUseEvent::ACTION_SET_SPAWN: + if($player->getSpawn() !== null && $player->getSpawn()->equals($this->position)){ + return true; + } + + $player->setSpawn($this->position); + $this->position->getWorld()->addSound($this->position, new RespawnAnchorSetSpawnSound()); + $player->sendMessage(KnownTranslationFactory::tile_respawn_anchor_respawnSet()->prefix(TextFormat::GRAY)); + return true; + } + } + return false; + } + + private function explode(?Player $player) : void{ + $ev = new BlockPreExplodeEvent($this, 5, $player); + $ev->setIncendiary(true); + + $ev->call(); + if($ev->isCancelled()){ + return; + } + + $this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR()); + + $explosion = new Explosion(Position::fromObject($this->position->add(0.5, 0.5, 0.5), $this->position->getWorld()), $ev->getRadius(), $this); + $explosion->setFireChance($ev->getFireChance()); + + if($ev->isBlockBreaking()){ + $explosion->explodeA(); + } + $explosion->explodeB(); + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 0a6d4b31c..54ec27fc2 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -694,6 +694,7 @@ use function strtolower; * @method static Stair RESIN_BRICK_STAIRS() * @method static Wall RESIN_BRICK_WALL() * @method static ResinClump RESIN_CLUMP() + * @method static RespawnAnchor RESPAWN_ANCHOR() * @method static DoublePlant ROSE_BUSH() * @method static Sand SAND() * @method static Opaque SANDSTONE() @@ -1647,6 +1648,8 @@ final class VanillaBlocks{ self::register("warped_roots", fn(BID $id) => new NetherRoots($id, "Warped Roots", $netherRootsInfo)); self::register("chain", fn(BID $id) => new Chain($id, "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); + + self::register("respawn_anchor", fn(BID $id) => new RespawnAnchor($id, "Respawn Anchor", new Info(BreakInfo::pickaxe(50.0, ToolTier::DIAMOND, 6000.0)))); } private static function registerBlocksR17() : void{ diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 45784d409..27d550f13 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -122,6 +122,7 @@ use pocketmine\block\RedstoneRepeater; use pocketmine\block\RedstoneTorch; use pocketmine\block\RedstoneWire; use pocketmine\block\ResinClump; +use pocketmine\block\RespawnAnchor; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\Sapling; use pocketmine\block\SeaPickle; @@ -1754,6 +1755,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::RESIN_CLUMP) ->writeFacingFlags($block->getFaces()); }); + $this->map(Blocks::RESPAWN_ANCHOR(), function(RespawnAnchor $block) : Writer{ + return Writer::create(Ids::RESPAWN_ANCHOR) + ->writeInt(StateNames::RESPAWN_ANCHOR_CHARGE, $block->getCharges()); + }); $this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH))); $this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index ed45a47d3..c55fde77a 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -1717,6 +1717,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapStairs(Ids::RESIN_BRICK_STAIRS, fn() => Blocks::RESIN_BRICK_STAIRS()); $this->map(Ids::RESIN_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RESIN_BRICK_WALL(), $in)); $this->map(Ids::RESIN_CLUMP, fn(Reader $in) => Blocks::RESIN_CLUMP()->setFaces($in->readFacingFlags())); + $this->map(Ids::RESPAWN_ANCHOR, function(Reader $in) : Block{ + return Blocks::RESPAWN_ANCHOR() + ->setCharges($in->readBoundedInt(StateNames::RESPAWN_ANCHOR_CHARGE, 0, 4)); + }); $this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB()); $this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS()); $this->map(Ids::SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::SANDSTONE_WALL(), $in)); diff --git a/src/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php index afaeb6769..74c7664bf 100644 --- a/src/entity/object/EndCrystal.php +++ b/src/entity/object/EndCrystal.php @@ -129,7 +129,7 @@ class EndCrystal extends Entity implements Explosive{ $ev = new EntityPreExplodeEvent($this, 6); $ev->call(); if(!$ev->isCancelled()){ - $explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this); + $explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this, $ev->getFireChance()); if($ev->isBlockBreaking()){ $explosion->explodeA(); } diff --git a/src/entity/object/PrimedTNT.php b/src/entity/object/PrimedTNT.php index af3c97922..f3f299d70 100644 --- a/src/entity/object/PrimedTNT.php +++ b/src/entity/object/PrimedTNT.php @@ -121,7 +121,7 @@ class PrimedTNT extends Entity implements Explosive{ $ev->call(); if(!$ev->isCancelled()){ //TODO: deal with underwater TNT (underwater TNT treats water as if it has a blast resistance of 0) - $explosion = new Explosion(Position::fromObject($this->location->add(0, $this->size->getHeight() / 2, 0), $this->getWorld()), $ev->getRadius(), $this); + $explosion = new Explosion(Position::fromObject($this->location->add(0, $this->size->getHeight() / 2, 0), $this->getWorld()), $ev->getRadius(), $this, $ev->getFireChance()); if($ev->isBlockBreaking()){ $explosion->explodeA(); } diff --git a/src/event/block/BlockExplodeEvent.php b/src/event/block/BlockExplodeEvent.php new file mode 100644 index 000000000..a8501d475 --- /dev/null +++ b/src/event/block/BlockExplodeEvent.php @@ -0,0 +1,122 @@ + 100.0){ + throw new \InvalidArgumentException("Yield must be in range 0.0 - 100.0"); + } + } + + public function getPosition() : Position{ + return $this->position; + } + + /** + * Returns the percentage chance of drops from each block destroyed by the explosion. + * + * @return float 0-100 + */ + public function getYield() : float{ + return $this->yield; + } + + /** + * Sets the percentage chance of drops from each block destroyed by the explosion. + * + * @param float $yield 0-100 + */ + public function setYield(float $yield) : void{ + Utils::checkFloatNotInfOrNaN("yield", $yield); + if($yield < 0.0 || $yield > 100.0){ + throw new \InvalidArgumentException("Yield must be in range 0.0 - 100.0"); + } + $this->yield = $yield; + } + + /** + * Returns a list of blocks destroyed by the explosion. + * + * @return Block[] + */ + public function getAffectedBlocks() : array{ + return $this->blocks; + } + + /** + * Sets the blocks destroyed by the explosion. + * + * @param Block[] $blocks + */ + public function setAffectedBlocks(array $blocks) : void{ + Utils::validateArrayValueType($blocks, fn(Block $block) => null); + $this->blocks = $blocks; + } + + /** + * Returns a list of affected blocks that will be replaced by fire. + * + * @return Block[] + */ + public function getIgnitions() : array{ + return $this->ignitions; + } + + /** + * Set the list of blocks that will be replaced by fire. + * + * @param Block[] $ignitions + */ + public function setIgnitions(array $ignitions) : void{ + Utils::validateArrayValueType($ignitions, fn(Block $block) => null); + $this->ignitions = $ignitions; + } +} diff --git a/src/event/block/BlockPreExplodeEvent.php b/src/event/block/BlockPreExplodeEvent.php new file mode 100644 index 000000000..f41cb8a63 --- /dev/null +++ b/src/event/block/BlockPreExplodeEvent.php @@ -0,0 +1,129 @@ + 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } + parent::__construct($block); + } + + public function getRadius() : float{ + return $this->radius; + } + + public function setRadius(float $radius) : void{ + Utils::checkFloatNotInfOrNaN("radius", $radius); + if($radius <= 0){ + throw new \InvalidArgumentException("Explosion radius must be positive"); + } + $this->radius = $radius; + } + + public function isBlockBreaking() : bool{ + return $this->blockBreaking; + } + + public function setBlockBreaking(bool $affectsBlocks) : void{ + $this->blockBreaking = $affectsBlocks; + } + + /** + * Returns whether the explosion will create a fire. + */ + public function isIncendiary() : bool{ + return $this->fireChance > 0; + } + + /** + * Sets whether the explosion will create a fire by filling fireChance with default values. + * + * If $incendiary is true, the fire chance will be filled only if explosion isn't currently creating a fire (if fire chance is 0). + */ + public function setIncendiary(bool $incendiary) : void{ + if(!$incendiary){ + $this->fireChance = 0; + }elseif($this->fireChance <= 0){ + $this->fireChance = Explosion::DEFAULT_FIRE_CHANCE; + } + } + + /** + * Returns a chance between 0 and 1 of creating a fire. + */ + public function getFireChance() : float{ + return $this->fireChance; + } + + /** + * Sets a chance between 0 and 1 of creating a fire. + * For example, if the chance is 1/3, then that amount of affected blocks will be ignited. + * + * @param float $fireChance 0 ... 1 + */ + public function setFireChance(float $fireChance) : void{ + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } + $this->fireChance = $fireChance; + } + + /** + * Returns the player who triggered the block explosion. + * Returns null if the block was exploded by other means. + */ + public function getPlayer() : ?Player{ + return $this->player; + } +} diff --git a/src/event/entity/EntityExplodeEvent.php b/src/event/entity/EntityExplodeEvent.php index 0a0e4f696..c1750ccb3 100644 --- a/src/event/entity/EntityExplodeEvent.php +++ b/src/event/entity/EntityExplodeEvent.php @@ -43,13 +43,15 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{ /** * @param Block[] $blocks - * @param float $yield 0-100 + * @param float $yield 0-100 + * @param Block[] $ignitions */ public function __construct( Entity $entity, protected Position $position, protected array $blocks, - protected float $yield + protected float $yield, + private array $ignitions ){ $this->entity = $entity; if($yield < 0.0 || $yield > 100.0){ @@ -98,4 +100,23 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{ } $this->yield = $yield; } + + /** + * Set the list of blocks that will be replaced by fire. + * + * @param Block[] $ignitions + */ + public function setIgnitions(array $ignitions) : void{ + Utils::validateArrayValueType($ignitions, fn(Block $block) => null); + $this->ignitions = $ignitions; + } + + /** + * Returns a list of affected blocks that will be replaced by fire. + * + * @return Block[] + */ + public function getIgnitions() : array{ + return $this->ignitions; + } } diff --git a/src/event/entity/EntityPreExplodeEvent.php b/src/event/entity/EntityPreExplodeEvent.php index f02a85ecd..c88e83304 100644 --- a/src/event/entity/EntityPreExplodeEvent.php +++ b/src/event/entity/EntityPreExplodeEvent.php @@ -26,6 +26,8 @@ namespace pocketmine\event\entity; use pocketmine\entity\Entity; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\utils\Utils; +use pocketmine\world\Explosion; /** * Called when an entity decides to explode, before the explosion's impact is calculated. @@ -42,11 +44,16 @@ class EntityPreExplodeEvent extends EntityEvent implements Cancellable{ public function __construct( Entity $entity, - protected float $radius + protected float $radius, + private float $fireChance = 0.0, ){ if($radius <= 0){ throw new \InvalidArgumentException("Explosion radius must be positive"); } + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be between 0 and 1."); + } $this->entity = $entity; } @@ -61,6 +68,47 @@ class EntityPreExplodeEvent extends EntityEvent implements Cancellable{ $this->radius = $radius; } + /** + * Returns whether the explosion will create a fire. + */ + public function isIncendiary() : bool{ + return $this->fireChance > 0; + } + + /** + * Sets whether the explosion will create a fire by filling fireChance with default values. + * + * If $incendiary is true, the fire chance will be filled only if explosion isn't currently creating a fire (if fire chance is 0). + */ + public function setIncendiary(bool $incendiary) : void{ + if(!$incendiary){ + $this->fireChance = 0; + }elseif($this->fireChance <= 0){ + $this->fireChance = Explosion::DEFAULT_FIRE_CHANCE; + } + } + + /** + * Returns a chance between 0 and 1 of creating a fire. + */ + public function getFireChance() : float{ + return $this->fireChance; + } + + /** + * Sets a chance between 0 and 1 of creating a fire. + * For example, if the chance is 1/3, then that amount of affected blocks will be ignited. + * + * @param float $fireChance 0 ... 1 + */ + public function setFireChance(float $fireChance) : void{ + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be between 0 and 1."); + } + $this->fireChance = $fireChance; + } + public function isBlockBreaking() : bool{ return $this->blockBreaking; } diff --git a/src/event/player/PlayerRespawnAnchorUseEvent.php b/src/event/player/PlayerRespawnAnchorUseEvent.php new file mode 100644 index 000000000..be7697f11 --- /dev/null +++ b/src/event/player/PlayerRespawnAnchorUseEvent.php @@ -0,0 +1,56 @@ +player = $player; + } + + public function getBlock() : Block{ + return $this->block; + } + + public function getAction() : int{ + return $this->action; + } + + public function setAction(int $action) : void{ + $this->action = $action; + } +} diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index a3bd7b872..7a90babed 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -993,6 +993,7 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("resin_brick_wall", fn() => Blocks::RESIN_BRICK_WALL()); $result->registerBlock("resin_bricks", fn() => Blocks::RESIN_BRICKS()); $result->registerBlock("resin_clump", fn() => Blocks::RESIN_CLUMP()); + $result->registerBlock("respawn_anchor", fn() => Blocks::RESPAWN_ANCHOR()); $result->registerBlock("rooted_dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED)); $result->registerBlock("rose", fn() => Blocks::POPPY()); $result->registerBlock("rose_bush", fn() => Blocks::ROSE_BUSH()); diff --git a/src/player/Player.php b/src/player/Player.php index 3c494b980..6429d1281 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -26,6 +26,7 @@ namespace pocketmine\player; use pocketmine\block\BaseSign; use pocketmine\block\Bed; use pocketmine\block\BlockTypeTags; +use pocketmine\block\RespawnAnchor; use pocketmine\block\UnknownBlock; use pocketmine\block\VanillaBlocks; use pocketmine\command\CommandSender; @@ -136,6 +137,7 @@ use pocketmine\world\sound\EntityAttackNoDamageSound; use pocketmine\world\sound\EntityAttackSound; use pocketmine\world\sound\FireExtinguishSound; use pocketmine\world\sound\ItemBreakSound; +use pocketmine\world\sound\RespawnAnchorDepleteSound; use pocketmine\world\sound\Sound; use pocketmine\world\World; use pocketmine\YmlServerProperties; @@ -2538,6 +2540,21 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } $this->logger->debug("Respawn position located, completing respawn"); $ev = new PlayerRespawnEvent($this, $safeSpawn); + $spawnPosition = $ev->getRespawnPosition(); + $spawnBlock = $spawnPosition->getWorld()->getBlock($spawnPosition); + if($spawnBlock instanceof RespawnAnchor){ + if($spawnBlock->getCharges() > 0){ + $spawnPosition->getWorld()->setBlock($spawnPosition, $spawnBlock->setCharges($spawnBlock->getCharges() - 1)); + $spawnPosition->getWorld()->addSound($spawnPosition, new RespawnAnchorDepleteSound()); + }else{ + $defaultSpawn = $this->server->getWorldManager()->getDefaultWorld()?->getSpawnLocation(); + if($defaultSpawn !== null){ + $this->setSpawn($defaultSpawn); + $ev->setRespawnPosition($defaultSpawn); + $this->sendMessage(KnownTranslationFactory::tile_respawn_anchor_notValid()->prefix(TextFormat::GRAY)); + } + } + } $ev->call(); $realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getWorld()); diff --git a/src/world/Explosion.php b/src/world/Explosion.php index 601f9109e..9e83d06be 100644 --- a/src/world/Explosion.php +++ b/src/world/Explosion.php @@ -26,16 +26,20 @@ namespace pocketmine\world; use pocketmine\block\Block; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\TNT; +use pocketmine\block\utils\SupportType; use pocketmine\block\VanillaBlocks; use pocketmine\entity\Entity; +use pocketmine\event\block\BlockExplodeEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityExplodeEvent; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Utils; use pocketmine\world\format\SubChunk; use pocketmine\world\particle\HugeExplodeSeedParticle; use pocketmine\world\sound\ExplodeSound; @@ -48,25 +52,36 @@ use function mt_rand; use function sqrt; class Explosion{ + public const DEFAULT_FIRE_CHANCE = 1.0 / 3.0; + private int $rays = 16; public World $world; - /** @var Block[] */ + /** + * @var Block[] + * @phpstan-var array + */ public array $affectedBlocks = []; public float $stepLen = 0.3; + /** @var Block[] */ + private array $fireIgnitions = []; private SubChunkExplorer $subChunkExplorer; public function __construct( public Position $source, public float $radius, - private Entity|Block|null $what = null + private Entity|Block|null $what = null, + private float $fireChance = 0.0 ){ if(!$this->source->isValid()){ throw new \InvalidArgumentException("Position does not have a valid world"); } $this->world = $this->source->getWorld(); - + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } if($radius <= 0){ throw new \InvalidArgumentException("Explosion radius must be greater than 0, got $radius"); } @@ -85,6 +100,7 @@ class Explosion{ $blockFactory = RuntimeBlockStateRegistry::getInstance(); $mRays = $this->rays - 1; + $incendiary = $this->fireChance > 0; for($i = 0; $i < $this->rays; ++$i){ for($j = 0; $j < $this->rays; ++$j){ for($k = 0; $k < $this->rays; ++$k){ @@ -127,7 +143,12 @@ class Explosion{ $_block = $this->world->getBlockAt($vBlockX, $vBlockY, $vBlockZ, true, false); foreach($_block->getAffectedBlocks() as $_affectedBlock){ $_affectedBlockPos = $_affectedBlock->getPosition(); - $this->affectedBlocks[World::blockHash($_affectedBlockPos->x, $_affectedBlockPos->y, $_affectedBlockPos->z)] = $_affectedBlock; + $posHash = World::blockHash($_affectedBlockPos->x, $_affectedBlockPos->y, $_affectedBlockPos->z); + $this->affectedBlocks[$posHash] = $_affectedBlock; + + if($incendiary && Utils::getRandomFloat() <= $this->fireChance){ + $this->fireIgnitions[$posHash] = $_affectedBlock; + } } } } @@ -150,13 +171,32 @@ class Explosion{ $yield = min(100, (1 / $this->radius) * 100); if($this->what instanceof Entity){ - $ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield); + $ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield, $this->fireIgnitions); + + $ev->call(); + if($ev->isCancelled()){ + return false; + } + + $yield = $ev->getYield(); + $this->affectedBlocks = $ev->getBlockList(); + $this->fireIgnitions = $ev->getIgnitions(); + }elseif($this->what instanceof Block){ + $ev = new BlockExplodeEvent( + $this->what, + $this->source, + $this->affectedBlocks, + $yield, + $this->fireIgnitions, + ); + $ev->call(); if($ev->isCancelled()){ return false; }else{ $yield = $ev->getYield(); - $this->affectedBlocks = $ev->getBlockList(); + $this->affectedBlocks = $ev->getAffectedBlocks(); + $this->fireIgnitions = $ev->getIgnitions(); } } @@ -198,8 +238,9 @@ class Explosion{ $air = VanillaItems::AIR(); $airBlock = VanillaBlocks::AIR(); + $fireBlock = VanillaBlocks::FIRE(); - foreach($this->affectedBlocks as $block){ + foreach($this->affectedBlocks as $hash => $block){ $pos = $block->getPosition(); if($block instanceof TNT){ $block->ignite(mt_rand(10, 30)); @@ -212,7 +253,13 @@ class Explosion{ if(($t = $this->world->getTileAt($pos->x, $pos->y, $pos->z)) !== null){ $t->onBlockDestroyed(); //needed to create drops for inventories } - $this->world->setBlockAt($pos->x, $pos->y, $pos->z, $airBlock); + $targetBlock = + isset($this->fireIgnitions[$hash]) && + $block->getSide(Facing::DOWN)->getSupportType(Facing::UP) === SupportType::FULL ? + $fireBlock : + $airBlock; + + $this->world->setBlockAt($pos->x, $pos->y, $pos->z, $targetBlock); } } @@ -221,4 +268,18 @@ class Explosion{ return true; } + + /** + * Sets a chance between 0 and 1 of creating a fire. + * For example, if the chance is 1/3, then that amount of affected blocks will be ignited. + * + * @param float $fireChance 0 ... 1 + */ + public function setFireChance(float $fireChance) : void{ + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } + $this->fireChance = $fireChance; + } } diff --git a/src/world/sound/RespawnAnchorChargeSound.php b/src/world/sound/RespawnAnchorChargeSound.php new file mode 100644 index 000000000..5a5731262 --- /dev/null +++ b/src/world/sound/RespawnAnchorChargeSound.php @@ -0,0 +1,35 @@ + Date: Wed, 28 May 2025 21:00:22 +0100 Subject: [PATCH 039/140] Stem drops seeds according to binomial distribution fixes #6709 we really need a better way to reverse-engineer the chance parameter for these as the wiki just gives a probability table, which is quite tiresome to extract patterns from. --- src/block/Stem.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/block/Stem.php b/src/block/Stem.php index 2ac95aa3f..2b6f2150c 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -25,11 +25,12 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CropGrowthHelper; +use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\math\Facing; use function array_rand; -use function mt_rand; abstract class Stem extends Crops{ protected int $facing = Facing::UP; @@ -90,8 +91,10 @@ abstract class Stem extends Crops{ } public function getDropsForCompatibleTool(Item $item) : array{ + //TODO: bit annoying we have to pass an Item instance here + //this should not be affected by Fortune, but still follows a binomial distribution return [ - $this->asItem()->setCount(mt_rand(0, 2)) + $this->asItem()->setCount(FortuneDropHelper::binomial(VanillaItems::AIR(), 0, chance: ($this->age + 1) / 15)) ]; } } From b40b99fe72cec89b0b785e904cbaff9664c6fb0c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 May 2025 21:32:48 +0100 Subject: [PATCH 040/140] Player: fixed crash on action item return this can happen if the old item had a lower max damage than the new one, and the new one has a damage higher than the old one's max damage. it can also happen if the damage was overridden to some illegal value by a custom item as seen in https://crash.pmmp.io/view/12754811 --- src/player/Player.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/player/Player.php b/src/player/Player.php index 1c67b7182..e0a42ed1d 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1641,7 +1641,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $newReplica = clone $oldHeldItem; $newReplica->setCount($newHeldItem->getCount()); if($newReplica instanceof Durable && $newHeldItem instanceof Durable){ - $newReplica->setDamage($newHeldItem->getDamage()); + $newDamage = $newHeldItem->getDamage(); + if($newDamage >= 0 && $newDamage <= $newReplica->getMaxDurability()){ + $newReplica->setDamage($newDamage); + } } $damagedOrDeducted = $newReplica->equalsExact($newHeldItem); From 035d2dec230d14ec06887e3d49f4526a619fd0c2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 May 2025 22:03:29 +0100 Subject: [PATCH 041/140] LevelDB: make unknown block errors way less annoying these would previously generate a new line for every error. since errors are often repeated for different offsets (e.g. different states of the same block), we can save a lot of spam by deduplicating them and telling which offsets the errors occurred in. --- src/world/format/io/leveldb/LevelDB.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index 6223e66b8..3a64f93f6 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -36,6 +36,7 @@ use pocketmine\nbt\TreeRoot; use pocketmine\utils\Binary; use pocketmine\utils\BinaryDataException; use pocketmine\utils\BinaryStream; +use pocketmine\utils\Utils; use pocketmine\VersionInfo; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\BaseWorldProvider; @@ -204,23 +205,29 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ $blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt); }catch(BlockStateDeserializeException $e){ //while not ideal, this is not a fatal error - $blockDecodeErrors[] = "Palette offset $i / Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $errorMessage = "Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $blockDecodeErrors[$errorMessage][] = $i; $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); continue; } try{ $palette[] = $this->blockStateDeserializer->deserialize($blockStateData); }catch(UnsupportedBlockStateException $e){ - $blockDecodeErrors[] = "Palette offset $i / " . $e->getMessage(); + $blockDecodeErrors[$e->getMessage()][] = $i; $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); }catch(BlockStateDeserializeException $e){ - $blockDecodeErrors[] = "Palette offset $i / Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $errorMessage = "Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $blockDecodeErrors[$errorMessage][] = $i; $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); } } if(count($blockDecodeErrors) > 0){ - $logger->error("Errors decoding blocks:\n - " . implode("\n - ", $blockDecodeErrors)); + $finalErrors = []; + foreach(Utils::promoteKeys($blockDecodeErrors) as $errorMessage => $paletteOffsets){ + $finalErrors[] = "$errorMessage (palette offsets: " . implode(", ", $paletteOffsets) . ")"; + } + $logger->error("Errors decoding blocks:\n - " . implode("\n - ", $finalErrors)); } //TODO: exceptions From 56da492e48bf94cf5da4153f48f6d872ba8c0f21 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 May 2025 22:10:20 +0100 Subject: [PATCH 042/140] World: make less noise about deleted tile entities no need to repeat the same message 100 times --- src/world/World.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 792681a89..5c5e4cfbf 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2982,6 +2982,8 @@ class World implements ChunkManager{ if(count($chunkData->getEntityNBT()) !== 0){ $this->timings->syncChunkLoadEntities->startTiming(); $entityFactory = EntityFactory::getInstance(); + + $deletedEntities = []; foreach($chunkData->getEntityNBT() as $k => $nbt){ try{ $entity = $entityFactory->createFromData($this, $nbt); @@ -2998,18 +3000,23 @@ class World implements ChunkManager{ }elseif($saveIdTag instanceof IntTag){ //legacy MCPE format $saveId = "legacy(" . $saveIdTag->getValue() . ")"; } - $logger->warning("Deleted unknown entity type $saveId"); + $deletedEntities[$saveId] = ($deletedEntities[$saveId] ?? 0) + 1; } //TODO: we can't prevent entities getting added to unloaded chunks if they were saved in the wrong place //here, because entities currently add themselves to the world } + foreach(Utils::promoteKeys($deletedEntities) as $saveId => $count){ + $logger->warning("Deleted unknown entity type $saveId x$count"); + } $this->timings->syncChunkLoadEntities->stopTiming(); } if(count($chunkData->getTileNBT()) !== 0){ $this->timings->syncChunkLoadTileEntities->startTiming(); $tileFactory = TileFactory::getInstance(); + + $deletedTiles = []; foreach($chunkData->getTileNBT() as $k => $nbt){ try{ $tile = $tileFactory->createFromData($this, $nbt); @@ -3019,7 +3026,8 @@ class World implements ChunkManager{ continue; } if($tile === null){ - $logger->warning("Deleted unknown tile entity type " . $nbt->getString("id", "")); + $saveId = $nbt->getString("id", ""); + $deletedTiles[$saveId] = ($deletedTiles[$saveId] ?? 0) + 1; continue; } @@ -3035,6 +3043,10 @@ class World implements ChunkManager{ } } + foreach(Utils::promoteKeys($deletedTiles) as $saveId => $count){ + $logger->warning("Deleted unknown tile entity type $saveId x$count"); + } + $this->timings->syncChunkLoadTileEntities->stopTiming(); } } From 0910a219d4b66d53f1387eea27f25efbc4e34572 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 May 2025 23:29:37 +0100 Subject: [PATCH 043/140] Fixed improper pre-checking of PlayerAuthInputPacket flags --- composer.json | 2 +- composer.lock | 14 +++++++------- src/network/mcpe/handler/InGamePacketHandler.php | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 979973893..d2064bcd6 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", "pocketmine/bedrock-data": "~5.0.0+bedrock-1.21.80", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~38.0.0+bedrock-1.21.80", + "pocketmine/bedrock-protocol": "~38.1.0+bedrock-1.21.80", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 9cb0721fc..2e2e5a600 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": "ceb98091ac3f61f1a4b87708c48dc75a", + "content-hash": "fe62caebfdb35cd8bd57c8e61879b7c0", "packages": [ { "name": "adhocore/json-comment", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "38.0.1+bedrock-1.21.80", + "version": "38.1.0+bedrock-1.21.80", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f" + "reference": "a1fa215563517050045309bb779a67f75843b867" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f", - "reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a1fa215563517050045309bb779a67f75843b867", + "reference": "a1fa215563517050045309bb779a67f75843b867", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/38.0.1+bedrock-1.21.80" + "source": "https://github.com/pmmp/BedrockProtocol/tree/38.1.0+bedrock-1.21.80" }, - "time": "2025-05-17T11:56:33+00:00" + "time": "2025-05-28T22:19:59+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index eec200e4b..927ba38fa 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -211,7 +211,7 @@ class InGamePacketHandler extends PacketHandler{ } $inputFlags = $packet->getInputFlags(); - if($inputFlags !== $this->lastPlayerAuthInputFlags){ + if($this->lastPlayerAuthInputFlags === null || !$inputFlags->equals($this->lastPlayerAuthInputFlags)){ $this->lastPlayerAuthInputFlags = $inputFlags; $sneaking = $inputFlags->get(PlayerAuthInputFlags::SNEAKING); From b4b6bbe29f21754039db11ab8ca7d0758e4b43b5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 29 May 2025 17:18:45 +0100 Subject: [PATCH 044/140] BaseInventory: fixed internalAddItem() setting air slots to air this bug was introduced in #4237, but it was unnoticed due to having no adverse effects other than noisy debugs and network traffic. --- src/inventory/BaseInventory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inventory/BaseInventory.php b/src/inventory/BaseInventory.php index 0d5d1ffe6..c4afda43a 100644 --- a/src/inventory/BaseInventory.php +++ b/src/inventory/BaseInventory.php @@ -256,7 +256,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ $slotItem->setCount($slotItem->getCount() + $amount); $this->setItem($i, $slotItem); if($newItem->getCount() <= 0){ - break; + return $newItem; } } } @@ -270,7 +270,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ $slotItem->setCount($amount); $this->setItem($slotIndex, $slotItem); if($newItem->getCount() <= 0){ - break; + return $newItem; } } } From e99665fb12299519ad50f0da7f08000af9e2bc45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 14:08:14 +0000 Subject: [PATCH 045/140] Bump docker/build-push-action in the github-actions group (#6719) --- .github/workflows/build-docker-image.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index dc282ab71..a3921f820 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.16.0 + uses: docker/build-push-action@v6.18.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.16.0 + uses: docker/build-push-action@v6.18.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.16.0 + uses: docker/build-push-action@v6.18.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.16.0 + uses: docker/build-push-action@v6.18.0 with: push: true context: ./pocketmine-mp From a4ac28592c6c5f5876927aa2a140b65dad63d786 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Jun 2025 15:17:00 +0100 Subject: [PATCH 046/140] Updated dependencies --- composer.json | 2 +- composer.lock | 24 +++++++++++----------- tests/phpstan/configs/actual-problems.neon | 6 ------ tests/phpstan/configs/phpstan-bugs.neon | 12 ----------- 4 files changed, 13 insertions(+), 31 deletions(-) diff --git a/composer.json b/composer.json index d2064bcd6..1935bc290 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.16", + "phpstan/phpstan": "2.1.17", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index 2e2e5a600..cb60c7ace 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": "fe62caebfdb35cd8bd57c8e61879b7c0", + "content-hash": "69921783f476a0704fa1f8924b901a89", "packages": [ { "name": "adhocore/json-comment", @@ -1038,16 +1038,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", "shasum": "" }, "require": { @@ -1090,9 +1090,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-05-31T08:24:38+00:00" }, { "name": "phar-io/manifest", @@ -1214,16 +1214,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.16", + "version": "2.1.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9" + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", "shasum": "" }, "require": { @@ -1268,7 +1268,7 @@ "type": "github" } ], - "time": "2025-05-16T09:40:10+00:00" + "time": "2025-05-21T20:55:28+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index d3adde422..39ae4948c 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -702,12 +702,6 @@ parameters: count: 1 path: ../../../src/inventory/transaction/InventoryTransaction.php - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 2 - path: ../../../src/item/Item.php - - message: '#^Parameter \#1 \$buffer of method pocketmine\\nbt\\BaseNbtSerializer\:\:read\(\) expects string, mixed given\.$#' identifier: argument.type diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index aeb3fae29..75b1e82a7 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -18,12 +18,6 @@ parameters: count: 1 path: ../../../src/Server.php - - - message: '#^Method pocketmine\\block\\Block\:\:readStateFromWorld\(\) is marked as impure but does not have any side effects\.$#' - identifier: impureMethod.pure - count: 1 - path: ../../../src/block/Block.php - - message: '#^Method pocketmine\\block\\DoubleTallGrass\:\:traitGetDropsForIncompatibleTool\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -252,9 +246,3 @@ parameters: count: 2 path: ../../phpunit/promise/PromiseTest.php - - - message: '#^Strict comparison using \=\=\= between 0 and 0 will always evaluate to true\.$#' - identifier: identical.alwaysTrue - count: 1 - path: ../rules/UnsafeForeachArrayOfStringRule.php - From 5ebbcd5d33286d37f311a1c9f8a69100dcaf87e9 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Mon, 2 Jun 2025 15:24:25 +0100 Subject: [PATCH 047/140] Move to newer systems for movement and block break handling (#6718) MS is due to remove the non-server-auth versions of all of this stuff. Fortunately v3 server auth movement works just fine without any changes, although we will need to start sending player tick in some packets if someone wants to actually use the rewind stuff. --- src/network/mcpe/InventoryManager.php | 19 +++-- .../mcpe/handler/InGamePacketHandler.php | 85 +++++++++++-------- .../mcpe/handler/ItemStackRequestExecutor.php | 26 +++++- .../mcpe/handler/PreSpawnPacketHandler.php | 2 +- 4 files changed, 89 insertions(+), 43 deletions(-) diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 2ff23a73a..19bd94fce 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -41,6 +41,7 @@ use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\item\enchantment\EnchantingOption; use pocketmine\item\enchantment\EnchantmentInstance; +use pocketmine\item\Item; use pocketmine\network\mcpe\cache\CreativeInventoryCache; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; @@ -228,17 +229,25 @@ class InventoryManager{ return null; } - private function addPredictedSlotChange(Inventory $inventory, int $slot, ItemStack $item) : void{ + private function addPredictedSlotChangeInternal(Inventory $inventory, int $slot, ItemStack $item) : void{ $this->inventories[spl_object_id($inventory)]->predictions[$slot] = $item; } - public function addTransactionPredictedSlotChanges(InventoryTransaction $tx) : void{ + public function addPredictedSlotChange(Inventory $inventory, int $slot, Item $item) : void{ $typeConverter = $this->session->getTypeConverter(); + $itemStack = $typeConverter->coreItemStackToNet($item); + $this->addPredictedSlotChangeInternal($inventory, $slot, $itemStack); + } + + public function addTransactionPredictedSlotChanges(InventoryTransaction $tx) : void{ foreach($tx->getActions() as $action){ if($action instanceof SlotChangeAction){ //TODO: ItemStackRequestExecutor can probably build these predictions with much lower overhead - $itemStack = $typeConverter->coreItemStackToNet($action->getTargetItem()); - $this->addPredictedSlotChange($action->getInventory(), $action->getSlot(), $itemStack); + $this->addPredictedSlotChange( + $action->getInventory(), + $action->getSlot(), + $action->getTargetItem() + ); } } } @@ -267,7 +276,7 @@ class InventoryManager{ } [$inventory, $slot] = $info; - $this->addPredictedSlotChange($inventory, $slot, $action->newItem->getItemStack()); + $this->addPredictedSlotChangeInternal($inventory, $slot, $action->newItem->getItemStack()); } } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index eec200e4b..c2ca3cb5c 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -136,6 +136,8 @@ class InGamePacketHandler extends PacketHandler{ protected ?float $lastPlayerAuthInputPitch = null; protected ?BitSet $lastPlayerAuthInputFlags = null; + protected ?BlockPosition $lastBlockAttacked = null; + public bool $forceMoveSync = false; protected ?string $lastRequestedFullSkinId = null; @@ -248,6 +250,28 @@ class InGamePacketHandler extends PacketHandler{ $packetHandled = true; + $useItemTransaction = $packet->getItemInteractionData(); + if($useItemTransaction !== null){ + if(count($useItemTransaction->getTransactionData()->getActions()) > 100){ + throw new PacketHandlingException("Too many actions in item use transaction"); + } + + $this->inventoryManager->setCurrentItemStackRequestId($useItemTransaction->getRequestId()); + $this->inventoryManager->addRawPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions()); + if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){ + $packetHandled = false; + $this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")"); + }else{ + $this->inventoryManager->syncMismatchedPredictedSlotChanges(); + } + $this->inventoryManager->setCurrentItemStackRequestId(null); + } + + $itemStackRequest = $packet->getItemStackRequest(); + $itemStackResponseBuilder = $itemStackRequest !== null ? $this->handleSingleItemStackRequest($itemStackRequest) : null; + + //itemstack request or transaction may set predictions for the outcome of these actions, so these need to be + //processed last $blockActions = $packet->getBlockActions(); if($blockActions !== null){ if(count($blockActions) > 100){ @@ -268,27 +292,9 @@ class InGamePacketHandler extends PacketHandler{ } } - $useItemTransaction = $packet->getItemInteractionData(); - if($useItemTransaction !== null){ - if(count($useItemTransaction->getTransactionData()->getActions()) > 100){ - throw new PacketHandlingException("Too many actions in item use transaction"); - } - - $this->inventoryManager->setCurrentItemStackRequestId($useItemTransaction->getRequestId()); - $this->inventoryManager->addRawPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions()); - if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){ - $packetHandled = false; - $this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")"); - }else{ - $this->inventoryManager->syncMismatchedPredictedSlotChanges(); - } - $this->inventoryManager->setCurrentItemStackRequestId(null); - } - - $itemStackRequest = $packet->getItemStackRequest(); if($itemStackRequest !== null){ - $result = $this->handleSingleItemStackRequest($itemStackRequest); - $this->session->sendDataPacket(ItemStackResponsePacket::create([$result])); + $itemStackResponse = $itemStackResponseBuilder?->build() ?? new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $itemStackRequest->getRequestId()); + $this->session->sendDataPacket(ItemStackResponsePacket::create([$itemStackResponse])); } return $packetHandled; @@ -498,13 +504,6 @@ class InGamePacketHandler extends PacketHandler{ //if only the client would tell us what blocks it thinks changed... $this->syncBlocksNearby($vBlockPos, $data->getFace()); return true; - case UseItemTransactionData::ACTION_BREAK_BLOCK: - $blockPos = $data->getBlockPosition(); - $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); - if(!$this->player->breakBlock($vBlockPos)){ - $this->syncBlocksNearby($vBlockPos, null); - } - return true; case UseItemTransactionData::ACTION_CLICK_AIR: if($this->player->isUsingItem()){ if(!$this->player->consumeHeldItem()){ @@ -580,7 +579,7 @@ class InGamePacketHandler extends PacketHandler{ return false; } - private function handleSingleItemStackRequest(ItemStackRequest $request) : ItemStackResponse{ + private function handleSingleItemStackRequest(ItemStackRequest $request) : ?ItemStackResponseBuilder{ if(count($request->getActions()) > 60){ //recipe book auto crafting can affect all slots of the inventory when consuming inputs or producing outputs //this means there could be as many as 50 CraftingConsumeInput actions or Place (taking the result) actions @@ -597,7 +596,11 @@ class InGamePacketHandler extends PacketHandler{ $executor = new ItemStackRequestExecutor($this->player, $this->inventoryManager, $request); try{ $transaction = $executor->generateInventoryTransaction(); - $result = $this->executeInventoryTransaction($transaction, $request->getRequestId()); + if($transaction !== null){ + $result = $this->executeInventoryTransaction($transaction, $request->getRequestId()); + }else{ + $result = true; //predictions only, just send responses + } }catch(ItemStackRequestProcessException $e){ $result = false; $this->session->getLogger()->debug("ItemStackRequest #" . $request->getRequestId() . " failed: " . $e->getMessage()); @@ -605,10 +608,7 @@ class InGamePacketHandler extends PacketHandler{ $this->inventoryManager->requestSyncAll(); } - if(!$result){ - return new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $request->getRequestId()); - } - return $executor->buildItemStackResponse(); + return $result ? $executor->getItemStackResponseBuilder() : null; } public function handleItemStackRequest(ItemStackRequestPacket $packet) : bool{ @@ -618,7 +618,7 @@ class InGamePacketHandler extends PacketHandler{ throw new PacketHandlingException("Too many requests in ItemStackRequestPacket"); } foreach($packet->getRequests() as $request){ - $responses[] = $this->handleSingleItemStackRequest($request); + $responses[] = $this->handleSingleItemStackRequest($request)?->build() ?? new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $request->getRequestId()); } $this->session->sendDataPacket(ItemStackResponsePacket::create($responses)); @@ -681,16 +681,27 @@ class InGamePacketHandler extends PacketHandler{ switch($action){ case PlayerAction::START_BREAK: + case PlayerAction::CONTINUE_DESTROY_BLOCK: //destroy the next block while holding down left click self::validateFacing($face); + if($this->lastBlockAttacked !== null && $blockPosition->equals($this->lastBlockAttacked)){ + //the client will send CONTINUE_DESTROY_BLOCK for the currently targeted block directly before it + //sends PREDICT_DESTROY_BLOCK, but also when it starts to break the block + //this seems like a bug in the client and would cause spurious left-click events if we allowed it to + //be delivered to the player + $this->session->getLogger()->debug("Ignoring PlayerAction $action on $pos because we were already destroying this block"); + break; + } if(!$this->player->attackBlock($pos, $face)){ $this->syncBlocksNearby($pos, $face); } + $this->lastBlockAttacked = $blockPosition; break; case PlayerAction::ABORT_BREAK: case PlayerAction::STOP_BREAK: $this->player->stopBreakBlock($pos); + $this->lastBlockAttacked = null; break; case PlayerAction::START_SLEEPING: //unused @@ -701,11 +712,17 @@ class InGamePacketHandler extends PacketHandler{ case PlayerAction::CRACK_BREAK: self::validateFacing($face); $this->player->continueBreakBlock($pos, $face); + $this->lastBlockAttacked = $blockPosition; break; case PlayerAction::INTERACT_BLOCK: //TODO: ignored (for now) break; case PlayerAction::CREATIVE_PLAYER_DESTROY_BLOCK: //TODO: do we need to handle this? + case PlayerAction::PREDICT_DESTROY_BLOCK: + if(!$this->player->breakBlock($pos)){ + $this->syncBlocksNearby($pos, $face); + } + $this->lastBlockAttacked = null; break; case PlayerAction::START_ITEM_USE_ON: case PlayerAction::STOP_ITEM_USE_ON: diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 6db8f1e12..d71a1c6bf 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -33,9 +33,11 @@ use pocketmine\inventory\transaction\EnchantingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionBuilder; use pocketmine\inventory\transaction\TransactionBuilderInventory; +use pocketmine\item\Durable; use pocketmine\item\Item; use pocketmine\network\mcpe\InventoryManager; use pocketmine\network\mcpe\protocol\types\inventory\ContainerUIIds; +use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftingConsumeInputStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftingCreateSpecificResultStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftRecipeAutoStackRequestAction; @@ -47,6 +49,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\DropStackReque use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequest; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequestSlotInfo; +use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\MineBlockStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\PlaceStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\SwapStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\TakeStackRequestAction; @@ -362,6 +365,16 @@ class ItemStackRequestExecutor{ $this->setNextCreatedItem($nextResultItem); }elseif($action instanceof DeprecatedCraftingResultsStackRequestAction){ //no obvious use + }elseif($action instanceof MineBlockStackRequestAction){ + $slot = $action->getHotbarSlot(); + $this->requestSlotInfos[] = new ItemStackRequestSlotInfo(new FullContainerName(ContainerUIIds::HOTBAR), $slot, $action->getStackId()); + $inventory = $this->player->getInventory(); + $usedItem = $inventory->slotExists($slot) ? $inventory->getItem($slot) : null; + $predictedDamage = $action->getPredictedDurability(); + if($usedItem instanceof Durable && $predictedDamage >= 0 && $predictedDamage <= $usedItem->getMaxDurability()){ + $usedItem->setDamage($predictedDamage); + $this->inventoryManager->addPredictedSlotChange($inventory, $slot, $usedItem); + } }else{ throw new ItemStackRequestProcessException("Unhandled item stack request action"); } @@ -370,7 +383,7 @@ class ItemStackRequestExecutor{ /** * @throws ItemStackRequestProcessException */ - public function generateInventoryTransaction() : InventoryTransaction{ + public function generateInventoryTransaction() : ?InventoryTransaction{ foreach(Utils::promoteKeys($this->request->getActions()) as $k => $action){ try{ $this->processItemStackRequestAction($action); @@ -380,6 +393,9 @@ class ItemStackRequestExecutor{ } $this->setNextCreatedItem(null); $inventoryActions = $this->builder->generateActions(); + if(count($inventoryActions) === 0){ + return null; + } $transaction = $this->specialTransaction ?? new InventoryTransaction($this->player); foreach($inventoryActions as $action){ @@ -389,12 +405,16 @@ class ItemStackRequestExecutor{ return $transaction; } - public function buildItemStackResponse() : ItemStackResponse{ + public function getItemStackResponseBuilder() : ItemStackResponseBuilder{ $builder = new ItemStackResponseBuilder($this->request->getRequestId(), $this->inventoryManager); foreach($this->requestSlotInfos as $requestInfo){ $builder->addSlot($requestInfo->getContainerName()->getContainerId(), $requestInfo->getSlotId()); } - return $builder->build(); + return $builder; + } + + public function buildItemStackResponse() : ItemStackResponse{ + return $this->getItemStackResponseBuilder()->build(); } } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 9aa302c0c..161a679d6 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -99,7 +99,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->server->getMotd(), "", false, - new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false), + new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V3, 0, true), 0, 0, "", From 4c3a2ef46ed15dbdccec415d78603a23fea41fb1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 8 Jun 2025 18:44:37 +0100 Subject: [PATCH 048/140] Update dependencies (minor-next) --- composer.json | 4 +-- composer.lock | 83 ++++++++++++++++++++++----------------------------- 2 files changed, 37 insertions(+), 50 deletions(-) diff --git a/composer.json b/composer.json index 8c56bbf81..e65a6fb8e 100644 --- a/composer.json +++ b/composer.json @@ -45,10 +45,10 @@ "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.1.0", - "pocketmine/raklib": "~1.1.2", + "pocketmine/raklib": "~1.2.0", "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", - "ramsey/uuid": "~4.7.0", + "ramsey/uuid": "~4.8.0", "symfony/filesystem": "~6.4.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 10fb2a6ab..c01e7a299 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": "3db02308c5b44b94e1c990d60ffd2f28", + "content-hash": "7c3052613e98e566d8b00ae3c9119057", "packages": [ { "name": "adhocore/json-comment", @@ -67,16 +67,16 @@ }, { "name": "brick/math", - "version": "0.12.3", + "version": "0.13.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", "shasum": "" }, "require": { @@ -115,7 +115,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.3" + "source": "https://github.com/brick/math/tree/0.13.1" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2025-03-29T13:50:30+00:00" }, { "name": "netresearch/jsonmapper", @@ -618,16 +618,16 @@ }, { "name": "pocketmine/raklib", - "version": "1.1.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/pmmp/RakLib.git", - "reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f" + "reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/RakLib/zipball/4145a31cd812fe8931c3c9c691fcd2ded2f47e7f", - "reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f", + "url": "https://api.github.com/repos/pmmp/RakLib/zipball/a28d05216d34dbd00e8aed827a58df6b4c11510b", + "reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b", "shasum": "" }, "require": { @@ -655,9 +655,9 @@ "description": "A RakNet server implementation written in PHP", "support": { "issues": "https://github.com/pmmp/RakLib/issues", - "source": "https://github.com/pmmp/RakLib/tree/1.1.2" + "source": "https://github.com/pmmp/RakLib/tree/1.2.0" }, - "time": "2025-04-06T03:38:21+00:00" + "time": "2025-06-08T17:36:06+00:00" }, { "name": "pocketmine/raklib-ipc", @@ -818,20 +818,20 @@ }, { "name": "ramsey/uuid", - "version": "4.7.6", + "version": "4.8.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", + "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", "ext-json": "*", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" @@ -840,26 +840,23 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.10", + "captainhook/captainhook": "^5.25", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -894,19 +891,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.6" + "source": "https://github.com/ramsey/uuid/tree/4.8.1" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" - } - ], - "time": "2024-04-27T21:32:50+00:00" + "time": "2025-06-01T06:28:46+00:00" }, { "name": "symfony/filesystem", From c3ea6edc220e7dede269fcbf2299fb011137f822 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 8 Jun 2025 18:49:27 +0100 Subject: [PATCH 049/140] Bump minimum PHP version to 8.3 --- .github/workflows/discord-release-notify.yml | 2 +- .github/workflows/draft-release-pr-check.yml | 2 +- .github/workflows/draft-release.yml | 2 +- .github/workflows/main.yml | 2 +- composer.json | 4 ++-- composer.lock | 6 +++--- src/PocketMine.php | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index 93b2978aa..1116946da 100644 --- a/.github/workflows/discord-release-notify.yml +++ b/.github/workflows/discord-release-notify.yml @@ -15,7 +15,7 @@ jobs: - name: Setup PHP and tools uses: shivammathur/setup-php@2.33.0 with: - php-version: 8.2 + php-version: 8.3 - name: Restore Composer package cache uses: actions/cache@v4 diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml index 20b2200e6..de017f585 100644 --- a/.github/workflows/draft-release-pr-check.yml +++ b/.github/workflows/draft-release-pr-check.yml @@ -51,7 +51,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@2.33.0 with: - php-version: 8.2 + php-version: 8.3 - name: Restore Composer package cache uses: actions/cache@v4 diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index fa20d1912..bf23ef5ad 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -18,7 +18,7 @@ on: - "*" env: - PHP_VERSION: "8.2" + PHP_VERSION: "8.3" jobs: skip: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cabda54be..f246f5802 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1", "8.2", "8.3"] + php: ["8.3"] uses: ./.github/workflows/main-php-matrix.yml with: diff --git a/composer.json b/composer.json index 1754ad473..04f08bbeb 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "homepage": "https://pmmp.io", "license": "LGPL-3.0", "require": { - "php": "^8.1", + "php": "^8.3", "php-64bit": "*", "ext-chunkutils2": "^0.3.1", "ext-crypto": "^0.3.1", @@ -77,7 +77,7 @@ }, "config": { "platform": { - "php": "8.1.0" + "php": "8.3.0" }, "sort-packages": true }, diff --git a/composer.lock b/composer.lock index c01e7a299..5eb9e6da3 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": "7c3052613e98e566d8b00ae3c9119057", + "content-hash": "3aa2808cfb5b30b7a4eb481d19d76c7e", "packages": [ { "name": "adhocore/json-comment", @@ -2761,7 +2761,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.1", + "php": "^8.3", "php-64bit": "*", "ext-chunkutils2": "^0.3.1", "ext-crypto": "^0.3.1", @@ -2790,7 +2790,7 @@ }, "platform-dev": {}, "platform-overrides": { - "php": "8.1.0" + "php": "8.3.0" }, "plugin-api-version": "2.6.0" } diff --git a/src/PocketMine.php b/src/PocketMine.php index a71c9768d..04b95dd32 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -55,7 +55,7 @@ namespace pocketmine { require_once __DIR__ . '/VersionInfo.php'; - const MIN_PHP_VERSION = "8.1.0"; + const MIN_PHP_VERSION = "8.3.0"; /** * @param string $message From 215da7e3f41b2ca30a90094518f6cf3e7234e7d8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 8 Jun 2025 18:58:42 +0100 Subject: [PATCH 050/140] PHP 8.3 package bumps --- composer.json | 4 +- composer.lock | 622 ++++++++++++++++++++++++-------------------------- 2 files changed, 298 insertions(+), 328 deletions(-) diff --git a/composer.json b/composer.json index 04f08bbeb..80e111175 100644 --- a/composer.json +++ b/composer.json @@ -49,13 +49,13 @@ "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", "ramsey/uuid": "~4.8.0", - "symfony/filesystem": "~6.4.0" + "symfony/filesystem": "~7.3.0" }, "require-dev": { "phpstan/phpstan": "2.1.17", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", - "phpunit/phpunit": "^10.5.24" + "phpunit/phpunit": "^12.2.1" }, "replace": { "symfony/polyfill-ctype": "*", diff --git a/composer.lock b/composer.lock index 5eb9e6da3..bd651e120 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": "3aa2808cfb5b30b7a4eb481d19d76c7e", + "content-hash": "d9a2346fa3dbb8503ac686e78349e7f0", "packages": [ { "name": "adhocore/json-comment", @@ -897,25 +897,25 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.13", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "symfony/process": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -943,7 +943,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/filesystem/tree/v7.3.0" }, "funding": [ { @@ -959,7 +959,7 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2024-10-25T15:15:23+00:00" } ], "packages-dev": [ @@ -1360,35 +1360,34 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.16", + "version": "12.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + "reference": "9075a8efc66e11bc55c319062e147bdb06777267" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9075a8efc66e11bc55c319062e147bdb06777267", + "reference": "9075a8efc66e11bc55c319062e147bdb06777267", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-text-template": "^3.0.1", - "sebastian/code-unit-reverse-lookup": "^3.0.0", - "sebastian/complexity": "^3.2.0", - "sebastian/environment": "^6.1.0", - "sebastian/lines-of-code": "^2.0.2", - "sebastian/version": "^4.0.1", + "nikic/php-parser": "^5.4.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^10.1" + "phpunit/phpunit": "^12.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -1397,7 +1396,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1.x-dev" + "dev-main": "12.3.x-dev" } }, "autoload": { @@ -1426,40 +1425,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2024-08-22T04:31:57+00:00" + "time": "2025-05-23T15:49:03+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.1.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1487,7 +1498,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" }, "funding": [ { @@ -1495,28 +1506,28 @@ "type": "github" } ], - "time": "2023-08-31T06:24:48+00:00" + "time": "2025-02-07T04:58:37+00:00" }, { "name": "phpunit/php-invoker", - "version": "4.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -1524,7 +1535,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1550,7 +1561,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -1558,32 +1570,32 @@ "type": "github" } ], - "time": "2023-02-03T06:56:09+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "3.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -1610,7 +1622,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -1618,32 +1630,32 @@ "type": "github" } ], - "time": "2023-08-31T14:07:24+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "6.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -1669,7 +1681,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -1677,20 +1690,20 @@ "type": "github" } ], - "time": "2023-02-03T06:57:52+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "10.5.46", + "version": "12.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" + "reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f09fda04e7caea93cff50b4e90319184f3e6ee3", + "reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3", "shasum": "" }, "require": { @@ -1703,26 +1716,22 @@ "myclabs/deep-copy": "^1.13.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-invoker": "^4.0.0", - "phpunit/php-text-template": "^3.0.1", - "phpunit/php-timer": "^6.0.0", - "sebastian/cli-parser": "^2.0.1", - "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.3", - "sebastian/diff": "^5.1.1", - "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", - "sebastian/global-state": "^6.0.2", - "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", - "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.3.0", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.0.0", + "sebastian/comparator": "^7.0.1", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.2", + "sebastian/exporter": "^7.0.0", + "sebastian/global-state": "^8.0.0", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.2", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -1730,7 +1739,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "12.2-dev" } }, "autoload": { @@ -1762,7 +1771,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.1" }, "funding": [ { @@ -1786,32 +1795,32 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:46:24+00:00" + "time": "2025-06-07T05:17:47+00:00" }, { "name": "sebastian/cli-parser", - "version": "2.0.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1835,7 +1844,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" }, "funding": [ { @@ -1843,147 +1852,39 @@ "type": "github" } ], - "time": "2024-03-02T07:12:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:43+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2025-02-07T04:53:50+00:00" }, { "name": "sebastian/comparator", - "version": "5.0.3", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b478f34614f934e0291598d0c08cbaba9644bee5", + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2023,7 +1924,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.0.1" }, "funding": [ { @@ -2031,33 +1932,33 @@ "type": "github" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2025-03-07T07:00:32+00:00" }, { "name": "sebastian/complexity", - "version": "3.2.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2081,7 +1982,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -2089,33 +1990,33 @@ "type": "github" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "5.1.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2148,7 +2049,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -2156,27 +2057,27 @@ "type": "github" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "6.1.0", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d364b9e5d0d3b18a2573351a1786fbf96b7e0792", + "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-posix": "*" @@ -2184,7 +2085,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2212,42 +2113,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2025-05-21T15:05:44+00:00" }, { "name": "sebastian/exporter", - "version": "5.1.2", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2290,7 +2203,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" }, "funding": [ { @@ -2298,35 +2211,35 @@ "type": "github" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2025-02-07T04:56:42+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.2", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2352,7 +2265,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" }, "funding": [ { @@ -2360,33 +2273,33 @@ "type": "github" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2025-02-07T04:56:59+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.2", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -2410,7 +2323,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" }, "funding": [ { @@ -2418,34 +2331,34 @@ "type": "github" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2025-02-07T04:57:28+00:00" }, { "name": "sebastian/object-enumerator", - "version": "5.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2467,7 +2380,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -2475,32 +2389,32 @@ "type": "github" } ], - "time": "2023-02-03T07:08:32+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "3.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2522,7 +2436,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -2530,32 +2445,32 @@ "type": "github" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "5.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2585,7 +2500,8 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" }, "funding": [ { @@ -2593,32 +2509,32 @@ "type": "github" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-02-07T05:00:01+00:00" }, { "name": "sebastian/type", - "version": "4.0.0", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069", + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2641,7 +2557,8 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.2" }, "funding": [ { @@ -2649,29 +2566,29 @@ "type": "github" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2025-03-18T13:37:31+00:00" }, { "name": "sebastian/version", - "version": "4.0.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2694,7 +2611,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -2702,7 +2620,59 @@ "type": "github" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" }, { "name": "theseer/tokenizer", From 48b80ecf78400f8644ab6f67e40aac702ffd65d1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 8 Jun 2025 19:01:11 +0100 Subject: [PATCH 051/140] Change crashdump file name format this has bothered me for ages since it sorts into some absurd order by default due to the name starting with the day of the week. this way it'll ensure that the files are always alphanumerically ordered, which means the most recent crashdump should always be at the bottom. --- src/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server.php b/src/Server.php index 679a0ef0b..d6f0a8415 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1618,7 +1618,7 @@ class Server{ if(!is_dir($crashFolder)){ mkdir($crashFolder); } - $crashDumpPath = Path::join($crashFolder, date("D_M_j-H.i.s-T_Y", (int) $dump->getData()->time) . ".log"); + $crashDumpPath = Path::join($crashFolder, date("Y-m-d_H.i.s_T", (int) $dump->getData()->time) . ".log"); $fp = @fopen($crashDumpPath, "wb"); if(!is_resource($fp)){ From 6b5ff5016eaf49064479b3500621bf846304a35a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 8 Jun 2025 19:09:12 +0100 Subject: [PATCH 052/140] Fixed double loading of PluginDescription closes #4593 closes #6723 --- src/plugin/FolderPluginLoader.php | 7 ++----- src/plugin/PharPluginLoader.php | 7 ++----- src/plugin/PluginLoader.php | 2 +- src/plugin/PluginManager.php | 2 +- src/plugin/ScriptPluginLoader.php | 2 +- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/plugin/FolderPluginLoader.php b/src/plugin/FolderPluginLoader.php index 73f6b8841..b881b117b 100644 --- a/src/plugin/FolderPluginLoader.php +++ b/src/plugin/FolderPluginLoader.php @@ -41,11 +41,8 @@ class FolderPluginLoader implements PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void{ - $description = $this->getPluginDescription($path); - if($description !== null){ - $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); - } + public function loadPlugin(string $path, PluginDescription $description) : void{ + $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); } /** diff --git a/src/plugin/PharPluginLoader.php b/src/plugin/PharPluginLoader.php index 1ef8f2b84..865ab2edc 100644 --- a/src/plugin/PharPluginLoader.php +++ b/src/plugin/PharPluginLoader.php @@ -42,11 +42,8 @@ class PharPluginLoader implements PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void{ - $description = $this->getPluginDescription($path); - if($description !== null){ - $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); - } + public function loadPlugin(string $path, PluginDescription $description) : void{ + $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); } /** diff --git a/src/plugin/PluginLoader.php b/src/plugin/PluginLoader.php index d87daf9fc..b29448965 100644 --- a/src/plugin/PluginLoader.php +++ b/src/plugin/PluginLoader.php @@ -36,7 +36,7 @@ interface PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void; + public function loadPlugin(string $path, PluginDescription $description) : void; /** * Gets the PluginDescription from the file diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 3750af3ef..5e4196d7d 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -150,7 +150,7 @@ class PluginManager{ } $prefixed = $loader->getAccessProtocol() . $path; - $loader->loadPlugin($prefixed); + $loader->loadPlugin($prefixed, $description); $mainClass = $description->getMain(); if(!class_exists($mainClass, true)){ diff --git a/src/plugin/ScriptPluginLoader.php b/src/plugin/ScriptPluginLoader.php index 8e45eaab8..1d0e42d53 100644 --- a/src/plugin/ScriptPluginLoader.php +++ b/src/plugin/ScriptPluginLoader.php @@ -46,7 +46,7 @@ class ScriptPluginLoader implements PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void{ + public function loadPlugin(string $path, PluginDescription $description) : void{ include_once $path; } From 9e773ed439deeaf61b1d0153e345a844e0ad8f84 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 8 Jun 2025 19:19:17 +0100 Subject: [PATCH 053/140] PHPUnit migrated to attributes :( --- tests/phpunit/block/BrewingStandTest.php | 10 ++++------ .../command/utils/CommandStringHelperTest.php | 3 ++- .../ConsoleReaderChildProcessUtilsTest.php | 9 +++------ .../block/upgrade/BlockStateUpgraderTest.php | 13 ++++++------- tests/phpunit/event/HandlerListManagerTest.php | 7 +++---- .../item/LegacyStringToItemParserTest.php | 5 ++--- .../mcpe/convert/BlockTranslatorTest.php | 5 ++--- tests/phpunit/plugin/ApiVersionTest.php | 8 +++----- tests/phpunit/scheduler/AsyncPoolTest.php | 4 ++-- .../phpunit/utils/CloningRegistryTraitTest.php | 3 ++- tests/phpunit/utils/ConfigTest.php | 4 ++-- tests/phpunit/utils/UtilsTest.php | 18 +++++++----------- .../format/io/region/RegionLoaderTest.php | 7 +++---- .../io/region/RegionLocationTableEntryTest.php | 5 ++--- 14 files changed, 43 insertions(+), 58 deletions(-) diff --git a/tests/phpunit/block/BrewingStandTest.php b/tests/phpunit/block/BrewingStandTest.php index 85cdd90e1..d36326344 100644 --- a/tests/phpunit/block/BrewingStandTest.php +++ b/tests/phpunit/block/BrewingStandTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\block\utils\BrewingStandSlot; use function count; @@ -39,11 +40,10 @@ class BrewingStandTest extends TestCase{ } /** - * @dataProvider slotsProvider - * * @param BrewingStandSlot[] $slots * @phpstan-param list $slots */ + #[DataProvider("slotsProvider")] public function testHasAndSetSlot(array $slots) : void{ $block = VanillaBlocks::BREWING_STAND(); foreach($slots as $slot){ @@ -62,11 +62,10 @@ class BrewingStandTest extends TestCase{ } /** - * @dataProvider slotsProvider - * * @param BrewingStandSlot[] $slots * @phpstan-param list $slots */ + #[DataProvider("slotsProvider")] public function testGetSlots(array $slots) : void{ $block = VanillaBlocks::BREWING_STAND(); @@ -83,11 +82,10 @@ class BrewingStandTest extends TestCase{ } /** - * @dataProvider slotsProvider - * * @param BrewingStandSlot[] $slots * @phpstan-param list $slots */ + #[DataProvider("slotsProvider")] public function testSetSlots(array $slots) : void{ $block = VanillaBlocks::BREWING_STAND(); diff --git a/tests/phpunit/command/utils/CommandStringHelperTest.php b/tests/phpunit/command/utils/CommandStringHelperTest.php index 047dc7ceb..01a869ad7 100644 --- a/tests/phpunit/command/utils/CommandStringHelperTest.php +++ b/tests/phpunit/command/utils/CommandStringHelperTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\command\utils; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class CommandStringHelperTest extends TestCase{ @@ -47,9 +48,9 @@ class CommandStringHelperTest extends TestCase{ } /** - * @dataProvider parseQuoteAwareProvider * @param string[] $expected */ + #[DataProvider("parseQuoteAwareProvider")] public function testParseQuoteAware(string $commandLine, array $expected) : void{ $actual = CommandStringHelper::parseQuoteAware($commandLine); diff --git a/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php index 31ae2e27a..5b9209861 100644 --- a/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php +++ b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\console; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function mt_rand; use function str_repeat; @@ -40,9 +41,7 @@ final class ConsoleReaderChildProcessUtilsTest extends TestCase{ yield ["give \"Steve\" golden_apple"]; } - /** - * @dataProvider commandStringProvider - */ + #[DataProvider("commandStringProvider")] public function testCreateParseSymmetry(string $input) : void{ $counterCreate = $counterParse = mt_rand(); $message = ConsoleReaderChildProcessUtils::createMessage($input, $counterCreate); @@ -74,9 +73,7 @@ final class ConsoleReaderChildProcessUtilsTest extends TestCase{ yield ["a" . ConsoleReaderChildProcessUtils::TOKEN_DELIMITER . "b", false]; //message with delimiter but not a valid IPC message } - /** - * @dataProvider parseMessageProvider - */ + #[DataProvider("parseMessageProvider")] public static function testParseMessage(string $message, bool $valid) : void{ $counter = $oldCounter = 0; diff --git a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php index 91afd8ed9..9f4416a08 100644 --- a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php +++ b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\block\Block; use pocketmine\data\bedrock\block\BlockStateData; @@ -126,9 +127,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider removePropertyProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("removePropertyProvider")] public function testRemoveProperty(\Closure $getStateData) : void{ $this->prepareRemovePropertySchema($this->getNewSchema()); @@ -151,9 +152,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider renamePropertyProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("renamePropertyProvider")] public function testRenameProperty(\Closure $getStateData, ?int $valueAfter) : void{ $this->prepareRenamePropertySchema($this->getNewSchema()); @@ -187,9 +188,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider remapPropertyValueProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("remapPropertyValueProvider")] public function testRemapPropertyValue(\Closure $getStateData, ?int $valueAfter) : void{ $this->prepareRemapPropertyValueSchema($this->getNewSchema()); @@ -199,9 +200,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider remapPropertyValueProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("remapPropertyValueProvider")] public function testRemapAndRenameProperty(\Closure $getStateData, ?int $valueAfter) : void{ $schema = $this->getNewSchema(); $this->prepareRenamePropertySchema($schema); @@ -239,9 +240,7 @@ class BlockStateUpgraderTest extends TestCase{ yield [0x1_00_00_00, 0x1_00_01_00, false, 1]; //Block newer than schema: block must NOT be altered } - /** - * @dataProvider upgraderVersionCompatibilityProvider - */ + #[DataProvider("upgraderVersionCompatibilityProvider")] public function testUpgraderVersionCompatibility(int $schemaVersion, int $stateVersion, bool $shouldChange, int $schemaCount) : void{ for($i = 0; $i < $schemaCount; $i++){ $schema = $this->getNewSchemaVersion($schemaVersion, $i); diff --git a/tests/phpunit/event/HandlerListManagerTest.php b/tests/phpunit/event/HandlerListManagerTest.php index c61043dab..9a3c8e505 100644 --- a/tests/phpunit/event/HandlerListManagerTest.php +++ b/tests/phpunit/event/HandlerListManagerTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\event; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\event\fixtures\TestAbstractAllowHandleEvent; use pocketmine\event\fixtures\TestAbstractEvent; @@ -63,10 +64,9 @@ class HandlerListManagerTest extends TestCase{ } /** - * @dataProvider isValidClassProvider - * * @phpstan-param \ReflectionClass $class */ + #[DataProvider("isValidClassProvider")] public function testIsValidClass(\ReflectionClass $class, bool $isValid, string $reason) : void{ self::assertSame($isValid, ($this->isValidFunc)($class), $reason); } @@ -83,11 +83,10 @@ class HandlerListManagerTest extends TestCase{ } /** - * @dataProvider resolveParentClassProvider - * * @phpstan-param \ReflectionClass $class * @phpstan-param \ReflectionClass|null $expect */ + #[DataProvider("resolveParentClassProvider")] public function testResolveParentClass(\ReflectionClass $class, ?\ReflectionClass $expect) : void{ if($expect === null){ self::assertNull(($this->resolveParentFunc)($class)); diff --git a/tests/phpunit/item/LegacyStringToItemParserTest.php b/tests/phpunit/item/LegacyStringToItemParserTest.php index f9243df7e..78f9e16b3 100644 --- a/tests/phpunit/item/LegacyStringToItemParserTest.php +++ b/tests/phpunit/item/LegacyStringToItemParserTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\item; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\block\VanillaBlocks; @@ -43,9 +44,7 @@ class LegacyStringToItemParserTest extends TestCase{ ]; } - /** - * @dataProvider itemFromStringProvider - */ + #[DataProvider("itemFromStringProvider")] public function testFromStringSingle(string $string, Item $expected) : void{ $item = LegacyStringToItemParser::getInstance()->parse($string); diff --git a/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php b/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php index ef8e297c4..864acbfeb 100644 --- a/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php +++ b/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php @@ -23,14 +23,13 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\convert; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use pocketmine\block\RuntimeBlockStateRegistry; class BlockTranslatorTest extends TestCase{ - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testAllBlockStatesSerialize() : void{ $blockTranslator = TypeConverter::getInstance()->getBlockTranslator(); foreach(RuntimeBlockStateRegistry::getInstance()->getAllKnownStates() as $state){ diff --git a/tests/phpunit/plugin/ApiVersionTest.php b/tests/phpunit/plugin/ApiVersionTest.php index faa68200d..55afdbc73 100644 --- a/tests/phpunit/plugin/ApiVersionTest.php +++ b/tests/phpunit/plugin/ApiVersionTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\plugin; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function sort; @@ -47,9 +48,7 @@ class ApiVersionTest extends TestCase{ yield ["3.0.0-ALPHA1", "4.0.0-ALPHA1", false]; } - /** - * @dataProvider compatibleApiProvider - */ + #[DataProvider("compatibleApiProvider")] public function testCompatibleApi(string $myVersion, string $wantVersion, bool $expected) : void{ self::assertSame($expected, ApiVersion::isCompatible($myVersion, [$wantVersion]), "my version: $myVersion, their version: $wantVersion, expect " . ($expected ? "yes" : "no")); } @@ -67,11 +66,10 @@ class ApiVersionTest extends TestCase{ } /** - * @dataProvider ambiguousVersionsProvider - * * @param string[] $input * @param string[] $expectedOutput */ + #[DataProvider("ambiguousVersionsProvider")] public function testFindAmbiguousVersions(array $input, array $expectedOutput) : void{ $ambiguous = ApiVersion::checkAmbiguousVersions($input); diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 0f2e8554d..5f026c243 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\scheduler; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use pmmp\thread\ThreadSafeArray; use pocketmine\promise\PromiseResolver; @@ -84,9 +85,8 @@ class AsyncPoolTest extends TestCase{ * * Due to an unset() in the function body, other AsyncTask::__destruct() calls could be triggered during * an AsyncTask's destruction. If done in the wrong way, this could lead to a crash. - * - * @doesNotPerformAssertions This test is checking for a crash condition, not a specific output. */ + #[DoesNotPerformAssertions] public function testTaskDestructorReentrancy() : void{ $this->pool->submitTask(new class extends AsyncTask{ public function __construct(){ diff --git a/tests/phpunit/utils/CloningRegistryTraitTest.php b/tests/phpunit/utils/CloningRegistryTraitTest.php index e3b53ecb5..a343d0033 100644 --- a/tests/phpunit/utils/CloningRegistryTraitTest.php +++ b/tests/phpunit/utils/CloningRegistryTraitTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\utils; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class CloningRegistryTraitTest extends TestCase{ @@ -37,9 +38,9 @@ final class CloningRegistryTraitTest extends TestCase{ } /** - * @dataProvider cloningRegistryMembersProvider * @phpstan-param \Closure() : \stdClass $provider */ + #[DataProvider("cloningRegistryMembersProvider")] public function testEachMemberClone(\Closure $provider) : void{ self::assertNotSame($provider(), $provider(), "Cloning registry should never return the same object twice"); } diff --git a/tests/phpunit/utils/ConfigTest.php b/tests/phpunit/utils/ConfigTest.php index 55b4bc2d4..102a8dcf7 100644 --- a/tests/phpunit/utils/ConfigTest.php +++ b/tests/phpunit/utils/ConfigTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\utils; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function yaml_parse; @@ -60,10 +61,9 @@ class ConfigTest extends TestCase{ } /** - * @dataProvider fixYamlIndexesProvider - * * @param mixed[] $expected */ + #[DataProvider("fixYamlIndexesProvider")] public function testFixYamlIndexes(string $test, array $expected) : void{ $fixed = Config::fixYAMLIndexes($test); $decoded = yaml_parse($fixed); diff --git a/tests/phpunit/utils/UtilsTest.php b/tests/phpunit/utils/UtilsTest.php index f061abac2..96254ca01 100644 --- a/tests/phpunit/utils/UtilsTest.php +++ b/tests/phpunit/utils/UtilsTest.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace pocketmine\utils; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use pocketmine\utils\fixtures\TestAbstractClass; use pocketmine\utils\fixtures\TestInstantiableClass; @@ -52,9 +54,7 @@ class UtilsTest extends TestCase{ ]; } - /** - * @dataProvider parseDocCommentNewlineProvider - */ + #[DataProvider("parseDocCommentNewlineProvider")] public function testParseDocCommentNewlines(string $docComment) : void{ $tags = Utils::parseDocComment($docComment); @@ -76,9 +76,7 @@ class UtilsTest extends TestCase{ ]; } - /** - * @dataProvider parseDocCommentOneLineProvider - */ + #[DataProvider("parseDocCommentOneLineProvider")] public function testParseOneLineDocComment(string $comment) : void{ $tags = Utils::parseDocComment($comment); self::assertArrayHasKey("ignoreCancelled", $tags); @@ -120,11 +118,11 @@ class UtilsTest extends TestCase{ } /** - * @dataProvider validInstanceProvider - * @doesNotPerformAssertions * @phpstan-param class-string $className * @phpstan-param class-string $baseName */ + #[DataProvider("validInstanceProvider")] + #[DoesNotPerformAssertions] public function testValidInstanceWithValidCombinations(string $className, string $baseName) : void{ Utils::testValidInstance($className, $baseName); } @@ -146,9 +144,7 @@ class UtilsTest extends TestCase{ ]; } - /** - * @dataProvider validInstanceInvalidCombinationsProvider - */ + #[DataProvider("validInstanceInvalidCombinationsProvider")] public function testValidInstanceInvalidParameters(string $className, string $baseName) : void{ $this->expectException(\InvalidArgumentException::class); Utils::testValidInstance($className, $baseName); //@phpstan-ignore-line diff --git a/tests/phpunit/world/format/io/region/RegionLoaderTest.php b/tests/phpunit/world/format/io/region/RegionLoaderTest.php index ee96b6bc0..6d715867d 100644 --- a/tests/phpunit/world/format/io/region/RegionLoaderTest.php +++ b/tests/phpunit/world/format/io/region/RegionLoaderTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\format\io\region; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\world\format\ChunkException; use Symfony\Component\Filesystem\Path; @@ -82,11 +83,10 @@ class RegionLoaderTest extends TestCase{ } /** - * @dataProvider outOfBoundsCoordsProvider - * * @throws ChunkException * @throws \InvalidArgumentException */ + #[DataProvider("outOfBoundsCoordsProvider")] public function testWriteChunkOutOfBounds(int $x, int $z) : void{ $this->expectException(\InvalidArgumentException::class); $this->region->writeChunk($x, $z, str_repeat("\x00", 1000)); @@ -107,11 +107,10 @@ class RegionLoaderTest extends TestCase{ } /** - * @dataProvider outOfBoundsCoordsProvider - * * @throws \InvalidArgumentException * @throws \pocketmine\world\format\io\exception\CorruptedChunkException */ + #[DataProvider("outOfBoundsCoordsProvider")] public function testReadChunkOutOfBounds(int $x, int $z) : void{ $this->expectException(\InvalidArgumentException::class); $this->region->readChunk($x, $z); diff --git a/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php b/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php index 498e2767e..7aa67fabc 100644 --- a/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php +++ b/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\format\io\region; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function sprintf; @@ -40,9 +41,7 @@ class RegionLocationTableEntryTest extends TestCase{ yield [new RegionLocationTableEntry(2, 4, 0), new RegionLocationTableEntry(3, 1, 0), true]; } - /** - * @dataProvider overlapDataProvider - */ + #[DataProvider("overlapDataProvider")] public function testOverlap(RegionLocationTableEntry $entry1, RegionLocationTableEntry $entry2, bool $overlaps) : void{ $stringify = function(RegionLocationTableEntry $entry) : string{ return sprintf("entry first=%d last=%d size=%d", $entry->getFirstSector(), $entry->getLastSector(), $entry->getSectorCount()); From 8229ee18126da6e5dbef0632232faddd3d767340 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 11 Jun 2025 21:02:31 +0100 Subject: [PATCH 054/140] ChunkLoader is now a final class (#6730) --- src/player/Player.php | 2 +- src/world/ChunkLoader.php | 2 +- src/world/World.php | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/player/Player.php b/src/player/Player.php index d0003d4b2..762187b24 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -342,7 +342,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4) ** 2) * M_PI); $this->chunkSelector = new ChunkSelector(); - $this->chunkLoader = new class implements ChunkLoader{}; + $this->chunkLoader = new ChunkLoader(); $this->chunkTicker = new ChunkTicker(); $world = $spawnLocation->getWorld(); //load the spawn chunk so we can see the terrain diff --git a/src/world/ChunkLoader.php b/src/world/ChunkLoader.php index 9e909e928..c81c8e3ea 100644 --- a/src/world/ChunkLoader.php +++ b/src/world/ChunkLoader.php @@ -32,6 +32,6 @@ namespace pocketmine\world; * WARNING: When moving this object around in the world or destroying it, * be sure to unregister the loader from chunks you're not using, otherwise you'll leak memory. */ -interface ChunkLoader{ +final class ChunkLoader{ } diff --git a/src/world/World.php b/src/world/World.php index 44c8d3cd7..7264b90e2 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -3321,7 +3321,7 @@ class World implements ChunkManager{ /** @phpstan-var PromiseResolver $resolver */ $resolver = $this->chunkPopulationRequestMap[$chunkHash] = new PromiseResolver(); if($associatedChunkLoader === null){ - $temporaryLoader = new class implements ChunkLoader{}; + $temporaryLoader = new ChunkLoader(); $this->registerChunkLoader($temporaryLoader, $chunkX, $chunkZ); $resolver->getPromise()->onCompletion( fn() => $this->unregisterChunkLoader($temporaryLoader, $chunkX, $chunkZ), @@ -3368,7 +3368,7 @@ class World implements ChunkManager{ return [$resolver, false]; } - $temporaryChunkLoader = new class implements ChunkLoader{}; + $temporaryChunkLoader = new ChunkLoader(); $this->registerChunkLoader($temporaryChunkLoader, $chunkX, $chunkZ); $chunk = $this->loadChunk($chunkX, $chunkZ); $this->unregisterChunkLoader($temporaryChunkLoader, $chunkX, $chunkZ); @@ -3453,8 +3453,7 @@ class World implements ChunkManager{ $chunkPopulationLockId = new ChunkLockId(); - $temporaryChunkLoader = new class implements ChunkLoader{ - }; + $temporaryChunkLoader = new ChunkLoader(); for($xx = -1; $xx <= 1; ++$xx){ for($zz = -1; $zz <= 1; ++$zz){ $this->lockChunk($chunkX + $xx, $chunkZ + $zz, $chunkPopulationLockId); From 95b4db5169db52777b826f3ea804c42c52c42100 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 11 Jun 2025 21:29:03 +0100 Subject: [PATCH 055/140] Fix slow SubChunk garbage collection check, closes #6574 (#6731) --- src/world/format/SubChunk.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/world/format/SubChunk.php b/src/world/format/SubChunk.php index d8546e7e9..cc6673430 100644 --- a/src/world/format/SubChunk.php +++ b/src/world/format/SubChunk.php @@ -134,11 +134,8 @@ class SubChunk{ foreach($this->blockLayers as $layer){ $layer->collectGarbage(); - foreach($layer->getPalette() as $p){ - if($p !== $this->emptyBlockId){ - $cleanedLayers[] = $layer; - continue 2; - } + if($layer->getBitsPerBlock() !== 0 || $layer->get(0, 0, 0) !== $this->emptyBlockId){ + $cleanedLayers[] = $layer; } } $this->blockLayers = $cleanedLayers; From 9c71f4fc1ce6ed1beeff9f55b8b209fa361983cb Mon Sep 17 00:00:00 2001 From: Dries C <15795262+dries-c@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:15:00 +0200 Subject: [PATCH 056/140] Assemble 1.21.90 (#6736) --- changelogs/5.29.md | 25 +++++++ composer.json | 4 +- composer.lock | 26 +++---- src/VersionInfo.php | 4 +- src/data/bedrock/item/ItemTypeNames.php | 1 + .../mcpe/handler/LoginPacketHandler.php | 71 +++++++++++++++++-- .../mcpe/handler/PreSpawnPacketHandler.php | 3 +- .../handler/ResourcePacksPacketHandler.php | 3 +- src/world/format/io/data/BedrockWorldData.php | 4 +- 9 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 changelogs/5.29.md diff --git a/changelogs/5.29.md b/changelogs/5.29.md new file mode 100644 index 000000000..cb6e50da3 --- /dev/null +++ b/changelogs/5.29.md @@ -0,0 +1,25 @@ +# 5.29.0 +Released 18th June 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.90. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.90. +- Removed support for earlier versions. + +## Fixes +- Fixed thread crashes sometimes not reporting proper cause information in crashdumps. +- Fixed crash when a plugin replaced a player's held tool with a different tool with a damage exceeding the old tool's max damage during an action. +- Fixed performance issue of `PlayerAuthInputPacket` input flags handling (broken change detection). +- Fixed `BaseInventory->addItem()` triggering updates on empty slots when no items were added. +- Fixed slow check in `SubChunk` block layer garbage collection. + +## Internals +- `LoginPacketHandler->processLogin()` signature has changed. This will break any plugins overriding `LoginPacketHandler`. As noted above, this is _not_ covered by the API version guarantee. +- Automated branch sync for `minor-next` and `major-next` is now triggered by `repository_dispatch` from a cron job in this repository instead of `RestrictedActions`. The `RestrictedActions` cron job was getting automatically disabled by GitHub due to repo inactivity. diff --git a/composer.json b/composer.json index 1935bc290..1decaac39 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", - "pocketmine/bedrock-data": "~5.0.0+bedrock-1.21.80", + "pocketmine/bedrock-data": "~5.1.0+bedrock-1.21.90", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~38.1.0+bedrock-1.21.80", + "pocketmine/bedrock-protocol": "~39.0.0+bedrock-1.21.90", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index cb60c7ace..b6db8fe7d 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": "69921783f476a0704fa1f8924b901a89", + "content-hash": "bde74cbb65c043a2acf6f62b5b328e67", "packages": [ { "name": "adhocore/json-comment", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "5.0.0+bedrock-1.21.80", + "version": "5.1.0+bedrock-1.21.90", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080" + "reference": "89ed34957aeccc63e517aa849af593adae958e98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e38d5ea19f794ec5216e5f96742237e8c4e7f080", - "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/89ed34957aeccc63e517aa849af593adae958e98", + "reference": "89ed34957aeccc63e517aa849af593adae958e98", "shasum": "" }, "type": "library", @@ -224,9 +224,9 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.80" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.90" }, - "time": "2025-05-09T14:15:18+00:00" + "time": "2025-06-17T23:44:21+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "38.1.0+bedrock-1.21.80", + "version": "39.0.0+bedrock-1.21.90", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "a1fa215563517050045309bb779a67f75843b867" + "reference": "2b088183d12fc003523400867ee398e3893899ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a1fa215563517050045309bb779a67f75843b867", - "reference": "a1fa215563517050045309bb779a67f75843b867", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2b088183d12fc003523400867ee398e3893899ed", + "reference": "2b088183d12fc003523400867ee398e3893899ed", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/38.1.0+bedrock-1.21.80" + "source": "https://github.com/pmmp/BedrockProtocol/tree/39.0.0+bedrock-1.21.90" }, - "time": "2025-05-28T22:19:59+00:00" + "time": "2025-06-17T23:46:38+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 615024656..7033c2707 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.28.3"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.29.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index 5f86cde96..d2ab0996b 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -380,6 +380,7 @@ final class ItemTypeNames{ public const MUSIC_DISC_RELIC = "minecraft:music_disc_relic"; public const MUSIC_DISC_STAL = "minecraft:music_disc_stal"; public const MUSIC_DISC_STRAD = "minecraft:music_disc_strad"; + public const MUSIC_DISC_TEARS = "minecraft:music_disc_tears"; public const MUSIC_DISC_WAIT = "minecraft:music_disc_wait"; public const MUSIC_DISC_WARD = "minecraft:music_disc_ward"; public const MUTTON = "minecraft:mutton"; diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index c15753dad..5c467f2d4 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -33,6 +33,8 @@ use pocketmine\network\mcpe\JwtUtils; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\LoginPacket; use pocketmine\network\mcpe\protocol\types\login\AuthenticationData; +use pocketmine\network\mcpe\protocol\types\login\AuthenticationInfo; +use pocketmine\network\mcpe\protocol\types\login\AuthenticationType; use pocketmine\network\mcpe\protocol\types\login\ClientData; use pocketmine\network\mcpe\protocol\types\login\ClientDataToSkinDataHelper; use pocketmine\network\mcpe\protocol\types\login\JwtChain; @@ -42,7 +44,11 @@ use pocketmine\player\PlayerInfo; use pocketmine\player\XboxLivePlayerInfo; use pocketmine\Server; use Ramsey\Uuid\Uuid; +use function gettype; use function is_array; +use function is_object; +use function json_decode; +use const JSON_THROW_ON_ERROR; /** * Handles the initial login phase of the session. This handler is used as the initial state. @@ -60,7 +66,9 @@ class LoginPacketHandler extends PacketHandler{ ){} public function handleLogin(LoginPacket $packet) : bool{ - $extraData = $this->fetchAuthData($packet->chainDataJwt); + $authInfo = $this->parseAuthInfo($packet->authInfoJson); + $jwtChain = $this->parseJwtChain($authInfo->Certificate); + $extraData = $this->fetchAuthData($jwtChain); if(!Player::isValidUserName($extraData->displayName)){ $this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_invalidName()); @@ -139,11 +147,61 @@ class LoginPacketHandler extends PacketHandler{ return true; } - $this->processLogin($packet, $ev->isAuthRequired()); + $this->processLogin($authInfo->Token, AuthenticationType::from($authInfo->AuthenticationType), $jwtChain->chain, $packet->clientDataJwt, $ev->isAuthRequired()); return true; } + /** + * @throws PacketHandlingException + */ + protected function parseAuthInfo(string $authInfo) : AuthenticationInfo{ + try{ + $authInfoJson = json_decode($authInfo, associative: false, flags: JSON_THROW_ON_ERROR); + }catch(\JsonException $e){ + throw PacketHandlingException::wrap($e); + } + if(!is_object($authInfoJson)){ + throw new \RuntimeException("Unexpected type for auth info data: " . gettype($authInfoJson) . ", expected object"); + } + + $mapper = new \JsonMapper(); + $mapper->bExceptionOnMissingData = true; + $mapper->bExceptionOnUndefinedProperty = true; + $mapper->bStrictObjectTypeChecking = true; + try{ + $clientData = $mapper->map($authInfoJson, new AuthenticationInfo()); + }catch(\JsonMapper_Exception $e){ + throw PacketHandlingException::wrap($e); + } + return $clientData; + } + + /** + * @throws PacketHandlingException + */ + protected function parseJwtChain(string $chainDataJwt) : JwtChain{ + try{ + $jwtChainJson = json_decode($chainDataJwt, associative: false, flags: JSON_THROW_ON_ERROR); + }catch(\JsonException $e){ + throw PacketHandlingException::wrap($e); + } + if(!is_object($jwtChainJson)){ + throw new \RuntimeException("Unexpected type for JWT chain data: " . gettype($jwtChainJson) . ", expected object"); + } + + $mapper = new \JsonMapper(); + $mapper->bExceptionOnMissingData = true; + $mapper->bExceptionOnUndefinedProperty = true; + $mapper->bStrictObjectTypeChecking = true; + try{ + $clientData = $mapper->map($jwtChainJson, new JwtChain()); + }catch(\JsonMapper_Exception $e){ + throw PacketHandlingException::wrap($e); + } + return $clientData; + } + /** * @throws PacketHandlingException */ @@ -211,10 +269,15 @@ class LoginPacketHandler extends PacketHandler{ * TODO: This is separated for the purposes of allowing plugins (like Specter) to hack it and bypass authentication. * In the future this won't be necessary. * + * @param null|string[] $legacyCertificate + * * @throws \InvalidArgumentException */ - protected function processLogin(LoginPacket $packet, bool $authRequired) : void{ - $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($packet->chainDataJwt->chain, $packet->clientDataJwt, $authRequired, $this->authCallback)); + protected function processLogin(string $token, AuthenticationType $authType, ?array $legacyCertificate, string $clientData, bool $authRequired) : void{ + if($legacyCertificate === null){ + throw new PacketHandlingException("Legacy certificate cannot be null"); + } + $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($legacyCertificate, $clientData, $authRequired, $this->authCallback)); $this->session->setHandler(null); //drop packets received during login verification } } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 9aa302c0c..e528a5201 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -40,7 +40,6 @@ use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\LevelSettings; use pocketmine\network\mcpe\protocol\types\NetworkPermissions; use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings; -use pocketmine\network\mcpe\protocol\types\ServerAuthMovementMode; use pocketmine\network\mcpe\protocol\types\SpawnSettings; use pocketmine\player\Player; use pocketmine\Server; @@ -99,7 +98,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->server->getMotd(), "", false, - new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false), + new PlayerMovementSettings(0, false), 0, 0, "", diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index a1df394da..a9ffae6f7 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -120,7 +120,8 @@ class ResourcePacksPacketHandler extends PacketHandler{ hasAddons: false, hasScripts: false, worldTemplateId: Uuid::fromString(Uuid::NIL), - worldTemplateVersion: "" + worldTemplateVersion: "", + forceDisableVibrantVisuals: true, )); $this->session->getLogger()->debug("Waiting for client to accept resource packs"); } diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index d39c17a47..a971920ec 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -51,11 +51,11 @@ use function time; class BedrockWorldData extends BaseNbtWorldData{ public const CURRENT_STORAGE_VERSION = 10; - public const CURRENT_STORAGE_NETWORK_VERSION = 800; + public const CURRENT_STORAGE_NETWORK_VERSION = 818; public const CURRENT_CLIENT_VERSION_TARGET = [ 1, //major 21, //minor - 80, //patch + 90, //patch 3, //revision 0 //is beta ]; From 8843b1b5685a07f626bf9f62be7d0b6ce12411b4 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 00:16:13 +0000 Subject: [PATCH 057/140] 5.29.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/15720776704 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 7033c2707..ec525d6c3 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.29.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.29.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 670d3fb9971c0622666894da359ad2bd0aa34d3d Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 18 Jun 2025 19:29:28 +0100 Subject: [PATCH 058/140] Mention developer team in draft release notification --- .github/workflows/draft-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index fa20d1912..ca48ab8e0 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -188,4 +188,4 @@ jobs: if: github.event_name == 'pull_request_target' uses: thollander/actions-comment-pull-request@v3 with: - message: "[Draft release ${{ steps.get-pm-version.outputs.PM_VERSION }}](${{ steps.create-draft.outputs.html_url }}) has been created for commit ${{ github.sha }}. Please review and publish it." + message: "${{ vars.DRAFT_RELEASE_NOTIFICATION_MENTION }} [Draft release ${{ steps.get-pm-version.outputs.PM_VERSION }}](${{ steps.create-draft.outputs.html_url }}) has been created for commit ${{ github.sha }}. Please review and publish it." From 3643d3aeb81baf8ad2cc4e8b8409e6dd3ab1b5f5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 18 Jun 2025 20:42:10 +0100 Subject: [PATCH 059/140] Ready 5.30.0 release --- changelogs/5.30.md | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 ++-- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.30.md diff --git a/changelogs/5.30.md b/changelogs/5.30.md new file mode 100644 index 000000000..50889df26 --- /dev/null +++ b/changelogs/5.30.md @@ -0,0 +1,55 @@ +# 5.30.0 +Released 18th June 2025. + +This is a minor feature release containing API additions, internals cleanup and user experience improvements. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Significantly reduced log spam when unknown blocks, tiles and entities are found in saved worlds. +- The file name structure for crashdumps has been changed to improve sorting order in file browsers. +- Buffering is now skipped on the RakLib layer. In theory this could reduce player network latency by 10 ms (YMMV). + +## Gameplay +### Blocks +- Many blocks have had their hardness and blast resistance updated to match vanilla. +- Implemented Respawn Anchor. +- Melon Stem and Pumpkin Stem drop amounts should now match vanilla (using binomial distribution). + +## API +## General +- Verification of save registration has been added for blocks, entities and tiles. This is intended to make it easier to find mistakes when registering custom things, which previously would produce obscure core crashes. + +### `pocketmine\event\entity` +- The following classes have been added: + - `EntityExtinguishEvent` - called when a burning entity is extinguished by water or other sources + - `EntityFrostWalkerEvent` - called every tick upon which an entity wearing Frost Walker boots moves; this can be used to customise or cancel the behaviour of the Frost Walker enchantment + +### `pocketmine\entity` +- The following methods have been added: + - `public Entity->getStepHeight() : float` + - `public Entity->setStepHeight(float $stepHeight) : void` + +### `pocketmine\world\generator` +- Generator execution has been decoupled from `PopulationTask` and async tasks in general. The following classes have been added: + - `executor\GeneratorExecutor` + - `executor\SyncGeneratorExecutor` - runs a generator on the main thread (used for flat world generation, which doesn't need threads) + - `executor\AsyncGeneratorExecutor` - runs a generator inside an async task, as before + - `PopulationUtils` - contains population business logic previously baked into `PopulationTask` - this permits the reuse of that logic outside async tasks +- The following methods have signature changes: + - `GeneratorManager->addGenerator()` now accepts an optional `bool $fast` parameter, defaulting to `false`; setting this to `true` will cause your generator to run on the main thread +- The following methods have been added: + - `public GeneratorManagerEntry->isFast() : bool` - returns whether this generator should run on the main thread +- `PopulationTask` has been marked as `@internal`. In the next major version, it will move to the `generator\executor` namespace; however, for now it stays put because plugins currently have no other way to regenerate chunks. + +## Internals +- World data version numbers have been consolidated in `pocketmine\data\bedrock\WorldDataVersions`. This removes the need to modify several different files to support new world versions, and reduces the chances of things getting missed. +- Block hardness and blast resistance is now unit-tested against `block_properties_table.json` in `BedrockData`. This file comes from vanilla BDS, so we can use it to verify compliance. +- Protocol-layer "server auth block breaking" has been enabled. Functionally, this is no different from the previous system, it just works differently on the network layer. +- Various internal classes in the `pocketmine\world\generator` namespace have been moved to the `generator\executor` namespace. +- Removed `World->registerGenerator()` and `World->unregisterGenerator()`. +- Removed redundant calls to `curl_close()` (obsolete since PHP 8.0). diff --git a/src/VersionInfo.php b/src/VersionInfo.php index ec525d6c3..0b6a601bc 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.29.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.30.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 682642087613f7c4ef5b34194253e2cd088fa3ce Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 20:49:31 +0000 Subject: [PATCH 060/140] 5.30.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/15743323722 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 0b6a601bc..e6b323974 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.30.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.30.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 0e511ff783e5a5a3443622835fc775eecaf96c12 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 18 Jun 2025 21:55:53 +0100 Subject: [PATCH 061/140] smh --- changelogs/5.30.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelogs/5.30.md b/changelogs/5.30.md index 50889df26..6e9762ea9 100644 --- a/changelogs/5.30.md +++ b/changelogs/5.30.md @@ -24,11 +24,20 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if ## General - Verification of save registration has been added for blocks, entities and tiles. This is intended to make it easier to find mistakes when registering custom things, which previously would produce obscure core crashes. +### `pocketmine\event\block` +- The following classes have been added: + - `BlockPreExplodeEvent` - called before a block tries to explode + - `BlockExplodeEvent` - called when after a block's explosion calculation has been done, but before any changes are applied + ### `pocketmine\event\entity` - The following classes have been added: - `EntityExtinguishEvent` - called when a burning entity is extinguished by water or other sources - `EntityFrostWalkerEvent` - called every tick upon which an entity wearing Frost Walker boots moves; this can be used to customise or cancel the behaviour of the Frost Walker enchantment +### `pocketmine\event\player` +- The following classes have been added: + - `PlayerRespawnAnchorUseEvent` - called when a player interacts with a charged respawn anchor + ### `pocketmine\entity` - The following methods have been added: - `public Entity->getStepHeight() : float` From 04494e845c8ec9ae174604b6dde6c1cc6c22953a Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 20 Jun 2025 15:26:42 +0100 Subject: [PATCH 062/140] EntityExplodeEvent: Fixed accidental BC break introduced by #6646 thanks @Yexeed --- src/event/entity/EntityExplodeEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/entity/EntityExplodeEvent.php b/src/event/entity/EntityExplodeEvent.php index c1750ccb3..779ab879a 100644 --- a/src/event/entity/EntityExplodeEvent.php +++ b/src/event/entity/EntityExplodeEvent.php @@ -51,7 +51,7 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{ protected Position $position, protected array $blocks, protected float $yield, - private array $ignitions + private array $ignitions = [] ){ $this->entity = $entity; if($yield < 0.0 || $yield > 100.0){ From 258923cc78bc2fc5f9504e450cb3ba43a484d2c3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 21 Jun 2025 23:05:51 +0100 Subject: [PATCH 063/140] World: verify blockstate IDs in setChunk() I think I've finally traced the source of these problems back to BuilderTools setting bad values in async tasks :) --- src/world/World.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/world/World.php b/src/world/World.php index 5c5e4cfbf..ecec3d3b9 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2625,6 +2625,16 @@ class World implements ChunkManager{ } public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk) : void{ + foreach($chunk->getSubChunks() as $subChunk){ + foreach($subChunk->getBlockLayers() as $blockLayer){ + foreach($blockLayer->getPalette() as $blockStateId){ + if(!$this->blockStateRegistry->hasStateId($blockStateId)){ + throw new \InvalidArgumentException("Provided chunk contains unknown/unregistered blocks (found unknown state ID $blockStateId)"); + } + } + } + } + $chunkHash = World::chunkHash($chunkX, $chunkZ); $oldChunk = $this->loadChunk($chunkX, $chunkZ); if($oldChunk !== null && $oldChunk !== $chunk){ From 2a97b4294d90fcd01c520fb016db6ae11fa0159b Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Mon, 23 Jun 2025 18:59:40 +0300 Subject: [PATCH 064/140] Fixed held block placement after respawn anchor explosion (#6742) --- src/block/RespawnAnchor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/RespawnAnchor.php b/src/block/RespawnAnchor.php index f702d240d..e19ea8f6d 100644 --- a/src/block/RespawnAnchor.php +++ b/src/block/RespawnAnchor.php @@ -85,7 +85,7 @@ final class RespawnAnchor extends Opaque{ switch($ev->getAction()){ case PlayerRespawnAnchorUseEvent::ACTION_EXPLODE: $this->explode($player); - return false; + return true; case PlayerRespawnAnchorUseEvent::ACTION_SET_SPAWN: if($player->getSpawn() !== null && $player->getSpawn()->equals($this->position)){ From 177fa7643493a0f2fda13daf9c583e9b6d468609 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Mon, 23 Jun 2025 22:57:33 +0300 Subject: [PATCH 065/140] Disable client-side locator bar (#6743) Without a propper server-side implementation, it just a mess of white dots of nearby players --- src/network/mcpe/handler/PreSpawnPacketHandler.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index ad3cafd39..99f65e78f 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -81,7 +81,8 @@ class PreSpawnPacketHandler extends PacketHandler{ $levelSettings->lightningLevel = 0; $levelSettings->commandsEnabled = true; $levelSettings->gameRules = [ - "naturalregeneration" => new BoolGameRule(false, false) //Hack for client side regeneration + "naturalregeneration" => new BoolGameRule(false, false), //Hack for client side regeneration + "locatorbar" => new BoolGameRule(false, false) //Disable client-side tracking of nearby players ]; $levelSettings->experiments = new Experiments([], false); From cb508f43823db5f5e8c7a1829dddae0f0959bae8 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Mon, 23 Jun 2025 23:30:48 +0300 Subject: [PATCH 066/140] Release 5.30.1 (#6744) --- changelogs/5.30.md | 9 +++++++++ composer.lock | 24 ++++++++++++------------ src/VersionInfo.php | 2 +- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/changelogs/5.30.md b/changelogs/5.30.md index 6e9762ea9..3ecfd285e 100644 --- a/changelogs/5.30.md +++ b/changelogs/5.30.md @@ -62,3 +62,12 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - Various internal classes in the `pocketmine\world\generator` namespace have been moved to the `generator\executor` namespace. - Removed `World->registerGenerator()` and `World->unregisterGenerator()`. - Removed redundant calls to `curl_close()` (obsolete since PHP 8.0). + +# 5.30.1 +Released 23rd June 2025. + +## Fixes +- Fixed accidental break of backwards compatability in `EntityExplodeEvent` introduced in the previous release. +- Fixed placement of player holding block when exploding respawn anchor. +- Updated BedrockProtocol to fix incorrect encoding of `ServerScriptDebugDrawerPacket`. +- Disabled client-side locator bar, allowing plugins to write their own implementations. diff --git a/composer.lock b/composer.lock index e8ca2f08d..2967a967f 100644 --- a/composer.lock +++ b/composer.lock @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "39.0.0+bedrock-1.21.90", + "version": "39.0.1+bedrock-1.21.90", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "2b088183d12fc003523400867ee398e3893899ed" + "reference": "fd231bad0d94024ff50169dc06e8c4fca5aa2eb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2b088183d12fc003523400867ee398e3893899ed", - "reference": "2b088183d12fc003523400867ee398e3893899ed", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/fd231bad0d94024ff50169dc06e8c4fca5aa2eb3", + "reference": "fd231bad0d94024ff50169dc06e8c4fca5aa2eb3", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/39.0.0+bedrock-1.21.90" + "source": "https://github.com/pmmp/BedrockProtocol/tree/39.0.1+bedrock-1.21.90" }, - "time": "2025-06-17T23:46:38+00:00" + "time": "2025-06-23T13:22:50+00:00" }, { "name": "pocketmine/binaryutils", @@ -1681,16 +1681,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.46", + "version": "10.5.47", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" + "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", + "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", "shasum": "" }, "require": { @@ -1762,7 +1762,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.47" }, "funding": [ { @@ -1786,7 +1786,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:46:24+00:00" + "time": "2025-06-20T11:29:11+00:00" }, { "name": "sebastian/cli-parser", diff --git a/src/VersionInfo.php b/src/VersionInfo.php index e6b323974..ee9effeb0 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.30.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From e41551843598411e256f60e7f42f66f57d42f243 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 20:31:58 +0000 Subject: [PATCH 067/140] 5.30.2 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/15834488047 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index ee9effeb0..cb4ee32ca 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.30.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.30.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 40a3ee68dd7ca1ba0121a81abb0441a01f288543 Mon Sep 17 00:00:00 2001 From: ItzxDwi <107537435+ItzxDwi@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:35:32 +0800 Subject: [PATCH 068/140] fix typo in changelog (#6745) --- changelogs/5.30.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.30.md b/changelogs/5.30.md index 3ecfd285e..cc2ecbc1f 100644 --- a/changelogs/5.30.md +++ b/changelogs/5.30.md @@ -67,7 +67,7 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if Released 23rd June 2025. ## Fixes -- Fixed accidental break of backwards compatability in `EntityExplodeEvent` introduced in the previous release. +- Fixed accidental break of backwards compatibility in `EntityExplodeEvent` introduced in the previous release. - Fixed placement of player holding block when exploding respawn anchor. - Updated BedrockProtocol to fix incorrect encoding of `ServerScriptDebugDrawerPacket`. - Disabled client-side locator bar, allowing plugins to write their own implementations. From 3a5432b3162bab3e3119587a913c56d94555cd98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 00:16:12 +0000 Subject: [PATCH 069/140] Bump build/php from `1549433` to `ce1b095` (#6741) --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index 154943379..ce1b095a9 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 15494337976e645499e2e3e8c8b491227522be91 +Subproject commit ce1b095a9c6f47dadc7b5812da4e469d52f272bc From 92c3ce7f02c8eb7412f61fb279373bacd10dc6e4 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 26 Jun 2025 00:10:46 +0100 Subject: [PATCH 070/140] Create copilot-setup-steps.yml --- .github/workflows/copilot-setup-steps.yml | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..a9dc4606c --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,41 @@ +name: "Copilot Agent environment setup" + +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: pmmp/setup-php-action@3.2.0 + with: + php-version: ${{ inputs.php }} + install-path: "./bin" + pm-version-major: ${{ inputs.pm-version-major }} + + - name: Restore Composer package cache + uses: actions/cache@v4 + with: + path: | + ~/.cache/composer/files + ~/.cache/composer/vcs + key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}" + restore-keys: | + composer-v2-cache- + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-interaction From 3176e7549e887748ceb445be4819065c2de1fee9 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 26 Jun 2025 00:11:26 +0100 Subject: [PATCH 071/140] woops --- .github/workflows/copilot-setup-steps.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index a9dc4606c..b777f4ea0 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -23,9 +23,9 @@ jobs: - name: Setup PHP uses: pmmp/setup-php-action@3.2.0 with: - php-version: ${{ inputs.php }} + php-version: 8.3 install-path: "./bin" - pm-version-major: ${{ inputs.pm-version-major }} + pm-version-major: 5 - name: Restore Composer package cache uses: actions/cache@v4 From 6dbd4282cbe23ce389309639c9b4a4181e96ee77 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 26 Jun 2025 00:12:12 +0100 Subject: [PATCH 072/140] fix cache key --- .github/workflows/copilot-setup-steps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index b777f4ea0..702860569 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -33,7 +33,7 @@ jobs: path: | ~/.cache/composer/files ~/.cache/composer/vcs - key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}" + key: "composer-v2-cache-8.3-${{ hashFiles('./composer.lock') }}" restore-keys: | composer-v2-cache- From 7ea0f2ff43c2edebc694604f3a82b029d9fa51b5 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 26 Jun 2025 00:14:34 +0100 Subject: [PATCH 073/140] copilot-setup-steps: also add extension stubs --- .github/workflows/copilot-setup-steps.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 702860569..ef0b122e1 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -39,3 +39,9 @@ jobs: - name: Install Composer dependencies run: composer install --prefer-dist --no-interaction + + - name: Clone extension stubs + uses: actions/checkout@v4 + with: + repository: pmmp/phpstorm-stubs + path: extension-stubs From f1f6e796a424086647ea8ff647bbaa5f60104950 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 10:28:28 +0000 Subject: [PATCH 074/140] Bump the github-actions group with 2 updates (#6749) --- .github/workflows/discord-release-notify.yml | 2 +- .github/workflows/draft-release-pr-check.yml | 2 +- .github/workflows/draft-release.yml | 4 ++-- .github/workflows/main.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index 93b2978aa..906f227ea 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.33.0 + uses: shivammathur/setup-php@2.34.1 with: php-version: 8.2 diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml index 20b2200e6..b2575f9be 100644 --- a/.github/workflows/draft-release-pr-check.yml +++ b/.github/workflows/draft-release-pr-check.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP - uses: shivammathur/setup-php@2.33.0 + uses: shivammathur/setup-php@2.34.1 with: php-version: 8.2 diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index ca48ab8e0..014ea531c 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -87,7 +87,7 @@ jobs: submodules: true - name: Setup PHP - uses: shivammathur/setup-php@2.33.0 + uses: shivammathur/setup-php@2.34.1 with: php-version: ${{ env.PHP_VERSION }} @@ -165,7 +165,7 @@ jobs: ${{ github.workspace }}/core-permissions.rst - name: Create draft release - uses: ncipollo/release-action@v1.16.0 + uses: ncipollo/release-action@v1.18.0 id: create-draft with: artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cabda54be..4d020c10b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.33.0 + uses: shivammathur/setup-php@2.34.1 with: php-version: 8.3 tools: php-cs-fixer:3.75 From 50e15db9ac280f4646f33fc05283190092c53e28 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 8 Jul 2025 13:41:59 +0100 Subject: [PATCH 075/140] Prepare 5.31.0 release (#6752) --- changelogs/5.31.md | 14 +++++++++++++ composer.json | 4 ++-- composer.lock | 26 ++++++++++++------------- src/VersionInfo.php | 4 ++-- src/data/bedrock/item/ItemTypeNames.php | 1 + 5 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 changelogs/5.31.md diff --git a/changelogs/5.31.md b/changelogs/5.31.md new file mode 100644 index 000000000..60e797425 --- /dev/null +++ b/changelogs/5.31.md @@ -0,0 +1,14 @@ +# 5.31.0 +Released 8th July 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.93. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.93. +- Removed support for earlier versions. diff --git a/composer.json b/composer.json index 051550de6..419f29a4a 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", - "pocketmine/bedrock-data": "~5.1.0+bedrock-1.21.90", + "pocketmine/bedrock-data": "~5.2.0+bedrock-1.21.93", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~39.0.0+bedrock-1.21.90", + "pocketmine/bedrock-protocol": "~39.1.0+bedrock-1.21.93", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 2967a967f..9f550b715 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": "9bf4984c23f688264d3ce6a729a6ec17", + "content-hash": "679ab8fc31e55b5170daa34258dc0fd4", "packages": [ { "name": "adhocore/json-comment", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "5.1.0+bedrock-1.21.90", + "version": "5.2.0+bedrock-1.21.93", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "89ed34957aeccc63e517aa849af593adae958e98" + "reference": "740e18e490c6a102b774518ff2224a06762bcaf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/89ed34957aeccc63e517aa849af593adae958e98", - "reference": "89ed34957aeccc63e517aa849af593adae958e98", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/740e18e490c6a102b774518ff2224a06762bcaf8", + "reference": "740e18e490c6a102b774518ff2224a06762bcaf8", "shasum": "" }, "type": "library", @@ -224,9 +224,9 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.90" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.93" }, - "time": "2025-06-17T23:44:21+00:00" + "time": "2025-07-08T12:30:28+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "39.0.1+bedrock-1.21.90", + "version": "39.1.0+bedrock-1.21.93", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "fd231bad0d94024ff50169dc06e8c4fca5aa2eb3" + "reference": "e9bc5fb691d18dab229a158462c13f0c6fea79c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/fd231bad0d94024ff50169dc06e8c4fca5aa2eb3", - "reference": "fd231bad0d94024ff50169dc06e8c4fca5aa2eb3", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e9bc5fb691d18dab229a158462c13f0c6fea79c8", + "reference": "e9bc5fb691d18dab229a158462c13f0c6fea79c8", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/39.0.1+bedrock-1.21.90" + "source": "https://github.com/pmmp/BedrockProtocol/tree/39.1.0+bedrock-1.21.93" }, - "time": "2025-06-23T13:22:50+00:00" + "time": "2025-07-08T12:31:39+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/VersionInfo.php b/src/VersionInfo.php index cb4ee32ca..499833d34 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.30.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.31.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index d2ab0996b..3178386ca 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -372,6 +372,7 @@ final class ItemTypeNames{ public const MUSIC_DISC_CREATOR = "minecraft:music_disc_creator"; public const MUSIC_DISC_CREATOR_MUSIC_BOX = "minecraft:music_disc_creator_music_box"; public const MUSIC_DISC_FAR = "minecraft:music_disc_far"; + public const MUSIC_DISC_LAVA_CHICKEN = "minecraft:music_disc_lava_chicken"; public const MUSIC_DISC_MALL = "minecraft:music_disc_mall"; public const MUSIC_DISC_MELLOHI = "minecraft:music_disc_mellohi"; public const MUSIC_DISC_OTHERSIDE = "minecraft:music_disc_otherside"; From a1d74b57109a7c91ffab73718182f3b1e530fae3 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 12:43:11 +0000 Subject: [PATCH 076/140] 5.31.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/16143550499 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 499833d34..aeb4d9ff8 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.31.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.31.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 4047cbaafe1685211c8a1eaf87f8780bbb7b1ad9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:07:07 +0000 Subject: [PATCH 077/140] Bump phpstan/phpstan-strict-rules in the development-patch-updates group (#6756) --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 9f550b715..5981f64b3 100644 --- a/composer.lock +++ b/composer.lock @@ -1312,16 +1312,16 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.4", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a" + "reference": "1f1358da2f8e1317478c63c21beb9918c9821f6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", - "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1f1358da2f8e1317478c63c21beb9918c9821f6f", + "reference": "1f1358da2f8e1317478c63c21beb9918c9821f6f", "shasum": "" }, "require": { @@ -1354,9 +1354,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.5" }, - "time": "2025-03-18T11:42:40+00:00" + "time": "2025-07-17T12:01:44+00:00" }, { "name": "phpunit/php-code-coverage", From d41f1b288978109c69a58924baae3e0d93c3a534 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Jul 2025 18:01:02 +0100 Subject: [PATCH 078/140] World: avoid hammering the disk looking for known ungenerated chunks closes #6679 judging by the debug logs, this actually happens a lot during initial world generation, which I suppose isn't that surprising. --- src/world/World.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/world/World.php b/src/world/World.php index ecec3d3b9..8602c5803 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -289,6 +289,12 @@ class World implements ChunkManager{ */ private array $chunks = []; + /** + * @var true[] + * @phpstan-var array + */ + private array $knownUngeneratedChunks = []; + /** * @var Vector3[][] chunkHash => [relativeBlockHash => Vector3] * @phpstan-var array> @@ -625,6 +631,7 @@ class World implements ChunkManager{ self::getXZ($chunkHash, $chunkX, $chunkZ); $this->unloadChunk($chunkX, $chunkZ, false); } + $this->knownUngeneratedChunks = []; foreach($this->entitiesByChunk as $chunkHash => $entities){ self::getXZ($chunkHash, $chunkX, $chunkZ); @@ -2667,6 +2674,7 @@ class World implements ChunkManager{ } $this->chunks[$chunkHash] = $chunk; + unset($this->knownUngeneratedChunks[$chunkHash]); $this->blockCacheSize -= count($this->blockCache[$chunkHash] ?? []); unset($this->blockCache[$chunkHash]); @@ -2931,6 +2939,9 @@ class World implements ChunkManager{ if(isset($this->chunks[$chunkHash = World::chunkHash($x, $z)])){ return $this->chunks[$chunkHash]; } + if(isset($this->knownUngeneratedChunks[$chunkHash])){ + return null; + } $this->timings->syncChunkLoad->startTiming(); @@ -2950,6 +2961,7 @@ class World implements ChunkManager{ if($loadedChunkData === null){ $this->timings->syncChunkLoad->stopTiming(); + $this->knownUngeneratedChunks[$chunkHash] = true; return null; } From 866df55edf17d845fff362ff351e055e5d9b8fc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:25:19 +0000 Subject: [PATCH 079/140] Bump ramsey/uuid from 4.8.1 to 4.9.0 (#6748) --- composer.json | 2 +- composer.lock | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index 419f29a4a..ce4994d4f 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "pocketmine/raklib": "~1.2.0", "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", - "ramsey/uuid": "~4.8.0", + "ramsey/uuid": "~4.9.0", "symfony/filesystem": "~6.4.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 5981f64b3..0180fd0a6 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": "679ab8fc31e55b5170daa34258dc0fd4", + "content-hash": "4dc5ea726d881d8c52d1b5299485d940", "packages": [ { "name": "adhocore/json-comment", @@ -818,21 +818,20 @@ }, { "name": "ramsey/uuid", - "version": "4.8.1", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28" + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", "shasum": "" }, "require": { "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", - "ext-json": "*", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -891,9 +890,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.8.1" + "source": "https://github.com/ramsey/uuid/tree/4.9.0" }, - "time": "2025-06-01T06:28:46+00:00" + "time": "2025-06-25T14:20:11+00:00" }, { "name": "symfony/filesystem", @@ -2757,7 +2756,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -2788,9 +2787,9 @@ "ext-zlib": ">=1.2.11", "composer-runtime-api": "^2.0" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } From 40ea6dd30d9f5c874be39c2f8a6f5d19755268c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:26:25 +0000 Subject: [PATCH 080/140] Bump phpstan/phpstan-strict-rules in the development-patch-updates group (#6758) --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 0180fd0a6..2e5e83a94 100644 --- a/composer.lock +++ b/composer.lock @@ -1311,16 +1311,16 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.5", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "1f1358da2f8e1317478c63c21beb9918c9821f6f" + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1f1358da2f8e1317478c63c21beb9918c9821f6f", - "reference": "1f1358da2f8e1317478c63c21beb9918c9821f6f", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094", + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094", "shasum": "" }, "require": { @@ -1353,9 +1353,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.5" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6" }, - "time": "2025-07-17T12:01:44+00:00" + "time": "2025-07-21T12:19:29+00:00" }, { "name": "phpunit/php-code-coverage", From a83c62a4a2e5eea17fdfc3eb2bfd55f31534a9e3 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 30 Jul 2025 19:08:29 +0100 Subject: [PATCH 081/140] readme: Stop showing random PR statuses on CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f2b715ab..98f569346 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@

- CI + CI GitHub release (latest SemVer) Discord
From cc17e68072c1bcadefcab3c7ddb42cdb27be0dad Mon Sep 17 00:00:00 2001 From: Hugo_ <55756021+Dhaiven@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:48:47 +0200 Subject: [PATCH 082/140] Add blocks interfaces for commons properties (#6639) --- src/block/ActivatorRail.php | 3 +- src/block/AmethystCluster.php | 3 +- src/block/Anvil.php | 3 +- src/block/Barrel.php | 3 +- src/block/BaseBanner.php | 3 +- src/block/BaseBigDripleaf.php | 3 +- src/block/BaseCoral.php | 3 +- src/block/BaseSign.php | 3 +- src/block/Bed.php | 4 +- src/block/Bell.php | 3 +- src/block/BoneBlock.php | 3 +- src/block/Button.php | 3 +- src/block/Cactus.php | 3 +- src/block/CakeWithCandle.php | 3 +- src/block/CakeWithDyedCandle.php | 3 +- src/block/Campfire.php | 4 +- src/block/Candle.php | 3 +- src/block/Carpet.php | 3 +- src/block/CarvedPumpkin.php | 3 +- src/block/CaveVines.php | 3 +- src/block/Chain.php | 3 +- src/block/ChemistryTable.php | 3 +- src/block/Chest.php | 3 +- src/block/ChiseledBookshelf.php | 3 +- src/block/ChorusFlower.php | 3 +- src/block/CocoaBlock.php | 4 +- src/block/Concrete.php | 3 +- src/block/ConcretePowder.php | 3 +- src/block/CopperBulb.php | 4 +- src/block/CoralBlock.php | 3 +- src/block/Crops.php | 3 +- src/block/DaylightSensor.php | 3 +- src/block/Door.php | 3 +- src/block/DoublePitcherCrop.php | 3 +- src/block/DyedCandle.php | 3 +- src/block/DyedShulkerBox.php | 3 +- src/block/EndPortalFrame.php | 3 +- src/block/EndRod.php | 3 +- src/block/EnderChest.php | 3 +- src/block/FenceGate.php | 4 +- src/block/Fire.php | 3 +- src/block/FloorBanner.php | 3 +- src/block/FloorSign.php | 3 +- src/block/FrostedIce.php | 3 +- src/block/Furnace.php | 3 +- src/block/GlazedTerracotta.php | 4 +- src/block/GlowLichen.php | 3 +- src/block/HayBale.php | 3 +- src/block/Hopper.php | 3 +- src/block/ItemFrame.php | 3 +- src/block/Ladder.php | 3 +- src/block/Lectern.php | 3 +- src/block/LightningRod.php | 3 +- src/block/Loom.php | 3 +- src/block/NetherVines.php | 3 +- src/block/NetherWartPlant.php | 3 +- src/block/PinkPetals.php | 3 +- src/block/PitcherCrop.php | 3 +- src/block/Planks.php | 3 +- src/block/PoweredRail.php | 3 +- src/block/RedstoneComparator.php | 5 +- src/block/RedstoneLamp.php | 3 +- src/block/RedstoneOre.php | 3 +- src/block/RedstoneRepeater.php | 4 +- src/block/RedstoneTorch.php | 3 +- src/block/RedstoneWire.php | 3 +- src/block/ResinClump.php | 3 +- src/block/ShulkerBox.php | 3 +- src/block/SimplePillar.php | 3 +- src/block/SmallDripleaf.php | 3 +- src/block/StainedGlass.php | 3 +- src/block/StainedGlassPane.php | 3 +- src/block/StainedHardenedClay.php | 3 +- src/block/StainedHardenedGlass.php | 3 +- src/block/StainedHardenedGlassPane.php | 3 +- src/block/Stair.php | 3 +- src/block/Stonecutter.php | 3 +- src/block/Sugarcane.php | 3 +- src/block/SweetBerryBush.php | 3 +- src/block/Trapdoor.php | 3 +- src/block/TripwireHook.php | 3 +- src/block/WallBanner.php | 3 +- src/block/WallCoralFan.php | 3 +- src/block/WallSign.php | 3 +- src/block/WeightedPressurePlate.php | 3 +- src/block/Wood.php | 4 +- src/block/WoodenButton.php | 3 +- src/block/WoodenDoor.php | 3 +- src/block/WoodenFence.php | 3 +- src/block/WoodenPressurePlate.php | 3 +- src/block/WoodenSlab.php | 3 +- src/block/WoodenStairs.php | 3 +- src/block/WoodenTrapdoor.php | 3 +- src/block/Wool.php | 3 +- src/block/utils/Ageable.php | 34 ++++++++++++ .../utils/AnalogRedstoneSignalEmitter.php | 34 ++++++++++++ src/block/utils/AnyFacing.php | 41 ++++++++++++++ src/block/utils/Colored.php | 34 ++++++++++++ src/block/utils/CoralMaterial.php | 41 ++++++++++++++ src/block/utils/HorizontalFacing.php | 41 ++++++++++++++ src/block/utils/Lightable.php | 34 ++++++++++++ src/block/utils/MultiFacing.php | 54 +++++++++++++++++++ src/block/utils/PillarRotation.php | 39 ++++++++++++++ src/block/utils/PoweredByRedstone.php | 34 ++++++++++++ src/block/utils/SignLikeRotation.php | 39 ++++++++++++++ src/block/utils/WoodMaterial.php | 29 ++++++++++ 106 files changed, 652 insertions(+), 94 deletions(-) create mode 100644 src/block/utils/Ageable.php create mode 100644 src/block/utils/AnalogRedstoneSignalEmitter.php create mode 100644 src/block/utils/AnyFacing.php create mode 100644 src/block/utils/Colored.php create mode 100644 src/block/utils/CoralMaterial.php create mode 100644 src/block/utils/HorizontalFacing.php create mode 100644 src/block/utils/Lightable.php create mode 100644 src/block/utils/MultiFacing.php create mode 100644 src/block/utils/PillarRotation.php create mode 100644 src/block/utils/PoweredByRedstone.php create mode 100644 src/block/utils/SignLikeRotation.php create mode 100644 src/block/utils/WoodMaterial.php diff --git a/src/block/ActivatorRail.php b/src/block/ActivatorRail.php index dcd0ef93b..da15eb1e8 100644 --- a/src/block/ActivatorRail.php +++ b/src/block/ActivatorRail.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\RailPoweredByRedstoneTrait; -class ActivatorRail extends StraightOnlyRail{ +class ActivatorRail extends StraightOnlyRail implements PoweredByRedstone{ use RailPoweredByRedstoneTrait; //TODO diff --git a/src/block/AmethystCluster.php b/src/block/AmethystCluster.php index 639490456..8a750e974 100644 --- a/src/block/AmethystCluster.php +++ b/src/block/AmethystCluster.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\AmethystTrait; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\block\utils\SupportType; @@ -38,7 +39,7 @@ use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\world\BlockTransaction; -final class AmethystCluster extends Transparent{ +final class AmethystCluster extends Transparent implements AnyFacing{ use AmethystTrait; use AnyFacingTrait; diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 2c48f9a7c..fcb4d045c 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\AnvilInventory; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -41,7 +42,7 @@ use pocketmine\world\sound\AnvilFallSound; use pocketmine\world\sound\Sound; use function round; -class Anvil extends Transparent implements Fallable{ +class Anvil extends Transparent implements Fallable, HorizontalFacing{ use FallableTrait; use HorizontalFacingTrait; diff --git a/src/block/Barrel.php b/src/block/Barrel.php index 0f0499ab9..7b2ea356e 100644 --- a/src/block/Barrel.php +++ b/src/block/Barrel.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Barrel as TileBarrel; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function abs; -class Barrel extends Opaque{ +class Barrel extends Opaque implements AnyFacing{ use AnyFacingTrait; protected bool $open = false; diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index b56323453..376f1f9dc 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Banner as TileBanner; use pocketmine\block\utils\BannerPatternLayer; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Banner as ItemBanner; @@ -36,7 +37,7 @@ use pocketmine\world\BlockTransaction; use function assert; use function count; -abstract class BaseBanner extends Transparent{ +abstract class BaseBanner extends Transparent implements Colored{ use ColoredTrait; /** diff --git a/src/block/BaseBigDripleaf.php b/src/block/BaseBigDripleaf.php index f0ff59cf0..94e2c12a2 100644 --- a/src/block/BaseBigDripleaf.php +++ b/src/block/BaseBigDripleaf.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\event\block\StructureGrowEvent; @@ -33,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -abstract class BaseBigDripleaf extends Transparent{ +abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; abstract protected function isHead() : bool; diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php index b9c595a97..c1cc9bb25 100644 --- a/src/block/BaseCoral.php +++ b/src/block/BaseCoral.php @@ -24,12 +24,13 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\CoralMaterial; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use function mt_rand; -abstract class BaseCoral extends Transparent{ +abstract class BaseCoral extends Transparent implements CoralMaterial{ use CoralTypeTrait; public function onNearbyBlockChange() : void{ diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 0f5d77d58..0efaa603c 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -27,6 +27,7 @@ use pocketmine\block\tile\Sign as TileSign; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SignText; use pocketmine\block\utils\SupportType; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodTypeTrait; use pocketmine\color\Color; @@ -44,7 +45,7 @@ use function array_map; use function assert; use function strlen; -abstract class BaseSign extends Transparent{ +abstract class BaseSign extends Transparent implements WoodMaterial{ use WoodTypeTrait; protected SignText $text; diff --git a/src/block/Bed.php b/src/block/Bed.php index 133c4a9cc..21bd3a172 100644 --- a/src/block/Bed.php +++ b/src/block/Bed.php @@ -24,8 +24,10 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Bed as TileBed; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -41,7 +43,7 @@ use pocketmine\utils\TextFormat; use pocketmine\world\BlockTransaction; use pocketmine\world\World; -class Bed extends Transparent{ +class Bed extends Transparent implements Colored, HorizontalFacing{ use ColoredTrait; use HorizontalFacingTrait; diff --git a/src/block/Bell.php b/src/block/Bell.php index 53a6fc7fb..258abc628 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Bell as TileBell; use pocketmine\block\utils\BellAttachmentType; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -38,7 +39,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\BellRingSound; -final class Bell extends Transparent{ +final class Bell extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR; diff --git a/src/block/BoneBlock.php b/src/block/BoneBlock.php index 247bdb748..465d96033 100644 --- a/src/block/BoneBlock.php +++ b/src/block/BoneBlock.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; -class BoneBlock extends Opaque{ +class BoneBlock extends Opaque implements PillarRotation{ use PillarRotationTrait; } diff --git a/src/block/Button.php b/src/block/Button.php index 73bd1d556..4d1f1bbc5 100644 --- a/src/block/Button.php +++ b/src/block/Button.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\world\BlockTransaction; use pocketmine\world\sound\RedstonePowerOffSound; use pocketmine\world\sound\RedstonePowerOnSound; -abstract class Button extends Flowable{ +abstract class Button extends Flowable implements AnyFacing{ use AnyFacingTrait; protected bool $pressed = false; diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 67b15b946..51c98786b 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -33,7 +34,7 @@ use pocketmine\event\entity\EntityDamageEvent; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -class Cactus extends Transparent{ +class Cactus extends Transparent implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php index 546843d6c..1462776c2 100644 --- a/src/block/CakeWithCandle.php +++ b/src/block/CakeWithCandle.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\CandleTrait; +use pocketmine\block\utils\Lightable; use pocketmine\entity\Living; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -31,7 +32,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class CakeWithCandle extends BaseCake{ +class CakeWithCandle extends BaseCake implements Lightable{ use CandleTrait { onInteract as onInteractCandle; } diff --git a/src/block/CakeWithDyedCandle.php b/src/block/CakeWithDyedCandle.php index 0dff358e1..04ab0c6eb 100644 --- a/src/block/CakeWithDyedCandle.php +++ b/src/block/CakeWithDyedCandle.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; -class CakeWithDyedCandle extends CakeWithCandle{ +class CakeWithDyedCandle extends CakeWithCandle implements Colored{ use ColoredTrait; public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){ diff --git a/src/block/Campfire.php b/src/block/Campfire.php index 9f4c42a9c..edfc87ecb 100644 --- a/src/block/Campfire.php +++ b/src/block/Campfire.php @@ -25,7 +25,9 @@ namespace pocketmine\block; use pocketmine\block\inventory\CampfireInventory; use pocketmine\block\tile\Campfire as TileCampfire; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\block\utils\SupportType; use pocketmine\crafting\FurnaceRecipe; @@ -59,7 +61,7 @@ use function count; use function min; use function mt_rand; -class Campfire extends Transparent{ +class Campfire extends Transparent implements Lightable, HorizontalFacing{ use HorizontalFacingTrait{ HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState; } diff --git a/src/block/Candle.php b/src/block/Candle.php index 7f22641e1..977f13a09 100644 --- a/src/block/Candle.php +++ b/src/block/Candle.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\CandleTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -35,7 +36,7 @@ use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\world\BlockTransaction; -class Candle extends Transparent{ +class Candle extends Transparent implements Lightable{ use CandleTrait { describeBlockOnlyState as encodeLitState; getLightLevel as getBaseLightLevel; diff --git a/src/block/Carpet.php b/src/block/Carpet.php index 2d8e7ea47..fd8b42a09 100644 --- a/src/block/Carpet.php +++ b/src/block/Carpet.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -class Carpet extends Flowable{ +class Carpet extends Flowable implements Colored{ use ColoredTrait; use StaticSupportTrait; diff --git a/src/block/CarvedPumpkin.php b/src/block/CarvedPumpkin.php index 98f3c2307..cfdb8d49d 100644 --- a/src/block/CarvedPumpkin.php +++ b/src/block/CarvedPumpkin.php @@ -24,7 +24,8 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; -class CarvedPumpkin extends Opaque{ +class CarvedPumpkin extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; } diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php index daa973507..84d7d3169 100644 --- a/src/block/CaveVines.php +++ b/src/block/CaveVines.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -39,7 +40,7 @@ use pocketmine\world\BlockTransaction; use pocketmine\world\sound\GlowBerriesPickSound; use function mt_rand; -class CaveVines extends Flowable{ +class CaveVines extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/Chain.php b/src/block/Chain.php index e9cc2c9be..5ec3b1e2a 100644 --- a/src/block/Chain.php +++ b/src/block/Chain.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; use pocketmine\block\utils\SupportType; use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -final class Chain extends Transparent{ +final class Chain extends Transparent implements PillarRotation{ use PillarRotationTrait; public function getSupportType(int $facing) : SupportType{ diff --git a/src/block/ChemistryTable.php b/src/block/ChemistryTable.php index 058e40288..9b5e78f92 100644 --- a/src/block/ChemistryTable.php +++ b/src/block/ChemistryTable.php @@ -24,11 +24,12 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\player\Player; -final class ChemistryTable extends Opaque{ +final class ChemistryTable extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ diff --git a/src/block/Chest.php b/src/block/Chest.php index 7d2650007..c0dc09d9e 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Chest as TileChest; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\event\block\ChestPairEvent; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class Chest extends Transparent{ +class Chest extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; protected function recalculateCollisionBoxes() : array{ diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php index 73c4861bf..be49c7a91 100644 --- a/src/block/ChiseledBookshelf.php +++ b/src/block/ChiseledBookshelf.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf; use pocketmine\block\utils\ChiseledBookshelfSlot; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Book; @@ -38,7 +39,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function spl_object_id; -class ChiseledBookshelf extends Opaque{ +class ChiseledBookshelf extends Opaque implements HorizontalFacing{ use HorizontalFacingTrait; use FacesOppositePlacingPlayerTrait; diff --git a/src/block/ChorusFlower.php b/src/block/ChorusFlower.php index cc3c606d9..ef48d5a89 100644 --- a/src/block/ChorusFlower.php +++ b/src/block/ChorusFlower.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\entity\projectile\Projectile; @@ -40,7 +41,7 @@ use function array_rand; use function min; use function mt_rand; -final class ChorusFlower extends Flowable{ +final class ChorusFlower extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index 83e1de34b..ae09ccb0a 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\WoodType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -39,7 +41,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function mt_rand; -class CocoaBlock extends Flowable{ +class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ use HorizontalFacingTrait; use AgeableTrait; diff --git a/src/block/Concrete.php b/src/block/Concrete.php index fae6f8e2f..6cad11193 100644 --- a/src/block/Concrete.php +++ b/src/block/Concrete.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -class Concrete extends Opaque{ +class Concrete extends Opaque implements Colored{ use ColoredTrait; } diff --git a/src/block/ConcretePowder.php b/src/block/ConcretePowder.php index 59f14bc72..89b53053b 100644 --- a/src/block/ConcretePowder.php +++ b/src/block/ConcretePowder.php @@ -24,12 +24,13 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; use pocketmine\math\Facing; -class ConcretePowder extends Opaque implements Fallable{ +class ConcretePowder extends Opaque implements Fallable, Colored{ use ColoredTrait; use FallableTrait { onNearbyBlockChange as protected startFalling; diff --git a/src/block/CopperBulb.php b/src/block/CopperBulb.php index 97fc209fe..d0bdedf3c 100644 --- a/src/block/CopperBulb.php +++ b/src/block/CopperBulb.php @@ -26,11 +26,13 @@ namespace pocketmine\block; use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\CopperTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class CopperBulb extends Opaque implements CopperMaterial{ +class CopperBulb extends Opaque implements CopperMaterial, Lightable, PoweredByRedstone{ use CopperTrait; use PoweredByRedstoneTrait; use LightableTrait{ diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php index 3e7ca8224..c21209998 100644 --- a/src/block/CoralBlock.php +++ b/src/block/CoralBlock.php @@ -24,11 +24,12 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\CoralMaterial; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\item\Item; use function mt_rand; -final class CoralBlock extends Opaque{ +final class CoralBlock extends Opaque implements CoralMaterial{ use CoralTypeTrait; public function onNearbyBlockChange() : void{ diff --git a/src/block/Crops.php b/src/block/Crops.php index e90ac6236..b0488d150 100644 --- a/src/block/Crops.php +++ b/src/block/Crops.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CropGrowthHelper; @@ -34,7 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; -abstract class Crops extends Flowable{ +abstract class Crops extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php index 5720af529..ed56d2f44 100644 --- a/src/block/DaylightSensor.php +++ b/src/block/DaylightSensor.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -36,7 +37,7 @@ use function max; use function round; use const M_PI; -class DaylightSensor extends Transparent{ +class DaylightSensor extends Transparent implements AnalogRedstoneSignalEmitter{ use AnalogRedstoneSignalEmitterTrait; protected bool $inverted = false; diff --git a/src/block/Door.php b/src/block/Door.php index fa88267e1..2ff53645c 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\DoorSound; -class Door extends Transparent{ +class Door extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $top = false; diff --git a/src/block/DoublePitcherCrop.php b/src/block/DoublePitcherCrop.php index 1233ed05d..89d9e98b5 100644 --- a/src/block/DoublePitcherCrop.php +++ b/src/block/DoublePitcherCrop.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\CropGrowthHelper; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -37,7 +38,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class DoublePitcherCrop extends DoublePlant{ +final class DoublePitcherCrop extends DoublePlant implements Ageable{ use AgeableTrait { describeBlockOnlyState as describeAge; } diff --git a/src/block/DyedCandle.php b/src/block/DyedCandle.php index a495e8d00..57a8b5923 100644 --- a/src/block/DyedCandle.php +++ b/src/block/DyedCandle.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -class DyedCandle extends Candle{ +class DyedCandle extends Candle implements Colored{ use ColoredTrait; protected function getCandleIfCompatibleType(Block $block) : ?Candle{ diff --git a/src/block/DyedShulkerBox.php b/src/block/DyedShulkerBox.php index 5eae9237e..8be2718a2 100644 --- a/src/block/DyedShulkerBox.php +++ b/src/block/DyedShulkerBox.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class DyedShulkerBox extends ShulkerBox{ +final class DyedShulkerBox extends ShulkerBox implements Colored{ use ColoredTrait; } diff --git a/src/block/EndPortalFrame.php b/src/block/EndPortalFrame.php index ed5b77433..6c4865fdd 100644 --- a/src/block/EndPortalFrame.php +++ b/src/block/EndPortalFrame.php @@ -24,11 +24,12 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -class EndPortalFrame extends Opaque{ +class EndPortalFrame extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; protected bool $eye = false; diff --git a/src/block/EndRod.php b/src/block/EndRod.php index a6770f370..e3d439f6a 100644 --- a/src/block/EndRod.php +++ b/src/block/EndRod.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class EndRod extends Flowable{ +class EndRod extends Flowable implements AnyFacing{ use AnyFacingTrait; public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php index 6a8cf108c..5dec4f3af 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\EnderChestInventory; use pocketmine\block\tile\EnderChest as TileEnderChest; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -33,7 +34,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class EnderChest extends Transparent{ +class EnderChest extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function getLightLevel() : int{ diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php index 2bbfdf892..d8cdfe4e2 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -35,7 +37,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\DoorSound; -class FenceGate extends Transparent{ +class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ use WoodTypeTrait; use HorizontalFacingTrait; diff --git a/src/block/Fire.php b/src/block/Fire.php index 35a7a696c..62809fb9d 100644 --- a/src/block/Fire.php +++ b/src/block/Fire.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; @@ -35,7 +36,7 @@ use function max; use function min; use function mt_rand; -class Fire extends BaseFire{ +class Fire extends BaseFire implements Ageable{ use AgeableTrait; public const MAX_AGE = 15; diff --git a/src/block/FloorBanner.php b/src/block/FloorBanner.php index 73bc45787..ba089b6c0 100644 --- a/src/block/FloorBanner.php +++ b/src/block/FloorBanner.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SignLikeRotationTrait; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -30,7 +31,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class FloorBanner extends BaseBanner{ +final class FloorBanner extends BaseBanner implements SignLikeRotation{ use SignLikeRotationTrait; protected function getSupportingFace() : int{ diff --git a/src/block/FloorSign.php b/src/block/FloorSign.php index 5615d15d8..94e51ffe8 100644 --- a/src/block/FloorSign.php +++ b/src/block/FloorSign.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SignLikeRotationTrait; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -30,7 +31,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class FloorSign extends BaseSign{ +final class FloorSign extends BaseSign implements SignLikeRotation{ use SignLikeRotationTrait; protected function getSupportingFace() : int{ diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php index 3e8592306..046d75811 100644 --- a/src/block/FrostedIce.php +++ b/src/block/FrostedIce.php @@ -23,11 +23,12 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use function mt_rand; -class FrostedIce extends Ice{ +class FrostedIce extends Ice implements Ageable{ use AgeableTrait; public const MAX_AGE = 3; diff --git a/src/block/Furnace.php b/src/block/Furnace.php index 7a64e3cd3..54480e62c 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Furnace as TileFurnace; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\crafting\FurnaceType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -33,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; -class Furnace extends Opaque{ +class Furnace extends Opaque implements Lightable{ use FacesOppositePlacingPlayerTrait; use LightableTrait; diff --git a/src/block/GlazedTerracotta.php b/src/block/GlazedTerracotta.php index 15b3254e5..ccb928875 100644 --- a/src/block/GlazedTerracotta.php +++ b/src/block/GlazedTerracotta.php @@ -23,10 +23,12 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; -class GlazedTerracotta extends Opaque{ +class GlazedTerracotta extends Opaque implements Colored, HorizontalFacing{ use ColoredTrait; use FacesOppositePlacingPlayerTrait; } diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index a44c4d035..6ad8d3748 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\MultiAnySupportTrait; +use pocketmine\block\utils\MultiFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Fertilizer; use pocketmine\item\Item; @@ -35,7 +36,7 @@ use pocketmine\world\World; use function count; use function shuffle; -class GlowLichen extends Transparent{ +class GlowLichen extends Transparent implements MultiFacing{ use MultiAnySupportTrait; public function getLightLevel() : int{ diff --git a/src/block/HayBale.php b/src/block/HayBale.php index 6fdd2cb63..dacfe92fa 100644 --- a/src/block/HayBale.php +++ b/src/block/HayBale.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; use pocketmine\entity\Entity; -class HayBale extends Opaque{ +class HayBale extends Opaque implements PillarRotation{ use PillarRotationTrait; public function getFlameEncouragement() : int{ diff --git a/src/block/Hopper.php b/src/block/Hopper.php index 0d823674b..4956b668f 100644 --- a/src/block/Hopper.php +++ b/src/block/Hopper.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Hopper as TileHopper; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class Hopper extends Transparent{ +class Hopper extends Transparent implements PoweredByRedstone{ use PoweredByRedstoneTrait; private int $facing = Facing::DOWN; diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index c03806a3b..0fda77758 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\ItemFrame as TileItemFrame; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -39,7 +40,7 @@ use pocketmine\world\sound\ItemFrameRotateItemSound; use function is_infinite; use function is_nan; -class ItemFrame extends Flowable{ +class ItemFrame extends Flowable implements AnyFacing{ use AnyFacingTrait; public const ROTATIONS = 8; diff --git a/src/block/Ladder.php b/src/block/Ladder.php index 09c0b8f6b..6edaebbf2 100644 --- a/src/block/Ladder.php +++ b/src/block/Ladder.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\entity\Entity; @@ -35,7 +36,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class Ladder extends Transparent{ +class Ladder extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; public function hasEntityCollision() : bool{ diff --git a/src/block/Lectern.php b/src/block/Lectern.php index 03880b3c5..9ba01c1c5 100644 --- a/src/block/Lectern.php +++ b/src/block/Lectern.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Lectern as TileLectern; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -36,7 +37,7 @@ use pocketmine\player\Player; use pocketmine\world\sound\LecternPlaceBookSound; use function count; -class Lectern extends Transparent{ +class Lectern extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; protected int $viewedPage = 0; diff --git a/src/block/LightningRod.php b/src/block/LightningRod.php index a0dd50542..9c1971229 100644 --- a/src/block/LightningRod.php +++ b/src/block/LightningRod.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class LightningRod extends Transparent{ +final class LightningRod extends Transparent implements AnyFacing{ use AnyFacingTrait; protected function recalculateCollisionBoxes() : array{ diff --git a/src/block/Loom.php b/src/block/Loom.php index d3dd4f3c7..a2b9fc235 100644 --- a/src/block/Loom.php +++ b/src/block/Loom.php @@ -25,11 +25,12 @@ namespace pocketmine\block; use pocketmine\block\inventory\LoomInventory; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\player\Player; -final class Loom extends Opaque{ +final class Loom extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ diff --git a/src/block/NetherVines.php b/src/block/NetherVines.php index e8729c00f..67a0b6f94 100644 --- a/src/block/NetherVines.php +++ b/src/block/NetherVines.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -41,7 +42,7 @@ use function mt_rand; /** * This class is used for Weeping & Twisting vines, because they have same behaviour */ -class NetherVines extends Flowable{ +class NetherVines extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/NetherWartPlant.php b/src/block/NetherWartPlant.php index 34e6fd57e..df0609754 100644 --- a/src/block/NetherWartPlant.php +++ b/src/block/NetherWartPlant.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; @@ -31,7 +32,7 @@ use pocketmine\item\Item; use pocketmine\math\Facing; use function mt_rand; -class NetherWartPlant extends Flowable{ +class NetherWartPlant extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php index 17bc4c50a..b8b6e248c 100644 --- a/src/block/PinkPetals.php +++ b/src/block/PinkPetals.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class PinkPetals extends Flowable{ +class PinkPetals extends Flowable implements HorizontalFacing{ use HorizontalFacingTrait; use StaticSupportTrait { canBePlacedAt as supportedWhenPlacedAt; diff --git a/src/block/PitcherCrop.php b/src/block/PitcherCrop.php index d41aed284..1c771bb8a 100644 --- a/src/block/PitcherCrop.php +++ b/src/block/PitcherCrop.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CropGrowthHelper; @@ -38,7 +39,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class PitcherCrop extends Flowable{ +final class PitcherCrop extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/Planks.php b/src/block/Planks.php index 1074f8adf..ad7956361 100644 --- a/src/block/Planks.php +++ b/src/block/Planks.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class Planks extends Opaque{ +class Planks extends Opaque implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/PoweredRail.php b/src/block/PoweredRail.php index 25d6dc9e3..321b19a46 100644 --- a/src/block/PoweredRail.php +++ b/src/block/PoweredRail.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\RailPoweredByRedstoneTrait; -class PoweredRail extends StraightOnlyRail{ +class PoweredRail extends StraightOnlyRail implements PoweredByRedstone{ use RailPoweredByRedstoneTrait; } diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php index 40e1ef510..88050b85a 100644 --- a/src/block/RedstoneComparator.php +++ b/src/block/RedstoneComparator.php @@ -24,8 +24,11 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Comparator; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\block\utils\SupportType; @@ -38,7 +41,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function assert; -class RedstoneComparator extends Flowable{ +class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter, PoweredByRedstone, HorizontalFacing{ use HorizontalFacingTrait; use AnalogRedstoneSignalEmitterTrait; use PoweredByRedstoneTrait; diff --git a/src/block/RedstoneLamp.php b/src/block/RedstoneLamp.php index 58098c395..33a97801d 100644 --- a/src/block/RedstoneLamp.php +++ b/src/block/RedstoneLamp.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class RedstoneLamp extends Opaque{ +class RedstoneLamp extends Opaque implements PoweredByRedstone{ use PoweredByRedstoneTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ diff --git a/src/block/RedstoneOre.php b/src/block/RedstoneOre.php index 10e701a6f..3477a3519 100644 --- a/src/block/RedstoneOre.php +++ b/src/block/RedstoneOre.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FortuneDropHelper; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -31,7 +32,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; -class RedstoneOre extends Opaque{ +class RedstoneOre extends Opaque implements Lightable{ use LightableTrait; public function getLightLevel() : int{ diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php index bf9d0c5da..4059ff1cc 100644 --- a/src/block/RedstoneRepeater.php +++ b/src/block/RedstoneRepeater.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\block\utils\SupportType; @@ -35,7 +37,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class RedstoneRepeater extends Flowable{ +class RedstoneRepeater extends Flowable implements PoweredByRedstone, HorizontalFacing{ use HorizontalFacingTrait; use PoweredByRedstoneTrait; use StaticSupportTrait; diff --git a/src/block/RedstoneTorch.php b/src/block/RedstoneTorch.php index 26c86038b..f73076ccc 100644 --- a/src/block/RedstoneTorch.php +++ b/src/block/RedstoneTorch.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class RedstoneTorch extends Torch{ +class RedstoneTorch extends Torch implements Lightable{ use LightableTrait; public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){ diff --git a/src/block/RedstoneWire.php b/src/block/RedstoneWire.php index a2d293fca..6be27bcb4 100644 --- a/src/block/RedstoneWire.php +++ b/src/block/RedstoneWire.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\Facing; -class RedstoneWire extends Flowable{ +class RedstoneWire extends Flowable implements AnalogRedstoneSignalEmitter{ use AnalogRedstoneSignalEmitterTrait; use StaticSupportTrait; diff --git a/src/block/ResinClump.php b/src/block/ResinClump.php index 75126edf3..2855a7cc3 100644 --- a/src/block/ResinClump.php +++ b/src/block/ResinClump.php @@ -24,9 +24,10 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\MultiAnySupportTrait; +use pocketmine\block\utils\MultiFacing; use pocketmine\block\utils\SupportType; -final class ResinClump extends Transparent{ +final class ResinClump extends Transparent implements MultiFacing{ use MultiAnySupportTrait; public function isSolid() : bool{ diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php index d557401ee..52a3b83cd 100644 --- a/src/block/ShulkerBox.php +++ b/src/block/ShulkerBox.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\ShulkerBox as TileShulkerBox; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class ShulkerBox extends Opaque{ +class ShulkerBox extends Opaque implements AnyFacing{ use AnyFacingTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ diff --git a/src/block/SimplePillar.php b/src/block/SimplePillar.php index 98c89f89c..6a7af96ed 100644 --- a/src/block/SimplePillar.php +++ b/src/block/SimplePillar.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; /** * @internal This class provides a general base for pillar-like blocks. It **should not** be used for contract binding * in APIs, because not all pillar-like blocks extend this class. */ -class SimplePillar extends Opaque{ +class SimplePillar extends Opaque implements PillarRotation{ use PillarRotationTrait; } diff --git a/src/block/SmallDripleaf.php b/src/block/SmallDripleaf.php index d192e43db..846be5c93 100644 --- a/src/block/SmallDripleaf.php +++ b/src/block/SmallDripleaf.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -36,7 +37,7 @@ use pocketmine\world\BlockTransaction; use pocketmine\world\Position; use function mt_rand; -class SmallDripleaf extends Transparent{ +class SmallDripleaf extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $top = false; diff --git a/src/block/StainedGlass.php b/src/block/StainedGlass.php index bc0d84877..baf4755bb 100644 --- a/src/block/StainedGlass.php +++ b/src/block/StainedGlass.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedGlass extends Glass{ +final class StainedGlass extends Glass implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedGlassPane.php b/src/block/StainedGlassPane.php index 18ecfdee0..897c291c7 100644 --- a/src/block/StainedGlassPane.php +++ b/src/block/StainedGlassPane.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedGlassPane extends GlassPane{ +final class StainedGlassPane extends GlassPane implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedHardenedClay.php b/src/block/StainedHardenedClay.php index 2c2c01ba3..765e97e4f 100644 --- a/src/block/StainedHardenedClay.php +++ b/src/block/StainedHardenedClay.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedHardenedClay extends HardenedClay{ +final class StainedHardenedClay extends HardenedClay implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedHardenedGlass.php b/src/block/StainedHardenedGlass.php index cc609a49a..c3a794e4d 100644 --- a/src/block/StainedHardenedGlass.php +++ b/src/block/StainedHardenedGlass.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedHardenedGlass extends HardenedGlass{ +final class StainedHardenedGlass extends HardenedGlass implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedHardenedGlassPane.php b/src/block/StainedHardenedGlassPane.php index 63dbe1f77..9631ff5a9 100644 --- a/src/block/StainedHardenedGlassPane.php +++ b/src/block/StainedHardenedGlassPane.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedHardenedGlassPane extends HardenedGlassPane{ +final class StainedHardenedGlassPane extends HardenedGlassPane implements Colored{ use ColoredTrait; } diff --git a/src/block/Stair.php b/src/block/Stair.php index d66a9ce5c..56101de13 100644 --- a/src/block/Stair.php +++ b/src/block/Stair.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\StairShape; use pocketmine\block\utils\SupportType; @@ -35,7 +36,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class Stair extends Transparent{ +class Stair extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $upsideDown = false; diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php index 30c19d25d..0fd259326 100644 --- a/src/block/Stonecutter.php +++ b/src/block/Stonecutter.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\StonecutterInventory; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -32,7 +33,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class Stonecutter extends Transparent{ +class Stonecutter extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 2da2dc9b9..0874413c5 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -34,7 +35,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\Position; -class Sugarcane extends Flowable{ +class Sugarcane extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait { onNearbyBlockChange as onSupportBlockChange; diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index eabdde118..881b7359f 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; @@ -39,7 +40,7 @@ use pocketmine\player\Player; use pocketmine\world\sound\SweetBerriesPickSound; use function mt_rand; -class SweetBerryBush extends Flowable{ +class SweetBerryBush extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php index a903e1b5e..5e8a7dc3f 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\DoorSound; -class Trapdoor extends Transparent{ +class Trapdoor extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $open = false; diff --git a/src/block/TripwireHook.php b/src/block/TripwireHook.php index 325819825..4e40cf62e 100644 --- a/src/block/TripwireHook.php +++ b/src/block/TripwireHook.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class TripwireHook extends Flowable{ +class TripwireHook extends Flowable implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $connected = false; diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php index 5182fe9f8..ddb157cda 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -31,7 +32,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class WallBanner extends BaseBanner{ +final class WallBanner extends BaseBanner implements HorizontalFacing{ use HorizontalFacingTrait; protected function getSupportingFace() : int{ diff --git a/src/block/WallCoralFan.php b/src/block/WallCoralFan.php index f9dece1cd..67745a537 100644 --- a/src/block/WallCoralFan.php +++ b/src/block/WallCoralFan.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class WallCoralFan extends BaseCoral{ +final class WallCoralFan extends BaseCoral implements HorizontalFacing{ use HorizontalFacingTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ diff --git a/src/block/WallSign.php b/src/block/WallSign.php index 07826eae7..c6b42608d 100644 --- a/src/block/WallSign.php +++ b/src/block/WallSign.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -31,7 +32,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class WallSign extends BaseSign{ +final class WallSign extends BaseSign implements HorizontalFacing{ use HorizontalFacingTrait; protected function getSupportingFace() : int{ diff --git a/src/block/WeightedPressurePlate.php b/src/block/WeightedPressurePlate.php index 726b31f6b..065f8b2c8 100644 --- a/src/block/WeightedPressurePlate.php +++ b/src/block/WeightedPressurePlate.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; use function ceil; use function count; use function max; use function min; -class WeightedPressurePlate extends PressurePlate{ +class WeightedPressurePlate extends PressurePlate implements AnalogRedstoneSignalEmitter{ use AnalogRedstoneSignalEmitterTrait; private readonly float $signalStrengthFactor; diff --git a/src/block/Wood.php b/src/block/Wood.php index 127533b98..7aa667bc8 100644 --- a/src/block/Wood.php +++ b/src/block/Wood.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Axe; @@ -32,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\ItemUseOnBlockSound; -class Wood extends Opaque{ +class Wood extends Opaque implements PillarRotation, WoodMaterial{ use PillarRotationTrait; use WoodTypeTrait; diff --git a/src/block/WoodenButton.php b/src/block/WoodenButton.php index 7ba8a7af0..4eed3a50e 100644 --- a/src/block/WoodenButton.php +++ b/src/block/WoodenButton.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenButton extends Button{ +class WoodenButton extends Button implements WoodMaterial{ use WoodTypeTrait; protected function getActivationTime() : int{ diff --git a/src/block/WoodenDoor.php b/src/block/WoodenDoor.php index 96f349e49..b7918a820 100644 --- a/src/block/WoodenDoor.php +++ b/src/block/WoodenDoor.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenDoor extends Door{ +class WoodenDoor extends Door implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenFence.php b/src/block/WoodenFence.php index c12043a86..73c8da44d 100644 --- a/src/block/WoodenFence.php +++ b/src/block/WoodenFence.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenFence extends Fence{ +class WoodenFence extends Fence implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenPressurePlate.php b/src/block/WoodenPressurePlate.php index a629c2f1c..6d638788b 100644 --- a/src/block/WoodenPressurePlate.php +++ b/src/block/WoodenPressurePlate.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodTypeTrait; -class WoodenPressurePlate extends SimplePressurePlate{ +class WoodenPressurePlate extends SimplePressurePlate implements WoodMaterial{ use WoodTypeTrait; public function __construct( diff --git a/src/block/WoodenSlab.php b/src/block/WoodenSlab.php index b388a36ea..3ed606c64 100644 --- a/src/block/WoodenSlab.php +++ b/src/block/WoodenSlab.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenSlab extends Slab{ +class WoodenSlab extends Slab implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenStairs.php b/src/block/WoodenStairs.php index 0d9ba62ce..8da3fceb6 100644 --- a/src/block/WoodenStairs.php +++ b/src/block/WoodenStairs.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenStairs extends Stair{ +class WoodenStairs extends Stair implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenTrapdoor.php b/src/block/WoodenTrapdoor.php index c0a6623a1..2b796df52 100644 --- a/src/block/WoodenTrapdoor.php +++ b/src/block/WoodenTrapdoor.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenTrapdoor extends Trapdoor{ +class WoodenTrapdoor extends Trapdoor implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/Wool.php b/src/block/Wool.php index 0b008ac04..d47c27d27 100644 --- a/src/block/Wool.php +++ b/src/block/Wool.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -class Wool extends Opaque{ +class Wool extends Opaque implements Colored{ use ColoredTrait; public function getFlameEncouragement() : int{ diff --git a/src/block/utils/Ageable.php b/src/block/utils/Ageable.php new file mode 100644 index 000000000..31fe3f459 --- /dev/null +++ b/src/block/utils/Ageable.php @@ -0,0 +1,34 @@ + Date: Sun, 3 Aug 2025 15:47:12 +0100 Subject: [PATCH 083/140] BlockStateUpgrader: All but removed dependency on BlockStateData --- .../block/upgrade/BlockStateUpgrader.php | 83 +++++++++---------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index 2dce762b8..a3e72807e 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -75,6 +75,8 @@ final class BlockStateUpgrader{ public function upgrade(BlockStateData $blockStateData) : BlockStateData{ $version = $blockStateData->getVersion(); + $name = $blockStateData->getName(); + $states = $blockStateData->getStates(); foreach($this->upgradeSchemas as $resultVersion => $schemaList){ /* * Sometimes Mojang made changes without bumping the version ID. @@ -91,57 +93,54 @@ final class BlockStateUpgrader{ } foreach($schemaList as $schema){ - $blockStateData = $this->applySchema($schema, $blockStateData); + [$name, $states] = $this->applySchema($schema, $name, $states); } } - if($this->outputVersion > $version){ - //always update the version number of the blockstate, even if it didn't change - this is needed for - //external tools - $blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $this->outputVersion); - } - return $blockStateData; + return new BlockStateData($name, $states, $this->outputVersion); } - private function applySchema(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : BlockStateData{ - $newStateData = $this->applyStateRemapped($schema, $blockStateData); - if($newStateData !== null){ - return $newStateData; + /** + * @param Tag[] $states + * @phpstan-param array $states + * + * @return (string|Tag[])[] + * @phpstan-return array{0: string, 1: array} + */ + private function applySchema(BlockStateUpgradeSchema $schema, string $oldName, array $states) : array{ + $remapped = $this->applyStateRemapped($schema, $oldName, $states); + if($remapped !== null){ + return $remapped; } - $oldName = $blockStateData->getName(); - $states = $blockStateData->getStates(); - if(isset($schema->renamedIds[$oldName]) && isset($schema->flattenedProperties[$oldName])){ //TODO: this probably ought to be validated when the schema is constructed throw new AssumptionFailedError("Both renamedIds and flattenedProperties are set for the same block ID \"$oldName\" - don't know what to do"); } if(isset($schema->renamedIds[$oldName])){ - $newName = $schema->renamedIds[$oldName] ?? null; + $newName = $schema->renamedIds[$oldName]; }elseif(isset($schema->flattenedProperties[$oldName])){ [$newName, $states] = $this->applyPropertyFlattened($schema->flattenedProperties[$oldName], $oldName, $states); }else{ - $newName = null; + $newName = $oldName; } - $stateChanges = 0; + $states = $this->applyPropertyAdded($schema, $oldName, $states); + $states = $this->applyPropertyRemoved($schema, $oldName, $states); + $states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states); + $states = $this->applyPropertyValueChanged($schema, $oldName, $states); - $states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges); - $states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges); - $states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges); - $states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges); - - if($newName !== null || $stateChanges > 0){ - return new BlockStateData($newName ?? $oldName, $states, $schema->getVersionId()); - } - - return $blockStateData; + return [$newName, $states]; } - private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{ - $oldName = $blockStateData->getName(); - $oldState = $blockStateData->getStates(); - + /** + * @param Tag[] $oldState + * @phpstan-param array $oldState + * + * @return (string|Tag[])[]|null + * @phpstan-return array{0: string, 1: array}|null + */ + private function applyStateRemapped(BlockStateUpgradeSchema $schema, string $oldName, array $oldState) : ?array{ if(isset($schema->remappedStates[$oldName])){ foreach($schema->remappedStates[$oldName] as $remap){ if(count($remap->oldState) > count($oldState)){ @@ -168,7 +167,7 @@ final class BlockStateUpgrader{ } } - return new BlockStateData($newName, $newState, $schema->getVersionId()); + return [$newName, $newState]; } } @@ -182,11 +181,10 @@ final class BlockStateUpgrader{ * @return Tag[] * @phpstan-return array */ - private function applyPropertyAdded(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{ + private function applyPropertyAdded(BlockStateUpgradeSchema $schema, string $oldName, array $states) : array{ if(isset($schema->addedProperties[$oldName])){ foreach(Utils::stringifyKeys($schema->addedProperties[$oldName]) as $propertyName => $value){ if(!isset($states[$propertyName])){ - $stateChanges++; $states[$propertyName] = $value; } } @@ -202,13 +200,10 @@ final class BlockStateUpgrader{ * @return Tag[] * @phpstan-return array */ - private function applyPropertyRemoved(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{ + private function applyPropertyRemoved(BlockStateUpgradeSchema $schema, string $oldName, array $states) : array{ if(isset($schema->removedProperties[$oldName])){ foreach($schema->removedProperties[$oldName] as $propertyName){ - if(isset($states[$propertyName])){ - $stateChanges++; - unset($states[$propertyName]); - } + unset($states[$propertyName]); } } @@ -234,12 +229,11 @@ final class BlockStateUpgrader{ * @return Tag[] * @phpstan-return array */ - private function applyPropertyRenamedOrValueChanged(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{ + private function applyPropertyRenamedOrValueChanged(BlockStateUpgradeSchema $schema, string $oldName, array $states) : array{ if(isset($schema->renamedProperties[$oldName])){ foreach(Utils::stringifyKeys($schema->renamedProperties[$oldName]) as $oldPropertyName => $newPropertyName){ $oldValue = $states[$oldPropertyName] ?? null; if($oldValue !== null){ - $stateChanges++; unset($states[$oldPropertyName]); //If a value remap is needed, we need to do it here, since we won't be able to locate the property @@ -260,16 +254,13 @@ final class BlockStateUpgrader{ * @return Tag[] * @phpstan-return array */ - private function applyPropertyValueChanged(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{ + private function applyPropertyValueChanged(BlockStateUpgradeSchema $schema, string $oldName, array $states) : array{ if(isset($schema->remappedPropertyValues[$oldName])){ foreach(Utils::stringifyKeys($schema->remappedPropertyValues[$oldName]) as $oldPropertyName => $remappedValues){ $oldValue = $states[$oldPropertyName] ?? null; if($oldValue !== null){ $newValue = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue); - if($newValue !== $oldValue){ - $stateChanges++; - $states[$oldPropertyName] = $newValue; - } + $states[$oldPropertyName] = $newValue; } } } From 89d18f929ffdd31d7da0cedb24e120bd33170355 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 6 Aug 2025 15:48:29 +0100 Subject: [PATCH 084/140] RakLibServer: fixed deadlock on thread crash synchronized block -> getCrashInfo -> join -> synchronized on the same context on the child thread -> deadlock instead we check for isTerminated and then get the crash info outside of the synchronized block. --- src/network/mcpe/raklib/RakLibServer.php | 16 ++++++++-------- src/thread/CommonThreadPartsTrait.php | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/network/mcpe/raklib/RakLibServer.php b/src/network/mcpe/raklib/RakLibServer.php index d5e825bee..f66734ee5 100644 --- a/src/network/mcpe/raklib/RakLibServer.php +++ b/src/network/mcpe/raklib/RakLibServer.php @@ -68,17 +68,17 @@ class RakLibServer extends Thread{ public function startAndWait(int $options = NativeThread::INHERIT_NONE) : void{ $this->start($options); $this->synchronized(function() : void{ - while(!$this->ready && $this->getCrashInfo() === null){ + while(!$this->ready && !$this->isTerminated()){ $this->wait(); } - $crashInfo = $this->getCrashInfo(); - if($crashInfo !== null){ - if($crashInfo->getType() === SocketException::class){ - throw new SocketException($crashInfo->getMessage()); - } - throw new ThreadCrashException("RakLib failed to start", $crashInfo); - } }); + $crashInfo = $this->getCrashInfo(); + if($crashInfo !== null){ + if($crashInfo->getType() === SocketException::class){ + throw new SocketException($crashInfo->getMessage()); + } + throw new ThreadCrashException("RakLib failed to start", $crashInfo); + } } protected function onRun() : void{ diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index de606a7b2..d7d51d40f 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -100,6 +100,8 @@ trait CommonThreadPartsTrait{ //*before* the shutdown handler is invoked, so we might land here before the crash info has been set. //In the future this should probably be fixed by running the shutdown handlers before setting isTerminated, //but this workaround should be good enough for now. + //WARNING: Do not call this inside a synchronized block on this thread's context. Because the shutdown handler + //runs in a synchronized block, this will result in a deadlock. if($this->isTerminated() && !$this->isJoined()){ $this->join(); } From 173b685b022d40de3c8fbe5de6a1435a81a148af Mon Sep 17 00:00:00 2001 From: Dries C <15795262+dries-c@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:41:44 +0200 Subject: [PATCH 085/140] Bedrock 1.21.100 (#6760) --------- Co-authored-by: Dylan T. --- changelogs/5.32.md | 17 +++++++ composer.json | 6 +-- composer.lock | 44 +++++++++---------- src/VersionInfo.php | 4 +- src/data/bedrock/WorldDataVersions.php | 6 +-- src/data/bedrock/block/BlockTypeNames.php | 8 ++++ src/data/bedrock/item/ItemTypeNames.php | 11 +++++ .../mcpe/handler/PreSpawnPacketHandler.php | 1 + .../biome/model/BiomeDefinitionEntryData.php | 2 +- 9 files changed, 68 insertions(+), 31 deletions(-) create mode 100644 changelogs/5.32.md diff --git a/changelogs/5.32.md b/changelogs/5.32.md new file mode 100644 index 000000000..16b9aaff4 --- /dev/null +++ b/changelogs/5.32.md @@ -0,0 +1,17 @@ +# 5.32.0 +Released 6th August 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.100. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.100. +- Removed support for earlier versions. + +## Fixes +- Fixed deadlock on RakLib thread crash (e.g. due to port binding failure). diff --git a/composer.json b/composer.json index ce4994d4f..e66269b40 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", - "pocketmine/bedrock-data": "~5.2.0+bedrock-1.21.93", - "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~39.1.0+bedrock-1.21.93", + "pocketmine/bedrock-data": "~5.3.0+bedrock-1.21.100", + "pocketmine/bedrock-item-upgrade-schema": "~1.15.0+bedrock-1.21.100", + "pocketmine/bedrock-protocol": "~40.0.0+bedrock-1.21.100", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 2e5e83a94..9a69e6e14 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": "4dc5ea726d881d8c52d1b5299485d940", + "content-hash": "402ad5667b1e636a8ec6acf2f1b5f055", "packages": [ { "name": "adhocore/json-comment", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "5.2.0+bedrock-1.21.93", + "version": "5.3.0+bedrock-1.21.100", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "740e18e490c6a102b774518ff2224a06762bcaf8" + "reference": "5279e76261df158d5af187cfaafc1618c1da9e3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/740e18e490c6a102b774518ff2224a06762bcaf8", - "reference": "740e18e490c6a102b774518ff2224a06762bcaf8", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/5279e76261df158d5af187cfaafc1618c1da9e3f", + "reference": "5279e76261df158d5af187cfaafc1618c1da9e3f", "shasum": "" }, "type": "library", @@ -224,22 +224,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.93" + "source": "https://github.com/pmmp/BedrockData/tree/5.3.0+bedrock-1.21.100" }, - "time": "2025-07-08T12:30:28+00:00" + "time": "2025-07-30T22:07:56+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", - "version": "1.14.0", + "version": "1.15.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git", - "reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb" + "reference": "09e0dbe9743f21a76b1fe04b2b4136785775f52b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/9fc7c9bbb558a017395c1cb7dd819c033ee971bb", - "reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb", + "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/09e0dbe9743f21a76b1fe04b2b4136785775f52b", + "reference": "09e0dbe9743f21a76b1fe04b2b4136785775f52b", "shasum": "" }, "type": "library", @@ -250,22 +250,22 @@ "description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves", "support": { "issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.14.0" + "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.15.0" }, - "time": "2024-12-04T12:22:49+00:00" + "time": "2025-08-06T15:08:48+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "39.1.0+bedrock-1.21.93", + "version": "40.0.0+bedrock-1.21.100", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "e9bc5fb691d18dab229a158462c13f0c6fea79c8" + "reference": "5e95cab3a6e6c24920e0c25ca4aaf887ed4b70ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e9bc5fb691d18dab229a158462c13f0c6fea79c8", - "reference": "e9bc5fb691d18dab229a158462c13f0c6fea79c8", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/5e95cab3a6e6c24920e0c25ca4aaf887ed4b70ca", + "reference": "5e95cab3a6e6c24920e0c25ca4aaf887ed4b70ca", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/39.1.0+bedrock-1.21.93" + "source": "https://github.com/pmmp/BedrockProtocol/tree/40.0.0+bedrock-1.21.100" }, - "time": "2025-07-08T12:31:39+00:00" + "time": "2025-08-06T15:13:45+00:00" }, { "name": "pocketmine/binaryutils", @@ -2756,7 +2756,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -2787,9 +2787,9 @@ "ext-zlib": ">=1.2.11", "composer-runtime-api": "^2.0" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/VersionInfo.php b/src/VersionInfo.php index aeb4d9ff8..b0a533298 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.31.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.32.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** diff --git a/src/data/bedrock/WorldDataVersions.php b/src/data/bedrock/WorldDataVersions.php index ca36795fa..e682eb43a 100644 --- a/src/data/bedrock/WorldDataVersions.php +++ b/src/data/bedrock/WorldDataVersions.php @@ -54,13 +54,13 @@ final class WorldDataVersions{ * This may be lower than the current protocol version if PocketMine-MP does not yet support features of the newer * version. This allows the protocol to be updated independently of world format support. */ - public const NETWORK = 818; + public const NETWORK = 827; public const LAST_OPENED_IN = [ 1, //major 21, //minor - 90, //patch - 3, //revision + 100, //patch + 23, //revision 0 //is beta ]; } diff --git a/src/data/bedrock/block/BlockTypeNames.php b/src/data/bedrock/block/BlockTypeNames.php index 527a01345..71402c74a 100644 --- a/src/data/bedrock/block/BlockTypeNames.php +++ b/src/data/bedrock/block/BlockTypeNames.php @@ -255,6 +255,7 @@ final class BlockTypeNames{ public const CONDUIT = "minecraft:conduit"; public const COPPER_BLOCK = "minecraft:copper_block"; public const COPPER_BULB = "minecraft:copper_bulb"; + public const COPPER_CHEST = "minecraft:copper_chest"; public const COPPER_DOOR = "minecraft:copper_door"; public const COPPER_GRATE = "minecraft:copper_grate"; public const COPPER_ORE = "minecraft:copper_ore"; @@ -533,6 +534,7 @@ final class BlockTypeNames{ public const EXPOSED_CHISELED_COPPER = "minecraft:exposed_chiseled_copper"; public const EXPOSED_COPPER = "minecraft:exposed_copper"; public const EXPOSED_COPPER_BULB = "minecraft:exposed_copper_bulb"; + public const EXPOSED_COPPER_CHEST = "minecraft:exposed_copper_chest"; public const EXPOSED_COPPER_DOOR = "minecraft:exposed_copper_door"; public const EXPOSED_COPPER_GRATE = "minecraft:exposed_copper_grate"; public const EXPOSED_COPPER_TRAPDOOR = "minecraft:exposed_copper_trapdoor"; @@ -857,6 +859,7 @@ final class BlockTypeNames{ public const OXIDIZED_CHISELED_COPPER = "minecraft:oxidized_chiseled_copper"; public const OXIDIZED_COPPER = "minecraft:oxidized_copper"; public const OXIDIZED_COPPER_BULB = "minecraft:oxidized_copper_bulb"; + public const OXIDIZED_COPPER_CHEST = "minecraft:oxidized_copper_chest"; public const OXIDIZED_COPPER_DOOR = "minecraft:oxidized_copper_door"; public const OXIDIZED_COPPER_GRATE = "minecraft:oxidized_copper_grate"; public const OXIDIZED_COPPER_TRAPDOOR = "minecraft:oxidized_copper_trapdoor"; @@ -1211,6 +1214,7 @@ final class BlockTypeNames{ public const WAXED_CHISELED_COPPER = "minecraft:waxed_chiseled_copper"; public const WAXED_COPPER = "minecraft:waxed_copper"; public const WAXED_COPPER_BULB = "minecraft:waxed_copper_bulb"; + public const WAXED_COPPER_CHEST = "minecraft:waxed_copper_chest"; public const WAXED_COPPER_DOOR = "minecraft:waxed_copper_door"; public const WAXED_COPPER_GRATE = "minecraft:waxed_copper_grate"; public const WAXED_COPPER_TRAPDOOR = "minecraft:waxed_copper_trapdoor"; @@ -1221,6 +1225,7 @@ final class BlockTypeNames{ public const WAXED_EXPOSED_CHISELED_COPPER = "minecraft:waxed_exposed_chiseled_copper"; public const WAXED_EXPOSED_COPPER = "minecraft:waxed_exposed_copper"; public const WAXED_EXPOSED_COPPER_BULB = "minecraft:waxed_exposed_copper_bulb"; + public const WAXED_EXPOSED_COPPER_CHEST = "minecraft:waxed_exposed_copper_chest"; public const WAXED_EXPOSED_COPPER_DOOR = "minecraft:waxed_exposed_copper_door"; public const WAXED_EXPOSED_COPPER_GRATE = "minecraft:waxed_exposed_copper_grate"; public const WAXED_EXPOSED_COPPER_TRAPDOOR = "minecraft:waxed_exposed_copper_trapdoor"; @@ -1231,6 +1236,7 @@ final class BlockTypeNames{ public const WAXED_OXIDIZED_CHISELED_COPPER = "minecraft:waxed_oxidized_chiseled_copper"; public const WAXED_OXIDIZED_COPPER = "minecraft:waxed_oxidized_copper"; public const WAXED_OXIDIZED_COPPER_BULB = "minecraft:waxed_oxidized_copper_bulb"; + public const WAXED_OXIDIZED_COPPER_CHEST = "minecraft:waxed_oxidized_copper_chest"; public const WAXED_OXIDIZED_COPPER_DOOR = "minecraft:waxed_oxidized_copper_door"; public const WAXED_OXIDIZED_COPPER_GRATE = "minecraft:waxed_oxidized_copper_grate"; public const WAXED_OXIDIZED_COPPER_TRAPDOOR = "minecraft:waxed_oxidized_copper_trapdoor"; @@ -1241,6 +1247,7 @@ final class BlockTypeNames{ public const WAXED_WEATHERED_CHISELED_COPPER = "minecraft:waxed_weathered_chiseled_copper"; public const WAXED_WEATHERED_COPPER = "minecraft:waxed_weathered_copper"; public const WAXED_WEATHERED_COPPER_BULB = "minecraft:waxed_weathered_copper_bulb"; + public const WAXED_WEATHERED_COPPER_CHEST = "minecraft:waxed_weathered_copper_chest"; public const WAXED_WEATHERED_COPPER_DOOR = "minecraft:waxed_weathered_copper_door"; public const WAXED_WEATHERED_COPPER_GRATE = "minecraft:waxed_weathered_copper_grate"; public const WAXED_WEATHERED_COPPER_TRAPDOOR = "minecraft:waxed_weathered_copper_trapdoor"; @@ -1251,6 +1258,7 @@ final class BlockTypeNames{ public const WEATHERED_CHISELED_COPPER = "minecraft:weathered_chiseled_copper"; public const WEATHERED_COPPER = "minecraft:weathered_copper"; public const WEATHERED_COPPER_BULB = "minecraft:weathered_copper_bulb"; + public const WEATHERED_COPPER_CHEST = "minecraft:weathered_copper_chest"; public const WEATHERED_COPPER_DOOR = "minecraft:weathered_copper_door"; public const WEATHERED_COPPER_GRATE = "minecraft:weathered_copper_grate"; public const WEATHERED_COPPER_TRAPDOOR = "minecraft:weathered_copper_trapdoor"; diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index 3178386ca..5c648ff38 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -153,8 +153,19 @@ final class ItemTypeNames{ public const COOKED_RABBIT = "minecraft:cooked_rabbit"; public const COOKED_SALMON = "minecraft:cooked_salmon"; public const COOKIE = "minecraft:cookie"; + public const COPPER_AXE = "minecraft:copper_axe"; + public const COPPER_BOOTS = "minecraft:copper_boots"; + public const COPPER_CHESTPLATE = "minecraft:copper_chestplate"; public const COPPER_DOOR = "minecraft:copper_door"; + public const COPPER_GOLEM_SPAWN_EGG = "minecraft:copper_golem_spawn_egg"; + public const COPPER_HELMET = "minecraft:copper_helmet"; + public const COPPER_HOE = "minecraft:copper_hoe"; public const COPPER_INGOT = "minecraft:copper_ingot"; + public const COPPER_LEGGINGS = "minecraft:copper_leggings"; + public const COPPER_NUGGET = "minecraft:copper_nugget"; + public const COPPER_PICKAXE = "minecraft:copper_pickaxe"; + public const COPPER_SHOVEL = "minecraft:copper_shovel"; + public const COPPER_SWORD = "minecraft:copper_sword"; public const CORAL = "minecraft:coral"; public const CORAL_BLOCK = "minecraft:coral_block"; public const CORAL_FAN = "minecraft:coral_fan"; diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 99f65e78f..4aa8be227 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -108,6 +108,7 @@ class PreSpawnPacketHandler extends PacketHandler{ Uuid::fromString(Uuid::NIL), false, false, + false, new NetworkPermissions(disableClientSounds: true), [], 0, diff --git a/src/world/biome/model/BiomeDefinitionEntryData.php b/src/world/biome/model/BiomeDefinitionEntryData.php index 8a5c3d354..bb63b36e1 100644 --- a/src/world/biome/model/BiomeDefinitionEntryData.php +++ b/src/world/biome/model/BiomeDefinitionEntryData.php @@ -28,7 +28,7 @@ namespace pocketmine\world\biome\model; */ final class BiomeDefinitionEntryData{ /** @required */ - public ?int $id; + public int $id; /** @required */ public float $temperature; From 275fdc4280bcbcf48556e4f5c282ab73f82cf56c Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 15:42:50 +0000 Subject: [PATCH 086/140] 5.32.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/16781699267 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index b0a533298..f28239816 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.32.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.32.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 0b9e680753270ba8ccc4f724c89b14df1b7ae2fe Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 6 Aug 2025 17:15:21 +0100 Subject: [PATCH 087/140] Fix GitHub release trigger actions borked https://github.com/actions/runner/issues/2788 --- .github/workflows/build-docker-image.yml | 31 +++++++++++++++----- .github/workflows/discord-release-notify.yml | 17 +++++++++-- .github/workflows/update-updater-api.yml | 17 +++++++++-- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index a3921f820..9319f9e2f 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -4,6 +4,11 @@ on: release: types: - published + workflow_dispatch: + inputs: + release: + description: 'Tag name to build' + required: true jobs: build: @@ -33,11 +38,23 @@ jobs: repository: pmmp/PocketMine-Docker fetch-depth: 1 - - name: Get tag names + + - name: Get tag name id: tag-name run: | - VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') - echo TAG_NAME=$VERSION >> $GITHUB_OUTPUT + if [[ "${{ github.event_name }}" == "release" ]]; then + echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT + else + echo "Unsupported event type: ${{ github.event_name }}" + exit 1 + fi + + - name: Parse version + id: version + run: | + VERSION="${{ steps.tag-name.outputs.TAG_NAME }}" echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT @@ -71,8 +88,8 @@ jobs: push: true context: ./pocketmine-mp tags: | - ${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }} - ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }} + ${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }} + ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }} build-args: | PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }} PMMP_REPO=${{ github.repository }} @@ -84,8 +101,8 @@ jobs: push: true context: ./pocketmine-mp tags: | - ${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }} - ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }} + ${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }} + ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }} build-args: | PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }} PMMP_REPO=${{ github.repository }} diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index 906f227ea..697b6358b 100644 --- a/.github/workflows/discord-release-notify.yml +++ b/.github/workflows/discord-release-notify.yml @@ -4,6 +4,11 @@ on: release: types: - published + workflow_dispatch: + inputs: + release: + description: 'Release to make notification for' + required: true jobs: build: @@ -30,9 +35,17 @@ jobs: - name: Install Composer dependencies run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs - - name: Get actual tag name + - name: Get tag name id: tag-name - run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT + run: | + if [[ "${{ github.event_name }}" == "release" ]]; then + echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT + else + echo "Unsupported event type: ${{ github.event_name }}" + exit 1 + fi - name: Run webhook post script run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }} ${{ secrets.DISCORD_NEWS_PING_ROLE_ID }} diff --git a/.github/workflows/update-updater-api.yml b/.github/workflows/update-updater-api.yml index 3f42062fd..031950ba8 100644 --- a/.github/workflows/update-updater-api.yml +++ b/.github/workflows/update-updater-api.yml @@ -4,6 +4,11 @@ on: release: types: - published + workflow_dispatch: + inputs: + release: + description: 'Release to publish info for' + required: true jobs: build: @@ -19,9 +24,17 @@ jobs: repository: ${{ github.repository_owner }}/update.pmmp.io ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }} - - name: Get actual tag name + - name: Get tag name id: tag-name - run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT + run: | + if [[ "${{ github.event_name }}" == "release" ]]; then + echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT + else + echo "Unsupported event type: ${{ github.event_name }}" + exit 1 + fi - name: Download new release information run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json From 11612ed0e2a554ed94f654a625656e5f6b9a14b3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 11 Aug 2025 00:49:37 +0100 Subject: [PATCH 088/140] Fixed content log warning about recipe with missing ID --- src/network/mcpe/cache/CraftingDataCache.php | 16 ++++++++++++---- .../mcpe/handler/ItemStackRequestExecutor.php | 6 ++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/network/mcpe/cache/CraftingDataCache.php b/src/network/mcpe/cache/CraftingDataCache.php index 14523f74c..da0f37c44 100644 --- a/src/network/mcpe/cache/CraftingDataCache.php +++ b/src/network/mcpe/cache/CraftingDataCache.php @@ -56,6 +56,12 @@ final class CraftingDataCache{ */ private array $caches = []; + /** + * The client doesn't like recipes with ID 0 (as of 1.21.100) and complains about them in the content log + * This doesn't actually affect the function of the recipe, but it is annoying, so this offset fixes it + */ + public const RECIPE_ID_OFFSET = 1; + public function getCache(CraftingManager $manager) : CraftingDataPacket{ $id = spl_object_id($manager); if(!isset($this->caches[$id])){ @@ -82,6 +88,8 @@ final class CraftingDataCache{ $noUnlockingRequirement = new RecipeUnlockingRequirement(null); foreach($manager->getCraftingRecipeIndex() as $index => $recipe){ + //the client doesn't like recipes with an ID of 0, so we need to offset them + $recipeNetId = $index + self::RECIPE_ID_OFFSET; if($recipe instanceof ShapelessRecipe){ $typeTag = match($recipe->getType()){ ShapelessRecipeType::CRAFTING => CraftingRecipeBlockName::CRAFTING_TABLE, @@ -91,14 +99,14 @@ final class CraftingDataCache{ }; $recipesWithTypeIds[] = new ProtocolShapelessRecipe( CraftingDataPacket::ENTRY_SHAPELESS, - Binary::writeInt($index), + Binary::writeInt($recipeNetId), array_map($converter->coreRecipeIngredientToNet(...), $recipe->getIngredientList()), array_map($converter->coreItemStackToNet(...), $recipe->getResults()), $nullUUID, $typeTag, 50, $noUnlockingRequirement, - $index + $recipeNetId ); }elseif($recipe instanceof ShapedRecipe){ $inputs = []; @@ -110,7 +118,7 @@ final class CraftingDataCache{ } $recipesWithTypeIds[] = $r = new ProtocolShapedRecipe( CraftingDataPacket::ENTRY_SHAPED, - Binary::writeInt($index), + Binary::writeInt($recipeNetId), $inputs, array_map($converter->coreItemStackToNet(...), $recipe->getResults()), $nullUUID, @@ -118,7 +126,7 @@ final class CraftingDataCache{ 50, true, $noUnlockingRequirement, - $index, + $recipeNetId, ); }else{ //TODO: probably special recipe types diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index d71a1c6bf..4eddf3100 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -35,6 +35,7 @@ use pocketmine\inventory\transaction\TransactionBuilder; use pocketmine\inventory\transaction\TransactionBuilderInventory; use pocketmine\item\Durable; use pocketmine\item\Item; +use pocketmine\network\mcpe\cache\CraftingDataCache; use pocketmine\network\mcpe\InventoryManager; use pocketmine\network\mcpe\protocol\types\inventory\ContainerUIIds; use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName; @@ -238,9 +239,10 @@ class ItemStackRequestExecutor{ throw new ItemStackRequestProcessException("Cannot craft a recipe more than 256 times"); } $craftingManager = $this->player->getServer()->getCraftingManager(); - $recipe = $craftingManager->getCraftingRecipeFromIndex($recipeId); + $recipeIndex = $recipeId - CraftingDataCache::RECIPE_ID_OFFSET; + $recipe = $craftingManager->getCraftingRecipeFromIndex($recipeIndex); if($recipe === null){ - throw new ItemStackRequestProcessException("No such crafting recipe index: $recipeId"); + throw new ItemStackRequestProcessException("No such crafting recipe index: $recipeIndex"); } $this->specialTransaction = new CraftingTransaction($this->player, $craftingManager, [], $recipe, $repetitions); From c417ecd30d20520227b15e09eda87db492ab0a6a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Aug 2025 18:38:24 +0100 Subject: [PATCH 089/140] NetworkSession: Abort packet processing if handling triggered a disconnection this shows up when requesting invalid data during resource pack handling, for example --- src/network/mcpe/NetworkSession.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index bea3f8131..234ad4765 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -415,6 +415,11 @@ class NetworkSession{ $this->logger->debug($packet->getName() . ": " . base64_encode($buffer)); throw PacketHandlingException::wrap($e, "Error processing " . $packet->getName()); } + if(!$this->isConnected()){ + //handling this packet may have caused a disconnection + $this->logger->debug("Aborting batch processing due to server-side disconnection"); + break; + } } }catch(PacketDecodeException|BinaryDataException $e){ $this->logger->logException($e); From e375437439df51f7862b6b98318394643fcd6724 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Aug 2025 20:11:35 +0100 Subject: [PATCH 090/140] ResourcePacksPacketHandler: harden checks for client responses --- .../handler/ResourcePacksPacketHandler.php | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index a9ffae6f7..d98d8e9ad 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -36,6 +36,7 @@ use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry; use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry; use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType; +use pocketmine\network\PacketHandlingException; use pocketmine\resourcepacks\ResourcePack; use Ramsey\Uuid\Uuid; use function array_keys; @@ -43,6 +44,7 @@ use function array_map; use function ceil; use function count; use function implode; +use function sprintf; use function strpos; use function strtolower; use function substr; @@ -66,6 +68,9 @@ class ResourcePacksPacketHandler extends PacketHandler{ */ private array $resourcePacksById = []; + private bool $requestedMetadata = false; + private bool $requestedStack = false; + /** @var bool[][] uuid => [chunk index => hasSent] */ private array $downloadedChunks = []; @@ -140,6 +145,21 @@ class ResourcePacksPacketHandler extends PacketHandler{ $this->session->disconnect("Refused resource packs", "You must accept resource packs to join this server.", true); break; case ResourcePackClientResponsePacket::STATUS_SEND_PACKS: + if($this->requestedMetadata){ + throw new PacketHandlingException("Cannot request resource pack metadata multiple times"); + } + $this->requestedMetadata = true; + + if($this->requestedStack){ + //client already told us that they have all the packs, they shouldn't be asking for more + throw new PacketHandlingException("Cannot request resource pack metadata after resource pack stack"); + } + + if(count($packet->packIds) > count($this->resourcePacksById)){ + throw new PacketHandlingException(sprintf("Requested metadata for more resource packs (%d) than available on the server (%d)", count($packet->packIds), count($this->resourcePacksById))); + } + + $seen = []; foreach($packet->packIds as $uuid){ //dirty hack for mojang's dirty hack for versions $splitPos = strpos($uuid, "_"); @@ -153,6 +173,9 @@ class ResourcePacksPacketHandler extends PacketHandler{ $this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", array_keys($this->resourcePacksById))); return false; } + if(isset($seen[$pack->getPackId()])){ + throw new PacketHandlingException("Repeated metadata request for pack $uuid"); + } $this->session->sendDataPacket(ResourcePackDataInfoPacket::create( $pack->getPackId(), @@ -163,11 +186,16 @@ class ResourcePacksPacketHandler extends PacketHandler{ false, ResourcePackType::RESOURCES //TODO: this might be an addon (not behaviour pack), needed to properly support client-side custom items )); + $seen[$pack->getPackId()] = true; } $this->session->getLogger()->debug("Player requested download of " . count($packet->packIds) . " resource packs"); - break; case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS: + if($this->requestedStack){ + throw new PacketHandlingException("Cannot request resource pack stack multiple times"); + } + $this->requestedStack = true; + $stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{ return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks }, $this->resourcePackStack); From 442049d564dbf4af4d4a2c741501793b1a8b600a Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 14 Aug 2025 11:37:24 +0100 Subject: [PATCH 091/140] Prepare 5.32.1 release (#6766) --- changelogs/5.32.md | 8 ++++++++ src/VersionInfo.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changelogs/5.32.md b/changelogs/5.32.md index 16b9aaff4..414330351 100644 --- a/changelogs/5.32.md +++ b/changelogs/5.32.md @@ -15,3 +15,11 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if ## Fixes - Fixed deadlock on RakLib thread crash (e.g. due to port binding failure). + +# 5.32.1 +Released 14th August 2025. + +## Fixes +- Hardened checks when processing resource pack sending during player logins. +- Fixed content log warning about crafting recipe with missing ID. +- Fixed packets in a batch still being processed after one of them caused the session to be terminated. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index f28239816..77a9a2bee 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.32.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From f633416f0595e81e4c176fc00bd85652afe3f4bb Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:38:22 +0000 Subject: [PATCH 092/140] 5.32.2 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/16962847004 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 77a9a2bee..b3f37677b 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.32.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.32.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From cb7a562c8b0a8577b40f8689c52bf03b13a3cf8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 00:17:24 +0000 Subject: [PATCH 093/140] Bump the github-actions group across 1 directory with 2 updates (#6767) --- .github/workflows/build-docker-image.yml | 2 +- .github/workflows/copilot-setup-steps.yml | 4 ++-- .github/workflows/discord-release-notify.yml | 4 ++-- .github/workflows/draft-release-pr-check.yml | 6 +++--- .github/workflows/draft-release.yml | 4 ++-- .github/workflows/main-php-matrix.yml | 8 ++++---- .github/workflows/main.yml | 6 +++--- .github/workflows/update-updater-api.yml | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 9319f9e2f..acfc3d3a7 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -33,7 +33,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Clone pmmp/PocketMine-Docker repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: pmmp/PocketMine-Docker fetch-depth: 1 diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index ef0b122e1..c644876e0 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -18,7 +18,7 @@ jobs: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP uses: pmmp/setup-php-action@3.2.0 @@ -41,7 +41,7 @@ jobs: run: composer install --prefer-dist --no-interaction - name: Clone extension stubs - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: pmmp/phpstorm-stubs path: extension-stubs diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index 697b6358b..e15134fdb 100644 --- a/.github/workflows/discord-release-notify.yml +++ b/.github/workflows/discord-release-notify.yml @@ -15,10 +15,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.34.1 + uses: shivammathur/setup-php@2.35.3 with: php-version: 8.2 diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml index b2575f9be..cf6036968 100644 --- a/.github/workflows/draft-release-pr-check.yml +++ b/.github/workflows/draft-release-pr-check.yml @@ -30,7 +30,7 @@ jobs: valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Check IS_DEVELOPMENT_BUILD flag id: validate @@ -46,10 +46,10 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP - uses: shivammathur/setup-php@2.34.1 + uses: shivammathur/setup-php@2.35.3 with: php-version: 8.2 diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 014ea531c..052635234 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -82,12 +82,12 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: true - name: Setup PHP - uses: shivammathur/setup-php@2.34.1 + uses: shivammathur/setup-php@2.35.3 with: php-version: ${{ env.PHP_VERSION }} diff --git a/.github/workflows/main-php-matrix.yml b/.github/workflows/main-php-matrix.yml index 015a33188..7637a3956 100644 --- a/.github/workflows/main-php-matrix.yml +++ b/.github/workflows/main-php-matrix.yml @@ -27,7 +27,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP uses: pmmp/setup-php-action@3.2.0 @@ -59,7 +59,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP uses: pmmp/setup-php-action@3.2.0 @@ -91,7 +91,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: true @@ -125,7 +125,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP uses: pmmp/setup-php-action@3.2.0 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4d020c10b..9d5f282bb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,10 +25,10 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.34.1 + uses: shivammathur/setup-php@2.35.3 with: php-version: 8.3 tools: php-cs-fixer:3.75 @@ -45,7 +45,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Run ShellCheck uses: ludeeus/action-shellcheck@2.0.0 diff --git a/.github/workflows/update-updater-api.yml b/.github/workflows/update-updater-api.yml index 031950ba8..841fa7d44 100644 --- a/.github/workflows/update-updater-api.yml +++ b/.github/workflows/update-updater-api.yml @@ -19,7 +19,7 @@ jobs: - name: Install jq run: sudo apt update && sudo apt install jq -y - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: repository: ${{ github.repository_owner }}/update.pmmp.io ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }} From 1e8612cfc895d3884ecbc91f0d60c48edc959ffc Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 Date: Fri, 15 Aug 2025 21:39:13 +0200 Subject: [PATCH 094/140] BlockObjectToStateSerializer: Avoid unnecessary Writer and Closure (#6759) --------- Co-authored-by: Dylan K. Taylor --- .../convert/BlockObjectToStateSerializer.php | 264 +++++++++--------- 1 file changed, 132 insertions(+), 132 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 27d550f13..49f0269ed 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -195,8 +195,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ * These callables actually accept Block, but for the sake of type completeness, it has to be never, since we can't * describe the bottom type of a type hierarchy only containing Block. * - * @var \Closure[] - * @phpstan-var array + * @var (\Closure|BlockStateData)[] + * @phpstan-var array */ private array $serializers = []; @@ -233,17 +233,18 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ /** * @phpstan-template TBlockType of Block * @phpstan-param TBlockType $block - * @phpstan-param \Closure(TBlockType) : Writer $serializer + * @phpstan-param \Closure(TBlockType) : (Writer|BlockStateData)|Writer|BlockStateData $serializer */ - public function map(Block $block, \Closure $serializer) : void{ + public function map(Block $block, \Closure|Writer|BlockStateData $serializer) : void{ if(isset($this->serializers[$block->getTypeId()])){ throw new \InvalidArgumentException("Block type ID " . $block->getTypeId() . " already has a serializer registered"); } - $this->serializers[$block->getTypeId()] = $serializer; + //writer accepted for convenience only + $this->serializers[$block->getTypeId()] = $serializer instanceof Writer ? $serializer->getBlockStateData() : $serializer; } public function mapSimple(Block $block, string $id) : void{ - $this->map($block, fn() => Writer::create($id)); + $this->map($block, BlockStateData::current($id, [])); } public function mapSlab(Slab $block, string $singleId, string $doubleId) : void{ @@ -272,19 +273,21 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ throw new BlockStateSerializeException("No serializer registered for " . get_class($blockState) . " with type ID $typeId"); } + if($locatedSerializer instanceof BlockStateData){ //static data, not dependent on state + return $locatedSerializer; + } + /** * TODO: there is no guarantee that this type actually matches that of $blockState - a plugin may have stolen * the type ID of the block (which never makes sense, even in a world where overriding block types is a thing). * In the future we'll need some way to guarantee that type IDs are never reused (perhaps spl_object_id()?) * - * @var \Closure $serializer - * @phpstan-var \Closure(TBlockType) : Writer $serializer + * @var \Closure $locatedSerializer + * @phpstan-var \Closure(TBlockType) : (Writer|BlockStateData) $locatedSerializer */ - $serializer = $locatedSerializer; + $result = $locatedSerializer($blockState); - /** @var Writer $writer */ - $writer = $serializer($blockState); - return $writer->getBlockStateData(); + return $result instanceof Writer ? $result->getBlockStateData() : $result; } private function registerCandleSerializers() : void{ @@ -330,7 +333,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ } public function registerFlatColorBlockSerializers() : void{ - $this->map(Blocks::STAINED_HARDENED_GLASS(), fn(StainedHardenedGlass $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::STAINED_HARDENED_GLASS(), fn(StainedHardenedGlass $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS, DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS, DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS, @@ -347,9 +350,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::HARD_RED_STAINED_GLASS, DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS, DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS, - })); + }, [])); - $this->map(Blocks::STAINED_HARDENED_GLASS_PANE(), fn(StainedHardenedGlassPane $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::STAINED_HARDENED_GLASS_PANE(), fn(StainedHardenedGlassPane $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS_PANE, DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS_PANE, DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS_PANE, @@ -366,7 +369,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::HARD_RED_STAINED_GLASS_PANE, DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS_PANE, DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS_PANE, - })); + }, [])); $this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{ return Writer::create(match($block->getColor()){ @@ -390,7 +393,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeHorizontalFacing($block->getFacing()); }); - $this->map(Blocks::WOOL(), fn(Wool $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::WOOL(), fn(Wool $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_WOOL, DyeColor::BLUE => Ids::BLUE_WOOL, DyeColor::BROWN => Ids::BROWN_WOOL, @@ -407,9 +410,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_WOOL, DyeColor::WHITE => Ids::WHITE_WOOL, DyeColor::YELLOW => Ids::YELLOW_WOOL, - })); + }, [])); - $this->map(Blocks::CARPET(), fn(Carpet $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::CARPET(), fn(Carpet $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_CARPET, DyeColor::BLUE => Ids::BLUE_CARPET, DyeColor::BROWN => Ids::BROWN_CARPET, @@ -426,9 +429,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_CARPET, DyeColor::WHITE => Ids::WHITE_CARPET, DyeColor::YELLOW => Ids::YELLOW_CARPET, - })); + }, [])); - $this->map(Blocks::DYED_SHULKER_BOX(), fn(DyedShulkerBox $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::DYED_SHULKER_BOX(), fn(DyedShulkerBox $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_SHULKER_BOX, DyeColor::BLUE => Ids::BLUE_SHULKER_BOX, DyeColor::BROWN => Ids::BROWN_SHULKER_BOX, @@ -445,9 +448,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_SHULKER_BOX, DyeColor::WHITE => Ids::WHITE_SHULKER_BOX, DyeColor::YELLOW => Ids::YELLOW_SHULKER_BOX, - })); + }, [])); - $this->map(Blocks::CONCRETE(), fn(Concrete $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::CONCRETE(), fn(Concrete $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_CONCRETE, DyeColor::BLUE => Ids::BLUE_CONCRETE, DyeColor::BROWN => Ids::BROWN_CONCRETE, @@ -464,9 +467,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_CONCRETE, DyeColor::WHITE => Ids::WHITE_CONCRETE, DyeColor::YELLOW => Ids::YELLOW_CONCRETE, - })); + }, [])); - $this->map(Blocks::CONCRETE_POWDER(), fn(ConcretePowder $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::CONCRETE_POWDER(), fn(ConcretePowder $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_CONCRETE_POWDER, DyeColor::BLUE => Ids::BLUE_CONCRETE_POWDER, DyeColor::BROWN => Ids::BROWN_CONCRETE_POWDER, @@ -483,9 +486,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_CONCRETE_POWDER, DyeColor::WHITE => Ids::WHITE_CONCRETE_POWDER, DyeColor::YELLOW => Ids::YELLOW_CONCRETE_POWDER, - })); + }, [])); - $this->map(Blocks::STAINED_CLAY(), fn(StainedHardenedClay $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::STAINED_CLAY(), fn(StainedHardenedClay $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_TERRACOTTA, DyeColor::BLUE => Ids::BLUE_TERRACOTTA, DyeColor::BROWN => Ids::BROWN_TERRACOTTA, @@ -502,9 +505,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_TERRACOTTA, DyeColor::WHITE => Ids::WHITE_TERRACOTTA, DyeColor::YELLOW => Ids::YELLOW_TERRACOTTA, - })); + }, [])); - $this->map(Blocks::STAINED_GLASS(), fn(StainedGlass $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::STAINED_GLASS(), fn(StainedGlass $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_STAINED_GLASS, DyeColor::BLUE => Ids::BLUE_STAINED_GLASS, DyeColor::BROWN => Ids::BROWN_STAINED_GLASS, @@ -521,9 +524,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_STAINED_GLASS, DyeColor::WHITE => Ids::WHITE_STAINED_GLASS, DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS, - })); + }, [])); - $this->map(Blocks::STAINED_GLASS_PANE(), fn(StainedGlassPane $block) => Writer::create(match($block->getColor()){ + $this->map(Blocks::STAINED_GLASS_PANE(), fn(StainedGlassPane $block) => BlockStateData::current(match($block->getColor()){ DyeColor::BLACK => Ids::BLACK_STAINED_GLASS_PANE, DyeColor::BLUE => Ids::BLUE_STAINED_GLASS_PANE, DyeColor::BROWN => Ids::BROWN_STAINED_GLASS_PANE, @@ -540,19 +543,17 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::RED => Ids::RED_STAINED_GLASS_PANE, DyeColor::WHITE => Ids::WHITE_STAINED_GLASS_PANE, DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS_PANE, - })); + }, [])); } private function registerFlatCoralSerializers() : void{ - $this->map(Blocks::CORAL(), fn(Coral $block) => Writer::create( - match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL : Ids::BRAIN_CORAL, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL : Ids::BUBBLE_CORAL, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL : Ids::FIRE_CORAL, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL : Ids::HORN_CORAL, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL, - } - )); + $this->map(Blocks::CORAL(), fn(Coral $block) => BlockStateData::current(match($block->getCoralType()){ + CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL : Ids::BRAIN_CORAL, + CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL : Ids::BUBBLE_CORAL, + CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL : Ids::FIRE_CORAL, + CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL : Ids::HORN_CORAL, + CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL, + }, [])); $this->map(Blocks::CORAL_FAN(), fn(FloorCoralFan $block) => Writer::create( match($block->getCoralType()){ @@ -568,15 +569,13 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ default => throw new BlockStateSerializeException("Invalid axis {$axis}"), })); - $this->map(Blocks::CORAL_BLOCK(), fn(CoralBlock $block) => Writer::create( - match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_BLOCK : Ids::BRAIN_CORAL_BLOCK, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_BLOCK : Ids::BUBBLE_CORAL_BLOCK, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_BLOCK : Ids::FIRE_CORAL_BLOCK, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_BLOCK : Ids::HORN_CORAL_BLOCK, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_BLOCK : Ids::TUBE_CORAL_BLOCK, - } - )); + $this->map(Blocks::CORAL_BLOCK(), fn(CoralBlock $block) => BlockStateData::current(match($block->getCoralType()){ + CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_BLOCK : Ids::BRAIN_CORAL_BLOCK, + CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_BLOCK : Ids::BUBBLE_CORAL_BLOCK, + CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_BLOCK : Ids::FIRE_CORAL_BLOCK, + CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_BLOCK : Ids::HORN_CORAL_BLOCK, + CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_BLOCK : Ids::TUBE_CORAL_BLOCK, + }, [])); $this->map(Blocks::WALL_CORAL_FAN(), fn(WallCoralFan $block) => Writer::create( match($block->getCoralType()){ @@ -591,7 +590,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ } private function registerCauldronSerializers() : void{ - $this->map(Blocks::CAULDRON(), fn() => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); + $this->map(Blocks::CAULDRON(), Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); $this->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel())); //potion cauldrons store their real information in the block actor data $this->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); @@ -798,52 +797,60 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ } private function registerCopperSerializers() : void{ - $this->map(Blocks::COPPER(), function(Copper $block) : Writer{ + $this->map(Blocks::COPPER(), function(Copper $block) : BlockStateData{ $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) : - Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER) + return BlockStateData::current( + $block->isWaxed() ? + Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) : + Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER), + [] ); }); - $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{ + $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : BlockStateData{ $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_CHISELED_COPPER, - Ids::WAXED_EXPOSED_CHISELED_COPPER, - Ids::WAXED_WEATHERED_CHISELED_COPPER, - Ids::WAXED_OXIDIZED_CHISELED_COPPER - ) : - Helper::selectCopperId($oxidation, - Ids::CHISELED_COPPER, - Ids::EXPOSED_CHISELED_COPPER, - Ids::WEATHERED_CHISELED_COPPER, - Ids::OXIDIZED_CHISELED_COPPER - ) + return BlockStateData::current( + $block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_CHISELED_COPPER, + Ids::WAXED_EXPOSED_CHISELED_COPPER, + Ids::WAXED_WEATHERED_CHISELED_COPPER, + Ids::WAXED_OXIDIZED_CHISELED_COPPER + ) : + Helper::selectCopperId($oxidation, + Ids::CHISELED_COPPER, + Ids::EXPOSED_CHISELED_COPPER, + Ids::WEATHERED_CHISELED_COPPER, + Ids::OXIDIZED_CHISELED_COPPER + ), + [] ); }); - $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{ + $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : BlockStateData{ $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_COPPER_GRATE, - Ids::WAXED_EXPOSED_COPPER_GRATE, - Ids::WAXED_WEATHERED_COPPER_GRATE, - Ids::WAXED_OXIDIZED_COPPER_GRATE - ) : - Helper::selectCopperId($oxidation, - Ids::COPPER_GRATE, - Ids::EXPOSED_COPPER_GRATE, - Ids::WEATHERED_COPPER_GRATE, - Ids::OXIDIZED_COPPER_GRATE - ) + return BlockStateData::current( + $block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_COPPER_GRATE, + Ids::WAXED_EXPOSED_COPPER_GRATE, + Ids::WAXED_WEATHERED_COPPER_GRATE, + Ids::WAXED_OXIDIZED_COPPER_GRATE + ) : + Helper::selectCopperId($oxidation, + Ids::COPPER_GRATE, + Ids::EXPOSED_COPPER_GRATE, + Ids::WEATHERED_COPPER_GRATE, + Ids::OXIDIZED_COPPER_GRATE + ), + [] ); }); - $this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{ + $this->map(Blocks::CUT_COPPER(), function(Copper $block) : BlockStateData{ $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) : - Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER) + return BlockStateData::current( + $block->isWaxed() ? + Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) : + Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER), + [] ); }); $this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{ @@ -1279,7 +1286,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); }); - $this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::MUSHROOM_STEM) + $this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); $this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create( match($stage = $block->getStage()){ @@ -1476,13 +1483,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSlab(Blocks::DIORITE_SLAB(), Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB); $this->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS); $this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::DIORITE_WALL))); - $this->map(Blocks::DIRT(), function(Dirt $block) : Writer{ - return Writer::create(match($block->getDirtType()){ - DirtType::NORMAL => Ids::DIRT, - DirtType::COARSE => Ids::COARSE_DIRT, - DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, - }); - }); + $this->map(Blocks::DIRT(), fn(Dirt $block) => BlockStateData::current(match($block->getDirtType()){ + DirtType::NORMAL => Ids::DIRT, + DirtType::COARSE => Ids::COARSE_DIRT, + DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, + }, [])); $this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::TALL_GRASS))); $this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::ELEMENT_CONSTRUCTOR))); $this->map(Blocks::ENDER_CHEST(), function(EnderChest $block) : Writer{ @@ -1510,10 +1515,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::FIRE) ->writeInt(StateNames::AGE, $block->getAge()); }); - $this->map(Blocks::FLOWER_POT(), function() : Writer{ - return Writer::create(Ids::FLOWER_POT) - ->writeBool(StateNames::UPDATE_BIT, false); //to keep MCPE happy - }); + $this->map(Blocks::FLOWER_POT(), Writer::create(Ids::FLOWER_POT) + ->writeBool(StateNames::UPDATE_BIT, false) //to keep MCPE happy + ); $this->map(Blocks::FROGLIGHT(), function(Froglight $block){ return Writer::create(match($block->getFroglightType()){ FroglightType::OCHRE => Ids::OCHRE_FROGLIGHT, @@ -1579,27 +1583,25 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ LeverFacing::EAST => StringValues::LEVER_DIRECTION_EAST, }); }); - $this->map(Blocks::LIGHT(), function(Light $block) : Writer{ - return Writer::create(match($block->getLightLevel()){ - 0 => Ids::LIGHT_BLOCK_0, - 1 => Ids::LIGHT_BLOCK_1, - 2 => Ids::LIGHT_BLOCK_2, - 3 => Ids::LIGHT_BLOCK_3, - 4 => Ids::LIGHT_BLOCK_4, - 5 => Ids::LIGHT_BLOCK_5, - 6 => Ids::LIGHT_BLOCK_6, - 7 => Ids::LIGHT_BLOCK_7, - 8 => Ids::LIGHT_BLOCK_8, - 9 => Ids::LIGHT_BLOCK_9, - 10 => Ids::LIGHT_BLOCK_10, - 11 => Ids::LIGHT_BLOCK_11, - 12 => Ids::LIGHT_BLOCK_12, - 13 => Ids::LIGHT_BLOCK_13, - 14 => Ids::LIGHT_BLOCK_14, - 15 => Ids::LIGHT_BLOCK_15, - default => throw new BlockStateSerializeException("Invalid light level " . $block->getLightLevel()), - }); - }); + $this->map(Blocks::LIGHT(), fn(Light $block) => BlockStateData::current(match($block->getLightLevel()){ + 0 => Ids::LIGHT_BLOCK_0, + 1 => Ids::LIGHT_BLOCK_1, + 2 => Ids::LIGHT_BLOCK_2, + 3 => Ids::LIGHT_BLOCK_3, + 4 => Ids::LIGHT_BLOCK_4, + 5 => Ids::LIGHT_BLOCK_5, + 6 => Ids::LIGHT_BLOCK_6, + 7 => Ids::LIGHT_BLOCK_7, + 8 => Ids::LIGHT_BLOCK_8, + 9 => Ids::LIGHT_BLOCK_9, + 10 => Ids::LIGHT_BLOCK_10, + 11 => Ids::LIGHT_BLOCK_11, + 12 => Ids::LIGHT_BLOCK_12, + 13 => Ids::LIGHT_BLOCK_13, + 14 => Ids::LIGHT_BLOCK_14, + 15 => Ids::LIGHT_BLOCK_15, + default => throw new BlockStateSerializeException("Invalid light level " . $block->getLightLevel()), + }, [])); $this->map(Blocks::LIGHTNING_ROD(), function(LightningRod $block) : Writer{ return Writer::create(Ids::LIGHTNING_ROD) ->writeFacingDirection($block->getFacing()); @@ -1626,7 +1628,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::MUD_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::MUD_BRICK_WALL))); $this->map(Blocks::MUDDY_MANGROVE_ROOTS(), fn(SimplePillar $block) => Writer::create(Ids::MUDDY_MANGROVE_ROOTS) ->writePillarAxis($block->getAxis())); - $this->map(Blocks::MUSHROOM_STEM(), fn() => Writer::create(Ids::MUSHROOM_STEM) + $this->map(Blocks::MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); $this->mapSlab(Blocks::NETHER_BRICK_SLAB(), Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); @@ -1698,12 +1700,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSlab(Blocks::PRISMARINE_SLAB(), Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB); $this->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS); $this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::PRISMARINE_WALL))); - $this->map(Blocks::PUMPKIN(), function() : Writer{ - return Writer::create(Ids::PUMPKIN) - ->writeCardinalHorizontalFacing(Facing::SOUTH); //no longer used - }); + $this->map(Blocks::PUMPKIN(), Writer::create(Ids::PUMPKIN) + ->writeCardinalHorizontalFacing(Facing::SOUTH) //no longer used + ); $this->map(Blocks::PUMPKIN_STEM(), fn(PumpkinStem $block) => Helper::encodeStem($block, new Writer(Ids::PUMPKIN_STEM))); - $this->map(Blocks::PURPUR(), fn() => Writer::create(Ids::PURPUR_BLOCK)->writePillarAxis(Axis::Y)); + $this->map(Blocks::PURPUR(), Writer::create(Ids::PURPUR_BLOCK)->writePillarAxis(Axis::Y)); $this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_PURPLE))); $this->map(Blocks::PURPUR_PILLAR(), function(SimplePillar $block) : Writer{ return Writer::create(Ids::PURPUR_PILLAR) @@ -1711,7 +1712,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); $this->mapSlab(Blocks::PURPUR_SLAB(), Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB); $this->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS); - $this->map(Blocks::QUARTZ(), fn() => Helper::encodeQuartz(Axis::Y, Writer::create(Ids::QUARTZ_BLOCK))); + $this->map(Blocks::QUARTZ(), Helper::encodeQuartz(Axis::Y, Writer::create(Ids::QUARTZ_BLOCK))); $this->map(Blocks::QUARTZ_PILLAR(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::QUARTZ_PILLAR))); $this->mapSlab(Blocks::QUARTZ_SLAB(), Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB); $this->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS); @@ -1774,7 +1775,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); }); $this->map(Blocks::SMOKER(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::SMOKER, Ids::LIT_SMOKER)); - $this->map(Blocks::SMOOTH_QUARTZ(), fn() => Helper::encodeQuartz(Axis::Y, Writer::create(Ids::SMOOTH_QUARTZ))); + $this->map(Blocks::SMOOTH_QUARTZ(), Helper::encodeQuartz(Axis::Y, Writer::create(Ids::SMOOTH_QUARTZ))); $this->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB); $this->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS); $this->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB); @@ -1792,10 +1793,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeCardinalHorizontalFacing($block->getFacing()) ->writeBool(StateNames::EXTINGUISHED, !$block->isLit()); }); - $this->map(Blocks::SOUL_FIRE(), function() : Writer{ - return Writer::create(Ids::SOUL_FIRE) - ->writeInt(StateNames::AGE, 0); //useless for soul fire, we don't track it - }); + $this->map(Blocks::SOUL_FIRE(), Writer::create(Ids::SOUL_FIRE) + ->writeInt(StateNames::AGE, 0) //useless for soul fire, we don't track it + ); $this->map(Blocks::SOUL_LANTERN(), function(Lantern $block) : Writer{ return Writer::create(Ids::SOUL_LANTERN) ->writeBool(StateNames::HANGING, $block->isHanging()); From e89523ce66729f79ef63c2edce676e0c1e1261bd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Aug 2025 22:02:12 +0100 Subject: [PATCH 095/140] First look at flattened ID specialisation for block serializers in the future we should be able to unify these, similarly to simple mappings. unifying blocks with states will, however, be considerably more work. only color benefits from this so far --- .../convert/BlockObjectToStateSerializer.php | 334 ++++++------------ .../BlockStateToObjectDeserializer.php | 330 ++++++----------- .../bedrock/block/convert/StringEnumMap.php | 78 ++++ .../bedrock/block/convert/ValueMappings.php | 83 +++++ 4 files changed, 377 insertions(+), 448 deletions(-) create mode 100644 src/data/bedrock/block/convert/StringEnumMap.php create mode 100644 src/data/bedrock/block/convert/ValueMappings.php diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 49f0269ed..6b545bd01 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -290,86 +290,72 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return $result instanceof Writer ? $result->getBlockStateData() : $result; } + /** + * @phpstan-template TBlock of Block + * @phpstan-template TEnum of \UnitEnum + * + * @phpstan-param TBlock $block + * @phpstan-param StringEnumMap $mapProperty + * @phpstan-param \Closure(TBlock) : TEnum $getProperty + * @phpstan-param ?\Closure(TBlock, Writer) : Writer $extra + */ + public function mapFlattenedEnum( + Block $block, + StringEnumMap $mapProperty, + string $prefix, + string $suffix, + \Closure $getProperty, + ?\Closure $extra = null + ) : void{ + $this->map($block, function(Block $block) use ($getProperty, $mapProperty, $prefix, $suffix, $extra) : Writer{ + $property = $getProperty($block); + $infix = $mapProperty->enumToValue($property); + $id = $prefix . $infix . $suffix; + $writer = new Writer($id); + if($extra !== null){ + $extra($block, $writer); + } + return $writer; + }); + } + private function registerCandleSerializers() : void{ $this->map(Blocks::CANDLE(), fn(Candle $block) => Helper::encodeCandle($block, new Writer(Ids::CANDLE))); - $this->map(Blocks::DYED_CANDLE(), fn(DyedCandle $block) => Helper::encodeCandle($block, new Writer(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_CANDLE, - DyeColor::BLUE => Ids::BLUE_CANDLE, - DyeColor::BROWN => Ids::BROWN_CANDLE, - DyeColor::CYAN => Ids::CYAN_CANDLE, - DyeColor::GRAY => Ids::GRAY_CANDLE, - DyeColor::GREEN => Ids::GREEN_CANDLE, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CANDLE, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CANDLE, - DyeColor::LIME => Ids::LIME_CANDLE, - DyeColor::MAGENTA => Ids::MAGENTA_CANDLE, - DyeColor::ORANGE => Ids::ORANGE_CANDLE, - DyeColor::PINK => Ids::PINK_CANDLE, - DyeColor::PURPLE => Ids::PURPLE_CANDLE, - DyeColor::RED => Ids::RED_CANDLE, - DyeColor::WHITE => Ids::WHITE_CANDLE, - DyeColor::YELLOW => Ids::YELLOW_CANDLE, - }))); + $this->mapFlattenedEnum( + Blocks::DYED_CANDLE(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_candle", + fn(DyedCandle $block) => $block->getColor(), + Helper::encodeCandle(...) + ); $this->map(Blocks::CAKE_WITH_CANDLE(), fn(CakeWithCandle $block) => Writer::create(Ids::CANDLE_CAKE) ->writeBool(StateNames::LIT, $block->isLit())); - $this->map(Blocks::CAKE_WITH_DYED_CANDLE(), fn(CakeWithDyedCandle $block) => Writer::create(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_CANDLE_CAKE, - DyeColor::BLUE => Ids::BLUE_CANDLE_CAKE, - DyeColor::BROWN => Ids::BROWN_CANDLE_CAKE, - DyeColor::CYAN => Ids::CYAN_CANDLE_CAKE, - DyeColor::GRAY => Ids::GRAY_CANDLE_CAKE, - DyeColor::GREEN => Ids::GREEN_CANDLE_CAKE, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CANDLE_CAKE, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CANDLE_CAKE, - DyeColor::LIME => Ids::LIME_CANDLE_CAKE, - DyeColor::MAGENTA => Ids::MAGENTA_CANDLE_CAKE, - DyeColor::ORANGE => Ids::ORANGE_CANDLE_CAKE, - DyeColor::PINK => Ids::PINK_CANDLE_CAKE, - DyeColor::PURPLE => Ids::PURPLE_CANDLE_CAKE, - DyeColor::RED => Ids::RED_CANDLE_CAKE, - DyeColor::WHITE => Ids::WHITE_CANDLE_CAKE, - DyeColor::YELLOW => Ids::YELLOW_CANDLE_CAKE, - })->writeBool(StateNames::LIT, $block->isLit())); + $this->mapFlattenedEnum( + Blocks::CAKE_WITH_DYED_CANDLE(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_candle_cake", + fn(CakeWithDyedCandle $block) => $block->getColor(), + fn(CakeWithDyedCandle $block, Writer $writer) => $writer->writeBool(StateNames::LIT, $block->isLit()) + ); } public function registerFlatColorBlockSerializers() : void{ - $this->map(Blocks::STAINED_HARDENED_GLASS(), fn(StainedHardenedGlass $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS, - DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS, - DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS, - DyeColor::CYAN => Ids::HARD_CYAN_STAINED_GLASS, - DyeColor::GRAY => Ids::HARD_GRAY_STAINED_GLASS, - DyeColor::GREEN => Ids::HARD_GREEN_STAINED_GLASS, - DyeColor::LIGHT_BLUE => Ids::HARD_LIGHT_BLUE_STAINED_GLASS, - DyeColor::LIGHT_GRAY => Ids::HARD_LIGHT_GRAY_STAINED_GLASS, - DyeColor::LIME => Ids::HARD_LIME_STAINED_GLASS, - DyeColor::MAGENTA => Ids::HARD_MAGENTA_STAINED_GLASS, - DyeColor::ORANGE => Ids::HARD_ORANGE_STAINED_GLASS, - DyeColor::PINK => Ids::HARD_PINK_STAINED_GLASS, - DyeColor::PURPLE => Ids::HARD_PURPLE_STAINED_GLASS, - DyeColor::RED => Ids::HARD_RED_STAINED_GLASS, - DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS, - DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS, - }, [])); - - $this->map(Blocks::STAINED_HARDENED_GLASS_PANE(), fn(StainedHardenedGlassPane $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS_PANE, - DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS_PANE, - DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS_PANE, - DyeColor::CYAN => Ids::HARD_CYAN_STAINED_GLASS_PANE, - DyeColor::GRAY => Ids::HARD_GRAY_STAINED_GLASS_PANE, - DyeColor::GREEN => Ids::HARD_GREEN_STAINED_GLASS_PANE, - DyeColor::LIGHT_BLUE => Ids::HARD_LIGHT_BLUE_STAINED_GLASS_PANE, - DyeColor::LIGHT_GRAY => Ids::HARD_LIGHT_GRAY_STAINED_GLASS_PANE, - DyeColor::LIME => Ids::HARD_LIME_STAINED_GLASS_PANE, - DyeColor::MAGENTA => Ids::HARD_MAGENTA_STAINED_GLASS_PANE, - DyeColor::ORANGE => Ids::HARD_ORANGE_STAINED_GLASS_PANE, - DyeColor::PINK => Ids::HARD_PINK_STAINED_GLASS_PANE, - DyeColor::PURPLE => Ids::HARD_PURPLE_STAINED_GLASS_PANE, - DyeColor::RED => Ids::HARD_RED_STAINED_GLASS_PANE, - DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS_PANE, - DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS_PANE, - }, [])); + $this->mapFlattenedEnum( + Blocks::STAINED_HARDENED_GLASS(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:hard_", + "_stained_glass", + fn(StainedHardenedGlass $block) => $block->getColor() + ); + $this->mapFlattenedEnum( + Blocks::STAINED_HARDENED_GLASS_PANE(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:hard_", + "_stained_glass_pane", + fn(StainedHardenedGlassPane $block) => $block->getColor(), + ); $this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{ return Writer::create(match($block->getColor()){ @@ -393,157 +379,67 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeHorizontalFacing($block->getFacing()); }); - $this->map(Blocks::WOOL(), fn(Wool $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_WOOL, - DyeColor::BLUE => Ids::BLUE_WOOL, - DyeColor::BROWN => Ids::BROWN_WOOL, - DyeColor::CYAN => Ids::CYAN_WOOL, - DyeColor::GRAY => Ids::GRAY_WOOL, - DyeColor::GREEN => Ids::GREEN_WOOL, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_WOOL, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_WOOL, - DyeColor::LIME => Ids::LIME_WOOL, - DyeColor::MAGENTA => Ids::MAGENTA_WOOL, - DyeColor::ORANGE => Ids::ORANGE_WOOL, - DyeColor::PINK => Ids::PINK_WOOL, - DyeColor::PURPLE => Ids::PURPLE_WOOL, - DyeColor::RED => Ids::RED_WOOL, - DyeColor::WHITE => Ids::WHITE_WOOL, - DyeColor::YELLOW => Ids::YELLOW_WOOL, - }, [])); + $this->mapFlattenedEnum( + Blocks::WOOL(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_wool", + fn(Wool $block) => $block->getColor() + ); - $this->map(Blocks::CARPET(), fn(Carpet $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_CARPET, - DyeColor::BLUE => Ids::BLUE_CARPET, - DyeColor::BROWN => Ids::BROWN_CARPET, - DyeColor::CYAN => Ids::CYAN_CARPET, - DyeColor::GRAY => Ids::GRAY_CARPET, - DyeColor::GREEN => Ids::GREEN_CARPET, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CARPET, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CARPET, - DyeColor::LIME => Ids::LIME_CARPET, - DyeColor::MAGENTA => Ids::MAGENTA_CARPET, - DyeColor::ORANGE => Ids::ORANGE_CARPET, - DyeColor::PINK => Ids::PINK_CARPET, - DyeColor::PURPLE => Ids::PURPLE_CARPET, - DyeColor::RED => Ids::RED_CARPET, - DyeColor::WHITE => Ids::WHITE_CARPET, - DyeColor::YELLOW => Ids::YELLOW_CARPET, - }, [])); + $this->mapFlattenedEnum( + Blocks::CARPET(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_carpet", + fn(Carpet $block) => $block->getColor() + ); - $this->map(Blocks::DYED_SHULKER_BOX(), fn(DyedShulkerBox $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_SHULKER_BOX, - DyeColor::BLUE => Ids::BLUE_SHULKER_BOX, - DyeColor::BROWN => Ids::BROWN_SHULKER_BOX, - DyeColor::CYAN => Ids::CYAN_SHULKER_BOX, - DyeColor::GRAY => Ids::GRAY_SHULKER_BOX, - DyeColor::GREEN => Ids::GREEN_SHULKER_BOX, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_SHULKER_BOX, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_SHULKER_BOX, - DyeColor::LIME => Ids::LIME_SHULKER_BOX, - DyeColor::MAGENTA => Ids::MAGENTA_SHULKER_BOX, - DyeColor::ORANGE => Ids::ORANGE_SHULKER_BOX, - DyeColor::PINK => Ids::PINK_SHULKER_BOX, - DyeColor::PURPLE => Ids::PURPLE_SHULKER_BOX, - DyeColor::RED => Ids::RED_SHULKER_BOX, - DyeColor::WHITE => Ids::WHITE_SHULKER_BOX, - DyeColor::YELLOW => Ids::YELLOW_SHULKER_BOX, - }, [])); + $this->mapFlattenedEnum( + Blocks::DYED_SHULKER_BOX(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_shulker_box", + fn(DyedShulkerBox $block) => $block->getColor() + ); - $this->map(Blocks::CONCRETE(), fn(Concrete $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_CONCRETE, - DyeColor::BLUE => Ids::BLUE_CONCRETE, - DyeColor::BROWN => Ids::BROWN_CONCRETE, - DyeColor::CYAN => Ids::CYAN_CONCRETE, - DyeColor::GRAY => Ids::GRAY_CONCRETE, - DyeColor::GREEN => Ids::GREEN_CONCRETE, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CONCRETE, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CONCRETE, - DyeColor::LIME => Ids::LIME_CONCRETE, - DyeColor::MAGENTA => Ids::MAGENTA_CONCRETE, - DyeColor::ORANGE => Ids::ORANGE_CONCRETE, - DyeColor::PINK => Ids::PINK_CONCRETE, - DyeColor::PURPLE => Ids::PURPLE_CONCRETE, - DyeColor::RED => Ids::RED_CONCRETE, - DyeColor::WHITE => Ids::WHITE_CONCRETE, - DyeColor::YELLOW => Ids::YELLOW_CONCRETE, - }, [])); + $this->mapFlattenedEnum( + Blocks::CONCRETE(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_concrete", + fn(Concrete $block) => $block->getColor() + ); + $this->mapFlattenedEnum( + Blocks::CONCRETE_POWDER(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_concrete_powder", + fn(ConcretePowder $block) => $block->getColor() + ); - $this->map(Blocks::CONCRETE_POWDER(), fn(ConcretePowder $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_CONCRETE_POWDER, - DyeColor::BLUE => Ids::BLUE_CONCRETE_POWDER, - DyeColor::BROWN => Ids::BROWN_CONCRETE_POWDER, - DyeColor::CYAN => Ids::CYAN_CONCRETE_POWDER, - DyeColor::GRAY => Ids::GRAY_CONCRETE_POWDER, - DyeColor::GREEN => Ids::GREEN_CONCRETE_POWDER, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CONCRETE_POWDER, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CONCRETE_POWDER, - DyeColor::LIME => Ids::LIME_CONCRETE_POWDER, - DyeColor::MAGENTA => Ids::MAGENTA_CONCRETE_POWDER, - DyeColor::ORANGE => Ids::ORANGE_CONCRETE_POWDER, - DyeColor::PINK => Ids::PINK_CONCRETE_POWDER, - DyeColor::PURPLE => Ids::PURPLE_CONCRETE_POWDER, - DyeColor::RED => Ids::RED_CONCRETE_POWDER, - DyeColor::WHITE => Ids::WHITE_CONCRETE_POWDER, - DyeColor::YELLOW => Ids::YELLOW_CONCRETE_POWDER, - }, [])); + $this->mapFlattenedEnum( + Blocks::STAINED_CLAY(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_terracotta", + fn(StainedHardenedClay $block) => $block->getColor() + ); - $this->map(Blocks::STAINED_CLAY(), fn(StainedHardenedClay $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_TERRACOTTA, - DyeColor::BLUE => Ids::BLUE_TERRACOTTA, - DyeColor::BROWN => Ids::BROWN_TERRACOTTA, - DyeColor::CYAN => Ids::CYAN_TERRACOTTA, - DyeColor::GRAY => Ids::GRAY_TERRACOTTA, - DyeColor::GREEN => Ids::GREEN_TERRACOTTA, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_TERRACOTTA, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_TERRACOTTA, - DyeColor::LIME => Ids::LIME_TERRACOTTA, - DyeColor::MAGENTA => Ids::MAGENTA_TERRACOTTA, - DyeColor::ORANGE => Ids::ORANGE_TERRACOTTA, - DyeColor::PINK => Ids::PINK_TERRACOTTA, - DyeColor::PURPLE => Ids::PURPLE_TERRACOTTA, - DyeColor::RED => Ids::RED_TERRACOTTA, - DyeColor::WHITE => Ids::WHITE_TERRACOTTA, - DyeColor::YELLOW => Ids::YELLOW_TERRACOTTA, - }, [])); - - $this->map(Blocks::STAINED_GLASS(), fn(StainedGlass $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_STAINED_GLASS, - DyeColor::BLUE => Ids::BLUE_STAINED_GLASS, - DyeColor::BROWN => Ids::BROWN_STAINED_GLASS, - DyeColor::CYAN => Ids::CYAN_STAINED_GLASS, - DyeColor::GRAY => Ids::GRAY_STAINED_GLASS, - DyeColor::GREEN => Ids::GREEN_STAINED_GLASS, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_STAINED_GLASS, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_STAINED_GLASS, - DyeColor::LIME => Ids::LIME_STAINED_GLASS, - DyeColor::MAGENTA => Ids::MAGENTA_STAINED_GLASS, - DyeColor::ORANGE => Ids::ORANGE_STAINED_GLASS, - DyeColor::PINK => Ids::PINK_STAINED_GLASS, - DyeColor::PURPLE => Ids::PURPLE_STAINED_GLASS, - DyeColor::RED => Ids::RED_STAINED_GLASS, - DyeColor::WHITE => Ids::WHITE_STAINED_GLASS, - DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS, - }, [])); - - $this->map(Blocks::STAINED_GLASS_PANE(), fn(StainedGlassPane $block) => BlockStateData::current(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_STAINED_GLASS_PANE, - DyeColor::BLUE => Ids::BLUE_STAINED_GLASS_PANE, - DyeColor::BROWN => Ids::BROWN_STAINED_GLASS_PANE, - DyeColor::CYAN => Ids::CYAN_STAINED_GLASS_PANE, - DyeColor::GRAY => Ids::GRAY_STAINED_GLASS_PANE, - DyeColor::GREEN => Ids::GREEN_STAINED_GLASS_PANE, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_STAINED_GLASS_PANE, - DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_STAINED_GLASS_PANE, - DyeColor::LIME => Ids::LIME_STAINED_GLASS_PANE, - DyeColor::MAGENTA => Ids::MAGENTA_STAINED_GLASS_PANE, - DyeColor::ORANGE => Ids::ORANGE_STAINED_GLASS_PANE, - DyeColor::PINK => Ids::PINK_STAINED_GLASS_PANE, - DyeColor::PURPLE => Ids::PURPLE_STAINED_GLASS_PANE, - DyeColor::RED => Ids::RED_STAINED_GLASS_PANE, - DyeColor::WHITE => Ids::WHITE_STAINED_GLASS_PANE, - DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS_PANE, - }, [])); + $this->mapFlattenedEnum( + Blocks::STAINED_GLASS(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_stained_glass", + fn(StainedGlass $block) => $block->getColor() + ); + $this->mapFlattenedEnum( + Blocks::STAINED_GLASS_PANE(), + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_stained_glass_pane", + fn(StainedGlassPane $block) => $block->getColor() + ); } private function registerFlatCoralSerializers() : void{ diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index c55fde77a..cbeadc819 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -27,6 +27,7 @@ use pocketmine\block\AmethystCluster; use pocketmine\block\Anvil; use pocketmine\block\Bamboo; use pocketmine\block\Block; +use pocketmine\block\CakeWithDyedCandle; use pocketmine\block\CaveVines; use pocketmine\block\ChorusFlower; use pocketmine\block\DoublePitcherCrop; @@ -157,97 +158,68 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map($strippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), true, $in)); } - private function registerCandleDeserializers() : void{ - $this->map(Ids::CANDLE, fn(Reader $in) => Helper::decodeCandle(Blocks::CANDLE(), $in)); - foreach([ - Ids::BLACK_CANDLE => DyeColor::BLACK, - Ids::BLUE_CANDLE => DyeColor::BLUE, - Ids::BROWN_CANDLE => DyeColor::BROWN, - Ids::CYAN_CANDLE => DyeColor::CYAN, - Ids::GRAY_CANDLE => DyeColor::GRAY, - Ids::GREEN_CANDLE => DyeColor::GREEN, - Ids::LIGHT_BLUE_CANDLE => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_CANDLE => DyeColor::LIGHT_GRAY, - Ids::LIME_CANDLE => DyeColor::LIME, - Ids::MAGENTA_CANDLE => DyeColor::MAGENTA, - Ids::ORANGE_CANDLE => DyeColor::ORANGE, - Ids::PINK_CANDLE => DyeColor::PINK, - Ids::PURPLE_CANDLE => DyeColor::PURPLE, - Ids::RED_CANDLE => DyeColor::RED, - Ids::WHITE_CANDLE => DyeColor::WHITE, - Ids::YELLOW_CANDLE => DyeColor::YELLOW, - ] as $id => $color){ - $this->map($id, fn(Reader $in) => Helper::decodeCandle(Blocks::DYED_CANDLE()->setColor($color), $in)); - } - - $this->map(Ids::CANDLE_CAKE, fn(Reader $in) => Blocks::CAKE_WITH_CANDLE()->setLit($in->readBool(StateNames::LIT))); - foreach([ - Ids::BLACK_CANDLE_CAKE => DyeColor::BLACK, - Ids::BLUE_CANDLE_CAKE => DyeColor::BLUE, - Ids::BROWN_CANDLE_CAKE => DyeColor::BROWN, - Ids::CYAN_CANDLE_CAKE => DyeColor::CYAN, - Ids::GRAY_CANDLE_CAKE => DyeColor::GRAY, - Ids::GREEN_CANDLE_CAKE => DyeColor::GREEN, - Ids::LIGHT_BLUE_CANDLE_CAKE => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_CANDLE_CAKE => DyeColor::LIGHT_GRAY, - Ids::LIME_CANDLE_CAKE => DyeColor::LIME, - Ids::MAGENTA_CANDLE_CAKE => DyeColor::MAGENTA, - Ids::ORANGE_CANDLE_CAKE => DyeColor::ORANGE, - Ids::PINK_CANDLE_CAKE => DyeColor::PINK, - Ids::PURPLE_CANDLE_CAKE => DyeColor::PURPLE, - Ids::RED_CANDLE_CAKE => DyeColor::RED, - Ids::WHITE_CANDLE_CAKE => DyeColor::WHITE, - Ids::YELLOW_CANDLE_CAKE => DyeColor::YELLOW, - ] as $id => $color){ - $this->map($id, fn(Reader $in) => Blocks::CAKE_WITH_DYED_CANDLE() - ->setColor($color) - ->setLit($in->readBool(StateNames::LIT)) - ); + /** + * @phpstan-template TBlock of Block + * @phpstan-template TEnum of \UnitEnum + * + * @phpstan-param StringEnumMap $mapProperty + * @phpstan-param \Closure(TEnum) : TBlock $getBlock + * @phpstan-param ?\Closure(TBlock, Reader) : TBlock $extra + */ + public function mapFlattenedEnum( + StringEnumMap $mapProperty, + string $prefix, + string $suffix, + \Closure $getBlock, + ?\Closure $extra = null + ) : void{ + foreach(Utils::stringifyKeys($mapProperty->getValueToEnum()) as $infix => $enumCase){ + $id = $prefix . $infix . $suffix; + if($extra === null){ + $this->map($id, fn() => $getBlock($enumCase)); + }else{ + $this->map($id, function(Reader $in) use ($enumCase, $getBlock, $extra) : Block{ + $block = $getBlock($enumCase); + $extra($block, $in); + return $block; + }); + } } } - private function registerFlatColorBlockDeserializers() : void{ - foreach([ - Ids::HARD_BLACK_STAINED_GLASS => DyeColor::BLACK, - Ids::HARD_BLUE_STAINED_GLASS => DyeColor::BLUE, - Ids::HARD_BROWN_STAINED_GLASS => DyeColor::BROWN, - Ids::HARD_CYAN_STAINED_GLASS => DyeColor::CYAN, - Ids::HARD_GRAY_STAINED_GLASS => DyeColor::GRAY, - Ids::HARD_GREEN_STAINED_GLASS => DyeColor::GREEN, - Ids::HARD_LIGHT_BLUE_STAINED_GLASS => DyeColor::LIGHT_BLUE, - Ids::HARD_LIGHT_GRAY_STAINED_GLASS => DyeColor::LIGHT_GRAY, - Ids::HARD_LIME_STAINED_GLASS => DyeColor::LIME, - Ids::HARD_MAGENTA_STAINED_GLASS => DyeColor::MAGENTA, - Ids::HARD_ORANGE_STAINED_GLASS => DyeColor::ORANGE, - Ids::HARD_PINK_STAINED_GLASS => DyeColor::PINK, - Ids::HARD_PURPLE_STAINED_GLASS => DyeColor::PURPLE, - Ids::HARD_RED_STAINED_GLASS => DyeColor::RED, - Ids::HARD_WHITE_STAINED_GLASS => DyeColor::WHITE, - Ids::HARD_YELLOW_STAINED_GLASS => DyeColor::YELLOW, - ] as $id => $color){ - $this->map($id, fn(Reader $in) => Blocks::STAINED_HARDENED_GLASS()->setColor($color)); - } + private function registerCandleDeserializers() : void{ + $this->map(Ids::CANDLE, fn(Reader $in) => Helper::decodeCandle(Blocks::CANDLE(), $in)); + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_candle", + fn(DyeColor $color) => Blocks::DYED_CANDLE()->setColor($color), + Helper::decodeCandle(...) + ); - foreach([ - Ids::HARD_BLACK_STAINED_GLASS_PANE => DyeColor::BLACK, - Ids::HARD_BLUE_STAINED_GLASS_PANE => DyeColor::BLUE, - Ids::HARD_BROWN_STAINED_GLASS_PANE => DyeColor::BROWN, - Ids::HARD_CYAN_STAINED_GLASS_PANE => DyeColor::CYAN, - Ids::HARD_GRAY_STAINED_GLASS_PANE => DyeColor::GRAY, - Ids::HARD_GREEN_STAINED_GLASS_PANE => DyeColor::GREEN, - Ids::HARD_LIGHT_BLUE_STAINED_GLASS_PANE => DyeColor::LIGHT_BLUE, - Ids::HARD_LIGHT_GRAY_STAINED_GLASS_PANE => DyeColor::LIGHT_GRAY, - Ids::HARD_LIME_STAINED_GLASS_PANE => DyeColor::LIME, - Ids::HARD_MAGENTA_STAINED_GLASS_PANE => DyeColor::MAGENTA, - Ids::HARD_ORANGE_STAINED_GLASS_PANE => DyeColor::ORANGE, - Ids::HARD_PINK_STAINED_GLASS_PANE => DyeColor::PINK, - Ids::HARD_PURPLE_STAINED_GLASS_PANE => DyeColor::PURPLE, - Ids::HARD_RED_STAINED_GLASS_PANE => DyeColor::RED, - Ids::HARD_WHITE_STAINED_GLASS_PANE => DyeColor::WHITE, - Ids::HARD_YELLOW_STAINED_GLASS_PANE => DyeColor::YELLOW, - ] as $id => $color){ - $this->map($id, fn(Reader $in) => Blocks::STAINED_HARDENED_GLASS_PANE()->setColor($color)); - } + $this->map(Ids::CANDLE_CAKE, fn(Reader $in) => Blocks::CAKE_WITH_CANDLE()->setLit($in->readBool(StateNames::LIT))); + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_candle_cake", + fn(DyeColor $color) => Blocks::CAKE_WITH_DYED_CANDLE()->setColor($color), + fn(CakeWithDyedCandle $block, Reader $in) => $block->setLit($in->readBool(StateNames::LIT)) + ); + } + + private function registerFlatColorBlockDeserializers() : void{ + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:hard_", + "_stained_glass", + fn(DyeColor $color) => Blocks::STAINED_HARDENED_GLASS()->setColor($color) + ); + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:hard_", + "_stained_glass_pane", + fn(DyeColor $color) => Blocks::STAINED_HARDENED_GLASS_PANE()->setColor($color) + ); foreach([ Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK, @@ -273,110 +245,38 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ); } - foreach([ - Ids::BLACK_WOOL => DyeColor::BLACK, - Ids::BLUE_WOOL => DyeColor::BLUE, - Ids::BROWN_WOOL => DyeColor::BROWN, - Ids::CYAN_WOOL => DyeColor::CYAN, - Ids::GRAY_WOOL => DyeColor::GRAY, - Ids::GREEN_WOOL => DyeColor::GREEN, - Ids::LIGHT_BLUE_WOOL => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_WOOL => DyeColor::LIGHT_GRAY, - Ids::LIME_WOOL => DyeColor::LIME, - Ids::MAGENTA_WOOL => DyeColor::MAGENTA, - Ids::ORANGE_WOOL => DyeColor::ORANGE, - Ids::PINK_WOOL => DyeColor::PINK, - Ids::PURPLE_WOOL => DyeColor::PURPLE, - Ids::RED_WOOL => DyeColor::RED, - Ids::WHITE_WOOL => DyeColor::WHITE, - Ids::YELLOW_WOOL => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::WOOL()->setColor($color)); - } + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_wool", + fn(DyeColor $color) => Blocks::WOOL()->setColor($color) + ); + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_carpet", + fn(DyeColor $color) => Blocks::CARPET()->setColor($color) + ); - foreach([ - Ids::BLACK_CARPET => DyeColor::BLACK, - Ids::BLUE_CARPET => DyeColor::BLUE, - Ids::BROWN_CARPET => DyeColor::BROWN, - Ids::CYAN_CARPET => DyeColor::CYAN, - Ids::GRAY_CARPET => DyeColor::GRAY, - Ids::GREEN_CARPET => DyeColor::GREEN, - Ids::LIGHT_BLUE_CARPET => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_CARPET => DyeColor::LIGHT_GRAY, - Ids::LIME_CARPET => DyeColor::LIME, - Ids::MAGENTA_CARPET => DyeColor::MAGENTA, - Ids::ORANGE_CARPET => DyeColor::ORANGE, - Ids::PINK_CARPET => DyeColor::PINK, - Ids::PURPLE_CARPET => DyeColor::PURPLE, - Ids::RED_CARPET => DyeColor::RED, - Ids::WHITE_CARPET => DyeColor::WHITE, - Ids::YELLOW_CARPET => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::CARPET()->setColor($color)); - } + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_shulker_box", + fn(DyeColor $color) => Blocks::DYED_SHULKER_BOX()->setColor($color) + ); - foreach([ - Ids::BLACK_SHULKER_BOX => DyeColor::BLACK, - Ids::BLUE_SHULKER_BOX => DyeColor::BLUE, - Ids::BROWN_SHULKER_BOX => DyeColor::BROWN, - Ids::CYAN_SHULKER_BOX => DyeColor::CYAN, - Ids::GRAY_SHULKER_BOX => DyeColor::GRAY, - Ids::GREEN_SHULKER_BOX => DyeColor::GREEN, - Ids::LIGHT_BLUE_SHULKER_BOX => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_SHULKER_BOX => DyeColor::LIGHT_GRAY, - Ids::LIME_SHULKER_BOX => DyeColor::LIME, - Ids::MAGENTA_SHULKER_BOX => DyeColor::MAGENTA, - Ids::ORANGE_SHULKER_BOX => DyeColor::ORANGE, - Ids::PINK_SHULKER_BOX => DyeColor::PINK, - Ids::PURPLE_SHULKER_BOX => DyeColor::PURPLE, - Ids::RED_SHULKER_BOX => DyeColor::RED, - Ids::WHITE_SHULKER_BOX => DyeColor::WHITE, - Ids::YELLOW_SHULKER_BOX => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::DYED_SHULKER_BOX()->setColor($color)); - } - - foreach([ - Ids::BLACK_CONCRETE => DyeColor::BLACK, - Ids::BLUE_CONCRETE => DyeColor::BLUE, - Ids::BROWN_CONCRETE => DyeColor::BROWN, - Ids::CYAN_CONCRETE => DyeColor::CYAN, - Ids::GRAY_CONCRETE => DyeColor::GRAY, - Ids::GREEN_CONCRETE => DyeColor::GREEN, - Ids::LIGHT_BLUE_CONCRETE => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_CONCRETE => DyeColor::LIGHT_GRAY, - Ids::LIME_CONCRETE => DyeColor::LIME, - Ids::MAGENTA_CONCRETE => DyeColor::MAGENTA, - Ids::ORANGE_CONCRETE => DyeColor::ORANGE, - Ids::PINK_CONCRETE => DyeColor::PINK, - Ids::PURPLE_CONCRETE => DyeColor::PURPLE, - Ids::RED_CONCRETE => DyeColor::RED, - Ids::WHITE_CONCRETE => DyeColor::WHITE, - Ids::YELLOW_CONCRETE => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::CONCRETE()->setColor($color)); - } - - foreach([ - Ids::BLACK_CONCRETE_POWDER => DyeColor::BLACK, - Ids::BLUE_CONCRETE_POWDER => DyeColor::BLUE, - Ids::BROWN_CONCRETE_POWDER => DyeColor::BROWN, - Ids::CYAN_CONCRETE_POWDER => DyeColor::CYAN, - Ids::GRAY_CONCRETE_POWDER => DyeColor::GRAY, - Ids::GREEN_CONCRETE_POWDER => DyeColor::GREEN, - Ids::LIGHT_BLUE_CONCRETE_POWDER => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_CONCRETE_POWDER => DyeColor::LIGHT_GRAY, - Ids::LIME_CONCRETE_POWDER => DyeColor::LIME, - Ids::MAGENTA_CONCRETE_POWDER => DyeColor::MAGENTA, - Ids::ORANGE_CONCRETE_POWDER => DyeColor::ORANGE, - Ids::PINK_CONCRETE_POWDER => DyeColor::PINK, - Ids::PURPLE_CONCRETE_POWDER => DyeColor::PURPLE, - Ids::RED_CONCRETE_POWDER => DyeColor::RED, - Ids::WHITE_CONCRETE_POWDER => DyeColor::WHITE, - Ids::YELLOW_CONCRETE_POWDER => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::CONCRETE_POWDER()->setColor($color)); - } + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_concrete", + fn(DyeColor $color) => Blocks::CONCRETE()->setColor($color) + ); + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_concrete_powder", + fn(DyeColor $color) => Blocks::CONCRETE_POWDER()->setColor($color) + ); foreach([ Ids::BLACK_TERRACOTTA => DyeColor::BLACK, @@ -399,47 +299,19 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple($id, fn() => Blocks::STAINED_CLAY()->setColor($color)); } - foreach([ - Ids::BLACK_STAINED_GLASS => DyeColor::BLACK, - Ids::BLUE_STAINED_GLASS => DyeColor::BLUE, - Ids::BROWN_STAINED_GLASS => DyeColor::BROWN, - Ids::CYAN_STAINED_GLASS => DyeColor::CYAN, - Ids::GRAY_STAINED_GLASS => DyeColor::GRAY, - Ids::GREEN_STAINED_GLASS => DyeColor::GREEN, - Ids::LIGHT_BLUE_STAINED_GLASS => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_STAINED_GLASS => DyeColor::LIGHT_GRAY, - Ids::LIME_STAINED_GLASS => DyeColor::LIME, - Ids::MAGENTA_STAINED_GLASS => DyeColor::MAGENTA, - Ids::ORANGE_STAINED_GLASS => DyeColor::ORANGE, - Ids::PINK_STAINED_GLASS => DyeColor::PINK, - Ids::PURPLE_STAINED_GLASS => DyeColor::PURPLE, - Ids::RED_STAINED_GLASS => DyeColor::RED, - Ids::WHITE_STAINED_GLASS => DyeColor::WHITE, - Ids::YELLOW_STAINED_GLASS => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::STAINED_GLASS()->setColor($color)); - } + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_stained_glass", + fn(DyeColor $color) => Blocks::STAINED_GLASS()->setColor($color) + ); - foreach([ - Ids::BLACK_STAINED_GLASS_PANE => DyeColor::BLACK, - Ids::BLUE_STAINED_GLASS_PANE => DyeColor::BLUE, - Ids::BROWN_STAINED_GLASS_PANE => DyeColor::BROWN, - Ids::CYAN_STAINED_GLASS_PANE => DyeColor::CYAN, - Ids::GRAY_STAINED_GLASS_PANE => DyeColor::GRAY, - Ids::GREEN_STAINED_GLASS_PANE => DyeColor::GREEN, - Ids::LIGHT_BLUE_STAINED_GLASS_PANE => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_STAINED_GLASS_PANE => DyeColor::LIGHT_GRAY, - Ids::LIME_STAINED_GLASS_PANE => DyeColor::LIME, - Ids::MAGENTA_STAINED_GLASS_PANE => DyeColor::MAGENTA, - Ids::ORANGE_STAINED_GLASS_PANE => DyeColor::ORANGE, - Ids::PINK_STAINED_GLASS_PANE => DyeColor::PINK, - Ids::PURPLE_STAINED_GLASS_PANE => DyeColor::PURPLE, - Ids::RED_STAINED_GLASS_PANE => DyeColor::RED, - Ids::WHITE_STAINED_GLASS_PANE => DyeColor::WHITE, - Ids::YELLOW_STAINED_GLASS_PANE => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::STAINED_GLASS_PANE()->setColor($color)); - } + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_stained_glass_pane", + fn(DyeColor $color) => Blocks::STAINED_GLASS_PANE()->setColor($color) + ); } private function registerFlatCoralDeserializers() : void{ diff --git a/src/data/bedrock/block/convert/StringEnumMap.php b/src/data/bedrock/block/convert/StringEnumMap.php new file mode 100644 index 000000000..6171c0e71 --- /dev/null +++ b/src/data/bedrock/block/convert/StringEnumMap.php @@ -0,0 +1,78 @@ + + */ + private array $enumToValue = []; + + /** + * @var \UnitEnum[] + * @phpstan-var array + */ + private array $valueToEnum = []; + + /** + * @phpstan-param class-string $class + * @phpstan-param \Closure(TEnum) : string $mapper + */ + public function __construct( + private string $class, + \Closure $mapper + ){ + foreach($class::cases() as $case){ + $string = $mapper($case); + $this->valueToEnum[$string] = $case; + $this->enumToValue[spl_object_id($case)] = $string; + } + } + + /** + * @phpstan-param TEnum $enum + */ + public function enumToValue(\UnitEnum $enum) : string{ + return $this->enumToValue[spl_object_id($enum)]; + } + + public function valueToEnum(string $string) : ?\UnitEnum{ + return $this->valueToEnum[$string] ?? throw new BlockStateDeserializeException("No $this->class enum mapping for \"$string\""); + } + + /** + * @return \UnitEnum[] + * @phpstan-return array + */ + public function getValueToEnum() : array{ + return $this->valueToEnum; + } +} diff --git a/src/data/bedrock/block/convert/ValueMappings.php b/src/data/bedrock/block/convert/ValueMappings.php new file mode 100644 index 000000000..df57d6f53 --- /dev/null +++ b/src/data/bedrock/block/convert/ValueMappings.php @@ -0,0 +1,83 @@ +, StringEnumMap> + */ + private array $enumMappings = []; + + public function __construct(){ + $this->addEnum(DyeColor::class, fn(DyeColor $case) => match ($case) { + DyeColor::BLACK => "black", + DyeColor::BLUE => "blue", + DyeColor::BROWN => "brown", + DyeColor::CYAN => "cyan", + DyeColor::GRAY => "gray", + DyeColor::GREEN => "green", + DyeColor::LIGHT_BLUE => "light_blue", + DyeColor::LIGHT_GRAY => "light_gray", + DyeColor::LIME => "lime", + DyeColor::MAGENTA => "magenta", + DyeColor::ORANGE => "orange", + DyeColor::PINK => "pink", + DyeColor::PURPLE => "purple", + DyeColor::RED => "red", + DyeColor::WHITE => "white", + DyeColor::YELLOW => "yellow" + }); + } + + /** + * @phpstan-template TEnum of \UnitEnum + * @phpstan-param class-string $class + * @phpstan-param \Closure(TEnum): string $mapper + */ + private function addEnum(string $class, \Closure $mapper) : void{ + $this->enumMappings[$class] = new StringEnumMap($class, $mapper); + } + + /** + * @phpstan-template TEnum of \UnitEnum + * @phpstan-param class-string $class + * @phpstan-return StringEnumMap + */ + public function getEnumMap(string $class) : StringEnumMap{ + if(!isset($this->enumMappings[$class])){ + throw new \InvalidArgumentException("No enum mapping found for class: $class"); + } + /** + * @phpstan-var StringEnumMap $map + */ + $map = $this->enumMappings[$class]; + return $map; + } +} From c0fad353a2667046a073fb14f8a5a65a9f65ed7b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Aug 2025 22:09:54 +0100 Subject: [PATCH 096/140] missed one sadly glazed_terracotta had to be special --- .../BlockStateToObjectDeserializer.php | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index cbeadc819..0f6d4930b 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -278,26 +278,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ fn(DyeColor $color) => Blocks::CONCRETE_POWDER()->setColor($color) ); - foreach([ - Ids::BLACK_TERRACOTTA => DyeColor::BLACK, - Ids::BLUE_TERRACOTTA => DyeColor::BLUE, - Ids::BROWN_TERRACOTTA => DyeColor::BROWN, - Ids::CYAN_TERRACOTTA => DyeColor::CYAN, - Ids::GRAY_TERRACOTTA => DyeColor::GRAY, - Ids::GREEN_TERRACOTTA => DyeColor::GREEN, - Ids::LIGHT_BLUE_TERRACOTTA => DyeColor::LIGHT_BLUE, - Ids::LIGHT_GRAY_TERRACOTTA => DyeColor::LIGHT_GRAY, - Ids::LIME_TERRACOTTA => DyeColor::LIME, - Ids::MAGENTA_TERRACOTTA => DyeColor::MAGENTA, - Ids::ORANGE_TERRACOTTA => DyeColor::ORANGE, - Ids::PINK_TERRACOTTA => DyeColor::PINK, - Ids::PURPLE_TERRACOTTA => DyeColor::PURPLE, - Ids::RED_TERRACOTTA => DyeColor::RED, - Ids::WHITE_TERRACOTTA => DyeColor::WHITE, - Ids::YELLOW_TERRACOTTA => DyeColor::YELLOW, - ] as $id => $color){ - $this->mapSimple($id, fn() => Blocks::STAINED_CLAY()->setColor($color)); - } + $this->mapFlattenedEnum( + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + "minecraft:", + "_terracotta", + fn(DyeColor $color) => Blocks::STAINED_CLAY()->setColor($color) + ); $this->mapFlattenedEnum( ValueMappings::getInstance()->getEnumMap(DyeColor::class), From 431790a3195ffe13859a040fa5e2b6a8dd1a5974 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Aug 2025 22:24:27 +0100 Subject: [PATCH 097/140] Additional specialisation for colored blocks this reduces boilerplate even further --- .../convert/BlockObjectToStateSerializer.php | 130 +++++------------- .../convert/BlockStateDeserializerHelper.php | 8 +- .../BlockStateToObjectDeserializer.php | 106 +++++--------- 3 files changed, 76 insertions(+), 168 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 6b545bd01..1a3467fe9 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -45,7 +45,6 @@ use pocketmine\block\CakeWithCandle; use pocketmine\block\CakeWithDyedCandle; use pocketmine\block\Campfire; use pocketmine\block\Candle; -use pocketmine\block\Carpet; use pocketmine\block\Carrot; use pocketmine\block\CarvedPumpkin; use pocketmine\block\CaveVines; @@ -55,8 +54,6 @@ use pocketmine\block\Chest; use pocketmine\block\ChiseledBookshelf; use pocketmine\block\ChorusFlower; use pocketmine\block\CocoaBlock; -use pocketmine\block\Concrete; -use pocketmine\block\ConcretePowder; use pocketmine\block\Copper; use pocketmine\block\CopperBulb; use pocketmine\block\CopperDoor; @@ -73,8 +70,6 @@ use pocketmine\block\Door; use pocketmine\block\DoublePitcherCrop; use pocketmine\block\DoublePlant; use pocketmine\block\DoubleTallGrass; -use pocketmine\block\DyedCandle; -use pocketmine\block\DyedShulkerBox; use pocketmine\block\EnderChest; use pocketmine\block\EndPortalFrame; use pocketmine\block\EndRod; @@ -133,11 +128,6 @@ use pocketmine\block\SmallDripleaf; use pocketmine\block\SnowLayer; use pocketmine\block\SoulCampfire; use pocketmine\block\Sponge; -use pocketmine\block\StainedGlass; -use pocketmine\block\StainedGlassPane; -use pocketmine\block\StainedHardenedClay; -use pocketmine\block\StainedHardenedGlass; -use pocketmine\block\StainedHardenedGlassPane; use pocketmine\block\Stair; use pocketmine\block\StoneButton; use pocketmine\block\Stonecutter; @@ -153,6 +143,7 @@ use pocketmine\block\Tripwire; use pocketmine\block\TripwireHook; use pocketmine\block\UnderwaterTorch; use pocketmine\block\utils\BrewingStandSlot; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\DirtType; use pocketmine\block\utils\DripleafState; @@ -176,7 +167,6 @@ use pocketmine\block\WoodenDoor; use pocketmine\block\WoodenPressurePlate; use pocketmine\block\WoodenStairs; use pocketmine\block\WoodenTrapdoor; -use pocketmine\block\Wool; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; @@ -319,43 +309,57 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); } + /** + * @phpstan-template TBlock of Block&Colored + * @phpstan-param TBlock $block + * @phpstan-param ?\Closure(TBlock, Writer) : Writer $extra + */ + public function mapColored( + Block $block, + string $prefix, + string $suffix, + ?\Closure $extra = null + ) : void{ + $this->mapFlattenedEnum( + $block, + ValueMappings::getInstance()->getEnumMap(DyeColor::class), + $prefix, + $suffix, + fn(Colored $block) => $block->getColor(), + $extra + ); + } + private function registerCandleSerializers() : void{ $this->map(Blocks::CANDLE(), fn(Candle $block) => Helper::encodeCandle($block, new Writer(Ids::CANDLE))); - $this->mapFlattenedEnum( + $this->mapColored( Blocks::DYED_CANDLE(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), "minecraft:", "_candle", - fn(DyedCandle $block) => $block->getColor(), Helper::encodeCandle(...) ); $this->map(Blocks::CAKE_WITH_CANDLE(), fn(CakeWithCandle $block) => Writer::create(Ids::CANDLE_CAKE) ->writeBool(StateNames::LIT, $block->isLit())); - $this->mapFlattenedEnum( + $this->mapColored( Blocks::CAKE_WITH_DYED_CANDLE(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), "minecraft:", "_candle_cake", - fn(CakeWithDyedCandle $block) => $block->getColor(), fn(CakeWithDyedCandle $block, Writer $writer) => $writer->writeBool(StateNames::LIT, $block->isLit()) ); } public function registerFlatColorBlockSerializers() : void{ - $this->mapFlattenedEnum( - Blocks::STAINED_HARDENED_GLASS(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:hard_", - "_stained_glass", - fn(StainedHardenedGlass $block) => $block->getColor() - ); - $this->mapFlattenedEnum( - Blocks::STAINED_HARDENED_GLASS_PANE(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:hard_", - "_stained_glass_pane", - fn(StainedHardenedGlassPane $block) => $block->getColor(), - ); + $this->mapColored(Blocks::STAINED_HARDENED_GLASS(), "minecraft:hard_", "_stained_glass"); + $this->mapColored(Blocks::STAINED_HARDENED_GLASS_PANE(), "minecraft:hard_", "_stained_glass_pane"); + + $this->mapColored(Blocks::CARPET(), "minecraft:", "_carpet"); + $this->mapColored(Blocks::CONCRETE(), "minecraft:", "_concrete"); + $this->mapColored(Blocks::CONCRETE_POWDER(), "minecraft:", "_concrete_powder"); + $this->mapColored(Blocks::DYED_SHULKER_BOX(), "minecraft:", "_shulker_box"); + $this->mapColored(Blocks::STAINED_CLAY(), "minecraft:", "_terracotta"); + $this->mapColored(Blocks::STAINED_GLASS(), "minecraft:", "_stained_glass"); + $this->mapColored(Blocks::STAINED_GLASS_PANE(), "minecraft:", "_stained_glass_pane"); + $this->mapColored(Blocks::WOOL(), "minecraft:", "_wool"); $this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{ return Writer::create(match($block->getColor()){ @@ -366,7 +370,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ DyeColor::GRAY => Ids::GRAY_GLAZED_TERRACOTTA, DyeColor::GREEN => Ids::GREEN_GLAZED_TERRACOTTA, DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_GLAZED_TERRACOTTA, - DyeColor::LIGHT_GRAY => Ids::SILVER_GLAZED_TERRACOTTA, + DyeColor::LIGHT_GRAY => Ids::SILVER_GLAZED_TERRACOTTA, //minecraft sadness DyeColor::LIME => Ids::LIME_GLAZED_TERRACOTTA, DyeColor::MAGENTA => Ids::MAGENTA_GLAZED_TERRACOTTA, DyeColor::ORANGE => Ids::ORANGE_GLAZED_TERRACOTTA, @@ -378,68 +382,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }) ->writeHorizontalFacing($block->getFacing()); }); - - $this->mapFlattenedEnum( - Blocks::WOOL(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_wool", - fn(Wool $block) => $block->getColor() - ); - - $this->mapFlattenedEnum( - Blocks::CARPET(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_carpet", - fn(Carpet $block) => $block->getColor() - ); - - $this->mapFlattenedEnum( - Blocks::DYED_SHULKER_BOX(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_shulker_box", - fn(DyedShulkerBox $block) => $block->getColor() - ); - - $this->mapFlattenedEnum( - Blocks::CONCRETE(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_concrete", - fn(Concrete $block) => $block->getColor() - ); - $this->mapFlattenedEnum( - Blocks::CONCRETE_POWDER(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_concrete_powder", - fn(ConcretePowder $block) => $block->getColor() - ); - - $this->mapFlattenedEnum( - Blocks::STAINED_CLAY(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_terracotta", - fn(StainedHardenedClay $block) => $block->getColor() - ); - - $this->mapFlattenedEnum( - Blocks::STAINED_GLASS(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_stained_glass", - fn(StainedGlass $block) => $block->getColor() - ); - $this->mapFlattenedEnum( - Blocks::STAINED_GLASS_PANE(), - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_stained_glass_pane", - fn(StainedGlassPane $block) => $block->getColor() - ); } private function registerFlatCoralSerializers() : void{ diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 5cf3f7f76..3cf55429e 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -70,7 +70,13 @@ final class BlockStateDeserializerHelper{ ->setPressed($in->readBool(BlockStateNames::BUTTON_PRESSED_BIT)); } - /** @throws BlockStateDeserializeException */ + /** + * @phpstan-template TCandle of Candle + * @phpstan-param TCandle $block + * @phpstan-return TCandle + * + * @throws BlockStateDeserializeException + */ public static function decodeCandle(Candle $block, BlockStateReader $in) : Candle{ return $block ->setCount($in->readBoundedInt(StateNames::CANDLES, 0, 3) + 1) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 0f6d4930b..b21642cb4 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -40,6 +40,7 @@ use pocketmine\block\Stair; use pocketmine\block\SweetBerryBush; use pocketmine\block\utils\BrewingStandSlot; use pocketmine\block\utils\ChiseledBookshelfSlot; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\CoralType; @@ -187,39 +188,52 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ } } - private function registerCandleDeserializers() : void{ - $this->map(Ids::CANDLE, fn(Reader $in) => Helper::decodeCandle(Blocks::CANDLE(), $in)); + /** + * @phpstan-template TBlock of Block&Colored + * @phpstan-param \Closure() : TBlock $getBlock + * @phpstan-param ?\Closure(TBlock, Reader) : TBlock $extra + */ + public function mapColored(string $prefix, string $suffix, \Closure $getBlock, ?\Closure $extra = null) : void{ $this->mapFlattenedEnum( ValueMappings::getInstance()->getEnumMap(DyeColor::class), + $prefix, + $suffix, + fn(DyeColor $color) => $getBlock()->setColor($color), + $extra + ); + } + + private function registerCandleDeserializers() : void{ + $this->map(Ids::CANDLE, fn(Reader $in) => Helper::decodeCandle(Blocks::CANDLE(), $in)); + $this->mapColored( "minecraft:", "_candle", - fn(DyeColor $color) => Blocks::DYED_CANDLE()->setColor($color), + fn() => Blocks::DYED_CANDLE(), Helper::decodeCandle(...) ); $this->map(Ids::CANDLE_CAKE, fn(Reader $in) => Blocks::CAKE_WITH_CANDLE()->setLit($in->readBool(StateNames::LIT))); - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), + + $this->mapColored( "minecraft:", "_candle_cake", - fn(DyeColor $color) => Blocks::CAKE_WITH_DYED_CANDLE()->setColor($color), + fn() => Blocks::CAKE_WITH_DYED_CANDLE(), fn(CakeWithDyedCandle $block, Reader $in) => $block->setLit($in->readBool(StateNames::LIT)) ); } private function registerFlatColorBlockDeserializers() : void{ - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:hard_", - "_stained_glass", - fn(DyeColor $color) => Blocks::STAINED_HARDENED_GLASS()->setColor($color) - ); - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:hard_", - "_stained_glass_pane", - fn(DyeColor $color) => Blocks::STAINED_HARDENED_GLASS_PANE()->setColor($color) - ); + $this->mapColored("minecraft:hard_", "_stained_glass", fn() => Blocks::STAINED_HARDENED_GLASS()); + $this->mapColored("minecraft:hard_", "_stained_glass_pane", fn() => Blocks::STAINED_HARDENED_GLASS_PANE()); + + $this->mapColored("minecraft:", "_carpet", fn() => Blocks::CARPET()); + $this->mapColored("minecraft:", "_concrete", fn() => Blocks::CONCRETE()); + $this->mapColored("minecraft:", "_concrete_powder", fn() => Blocks::CONCRETE_POWDER()); + $this->mapColored("minecraft:", "_shulker_box", fn() => Blocks::DYED_SHULKER_BOX()); + $this->mapColored("minecraft:", "_stained_glass", fn() => Blocks::STAINED_GLASS()); + $this->mapColored("minecraft:", "_stained_glass_pane", fn() => Blocks::STAINED_GLASS_PANE()); + $this->mapColored("minecraft:", "_terracotta", fn() => Blocks::STAINED_CLAY()); + $this->mapColored("minecraft:", "_wool", fn() => Blocks::WOOL()); foreach([ Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK, @@ -229,7 +243,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ Ids::GRAY_GLAZED_TERRACOTTA => DyeColor::GRAY, Ids::GREEN_GLAZED_TERRACOTTA => DyeColor::GREEN, Ids::LIGHT_BLUE_GLAZED_TERRACOTTA => DyeColor::LIGHT_BLUE, - Ids::SILVER_GLAZED_TERRACOTTA => DyeColor::LIGHT_GRAY, + Ids::SILVER_GLAZED_TERRACOTTA => DyeColor::LIGHT_GRAY, //minecraft sadness Ids::LIME_GLAZED_TERRACOTTA => DyeColor::LIME, Ids::MAGENTA_GLAZED_TERRACOTTA => DyeColor::MAGENTA, Ids::ORANGE_GLAZED_TERRACOTTA => DyeColor::ORANGE, @@ -244,60 +258,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setFacing($in->readHorizontalFacing()) ); } - - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_wool", - fn(DyeColor $color) => Blocks::WOOL()->setColor($color) - ); - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_carpet", - fn(DyeColor $color) => Blocks::CARPET()->setColor($color) - ); - - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_shulker_box", - fn(DyeColor $color) => Blocks::DYED_SHULKER_BOX()->setColor($color) - ); - - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_concrete", - fn(DyeColor $color) => Blocks::CONCRETE()->setColor($color) - ); - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_concrete_powder", - fn(DyeColor $color) => Blocks::CONCRETE_POWDER()->setColor($color) - ); - - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_terracotta", - fn(DyeColor $color) => Blocks::STAINED_CLAY()->setColor($color) - ); - - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_stained_glass", - fn(DyeColor $color) => Blocks::STAINED_GLASS()->setColor($color) - ); - - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - "minecraft:", - "_stained_glass_pane", - fn(DyeColor $color) => Blocks::STAINED_GLASS_PANE()->setColor($color) - ); } private function registerFlatCoralDeserializers() : void{ From eea4f40138fe3af6a5061ebf5c4ef5a25f85faf0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 17 Aug 2025 15:24:40 +0100 Subject: [PATCH 098/140] BlockStateToObjectDeserializer: Remove duplicated CHISELED_COPPER registration allowing overriding of serializers by the same method as first registration was a mistake... --- .../bedrock/block/convert/BlockStateToObjectDeserializer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index c55fde77a..12c4bc43c 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -1355,7 +1355,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return $block; }); - $this->map(Ids::CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE)); $this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{ return Blocks::CHISELED_QUARTZ() ->setAxis($in->readPillarAxis()); From 2bb78f2a943c031380a733550409a6524cc918a9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Aug 2025 00:58:57 +0100 Subject: [PATCH 099/140] Fixed Furnace not implementing HorizontalFacing looks like this was missed in #6639 I checked all other uses of HorizontalFacingTrait and FacesOppositePlacingPlayerTrait and this seems to be the only one. --- src/block/Furnace.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/block/Furnace.php b/src/block/Furnace.php index 54480e62c..43ab463a6 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Furnace as TileFurnace; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\crafting\FurnaceType; @@ -34,7 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; -class Furnace extends Opaque implements Lightable{ +class Furnace extends Opaque implements Lightable, HorizontalFacing{ use FacesOppositePlacingPlayerTrait; use LightableTrait; From e824266457c3207bb2565256e2f9a58652d98a51 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 22 Aug 2025 18:27:06 +0100 Subject: [PATCH 100/140] ChiseledBookshelf: add setSlots() --- src/block/ChiseledBookshelf.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php index be49c7a91..cbe22a94b 100644 --- a/src/block/ChiseledBookshelf.php +++ b/src/block/ChiseledBookshelf.php @@ -114,6 +114,18 @@ class ChiseledBookshelf extends Opaque implements HorizontalFacing{ return $this->slots; } + /** + * @param ChiseledBookshelfSlot[] $slots + * @return $this + */ + public function setSlots(array $slots) : self{ + $this->slots = []; + foreach($slots as $slot){ + $this->setSlot($slot, true); + } + return $this; + } + /** * Returns the last slot interacted by a player or null if no slot has been interacted with yet. */ From 47140cb8d7a956d767423de56410c46526c10468 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 22 Aug 2025 18:27:32 +0100 Subject: [PATCH 101/140] RedstoneLamp: implement Lightable, shimmed to powered --- src/block/RedstoneLamp.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/block/RedstoneLamp.php b/src/block/RedstoneLamp.php index 33a97801d..530fa2410 100644 --- a/src/block/RedstoneLamp.php +++ b/src/block/RedstoneLamp.php @@ -23,11 +23,12 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class RedstoneLamp extends Opaque implements PoweredByRedstone{ +class RedstoneLamp extends Opaque implements PoweredByRedstone, Lightable{ use PoweredByRedstoneTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ @@ -37,4 +38,14 @@ class RedstoneLamp extends Opaque implements PoweredByRedstone{ public function getLightLevel() : int{ return $this->powered ? 15 : 0; } + + public function isLit() : bool{ + return $this->powered; + } + + /** @return $this */ + public function setLit(bool $lit = true) : self{ + $this->powered = $lit; + return $this; + } } From 7c521b456e181fed271b4e050731ed4ebc8faf1f Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 24 Aug 2025 14:12:18 +0100 Subject: [PATCH 102/140] Unify block serializers (#6769) This has several advantages: Easier to implement new blocks (one less file to modify) Easier to adjust serialization of existing blocks Guaranteed consistency between serializers and deserializers Potentially, exposes more metadata for programmatic analysis, instead of having everything baked inside opaque Closures There are some exceptions which still use the old approach: big dripleaf, cauldrons, mushroom stems, and pitcher crops. These all have multiple PM block types for a single ID, with relatively complex logic to select which to use. These weren't worth the effort to unify due to their small number. I may revisit this in the future, but I already spent a lot of brainpower on it. --- src/data/bedrock/MushroomBlockTypeIdMap.php | 20 +- .../bedrock/block/BlockLegacyMetadata.php | 2 + .../convert/BlockObjectToStateSerializer.php | 1619 +---------------- .../BlockSerializerDeserializerRegistrar.php | 237 +++ .../convert/BlockStateDeserializerHelper.php | 52 +- .../block/convert/BlockStateReader.php | 228 ++- .../convert/BlockStateSerializerHelper.php | 33 + .../BlockStateToObjectDeserializer.php | 1593 +--------------- .../block/convert/BlockStateWriter.php | 228 +-- .../block/convert/FlattenedIdModel.php | 107 ++ src/data/bedrock/block/convert/Model.php | 82 + .../bedrock/block/convert/StringEnumMap.php | 78 - .../bedrock/block/convert/ValueMappings.php | 83 - .../block/convert/VanillaBlockMappings.php | 1561 ++++++++++++++++ .../property/BoolFromStringProperty.php | 78 + .../block/convert/property/BoolProperty.php | 71 + .../convert/property/CommonProperties.php | 429 +++++ .../block/convert/property/DummyProperty.php | 61 + .../convert/property/EnumFromRawStateMap.php | 109 ++ .../property/FlattenedCaveVinesVariant.php | 35 + .../convert/property/IntFromRawStateMap.php | 104 ++ .../block/convert/property/IntProperty.php | 70 + .../property/OptionSetFromIntProperty.php | 93 + .../block/convert/property/Property.php | 44 + .../block/convert/property/StateMap.php | 53 + .../block/convert/property/StringProperty.php | 50 + .../convert/property/ValueFromIntProperty.php | 75 + .../property/ValueFromStringProperty.php | 81 + .../block/convert/property/ValueMappings.php | 305 ++++ .../property/WallConnectionTypeShim.php | 66 + .../format/io/GlobalBlockStateHandlers.php | 22 +- tests/phpstan/configs/actual-problems.neon | 6 + .../BlockSerializerDeserializerTest.php | 6 +- 33 files changed, 4072 insertions(+), 3609 deletions(-) create mode 100644 src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php create mode 100644 src/data/bedrock/block/convert/FlattenedIdModel.php create mode 100644 src/data/bedrock/block/convert/Model.php delete mode 100644 src/data/bedrock/block/convert/StringEnumMap.php delete mode 100644 src/data/bedrock/block/convert/ValueMappings.php create mode 100644 src/data/bedrock/block/convert/VanillaBlockMappings.php create mode 100644 src/data/bedrock/block/convert/property/BoolFromStringProperty.php create mode 100644 src/data/bedrock/block/convert/property/BoolProperty.php create mode 100644 src/data/bedrock/block/convert/property/CommonProperties.php create mode 100644 src/data/bedrock/block/convert/property/DummyProperty.php create mode 100644 src/data/bedrock/block/convert/property/EnumFromRawStateMap.php create mode 100644 src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php create mode 100644 src/data/bedrock/block/convert/property/IntFromRawStateMap.php create mode 100644 src/data/bedrock/block/convert/property/IntProperty.php create mode 100644 src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php create mode 100644 src/data/bedrock/block/convert/property/Property.php create mode 100644 src/data/bedrock/block/convert/property/StateMap.php create mode 100644 src/data/bedrock/block/convert/property/StringProperty.php create mode 100644 src/data/bedrock/block/convert/property/ValueFromIntProperty.php create mode 100644 src/data/bedrock/block/convert/property/ValueFromStringProperty.php create mode 100644 src/data/bedrock/block/convert/property/ValueMappings.php create mode 100644 src/data/bedrock/block/convert/property/WallConnectionTypeShim.php diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php index a25336d89..6357d3e14 100644 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ b/src/data/bedrock/MushroomBlockTypeIdMap.php @@ -24,29 +24,21 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; use pocketmine\block\utils\MushroomBlockType; -use pocketmine\data\bedrock\block\BlockLegacyMetadata as LegacyMeta; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\utils\SingletonTrait; +/** + * @deprecated + */ final class MushroomBlockTypeIdMap{ use SingletonTrait; /** @phpstan-use IntSaveIdMapTrait */ use IntSaveIdMapTrait; public function __construct(){ + $newMapping = ValueMappings::getInstance()->mushroomBlockType; foreach(MushroomBlockType::cases() as $case){ - $this->register(match($case){ - MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, - MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, - MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, - MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, - MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, - MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, - MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, - MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, - MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, - MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, - MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, - }, $case); + $this->register($newMapping->valueToRaw($case), $case); } } } diff --git a/src/data/bedrock/block/BlockLegacyMetadata.php b/src/data/bedrock/block/BlockLegacyMetadata.php index e094bd3bd..d324a7ccc 100644 --- a/src/data/bedrock/block/BlockLegacyMetadata.php +++ b/src/data/bedrock/block/BlockLegacyMetadata.php @@ -38,6 +38,8 @@ final class BlockLegacyMetadata{ public const CORAL_VARIANT_FIRE = 3; public const CORAL_VARIANT_HORN = 4; + public const LIQUID_FALLING_FLAG = 0x08; + public const MULTI_FACE_DIRECTION_FLAG_DOWN = 0x01; public const MULTI_FACE_DIRECTION_FLAG_UP = 0x02; public const MULTI_FACE_DIRECTION_FLAG_SOUTH = 0x04; diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 1a3467fe9..90f6af97a 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -23,161 +23,17 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\convert; -use pocketmine\block\ActivatorRail; -use pocketmine\block\AmethystCluster; -use pocketmine\block\Anvil; -use pocketmine\block\Bamboo; -use pocketmine\block\BambooSapling; -use pocketmine\block\Barrel; -use pocketmine\block\Bed; -use pocketmine\block\Beetroot; -use pocketmine\block\Bell; -use pocketmine\block\BigDripleafHead; -use pocketmine\block\BigDripleafStem; use pocketmine\block\Block; -use pocketmine\block\BoneBlock; -use pocketmine\block\BrewingStand; -use pocketmine\block\BrownMushroomBlock; -use pocketmine\block\Button; -use pocketmine\block\Cactus; -use pocketmine\block\Cake; -use pocketmine\block\CakeWithCandle; -use pocketmine\block\CakeWithDyedCandle; -use pocketmine\block\Campfire; -use pocketmine\block\Candle; -use pocketmine\block\Carrot; -use pocketmine\block\CarvedPumpkin; -use pocketmine\block\CaveVines; -use pocketmine\block\Chain; -use pocketmine\block\ChemistryTable; -use pocketmine\block\Chest; -use pocketmine\block\ChiseledBookshelf; -use pocketmine\block\ChorusFlower; -use pocketmine\block\CocoaBlock; -use pocketmine\block\Copper; -use pocketmine\block\CopperBulb; -use pocketmine\block\CopperDoor; -use pocketmine\block\CopperGrate; -use pocketmine\block\CopperSlab; -use pocketmine\block\CopperStairs; -use pocketmine\block\CopperTrapdoor; -use pocketmine\block\Coral; -use pocketmine\block\CoralBlock; -use pocketmine\block\DaylightSensor; -use pocketmine\block\DetectorRail; -use pocketmine\block\Dirt; -use pocketmine\block\Door; -use pocketmine\block\DoublePitcherCrop; -use pocketmine\block\DoublePlant; -use pocketmine\block\DoubleTallGrass; -use pocketmine\block\EnderChest; -use pocketmine\block\EndPortalFrame; -use pocketmine\block\EndRod; -use pocketmine\block\Farmland; -use pocketmine\block\FenceGate; -use pocketmine\block\FillableCauldron; -use pocketmine\block\Fire; -use pocketmine\block\FloorBanner; -use pocketmine\block\FloorCoralFan; -use pocketmine\block\FloorSign; -use pocketmine\block\Froglight; -use pocketmine\block\FrostedIce; -use pocketmine\block\Furnace; -use pocketmine\block\GlazedTerracotta; -use pocketmine\block\GlowLichen; -use pocketmine\block\HayBale; -use pocketmine\block\Hopper; -use pocketmine\block\ItemFrame; -use pocketmine\block\Ladder; -use pocketmine\block\Lantern; -use pocketmine\block\Lava; -use pocketmine\block\Leaves; -use pocketmine\block\Lectern; -use pocketmine\block\Lever; -use pocketmine\block\Light; -use pocketmine\block\LightningRod; -use pocketmine\block\LitPumpkin; -use pocketmine\block\Loom; -use pocketmine\block\MelonStem; -use pocketmine\block\MobHead; -use pocketmine\block\NetherPortal; -use pocketmine\block\NetherVines; -use pocketmine\block\NetherWartPlant; -use pocketmine\block\PinkPetals; -use pocketmine\block\PitcherCrop; -use pocketmine\block\Potato; -use pocketmine\block\PoweredRail; -use pocketmine\block\PumpkinStem; -use pocketmine\block\Rail; -use pocketmine\block\RedMushroomBlock; -use pocketmine\block\RedstoneComparator; -use pocketmine\block\RedstoneLamp; -use pocketmine\block\RedstoneOre; -use pocketmine\block\RedstoneRepeater; -use pocketmine\block\RedstoneTorch; -use pocketmine\block\RedstoneWire; -use pocketmine\block\ResinClump; -use pocketmine\block\RespawnAnchor; use pocketmine\block\RuntimeBlockStateRegistry; -use pocketmine\block\Sapling; -use pocketmine\block\SeaPickle; -use pocketmine\block\SimplePillar; -use pocketmine\block\SimplePressurePlate; use pocketmine\block\Slab; -use pocketmine\block\SmallDripleaf; -use pocketmine\block\SnowLayer; -use pocketmine\block\SoulCampfire; -use pocketmine\block\Sponge; use pocketmine\block\Stair; -use pocketmine\block\StoneButton; -use pocketmine\block\Stonecutter; -use pocketmine\block\StonePressurePlate; -use pocketmine\block\Sugarcane; -use pocketmine\block\SweetBerryBush; -use pocketmine\block\TNT; -use pocketmine\block\Torch; -use pocketmine\block\TorchflowerCrop; -use pocketmine\block\Trapdoor; -use pocketmine\block\TrappedChest; -use pocketmine\block\Tripwire; -use pocketmine\block\TripwireHook; -use pocketmine\block\UnderwaterTorch; -use pocketmine\block\utils\BrewingStandSlot; -use pocketmine\block\utils\Colored; -use pocketmine\block\utils\CoralType; -use pocketmine\block\utils\DirtType; -use pocketmine\block\utils\DripleafState; -use pocketmine\block\utils\DyeColor; -use pocketmine\block\utils\FroglightType; -use pocketmine\block\utils\LeverFacing; -use pocketmine\block\utils\MobHeadType; -use pocketmine\block\VanillaBlocks as Blocks; -use pocketmine\block\Vine; -use pocketmine\block\Wall; -use pocketmine\block\WallBanner; -use pocketmine\block\WallCoralFan; -use pocketmine\block\WallSign; -use pocketmine\block\Water; -use pocketmine\block\WeightedPressurePlateHeavy; -use pocketmine\block\WeightedPressurePlateLight; -use pocketmine\block\Wheat; use pocketmine\block\Wood; -use pocketmine\block\WoodenButton; -use pocketmine\block\WoodenDoor; -use pocketmine\block\WoodenPressurePlate; -use pocketmine\block\WoodenStairs; -use pocketmine\block\WoodenTrapdoor; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateData; -use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateSerializeException; use pocketmine\data\bedrock\block\BlockStateSerializer; -use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; use pocketmine\data\bedrock\block\BlockTypeNames as Ids; use pocketmine\data\bedrock\block\convert\BlockStateSerializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer; -use pocketmine\math\Axis; -use pocketmine\math\Facing; use function get_class; final class BlockObjectToStateSerializer implements BlockStateSerializer{ @@ -196,20 +52,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ */ private array $cache = []; - public function __construct(){ - $this->registerCandleSerializers(); - $this->registerFlatColorBlockSerializers(); - $this->registerFlatCoralSerializers(); - $this->registerCauldronSerializers(); - $this->registerFlatWoodBlockSerializers(); - $this->registerLeavesSerializers(); - $this->registerSaplingSerializers(); - $this->registerMobHeadSerializers(); - $this->registerCopperSerializers(); - $this->registerSimpleSerializers(); - $this->registerSerializers(); - } - public function serialize(int $stateId) : BlockStateData{ //TODO: singleton usage not ideal //TODO: we may want to deduplicate cache entries to avoid wasting memory @@ -227,24 +69,36 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ */ public function map(Block $block, \Closure|Writer|BlockStateData $serializer) : void{ if(isset($this->serializers[$block->getTypeId()])){ - throw new \InvalidArgumentException("Block type ID " . $block->getTypeId() . " already has a serializer registered"); + throw new \InvalidArgumentException("Block type ID " . $block->getTypeId() . " (" . $block->getName() . ") already has a serializer registered"); } //writer accepted for convenience only $this->serializers[$block->getTypeId()] = $serializer instanceof Writer ? $serializer->getBlockStateData() : $serializer; } + /** + * @deprecated + */ public function mapSimple(Block $block, string $id) : void{ $this->map($block, BlockStateData::current($id, [])); } + /** + * @deprecated + */ public function mapSlab(Slab $block, string $singleId, string $doubleId) : void{ $this->map($block, fn(Slab $block) => Helper::encodeSlab($block, $singleId, $doubleId)); } + /** + * @deprecated + */ public function mapStairs(Stair $block, string $id) : void{ $this->map($block, fn(Stair $block) => Helper::encodeStairs($block, Writer::create($id))); } + /** + * @deprecated + */ public function mapLog(Wood $block, string $unstrippedId, string $strippedId) : void{ $this->map($block, fn(Wood $block) => Helper::encodeLog($block, $unstrippedId, $strippedId)); } @@ -279,1451 +133,4 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return $result instanceof Writer ? $result->getBlockStateData() : $result; } - - /** - * @phpstan-template TBlock of Block - * @phpstan-template TEnum of \UnitEnum - * - * @phpstan-param TBlock $block - * @phpstan-param StringEnumMap $mapProperty - * @phpstan-param \Closure(TBlock) : TEnum $getProperty - * @phpstan-param ?\Closure(TBlock, Writer) : Writer $extra - */ - public function mapFlattenedEnum( - Block $block, - StringEnumMap $mapProperty, - string $prefix, - string $suffix, - \Closure $getProperty, - ?\Closure $extra = null - ) : void{ - $this->map($block, function(Block $block) use ($getProperty, $mapProperty, $prefix, $suffix, $extra) : Writer{ - $property = $getProperty($block); - $infix = $mapProperty->enumToValue($property); - $id = $prefix . $infix . $suffix; - $writer = new Writer($id); - if($extra !== null){ - $extra($block, $writer); - } - return $writer; - }); - } - - /** - * @phpstan-template TBlock of Block&Colored - * @phpstan-param TBlock $block - * @phpstan-param ?\Closure(TBlock, Writer) : Writer $extra - */ - public function mapColored( - Block $block, - string $prefix, - string $suffix, - ?\Closure $extra = null - ) : void{ - $this->mapFlattenedEnum( - $block, - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - $prefix, - $suffix, - fn(Colored $block) => $block->getColor(), - $extra - ); - } - - private function registerCandleSerializers() : void{ - $this->map(Blocks::CANDLE(), fn(Candle $block) => Helper::encodeCandle($block, new Writer(Ids::CANDLE))); - $this->mapColored( - Blocks::DYED_CANDLE(), - "minecraft:", - "_candle", - Helper::encodeCandle(...) - ); - $this->map(Blocks::CAKE_WITH_CANDLE(), fn(CakeWithCandle $block) => Writer::create(Ids::CANDLE_CAKE) - ->writeBool(StateNames::LIT, $block->isLit())); - $this->mapColored( - Blocks::CAKE_WITH_DYED_CANDLE(), - "minecraft:", - "_candle_cake", - fn(CakeWithDyedCandle $block, Writer $writer) => $writer->writeBool(StateNames::LIT, $block->isLit()) - ); - } - - public function registerFlatColorBlockSerializers() : void{ - $this->mapColored(Blocks::STAINED_HARDENED_GLASS(), "minecraft:hard_", "_stained_glass"); - $this->mapColored(Blocks::STAINED_HARDENED_GLASS_PANE(), "minecraft:hard_", "_stained_glass_pane"); - - $this->mapColored(Blocks::CARPET(), "minecraft:", "_carpet"); - $this->mapColored(Blocks::CONCRETE(), "minecraft:", "_concrete"); - $this->mapColored(Blocks::CONCRETE_POWDER(), "minecraft:", "_concrete_powder"); - $this->mapColored(Blocks::DYED_SHULKER_BOX(), "minecraft:", "_shulker_box"); - $this->mapColored(Blocks::STAINED_CLAY(), "minecraft:", "_terracotta"); - $this->mapColored(Blocks::STAINED_GLASS(), "minecraft:", "_stained_glass"); - $this->mapColored(Blocks::STAINED_GLASS_PANE(), "minecraft:", "_stained_glass_pane"); - $this->mapColored(Blocks::WOOL(), "minecraft:", "_wool"); - - $this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{ - return Writer::create(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_GLAZED_TERRACOTTA, - DyeColor::BLUE => Ids::BLUE_GLAZED_TERRACOTTA, - DyeColor::BROWN => Ids::BROWN_GLAZED_TERRACOTTA, - DyeColor::CYAN => Ids::CYAN_GLAZED_TERRACOTTA, - DyeColor::GRAY => Ids::GRAY_GLAZED_TERRACOTTA, - DyeColor::GREEN => Ids::GREEN_GLAZED_TERRACOTTA, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_GLAZED_TERRACOTTA, - DyeColor::LIGHT_GRAY => Ids::SILVER_GLAZED_TERRACOTTA, //minecraft sadness - DyeColor::LIME => Ids::LIME_GLAZED_TERRACOTTA, - DyeColor::MAGENTA => Ids::MAGENTA_GLAZED_TERRACOTTA, - DyeColor::ORANGE => Ids::ORANGE_GLAZED_TERRACOTTA, - DyeColor::PINK => Ids::PINK_GLAZED_TERRACOTTA, - DyeColor::PURPLE => Ids::PURPLE_GLAZED_TERRACOTTA, - DyeColor::RED => Ids::RED_GLAZED_TERRACOTTA, - DyeColor::WHITE => Ids::WHITE_GLAZED_TERRACOTTA, - DyeColor::YELLOW => Ids::YELLOW_GLAZED_TERRACOTTA, - }) - ->writeHorizontalFacing($block->getFacing()); - }); - } - - private function registerFlatCoralSerializers() : void{ - $this->map(Blocks::CORAL(), fn(Coral $block) => BlockStateData::current(match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL : Ids::BRAIN_CORAL, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL : Ids::BUBBLE_CORAL, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL : Ids::FIRE_CORAL, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL : Ids::HORN_CORAL, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL, - }, [])); - - $this->map(Blocks::CORAL_FAN(), fn(FloorCoralFan $block) => Writer::create( - match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_FAN : Ids::BRAIN_CORAL_FAN, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_FAN : Ids::BUBBLE_CORAL_FAN, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_FAN : Ids::FIRE_CORAL_FAN, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_FAN : Ids::HORN_CORAL_FAN, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_FAN : Ids::TUBE_CORAL_FAN, - }) - ->writeInt(StateNames::CORAL_FAN_DIRECTION, match($axis = $block->getAxis()){ - Axis::X => 0, - Axis::Z => 1, - default => throw new BlockStateSerializeException("Invalid axis {$axis}"), - })); - - $this->map(Blocks::CORAL_BLOCK(), fn(CoralBlock $block) => BlockStateData::current(match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_BLOCK : Ids::BRAIN_CORAL_BLOCK, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_BLOCK : Ids::BUBBLE_CORAL_BLOCK, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_BLOCK : Ids::FIRE_CORAL_BLOCK, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_BLOCK : Ids::HORN_CORAL_BLOCK, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_BLOCK : Ids::TUBE_CORAL_BLOCK, - }, [])); - - $this->map(Blocks::WALL_CORAL_FAN(), fn(WallCoralFan $block) => Writer::create( - match($block->getCoralType()){ - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_WALL_FAN : Ids::TUBE_CORAL_WALL_FAN, - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_WALL_FAN : Ids::BRAIN_CORAL_WALL_FAN, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_WALL_FAN : Ids::BUBBLE_CORAL_WALL_FAN, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_WALL_FAN : Ids::FIRE_CORAL_WALL_FAN, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_WALL_FAN : Ids::HORN_CORAL_WALL_FAN, - }) - ->writeCoralFacing($block->getFacing()) - ); - } - - private function registerCauldronSerializers() : void{ - $this->map(Blocks::CAULDRON(), Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); - $this->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel())); - //potion cauldrons store their real information in the block actor data - $this->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - $this->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - } - - private function registerFlatWoodBlockSerializers() : void{ - $this->map(Blocks::ACACIA_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::ACACIA_BUTTON))); - $this->map(Blocks::ACACIA_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::ACACIA_DOOR))); - $this->map(Blocks::ACACIA_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::ACACIA_FENCE_GATE))); - $this->map(Blocks::ACACIA_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::ACACIA_PRESSURE_PLATE))); - $this->map(Blocks::ACACIA_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::ACACIA_STANDING_SIGN))); - $this->map(Blocks::ACACIA_STAIRS(), fn(WoodenStairs $block) => Helper::encodeStairs($block, new Writer(Ids::ACACIA_STAIRS))); - $this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR))); - $this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN))); - $this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG); - $this->mapLog(Blocks::ACACIA_WOOD(), Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD); - $this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE); - $this->mapSimple(Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS); - $this->mapSlab(Blocks::ACACIA_SLAB(), Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB); - - $this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON))); - $this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR))); - $this->map(Blocks::BIRCH_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::BIRCH_FENCE_GATE))); - $this->map(Blocks::BIRCH_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::BIRCH_PRESSURE_PLATE))); - $this->map(Blocks::BIRCH_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::BIRCH_STANDING_SIGN))); - $this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR))); - $this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN))); - $this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG); - $this->mapLog(Blocks::BIRCH_WOOD(), Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD); - $this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE); - $this->mapSimple(Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS); - $this->mapSlab(Blocks::BIRCH_SLAB(), Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB); - $this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS); - - $this->map(Blocks::CHERRY_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CHERRY_BUTTON))); - $this->map(Blocks::CHERRY_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CHERRY_DOOR))); - $this->map(Blocks::CHERRY_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CHERRY_FENCE_GATE))); - $this->map(Blocks::CHERRY_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG)); - $this->map(Blocks::CHERRY_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CHERRY_PRESSURE_PLATE))); - $this->map(Blocks::CHERRY_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CHERRY_STANDING_SIGN))); - $this->map(Blocks::CHERRY_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CHERRY_TRAPDOOR))); - $this->map(Blocks::CHERRY_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CHERRY_WALL_SIGN))); - $this->mapSimple(Blocks::CHERRY_FENCE(), Ids::CHERRY_FENCE); - $this->mapSimple(Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS); - $this->mapSlab(Blocks::CHERRY_SLAB(), Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB); - $this->mapStairs(Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS); - $this->mapLog(Blocks::CHERRY_WOOD(), Ids::CHERRY_WOOD, Ids::STRIPPED_CHERRY_WOOD); - - $this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON))); - $this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR))); - $this->map(Blocks::CRIMSON_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CRIMSON_FENCE_GATE))); - $this->map(Blocks::CRIMSON_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE)); - $this->map(Blocks::CRIMSON_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CRIMSON_PRESSURE_PLATE))); - $this->map(Blocks::CRIMSON_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CRIMSON_STANDING_SIGN))); - $this->map(Blocks::CRIMSON_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM)); - $this->map(Blocks::CRIMSON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CRIMSON_TRAPDOOR))); - $this->map(Blocks::CRIMSON_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CRIMSON_WALL_SIGN))); - $this->mapSimple(Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE); - $this->mapSimple(Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS); - $this->mapSlab(Blocks::CRIMSON_SLAB(), Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB); - $this->mapStairs(Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS); - - $this->map(Blocks::DARK_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::DARK_OAK_BUTTON))); - $this->map(Blocks::DARK_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::DARK_OAK_DOOR))); - $this->map(Blocks::DARK_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::DARK_OAK_FENCE_GATE))); - $this->map(Blocks::DARK_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::DARK_OAK_PRESSURE_PLATE))); - $this->map(Blocks::DARK_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::DARKOAK_STANDING_SIGN))); - $this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR))); - $this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN))); - $this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG); - $this->mapLog(Blocks::DARK_OAK_WOOD(), Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD); - $this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE); - $this->mapSimple(Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS); - $this->mapSlab(Blocks::DARK_OAK_SLAB(), Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB); - $this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS); - - $this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON))); - $this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR))); - $this->map(Blocks::JUNGLE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::JUNGLE_FENCE_GATE))); - $this->map(Blocks::JUNGLE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::JUNGLE_PRESSURE_PLATE))); - $this->map(Blocks::JUNGLE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::JUNGLE_STANDING_SIGN))); - $this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR))); - $this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN))); - $this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG); - $this->mapLog(Blocks::JUNGLE_WOOD(), Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD); - $this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE); - $this->mapSimple(Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS); - $this->mapSlab(Blocks::JUNGLE_SLAB(), Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB); - $this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS); - - $this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON))); - $this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR))); - $this->map(Blocks::MANGROVE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::MANGROVE_FENCE_GATE))); - $this->map(Blocks::MANGROVE_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG)); - $this->map(Blocks::MANGROVE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::MANGROVE_PRESSURE_PLATE))); - $this->map(Blocks::MANGROVE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::MANGROVE_STANDING_SIGN))); - $this->map(Blocks::MANGROVE_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::MANGROVE_TRAPDOOR))); - $this->map(Blocks::MANGROVE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::MANGROVE_WALL_SIGN))); - $this->mapSimple(Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE); - $this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS); - $this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB); - $this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS); - $this->mapLog(Blocks::MANGROVE_WOOD(), Ids::MANGROVE_WOOD, Ids::STRIPPED_MANGROVE_WOOD); - - $this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON))); - $this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR))); - $this->map(Blocks::OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::FENCE_GATE))); - $this->map(Blocks::OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WOODEN_PRESSURE_PLATE))); - $this->map(Blocks::OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::STANDING_SIGN))); - $this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR))); - $this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN))); - $this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG); - $this->mapLog(Blocks::OAK_WOOD(), Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD); - $this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE); - $this->mapSimple(Blocks::OAK_PLANKS(), Ids::OAK_PLANKS); - $this->mapSlab(Blocks::OAK_SLAB(), Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB); - $this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS); - - $this->map(Blocks::PALE_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::PALE_OAK_BUTTON))); - $this->map(Blocks::PALE_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::PALE_OAK_DOOR))); - $this->map(Blocks::PALE_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::PALE_OAK_FENCE_GATE))); - $this->map(Blocks::PALE_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::PALE_OAK_PRESSURE_PLATE))); - $this->map(Blocks::PALE_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::PALE_OAK_STANDING_SIGN))); - $this->map(Blocks::PALE_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::PALE_OAK_TRAPDOOR))); - $this->map(Blocks::PALE_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::PALE_OAK_WALL_SIGN))); - $this->mapLog(Blocks::PALE_OAK_LOG(), Ids::PALE_OAK_LOG, Ids::STRIPPED_PALE_OAK_LOG); - $this->mapLog(Blocks::PALE_OAK_WOOD(), Ids::PALE_OAK_WOOD, Ids::STRIPPED_PALE_OAK_WOOD); - $this->mapSimple(Blocks::PALE_OAK_FENCE(), Ids::PALE_OAK_FENCE); - $this->mapSimple(Blocks::PALE_OAK_PLANKS(), Ids::PALE_OAK_PLANKS); - $this->mapSlab(Blocks::PALE_OAK_SLAB(), Ids::PALE_OAK_SLAB, Ids::PALE_OAK_DOUBLE_SLAB); - $this->mapStairs(Blocks::PALE_OAK_STAIRS(), Ids::PALE_OAK_STAIRS); - - $this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON))); - $this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR))); - $this->map(Blocks::SPRUCE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::SPRUCE_FENCE_GATE))); - $this->map(Blocks::SPRUCE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::SPRUCE_PRESSURE_PLATE))); - $this->map(Blocks::SPRUCE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::SPRUCE_STANDING_SIGN))); - $this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR))); - $this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN))); - $this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG); - $this->mapLog(Blocks::SPRUCE_WOOD(), Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD); - $this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE); - $this->mapSimple(Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS); - $this->mapSlab(Blocks::SPRUCE_SLAB(), Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS); - //wood and slabs still use the old way of storing wood type - - $this->map(Blocks::WARPED_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::WARPED_BUTTON))); - $this->map(Blocks::WARPED_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::WARPED_DOOR))); - $this->map(Blocks::WARPED_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::WARPED_FENCE_GATE))); - $this->map(Blocks::WARPED_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE)); - $this->map(Blocks::WARPED_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WARPED_PRESSURE_PLATE))); - $this->map(Blocks::WARPED_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::WARPED_STANDING_SIGN))); - $this->map(Blocks::WARPED_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM)); - $this->map(Blocks::WARPED_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::WARPED_TRAPDOOR))); - $this->map(Blocks::WARPED_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WARPED_WALL_SIGN))); - $this->mapSimple(Blocks::WARPED_FENCE(), Ids::WARPED_FENCE); - $this->mapSimple(Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS); - $this->mapSlab(Blocks::WARPED_SLAB(), Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB); - $this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS); - } - - private function registerLeavesSerializers() : void{ - //flattened IDs - $this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES))); - $this->map(Blocks::CHERRY_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::CHERRY_LEAVES))); - $this->map(Blocks::FLOWERING_AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES_FLOWERED))); - $this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES))); - $this->map(Blocks::PALE_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::PALE_OAK_LEAVES))); - - //legacy mess - $this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::ACACIA_LEAVES))); - $this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::BIRCH_LEAVES))); - $this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::DARK_OAK_LEAVES))); - $this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::JUNGLE_LEAVES))); - $this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::OAK_LEAVES))); - $this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::SPRUCE_LEAVES))); - } - - private function registerSaplingSerializers() : void{ - foreach([ - Ids::ACACIA_SAPLING => Blocks::ACACIA_SAPLING(), - Ids::BIRCH_SAPLING => Blocks::BIRCH_SAPLING(), - Ids::DARK_OAK_SAPLING => Blocks::DARK_OAK_SAPLING(), - Ids::JUNGLE_SAPLING => Blocks::JUNGLE_SAPLING(), - Ids::OAK_SAPLING => Blocks::OAK_SAPLING(), - Ids::SPRUCE_SAPLING => Blocks::SPRUCE_SAPLING(), - ] as $id => $block){ - $this->map($block, fn(Sapling $block) => Helper::encodeSapling($block, new Writer($id))); - } - } - - private function registerMobHeadSerializers() : void{ - $this->map(Blocks::MOB_HEAD(), fn(MobHead $block) => Writer::create(match ($block->getMobHeadType()){ - MobHeadType::CREEPER => Ids::CREEPER_HEAD, - MobHeadType::DRAGON => Ids::DRAGON_HEAD, - MobHeadType::PIGLIN => Ids::PIGLIN_HEAD, - MobHeadType::PLAYER => Ids::PLAYER_HEAD, - MobHeadType::SKELETON => Ids::SKELETON_SKULL, - MobHeadType::WITHER_SKELETON => Ids::WITHER_SKELETON_SKULL, - MobHeadType::ZOMBIE => Ids::ZOMBIE_HEAD, - })->writeFacingWithoutDown($block->getFacing())); - } - - private function registerCopperSerializers() : void{ - $this->map(Blocks::COPPER(), function(Copper $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) : - Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER), - [] - ); - }); - $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_CHISELED_COPPER, - Ids::WAXED_EXPOSED_CHISELED_COPPER, - Ids::WAXED_WEATHERED_CHISELED_COPPER, - Ids::WAXED_OXIDIZED_CHISELED_COPPER - ) : - Helper::selectCopperId($oxidation, - Ids::CHISELED_COPPER, - Ids::EXPOSED_CHISELED_COPPER, - Ids::WEATHERED_CHISELED_COPPER, - Ids::OXIDIZED_CHISELED_COPPER - ), - [] - ); - }); - $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_COPPER_GRATE, - Ids::WAXED_EXPOSED_COPPER_GRATE, - Ids::WAXED_WEATHERED_COPPER_GRATE, - Ids::WAXED_OXIDIZED_COPPER_GRATE - ) : - Helper::selectCopperId($oxidation, - Ids::COPPER_GRATE, - Ids::EXPOSED_COPPER_GRATE, - Ids::WEATHERED_COPPER_GRATE, - Ids::OXIDIZED_COPPER_GRATE - ), - [] - ); - }); - $this->map(Blocks::CUT_COPPER(), function(Copper $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) : - Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER), - [] - ); - }); - $this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeSlab( - $block, - ($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB - ) : - Helper::selectCopperId( - $oxidation, - Ids::CUT_COPPER_SLAB, - Ids::EXPOSED_CUT_COPPER_SLAB, - Ids::WEATHERED_CUT_COPPER_SLAB, - Ids::OXIDIZED_CUT_COPPER_SLAB - ) - ), - ($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB - ) : - Helper::selectCopperId( - $oxidation, - Ids::DOUBLE_CUT_COPPER_SLAB, - Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB - ) - ) - ); - }); - $this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeStairs( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_CUT_COPPER_STAIRS, - Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, - Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, - Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS - ) : - Helper::selectCopperId( - $oxidation, - Ids::CUT_COPPER_STAIRS, - Ids::EXPOSED_CUT_COPPER_STAIRS, - Ids::WEATHERED_CUT_COPPER_STAIRS, - Ids::OXIDIZED_CUT_COPPER_STAIRS - ) - ) - ); - }); - $this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{ - $oxidation = $block->getOxidation(); - return Writer::create($block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_COPPER_BULB, - Ids::WAXED_EXPOSED_COPPER_BULB, - Ids::WAXED_WEATHERED_COPPER_BULB, - Ids::WAXED_OXIDIZED_COPPER_BULB) : - Helper::selectCopperId($oxidation, - Ids::COPPER_BULB, - Ids::EXPOSED_COPPER_BULB, - Ids::WEATHERED_COPPER_BULB, - Ids::OXIDIZED_COPPER_BULB - )) - ->writeBool(StateNames::LIT, $block->isLit()) - ->writeBool(StateNames::POWERED_BIT, $block->isPowered()); - }); - $this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeDoor( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_COPPER_DOOR, - Ids::WAXED_EXPOSED_COPPER_DOOR, - Ids::WAXED_WEATHERED_COPPER_DOOR, - Ids::WAXED_OXIDIZED_COPPER_DOOR - ) : - Helper::selectCopperId( - $oxidation, - Ids::COPPER_DOOR, - Ids::EXPOSED_COPPER_DOOR, - Ids::WEATHERED_COPPER_DOOR, - Ids::OXIDIZED_COPPER_DOOR - ) - ) - ); - }); - $this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeTrapdoor( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_COPPER_TRAPDOOR, - Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, - Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, - Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR - ) : - Helper::selectCopperId( - $oxidation, - Ids::COPPER_TRAPDOOR, - Ids::EXPOSED_COPPER_TRAPDOOR, - Ids::WEATHERED_COPPER_TRAPDOOR, - Ids::OXIDIZED_COPPER_TRAPDOOR - ) - ) - ); - }); - } - - private function registerSimpleSerializers() : void{ - $this->mapSimple(Blocks::AIR(), Ids::AIR); - $this->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK); - $this->mapSimple(Blocks::ANCIENT_DEBRIS(), Ids::ANCIENT_DEBRIS); - $this->mapSimple(Blocks::ANDESITE(), Ids::ANDESITE); - $this->mapSimple(Blocks::BARRIER(), Ids::BARRIER); - $this->mapSimple(Blocks::BEACON(), Ids::BEACON); - $this->mapSimple(Blocks::BLACKSTONE(), Ids::BLACKSTONE); - $this->mapSimple(Blocks::BLUE_ICE(), Ids::BLUE_ICE); - $this->mapSimple(Blocks::BOOKSHELF(), Ids::BOOKSHELF); - $this->mapSimple(Blocks::BRICKS(), Ids::BRICK_BLOCK); - $this->mapSimple(Blocks::BROWN_MUSHROOM(), Ids::BROWN_MUSHROOM); - $this->mapSimple(Blocks::BUDDING_AMETHYST(), Ids::BUDDING_AMETHYST); - $this->mapSimple(Blocks::CALCITE(), Ids::CALCITE); - $this->mapSimple(Blocks::CARTOGRAPHY_TABLE(), Ids::CARTOGRAPHY_TABLE); - $this->mapSimple(Blocks::CHEMICAL_HEAT(), Ids::CHEMICAL_HEAT); - $this->mapSimple(Blocks::CHISELED_DEEPSLATE(), Ids::CHISELED_DEEPSLATE); - $this->mapSimple(Blocks::CHISELED_NETHER_BRICKS(), Ids::CHISELED_NETHER_BRICKS); - $this->mapSimple(Blocks::CHISELED_POLISHED_BLACKSTONE(), Ids::CHISELED_POLISHED_BLACKSTONE); - $this->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE); - $this->mapSimple(Blocks::CHISELED_RESIN_BRICKS(), Ids::CHISELED_RESIN_BRICKS); - $this->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE); - $this->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS); - $this->mapSimple(Blocks::CHISELED_TUFF(), Ids::CHISELED_TUFF); - $this->mapSimple(Blocks::CHISELED_TUFF_BRICKS(), Ids::CHISELED_TUFF_BRICKS); - $this->mapSimple(Blocks::CHORUS_PLANT(), Ids::CHORUS_PLANT); - $this->mapSimple(Blocks::CLAY(), Ids::CLAY); - $this->mapSimple(Blocks::COAL(), Ids::COAL_BLOCK); - $this->mapSimple(Blocks::COAL_ORE(), Ids::COAL_ORE); - $this->mapSimple(Blocks::COBBLED_DEEPSLATE(), Ids::COBBLED_DEEPSLATE); - $this->mapSimple(Blocks::COBBLESTONE(), Ids::COBBLESTONE); - $this->mapSimple(Blocks::COBWEB(), Ids::WEB); - $this->mapSimple(Blocks::COPPER_ORE(), Ids::COPPER_ORE); - $this->mapSimple(Blocks::CRACKED_DEEPSLATE_BRICKS(), Ids::CRACKED_DEEPSLATE_BRICKS); - $this->mapSimple(Blocks::CRACKED_DEEPSLATE_TILES(), Ids::CRACKED_DEEPSLATE_TILES); - $this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS); - $this->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS); - $this->mapSimple(Blocks::CRACKED_STONE_BRICKS(), Ids::CRACKED_STONE_BRICKS); - $this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE); - $this->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS); - $this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN); - $this->mapSimple(Blocks::DANDELION(), Ids::DANDELION); - $this->mapSimple(Blocks::CUT_RED_SANDSTONE(), Ids::CUT_RED_SANDSTONE); - $this->mapSimple(Blocks::CUT_SANDSTONE(), Ids::CUT_SANDSTONE); - $this->mapSimple(Blocks::DARK_PRISMARINE(), Ids::DARK_PRISMARINE); - $this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH); - $this->mapSimple(Blocks::DEEPSLATE_BRICKS(), Ids::DEEPSLATE_BRICKS); - $this->mapSimple(Blocks::DEEPSLATE_COAL_ORE(), Ids::DEEPSLATE_COAL_ORE); - $this->mapSimple(Blocks::DEEPSLATE_COPPER_ORE(), Ids::DEEPSLATE_COPPER_ORE); - $this->mapSimple(Blocks::DEEPSLATE_DIAMOND_ORE(), Ids::DEEPSLATE_DIAMOND_ORE); - $this->mapSimple(Blocks::DEEPSLATE_EMERALD_ORE(), Ids::DEEPSLATE_EMERALD_ORE); - $this->mapSimple(Blocks::DEEPSLATE_GOLD_ORE(), Ids::DEEPSLATE_GOLD_ORE); - $this->mapSimple(Blocks::DEEPSLATE_IRON_ORE(), Ids::DEEPSLATE_IRON_ORE); - $this->mapSimple(Blocks::DEEPSLATE_LAPIS_LAZULI_ORE(), Ids::DEEPSLATE_LAPIS_ORE); - $this->mapSimple(Blocks::DEEPSLATE_TILES(), Ids::DEEPSLATE_TILES); - $this->mapSimple(Blocks::DIAMOND(), Ids::DIAMOND_BLOCK); - $this->mapSimple(Blocks::DIAMOND_ORE(), Ids::DIAMOND_ORE); - $this->mapSimple(Blocks::DIORITE(), Ids::DIORITE); - $this->mapSimple(Blocks::DRAGON_EGG(), Ids::DRAGON_EGG); - $this->mapSimple(Blocks::DRIED_KELP(), Ids::DRIED_KELP_BLOCK); - $this->mapSimple(Blocks::ELEMENT_ACTINIUM(), Ids::ELEMENT_89); - $this->mapSimple(Blocks::ELEMENT_ALUMINUM(), Ids::ELEMENT_13); - $this->mapSimple(Blocks::ELEMENT_AMERICIUM(), Ids::ELEMENT_95); - $this->mapSimple(Blocks::ELEMENT_ANTIMONY(), Ids::ELEMENT_51); - $this->mapSimple(Blocks::ELEMENT_ARGON(), Ids::ELEMENT_18); - $this->mapSimple(Blocks::ELEMENT_ARSENIC(), Ids::ELEMENT_33); - $this->mapSimple(Blocks::ELEMENT_ASTATINE(), Ids::ELEMENT_85); - $this->mapSimple(Blocks::ELEMENT_BARIUM(), Ids::ELEMENT_56); - $this->mapSimple(Blocks::ELEMENT_BERKELIUM(), Ids::ELEMENT_97); - $this->mapSimple(Blocks::ELEMENT_BERYLLIUM(), Ids::ELEMENT_4); - $this->mapSimple(Blocks::ELEMENT_BISMUTH(), Ids::ELEMENT_83); - $this->mapSimple(Blocks::ELEMENT_BOHRIUM(), Ids::ELEMENT_107); - $this->mapSimple(Blocks::ELEMENT_BORON(), Ids::ELEMENT_5); - $this->mapSimple(Blocks::ELEMENT_BROMINE(), Ids::ELEMENT_35); - $this->mapSimple(Blocks::ELEMENT_CADMIUM(), Ids::ELEMENT_48); - $this->mapSimple(Blocks::ELEMENT_CALCIUM(), Ids::ELEMENT_20); - $this->mapSimple(Blocks::ELEMENT_CALIFORNIUM(), Ids::ELEMENT_98); - $this->mapSimple(Blocks::ELEMENT_CARBON(), Ids::ELEMENT_6); - $this->mapSimple(Blocks::ELEMENT_CERIUM(), Ids::ELEMENT_58); - $this->mapSimple(Blocks::ELEMENT_CESIUM(), Ids::ELEMENT_55); - $this->mapSimple(Blocks::ELEMENT_CHLORINE(), Ids::ELEMENT_17); - $this->mapSimple(Blocks::ELEMENT_CHROMIUM(), Ids::ELEMENT_24); - $this->mapSimple(Blocks::ELEMENT_COBALT(), Ids::ELEMENT_27); - $this->mapSimple(Blocks::ELEMENT_COPERNICIUM(), Ids::ELEMENT_112); - $this->mapSimple(Blocks::ELEMENT_COPPER(), Ids::ELEMENT_29); - $this->mapSimple(Blocks::ELEMENT_CURIUM(), Ids::ELEMENT_96); - $this->mapSimple(Blocks::ELEMENT_DARMSTADTIUM(), Ids::ELEMENT_110); - $this->mapSimple(Blocks::ELEMENT_DUBNIUM(), Ids::ELEMENT_105); - $this->mapSimple(Blocks::ELEMENT_DYSPROSIUM(), Ids::ELEMENT_66); - $this->mapSimple(Blocks::ELEMENT_EINSTEINIUM(), Ids::ELEMENT_99); - $this->mapSimple(Blocks::ELEMENT_ERBIUM(), Ids::ELEMENT_68); - $this->mapSimple(Blocks::ELEMENT_EUROPIUM(), Ids::ELEMENT_63); - $this->mapSimple(Blocks::ELEMENT_FERMIUM(), Ids::ELEMENT_100); - $this->mapSimple(Blocks::ELEMENT_FLEROVIUM(), Ids::ELEMENT_114); - $this->mapSimple(Blocks::ELEMENT_FLUORINE(), Ids::ELEMENT_9); - $this->mapSimple(Blocks::ELEMENT_FRANCIUM(), Ids::ELEMENT_87); - $this->mapSimple(Blocks::ELEMENT_GADOLINIUM(), Ids::ELEMENT_64); - $this->mapSimple(Blocks::ELEMENT_GALLIUM(), Ids::ELEMENT_31); - $this->mapSimple(Blocks::ELEMENT_GERMANIUM(), Ids::ELEMENT_32); - $this->mapSimple(Blocks::ELEMENT_GOLD(), Ids::ELEMENT_79); - $this->mapSimple(Blocks::ELEMENT_HAFNIUM(), Ids::ELEMENT_72); - $this->mapSimple(Blocks::ELEMENT_HASSIUM(), Ids::ELEMENT_108); - $this->mapSimple(Blocks::ELEMENT_HELIUM(), Ids::ELEMENT_2); - $this->mapSimple(Blocks::ELEMENT_HOLMIUM(), Ids::ELEMENT_67); - $this->mapSimple(Blocks::ELEMENT_HYDROGEN(), Ids::ELEMENT_1); - $this->mapSimple(Blocks::ELEMENT_INDIUM(), Ids::ELEMENT_49); - $this->mapSimple(Blocks::ELEMENT_IODINE(), Ids::ELEMENT_53); - $this->mapSimple(Blocks::ELEMENT_IRIDIUM(), Ids::ELEMENT_77); - $this->mapSimple(Blocks::ELEMENT_IRON(), Ids::ELEMENT_26); - $this->mapSimple(Blocks::ELEMENT_KRYPTON(), Ids::ELEMENT_36); - $this->mapSimple(Blocks::ELEMENT_LANTHANUM(), Ids::ELEMENT_57); - $this->mapSimple(Blocks::ELEMENT_LAWRENCIUM(), Ids::ELEMENT_103); - $this->mapSimple(Blocks::ELEMENT_LEAD(), Ids::ELEMENT_82); - $this->mapSimple(Blocks::ELEMENT_LITHIUM(), Ids::ELEMENT_3); - $this->mapSimple(Blocks::ELEMENT_LIVERMORIUM(), Ids::ELEMENT_116); - $this->mapSimple(Blocks::ELEMENT_LUTETIUM(), Ids::ELEMENT_71); - $this->mapSimple(Blocks::ELEMENT_MAGNESIUM(), Ids::ELEMENT_12); - $this->mapSimple(Blocks::ELEMENT_MANGANESE(), Ids::ELEMENT_25); - $this->mapSimple(Blocks::ELEMENT_MEITNERIUM(), Ids::ELEMENT_109); - $this->mapSimple(Blocks::ELEMENT_MENDELEVIUM(), Ids::ELEMENT_101); - $this->mapSimple(Blocks::ELEMENT_MERCURY(), Ids::ELEMENT_80); - $this->mapSimple(Blocks::ELEMENT_MOLYBDENUM(), Ids::ELEMENT_42); - $this->mapSimple(Blocks::ELEMENT_MOSCOVIUM(), Ids::ELEMENT_115); - $this->mapSimple(Blocks::ELEMENT_NEODYMIUM(), Ids::ELEMENT_60); - $this->mapSimple(Blocks::ELEMENT_NEON(), Ids::ELEMENT_10); - $this->mapSimple(Blocks::ELEMENT_NEPTUNIUM(), Ids::ELEMENT_93); - $this->mapSimple(Blocks::ELEMENT_NICKEL(), Ids::ELEMENT_28); - $this->mapSimple(Blocks::ELEMENT_NIHONIUM(), Ids::ELEMENT_113); - $this->mapSimple(Blocks::ELEMENT_NIOBIUM(), Ids::ELEMENT_41); - $this->mapSimple(Blocks::ELEMENT_NITROGEN(), Ids::ELEMENT_7); - $this->mapSimple(Blocks::ELEMENT_NOBELIUM(), Ids::ELEMENT_102); - $this->mapSimple(Blocks::ELEMENT_OGANESSON(), Ids::ELEMENT_118); - $this->mapSimple(Blocks::ELEMENT_OSMIUM(), Ids::ELEMENT_76); - $this->mapSimple(Blocks::ELEMENT_OXYGEN(), Ids::ELEMENT_8); - $this->mapSimple(Blocks::ELEMENT_PALLADIUM(), Ids::ELEMENT_46); - $this->mapSimple(Blocks::ELEMENT_PHOSPHORUS(), Ids::ELEMENT_15); - $this->mapSimple(Blocks::ELEMENT_PLATINUM(), Ids::ELEMENT_78); - $this->mapSimple(Blocks::ELEMENT_PLUTONIUM(), Ids::ELEMENT_94); - $this->mapSimple(Blocks::ELEMENT_POLONIUM(), Ids::ELEMENT_84); - $this->mapSimple(Blocks::ELEMENT_POTASSIUM(), Ids::ELEMENT_19); - $this->mapSimple(Blocks::ELEMENT_PRASEODYMIUM(), Ids::ELEMENT_59); - $this->mapSimple(Blocks::ELEMENT_PROMETHIUM(), Ids::ELEMENT_61); - $this->mapSimple(Blocks::ELEMENT_PROTACTINIUM(), Ids::ELEMENT_91); - $this->mapSimple(Blocks::ELEMENT_RADIUM(), Ids::ELEMENT_88); - $this->mapSimple(Blocks::ELEMENT_RADON(), Ids::ELEMENT_86); - $this->mapSimple(Blocks::ELEMENT_RHENIUM(), Ids::ELEMENT_75); - $this->mapSimple(Blocks::ELEMENT_RHODIUM(), Ids::ELEMENT_45); - $this->mapSimple(Blocks::ELEMENT_ROENTGENIUM(), Ids::ELEMENT_111); - $this->mapSimple(Blocks::ELEMENT_RUBIDIUM(), Ids::ELEMENT_37); - $this->mapSimple(Blocks::ELEMENT_RUTHENIUM(), Ids::ELEMENT_44); - $this->mapSimple(Blocks::ELEMENT_RUTHERFORDIUM(), Ids::ELEMENT_104); - $this->mapSimple(Blocks::ELEMENT_SAMARIUM(), Ids::ELEMENT_62); - $this->mapSimple(Blocks::ELEMENT_SCANDIUM(), Ids::ELEMENT_21); - $this->mapSimple(Blocks::ELEMENT_SEABORGIUM(), Ids::ELEMENT_106); - $this->mapSimple(Blocks::ELEMENT_SELENIUM(), Ids::ELEMENT_34); - $this->mapSimple(Blocks::ELEMENT_SILICON(), Ids::ELEMENT_14); - $this->mapSimple(Blocks::ELEMENT_SILVER(), Ids::ELEMENT_47); - $this->mapSimple(Blocks::ELEMENT_SODIUM(), Ids::ELEMENT_11); - $this->mapSimple(Blocks::ELEMENT_STRONTIUM(), Ids::ELEMENT_38); - $this->mapSimple(Blocks::ELEMENT_SULFUR(), Ids::ELEMENT_16); - $this->mapSimple(Blocks::ELEMENT_TANTALUM(), Ids::ELEMENT_73); - $this->mapSimple(Blocks::ELEMENT_TECHNETIUM(), Ids::ELEMENT_43); - $this->mapSimple(Blocks::ELEMENT_TELLURIUM(), Ids::ELEMENT_52); - $this->mapSimple(Blocks::ELEMENT_TENNESSINE(), Ids::ELEMENT_117); - $this->mapSimple(Blocks::ELEMENT_TERBIUM(), Ids::ELEMENT_65); - $this->mapSimple(Blocks::ELEMENT_THALLIUM(), Ids::ELEMENT_81); - $this->mapSimple(Blocks::ELEMENT_THORIUM(), Ids::ELEMENT_90); - $this->mapSimple(Blocks::ELEMENT_THULIUM(), Ids::ELEMENT_69); - $this->mapSimple(Blocks::ELEMENT_TIN(), Ids::ELEMENT_50); - $this->mapSimple(Blocks::ELEMENT_TITANIUM(), Ids::ELEMENT_22); - $this->mapSimple(Blocks::ELEMENT_TUNGSTEN(), Ids::ELEMENT_74); - $this->mapSimple(Blocks::ELEMENT_URANIUM(), Ids::ELEMENT_92); - $this->mapSimple(Blocks::ELEMENT_VANADIUM(), Ids::ELEMENT_23); - $this->mapSimple(Blocks::ELEMENT_XENON(), Ids::ELEMENT_54); - $this->mapSimple(Blocks::ELEMENT_YTTERBIUM(), Ids::ELEMENT_70); - $this->mapSimple(Blocks::ELEMENT_YTTRIUM(), Ids::ELEMENT_39); - $this->mapSimple(Blocks::ELEMENT_ZERO(), Ids::ELEMENT_0); - $this->mapSimple(Blocks::ELEMENT_ZINC(), Ids::ELEMENT_30); - $this->mapSimple(Blocks::ELEMENT_ZIRCONIUM(), Ids::ELEMENT_40); - $this->mapSimple(Blocks::EMERALD(), Ids::EMERALD_BLOCK); - $this->mapSimple(Blocks::EMERALD_ORE(), Ids::EMERALD_ORE); - $this->mapSimple(Blocks::ENCHANTING_TABLE(), Ids::ENCHANTING_TABLE); - $this->mapSimple(Blocks::END_STONE(), Ids::END_STONE); - $this->mapSimple(Blocks::END_STONE_BRICKS(), Ids::END_BRICKS); - $this->mapSimple(Blocks::FERN(), Ids::FERN); - $this->mapSimple(Blocks::FLETCHING_TABLE(), Ids::FLETCHING_TABLE); - $this->mapSimple(Blocks::GILDED_BLACKSTONE(), Ids::GILDED_BLACKSTONE); - $this->mapSimple(Blocks::GLASS(), Ids::GLASS); - $this->mapSimple(Blocks::GLASS_PANE(), Ids::GLASS_PANE); - $this->mapSimple(Blocks::GLOWING_OBSIDIAN(), Ids::GLOWINGOBSIDIAN); - $this->mapSimple(Blocks::GLOWSTONE(), Ids::GLOWSTONE); - $this->mapSimple(Blocks::GOLD(), Ids::GOLD_BLOCK); - $this->mapSimple(Blocks::GOLD_ORE(), Ids::GOLD_ORE); - $this->mapSimple(Blocks::GRANITE(), Ids::GRANITE); - $this->mapSimple(Blocks::GRASS(), Ids::GRASS_BLOCK); - $this->mapSimple(Blocks::GRASS_PATH(), Ids::GRASS_PATH); - $this->mapSimple(Blocks::GRAVEL(), Ids::GRAVEL); - $this->mapSimple(Blocks::HANGING_ROOTS(), Ids::HANGING_ROOTS); - $this->mapSimple(Blocks::HARDENED_CLAY(), Ids::HARDENED_CLAY); - $this->mapSimple(Blocks::HARDENED_GLASS(), Ids::HARD_GLASS); - $this->mapSimple(Blocks::HARDENED_GLASS_PANE(), Ids::HARD_GLASS_PANE); - $this->mapSimple(Blocks::HONEYCOMB(), Ids::HONEYCOMB_BLOCK); - $this->mapSimple(Blocks::ICE(), Ids::ICE); - $this->mapSimple(Blocks::INFESTED_CHISELED_STONE_BRICK(), Ids::INFESTED_CHISELED_STONE_BRICKS); - $this->mapSimple(Blocks::INFESTED_COBBLESTONE(), Ids::INFESTED_COBBLESTONE); - $this->mapSimple(Blocks::INFESTED_CRACKED_STONE_BRICK(), Ids::INFESTED_CRACKED_STONE_BRICKS); - $this->mapSimple(Blocks::INFESTED_MOSSY_STONE_BRICK(), Ids::INFESTED_MOSSY_STONE_BRICKS); - $this->mapSimple(Blocks::INFESTED_STONE(), Ids::INFESTED_STONE); - $this->mapSimple(Blocks::INFESTED_STONE_BRICK(), Ids::INFESTED_STONE_BRICKS); - $this->mapSimple(Blocks::INFO_UPDATE(), Ids::INFO_UPDATE); - $this->mapSimple(Blocks::INFO_UPDATE2(), Ids::INFO_UPDATE2); - $this->mapSimple(Blocks::INVISIBLE_BEDROCK(), Ids::INVISIBLE_BEDROCK); - $this->mapSimple(Blocks::IRON(), Ids::IRON_BLOCK); - $this->mapSimple(Blocks::IRON_BARS(), Ids::IRON_BARS); - $this->mapSimple(Blocks::IRON_ORE(), Ids::IRON_ORE); - $this->mapSimple(Blocks::JUKEBOX(), Ids::JUKEBOX); - $this->mapSimple(Blocks::LAPIS_LAZULI(), Ids::LAPIS_BLOCK); - $this->mapSimple(Blocks::LAPIS_LAZULI_ORE(), Ids::LAPIS_ORE); - $this->mapSimple(Blocks::LEGACY_STONECUTTER(), Ids::STONECUTTER); - $this->mapSimple(Blocks::LILY_PAD(), Ids::WATERLILY); - $this->mapSimple(Blocks::MAGMA(), Ids::MAGMA); - $this->mapSimple(Blocks::MANGROVE_ROOTS(), Ids::MANGROVE_ROOTS); - $this->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK); - $this->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER); - $this->mapSimple(Blocks::MOSSY_COBBLESTONE(), Ids::MOSSY_COBBLESTONE); - $this->mapSimple(Blocks::MOSSY_STONE_BRICKS(), Ids::MOSSY_STONE_BRICKS); - $this->mapSimple(Blocks::MUD(), Ids::MUD); - $this->mapSimple(Blocks::MUD_BRICKS(), Ids::MUD_BRICKS); - $this->mapSimple(Blocks::MYCELIUM(), Ids::MYCELIUM); - $this->mapSimple(Blocks::NETHERITE(), Ids::NETHERITE_BLOCK); - $this->mapSimple(Blocks::NETHERRACK(), Ids::NETHERRACK); - $this->mapSimple(Blocks::NETHER_BRICKS(), Ids::NETHER_BRICK); - $this->mapSimple(Blocks::NETHER_BRICK_FENCE(), Ids::NETHER_BRICK_FENCE); - $this->mapSimple(Blocks::NETHER_GOLD_ORE(), Ids::NETHER_GOLD_ORE); - $this->mapSimple(Blocks::NETHER_QUARTZ_ORE(), Ids::QUARTZ_ORE); - $this->mapSimple(Blocks::NETHER_REACTOR_CORE(), Ids::NETHERREACTOR); - $this->mapSimple(Blocks::NETHER_WART_BLOCK(), Ids::NETHER_WART_BLOCK); - $this->mapSimple(Blocks::NOTE_BLOCK(), Ids::NOTEBLOCK); - $this->mapSimple(Blocks::OBSIDIAN(), Ids::OBSIDIAN); - $this->mapSimple(Blocks::PACKED_ICE(), Ids::PACKED_ICE); - $this->mapSimple(Blocks::PACKED_MUD(), Ids::PACKED_MUD); - $this->mapSimple(Blocks::PODZOL(), Ids::PODZOL); - $this->mapSimple(Blocks::POLISHED_ANDESITE(), Ids::POLISHED_ANDESITE); - $this->mapSimple(Blocks::POLISHED_BLACKSTONE(), Ids::POLISHED_BLACKSTONE); - $this->mapSimple(Blocks::POLISHED_BLACKSTONE_BRICKS(), Ids::POLISHED_BLACKSTONE_BRICKS); - $this->mapSimple(Blocks::POLISHED_DEEPSLATE(), Ids::POLISHED_DEEPSLATE); - $this->mapSimple(Blocks::POLISHED_DIORITE(), Ids::POLISHED_DIORITE); - $this->mapSimple(Blocks::POLISHED_GRANITE(), Ids::POLISHED_GRANITE); - $this->mapSimple(Blocks::POLISHED_TUFF(), Ids::POLISHED_TUFF); - $this->mapSimple(Blocks::PRISMARINE(), Ids::PRISMARINE); - $this->mapSimple(Blocks::PRISMARINE_BRICKS(), Ids::PRISMARINE_BRICKS); - $this->mapSimple(Blocks::QUARTZ_BRICKS(), Ids::QUARTZ_BRICKS); - $this->mapSimple(Blocks::RAW_COPPER(), Ids::RAW_COPPER_BLOCK); - $this->mapSimple(Blocks::RAW_GOLD(), Ids::RAW_GOLD_BLOCK); - $this->mapSimple(Blocks::RAW_IRON(), Ids::RAW_IRON_BLOCK); - $this->mapSimple(Blocks::REDSTONE(), Ids::REDSTONE_BLOCK); - $this->mapSimple(Blocks::RED_MUSHROOM(), Ids::RED_MUSHROOM); - $this->mapSimple(Blocks::RED_NETHER_BRICKS(), Ids::RED_NETHER_BRICK); - $this->mapSimple(Blocks::RED_SAND(), Ids::RED_SAND); - $this->mapSimple(Blocks::RED_SANDSTONE(), Ids::RED_SANDSTONE); - $this->mapSimple(Blocks::REINFORCED_DEEPSLATE(), Ids::REINFORCED_DEEPSLATE); - $this->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6); - $this->mapSimple(Blocks::RESIN(), Ids::RESIN_BLOCK); - $this->mapSimple(Blocks::RESIN_BRICKS(), Ids::RESIN_BRICKS); - $this->mapSimple(Blocks::SAND(), Ids::SAND); - $this->mapSimple(Blocks::SANDSTONE(), Ids::SANDSTONE); - $this->mapSimple(Blocks::SCULK(), Ids::SCULK); - $this->mapSimple(Blocks::SEA_LANTERN(), Ids::SEA_LANTERN); - $this->mapSimple(Blocks::SHROOMLIGHT(), Ids::SHROOMLIGHT); - $this->mapSimple(Blocks::SHULKER_BOX(), Ids::UNDYED_SHULKER_BOX); - $this->mapSimple(Blocks::SLIME(), Ids::SLIME); - $this->mapSimple(Blocks::SMITHING_TABLE(), Ids::SMITHING_TABLE); - $this->mapSimple(Blocks::SMOOTH_BASALT(), Ids::SMOOTH_BASALT); - $this->mapSimple(Blocks::SMOOTH_RED_SANDSTONE(), Ids::SMOOTH_RED_SANDSTONE); - $this->mapSimple(Blocks::SMOOTH_SANDSTONE(), Ids::SMOOTH_SANDSTONE); - $this->mapSimple(Blocks::SMOOTH_STONE(), Ids::SMOOTH_STONE); - $this->mapSimple(Blocks::SNOW(), Ids::SNOW); - $this->mapSimple(Blocks::SOUL_SAND(), Ids::SOUL_SAND); - $this->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL); - $this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM); - $this->mapSimple(Blocks::STONE(), Ids::STONE); - $this->mapSimple(Blocks::STONE_BRICKS(), Ids::STONE_BRICKS); - $this->mapSimple(Blocks::TALL_GRASS(), Ids::SHORT_GRASS); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( - $this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS); - $this->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER); - $this->mapSimple(Blocks::TUFF(), Ids::TUFF); - $this->mapSimple(Blocks::TUFF_BRICKS(), Ids::TUFF_BRICKS); - $this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK); - $this->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS); - $this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE); - - $this->mapSimple(Blocks::ALLIUM(), Ids::ALLIUM); - $this->mapSimple(Blocks::CORNFLOWER(), Ids::CORNFLOWER); - $this->mapSimple(Blocks::AZURE_BLUET(), Ids::AZURE_BLUET); - $this->mapSimple(Blocks::LILY_OF_THE_VALLEY(), Ids::LILY_OF_THE_VALLEY); - $this->mapSimple(Blocks::BLUE_ORCHID(), Ids::BLUE_ORCHID); - $this->mapSimple(Blocks::OXEYE_DAISY(), Ids::OXEYE_DAISY); - $this->mapSimple(Blocks::POPPY(), Ids::POPPY); - $this->mapSimple(Blocks::ORANGE_TULIP(), Ids::ORANGE_TULIP); - $this->mapSimple(Blocks::PINK_TULIP(), Ids::PINK_TULIP); - $this->mapSimple(Blocks::RED_TULIP(), Ids::RED_TULIP); - $this->mapSimple(Blocks::WHITE_TULIP(), Ids::WHITE_TULIP); - } - - private function registerSerializers() : void{ - $this->map(Blocks::ACTIVATOR_RAIL(), function(ActivatorRail $block) : Writer{ - return Writer::create(Ids::ACTIVATOR_RAIL) - ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); - $this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create( - match($stage = $block->getStage()){ - AmethystCluster::STAGE_SMALL_BUD => Ids::SMALL_AMETHYST_BUD, - AmethystCluster::STAGE_MEDIUM_BUD => Ids::MEDIUM_AMETHYST_BUD, - AmethystCluster::STAGE_LARGE_BUD => Ids::LARGE_AMETHYST_BUD, - AmethystCluster::STAGE_CLUSTER => Ids::AMETHYST_CLUSTER, - default => throw new BlockStateSerializeException("Invalid Amethyst Cluster stage $stage"), - }) - ->writeBlockFace($block->getFacing()) - ); - $this->mapSlab(Blocks::ANDESITE_SLAB(), Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB); - $this->map(Blocks::ANDESITE_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::ANDESITE_STAIRS))); - $this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::ANDESITE_WALL))); - $this->map(Blocks::ANVIL(), fn(Anvil $block) : Writer => Writer::create( - match($damage = $block->getDamage()){ - 0 => Ids::ANVIL, - 1 => Ids::CHIPPED_ANVIL, - 2 => Ids::DAMAGED_ANVIL, - default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"), - }) - ->writeCardinalHorizontalFacing($block->getFacing()) - ); - $this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{ - return Writer::create(Ids::BAMBOO) - ->writeBool(StateNames::AGE_BIT, $block->isReady()) - ->writeString(StateNames::BAMBOO_LEAF_SIZE, match($block->getLeafSize()){ - Bamboo::NO_LEAVES => StringValues::BAMBOO_LEAF_SIZE_NO_LEAVES, - Bamboo::SMALL_LEAVES => StringValues::BAMBOO_LEAF_SIZE_SMALL_LEAVES, - Bamboo::LARGE_LEAVES => StringValues::BAMBOO_LEAF_SIZE_LARGE_LEAVES, - default => throw new BlockStateSerializeException("Invalid Bamboo leaf thickness " . $block->getLeafSize()), - }) - ->writeString(StateNames::BAMBOO_STALK_THICKNESS, $block->isThick() ? StringValues::BAMBOO_STALK_THICKNESS_THICK : StringValues::BAMBOO_STALK_THICKNESS_THIN); - }); - $this->map(Blocks::BAMBOO_SAPLING(), function(BambooSapling $block) : Writer{ - return Writer::create(Ids::BAMBOO_SAPLING) - ->writeBool(StateNames::AGE_BIT, $block->isReady()); - }); - $this->map(Blocks::BANNER(), function(FloorBanner $block) : Writer{ - return Writer::create(Ids::STANDING_BANNER) - ->writeInt(StateNames::GROUND_SIGN_DIRECTION, $block->getRotation()); - }); - $this->map(Blocks::BARREL(), function(Barrel $block) : Writer{ - return Writer::create(Ids::BARREL) - ->writeBool(StateNames::OPEN_BIT, $block->isOpen()) - ->writeFacingDirection($block->getFacing()); - }); - $this->map(Blocks::BASALT(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::BASALT) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::BED(), function(Bed $block) : Writer{ - return Writer::create(Ids::BED) - ->writeBool(StateNames::HEAD_PIECE_BIT, $block->isHeadPart()) - ->writeBool(StateNames::OCCUPIED_BIT, $block->isOccupied()) - ->writeLegacyHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::BEDROCK(), function(Block $block) : Writer{ - return Writer::create(Ids::BEDROCK) - ->writeBool(StateNames::INFINIBURN_BIT, $block->burnsForever()); - }); - $this->map(Blocks::BEETROOTS(), fn(Beetroot $block) => Helper::encodeCrops($block, new Writer(Ids::BEETROOT))); - $this->map(Blocks::BELL(), function(Bell $block) : Writer{ - return Writer::create(Ids::BELL) - ->writeBellAttachmentType($block->getAttachmentType()) - ->writeBool(StateNames::TOGGLE_BIT, false) //we don't care about this; it's just to keep MCPE happy - ->writeLegacyHorizontalFacing($block->getFacing()); - - }); - $this->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeString(StateNames::BIG_DRIPLEAF_TILT, match($block->getLeafState()){ - DripleafState::STABLE => StringValues::BIG_DRIPLEAF_TILT_NONE, - DripleafState::UNSTABLE => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE, - DripleafState::PARTIAL_TILT => StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT, - DripleafState::FULL_TILT => StringValues::BIG_DRIPLEAF_TILT_FULL_TILT, - }) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true); - }); - $this->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false); - }); - $this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS); - $this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL))); - $this->map(Blocks::BLAST_FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::BLAST_FURNACE, Ids::LIT_BLAST_FURNACE)); - $this->map(Blocks::BLUE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_BLUE))); - $this->map(Blocks::BONE_BLOCK(), function(BoneBlock $block) : Writer{ - return Writer::create(Ids::BONE_BLOCK) - ->writeInt(StateNames::DEPRECATED, 0) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::BREWING_STAND(), function(BrewingStand $block) : Writer{ - return Writer::create(Ids::BREWING_STAND) - ->writeBool(StateNames::BREWING_STAND_SLOT_A_BIT, $block->hasSlot(BrewingStandSlot::EAST)) - ->writeBool(StateNames::BREWING_STAND_SLOT_B_BIT, $block->hasSlot(BrewingStandSlot::SOUTHWEST)) - ->writeBool(StateNames::BREWING_STAND_SLOT_C_BIT, $block->hasSlot(BrewingStandSlot::NORTHWEST)); - }); - $this->mapSlab(Blocks::BRICK_SLAB(), Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS); - $this->map(Blocks::BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::BRICK_WALL))); - $this->map(Blocks::BROWN_MUSHROOM_BLOCK(), fn(BrownMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::BROWN_MUSHROOM_BLOCK))); - $this->map(Blocks::CACTUS(), function(Cactus $block) : Writer{ - return Writer::create(Ids::CACTUS) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::CAKE(), function(Cake $block) : Writer{ - return Writer::create(Ids::CAKE) - ->writeInt(StateNames::BITE_COUNTER, $block->getBites()); - }); - $this->map(Blocks::CAMPFIRE(), function(Campfire $block) : Writer{ - return Writer::create(Ids::CAMPFIRE) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeBool(StateNames::EXTINGUISHED, !$block->isLit()); - }); - $this->map(Blocks::CARROTS(), fn(Carrot $block) => Helper::encodeCrops($block, new Writer(Ids::CARROTS))); - $this->map(Blocks::CARVED_PUMPKIN(), function(CarvedPumpkin $block) : Writer{ - return Writer::create(Ids::CARVED_PUMPKIN) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::CAVE_VINES(), function(CaveVines $block) : Writer{ - //I have no idea why this only has 3 IDs - there are 4 in Java and 4 visually distinct states in Bedrock - return Writer::create($block->hasBerries() ? - ($block->isHead() ? - Ids::CAVE_VINES_HEAD_WITH_BERRIES : - Ids::CAVE_VINES_BODY_WITH_BERRIES - ) : - Ids::CAVE_VINES - ) - ->writeInt(StateNames::GROWING_PLANT_AGE, $block->getAge()); - }); - $this->map(Blocks::CHAIN(), function(Chain $block) : Writer{ - return Writer::create(Ids::CHAIN) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::CHEST(), function(Chest $block) : Writer{ - return Writer::create(Ids::CHEST) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::CHISELED_BOOKSHELF(), function(ChiseledBookshelf $block) : Writer{ - $flags = 0; - foreach($block->getSlots() as $slot){ - $flags |= 1 << $slot->value; - } - return Writer::create(Ids::CHISELED_BOOKSHELF) - ->writeLegacyHorizontalFacing($block->getFacing()) - ->writeInt(StateNames::BOOKS_STORED, $flags); - }); - $this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::CHISELED_QUARTZ_BLOCK))); - $this->map(Blocks::CHORUS_FLOWER(), function(ChorusFlower $block) : Writer{ - return Writer::create(Ids::CHORUS_FLOWER) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->mapSlab(Blocks::COBBLED_DEEPSLATE_SLAB(), Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB); - $this->mapStairs(Blocks::COBBLED_DEEPSLATE_STAIRS(), Ids::COBBLED_DEEPSLATE_STAIRS); - $this->map(Blocks::COBBLED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::COBBLED_DEEPSLATE_WALL))); - $this->mapSlab(Blocks::COBBLESTONE_SLAB(), Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS); - $this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL))); - $this->map(Blocks::COCOA_POD(), function(CocoaBlock $block) : Writer{ - return Writer::create(Ids::COCOA) - ->writeInt(StateNames::AGE, $block->getAge()) - ->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing())); - }); - $this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::COMPOUND_CREATOR))); - $this->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB); - $this->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB); - $this->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB); - $this->mapStairs(Blocks::DARK_PRISMARINE_STAIRS(), Ids::DARK_PRISMARINE_STAIRS); - $this->map(Blocks::DAYLIGHT_SENSOR(), function(DaylightSensor $block) : Writer{ - return Writer::create($block->isInverted() ? Ids::DAYLIGHT_DETECTOR_INVERTED : Ids::DAYLIGHT_DETECTOR) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::DEEPSLATE(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::DEEPSLATE) - ->writePillarAxis($block->getAxis()); - }); - $this->mapSlab(Blocks::DEEPSLATE_BRICK_SLAB(), Ids::DEEPSLATE_BRICK_SLAB, Ids::DEEPSLATE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::DEEPSLATE_BRICK_STAIRS(), Ids::DEEPSLATE_BRICK_STAIRS); - $this->map(Blocks::DEEPSLATE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::DEEPSLATE_BRICK_WALL))); - $this->map(Blocks::DEEPSLATE_REDSTONE_ORE(), fn(RedstoneOre $block) => new Writer($block->isLit() ? Ids::LIT_DEEPSLATE_REDSTONE_ORE : Ids::DEEPSLATE_REDSTONE_ORE)); - $this->mapSlab(Blocks::DEEPSLATE_TILE_SLAB(), Ids::DEEPSLATE_TILE_SLAB, Ids::DEEPSLATE_TILE_DOUBLE_SLAB); - $this->mapStairs(Blocks::DEEPSLATE_TILE_STAIRS(), Ids::DEEPSLATE_TILE_STAIRS); - $this->map(Blocks::DEEPSLATE_TILE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::DEEPSLATE_TILE_WALL))); - $this->map(Blocks::DETECTOR_RAIL(), function(DetectorRail $block) : Writer{ - return Writer::create(Ids::DETECTOR_RAIL) - ->writeBool(StateNames::RAIL_DATA_BIT, $block->isActivated()) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->mapSlab(Blocks::DIORITE_SLAB(), Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS); - $this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::DIORITE_WALL))); - $this->map(Blocks::DIRT(), fn(Dirt $block) => BlockStateData::current(match($block->getDirtType()){ - DirtType::NORMAL => Ids::DIRT, - DirtType::COARSE => Ids::COARSE_DIRT, - DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, - }, [])); - $this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::TALL_GRASS))); - $this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::ELEMENT_CONSTRUCTOR))); - $this->map(Blocks::ENDER_CHEST(), function(EnderChest $block) : Writer{ - return Writer::create(Ids::ENDER_CHEST) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::END_PORTAL_FRAME(), function(EndPortalFrame $block) : Writer{ - return Writer::create(Ids::END_PORTAL_FRAME) - ->writeBool(StateNames::END_PORTAL_EYE_BIT, $block->hasEye()) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::END_ROD(), function(EndRod $block) : Writer{ - return Writer::create(Ids::END_ROD) - ->writeEndRodFacingDirection($block->getFacing()); - }); - $this->mapSlab(Blocks::END_STONE_BRICK_SLAB(), Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS); - $this->map(Blocks::END_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::END_STONE_BRICK_WALL))); - $this->mapSlab(Blocks::FAKE_WOODEN_SLAB(), Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB); - $this->map(Blocks::FARMLAND(), function(Farmland $block) : Writer{ - return Writer::create(Ids::FARMLAND) - ->writeInt(StateNames::MOISTURIZED_AMOUNT, $block->getWetness()); - }); - $this->map(Blocks::FIRE(), function(Fire $block) : Writer{ - return Writer::create(Ids::FIRE) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::FLOWER_POT(), Writer::create(Ids::FLOWER_POT) - ->writeBool(StateNames::UPDATE_BIT, false) //to keep MCPE happy - ); - $this->map(Blocks::FROGLIGHT(), function(Froglight $block){ - return Writer::create(match($block->getFroglightType()){ - FroglightType::OCHRE => Ids::OCHRE_FROGLIGHT, - FroglightType::PEARLESCENT => Ids::PEARLESCENT_FROGLIGHT, - FroglightType::VERDANT => Ids::VERDANT_FROGLIGHT, - }) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::FROSTED_ICE(), function(FrostedIce $block) : Writer{ - return Writer::create(Ids::FROSTED_ICE) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::FURNACE, Ids::LIT_FURNACE)); - $this->map(Blocks::GLOW_LICHEN(), function(GlowLichen $block) : Writer{ - return Writer::create(Ids::GLOW_LICHEN) - ->writeFacingFlags($block->getFaces()); - }); - $this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME)); - $this->mapSlab(Blocks::GRANITE_SLAB(), Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS); - $this->map(Blocks::GRANITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::GRANITE_WALL))); - $this->map(Blocks::GREEN_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_GREEN))); - $this->map(Blocks::HAY_BALE(), function(HayBale $block) : Writer{ - return Writer::create(Ids::HAY_BLOCK) - ->writeInt(StateNames::DEPRECATED, 0) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::HOPPER(), function(Hopper $block) : Writer{ - return Writer::create(Ids::HOPPER) - ->writeBool(StateNames::TOGGLE_BIT, $block->isPowered()) - ->writeFacingWithoutUp($block->getFacing()); - }); - $this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR))); - $this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR))); - $this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME)); - $this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::LAB_TABLE))); - $this->map(Blocks::LADDER(), function(Ladder $block) : Writer{ - return Writer::create(Ids::LADDER) - ->writeHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::LANTERN(), function(Lantern $block) : Writer{ - return Writer::create(Ids::LANTERN) - ->writeBool(StateNames::HANGING, $block->isHanging()); - }); - $this->map(Blocks::LARGE_FERN(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::LARGE_FERN))); - $this->map(Blocks::LAVA(), fn(Lava $block) => Helper::encodeLiquid($block, Ids::LAVA, Ids::FLOWING_LAVA)); - $this->map(Blocks::LECTERN(), function(Lectern $block) : Writer{ - return Writer::create(Ids::LECTERN) - ->writeBool(StateNames::POWERED_BIT, $block->isProducingSignal()) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::LEVER(), function(Lever $block) : Writer{ - return Writer::create(Ids::LEVER) - ->writeBool(StateNames::OPEN_BIT, $block->isActivated()) - ->writeString(StateNames::LEVER_DIRECTION, match($block->getFacing()){ - LeverFacing::DOWN_AXIS_Z => StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH, - LeverFacing::DOWN_AXIS_X => StringValues::LEVER_DIRECTION_DOWN_EAST_WEST, - LeverFacing::UP_AXIS_Z => StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH, - LeverFacing::UP_AXIS_X => StringValues::LEVER_DIRECTION_UP_EAST_WEST, - LeverFacing::NORTH => StringValues::LEVER_DIRECTION_NORTH, - LeverFacing::SOUTH => StringValues::LEVER_DIRECTION_SOUTH, - LeverFacing::WEST => StringValues::LEVER_DIRECTION_WEST, - LeverFacing::EAST => StringValues::LEVER_DIRECTION_EAST, - }); - }); - $this->map(Blocks::LIGHT(), fn(Light $block) => BlockStateData::current(match($block->getLightLevel()){ - 0 => Ids::LIGHT_BLOCK_0, - 1 => Ids::LIGHT_BLOCK_1, - 2 => Ids::LIGHT_BLOCK_2, - 3 => Ids::LIGHT_BLOCK_3, - 4 => Ids::LIGHT_BLOCK_4, - 5 => Ids::LIGHT_BLOCK_5, - 6 => Ids::LIGHT_BLOCK_6, - 7 => Ids::LIGHT_BLOCK_7, - 8 => Ids::LIGHT_BLOCK_8, - 9 => Ids::LIGHT_BLOCK_9, - 10 => Ids::LIGHT_BLOCK_10, - 11 => Ids::LIGHT_BLOCK_11, - 12 => Ids::LIGHT_BLOCK_12, - 13 => Ids::LIGHT_BLOCK_13, - 14 => Ids::LIGHT_BLOCK_14, - 15 => Ids::LIGHT_BLOCK_15, - default => throw new BlockStateSerializeException("Invalid light level " . $block->getLightLevel()), - }, [])); - $this->map(Blocks::LIGHTNING_ROD(), function(LightningRod $block) : Writer{ - return Writer::create(Ids::LIGHTNING_ROD) - ->writeFacingDirection($block->getFacing()); - }); - $this->map(Blocks::LILAC(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::LILAC))); - $this->map(Blocks::LIT_PUMPKIN(), function(LitPumpkin $block) : Writer{ - return Writer::create(Ids::LIT_PUMPKIN) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::LOOM(), function(Loom $block) : Writer{ - return Writer::create(Ids::LOOM) - ->writeLegacyHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::MATERIAL_REDUCER))); - $this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM))); - $this->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS); - $this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_COBBLESTONE_WALL))); - $this->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS); - $this->map(Blocks::MOSSY_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_STONE_BRICK_WALL))); - $this->mapSlab(Blocks::MUD_BRICK_SLAB(), Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::MUD_BRICK_STAIRS(), Ids::MUD_BRICK_STAIRS); - $this->map(Blocks::MUD_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::MUD_BRICK_WALL))); - $this->map(Blocks::MUDDY_MANGROVE_ROOTS(), fn(SimplePillar $block) => Writer::create(Ids::MUDDY_MANGROVE_ROOTS) - ->writePillarAxis($block->getAxis())); - $this->map(Blocks::MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); - $this->mapSlab(Blocks::NETHER_BRICK_SLAB(), Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); - $this->map(Blocks::NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::NETHER_BRICK_WALL))); - $this->map(Blocks::NETHER_PORTAL(), function(NetherPortal $block) : Writer{ - return Writer::create(Ids::PORTAL) - ->writeString(StateNames::PORTAL_AXIS, match($block->getAxis()){ - Axis::X => StringValues::PORTAL_AXIS_X, - Axis::Z => StringValues::PORTAL_AXIS_Z, - default => throw new BlockStateSerializeException("Invalid Nether Portal axis " . $block->getAxis()), - }); - }); - $this->map(Blocks::NETHER_WART(), function(NetherWartPlant $block) : Writer{ - return Writer::create(Ids::NETHER_WART) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::PEONY))); - $this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{ - return Writer::create(Ids::PINK_PETALS) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeInt(StateNames::GROWTH, $block->getCount() - 1); - }); - $this->map(Blocks::PITCHER_PLANT(), function(DoublePlant $block) : Writer{ - return Writer::create(Ids::PITCHER_PLANT) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $this->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge()) - ->writeBool(StateNames::UPPER_BLOCK_BIT, false); - }); - $this->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $this->mapSlab(Blocks::POLISHED_ANDESITE_SLAB(), Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS); - $this->map(Blocks::POLISHED_BASALT(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::POLISHED_BASALT) - ->writePillarAxis($block->getAxis()); - }); - $this->mapSlab(Blocks::POLISHED_BLACKSTONE_BRICK_SLAB(), Ids::POLISHED_BLACKSTONE_BRICK_SLAB, Ids::POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_BLACKSTONE_BRICK_STAIRS(), Ids::POLISHED_BLACKSTONE_BRICK_STAIRS); - $this->map(Blocks::POLISHED_BLACKSTONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_BLACKSTONE_BRICK_WALL))); - $this->map(Blocks::POLISHED_BLACKSTONE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::POLISHED_BLACKSTONE_BUTTON))); - $this->map(Blocks::POLISHED_BLACKSTONE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE))); - $this->mapSlab(Blocks::POLISHED_BLACKSTONE_SLAB(), Ids::POLISHED_BLACKSTONE_SLAB, Ids::POLISHED_BLACKSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_BLACKSTONE_STAIRS(), Ids::POLISHED_BLACKSTONE_STAIRS); - $this->map(Blocks::POLISHED_BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_BLACKSTONE_WALL))); - $this->mapSlab(Blocks::POLISHED_DEEPSLATE_SLAB(), Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_DEEPSLATE_STAIRS(), Ids::POLISHED_DEEPSLATE_STAIRS); - $this->map(Blocks::POLISHED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_DEEPSLATE_WALL))); - $this->mapSlab(Blocks::POLISHED_DIORITE_SLAB(), Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS); - $this->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS); - $this->mapSlab(Blocks::POLISHED_TUFF_SLAB(), Ids::POLISHED_TUFF_SLAB, Ids::POLISHED_TUFF_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_TUFF_STAIRS(), Ids::POLISHED_TUFF_STAIRS); - $this->map(Blocks::POLISHED_TUFF_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_TUFF_WALL))); - $this->map(Blocks::POTATOES(), fn(Potato $block) => Helper::encodeCrops($block, new Writer(Ids::POTATOES))); - $this->map(Blocks::POWERED_RAIL(), function(PoweredRail $block) : Writer{ - return Writer::create(Ids::GOLDEN_RAIL) - ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->mapSlab(Blocks::PRISMARINE_BRICKS_SLAB(), Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS); - $this->mapSlab(Blocks::PRISMARINE_SLAB(), Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB); - $this->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS); - $this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::PRISMARINE_WALL))); - $this->map(Blocks::PUMPKIN(), Writer::create(Ids::PUMPKIN) - ->writeCardinalHorizontalFacing(Facing::SOUTH) //no longer used - ); - $this->map(Blocks::PUMPKIN_STEM(), fn(PumpkinStem $block) => Helper::encodeStem($block, new Writer(Ids::PUMPKIN_STEM))); - $this->map(Blocks::PURPUR(), Writer::create(Ids::PURPUR_BLOCK)->writePillarAxis(Axis::Y)); - $this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_PURPLE))); - $this->map(Blocks::PURPUR_PILLAR(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::PURPUR_PILLAR) - ->writePillarAxis($block->getAxis()); - }); - $this->mapSlab(Blocks::PURPUR_SLAB(), Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB); - $this->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS); - $this->map(Blocks::QUARTZ(), Helper::encodeQuartz(Axis::Y, Writer::create(Ids::QUARTZ_BLOCK))); - $this->map(Blocks::QUARTZ_PILLAR(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::QUARTZ_PILLAR))); - $this->mapSlab(Blocks::QUARTZ_SLAB(), Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB); - $this->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS); - $this->map(Blocks::RAIL(), function(Rail $block) : Writer{ - return Writer::create(Ids::RAIL) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->map(Blocks::REDSTONE_COMPARATOR(), function(RedstoneComparator $block) : Writer{ - return Writer::create($block->isPowered() ? Ids::POWERED_COMPARATOR : Ids::UNPOWERED_COMPARATOR) - ->writeBool(StateNames::OUTPUT_LIT_BIT, $block->isPowered()) - ->writeBool(StateNames::OUTPUT_SUBTRACT_BIT, $block->isSubtractMode()) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::REDSTONE_LAMP(), fn(RedstoneLamp $block) => new Writer($block->isPowered() ? Ids::LIT_REDSTONE_LAMP : Ids::REDSTONE_LAMP)); - $this->map(Blocks::REDSTONE_ORE(), fn(RedstoneOre $block) => new Writer($block->isLit() ? Ids::LIT_REDSTONE_ORE : Ids::REDSTONE_ORE)); - $this->map(Blocks::REDSTONE_REPEATER(), function(RedstoneRepeater $block) : Writer{ - return Writer::create($block->isPowered() ? Ids::POWERED_REPEATER : Ids::UNPOWERED_REPEATER) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeInt(StateNames::REPEATER_DELAY, $block->getDelay() - 1); - }); - $this->map(Blocks::REDSTONE_TORCH(), function(RedstoneTorch $block) : Writer{ - return Writer::create($block->isLit() ? Ids::REDSTONE_TORCH : Ids::UNLIT_REDSTONE_TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::REDSTONE_WIRE(), function(RedstoneWire $block) : Writer{ - return Writer::create(Ids::REDSTONE_WIRE) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::RED_MUSHROOM_BLOCK(), fn(RedMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::RED_MUSHROOM_BLOCK))); - $this->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS); - $this->map(Blocks::RED_NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_NETHER_BRICK_WALL))); - $this->mapSlab(Blocks::RED_SANDSTONE_SLAB(), Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS); - $this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_SANDSTONE_WALL))); - $this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_RED))); - $this->mapSlab(Blocks::RESIN_BRICK_SLAB(), Ids::RESIN_BRICK_SLAB, Ids::RESIN_BRICK_DOUBLE_SLAB); - $this->map(Blocks::RESIN_BRICK_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::RESIN_BRICK_STAIRS))); - $this->map(Blocks::RESIN_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RESIN_BRICK_WALL))); - $this->map(Blocks::RESIN_CLUMP(), function(ResinClump $block) : Writer{ - return Writer::create(Ids::RESIN_CLUMP) - ->writeFacingFlags($block->getFaces()); - }); - $this->map(Blocks::RESPAWN_ANCHOR(), function(RespawnAnchor $block) : Writer{ - return Writer::create(Ids::RESPAWN_ANCHOR) - ->writeInt(StateNames::RESPAWN_ANCHOR_CHARGE, $block->getCharges()); - }); - $this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH))); - $this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); - $this->map(Blocks::SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::SANDSTONE_WALL))); - $this->map(Blocks::SEA_PICKLE(), function(SeaPickle $block) : Writer{ - return Writer::create(Ids::SEA_PICKLE) - ->writeBool(StateNames::DEAD_BIT, !$block->isUnderwater()) - ->writeInt(StateNames::CLUSTER_COUNT, $block->getCount() - 1); - }); - $this->map(Blocks::SMALL_DRIPLEAF(), function(SmallDripleaf $block) : Writer{ - return Writer::create(Ids::SMALL_DRIPLEAF_BLOCK) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $this->map(Blocks::SMOKER(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::SMOKER, Ids::LIT_SMOKER)); - $this->map(Blocks::SMOOTH_QUARTZ(), Helper::encodeQuartz(Axis::Y, Writer::create(Ids::SMOOTH_QUARTZ))); - $this->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB); - $this->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS); - $this->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SMOOTH_RED_SANDSTONE_STAIRS(), Ids::SMOOTH_RED_SANDSTONE_STAIRS); - $this->mapSlab(Blocks::SMOOTH_SANDSTONE_SLAB(), Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SMOOTH_SANDSTONE_STAIRS(), Ids::SMOOTH_SANDSTONE_STAIRS); - $this->mapSlab(Blocks::SMOOTH_STONE_SLAB(), Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB); - $this->map(Blocks::SNOW_LAYER(), function(SnowLayer $block) : Writer{ - return Writer::create(Ids::SNOW_LAYER) - ->writeBool(StateNames::COVERED_BIT, false) - ->writeInt(StateNames::HEIGHT, $block->getLayers() - 1); - }); - $this->map(Blocks::SOUL_CAMPFIRE(), function(SoulCampfire $block) : Writer{ - return Writer::create(Ids::SOUL_CAMPFIRE) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeBool(StateNames::EXTINGUISHED, !$block->isLit()); - }); - $this->map(Blocks::SOUL_FIRE(), Writer::create(Ids::SOUL_FIRE) - ->writeInt(StateNames::AGE, 0) //useless for soul fire, we don't track it - ); - $this->map(Blocks::SOUL_LANTERN(), function(Lantern $block) : Writer{ - return Writer::create(Ids::SOUL_LANTERN) - ->writeBool(StateNames::HANGING, $block->isHanging()); - }); - $this->map(Blocks::SOUL_TORCH(), function(Torch $block) : Writer{ - return Writer::create(Ids::SOUL_TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::SPONGE(), fn(Sponge $block) => Writer::create($block->isWet() ? Ids::WET_SPONGE : Ids::SPONGE)); - $this->map(Blocks::STONECUTTER(), fn(Stonecutter $block) => Writer::create(Ids::STONECUTTER_BLOCK) - ->writeCardinalHorizontalFacing($block->getFacing())); - $this->mapSlab(Blocks::STONE_BRICK_SLAB(), Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS); - $this->map(Blocks::STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::STONE_BRICK_WALL))); - $this->map(Blocks::STONE_BUTTON(), fn(StoneButton $block) => Helper::encodeButton($block, new Writer(Ids::STONE_BUTTON))); - $this->map(Blocks::STONE_PRESSURE_PLATE(), fn(StonePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::STONE_PRESSURE_PLATE))); - $this->mapSlab(Blocks::STONE_SLAB(), Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::STONE_STAIRS(), Ids::NORMAL_STONE_STAIRS); - $this->map(Blocks::SUGARCANE(), function(Sugarcane $block) : Writer{ - return Writer::create(Ids::REEDS) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::SUNFLOWER(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::SUNFLOWER))); - $this->map(Blocks::SWEET_BERRY_BUSH(), function(SweetBerryBush $block) : Writer{ - return Writer::create(Ids::SWEET_BERRY_BUSH) - ->writeInt(StateNames::GROWTH, $block->getAge()); - }); - $this->map(Blocks::TNT(), fn(TNT $block) => Writer::create($block->worksUnderwater() ? Ids::UNDERWATER_TNT : Ids::TNT) - ->writeBool(StateNames::EXPLODE_BIT, $block->isUnstable()) - ); - $this->map(Blocks::TORCH(), function(Torch $block) : Writer{ - return Writer::create(Ids::TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::TORCHFLOWER_CROP(), function(TorchflowerCrop $block){ - return Writer::create(Ids::TORCHFLOWER_CROP) - ->writeInt(StateNames::GROWTH, $block->isReady() ? 1 : 0); - }); - $this->map(Blocks::TRAPPED_CHEST(), function(TrappedChest $block) : Writer{ - return Writer::create(Ids::TRAPPED_CHEST) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::TRIPWIRE(), function(Tripwire $block) : Writer{ - return Writer::create(Ids::TRIP_WIRE) - ->writeBool(StateNames::ATTACHED_BIT, $block->isConnected()) - ->writeBool(StateNames::DISARMED_BIT, $block->isDisarmed()) - ->writeBool(StateNames::POWERED_BIT, $block->isTriggered()) - ->writeBool(StateNames::SUSPENDED_BIT, $block->isSuspended()); - }); - $this->map(Blocks::TRIPWIRE_HOOK(), function(TripwireHook $block) : Writer{ - return Writer::create(Ids::TRIPWIRE_HOOK) - ->writeBool(StateNames::ATTACHED_BIT, $block->isConnected()) - ->writeBool(StateNames::POWERED_BIT, $block->isPowered()) - ->writeLegacyHorizontalFacing($block->getFacing()); - }); - $this->mapSlab(Blocks::TUFF_BRICK_SLAB(), Ids::TUFF_BRICK_SLAB, Ids::TUFF_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::TUFF_BRICK_STAIRS(), Ids::TUFF_BRICK_STAIRS); - $this->map(Blocks::TUFF_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::TUFF_BRICK_WALL))); - $this->mapSlab(Blocks::TUFF_SLAB(), Ids::TUFF_SLAB, Ids::TUFF_DOUBLE_SLAB); - $this->mapStairs(Blocks::TUFF_STAIRS(), Ids::TUFF_STAIRS); - $this->map(Blocks::TUFF_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::TUFF_WALL))); - $this->map(Blocks::TWISTING_VINES(), function(NetherVines $block) : Writer{ - return Writer::create(Ids::TWISTING_VINES) - ->writeInt(StateNames::TWISTING_VINES_AGE, $block->getAge()); - }); - $this->map(Blocks::UNDERWATER_TORCH(), function(UnderwaterTorch $block) : Writer{ - return Writer::create(Ids::UNDERWATER_TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::VINES(), function(Vine $block) : Writer{ - return Writer::create(Ids::VINE) - ->writeInt(StateNames::VINE_DIRECTION_BITS, ($block->hasFace(Facing::NORTH) ? BlockLegacyMetadata::VINE_FLAG_NORTH : 0) | ($block->hasFace(Facing::SOUTH) ? BlockLegacyMetadata::VINE_FLAG_SOUTH : 0) | ($block->hasFace(Facing::WEST) ? BlockLegacyMetadata::VINE_FLAG_WEST : 0) | ($block->hasFace(Facing::EAST) ? BlockLegacyMetadata::VINE_FLAG_EAST : 0)); - }); - $this->map(Blocks::WALL_BANNER(), function(WallBanner $block) : Writer{ - return Writer::create(Ids::WALL_BANNER) - ->writeHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::WATER(), fn(Water $block) => Helper::encodeLiquid($block, Ids::WATER, Ids::FLOWING_WATER)); - $this->map(Blocks::WEEPING_VINES(), function(NetherVines $block) : Writer{ - return Writer::create(Ids::WEEPING_VINES) - ->writeInt(StateNames::WEEPING_VINES_AGE, $block->getAge()); - }); - $this->map(Blocks::WEIGHTED_PRESSURE_PLATE_HEAVY(), function(WeightedPressurePlateHeavy $block) : Writer{ - return Writer::create(Ids::HEAVY_WEIGHTED_PRESSURE_PLATE) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), function(WeightedPressurePlateLight $block) : Writer{ - return Writer::create(Ids::LIGHT_WEIGHTED_PRESSURE_PLATE) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::WHEAT(), fn(Wheat $block) => Helper::encodeCrops($block, new Writer(Ids::WHEAT))); - } } diff --git a/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php new file mode 100644 index 000000000..02491bae6 --- /dev/null +++ b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php @@ -0,0 +1,237 @@ +> $components + * + * @return string[][] + * @phpstan-return list> + */ + private static function compileFlattenedIdPartMatrix(array $components) : array{ + $result = []; + foreach($components as $component){ + $column = is_string($component) ? [$component] : $component->getPossibleValues(); + + if(count($result) === 0){ + $result = array_map(fn($value) => [$value], $column); + }else{ + $stepResult = []; + foreach($result as $parts){ + foreach($column as $value){ + $stepPart = $parts; + $stepPart[] = $value; + $stepResult[] = $stepPart; + } + } + + $result = $stepResult; + } + } + + return $result; + } + + /** + * @param string[]|StringProperty[] $idComponents + * + * @phpstan-template TBlock of Block + * + * @phpstan-param TBlock $block + * @phpstan-param list> $idComponents + */ + private static function serializeFlattenedId(Block $block, array $idComponents) : string{ + $id = ""; + foreach($idComponents as $infix){ + $id .= is_string($infix) ? $infix : $infix->serializePlain($block); + } + return $id; + } + + /** + * @param string[]|StringProperty[] $idComponents + * @param string[] $idPropertyValues + * + * @phpstan-template TBlock of Block + * + * @phpstan-param TBlock $baseBlock + * @phpstan-param list> $idComponents + * @phpstan-param list $idPropertyValues + * + * @phpstan-return TBlock + */ + private static function deserializeFlattenedId(Block $baseBlock, array $idComponents, array $idPropertyValues) : Block{ + $preparedBlock = clone $baseBlock; + foreach($idComponents as $k => $component){ + if($component instanceof StringProperty){ + $fakeValue = $idPropertyValues[$k]; + $component->deserializePlain($preparedBlock, $fakeValue); + } + } + + return $preparedBlock; + } + + public function mapSimple(Block $block, string $id) : void{ + $this->deserializer->mapSimple($id, fn() => clone $block); + $this->serializer->mapSimple($block, $id); + } + + /** + * @phpstan-template TBlock of Block + * @phpstan-param FlattenedIdModel $model + */ + public function mapFlattenedId(FlattenedIdModel $model) : void{ + $block = $model->getBlock(); + + $idComponents = $model->getIdComponents(); + if(count($idComponents) === 0){ + throw new \InvalidArgumentException("No ID components provided"); + } + $properties = $model->getProperties(); + + //This is a really cursed hack that lets us essentially write flattened properties as blockstate properties, and + //then pull them out to compile an ID :D + //This works surprisingly well and is much more elegant than I would've expected + + if(count($properties) > 0){ + $this->serializer->map($block, function(Block $block) use ($idComponents, $properties) : Writer{ + $id = self::serializeFlattenedId($block, $idComponents); + + $writer = new Writer($id); + foreach($properties as $property){ + $property->serialize($block, $writer); + } + + return $writer; + }); + }else{ + $this->serializer->map($block, function(Block $block) use ($idComponents) : BlockStateData{ + //fast path for blocks with no state properties + $id = self::serializeFlattenedId($block, $idComponents); + return BlockStateData::current($id, []); + }); + } + + $idPermutations = self::compileFlattenedIdPartMatrix($idComponents); + foreach($idPermutations as $idParts){ + //deconstruct the ID into a partial state + //we can do this at registration time since there will be multiple deserializers + $preparedBlock = self::deserializeFlattenedId($block, $idComponents, $idParts); + $id = implode("", $idParts); + + if(count($properties) > 0){ + $this->deserializer->map($id, function(Reader $reader) use ($preparedBlock, $properties) : Block{ + $block = clone $preparedBlock; + + foreach($properties as $property){ + $property->deserialize($block, $reader); + } + return $block; + }); + }else{ + //fast path for blocks with no state properties + $this->deserializer->map($id, fn() => clone $preparedBlock); + } + } + } + + /** + * @phpstan-template TBlock of Block&Colored + * @phpstan-param TBlock $block + */ + public function mapColored(Block $block, string $idPrefix, string $idSuffix) : void{ + $this->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([ + $idPrefix, + CommonProperties::getInstance()->dyeColorIdInfix, + $idSuffix + ]) + ); + } + + public function mapSlab(Slab $block, string $type) : void{ + $commonProperties = CommonProperties::getInstance(); + $this->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents(["minecraft:", $type, "_", $commonProperties->slabIdInfix, "slab"]) + ->properties([$commonProperties->slabPositionProperty]) + ); + } + + public function mapStairs(Stair $block, string $id) : void{ + $this->mapModel(Model::create($block, $id)->properties(CommonProperties::getInstance()->stairProperties)); + } + + /** + * @phpstan-template TBlock of Block + * @phpstan-param Model $model + */ + public function mapModel(Model $model) : void{ + $id = $model->getId(); + $block = $model->getBlock(); + $propertyDescriptors = $model->getProperties(); + + $this->deserializer->map($id, static function(Reader $in) use ($block, $propertyDescriptors) : Block{ + $newBlock = clone $block; + foreach($propertyDescriptors as $descriptor){ + $descriptor->deserialize($newBlock, $in); + } + return $newBlock; + }); + $this->serializer->map($block, static function(Block $block) use ($id, $propertyDescriptors) : Writer{ + $writer = new Writer($id); + foreach($propertyDescriptors as $descriptor){ + $descriptor->serialize($block, $writer); + } + return $writer; + }); + } +} diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 3cf55429e..1d48ec76f 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -56,11 +56,13 @@ use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\data\bedrock\MushroomBlockTypeIdMap; -use pocketmine\math\Axis; use pocketmine\math\Facing; -use pocketmine\utils\AssumptionFailedError; +/** + * @deprecated + */ final class BlockStateDeserializerHelper{ /** @throws BlockStateDeserializeException */ @@ -71,6 +73,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TCandle of Candle * @phpstan-param TCandle $block * @phpstan-return TCandle @@ -103,6 +106,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TBlock of CopperMaterial * * @phpstan-param TBlock $block @@ -115,6 +119,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TBlock of CopperMaterial * * @phpstan-param TBlock $block @@ -133,6 +138,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TDoor of Door * @phpstan-param TDoor $block * @phpstan-return TDoor @@ -155,7 +161,10 @@ final class BlockStateDeserializerHelper{ ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{ return $block ->setFacing($in->readCardinalHorizontalFacing()) @@ -163,17 +172,19 @@ final class BlockStateDeserializerHelper{ ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{ return $block - ->setAxis(match($in->readBoundedInt(BlockStateNames::CORAL_FAN_DIRECTION, 0, 1)){ - 0 => Axis::X, - 1 => Axis::Z, - default => throw new AssumptionFailedError("readBoundedInt() should have prevented this"), - }); + ->setAxis($in->mapIntFromInt(BlockStateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis)); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeFloorSign(FloorSign $block, BlockStateReader $in) : FloorSign{ return $block ->setRotation($in->readBoundedInt(BlockStateNames::GROUND_SIGN_DIRECTION, 0, 15)); @@ -186,7 +197,10 @@ final class BlockStateDeserializerHelper{ ->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT)); } - /** @throws BlockStateDeserializeException */ + /** + * @throws BlockStateDeserializeException + * @deprecated + */ public static function decodeLeaves(Leaves $block, BlockStateReader $in) : Leaves{ return $block ->setNoDecay($in->readBool(StateNames::PERSISTENT_BIT)) @@ -236,7 +250,10 @@ final class BlockStateDeserializerHelper{ ->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1); } - /** @throws BlockStateDeserializeException */ + /** + * @throws BlockStateDeserializeException + * @deprecated + */ public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{ return $block ->setReady($in->readBool(BlockStateNames::AGE_BIT)); @@ -273,6 +290,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TStair of Stair * @phpstan-param TStair $block * @phpstan-return TStair @@ -296,6 +314,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TTrapdoor of Trapdoor * @phpstan-param TTrapdoor $block * @phpstan-return TTrapdoor @@ -320,12 +339,19 @@ final class BlockStateDeserializerHelper{ return $block; } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{ return $block ->setFacing($in->readHorizontalFacing()); } + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeWeightedPressurePlate(WeightedPressurePlate $block, BlockStateReader $in) : WeightedPressurePlate{ return $block ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); diff --git a/src/data/bedrock/block/convert/BlockStateReader.php b/src/data/bedrock/block/convert/BlockStateReader.php index e3a02885f..4d09d2f85 100644 --- a/src/data/bedrock/block/convert/BlockStateReader.php +++ b/src/data/bedrock/block/convert/BlockStateReader.php @@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; +use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\nbt\tag\ByteTag; @@ -112,45 +115,45 @@ final class BlockStateReader{ } /** - * @param int[] $mapping - * @phpstan-param array $mapping - * @phpstan-return int + * @deprecated + * @phpstan-param IntFromRawStateMap $map * @throws BlockStateDeserializeException */ - private function parseFacingValue(int $value, array $mapping) : int{ - $result = $mapping[$value] ?? null; - if($result === null){ - throw new BlockStateDeserializeException("Unmapped facing value " . $value); - } - return $result; - } + public function mapIntFromString(string $name, IntFromRawStateMap $map) : int{ + $raw = $this->readString($name); - /** @throws BlockStateDeserializeException */ - public function readFacingDirection() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::FACING_DIRECTION), [ - 0 => Facing::DOWN, - 1 => Facing::UP, - 2 => Facing::NORTH, - 3 => Facing::SOUTH, - 4 => Facing::WEST, - 5 => Facing::EAST - ]); - } - - /** @throws BlockStateDeserializeException */ - public function readBlockFace() : int{ - return match($raw = $this->readString(BlockStateNames::MC_BLOCK_FACE)){ - StringValues::MC_BLOCK_FACE_DOWN => Facing::DOWN, - StringValues::MC_BLOCK_FACE_UP => Facing::UP, - StringValues::MC_BLOCK_FACE_NORTH => Facing::NORTH, - StringValues::MC_BLOCK_FACE_SOUTH => Facing::SOUTH, - StringValues::MC_BLOCK_FACE_WEST => Facing::WEST, - StringValues::MC_BLOCK_FACE_EAST => Facing::EAST, - default => throw $this->badValueException(BlockStateNames::MC_BLOCK_FACE, $raw) - }; + return $map->rawToValue($raw) ?? throw $this->badValueException($name, $raw); } /** + * @deprecated + * @phpstan-param IntFromRawStateMap $map + * @throws BlockStateDeserializeException + */ + public function mapIntFromInt(string $name, IntFromRawStateMap $map) : int{ + $raw = $this->readInt($name); + + return $map->rawToValue($raw) ?? throw $this->badValueException($name, (string) $raw); + } + + /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readFacingDirection() : int{ + return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing); + } + + /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readBlockFace() : int{ + return $this->mapIntFromString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace); + } + + /** + * @deprecated * @return int[] * @phpstan-return array */ @@ -173,82 +176,69 @@ final class BlockStateReader{ return $result; } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readEndRodFacingDirection() : int{ $result = $this->readFacingDirection(); return Facing::axis($result) !== Axis::Y ? Facing::opposite($result) : $result; } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::FACING_DIRECTION), [ - 0 => Facing::NORTH, //should be illegal, but 1.13 allows it - 1 => Facing::NORTH, //also should be illegal - 2 => Facing::NORTH, - 3 => Facing::SOUTH, - 4 => Facing::WEST, - 5 => Facing::EAST - ]); - } - - /** @throws BlockStateDeserializeException */ - public function readWeirdoHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::WEIRDO_DIRECTION), [ - 0 => Facing::EAST, - 1 => Facing::WEST, - 2 => Facing::SOUTH, - 3 => Facing::NORTH - ]); - } - - /** @throws BlockStateDeserializeException */ - public function readLegacyHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::DIRECTION), [ - 0 => Facing::SOUTH, - 1 => Facing::WEST, - 2 => Facing::NORTH, - 3 => Facing::EAST - ]); + return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic); } /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readWeirdoHorizontalFacing() : int{ + return $this->mapIntFromInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus); + } + + /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readLegacyHorizontalFacing() : int{ + return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE); + } + + /** + * @deprecated * This is for trapdoors, because Mojang botched the conversion in 1.13 * @throws BlockStateDeserializeException */ public function read5MinusHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::DIRECTION), [ - 0 => Facing::EAST, - 1 => Facing::WEST, - 2 => Facing::SOUTH, - 3 => Facing::NORTH - ]); + return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus); } /** + * @deprecated * Used by pumpkins as of 1.20.0.23 beta * @throws BlockStateDeserializeException */ public function readCardinalHorizontalFacing() : int{ - return match($raw = $this->readString(BlockStateNames::MC_CARDINAL_DIRECTION)){ - StringValues::MC_CARDINAL_DIRECTION_NORTH => Facing::NORTH, - StringValues::MC_CARDINAL_DIRECTION_SOUTH => Facing::SOUTH, - StringValues::MC_CARDINAL_DIRECTION_WEST => Facing::WEST, - StringValues::MC_CARDINAL_DIRECTION_EAST => Facing::EAST, - default => throw $this->badValueException(BlockStateNames::MC_CARDINAL_DIRECTION, $raw) - }; + return $this->mapIntFromString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readCoralFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::CORAL_DIRECTION), [ - 0 => Facing::WEST, - 1 => Facing::EAST, - 2 => Facing::NORTH, - 3 => Facing::SOUTH - ]); + return $this->mapIntFromInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readFacingWithoutDown() : int{ $result = $this->readFacingDirection(); if($result === Facing::DOWN){ //shouldn't be legal, but 1.13 allows it @@ -257,6 +247,10 @@ final class BlockStateReader{ return $result; } + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readFacingWithoutUp() : int{ $result = $this->readFacingDirection(); if($result === Facing::UP){ @@ -266,23 +260,17 @@ final class BlockStateReader{ } /** - * @phpstan-return Axis::* + * @deprecated * @throws BlockStateDeserializeException */ public function readPillarAxis() : int{ - $rawValue = $this->readString(BlockStateNames::PILLAR_AXIS); - $value = [ - StringValues::PILLAR_AXIS_X => Axis::X, - StringValues::PILLAR_AXIS_Y => Axis::Y, - StringValues::PILLAR_AXIS_Z => Axis::Z - ][$rawValue] ?? null; - if($value === null){ - throw $this->badValueException(BlockStateNames::PILLAR_AXIS, $rawValue, "Invalid axis value"); - } - return $value; + return $this->mapIntFromString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readSlabPosition() : SlabType{ return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){ StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM, @@ -292,34 +280,25 @@ final class BlockStateReader{ } /** - * @phpstan-return Facing::UP|Facing::NORTH|Facing::SOUTH|Facing::WEST|Facing::EAST + * @deprecated * @throws BlockStateDeserializeException */ public function readTorchFacing() : int{ - //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) - return match($rawValue = $this->readString(BlockStateNames::TORCH_FACING_DIRECTION)){ - StringValues::TORCH_FACING_DIRECTION_EAST => Facing::WEST, - StringValues::TORCH_FACING_DIRECTION_NORTH => Facing::SOUTH, - StringValues::TORCH_FACING_DIRECTION_SOUTH => Facing::NORTH, - StringValues::TORCH_FACING_DIRECTION_TOP => Facing::UP, - StringValues::TORCH_FACING_DIRECTION_UNKNOWN => Facing::UP, //should be illegal, but 1.13 allows it - StringValues::TORCH_FACING_DIRECTION_WEST => Facing::EAST, - default => throw $this->badValueException(BlockStateNames::TORCH_FACING_DIRECTION, $rawValue, "Invalid torch facing"), - }; + return $this->mapIntFromString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readBellAttachmentType() : BellAttachmentType{ - return match($type = $this->readString(BlockStateNames::ATTACHMENT)){ - StringValues::ATTACHMENT_HANGING => BellAttachmentType::CEILING, - StringValues::ATTACHMENT_STANDING => BellAttachmentType::FLOOR, - StringValues::ATTACHMENT_SIDE => BellAttachmentType::ONE_WALL, - StringValues::ATTACHMENT_MULTIPLE => BellAttachmentType::TWO_WALLS, - default => throw $this->badValueException(BlockStateNames::ATTACHMENT, $type), - }; + return $this->readUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readWallConnectionType(string $name) : ?WallConnectionType{ return match($type = $this->readString($name)){ //TODO: this looks a bit confusing due to use of EAST, but the values are the same for all connections @@ -332,6 +311,23 @@ final class BlockStateReader{ }; } + /** + * @deprecated + * @phpstan-template TEnum of \UnitEnum + * @phpstan-param EnumFromRawStateMap $map + * @phpstan-return TEnum + * @throws BlockStateDeserializeException + */ + public function readUnitEnum(string $name, EnumFromRawStateMap $map) : \UnitEnum{ + $value = $this->readString($name); + + $mapped = $map->rawToValue($value); + if($mapped === null){ + throw $this->badValueException($name, $value); + } + return $mapped; + } + /** * Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled */ diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php index a25044153..da3dbb387 100644 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php @@ -55,6 +55,9 @@ use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer; use pocketmine\data\bedrock\MushroomBlockTypeIdMap; use pocketmine\math\Facing; +/** + * @deprecated + */ final class BlockStateSerializerHelper{ public static function encodeButton(Button $block, Writer $out) : Writer{ return $out @@ -77,6 +80,9 @@ final class BlockStateSerializerHelper{ return $out->writeInt(BlockStateNames::GROWTH, $block->getAge()); } + /** + * @deprecated + */ public static function encodeTorch(Torch $block, Writer $out) : Writer{ return $out ->writeTorchFacing($block->getFacing()); @@ -97,6 +103,9 @@ final class BlockStateSerializerHelper{ }; } + /** + * @deprecated + */ public static function encodeDoor(Door $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()) @@ -111,6 +120,9 @@ final class BlockStateSerializerHelper{ ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()); } + /** + * @deprecated + */ public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{ return $out ->writeCardinalHorizontalFacing($block->getFacing()) @@ -118,6 +130,9 @@ final class BlockStateSerializerHelper{ ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); } + /** + * @deprecated + */ public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{ return $out ->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation()); @@ -135,6 +150,9 @@ final class BlockStateSerializerHelper{ ->writeFacingDirection($block->getFacing()); } + /** + * @deprecated + */ public static function encodeLeaves(Leaves $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay()) @@ -159,11 +177,17 @@ final class BlockStateSerializerHelper{ ->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType())); } + /** + * @deprecated + */ public static function encodeQuartz(int $axis, Writer $out) : Writer{ return $out ->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway } + /** + * @deprecated + */ public static function encodeSapling(Sapling $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::AGE_BIT, $block->isReady()); @@ -193,6 +217,9 @@ final class BlockStateSerializerHelper{ self::encodeSingleSlab($block, $singleId); } + /** + * @deprecated + */ public static function encodeStairs(Stair $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown()) @@ -208,6 +235,9 @@ final class BlockStateSerializerHelper{ ->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing); } + /** + * @deprecated + */ public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{ return $out ->write5MinusHorizontalFacing($block->getFacing()) @@ -224,6 +254,9 @@ final class BlockStateSerializerHelper{ ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST)); } + /** + * @deprecated + */ public static function encodeWallSign(WallSign $block, Writer $out) : Writer{ return $out ->writeHorizontalFacing($block->getFacing()); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 1e9a4041f..ca5c12412 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -23,50 +23,18 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\convert; -use pocketmine\block\AmethystCluster; -use pocketmine\block\Anvil; -use pocketmine\block\Bamboo; use pocketmine\block\Block; -use pocketmine\block\CakeWithDyedCandle; -use pocketmine\block\CaveVines; -use pocketmine\block\ChorusFlower; -use pocketmine\block\DoublePitcherCrop; -use pocketmine\block\Opaque; -use pocketmine\block\PinkPetals; -use pocketmine\block\PitcherCrop; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\Slab; use pocketmine\block\Stair; -use pocketmine\block\SweetBerryBush; -use pocketmine\block\utils\BrewingStandSlot; -use pocketmine\block\utils\ChiseledBookshelfSlot; -use pocketmine\block\utils\Colored; -use pocketmine\block\utils\CopperMaterial; -use pocketmine\block\utils\CopperOxidation; -use pocketmine\block\utils\CoralType; -use pocketmine\block\utils\DirtType; -use pocketmine\block\utils\DripleafState; -use pocketmine\block\utils\DyeColor; -use pocketmine\block\utils\FroglightType; -use pocketmine\block\utils\LeverFacing; -use pocketmine\block\utils\MobHeadType; -use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Wood; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializer; -use pocketmine\data\bedrock\block\BlockStateNames as StateNames; -use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; -use pocketmine\data\bedrock\block\BlockTypeNames as Ids; use pocketmine\data\bedrock\block\convert\BlockStateDeserializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader; -use pocketmine\math\Axis; -use pocketmine\math\Facing; -use pocketmine\utils\Utils; use function array_key_exists; use function count; -use function min; final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ @@ -82,21 +50,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ */ private array $simpleCache = []; - public function __construct(){ - $this->registerCandleDeserializers(); - $this->registerFlatColorBlockDeserializers(); - $this->registerFlatCoralDeserializers(); - $this->registerCauldronDeserializers(); - $this->registerFlatWoodBlockDeserializers(); - $this->registerLeavesDeserializers(); - $this->registerSaplingDeserializers(); - $this->registerLightDeserializers(); - $this->registerMobHeadDeserializers(); - $this->registerCopperDeserializers(); - $this->registerSimpleDeserializers(); - $this->registerDeserializers(); - } - public function deserialize(BlockStateData $stateData) : int{ if(count($stateData->getStates()) === 0){ //if a block has zero properties, we can keep a map of string ID -> internal blockstate ID @@ -133,12 +86,16 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return $this->deserializeFuncs[$id] ?? null; } - /** @phpstan-param \Closure() : Block $getBlock */ + /** + * @deprecated + * @phpstan-param \Closure() : Block $getBlock + */ public function mapSimple(string $id, \Closure $getBlock) : void{ $this->map($id, $getBlock); } /** + * @deprecated * @phpstan-param \Closure(Reader) : Slab $getBlock */ public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock) : void{ @@ -147,1552 +104,22 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ } /** + * @deprecated * @phpstan-param \Closure() : Stair $getBlock */ public function mapStairs(string $id, \Closure $getBlock) : void{ $this->map($id, fn(Reader $in) : Stair => Helper::decodeStairs($getBlock(), $in)); } - /** @phpstan-param \Closure() : Wood $getBlock */ + /** + * @deprecated + * @phpstan-param \Closure() : Wood $getBlock + */ public function mapLog(string $unstrippedId, string $strippedId, \Closure $getBlock) : void{ $this->map($unstrippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), false, $in)); $this->map($strippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), true, $in)); } - /** - * @phpstan-template TBlock of Block - * @phpstan-template TEnum of \UnitEnum - * - * @phpstan-param StringEnumMap $mapProperty - * @phpstan-param \Closure(TEnum) : TBlock $getBlock - * @phpstan-param ?\Closure(TBlock, Reader) : TBlock $extra - */ - public function mapFlattenedEnum( - StringEnumMap $mapProperty, - string $prefix, - string $suffix, - \Closure $getBlock, - ?\Closure $extra = null - ) : void{ - foreach(Utils::stringifyKeys($mapProperty->getValueToEnum()) as $infix => $enumCase){ - $id = $prefix . $infix . $suffix; - if($extra === null){ - $this->map($id, fn() => $getBlock($enumCase)); - }else{ - $this->map($id, function(Reader $in) use ($enumCase, $getBlock, $extra) : Block{ - $block = $getBlock($enumCase); - $extra($block, $in); - return $block; - }); - } - } - } - - /** - * @phpstan-template TBlock of Block&Colored - * @phpstan-param \Closure() : TBlock $getBlock - * @phpstan-param ?\Closure(TBlock, Reader) : TBlock $extra - */ - public function mapColored(string $prefix, string $suffix, \Closure $getBlock, ?\Closure $extra = null) : void{ - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - $prefix, - $suffix, - fn(DyeColor $color) => $getBlock()->setColor($color), - $extra - ); - } - - private function registerCandleDeserializers() : void{ - $this->map(Ids::CANDLE, fn(Reader $in) => Helper::decodeCandle(Blocks::CANDLE(), $in)); - $this->mapColored( - "minecraft:", - "_candle", - fn() => Blocks::DYED_CANDLE(), - Helper::decodeCandle(...) - ); - - $this->map(Ids::CANDLE_CAKE, fn(Reader $in) => Blocks::CAKE_WITH_CANDLE()->setLit($in->readBool(StateNames::LIT))); - - $this->mapColored( - "minecraft:", - "_candle_cake", - fn() => Blocks::CAKE_WITH_DYED_CANDLE(), - fn(CakeWithDyedCandle $block, Reader $in) => $block->setLit($in->readBool(StateNames::LIT)) - ); - } - - private function registerFlatColorBlockDeserializers() : void{ - $this->mapColored("minecraft:hard_", "_stained_glass", fn() => Blocks::STAINED_HARDENED_GLASS()); - $this->mapColored("minecraft:hard_", "_stained_glass_pane", fn() => Blocks::STAINED_HARDENED_GLASS_PANE()); - - $this->mapColored("minecraft:", "_carpet", fn() => Blocks::CARPET()); - $this->mapColored("minecraft:", "_concrete", fn() => Blocks::CONCRETE()); - $this->mapColored("minecraft:", "_concrete_powder", fn() => Blocks::CONCRETE_POWDER()); - $this->mapColored("minecraft:", "_shulker_box", fn() => Blocks::DYED_SHULKER_BOX()); - $this->mapColored("minecraft:", "_stained_glass", fn() => Blocks::STAINED_GLASS()); - $this->mapColored("minecraft:", "_stained_glass_pane", fn() => Blocks::STAINED_GLASS_PANE()); - $this->mapColored("minecraft:", "_terracotta", fn() => Blocks::STAINED_CLAY()); - $this->mapColored("minecraft:", "_wool", fn() => Blocks::WOOL()); - - foreach([ - Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK, - Ids::BLUE_GLAZED_TERRACOTTA => DyeColor::BLUE, - Ids::BROWN_GLAZED_TERRACOTTA => DyeColor::BROWN, - Ids::CYAN_GLAZED_TERRACOTTA => DyeColor::CYAN, - Ids::GRAY_GLAZED_TERRACOTTA => DyeColor::GRAY, - Ids::GREEN_GLAZED_TERRACOTTA => DyeColor::GREEN, - Ids::LIGHT_BLUE_GLAZED_TERRACOTTA => DyeColor::LIGHT_BLUE, - Ids::SILVER_GLAZED_TERRACOTTA => DyeColor::LIGHT_GRAY, //minecraft sadness - Ids::LIME_GLAZED_TERRACOTTA => DyeColor::LIME, - Ids::MAGENTA_GLAZED_TERRACOTTA => DyeColor::MAGENTA, - Ids::ORANGE_GLAZED_TERRACOTTA => DyeColor::ORANGE, - Ids::PINK_GLAZED_TERRACOTTA => DyeColor::PINK, - Ids::PURPLE_GLAZED_TERRACOTTA => DyeColor::PURPLE, - Ids::RED_GLAZED_TERRACOTTA => DyeColor::RED, - Ids::WHITE_GLAZED_TERRACOTTA => DyeColor::WHITE, - Ids::YELLOW_GLAZED_TERRACOTTA => DyeColor::YELLOW, - ] as $id => $color){ - $this->map($id, fn(Reader $in) => Blocks::GLAZED_TERRACOTTA() - ->setColor($color) - ->setFacing($in->readHorizontalFacing()) - ); - } - } - - private function registerFlatCoralDeserializers() : void{ - foreach([ - Ids::BRAIN_CORAL => CoralType::BRAIN, - Ids::BUBBLE_CORAL => CoralType::BUBBLE, - Ids::FIRE_CORAL => CoralType::FIRE, - Ids::HORN_CORAL => CoralType::HORN, - Ids::TUBE_CORAL => CoralType::TUBE, - ] as $id => $coralType){ - $this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(false)); - } - foreach([ - Ids::DEAD_BRAIN_CORAL => CoralType::BRAIN, - Ids::DEAD_BUBBLE_CORAL => CoralType::BUBBLE, - Ids::DEAD_FIRE_CORAL => CoralType::FIRE, - Ids::DEAD_HORN_CORAL => CoralType::HORN, - Ids::DEAD_TUBE_CORAL => CoralType::TUBE, - ] as $id => $coralType){ - $this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(true)); - } - - foreach([ - [CoralType::BRAIN, Ids::BRAIN_CORAL_FAN, Ids::DEAD_BRAIN_CORAL_FAN], - [CoralType::BUBBLE, Ids::BUBBLE_CORAL_FAN, Ids::DEAD_BUBBLE_CORAL_FAN], - [CoralType::FIRE, Ids::FIRE_CORAL_FAN, Ids::DEAD_FIRE_CORAL_FAN], - [CoralType::HORN, Ids::HORN_CORAL_FAN, Ids::DEAD_HORN_CORAL_FAN], - [CoralType::TUBE, Ids::TUBE_CORAL_FAN, Ids::DEAD_TUBE_CORAL_FAN], - ] as [$coralType, $aliveId, $deadId]){ - $this->map($aliveId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(false), $in)); - $this->map($deadId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(true), $in)); - } - - foreach([ - [CoralType::BRAIN, Ids::BRAIN_CORAL_BLOCK, Ids::DEAD_BRAIN_CORAL_BLOCK], - [CoralType::BUBBLE, Ids::BUBBLE_CORAL_BLOCK, Ids::DEAD_BUBBLE_CORAL_BLOCK], - [CoralType::FIRE, Ids::FIRE_CORAL_BLOCK, Ids::DEAD_FIRE_CORAL_BLOCK], - [CoralType::HORN, Ids::HORN_CORAL_BLOCK, Ids::DEAD_HORN_CORAL_BLOCK], - [CoralType::TUBE, Ids::TUBE_CORAL_BLOCK, Ids::DEAD_TUBE_CORAL_BLOCK], - ] as [$coralType, $aliveId, $deadId]){ - $this->map($aliveId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(false)); - $this->map($deadId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(true)); - } - - foreach([ - [CoralType::BRAIN, Ids::BRAIN_CORAL_WALL_FAN, Ids::DEAD_BRAIN_CORAL_WALL_FAN], - [CoralType::BUBBLE, Ids::BUBBLE_CORAL_WALL_FAN, Ids::DEAD_BUBBLE_CORAL_WALL_FAN], - [CoralType::FIRE, Ids::FIRE_CORAL_WALL_FAN, Ids::DEAD_FIRE_CORAL_WALL_FAN], - [CoralType::HORN, Ids::HORN_CORAL_WALL_FAN, Ids::DEAD_HORN_CORAL_WALL_FAN], - [CoralType::TUBE, Ids::TUBE_CORAL_WALL_FAN, Ids::DEAD_TUBE_CORAL_WALL_FAN], - ] as [$coralType, $aliveId, $deadId]){ - $this->map($aliveId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(false)); - $this->map($deadId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(true)); - } - } - - private function registerCauldronDeserializers() : void{ - $deserializer = function(Reader $in) : Block{ - $level = $in->readBoundedInt(StateNames::FILL_LEVEL, 0, 6); - if($level === 0){ - $in->ignored(StateNames::CAULDRON_LIQUID); - return Blocks::CAULDRON(); - } - - return (match($liquid = $in->readString(StateNames::CAULDRON_LIQUID)){ - StringValues::CAULDRON_LIQUID_WATER => Blocks::WATER_CAULDRON(), - StringValues::CAULDRON_LIQUID_LAVA => Blocks::LAVA_CAULDRON(), - StringValues::CAULDRON_LIQUID_POWDER_SNOW => throw new UnsupportedBlockStateException("Powder snow is not supported yet"), - default => throw $in->badValueException(StateNames::CAULDRON_LIQUID, $liquid) - })->setFillLevel($level); - }; - $this->map(Ids::CAULDRON, $deserializer); - } - - private function registerFlatWoodBlockDeserializers() : void{ - $this->map(Ids::ACACIA_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::ACACIA_BUTTON(), $in)); - $this->map(Ids::ACACIA_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::ACACIA_DOOR(), $in)); - $this->map(Ids::ACACIA_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::ACACIA_FENCE_GATE(), $in)); - $this->map(Ids::ACACIA_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::ACACIA_PRESSURE_PLATE(), $in)); - $this->map(Ids::ACACIA_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::ACACIA_SIGN(), $in)); - $this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in)); - $this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in)); - $this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG()); - $this->mapLog(Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD, fn() => Blocks::ACACIA_WOOD()); - $this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE()); - $this->mapSimple(Ids::ACACIA_PLANKS, fn() => Blocks::ACACIA_PLANKS()); - $this->mapSlab(Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB, fn() => Blocks::ACACIA_SLAB()); - $this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS()); - - $this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in)); - $this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in)); - $this->map(Ids::BIRCH_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::BIRCH_FENCE_GATE(), $in)); - $this->map(Ids::BIRCH_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::BIRCH_PRESSURE_PLATE(), $in)); - $this->map(Ids::BIRCH_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::BIRCH_SIGN(), $in)); - $this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in)); - $this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in)); - $this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG()); - $this->mapLog(Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD, fn() => Blocks::BIRCH_WOOD()); - $this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE()); - $this->mapSimple(Ids::BIRCH_PLANKS, fn() => Blocks::BIRCH_PLANKS()); - $this->mapSlab(Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB, fn() => Blocks::BIRCH_SLAB()); - $this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS()); - - $this->map(Ids::CHERRY_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CHERRY_BUTTON(), $in)); - $this->map(Ids::CHERRY_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CHERRY_DOOR(), $in)); - $this->map(Ids::CHERRY_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CHERRY_FENCE_GATE(), $in)); - $this->map(Ids::CHERRY_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CHERRY_PRESSURE_PLATE(), $in)); - $this->map(Ids::CHERRY_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CHERRY_SIGN(), $in)); - $this->map(Ids::CHERRY_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CHERRY_TRAPDOOR(), $in)); - $this->map(Ids::CHERRY_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CHERRY_WALL_SIGN(), $in)); - $this->mapLog(Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG, fn() => Blocks::CHERRY_LOG()); - $this->mapSimple(Ids::CHERRY_FENCE, fn() => Blocks::CHERRY_FENCE()); - $this->mapSimple(Ids::CHERRY_PLANKS, fn() => Blocks::CHERRY_PLANKS()); - $this->mapSlab(Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB, fn() => Blocks::CHERRY_SLAB()); - $this->mapStairs(Ids::CHERRY_STAIRS, fn() => Blocks::CHERRY_STAIRS()); - $this->map(Ids::CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in)); - $this->map(Ids::STRIPPED_CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), true, $in)); - - $this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in)); - $this->map(Ids::CRIMSON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CRIMSON_DOOR(), $in)); - $this->map(Ids::CRIMSON_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CRIMSON_FENCE_GATE(), $in)); - $this->map(Ids::CRIMSON_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CRIMSON_PRESSURE_PLATE(), $in)); - $this->map(Ids::CRIMSON_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CRIMSON_SIGN(), $in)); - $this->map(Ids::CRIMSON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CRIMSON_TRAPDOOR(), $in)); - $this->map(Ids::CRIMSON_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CRIMSON_WALL_SIGN(), $in)); - $this->mapLog(Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE, fn() => Blocks::CRIMSON_HYPHAE()); - $this->mapLog(Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM, fn() => Blocks::CRIMSON_STEM()); - $this->mapSimple(Ids::CRIMSON_FENCE, fn() => Blocks::CRIMSON_FENCE()); - $this->mapSimple(Ids::CRIMSON_PLANKS, fn() => Blocks::CRIMSON_PLANKS()); - $this->mapSlab(Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB, fn() => Blocks::CRIMSON_SLAB()); - $this->mapStairs(Ids::CRIMSON_STAIRS, fn() => Blocks::CRIMSON_STAIRS()); - - $this->map(Ids::DARKOAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::DARK_OAK_SIGN(), $in)); - $this->map(Ids::DARKOAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::DARK_OAK_WALL_SIGN(), $in)); - $this->map(Ids::DARK_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::DARK_OAK_BUTTON(), $in)); - $this->map(Ids::DARK_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::DARK_OAK_DOOR(), $in)); - $this->map(Ids::DARK_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::DARK_OAK_FENCE_GATE(), $in)); - $this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in)); - $this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in)); - $this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG()); - $this->mapLog(Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD, fn() => Blocks::DARK_OAK_WOOD()); - $this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE()); - $this->mapSimple(Ids::DARK_OAK_PLANKS, fn() => Blocks::DARK_OAK_PLANKS()); - $this->mapSlab(Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB, fn() => Blocks::DARK_OAK_SLAB()); - $this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS()); - - $this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in)); - $this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in)); - $this->map(Ids::JUNGLE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::JUNGLE_FENCE_GATE(), $in)); - $this->map(Ids::JUNGLE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::JUNGLE_PRESSURE_PLATE(), $in)); - $this->map(Ids::JUNGLE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::JUNGLE_SIGN(), $in)); - $this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in)); - $this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in)); - $this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG()); - $this->mapLog(Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD, fn() => Blocks::JUNGLE_WOOD()); - $this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE()); - $this->mapSimple(Ids::JUNGLE_PLANKS, fn() => Blocks::JUNGLE_PLANKS()); - $this->mapSlab(Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB, fn() => Blocks::JUNGLE_SLAB()); - $this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS()); - - $this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in)); - $this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in)); - $this->map(Ids::MANGROVE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::MANGROVE_FENCE_GATE(), $in)); - $this->map(Ids::MANGROVE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::MANGROVE_PRESSURE_PLATE(), $in)); - $this->map(Ids::MANGROVE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::MANGROVE_SIGN(), $in)); - $this->map(Ids::MANGROVE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::MANGROVE_TRAPDOOR(), $in)); - $this->map(Ids::MANGROVE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::MANGROVE_WALL_SIGN(), $in)); - $this->mapLog(Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG, fn() => Blocks::MANGROVE_LOG()); - $this->mapSimple(Ids::MANGROVE_FENCE, fn() => Blocks::MANGROVE_FENCE()); - $this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS()); - $this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB()); - $this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS()); - $this->map(Ids::MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in)); - $this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in)); - - //oak - due to age, many of these don't specify "oak", making for confusing reading - $this->map(Ids::WOODEN_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::OAK_BUTTON(), $in)); - $this->map(Ids::WOODEN_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::OAK_DOOR(), $in)); - $this->map(Ids::FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::OAK_FENCE_GATE(), $in)); - $this->map(Ids::WOODEN_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::OAK_PRESSURE_PLATE(), $in)); - $this->map(Ids::STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::OAK_SIGN(), $in)); - $this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in)); - $this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in)); - $this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG()); - $this->mapLog(Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD, fn() => Blocks::OAK_WOOD()); - $this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE()); - $this->mapSimple(Ids::OAK_PLANKS, fn() => Blocks::OAK_PLANKS()); - $this->mapSlab(Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB, fn() => Blocks::OAK_SLAB()); - $this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS()); - - $this->map(Ids::PALE_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::PALE_OAK_BUTTON(), $in)); - $this->map(Ids::PALE_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::PALE_OAK_DOOR(), $in)); - $this->map(Ids::PALE_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::PALE_OAK_FENCE_GATE(), $in)); - $this->map(Ids::PALE_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::PALE_OAK_PRESSURE_PLATE(), $in)); - $this->map(Ids::PALE_OAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::PALE_OAK_SIGN(), $in)); - $this->map(Ids::PALE_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::PALE_OAK_TRAPDOOR(), $in)); - $this->map(Ids::PALE_OAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::PALE_OAK_WALL_SIGN(), $in)); - $this->mapLog(Ids::PALE_OAK_LOG, Ids::STRIPPED_PALE_OAK_LOG, fn() => Blocks::PALE_OAK_LOG()); - $this->mapLog(Ids::PALE_OAK_WOOD, Ids::STRIPPED_PALE_OAK_WOOD, fn() => Blocks::PALE_OAK_WOOD()); - $this->mapSimple(Ids::PALE_OAK_FENCE, fn() => Blocks::PALE_OAK_FENCE()); - $this->mapSimple(Ids::PALE_OAK_PLANKS, fn() => Blocks::PALE_OAK_PLANKS()); - $this->mapSlab(Ids::PALE_OAK_SLAB, Ids::PALE_OAK_DOUBLE_SLAB, fn() => Blocks::PALE_OAK_SLAB()); - $this->mapStairs(Ids::PALE_OAK_STAIRS, fn() => Blocks::PALE_OAK_STAIRS()); - - $this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in)); - $this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in)); - $this->map(Ids::SPRUCE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::SPRUCE_FENCE_GATE(), $in)); - $this->map(Ids::SPRUCE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::SPRUCE_PRESSURE_PLATE(), $in)); - $this->map(Ids::SPRUCE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::SPRUCE_SIGN(), $in)); - $this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in)); - $this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in)); - $this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG()); - $this->mapLog(Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD, fn() => Blocks::SPRUCE_WOOD()); - $this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE()); - $this->mapSimple(Ids::SPRUCE_PLANKS, fn() => Blocks::SPRUCE_PLANKS()); - $this->mapSlab(Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB, fn() => Blocks::SPRUCE_SLAB()); - $this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS()); - - $this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in)); - $this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in)); - $this->map(Ids::WARPED_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::WARPED_FENCE_GATE(), $in)); - $this->map(Ids::WARPED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::WARPED_PRESSURE_PLATE(), $in)); - $this->map(Ids::WARPED_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::WARPED_SIGN(), $in)); - $this->map(Ids::WARPED_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::WARPED_TRAPDOOR(), $in)); - $this->map(Ids::WARPED_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::WARPED_WALL_SIGN(), $in)); - $this->mapLog(Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE, fn() => Blocks::WARPED_HYPHAE()); - $this->mapLog(Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM, fn() => Blocks::WARPED_STEM()); - $this->mapSimple(Ids::WARPED_FENCE, fn() => Blocks::WARPED_FENCE()); - $this->mapSimple(Ids::WARPED_PLANKS, fn() => Blocks::WARPED_PLANKS()); - $this->mapSlab(Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB, fn() => Blocks::WARPED_SLAB()); - $this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS()); - } - - private function registerLeavesDeserializers() : void{ - $this->map(Ids::ACACIA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::ACACIA_LEAVES(), $in)); - $this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in)); - $this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in)); - $this->map(Ids::BIRCH_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::BIRCH_LEAVES(), $in)); - $this->map(Ids::CHERRY_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::CHERRY_LEAVES(), $in)); - $this->map(Ids::DARK_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::DARK_OAK_LEAVES(), $in)); - $this->map(Ids::JUNGLE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::JUNGLE_LEAVES(), $in)); - $this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in)); - $this->map(Ids::OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::OAK_LEAVES(), $in)); - $this->map(Ids::PALE_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::PALE_OAK_LEAVES(), $in)); - $this->map(Ids::SPRUCE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::SPRUCE_LEAVES(), $in)); - } - - private function registerSaplingDeserializers() : void{ - foreach([ - Ids::ACACIA_SAPLING => fn() => Blocks::ACACIA_SAPLING(), - Ids::BIRCH_SAPLING => fn() => Blocks::BIRCH_SAPLING(), - Ids::DARK_OAK_SAPLING => fn() => Blocks::DARK_OAK_SAPLING(), - Ids::JUNGLE_SAPLING => fn() => Blocks::JUNGLE_SAPLING(), - Ids::OAK_SAPLING => fn() => Blocks::OAK_SAPLING(), - Ids::SPRUCE_SAPLING => fn() => Blocks::SPRUCE_SAPLING(), - ] as $id => $getBlock){ - $this->map($id, fn(Reader $in) => Helper::decodeSapling($getBlock(), $in)); - } - } - - private function registerLightDeserializers() : void{ - foreach([ - Ids::LIGHT_BLOCK_0 => 0, - Ids::LIGHT_BLOCK_1 => 1, - Ids::LIGHT_BLOCK_2 => 2, - Ids::LIGHT_BLOCK_3 => 3, - Ids::LIGHT_BLOCK_4 => 4, - Ids::LIGHT_BLOCK_5 => 5, - Ids::LIGHT_BLOCK_6 => 6, - Ids::LIGHT_BLOCK_7 => 7, - Ids::LIGHT_BLOCK_8 => 8, - Ids::LIGHT_BLOCK_9 => 9, - Ids::LIGHT_BLOCK_10 => 10, - Ids::LIGHT_BLOCK_11 => 11, - Ids::LIGHT_BLOCK_12 => 12, - Ids::LIGHT_BLOCK_13 => 13, - Ids::LIGHT_BLOCK_14 => 14, - Ids::LIGHT_BLOCK_15 => 15, - ] as $id => $level){ - $this->mapSimple($id, fn() => Blocks::LIGHT()->setLightLevel($level)); - } - } - - private function registerMobHeadDeserializers() : void{ - foreach([ - Ids::CREEPER_HEAD => MobHeadType::CREEPER, - Ids::DRAGON_HEAD => MobHeadType::DRAGON, - Ids::PIGLIN_HEAD => MobHeadType::PIGLIN, - Ids::PLAYER_HEAD => MobHeadType::PLAYER, - Ids::SKELETON_SKULL => MobHeadType::SKELETON, - Ids::WITHER_SKELETON_SKULL => MobHeadType::WITHER_SKELETON, - Ids::ZOMBIE_HEAD => MobHeadType::ZOMBIE - ] as $id => $mobHeadType){ - $this->map($id, fn(Reader $in) => Blocks::MOB_HEAD()->setMobHeadType($mobHeadType)->setFacing($in->readFacingWithoutDown())); - } - } - - /** - * @phpstan-param \Closure(Reader) : (CopperMaterial&Block) $deserializer - */ - private function mapCopper( - string $normalId, - string $waxedNormalId, - string $exposedId, - string $waxedExposedId, - string $weatheredId, - string $waxedWeatheredId, - string $oxidizedId, - string $waxedOxidizedId, - \Closure $deserializer - ) : void{ - foreach(Utils::stringifyKeys([ - $normalId => [CopperOxidation::NONE, false], - $waxedNormalId => [CopperOxidation::NONE, true], - $exposedId => [CopperOxidation::EXPOSED, false], - $waxedExposedId => [CopperOxidation::EXPOSED, true], - $weatheredId => [CopperOxidation::WEATHERED, false], - $waxedWeatheredId => [CopperOxidation::WEATHERED, true], - $oxidizedId => [CopperOxidation::OXIDIZED, false], - $waxedOxidizedId => [CopperOxidation::OXIDIZED, true], - ]) as $id => [$oxidation, $waxed]){ - $this->map($id, fn(Reader $in) => $deserializer($in)->setOxidation($oxidation)->setWaxed($waxed)); - } - } - - private function registerCopperDeserializers() : void{ - $this->mapCopper( - Ids::CUT_COPPER_SLAB, - Ids::WAXED_CUT_COPPER_SLAB, - Ids::EXPOSED_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, - Ids::WEATHERED_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, - Ids::OXIDIZED_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, - fn(Reader $in) => Helper::decodeSingleSlab(Blocks::CUT_COPPER_SLAB(), $in) - ); - $this->mapCopper( - Ids::DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, - Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, - fn(Reader $in) => Helper::decodeDoubleSlab(Blocks::CUT_COPPER_SLAB(), $in) - ); - - $this->mapCopper( - Ids::COPPER_BULB, - Ids::WAXED_COPPER_BULB, - Ids::EXPOSED_COPPER_BULB, - Ids::WAXED_EXPOSED_COPPER_BULB, - Ids::WEATHERED_COPPER_BULB, - Ids::WAXED_WEATHERED_COPPER_BULB, - Ids::OXIDIZED_COPPER_BULB, - Ids::WAXED_OXIDIZED_COPPER_BULB, - fn(Reader $in) => Blocks::COPPER_BULB() - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)) - ); - $this->mapCopper( - Ids::COPPER_DOOR, - Ids::WAXED_COPPER_DOOR, - Ids::EXPOSED_COPPER_DOOR, - Ids::WAXED_EXPOSED_COPPER_DOOR, - Ids::WEATHERED_COPPER_DOOR, - Ids::WAXED_WEATHERED_COPPER_DOOR, - Ids::OXIDIZED_COPPER_DOOR, - Ids::WAXED_OXIDIZED_COPPER_DOOR, - fn(Reader $in) => Helper::decodeDoor(Blocks::COPPER_DOOR(), $in) - ); - $this->mapCopper( - Ids::COPPER_TRAPDOOR, - Ids::WAXED_COPPER_TRAPDOOR, - Ids::EXPOSED_COPPER_TRAPDOOR, - Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, - Ids::WEATHERED_COPPER_TRAPDOOR, - Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, - Ids::OXIDIZED_COPPER_TRAPDOOR, - Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, - fn(Reader $in) => Helper::decodeTrapdoor(Blocks::COPPER_TRAPDOOR(), $in) - ); - $this->mapCopper( - Ids::COPPER_BLOCK, - Ids::WAXED_COPPER, - Ids::EXPOSED_COPPER, - Ids::WAXED_EXPOSED_COPPER, - Ids::WEATHERED_COPPER, - Ids::WAXED_WEATHERED_COPPER, - Ids::OXIDIZED_COPPER, - Ids::WAXED_OXIDIZED_COPPER, - fn(Reader $in) => Blocks::COPPER() - ); - $this->mapCopper( - Ids::CHISELED_COPPER, - Ids::WAXED_CHISELED_COPPER, - Ids::EXPOSED_CHISELED_COPPER, - Ids::WAXED_EXPOSED_CHISELED_COPPER, - Ids::WEATHERED_CHISELED_COPPER, - Ids::WAXED_WEATHERED_CHISELED_COPPER, - Ids::OXIDIZED_CHISELED_COPPER, - Ids::WAXED_OXIDIZED_CHISELED_COPPER, - fn(Reader $in) => Blocks::CHISELED_COPPER() - ); - $this->mapCopper( - Ids::COPPER_GRATE, - Ids::WAXED_COPPER_GRATE, - Ids::EXPOSED_COPPER_GRATE, - Ids::WAXED_EXPOSED_COPPER_GRATE, - Ids::WEATHERED_COPPER_GRATE, - Ids::WAXED_WEATHERED_COPPER_GRATE, - Ids::OXIDIZED_COPPER_GRATE, - Ids::WAXED_OXIDIZED_COPPER_GRATE, - fn(Reader $in) => Blocks::COPPER_GRATE() - ); - $this->mapCopper( - Ids::CUT_COPPER, - Ids::WAXED_CUT_COPPER, - Ids::EXPOSED_CUT_COPPER, - Ids::WAXED_EXPOSED_CUT_COPPER, - Ids::WEATHERED_CUT_COPPER, - Ids::WAXED_WEATHERED_CUT_COPPER, - Ids::OXIDIZED_CUT_COPPER, - Ids::WAXED_OXIDIZED_CUT_COPPER, - fn(Reader $in) => Blocks::CUT_COPPER() - ); - $this->mapCopper( - Ids::CUT_COPPER_STAIRS, - Ids::WAXED_CUT_COPPER_STAIRS, - Ids::EXPOSED_CUT_COPPER_STAIRS, - Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, - Ids::WEATHERED_CUT_COPPER_STAIRS, - Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, - Ids::OXIDIZED_CUT_COPPER_STAIRS, - Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, - fn(Reader $in) => Helper::decodeStairs(Blocks::CUT_COPPER_STAIRS(), $in) - ); - } - - private function registerSimpleDeserializers() : void{ - $this->mapSimple(Ids::AIR, fn() => Blocks::AIR()); - $this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST()); - $this->mapSimple(Ids::ANCIENT_DEBRIS, fn() => Blocks::ANCIENT_DEBRIS()); - $this->mapSimple(Ids::ANDESITE, fn() => Blocks::ANDESITE()); - $this->mapSimple(Ids::BARRIER, fn() => Blocks::BARRIER()); - $this->mapSimple(Ids::BEACON, fn() => Blocks::BEACON()); - $this->mapSimple(Ids::BLACKSTONE, fn() => Blocks::BLACKSTONE()); - $this->mapSimple(Ids::BLUE_ICE, fn() => Blocks::BLUE_ICE()); - $this->mapSimple(Ids::BOOKSHELF, fn() => Blocks::BOOKSHELF()); - $this->mapSimple(Ids::BRICK_BLOCK, fn() => Blocks::BRICKS()); - $this->mapSimple(Ids::BROWN_MUSHROOM, fn() => Blocks::BROWN_MUSHROOM()); - $this->mapSimple(Ids::BUDDING_AMETHYST, fn() => Blocks::BUDDING_AMETHYST()); - $this->mapSimple(Ids::CALCITE, fn() => Blocks::CALCITE()); - $this->mapSimple(Ids::CARTOGRAPHY_TABLE, fn() => Blocks::CARTOGRAPHY_TABLE()); - $this->mapSimple(Ids::CHEMICAL_HEAT, fn() => Blocks::CHEMICAL_HEAT()); - $this->mapSimple(Ids::CHISELED_DEEPSLATE, fn() => Blocks::CHISELED_DEEPSLATE()); - $this->mapSimple(Ids::CHISELED_NETHER_BRICKS, fn() => Blocks::CHISELED_NETHER_BRICKS()); - $this->mapSimple(Ids::CHISELED_POLISHED_BLACKSTONE, fn() => Blocks::CHISELED_POLISHED_BLACKSTONE()); - $this->mapSimple(Ids::CHISELED_RED_SANDSTONE, fn() => Blocks::CHISELED_RED_SANDSTONE()); - $this->mapSimple(Ids::CHISELED_RESIN_BRICKS, fn() => Blocks::CHISELED_RESIN_BRICKS()); - $this->mapSimple(Ids::CHISELED_SANDSTONE, fn() => Blocks::CHISELED_SANDSTONE()); - $this->mapSimple(Ids::CHISELED_STONE_BRICKS, fn() => Blocks::CHISELED_STONE_BRICKS()); - $this->mapSimple(Ids::CHISELED_TUFF, fn() => Blocks::CHISELED_TUFF()); - $this->mapSimple(Ids::CHISELED_TUFF_BRICKS, fn() => Blocks::CHISELED_TUFF_BRICKS()); - $this->mapSimple(Ids::CHORUS_PLANT, fn() => Blocks::CHORUS_PLANT()); - $this->mapSimple(Ids::CLAY, fn() => Blocks::CLAY()); - $this->mapSimple(Ids::COAL_BLOCK, fn() => Blocks::COAL()); - $this->mapSimple(Ids::COAL_ORE, fn() => Blocks::COAL_ORE()); - $this->mapSimple(Ids::COBBLED_DEEPSLATE, fn() => Blocks::COBBLED_DEEPSLATE()); - $this->mapSimple(Ids::COBBLESTONE, fn() => Blocks::COBBLESTONE()); - $this->mapSimple(Ids::COPPER_ORE, fn() => Blocks::COPPER_ORE()); - $this->mapSimple(Ids::CRACKED_DEEPSLATE_BRICKS, fn() => Blocks::CRACKED_DEEPSLATE_BRICKS()); - $this->mapSimple(Ids::CRACKED_DEEPSLATE_TILES, fn() => Blocks::CRACKED_DEEPSLATE_TILES()); - $this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS()); - $this->mapSimple(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS()); - $this->mapSimple(Ids::CRACKED_STONE_BRICKS, fn() => Blocks::CRACKED_STONE_BRICKS()); - $this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE()); - $this->mapSimple(Ids::CRIMSON_ROOTS, fn() => Blocks::CRIMSON_ROOTS()); - $this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN()); - $this->mapSimple(Ids::CUT_RED_SANDSTONE, fn() => Blocks::CUT_RED_SANDSTONE()); - $this->mapSimple(Ids::CUT_SANDSTONE, fn() => Blocks::CUT_SANDSTONE()); - $this->mapSimple(Ids::DARK_PRISMARINE, fn() => Blocks::DARK_PRISMARINE()); - $this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH()); - $this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS()); - $this->mapSimple(Ids::DEEPSLATE_COAL_ORE, fn() => Blocks::DEEPSLATE_COAL_ORE()); - $this->mapSimple(Ids::DEEPSLATE_COPPER_ORE, fn() => Blocks::DEEPSLATE_COPPER_ORE()); - $this->mapSimple(Ids::DEEPSLATE_DIAMOND_ORE, fn() => Blocks::DEEPSLATE_DIAMOND_ORE()); - $this->mapSimple(Ids::DEEPSLATE_EMERALD_ORE, fn() => Blocks::DEEPSLATE_EMERALD_ORE()); - $this->mapSimple(Ids::DEEPSLATE_GOLD_ORE, fn() => Blocks::DEEPSLATE_GOLD_ORE()); - $this->mapSimple(Ids::DEEPSLATE_IRON_ORE, fn() => Blocks::DEEPSLATE_IRON_ORE()); - $this->mapSimple(Ids::DEEPSLATE_LAPIS_ORE, fn() => Blocks::DEEPSLATE_LAPIS_LAZULI_ORE()); - $this->mapSimple(Ids::DEEPSLATE_TILES, fn() => Blocks::DEEPSLATE_TILES()); - $this->mapSimple(Ids::DIAMOND_BLOCK, fn() => Blocks::DIAMOND()); - $this->mapSimple(Ids::DIAMOND_ORE, fn() => Blocks::DIAMOND_ORE()); - $this->mapSimple(Ids::DIORITE, fn() => Blocks::DIORITE()); - $this->mapSimple(Ids::DRAGON_EGG, fn() => Blocks::DRAGON_EGG()); - $this->mapSimple(Ids::DRIED_KELP_BLOCK, fn() => Blocks::DRIED_KELP()); - $this->mapSimple(Ids::ELEMENT_0, fn() => Blocks::ELEMENT_ZERO()); - $this->mapSimple(Ids::ELEMENT_1, fn() => Blocks::ELEMENT_HYDROGEN()); - $this->mapSimple(Ids::ELEMENT_10, fn() => Blocks::ELEMENT_NEON()); - $this->mapSimple(Ids::ELEMENT_100, fn() => Blocks::ELEMENT_FERMIUM()); - $this->mapSimple(Ids::ELEMENT_101, fn() => Blocks::ELEMENT_MENDELEVIUM()); - $this->mapSimple(Ids::ELEMENT_102, fn() => Blocks::ELEMENT_NOBELIUM()); - $this->mapSimple(Ids::ELEMENT_103, fn() => Blocks::ELEMENT_LAWRENCIUM()); - $this->mapSimple(Ids::ELEMENT_104, fn() => Blocks::ELEMENT_RUTHERFORDIUM()); - $this->mapSimple(Ids::ELEMENT_105, fn() => Blocks::ELEMENT_DUBNIUM()); - $this->mapSimple(Ids::ELEMENT_106, fn() => Blocks::ELEMENT_SEABORGIUM()); - $this->mapSimple(Ids::ELEMENT_107, fn() => Blocks::ELEMENT_BOHRIUM()); - $this->mapSimple(Ids::ELEMENT_108, fn() => Blocks::ELEMENT_HASSIUM()); - $this->mapSimple(Ids::ELEMENT_109, fn() => Blocks::ELEMENT_MEITNERIUM()); - $this->mapSimple(Ids::ELEMENT_11, fn() => Blocks::ELEMENT_SODIUM()); - $this->mapSimple(Ids::ELEMENT_110, fn() => Blocks::ELEMENT_DARMSTADTIUM()); - $this->mapSimple(Ids::ELEMENT_111, fn() => Blocks::ELEMENT_ROENTGENIUM()); - $this->mapSimple(Ids::ELEMENT_112, fn() => Blocks::ELEMENT_COPERNICIUM()); - $this->mapSimple(Ids::ELEMENT_113, fn() => Blocks::ELEMENT_NIHONIUM()); - $this->mapSimple(Ids::ELEMENT_114, fn() => Blocks::ELEMENT_FLEROVIUM()); - $this->mapSimple(Ids::ELEMENT_115, fn() => Blocks::ELEMENT_MOSCOVIUM()); - $this->mapSimple(Ids::ELEMENT_116, fn() => Blocks::ELEMENT_LIVERMORIUM()); - $this->mapSimple(Ids::ELEMENT_117, fn() => Blocks::ELEMENT_TENNESSINE()); - $this->mapSimple(Ids::ELEMENT_118, fn() => Blocks::ELEMENT_OGANESSON()); - $this->mapSimple(Ids::ELEMENT_12, fn() => Blocks::ELEMENT_MAGNESIUM()); - $this->mapSimple(Ids::ELEMENT_13, fn() => Blocks::ELEMENT_ALUMINUM()); - $this->mapSimple(Ids::ELEMENT_14, fn() => Blocks::ELEMENT_SILICON()); - $this->mapSimple(Ids::ELEMENT_15, fn() => Blocks::ELEMENT_PHOSPHORUS()); - $this->mapSimple(Ids::ELEMENT_16, fn() => Blocks::ELEMENT_SULFUR()); - $this->mapSimple(Ids::ELEMENT_17, fn() => Blocks::ELEMENT_CHLORINE()); - $this->mapSimple(Ids::ELEMENT_18, fn() => Blocks::ELEMENT_ARGON()); - $this->mapSimple(Ids::ELEMENT_19, fn() => Blocks::ELEMENT_POTASSIUM()); - $this->mapSimple(Ids::ELEMENT_2, fn() => Blocks::ELEMENT_HELIUM()); - $this->mapSimple(Ids::ELEMENT_20, fn() => Blocks::ELEMENT_CALCIUM()); - $this->mapSimple(Ids::ELEMENT_21, fn() => Blocks::ELEMENT_SCANDIUM()); - $this->mapSimple(Ids::ELEMENT_22, fn() => Blocks::ELEMENT_TITANIUM()); - $this->mapSimple(Ids::ELEMENT_23, fn() => Blocks::ELEMENT_VANADIUM()); - $this->mapSimple(Ids::ELEMENT_24, fn() => Blocks::ELEMENT_CHROMIUM()); - $this->mapSimple(Ids::ELEMENT_25, fn() => Blocks::ELEMENT_MANGANESE()); - $this->mapSimple(Ids::ELEMENT_26, fn() => Blocks::ELEMENT_IRON()); - $this->mapSimple(Ids::ELEMENT_27, fn() => Blocks::ELEMENT_COBALT()); - $this->mapSimple(Ids::ELEMENT_28, fn() => Blocks::ELEMENT_NICKEL()); - $this->mapSimple(Ids::ELEMENT_29, fn() => Blocks::ELEMENT_COPPER()); - $this->mapSimple(Ids::ELEMENT_3, fn() => Blocks::ELEMENT_LITHIUM()); - $this->mapSimple(Ids::ELEMENT_30, fn() => Blocks::ELEMENT_ZINC()); - $this->mapSimple(Ids::ELEMENT_31, fn() => Blocks::ELEMENT_GALLIUM()); - $this->mapSimple(Ids::ELEMENT_32, fn() => Blocks::ELEMENT_GERMANIUM()); - $this->mapSimple(Ids::ELEMENT_33, fn() => Blocks::ELEMENT_ARSENIC()); - $this->mapSimple(Ids::ELEMENT_34, fn() => Blocks::ELEMENT_SELENIUM()); - $this->mapSimple(Ids::ELEMENT_35, fn() => Blocks::ELEMENT_BROMINE()); - $this->mapSimple(Ids::ELEMENT_36, fn() => Blocks::ELEMENT_KRYPTON()); - $this->mapSimple(Ids::ELEMENT_37, fn() => Blocks::ELEMENT_RUBIDIUM()); - $this->mapSimple(Ids::ELEMENT_38, fn() => Blocks::ELEMENT_STRONTIUM()); - $this->mapSimple(Ids::ELEMENT_39, fn() => Blocks::ELEMENT_YTTRIUM()); - $this->mapSimple(Ids::ELEMENT_4, fn() => Blocks::ELEMENT_BERYLLIUM()); - $this->mapSimple(Ids::ELEMENT_40, fn() => Blocks::ELEMENT_ZIRCONIUM()); - $this->mapSimple(Ids::ELEMENT_41, fn() => Blocks::ELEMENT_NIOBIUM()); - $this->mapSimple(Ids::ELEMENT_42, fn() => Blocks::ELEMENT_MOLYBDENUM()); - $this->mapSimple(Ids::ELEMENT_43, fn() => Blocks::ELEMENT_TECHNETIUM()); - $this->mapSimple(Ids::ELEMENT_44, fn() => Blocks::ELEMENT_RUTHENIUM()); - $this->mapSimple(Ids::ELEMENT_45, fn() => Blocks::ELEMENT_RHODIUM()); - $this->mapSimple(Ids::ELEMENT_46, fn() => Blocks::ELEMENT_PALLADIUM()); - $this->mapSimple(Ids::ELEMENT_47, fn() => Blocks::ELEMENT_SILVER()); - $this->mapSimple(Ids::ELEMENT_48, fn() => Blocks::ELEMENT_CADMIUM()); - $this->mapSimple(Ids::ELEMENT_49, fn() => Blocks::ELEMENT_INDIUM()); - $this->mapSimple(Ids::ELEMENT_5, fn() => Blocks::ELEMENT_BORON()); - $this->mapSimple(Ids::ELEMENT_50, fn() => Blocks::ELEMENT_TIN()); - $this->mapSimple(Ids::ELEMENT_51, fn() => Blocks::ELEMENT_ANTIMONY()); - $this->mapSimple(Ids::ELEMENT_52, fn() => Blocks::ELEMENT_TELLURIUM()); - $this->mapSimple(Ids::ELEMENT_53, fn() => Blocks::ELEMENT_IODINE()); - $this->mapSimple(Ids::ELEMENT_54, fn() => Blocks::ELEMENT_XENON()); - $this->mapSimple(Ids::ELEMENT_55, fn() => Blocks::ELEMENT_CESIUM()); - $this->mapSimple(Ids::ELEMENT_56, fn() => Blocks::ELEMENT_BARIUM()); - $this->mapSimple(Ids::ELEMENT_57, fn() => Blocks::ELEMENT_LANTHANUM()); - $this->mapSimple(Ids::ELEMENT_58, fn() => Blocks::ELEMENT_CERIUM()); - $this->mapSimple(Ids::ELEMENT_59, fn() => Blocks::ELEMENT_PRASEODYMIUM()); - $this->mapSimple(Ids::ELEMENT_6, fn() => Blocks::ELEMENT_CARBON()); - $this->mapSimple(Ids::ELEMENT_60, fn() => Blocks::ELEMENT_NEODYMIUM()); - $this->mapSimple(Ids::ELEMENT_61, fn() => Blocks::ELEMENT_PROMETHIUM()); - $this->mapSimple(Ids::ELEMENT_62, fn() => Blocks::ELEMENT_SAMARIUM()); - $this->mapSimple(Ids::ELEMENT_63, fn() => Blocks::ELEMENT_EUROPIUM()); - $this->mapSimple(Ids::ELEMENT_64, fn() => Blocks::ELEMENT_GADOLINIUM()); - $this->mapSimple(Ids::ELEMENT_65, fn() => Blocks::ELEMENT_TERBIUM()); - $this->mapSimple(Ids::ELEMENT_66, fn() => Blocks::ELEMENT_DYSPROSIUM()); - $this->mapSimple(Ids::ELEMENT_67, fn() => Blocks::ELEMENT_HOLMIUM()); - $this->mapSimple(Ids::ELEMENT_68, fn() => Blocks::ELEMENT_ERBIUM()); - $this->mapSimple(Ids::ELEMENT_69, fn() => Blocks::ELEMENT_THULIUM()); - $this->mapSimple(Ids::ELEMENT_7, fn() => Blocks::ELEMENT_NITROGEN()); - $this->mapSimple(Ids::ELEMENT_70, fn() => Blocks::ELEMENT_YTTERBIUM()); - $this->mapSimple(Ids::ELEMENT_71, fn() => Blocks::ELEMENT_LUTETIUM()); - $this->mapSimple(Ids::ELEMENT_72, fn() => Blocks::ELEMENT_HAFNIUM()); - $this->mapSimple(Ids::ELEMENT_73, fn() => Blocks::ELEMENT_TANTALUM()); - $this->mapSimple(Ids::ELEMENT_74, fn() => Blocks::ELEMENT_TUNGSTEN()); - $this->mapSimple(Ids::ELEMENT_75, fn() => Blocks::ELEMENT_RHENIUM()); - $this->mapSimple(Ids::ELEMENT_76, fn() => Blocks::ELEMENT_OSMIUM()); - $this->mapSimple(Ids::ELEMENT_77, fn() => Blocks::ELEMENT_IRIDIUM()); - $this->mapSimple(Ids::ELEMENT_78, fn() => Blocks::ELEMENT_PLATINUM()); - $this->mapSimple(Ids::ELEMENT_79, fn() => Blocks::ELEMENT_GOLD()); - $this->mapSimple(Ids::ELEMENT_8, fn() => Blocks::ELEMENT_OXYGEN()); - $this->mapSimple(Ids::ELEMENT_80, fn() => Blocks::ELEMENT_MERCURY()); - $this->mapSimple(Ids::ELEMENT_81, fn() => Blocks::ELEMENT_THALLIUM()); - $this->mapSimple(Ids::ELEMENT_82, fn() => Blocks::ELEMENT_LEAD()); - $this->mapSimple(Ids::ELEMENT_83, fn() => Blocks::ELEMENT_BISMUTH()); - $this->mapSimple(Ids::ELEMENT_84, fn() => Blocks::ELEMENT_POLONIUM()); - $this->mapSimple(Ids::ELEMENT_85, fn() => Blocks::ELEMENT_ASTATINE()); - $this->mapSimple(Ids::ELEMENT_86, fn() => Blocks::ELEMENT_RADON()); - $this->mapSimple(Ids::ELEMENT_87, fn() => Blocks::ELEMENT_FRANCIUM()); - $this->mapSimple(Ids::ELEMENT_88, fn() => Blocks::ELEMENT_RADIUM()); - $this->mapSimple(Ids::ELEMENT_89, fn() => Blocks::ELEMENT_ACTINIUM()); - $this->mapSimple(Ids::ELEMENT_9, fn() => Blocks::ELEMENT_FLUORINE()); - $this->mapSimple(Ids::ELEMENT_90, fn() => Blocks::ELEMENT_THORIUM()); - $this->mapSimple(Ids::ELEMENT_91, fn() => Blocks::ELEMENT_PROTACTINIUM()); - $this->mapSimple(Ids::ELEMENT_92, fn() => Blocks::ELEMENT_URANIUM()); - $this->mapSimple(Ids::ELEMENT_93, fn() => Blocks::ELEMENT_NEPTUNIUM()); - $this->mapSimple(Ids::ELEMENT_94, fn() => Blocks::ELEMENT_PLUTONIUM()); - $this->mapSimple(Ids::ELEMENT_95, fn() => Blocks::ELEMENT_AMERICIUM()); - $this->mapSimple(Ids::ELEMENT_96, fn() => Blocks::ELEMENT_CURIUM()); - $this->mapSimple(Ids::ELEMENT_97, fn() => Blocks::ELEMENT_BERKELIUM()); - $this->mapSimple(Ids::ELEMENT_98, fn() => Blocks::ELEMENT_CALIFORNIUM()); - $this->mapSimple(Ids::ELEMENT_99, fn() => Blocks::ELEMENT_EINSTEINIUM()); - $this->mapSimple(Ids::EMERALD_BLOCK, fn() => Blocks::EMERALD()); - $this->mapSimple(Ids::EMERALD_ORE, fn() => Blocks::EMERALD_ORE()); - $this->mapSimple(Ids::ENCHANTING_TABLE, fn() => Blocks::ENCHANTING_TABLE()); - $this->mapSimple(Ids::END_BRICKS, fn() => Blocks::END_STONE_BRICKS()); - $this->mapSimple(Ids::END_STONE, fn() => Blocks::END_STONE()); - $this->mapSimple(Ids::FERN, fn() => Blocks::FERN()); - $this->mapSimple(Ids::FLETCHING_TABLE, fn() => Blocks::FLETCHING_TABLE()); - $this->mapSimple(Ids::GILDED_BLACKSTONE, fn() => Blocks::GILDED_BLACKSTONE()); - $this->mapSimple(Ids::GLASS, fn() => Blocks::GLASS()); - $this->mapSimple(Ids::GLASS_PANE, fn() => Blocks::GLASS_PANE()); - $this->mapSimple(Ids::GLOWINGOBSIDIAN, fn() => Blocks::GLOWING_OBSIDIAN()); - $this->mapSimple(Ids::GLOWSTONE, fn() => Blocks::GLOWSTONE()); - $this->mapSimple(Ids::GOLD_BLOCK, fn() => Blocks::GOLD()); - $this->mapSimple(Ids::GOLD_ORE, fn() => Blocks::GOLD_ORE()); - $this->mapSimple(Ids::GRANITE, fn() => Blocks::GRANITE()); - $this->mapSimple(Ids::GRASS_BLOCK, fn() => Blocks::GRASS()); - $this->mapSimple(Ids::GRASS_PATH, fn() => Blocks::GRASS_PATH()); - $this->mapSimple(Ids::GRAVEL, fn() => Blocks::GRAVEL()); - $this->mapSimple(Ids::HANGING_ROOTS, fn() => Blocks::HANGING_ROOTS()); - $this->mapSimple(Ids::HARD_GLASS, fn() => Blocks::HARDENED_GLASS()); - $this->mapSimple(Ids::HARD_GLASS_PANE, fn() => Blocks::HARDENED_GLASS_PANE()); - $this->mapSimple(Ids::HARDENED_CLAY, fn() => Blocks::HARDENED_CLAY()); - $this->mapSimple(Ids::HONEYCOMB_BLOCK, fn() => Blocks::HONEYCOMB()); - $this->mapSimple(Ids::ICE, fn() => Blocks::ICE()); - $this->mapSimple(Ids::INFESTED_CHISELED_STONE_BRICKS, fn() => Blocks::INFESTED_CHISELED_STONE_BRICK()); - $this->mapSimple(Ids::INFESTED_COBBLESTONE, fn() => Blocks::INFESTED_COBBLESTONE()); - $this->mapSimple(Ids::INFESTED_CRACKED_STONE_BRICKS, fn() => Blocks::INFESTED_CRACKED_STONE_BRICK()); - $this->mapSimple(Ids::INFESTED_MOSSY_STONE_BRICKS, fn() => Blocks::INFESTED_MOSSY_STONE_BRICK()); - $this->mapSimple(Ids::INFESTED_STONE, fn() => Blocks::INFESTED_STONE()); - $this->mapSimple(Ids::INFESTED_STONE_BRICKS, fn() => Blocks::INFESTED_STONE_BRICK()); - $this->mapSimple(Ids::INFO_UPDATE, fn() => Blocks::INFO_UPDATE()); - $this->mapSimple(Ids::INFO_UPDATE2, fn() => Blocks::INFO_UPDATE2()); - $this->mapSimple(Ids::INVISIBLE_BEDROCK, fn() => Blocks::INVISIBLE_BEDROCK()); - $this->mapSimple(Ids::IRON_BARS, fn() => Blocks::IRON_BARS()); - $this->mapSimple(Ids::IRON_BLOCK, fn() => Blocks::IRON()); - $this->mapSimple(Ids::IRON_ORE, fn() => Blocks::IRON_ORE()); - $this->mapSimple(Ids::JUKEBOX, fn() => Blocks::JUKEBOX()); - $this->mapSimple(Ids::LAPIS_BLOCK, fn() => Blocks::LAPIS_LAZULI()); - $this->mapSimple(Ids::LAPIS_ORE, fn() => Blocks::LAPIS_LAZULI_ORE()); - $this->mapSimple(Ids::MAGMA, fn() => Blocks::MAGMA()); - $this->mapSimple(Ids::MANGROVE_ROOTS, fn() => Blocks::MANGROVE_ROOTS()); - $this->mapSimple(Ids::MELON_BLOCK, fn() => Blocks::MELON()); - $this->mapSimple(Ids::MOB_SPAWNER, fn() => Blocks::MONSTER_SPAWNER()); - $this->mapSimple(Ids::MOSSY_COBBLESTONE, fn() => Blocks::MOSSY_COBBLESTONE()); - $this->mapSimple(Ids::MOSSY_STONE_BRICKS, fn() => Blocks::MOSSY_STONE_BRICKS()); - $this->mapSimple(Ids::MUD, fn() => Blocks::MUD()); - $this->mapSimple(Ids::MUD_BRICKS, fn() => Blocks::MUD_BRICKS()); - $this->mapSimple(Ids::MYCELIUM, fn() => Blocks::MYCELIUM()); - $this->mapSimple(Ids::NETHER_BRICK, fn() => Blocks::NETHER_BRICKS()); - $this->mapSimple(Ids::NETHER_BRICK_FENCE, fn() => Blocks::NETHER_BRICK_FENCE()); - $this->mapSimple(Ids::NETHER_GOLD_ORE, fn() => Blocks::NETHER_GOLD_ORE()); - $this->mapSimple(Ids::NETHER_WART_BLOCK, fn() => Blocks::NETHER_WART_BLOCK()); - $this->mapSimple(Ids::NETHERITE_BLOCK, fn() => Blocks::NETHERITE()); - $this->mapSimple(Ids::NETHERRACK, fn() => Blocks::NETHERRACK()); - $this->mapSimple(Ids::NETHERREACTOR, fn() => Blocks::NETHER_REACTOR_CORE()); - $this->mapSimple(Ids::NOTEBLOCK, fn() => Blocks::NOTE_BLOCK()); - $this->mapSimple(Ids::OBSIDIAN, fn() => Blocks::OBSIDIAN()); - $this->mapSimple(Ids::PACKED_ICE, fn() => Blocks::PACKED_ICE()); - $this->mapSimple(Ids::PACKED_MUD, fn() => Blocks::PACKED_MUD()); - $this->mapSimple(Ids::PODZOL, fn() => Blocks::PODZOL()); - $this->mapSimple(Ids::POLISHED_ANDESITE, fn() => Blocks::POLISHED_ANDESITE()); - $this->mapSimple(Ids::POLISHED_BLACKSTONE, fn() => Blocks::POLISHED_BLACKSTONE()); - $this->mapSimple(Ids::POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::POLISHED_BLACKSTONE_BRICKS()); - $this->mapSimple(Ids::POLISHED_DEEPSLATE, fn() => Blocks::POLISHED_DEEPSLATE()); - $this->mapSimple(Ids::POLISHED_DIORITE, fn() => Blocks::POLISHED_DIORITE()); - $this->mapSimple(Ids::POLISHED_GRANITE, fn() => Blocks::POLISHED_GRANITE()); - $this->mapSimple(Ids::POLISHED_TUFF, fn() => Blocks::POLISHED_TUFF()); - $this->mapSimple(Ids::PRISMARINE, fn() => Blocks::PRISMARINE()); - $this->mapSimple(Ids::PRISMARINE_BRICKS, fn() => Blocks::PRISMARINE_BRICKS()); - $this->mapSimple(Ids::QUARTZ_BRICKS, fn() => Blocks::QUARTZ_BRICKS()); - $this->mapSimple(Ids::QUARTZ_ORE, fn() => Blocks::NETHER_QUARTZ_ORE()); - $this->mapSimple(Ids::RAW_COPPER_BLOCK, fn() => Blocks::RAW_COPPER()); - $this->mapSimple(Ids::RAW_GOLD_BLOCK, fn() => Blocks::RAW_GOLD()); - $this->mapSimple(Ids::RAW_IRON_BLOCK, fn() => Blocks::RAW_IRON()); - $this->mapSimple(Ids::RED_MUSHROOM, fn() => Blocks::RED_MUSHROOM()); - $this->mapSimple(Ids::RED_NETHER_BRICK, fn() => Blocks::RED_NETHER_BRICKS()); - $this->mapSimple(Ids::RED_SAND, fn() => Blocks::RED_SAND()); - $this->mapSimple(Ids::RED_SANDSTONE, fn() => Blocks::RED_SANDSTONE()); - $this->mapSimple(Ids::REDSTONE_BLOCK, fn() => Blocks::REDSTONE()); - $this->mapSimple(Ids::REINFORCED_DEEPSLATE, fn() => Blocks::REINFORCED_DEEPSLATE()); - $this->mapSimple(Ids::RESERVED6, fn() => Blocks::RESERVED6()); - $this->mapSimple(Ids::RESIN_BLOCK, fn() => Blocks::RESIN()); - $this->mapSimple(Ids::RESIN_BRICKS, fn() => Blocks::RESIN_BRICKS()); - $this->mapSimple(Ids::SAND, fn() => Blocks::SAND()); - $this->mapSimple(Ids::SANDSTONE, fn() => Blocks::SANDSTONE()); - $this->mapSimple(Ids::SCULK, fn() => Blocks::SCULK()); - $this->mapSimple(Ids::SEA_LANTERN, fn() => Blocks::SEA_LANTERN()); - $this->mapSimple(Ids::SHORT_GRASS, fn() => Blocks::TALL_GRASS()); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( - $this->mapSimple(Ids::SHROOMLIGHT, fn() => Blocks::SHROOMLIGHT()); - $this->mapSimple(Ids::SLIME, fn() => Blocks::SLIME()); - $this->mapSimple(Ids::SMITHING_TABLE, fn() => Blocks::SMITHING_TABLE()); - $this->mapSimple(Ids::SMOOTH_BASALT, fn() => Blocks::SMOOTH_BASALT()); - $this->mapSimple(Ids::SMOOTH_RED_SANDSTONE, fn() => Blocks::SMOOTH_RED_SANDSTONE()); - $this->mapSimple(Ids::SMOOTH_SANDSTONE, fn() => Blocks::SMOOTH_SANDSTONE()); - $this->mapSimple(Ids::SMOOTH_STONE, fn() => Blocks::SMOOTH_STONE()); - $this->mapSimple(Ids::SNOW, fn() => Blocks::SNOW()); - $this->mapSimple(Ids::SOUL_SAND, fn() => Blocks::SOUL_SAND()); - $this->mapSimple(Ids::SOUL_SOIL, fn() => Blocks::SOUL_SOIL()); - $this->mapSimple(Ids::SPORE_BLOSSOM, fn() => Blocks::SPORE_BLOSSOM()); - $this->mapSimple(Ids::SPONGE, fn() => Blocks::SPONGE()); - $this->mapSimple(Ids::STONE, fn() => Blocks::STONE()); - $this->mapSimple(Ids::STONECUTTER, fn() => Blocks::LEGACY_STONECUTTER()); - $this->mapSimple(Ids::STONE_BRICKS, fn() => Blocks::STONE_BRICKS()); - $this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS()); - $this->mapSimple(Ids::TORCHFLOWER, fn() => Blocks::TORCHFLOWER()); - $this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF()); - $this->mapSimple(Ids::TUFF_BRICKS, fn() => Blocks::TUFF_BRICKS()); - $this->mapSimple(Ids::UNDYED_SHULKER_BOX, fn() => Blocks::SHULKER_BOX()); - $this->mapSimple(Ids::WARPED_WART_BLOCK, fn() => Blocks::WARPED_WART_BLOCK()); - $this->mapSimple(Ids::WARPED_ROOTS, fn() => Blocks::WARPED_ROOTS()); - $this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD()); - $this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB()); - $this->mapSimple(Ids::WET_SPONGE, fn() => Blocks::SPONGE()->setWet(true)); - $this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE()); - $this->mapSimple(Ids::DANDELION, fn() => Blocks::DANDELION()); - - $this->mapSimple(Ids::ALLIUM, fn() => Blocks::ALLIUM()); - $this->mapSimple(Ids::CORNFLOWER, fn() => Blocks::CORNFLOWER()); - $this->mapSimple(Ids::AZURE_BLUET, fn() => Blocks::AZURE_BLUET()); - $this->mapSimple(Ids::LILY_OF_THE_VALLEY, fn() => Blocks::LILY_OF_THE_VALLEY()); - $this->mapSimple(Ids::BLUE_ORCHID, fn() => Blocks::BLUE_ORCHID()); - $this->mapSimple(Ids::OXEYE_DAISY, fn() => Blocks::OXEYE_DAISY()); - $this->mapSimple(Ids::POPPY, fn() => Blocks::POPPY()); - $this->mapSimple(Ids::ORANGE_TULIP, fn() => Blocks::ORANGE_TULIP()); - $this->mapSimple(Ids::PINK_TULIP, fn() => Blocks::PINK_TULIP()); - $this->mapSimple(Ids::RED_TULIP, fn() => Blocks::RED_TULIP()); - $this->mapSimple(Ids::WHITE_TULIP, fn() => Blocks::WHITE_TULIP()); - } - - private function registerDeserializers() : void{ - $this->map(Ids::ACTIVATOR_RAIL, function(Reader $in) : Block{ - return Blocks::ACTIVATOR_RAIL() - ->setPowered($in->readBool(StateNames::RAIL_DATA_BIT)) - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); - }); - $this->map(Ids::AMETHYST_CLUSTER, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_CLUSTER) - ->setFacing($in->readBlockFace()); - }); - $this->mapSlab(Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB, fn() => Blocks::ANDESITE_SLAB()); - $this->mapStairs(Ids::ANDESITE_STAIRS, fn() => Blocks::ANDESITE_STAIRS()); - $this->map(Ids::ANDESITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::ANDESITE_WALL(), $in)); - $this->map(Ids::ANVIL, function(Reader $in) : Block{ - return Blocks::ANVIL() - ->setDamage(Anvil::UNDAMAGED) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::CHIPPED_ANVIL, function(Reader $in) : Block{ - return Blocks::ANVIL() - ->setDamage(Anvil::SLIGHTLY_DAMAGED) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::DAMAGED_ANVIL, function(Reader $in) : Block{ - return Blocks::ANVIL() - ->setDamage(Anvil::VERY_DAMAGED) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::BAMBOO, function(Reader $in) : Block{ - return Blocks::BAMBOO() - ->setLeafSize(match($value = $in->readString(StateNames::BAMBOO_LEAF_SIZE)){ - StringValues::BAMBOO_LEAF_SIZE_NO_LEAVES => Bamboo::NO_LEAVES, - StringValues::BAMBOO_LEAF_SIZE_SMALL_LEAVES => Bamboo::SMALL_LEAVES, - StringValues::BAMBOO_LEAF_SIZE_LARGE_LEAVES => Bamboo::LARGE_LEAVES, - default => throw $in->badValueException(StateNames::BAMBOO_LEAF_SIZE, $value), - }) - ->setReady($in->readBool(StateNames::AGE_BIT)) - ->setThick(match($value = $in->readString(StateNames::BAMBOO_STALK_THICKNESS)){ - StringValues::BAMBOO_STALK_THICKNESS_THIN => false, - StringValues::BAMBOO_STALK_THICKNESS_THICK => true, - default => throw $in->badValueException(StateNames::BAMBOO_STALK_THICKNESS, $value), - }); - }); - $this->map(Ids::BAMBOO_SAPLING, function(Reader $in) : Block{ - return Blocks::BAMBOO_SAPLING()->setReady($in->readBool(StateNames::AGE_BIT)); - }); - $this->map(Ids::BARREL, function(Reader $in) : Block{ - return Blocks::BARREL() - ->setFacing($in->readFacingDirection()) - ->setOpen($in->readBool(StateNames::OPEN_BIT)); - }); - $this->map(Ids::BASALT, function(Reader $in){ - return Blocks::BASALT() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::BED, function(Reader $in) : Block{ - return Blocks::BED() - ->setFacing($in->readLegacyHorizontalFacing()) - ->setHead($in->readBool(StateNames::HEAD_PIECE_BIT)) - ->setOccupied($in->readBool(StateNames::OCCUPIED_BIT)); - }); - $this->map(Ids::BEDROCK, function(Reader $in) : Block{ - return Blocks::BEDROCK() - ->setBurnsForever($in->readBool(StateNames::INFINIBURN_BIT)); - }); - $this->map(Ids::BEETROOT, fn(Reader $in) => Helper::decodeCrops(Blocks::BEETROOTS(), $in)); - $this->map(Ids::BELL, function(Reader $in) : Block{ - $in->ignored(StateNames::TOGGLE_BIT); //only useful at runtime - return Blocks::BELL() - ->setFacing($in->readLegacyHorizontalFacing()) - ->setAttachmentType($in->readBellAttachmentType()); - }); - $this->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{ - if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){ - return Blocks::BIG_DRIPLEAF_HEAD() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLeafState(match($type = $in->readString(StateNames::BIG_DRIPLEAF_TILT)){ - StringValues::BIG_DRIPLEAF_TILT_NONE => DripleafState::STABLE, - StringValues::BIG_DRIPLEAF_TILT_UNSTABLE => DripleafState::UNSTABLE, - StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT => DripleafState::PARTIAL_TILT, - StringValues::BIG_DRIPLEAF_TILT_FULL_TILT => DripleafState::FULL_TILT, - default => throw $in->badValueException(StateNames::BIG_DRIPLEAF_TILT, $type), - }); - }else{ - $in->ignored(StateNames::BIG_DRIPLEAF_TILT); - return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readCardinalHorizontalFacing()); - } - }); - $this->mapSlab(Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::BLACKSTONE_SLAB()); - $this->mapStairs(Ids::BLACKSTONE_STAIRS, fn() => Blocks::BLACKSTONE_STAIRS()); - $this->map(Ids::BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BLACKSTONE_WALL(), $in)); - $this->map(Ids::BLAST_FURNACE, function(Reader $in) : Block{ - return Blocks::BLAST_FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(false); - }); - $this->map(Ids::BONE_BLOCK, function(Reader $in) : Block{ - $in->ignored(StateNames::DEPRECATED); - return Blocks::BONE_BLOCK()->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::BREWING_STAND, function(Reader $in) : Block{ - return Blocks::BREWING_STAND() - ->setSlot(BrewingStandSlot::EAST, $in->readBool(StateNames::BREWING_STAND_SLOT_A_BIT)) - ->setSlot(BrewingStandSlot::SOUTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_B_BIT)) - ->setSlot(BrewingStandSlot::NORTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_C_BIT)); - }); - $this->mapSlab(Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB, fn() => Blocks::BRICK_SLAB()); - $this->mapStairs(Ids::BRICK_STAIRS, fn() => Blocks::BRICK_STAIRS()); - $this->map(Ids::BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BRICK_WALL(), $in)); - $this->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)){ - BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(), - BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(), - default => throw new BlockStateDeserializeException("This state does not exist"), - }); - $this->map(Ids::BROWN_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::BROWN_MUSHROOM_BLOCK(), $in)); - $this->map(Ids::CACTUS, function(Reader $in) : Block{ - return Blocks::CACTUS() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); - }); - $this->map(Ids::CAKE, function(Reader $in) : Block{ - return Blocks::CAKE() - ->setBites($in->readBoundedInt(StateNames::BITE_COUNTER, 0, 6)); - }); - $this->map(Ids::CAMPFIRE, function(Reader $in) : Block{ - return Blocks::CAMPFIRE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(!$in->readBool(StateNames::EXTINGUISHED)); - }); - $this->map(Ids::CARROTS, fn(Reader $in) => Helper::decodeCrops(Blocks::CARROTS(), $in)); - $this->map(Ids::CARVED_PUMPKIN, function(Reader $in) : Block{ - return Blocks::CARVED_PUMPKIN() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::CAVE_VINES, function(Reader $in) : CaveVines{ - return Blocks::CAVE_VINES() - ->setBerries(false) - ->setHead(false) - ->setAge($in->readBoundedInt(StateNames::GROWING_PLANT_AGE, 0, 25)); - }); - $this->map(Ids::CAVE_VINES_BODY_WITH_BERRIES, function(Reader $in) : CaveVines{ - return Blocks::CAVE_VINES() - ->setBerries(true) - ->setHead(false) - ->setAge($in->readBoundedInt(StateNames::GROWING_PLANT_AGE, 0, 25)); - }); - $this->map(Ids::CAVE_VINES_HEAD_WITH_BERRIES, function(Reader $in) : CaveVines{ - return Blocks::CAVE_VINES() - ->setBerries(true) - ->setHead(true) - ->setAge($in->readBoundedInt(StateNames::GROWING_PLANT_AGE, 0, 25)); - }); - $this->map(Ids::CHAIN, function(Reader $in) : Block{ - return Blocks::CHAIN() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::CHISELED_BOOKSHELF, function(Reader $in) : Block{ - $block = Blocks::CHISELED_BOOKSHELF() - ->setFacing($in->readLegacyHorizontalFacing()); - - //we don't use API constant for bounds here as the data bounds might be different to what we support internally - $flags = $in->readBoundedInt(StateNames::BOOKS_STORED, 0, (1 << 6) - 1); - foreach(ChiseledBookshelfSlot::cases() as $slot){ - $block->setSlot($slot, ($flags & (1 << $slot->value)) !== 0); - } - - return $block; - }); - $this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{ - return Blocks::CHISELED_QUARTZ() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::CHEST, function(Reader $in) : Block{ - return Blocks::CHEST() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::CHORUS_FLOWER, function(Reader $in) : Block{ - return Blocks::CHORUS_FLOWER() - ->setAge($in->readBoundedInt(StateNames::AGE, ChorusFlower::MIN_AGE, ChorusFlower::MAX_AGE)); - }); - $this->map(Ids::COARSE_DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::COARSE)); - $this->mapSlab(Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::COBBLED_DEEPSLATE_SLAB()); - $this->mapStairs(Ids::COBBLED_DEEPSLATE_STAIRS, fn() => Blocks::COBBLED_DEEPSLATE_STAIRS()); - $this->map(Ids::COBBLED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLED_DEEPSLATE_WALL(), $in)); - $this->mapSlab(Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::COBBLESTONE_SLAB()); - $this->map(Ids::COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLESTONE_WALL(), $in)); - $this->map(Ids::COCOA, function(Reader $in) : Block{ - return Blocks::COCOA_POD() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 2)) - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())); - }); - $this->map(Ids::COLORED_TORCH_BLUE, fn(Reader $in) => Blocks::BLUE_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COLORED_TORCH_GREEN, fn(Reader $in) => Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COLORED_TORCH_PURPLE, fn(Reader $in) => Blocks::PURPLE_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COLORED_TORCH_RED, fn(Reader $in) => Blocks::RED_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COMPOUND_CREATOR, fn(Reader $in) => Blocks::COMPOUND_CREATOR() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->mapSlab(Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_RED_SANDSTONE_SLAB()); - $this->mapSlab(Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_SANDSTONE_SLAB()); - $this->mapSlab(Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB, fn() => Blocks::DARK_PRISMARINE_SLAB()); - $this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS()); - $this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in) - ->setInverted(false)); - $this->map(Ids::DAYLIGHT_DETECTOR_INVERTED, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in) - ->setInverted(true)); - $this->map(Ids::DEEPSLATE, function(Reader $in) : Block{ - return Blocks::DEEPSLATE() - ->setAxis($in->readPillarAxis()); - }); - $this->mapSlab(Ids::DEEPSLATE_BRICK_SLAB, Ids::DEEPSLATE_BRICK_DOUBLE_SLAB, fn() => Blocks::DEEPSLATE_BRICK_SLAB()); - $this->mapStairs(Ids::DEEPSLATE_BRICK_STAIRS, fn() => Blocks::DEEPSLATE_BRICK_STAIRS()); - $this->map(Ids::DEEPSLATE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DEEPSLATE_BRICK_WALL(), $in)); - $this->map(Ids::DEEPSLATE_REDSTONE_ORE, fn() => Blocks::DEEPSLATE_REDSTONE_ORE()->setLit(false)); - $this->mapSlab(Ids::DEEPSLATE_TILE_SLAB, Ids::DEEPSLATE_TILE_DOUBLE_SLAB, fn() => Blocks::DEEPSLATE_TILE_SLAB()); - $this->mapStairs(Ids::DEEPSLATE_TILE_STAIRS, fn() => Blocks::DEEPSLATE_TILE_STAIRS()); - $this->map(Ids::DEEPSLATE_TILE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DEEPSLATE_TILE_WALL(), $in)); - $this->map(Ids::DETECTOR_RAIL, function(Reader $in) : Block{ - return Blocks::DETECTOR_RAIL() - ->setActivated($in->readBool(StateNames::RAIL_DATA_BIT)) - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); - }); - $this->mapSlab(Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB, fn() => Blocks::DIORITE_SLAB()); - $this->mapStairs(Ids::DIORITE_STAIRS, fn() => Blocks::DIORITE_STAIRS()); - $this->map(Ids::DIORITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DIORITE_WALL(), $in)); - $this->map(Ids::DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::NORMAL)); - $this->map(Ids::DIRT_WITH_ROOTS, fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED)); - $this->map(Ids::LARGE_FERN, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LARGE_FERN(), $in)); - $this->map(Ids::TALL_GRASS, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::DOUBLE_TALLGRASS(), $in)); - $this->map(Ids::PEONY, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::PEONY(), $in)); - $this->map(Ids::ROSE_BUSH, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::ROSE_BUSH(), $in)); - $this->map(Ids::SUNFLOWER, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::SUNFLOWER(), $in)); - $this->map(Ids::LILAC, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LILAC(), $in)); - $this->map(Ids::ELEMENT_CONSTRUCTOR, fn(Reader $in) => Blocks::ELEMENT_CONSTRUCTOR() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->mapStairs(Ids::END_BRICK_STAIRS, fn() => Blocks::END_STONE_BRICK_STAIRS()); - $this->map(Ids::END_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::END_STONE_BRICK_WALL(), $in)); - $this->map(Ids::END_PORTAL_FRAME, function(Reader $in) : Block{ - return Blocks::END_PORTAL_FRAME() - ->setEye($in->readBool(StateNames::END_PORTAL_EYE_BIT)) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::END_ROD, function(Reader $in) : Block{ - return Blocks::END_ROD() - ->setFacing($in->readEndRodFacingDirection()); - }); - $this->mapSlab(Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::END_STONE_BRICK_SLAB()); - $this->map(Ids::ENDER_CHEST, function(Reader $in) : Block{ - return Blocks::ENDER_CHEST() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::FARMLAND, function(Reader $in) : Block{ - return Blocks::FARMLAND() - ->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7)); - }); - $this->map(Ids::FIRE, function(Reader $in) : Block{ - return Blocks::FIRE() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); - }); - $this->map(Ids::FLOWER_POT, function(Reader $in) : Block{ - $in->ignored(StateNames::UPDATE_BIT); - return Blocks::FLOWER_POT(); - }); - $this->map(Ids::FLOWING_LAVA, fn(Reader $in) => Helper::decodeFlowingLiquid(Blocks::LAVA(), $in)); - $this->map(Ids::FLOWING_WATER, fn(Reader $in) => Helper::decodeFlowingLiquid(Blocks::WATER(), $in)); - $this->map(Ids::FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::ITEM_FRAME(), $in)); - $this->map(Ids::FROSTED_ICE, function(Reader $in) : Block{ - return Blocks::FROSTED_ICE() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 3)); - }); - $this->map(Ids::FURNACE, function(Reader $in) : Block{ - return Blocks::FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(false); - }); - $this->map(Ids::GLOW_LICHEN, fn(Reader $in) => Blocks::GLOW_LICHEN()->setFaces($in->readFacingFlags())); - $this->map(Ids::GLOW_FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::GLOWING_ITEM_FRAME(), $in)); - $this->map(Ids::GOLDEN_RAIL, function(Reader $in) : Block{ - return Blocks::POWERED_RAIL() - ->setPowered($in->readBool(StateNames::RAIL_DATA_BIT)) - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); - }); - $this->mapSlab(Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB, fn() => Blocks::GRANITE_SLAB()); - $this->mapStairs(Ids::GRANITE_STAIRS, fn() => Blocks::GRANITE_STAIRS()); - $this->map(Ids::GRANITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::GRANITE_WALL(), $in)); - $this->map(Ids::HAY_BLOCK, function(Reader $in) : Block{ - $in->ignored(StateNames::DEPRECATED); - return Blocks::HAY_BALE()->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::HEAVY_WEIGHTED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeWeightedPressurePlate(Blocks::WEIGHTED_PRESSURE_PLATE_HEAVY(), $in)); - $this->map(Ids::HOPPER, function(Reader $in) : Block{ - return Blocks::HOPPER() - ->setFacing($in->readFacingWithoutUp()) - ->setPowered($in->readBool(StateNames::TOGGLE_BIT)); - }); - $this->map(Ids::IRON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::IRON_DOOR(), $in)); - $this->map(Ids::IRON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::IRON_TRAPDOOR(), $in)); - $this->map(Ids::LAB_TABLE, fn(Reader $in) => Blocks::LAB_TABLE() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->map(Ids::LADDER, function(Reader $in) : Block{ - return Blocks::LADDER() - ->setFacing($in->readHorizontalFacing()); - }); - $this->map(Ids::LANTERN, function(Reader $in) : Block{ - return Blocks::LANTERN() - ->setHanging($in->readBool(StateNames::HANGING)); - }); - $this->map(Ids::LARGE_AMETHYST_BUD, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_LARGE_BUD) - ->setFacing($in->readBlockFace()); - }); - $this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in)); - $this->map(Ids::LECTERN, function(Reader $in) : Block{ - return Blocks::LECTERN() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setProducingSignal($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::LEVER, function(Reader $in) : Block{ - return Blocks::LEVER() - ->setActivated($in->readBool(StateNames::OPEN_BIT)) - ->setFacing(match($value = $in->readString(StateNames::LEVER_DIRECTION)){ - StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH => LeverFacing::DOWN_AXIS_Z, - StringValues::LEVER_DIRECTION_DOWN_EAST_WEST => LeverFacing::DOWN_AXIS_X, - StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH => LeverFacing::UP_AXIS_Z, - StringValues::LEVER_DIRECTION_UP_EAST_WEST => LeverFacing::UP_AXIS_X, - StringValues::LEVER_DIRECTION_NORTH => LeverFacing::NORTH, - StringValues::LEVER_DIRECTION_SOUTH => LeverFacing::SOUTH, - StringValues::LEVER_DIRECTION_WEST => LeverFacing::WEST, - StringValues::LEVER_DIRECTION_EAST => LeverFacing::EAST, - default => throw $in->badValueException(StateNames::LEVER_DIRECTION, $value), - }); - }); - $this->map(Ids::LIGHTNING_ROD, function(Reader $in) : Block{ - return Blocks::LIGHTNING_ROD() - ->setFacing($in->readFacingDirection()); - }); - $this->map(Ids::LIGHT_WEIGHTED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeWeightedPressurePlate(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), $in)); - $this->map(Ids::LIT_BLAST_FURNACE, function(Reader $in) : Block{ - return Blocks::BLAST_FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(true); - }); - $this->map(Ids::LIT_DEEPSLATE_REDSTONE_ORE, fn() => Blocks::DEEPSLATE_REDSTONE_ORE()->setLit(true)); - $this->map(Ids::LIT_FURNACE, function(Reader $in) : Block{ - return Blocks::FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(true); - }); - $this->map(Ids::LIT_PUMPKIN, function(Reader $in) : Block{ - return Blocks::LIT_PUMPKIN() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::LIT_REDSTONE_LAMP, function() : Block{ - return Blocks::REDSTONE_LAMP() - ->setPowered(true); - }); - $this->map(Ids::LIT_REDSTONE_ORE, function() : Block{ - return Blocks::REDSTONE_ORE() - ->setLit(true); - }); - $this->map(Ids::LIT_SMOKER, function(Reader $in) : Block{ - return Blocks::SMOKER() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(true); - }); - $this->map(Ids::LOOM, function(Reader $in) : Block{ - return Blocks::LOOM() - ->setFacing($in->readLegacyHorizontalFacing()); - }); - $this->map(Ids::MATERIAL_REDUCER, fn(Reader $in) => Blocks::MATERIAL_REDUCER() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->map(Ids::MEDIUM_AMETHYST_BUD, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_MEDIUM_BUD) - ->setFacing($in->readBlockFace()); - }); - $this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in)); - $this->mapSlab(Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::MOSSY_COBBLESTONE_SLAB()); - $this->mapStairs(Ids::MOSSY_COBBLESTONE_STAIRS, fn() => Blocks::MOSSY_COBBLESTONE_STAIRS()); - $this->map(Ids::MOSSY_COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_COBBLESTONE_WALL(), $in)); - $this->mapSlab(Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::MOSSY_STONE_BRICK_SLAB()); - $this->mapStairs(Ids::MOSSY_STONE_BRICK_STAIRS, fn() => Blocks::MOSSY_STONE_BRICK_STAIRS()); - $this->map(Ids::MOSSY_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_STONE_BRICK_WALL(), $in)); - $this->mapSlab(Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB, fn() => Blocks::MUD_BRICK_SLAB()); - $this->mapStairs(Ids::MUD_BRICK_STAIRS, fn() => Blocks::MUD_BRICK_STAIRS()); - $this->map(Ids::MUD_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MUD_BRICK_WALL(), $in)); - $this->map(Ids::MUDDY_MANGROVE_ROOTS, function(Reader $in) : Block{ - return Blocks::MUDDY_MANGROVE_ROOTS() - ->setAxis($in->readPillarAxis()); - }); - $this->mapSlab(Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::NETHER_BRICK_SLAB()); - $this->mapStairs(Ids::NETHER_BRICK_STAIRS, fn() => Blocks::NETHER_BRICK_STAIRS()); - $this->map(Ids::NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::NETHER_BRICK_WALL(), $in)); - $this->map(Ids::NETHER_WART, function(Reader $in) : Block{ - return Blocks::NETHER_WART() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 3)); - }); - $this->mapSlab(Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB, fn() => Blocks::STONE_SLAB()); - $this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS()); - $this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis())); - $this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis())); - $this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB()); - $this->map(Ids::PINK_PETALS, function(Reader $in) : Block{ - //Pink petals only uses 0-3, but GROWTH state can go up to 7 - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - return Blocks::PINK_PETALS() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setCount(min($growth + 1, PinkPetals::MAX_COUNT)); - }); - $this->map(Ids::PITCHER_CROP, function(Reader $in) : Block{ - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - $top = $in->readBool(StateNames::UPPER_BLOCK_BIT); - if($growth <= PitcherCrop::MAX_AGE){ - //top pitcher crop with age 0-2 is an invalid state - //only the bottom half should exist in this case - return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth); - } - return Blocks::DOUBLE_PITCHER_CROP() - ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) - ->setTop($top); - }); - $this->map(Ids::PITCHER_PLANT, function(Reader $in) : Block{ - return Blocks::PITCHER_PLANT() - ->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT)); - }); - $this->mapSlab(Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_ANDESITE_SLAB()); - $this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS()); - $this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{ - return Blocks::POLISHED_BASALT() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::POLISHED_BLACKSTONE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::POLISHED_BLACKSTONE_BUTTON(), $in)); - $this->mapSlab(Ids::POLISHED_BLACKSTONE_SLAB, Ids::POLISHED_BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::POLISHED_BLACKSTONE_SLAB()); - $this->map(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::POLISHED_BLACKSTONE_PRESSURE_PLATE(), $in)); - $this->mapStairs(Ids::POLISHED_BLACKSTONE_STAIRS, fn() => Blocks::POLISHED_BLACKSTONE_STAIRS()); - $this->map(Ids::POLISHED_BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_BLACKSTONE_WALL(), $in)); - $this->mapSlab(Ids::POLISHED_BLACKSTONE_BRICK_SLAB, Ids::POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB, fn() => Blocks::POLISHED_BLACKSTONE_BRICK_SLAB()); - $this->mapStairs(Ids::POLISHED_BLACKSTONE_BRICK_STAIRS, fn() => Blocks::POLISHED_BLACKSTONE_BRICK_STAIRS()); - $this->map(Ids::POLISHED_BLACKSTONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_BLACKSTONE_BRICK_WALL(), $in)); - $this->mapSlab(Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DEEPSLATE_SLAB()); - $this->mapStairs(Ids::POLISHED_DEEPSLATE_STAIRS, fn() => Blocks::POLISHED_DEEPSLATE_STAIRS()); - $this->map(Ids::POLISHED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_DEEPSLATE_WALL(), $in)); - $this->mapSlab(Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DIORITE_SLAB()); - $this->mapStairs(Ids::POLISHED_DIORITE_STAIRS, fn() => Blocks::POLISHED_DIORITE_STAIRS()); - $this->mapSlab(Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_GRANITE_SLAB()); - $this->mapStairs(Ids::POLISHED_GRANITE_STAIRS, fn() => Blocks::POLISHED_GRANITE_STAIRS()); - $this->mapSlab(Ids::POLISHED_TUFF_SLAB, Ids::POLISHED_TUFF_DOUBLE_SLAB, fn() => Blocks::POLISHED_TUFF_SLAB()); - $this->mapStairs(Ids::POLISHED_TUFF_STAIRS, fn() => Blocks::POLISHED_TUFF_STAIRS()); - $this->map(Ids::POLISHED_TUFF_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_TUFF_WALL(), $in)); - $this->map(Ids::PORTAL, function(Reader $in) : Block{ - return Blocks::NETHER_PORTAL() - ->setAxis(match($value = $in->readString(StateNames::PORTAL_AXIS)){ - StringValues::PORTAL_AXIS_UNKNOWN => Axis::X, - StringValues::PORTAL_AXIS_X => Axis::X, - StringValues::PORTAL_AXIS_Z => Axis::Z, - default => throw $in->badValueException(StateNames::PORTAL_AXIS, $value), - }); - }); - $this->map(Ids::POTATOES, fn(Reader $in) => Helper::decodeCrops(Blocks::POTATOES(), $in)); - $this->map(Ids::POWERED_COMPARATOR, fn(Reader $in) => Helper::decodeComparator(Blocks::REDSTONE_COMPARATOR(), $in)); - $this->map(Ids::POWERED_REPEATER, fn(Reader $in) => Helper::decodeRepeater(Blocks::REDSTONE_REPEATER(), $in) - ->setPowered(true)); - $this->mapSlab(Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_BRICKS_SLAB()); - $this->mapStairs(Ids::PRISMARINE_BRICKS_STAIRS, fn() => Blocks::PRISMARINE_BRICKS_STAIRS()); - $this->map(Ids::PRISMARINE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::PRISMARINE_WALL(), $in)); - $this->mapSlab(Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_SLAB()); - $this->mapStairs(Ids::PRISMARINE_STAIRS, fn() => Blocks::PRISMARINE_STAIRS()); - $this->map(Ids::PUMPKIN, function(Reader $in) : Block{ - $in->ignored(StateNames::MC_CARDINAL_DIRECTION); //obsolete - return Blocks::PUMPKIN(); - }); - $this->map(Ids::PUMPKIN_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::PUMPKIN_STEM(), $in)); - $this->map(Ids::PURPUR_BLOCK, function(Reader $in) : Block{ - $in->ignored(StateNames::PILLAR_AXIS); //??? - return Blocks::PURPUR(); - }); - $this->map(Ids::PURPUR_PILLAR, fn(Reader $in) => Blocks::PURPUR_PILLAR()->setAxis($in->readPillarAxis())); - $this->mapSlab(Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB, fn() => Blocks::PURPUR_SLAB()); - $this->mapStairs(Ids::PURPUR_STAIRS, fn() => Blocks::PURPUR_STAIRS()); - $this->map(Ids::QUARTZ_BLOCK, function(Reader $in) : Opaque{ - $in->ignored(StateNames::PILLAR_AXIS); - return Blocks::QUARTZ(); - }); - $this->map(Ids::QUARTZ_PILLAR, function(Reader $in) : Block{ - return Blocks::QUARTZ_PILLAR() - ->setAxis($in->readPillarAxis()); - }); - $this->mapSlab(Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB, fn() => Blocks::QUARTZ_SLAB()); - $this->mapStairs(Ids::QUARTZ_STAIRS, fn() => Blocks::QUARTZ_STAIRS()); - $this->map(Ids::RAIL, function(Reader $in) : Block{ - return Blocks::RAIL() - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 9)); - }); - $this->map(Ids::RED_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::RED_MUSHROOM_BLOCK(), $in)); - $this->mapSlab(Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::RED_NETHER_BRICK_SLAB()); - $this->mapStairs(Ids::RED_NETHER_BRICK_STAIRS, fn() => Blocks::RED_NETHER_BRICK_STAIRS()); - $this->map(Ids::RED_NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_NETHER_BRICK_WALL(), $in)); - $this->mapSlab(Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::RED_SANDSTONE_SLAB()); - $this->mapStairs(Ids::RED_SANDSTONE_STAIRS, fn() => Blocks::RED_SANDSTONE_STAIRS()); - $this->map(Ids::RED_SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_SANDSTONE_WALL(), $in)); - $this->map(Ids::REDSTONE_LAMP, function() : Block{ - return Blocks::REDSTONE_LAMP() - ->setPowered(false); - }); - $this->map(Ids::REDSTONE_ORE, function() : Block{ - return Blocks::REDSTONE_ORE() - ->setLit(false); - }); - $this->map(Ids::REDSTONE_TORCH, function(Reader $in) : Block{ - return Blocks::REDSTONE_TORCH() - ->setFacing($in->readTorchFacing()) - ->setLit(true); - }); - $this->map(Ids::REDSTONE_WIRE, function(Reader $in) : Block{ - return Blocks::REDSTONE_WIRE() - ->setOutputSignalStrength($in->readBoundedInt(StateNames::REDSTONE_SIGNAL, 0, 15)); - }); - $this->map(Ids::REEDS, function(Reader $in) : Block{ - return Blocks::SUGARCANE() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); - }); - $this->mapSlab(Ids::RESIN_BRICK_SLAB, Ids::RESIN_BRICK_DOUBLE_SLAB, fn() => Blocks::RESIN_BRICK_SLAB()); - $this->mapStairs(Ids::RESIN_BRICK_STAIRS, fn() => Blocks::RESIN_BRICK_STAIRS()); - $this->map(Ids::RESIN_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RESIN_BRICK_WALL(), $in)); - $this->map(Ids::RESIN_CLUMP, fn(Reader $in) => Blocks::RESIN_CLUMP()->setFaces($in->readFacingFlags())); - $this->map(Ids::RESPAWN_ANCHOR, function(Reader $in) : Block{ - return Blocks::RESPAWN_ANCHOR() - ->setCharges($in->readBoundedInt(StateNames::RESPAWN_ANCHOR_CHARGE, 0, 4)); - }); - $this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB()); - $this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS()); - $this->map(Ids::SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::SANDSTONE_WALL(), $in)); - $this->map(Ids::SEA_PICKLE, function(Reader $in) : Block{ - return Blocks::SEA_PICKLE() - ->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1) - ->setUnderwater(!$in->readBool(StateNames::DEAD_BIT)); - }); - $this->map(Ids::SMOKER, function(Reader $in) : Block{ - return Blocks::SMOKER() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(false); - }); - $this->map(Ids::SMALL_AMETHYST_BUD, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_SMALL_BUD) - ->setFacing($in->readBlockFace()); - }); - $this->map(Ids::SMALL_DRIPLEAF_BLOCK, function(Reader $in) : Block{ - return Blocks::SMALL_DRIPLEAF() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT)); - }); - $this->map(Ids::SMOOTH_QUARTZ, function(Reader $in) : Block{ - $in->ignored(StateNames::PILLAR_AXIS); - return Blocks::SMOOTH_QUARTZ(); - }); - $this->mapSlab(Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB, fn() => Blocks::SMOOTH_QUARTZ_SLAB()); - $this->mapStairs(Ids::SMOOTH_QUARTZ_STAIRS, fn() => Blocks::SMOOTH_QUARTZ_STAIRS()); - $this->mapSlab(Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_RED_SANDSTONE_SLAB()); - $this->mapStairs(Ids::SMOOTH_RED_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_RED_SANDSTONE_STAIRS()); - $this->mapSlab(Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_SANDSTONE_SLAB()); - $this->mapStairs(Ids::SMOOTH_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_SANDSTONE_STAIRS()); - $this->mapSlab(Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_STONE_SLAB()); - $this->map(Ids::SNOW_LAYER, function(Reader $in) : Block{ - $in->ignored(StateNames::COVERED_BIT); //seems to be useless - return Blocks::SNOW_LAYER()->setLayers($in->readBoundedInt(StateNames::HEIGHT, 0, 7) + 1); - }); - $this->map(Ids::SOUL_CAMPFIRE, function(Reader $in) : Block{ - return Blocks::SOUL_CAMPFIRE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(!$in->readBool(StateNames::EXTINGUISHED)); - }); - $this->map(Ids::SOUL_FIRE, function(Reader $in) : Block{ - $in->ignored(StateNames::AGE); //this is useless for soul fire, since it doesn't have the logic associated - return Blocks::SOUL_FIRE(); - }); - $this->map(Ids::SOUL_LANTERN, function(Reader $in) : Block{ - return Blocks::SOUL_LANTERN() - ->setHanging($in->readBool(StateNames::HANGING)); - }); - $this->map(Ids::SOUL_TORCH, function(Reader $in) : Block{ - return Blocks::SOUL_TORCH() - ->setFacing($in->readTorchFacing()); - }); - $this->map(Ids::STANDING_BANNER, function(Reader $in) : Block{ - return Blocks::BANNER() - ->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15)); - }); - $this->mapSlab(Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::STONE_BRICK_SLAB()); - $this->mapStairs(Ids::STONE_BRICK_STAIRS, fn() => Blocks::STONE_BRICK_STAIRS()); - $this->map(Ids::STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::STONE_BRICK_WALL(), $in)); - $this->map(Ids::STONE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::STONE_BUTTON(), $in)); - $this->map(Ids::STONE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::STONE_PRESSURE_PLATE(), $in)); - $this->mapStairs(Ids::STONE_STAIRS, fn() => Blocks::COBBLESTONE_STAIRS()); - $this->map(Ids::STONECUTTER_BLOCK, function(Reader $in) : Block{ - return Blocks::STONECUTTER() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::SWEET_BERRY_BUSH, function(Reader $in) : Block{ - //berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7 - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - return Blocks::SWEET_BERRY_BUSH() - ->setAge(min($growth, SweetBerryBush::STAGE_MATURE)); - }); - $this->map(Ids::TNT, function(Reader $in) : Block{ - return Blocks::TNT() - ->setUnstable($in->readBool(StateNames::EXPLODE_BIT)) - ->setWorksUnderwater(false); - }); - $this->map(Ids::TORCH, function(Reader $in) : Block{ - return Blocks::TORCH() - ->setFacing($in->readTorchFacing()); - }); - $this->map(Ids::TORCHFLOWER_CROP, function(Reader $in) : Block{ - return Blocks::TORCHFLOWER_CROP() - //this property can have values 0-7, but only 0-1 are valid - ->setReady($in->readBoundedInt(StateNames::GROWTH, 0, 7) !== 0); - }); - $this->map(Ids::TRAPPED_CHEST, function(Reader $in) : Block{ - return Blocks::TRAPPED_CHEST() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::TRIP_WIRE, function(Reader $in) : Block{ - return Blocks::TRIPWIRE() - ->setConnected($in->readBool(StateNames::ATTACHED_BIT)) - ->setDisarmed($in->readBool(StateNames::DISARMED_BIT)) - ->setSuspended($in->readBool(StateNames::SUSPENDED_BIT)) - ->setTriggered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::TRIPWIRE_HOOK, function(Reader $in) : Block{ - return Blocks::TRIPWIRE_HOOK() - ->setConnected($in->readBool(StateNames::ATTACHED_BIT)) - ->setFacing($in->readLegacyHorizontalFacing()) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->mapSlab(Ids::TUFF_BRICK_SLAB, Ids::TUFF_BRICK_DOUBLE_SLAB, fn() => Blocks::TUFF_BRICK_SLAB()); - $this->mapStairs(Ids::TUFF_BRICK_STAIRS, fn() => Blocks::TUFF_BRICK_STAIRS()); - $this->map(Ids::TUFF_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::TUFF_BRICK_WALL(), $in)); - $this->mapSlab(Ids::TUFF_SLAB, Ids::TUFF_DOUBLE_SLAB, fn() => Blocks::TUFF_SLAB()); - $this->mapStairs(Ids::TUFF_STAIRS, fn() => Blocks::TUFF_STAIRS()); - $this->map(Ids::TUFF_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::TUFF_WALL(), $in)); - $this->map(Ids::TWISTING_VINES, function(Reader $in) : Block{ - return Blocks::TWISTING_VINES() - ->setAge($in->readBoundedInt(StateNames::TWISTING_VINES_AGE, 0, 25)); - }); - $this->map(Ids::UNDERWATER_TNT, function(Reader $in) : Block{ - return Blocks::TNT() - ->setUnstable($in->readBool(StateNames::EXPLODE_BIT)) - ->setWorksUnderwater(true); - }); - $this->map(Ids::UNDERWATER_TORCH, function(Reader $in) : Block{ - return Blocks::UNDERWATER_TORCH() - ->setFacing($in->readTorchFacing()); - }); - $this->map(Ids::UNLIT_REDSTONE_TORCH, function(Reader $in) : Block{ - return Blocks::REDSTONE_TORCH() - ->setFacing($in->readTorchFacing()) - ->setLit(false); - }); - $this->map(Ids::UNPOWERED_COMPARATOR, fn(Reader $in) => Helper::decodeComparator(Blocks::REDSTONE_COMPARATOR(), $in)); - $this->map(Ids::UNPOWERED_REPEATER, fn(Reader $in) => Helper::decodeRepeater(Blocks::REDSTONE_REPEATER(), $in) - ->setPowered(false)); - $this->map(Ids::VERDANT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::VERDANT)->setAxis($in->readPillarAxis())); - $this->map(Ids::VINE, function(Reader $in) : Block{ - $vineDirectionFlags = $in->readBoundedInt(StateNames::VINE_DIRECTION_BITS, 0, 15); - return Blocks::VINES() - ->setFace(Facing::NORTH, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_NORTH) !== 0) - ->setFace(Facing::SOUTH, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_SOUTH) !== 0) - ->setFace(Facing::WEST, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_WEST) !== 0) - ->setFace(Facing::EAST, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_EAST) !== 0); - }); - $this->map(Ids::WALL_BANNER, function(Reader $in) : Block{ - return Blocks::WALL_BANNER() - ->setFacing($in->readHorizontalFacing()); - }); - $this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in)); - - $this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{ - return Blocks::WEEPING_VINES() - ->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25)); - }); - $this->map(Ids::WHEAT, fn(Reader $in) => Helper::decodeCrops(Blocks::WHEAT(), $in)); - } - /** @throws BlockStateDeserializeException */ public function deserializeBlock(BlockStateData $blockStateData) : Block{ $id = $blockStateData->getName(); diff --git a/src/data/bedrock/block/convert/BlockStateWriter.php b/src/data/bedrock/block/convert/BlockStateWriter.php index 63af92d10..0119bd02e 100644 --- a/src/data/bedrock/block/convert/BlockStateWriter.php +++ b/src/data/bedrock/block/convert/BlockStateWriter.php @@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateSerializeException; use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; +use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\nbt\tag\ByteTag; @@ -73,35 +76,47 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ - public function writeFacingDirection(int $value) : self{ - $this->writeInt(BlockStateNames::FACING_DIRECTION, match($value){ - Facing::DOWN => 0, - Facing::UP => 1, - Facing::NORTH => 2, - Facing::SOUTH => 3, - Facing::WEST => 4, - Facing::EAST => 5, - default => throw new BlockStateSerializeException("Invalid Facing $value") - }); - return $this; - } - - /** @return $this */ - public function writeBlockFace(int $value) : self{ - $this->writeString(BlockStateNames::MC_BLOCK_FACE, match($value){ - Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN, - Facing::UP => StringValues::MC_BLOCK_FACE_UP, - Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH, - Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH, - Facing::WEST => StringValues::MC_BLOCK_FACE_WEST, - Facing::EAST => StringValues::MC_BLOCK_FACE_EAST, - default => throw new BlockStateSerializeException("Invalid Facing $value") - }); + /** + * @deprecated + * @phpstan-param IntFromRawStateMap $map + * @return $this + */ + public function mapIntToString(string $name, IntFromRawStateMap $map, int $value) : self{ + $raw = $map->valueToRaw($value); + $this->writeString($name, $raw); return $this; } /** + * @deprecated + * @phpstan-param IntFromRawStateMap $map + * @return $this + */ + public function mapIntToInt(string $name, IntFromRawStateMap $map, int $value) : self{ + $raw = $map->valueToRaw($value); + $this->writeInt($name, $raw); + return $this; + } + + /** + * @deprecated + * @return $this + */ + public function writeFacingDirection(int $value) : self{ + return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing, $value); + } + + /** + * @deprecated + * @return $this + */ + public function writeBlockFace(int $value) : self{ + $this->mapIntToString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace, $value); + return $this; + } + + /** + * @deprecated * @param int[] $faces * @phpstan-param array $faces * @return $this @@ -123,86 +138,69 @@ final class BlockStateWriter{ return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeEndRodFacingDirection(int $value) : self{ //end rods are stupid in bedrock and have everything except up/down the wrong way round return $this->writeFacingDirection(Facing::axis($value) !== Axis::Y ? Facing::opposite($value) : $value); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeHorizontalFacing(int $value) : self{ - if($value === Facing::UP || $value === Facing::DOWN){ - throw new BlockStateSerializeException("Y-axis facing is not allowed"); - } - - return $this->writeFacingDirection($value); - } - - /** @return $this */ - public function writeWeirdoHorizontalFacing(int $value) : self{ - $this->writeInt(BlockStateNames::WEIRDO_DIRECTION, match($value){ - Facing::EAST => 0, - Facing::WEST => 1, - Facing::SOUTH => 2, - Facing::NORTH => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); - return $this; - } - - /** @return $this */ - public function writeLegacyHorizontalFacing(int $value) : self{ - $this->writeInt(BlockStateNames::DIRECTION, match($value){ - Facing::SOUTH => 0, - Facing::WEST => 1, - Facing::NORTH => 2, - Facing::EAST => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); - return $this; + return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic, $value); } /** + * @deprecated + * @return $this + */ + public function writeWeirdoHorizontalFacing(int $value) : self{ + return $this->mapIntToInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value); + } + + /** + * @deprecated + * @return $this + */ + public function writeLegacyHorizontalFacing(int $value) : self{ + return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE, $value); + } + + /** + * @deprecated * This is for trapdoors, because Mojang botched the conversion in 1.13 * @return $this */ public function write5MinusHorizontalFacing(int $value) : self{ - return $this->writeInt(BlockStateNames::DIRECTION, match($value){ - Facing::EAST => 0, - Facing::WEST => 1, - Facing::SOUTH => 2, - Facing::NORTH => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); + return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value); } /** + * @deprecated * Used by pumpkins as of 1.20.0.23 beta * @return $this */ public function writeCardinalHorizontalFacing(int $value) : self{ - return $this->writeString(BlockStateNames::MC_CARDINAL_DIRECTION, match($value){ - Facing::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH, - Facing::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST, - Facing::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH, - Facing::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); + return $this->mapIntToString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection, $value); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeCoralFacing(int $value) : self{ - $this->writeInt(BlockStateNames::CORAL_DIRECTION, match($value){ - Facing::WEST => 0, - Facing::EAST => 1, - Facing::NORTH => 2, - Facing::SOUTH => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); - return $this; + return $this->mapIntToInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, $value); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeFacingWithoutDown(int $value) : self{ if($value === Facing::DOWN){ throw new BlockStateSerializeException("Invalid facing DOWN"); @@ -211,7 +209,10 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeFacingWithoutUp(int $value) : self{ if($value === Facing::UP){ throw new BlockStateSerializeException("Invalid facing UP"); @@ -220,18 +221,19 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writePillarAxis(int $axis) : self{ - $this->writeString(BlockStateNames::PILLAR_AXIS, match($axis){ - Axis::X => StringValues::PILLAR_AXIS_X, - Axis::Y => StringValues::PILLAR_AXIS_Y, - Axis::Z => StringValues::PILLAR_AXIS_Z, - default => throw new BlockStateSerializeException("Invalid axis $axis") - }); + $this->mapIntToString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis, $axis); return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeSlabPosition(SlabType $slabType) : self{ $this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){ SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP, @@ -241,32 +243,27 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeTorchFacing(int $facing) : self{ - //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) - $this->writeString(BlockStateNames::TORCH_FACING_DIRECTION, match($facing){ - Facing::UP => StringValues::TORCH_FACING_DIRECTION_TOP, - Facing::SOUTH => StringValues::TORCH_FACING_DIRECTION_NORTH, - Facing::NORTH => StringValues::TORCH_FACING_DIRECTION_SOUTH, - Facing::EAST => StringValues::TORCH_FACING_DIRECTION_WEST, - Facing::WEST => StringValues::TORCH_FACING_DIRECTION_EAST, - default => throw new BlockStateSerializeException("Invalid Torch facing $facing") - }); + $this->mapIntToString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing, $facing); return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{ - $this->writeString(BlockStateNames::ATTACHMENT, match($attachmentType){ - BellAttachmentType::FLOOR => StringValues::ATTACHMENT_STANDING, - BellAttachmentType::CEILING => StringValues::ATTACHMENT_HANGING, - BellAttachmentType::ONE_WALL => StringValues::ATTACHMENT_SIDE, - BellAttachmentType::TWO_WALLS => StringValues::ATTACHMENT_MULTIPLE, - }); - return $this; + return $this->writeUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, $attachmentType); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{ $this->writeString($name, match($wallConnectionType){ null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE, @@ -276,6 +273,21 @@ final class BlockStateWriter{ return $this; } + /** + * @deprecated + * @phpstan-template TEnum of \UnitEnum + * @phpstan-param EnumFromRawStateMap $map + * @phpstan-param TEnum $case + * + * @return $this + */ + public function writeUnitEnum(string $name, EnumFromRawStateMap $map, \UnitEnum $case) : self{ + $value = $map->valueToRaw($case); + $this->writeString($name, $value); + + return $this; + } + public function getBlockStateData() : BlockStateData{ return BlockStateData::current($this->id, $this->states); } diff --git a/src/data/bedrock/block/convert/FlattenedIdModel.php b/src/data/bedrock/block/convert/FlattenedIdModel.php new file mode 100644 index 000000000..50bf5cdd9 --- /dev/null +++ b/src/data/bedrock/block/convert/FlattenedIdModel.php @@ -0,0 +1,107 @@ +> + */ + private array $idComponents = []; + + /** + * @var Property[] + * @phpstan-var list> + */ + private array $properties = []; + + /** + * @phpstan-param TBlock $block + */ + private function __construct( + private Block $block + ){} + + /** + * @phpstan-template TBlock_ of Block + * @phpstan-param TBlock_ $block + * @return self + */ + public static function create(Block $block) : self{ + /** @phpstan-var self $result */ + $result = new self($block); + return $result; + } + + /** @phpstan-return TBlock */ + public function getBlock() : Block{ return $this->block; } + + /** + * @return string[]|StringProperty[] + * @phpstan-return list> + */ + public function getIdComponents() : array{ return $this->idComponents; } + + /** + * @return Property[] + * @phpstan-return list> + */ + public function getProperties() : array{ return $this->properties; } + + /** + * @param string[]|StringProperty[] $components + * @phpstan-param non-empty-list> $components + * @return $this + * @phpstan-this-out self + */ + public function idComponents(array $components) : self{ + $this->idComponents = $components; + return $this; + } + + /** + * @param Property[] $properties + * @phpstan-param non-empty-list> $properties + * @return $this + * @phpstan-this-out self + */ + public function properties(array $properties) : self{ + $this->properties = $properties; + return $this; + } +} diff --git a/src/data/bedrock/block/convert/Model.php b/src/data/bedrock/block/convert/Model.php new file mode 100644 index 000000000..3474a8932 --- /dev/null +++ b/src/data/bedrock/block/convert/Model.php @@ -0,0 +1,82 @@ +> + */ + private array $properties = []; + + /** + * @phpstan-param TBlock $block + */ + private function __construct( + private Block $block, + private string $id + ){} + + /** @phpstan-return TBlock */ + public function getBlock() : Block{ return $this->block; } + + public function getId() : string{ return $this->id; } + + /** + * @return Property[] + * @phpstan-return list> + */ + public function getProperties() : array{ return $this->properties; } + + /** + * @phpstan-template TBlock_ of Block + * @phpstan-param TBlock_ $block + * @phpstan-return self + */ + public static function create(Block $block, string $id) : self{ + return new self($block, $id); + } + + /** + * @param Property[] $properties + * @phpstan-param list> $properties + * @phpstan-return $this + */ + public function properties(array $properties) : self{ + $this->properties = $properties; + return $this; + } +} diff --git a/src/data/bedrock/block/convert/StringEnumMap.php b/src/data/bedrock/block/convert/StringEnumMap.php deleted file mode 100644 index 6171c0e71..000000000 --- a/src/data/bedrock/block/convert/StringEnumMap.php +++ /dev/null @@ -1,78 +0,0 @@ - - */ - private array $enumToValue = []; - - /** - * @var \UnitEnum[] - * @phpstan-var array - */ - private array $valueToEnum = []; - - /** - * @phpstan-param class-string $class - * @phpstan-param \Closure(TEnum) : string $mapper - */ - public function __construct( - private string $class, - \Closure $mapper - ){ - foreach($class::cases() as $case){ - $string = $mapper($case); - $this->valueToEnum[$string] = $case; - $this->enumToValue[spl_object_id($case)] = $string; - } - } - - /** - * @phpstan-param TEnum $enum - */ - public function enumToValue(\UnitEnum $enum) : string{ - return $this->enumToValue[spl_object_id($enum)]; - } - - public function valueToEnum(string $string) : ?\UnitEnum{ - return $this->valueToEnum[$string] ?? throw new BlockStateDeserializeException("No $this->class enum mapping for \"$string\""); - } - - /** - * @return \UnitEnum[] - * @phpstan-return array - */ - public function getValueToEnum() : array{ - return $this->valueToEnum; - } -} diff --git a/src/data/bedrock/block/convert/ValueMappings.php b/src/data/bedrock/block/convert/ValueMappings.php deleted file mode 100644 index df57d6f53..000000000 --- a/src/data/bedrock/block/convert/ValueMappings.php +++ /dev/null @@ -1,83 +0,0 @@ -, StringEnumMap> - */ - private array $enumMappings = []; - - public function __construct(){ - $this->addEnum(DyeColor::class, fn(DyeColor $case) => match ($case) { - DyeColor::BLACK => "black", - DyeColor::BLUE => "blue", - DyeColor::BROWN => "brown", - DyeColor::CYAN => "cyan", - DyeColor::GRAY => "gray", - DyeColor::GREEN => "green", - DyeColor::LIGHT_BLUE => "light_blue", - DyeColor::LIGHT_GRAY => "light_gray", - DyeColor::LIME => "lime", - DyeColor::MAGENTA => "magenta", - DyeColor::ORANGE => "orange", - DyeColor::PINK => "pink", - DyeColor::PURPLE => "purple", - DyeColor::RED => "red", - DyeColor::WHITE => "white", - DyeColor::YELLOW => "yellow" - }); - } - - /** - * @phpstan-template TEnum of \UnitEnum - * @phpstan-param class-string $class - * @phpstan-param \Closure(TEnum): string $mapper - */ - private function addEnum(string $class, \Closure $mapper) : void{ - $this->enumMappings[$class] = new StringEnumMap($class, $mapper); - } - - /** - * @phpstan-template TEnum of \UnitEnum - * @phpstan-param class-string $class - * @phpstan-return StringEnumMap - */ - public function getEnumMap(string $class) : StringEnumMap{ - if(!isset($this->enumMappings[$class])){ - throw new \InvalidArgumentException("No enum mapping found for class: $class"); - } - /** - * @phpstan-var StringEnumMap $map - */ - $map = $this->enumMappings[$class]; - return $map; - } -} diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php new file mode 100644 index 000000000..16ae1e244 --- /dev/null +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -0,0 +1,1561 @@ +mapSimple(Blocks::AIR(), Ids::AIR); + $reg->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK); + $reg->mapSimple(Blocks::ANCIENT_DEBRIS(), Ids::ANCIENT_DEBRIS); + $reg->mapSimple(Blocks::ANDESITE(), Ids::ANDESITE); + $reg->mapSimple(Blocks::BARRIER(), Ids::BARRIER); + $reg->mapSimple(Blocks::BEACON(), Ids::BEACON); + $reg->mapSimple(Blocks::BLACKSTONE(), Ids::BLACKSTONE); + $reg->mapSimple(Blocks::BLUE_ICE(), Ids::BLUE_ICE); + $reg->mapSimple(Blocks::BOOKSHELF(), Ids::BOOKSHELF); + $reg->mapSimple(Blocks::BRICKS(), Ids::BRICK_BLOCK); + $reg->mapSimple(Blocks::BROWN_MUSHROOM(), Ids::BROWN_MUSHROOM); + $reg->mapSimple(Blocks::BUDDING_AMETHYST(), Ids::BUDDING_AMETHYST); + $reg->mapSimple(Blocks::CALCITE(), Ids::CALCITE); + $reg->mapSimple(Blocks::CARTOGRAPHY_TABLE(), Ids::CARTOGRAPHY_TABLE); + $reg->mapSimple(Blocks::CHEMICAL_HEAT(), Ids::CHEMICAL_HEAT); + $reg->mapSimple(Blocks::CHISELED_DEEPSLATE(), Ids::CHISELED_DEEPSLATE); + $reg->mapSimple(Blocks::CHISELED_NETHER_BRICKS(), Ids::CHISELED_NETHER_BRICKS); + $reg->mapSimple(Blocks::CHISELED_POLISHED_BLACKSTONE(), Ids::CHISELED_POLISHED_BLACKSTONE); + $reg->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE); + $reg->mapSimple(Blocks::CHISELED_RESIN_BRICKS(), Ids::CHISELED_RESIN_BRICKS); + $reg->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE); + $reg->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS); + $reg->mapSimple(Blocks::CHISELED_TUFF(), Ids::CHISELED_TUFF); + $reg->mapSimple(Blocks::CHISELED_TUFF_BRICKS(), Ids::CHISELED_TUFF_BRICKS); + $reg->mapSimple(Blocks::CHORUS_PLANT(), Ids::CHORUS_PLANT); + $reg->mapSimple(Blocks::CLAY(), Ids::CLAY); + $reg->mapSimple(Blocks::COAL(), Ids::COAL_BLOCK); + $reg->mapSimple(Blocks::COAL_ORE(), Ids::COAL_ORE); + $reg->mapSimple(Blocks::COBBLED_DEEPSLATE(), Ids::COBBLED_DEEPSLATE); + $reg->mapSimple(Blocks::COBBLESTONE(), Ids::COBBLESTONE); + $reg->mapSimple(Blocks::COBWEB(), Ids::WEB); + $reg->mapSimple(Blocks::COPPER_ORE(), Ids::COPPER_ORE); + $reg->mapSimple(Blocks::CRACKED_DEEPSLATE_BRICKS(), Ids::CRACKED_DEEPSLATE_BRICKS); + $reg->mapSimple(Blocks::CRACKED_DEEPSLATE_TILES(), Ids::CRACKED_DEEPSLATE_TILES); + $reg->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS); + $reg->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS); + $reg->mapSimple(Blocks::CRACKED_STONE_BRICKS(), Ids::CRACKED_STONE_BRICKS); + $reg->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE); + $reg->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS); + $reg->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN); + $reg->mapSimple(Blocks::DANDELION(), Ids::DANDELION); + $reg->mapSimple(Blocks::CUT_RED_SANDSTONE(), Ids::CUT_RED_SANDSTONE); + $reg->mapSimple(Blocks::CUT_SANDSTONE(), Ids::CUT_SANDSTONE); + $reg->mapSimple(Blocks::DARK_PRISMARINE(), Ids::DARK_PRISMARINE); + $reg->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH); + $reg->mapSimple(Blocks::DEEPSLATE_BRICKS(), Ids::DEEPSLATE_BRICKS); + $reg->mapSimple(Blocks::DEEPSLATE_COAL_ORE(), Ids::DEEPSLATE_COAL_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_COPPER_ORE(), Ids::DEEPSLATE_COPPER_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_DIAMOND_ORE(), Ids::DEEPSLATE_DIAMOND_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_EMERALD_ORE(), Ids::DEEPSLATE_EMERALD_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_GOLD_ORE(), Ids::DEEPSLATE_GOLD_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_IRON_ORE(), Ids::DEEPSLATE_IRON_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_LAPIS_LAZULI_ORE(), Ids::DEEPSLATE_LAPIS_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_TILES(), Ids::DEEPSLATE_TILES); + $reg->mapSimple(Blocks::DIAMOND(), Ids::DIAMOND_BLOCK); + $reg->mapSimple(Blocks::DIAMOND_ORE(), Ids::DIAMOND_ORE); + $reg->mapSimple(Blocks::DIORITE(), Ids::DIORITE); + $reg->mapSimple(Blocks::DRAGON_EGG(), Ids::DRAGON_EGG); + $reg->mapSimple(Blocks::DRIED_KELP(), Ids::DRIED_KELP_BLOCK); + $reg->mapSimple(Blocks::ELEMENT_ACTINIUM(), Ids::ELEMENT_89); + $reg->mapSimple(Blocks::ELEMENT_ALUMINUM(), Ids::ELEMENT_13); + $reg->mapSimple(Blocks::ELEMENT_AMERICIUM(), Ids::ELEMENT_95); + $reg->mapSimple(Blocks::ELEMENT_ANTIMONY(), Ids::ELEMENT_51); + $reg->mapSimple(Blocks::ELEMENT_ARGON(), Ids::ELEMENT_18); + $reg->mapSimple(Blocks::ELEMENT_ARSENIC(), Ids::ELEMENT_33); + $reg->mapSimple(Blocks::ELEMENT_ASTATINE(), Ids::ELEMENT_85); + $reg->mapSimple(Blocks::ELEMENT_BARIUM(), Ids::ELEMENT_56); + $reg->mapSimple(Blocks::ELEMENT_BERKELIUM(), Ids::ELEMENT_97); + $reg->mapSimple(Blocks::ELEMENT_BERYLLIUM(), Ids::ELEMENT_4); + $reg->mapSimple(Blocks::ELEMENT_BISMUTH(), Ids::ELEMENT_83); + $reg->mapSimple(Blocks::ELEMENT_BOHRIUM(), Ids::ELEMENT_107); + $reg->mapSimple(Blocks::ELEMENT_BORON(), Ids::ELEMENT_5); + $reg->mapSimple(Blocks::ELEMENT_BROMINE(), Ids::ELEMENT_35); + $reg->mapSimple(Blocks::ELEMENT_CADMIUM(), Ids::ELEMENT_48); + $reg->mapSimple(Blocks::ELEMENT_CALCIUM(), Ids::ELEMENT_20); + $reg->mapSimple(Blocks::ELEMENT_CALIFORNIUM(), Ids::ELEMENT_98); + $reg->mapSimple(Blocks::ELEMENT_CARBON(), Ids::ELEMENT_6); + $reg->mapSimple(Blocks::ELEMENT_CERIUM(), Ids::ELEMENT_58); + $reg->mapSimple(Blocks::ELEMENT_CESIUM(), Ids::ELEMENT_55); + $reg->mapSimple(Blocks::ELEMENT_CHLORINE(), Ids::ELEMENT_17); + $reg->mapSimple(Blocks::ELEMENT_CHROMIUM(), Ids::ELEMENT_24); + $reg->mapSimple(Blocks::ELEMENT_COBALT(), Ids::ELEMENT_27); + $reg->mapSimple(Blocks::ELEMENT_COPERNICIUM(), Ids::ELEMENT_112); + $reg->mapSimple(Blocks::ELEMENT_COPPER(), Ids::ELEMENT_29); + $reg->mapSimple(Blocks::ELEMENT_CURIUM(), Ids::ELEMENT_96); + $reg->mapSimple(Blocks::ELEMENT_DARMSTADTIUM(), Ids::ELEMENT_110); + $reg->mapSimple(Blocks::ELEMENT_DUBNIUM(), Ids::ELEMENT_105); + $reg->mapSimple(Blocks::ELEMENT_DYSPROSIUM(), Ids::ELEMENT_66); + $reg->mapSimple(Blocks::ELEMENT_EINSTEINIUM(), Ids::ELEMENT_99); + $reg->mapSimple(Blocks::ELEMENT_ERBIUM(), Ids::ELEMENT_68); + $reg->mapSimple(Blocks::ELEMENT_EUROPIUM(), Ids::ELEMENT_63); + $reg->mapSimple(Blocks::ELEMENT_FERMIUM(), Ids::ELEMENT_100); + $reg->mapSimple(Blocks::ELEMENT_FLEROVIUM(), Ids::ELEMENT_114); + $reg->mapSimple(Blocks::ELEMENT_FLUORINE(), Ids::ELEMENT_9); + $reg->mapSimple(Blocks::ELEMENT_FRANCIUM(), Ids::ELEMENT_87); + $reg->mapSimple(Blocks::ELEMENT_GADOLINIUM(), Ids::ELEMENT_64); + $reg->mapSimple(Blocks::ELEMENT_GALLIUM(), Ids::ELEMENT_31); + $reg->mapSimple(Blocks::ELEMENT_GERMANIUM(), Ids::ELEMENT_32); + $reg->mapSimple(Blocks::ELEMENT_GOLD(), Ids::ELEMENT_79); + $reg->mapSimple(Blocks::ELEMENT_HAFNIUM(), Ids::ELEMENT_72); + $reg->mapSimple(Blocks::ELEMENT_HASSIUM(), Ids::ELEMENT_108); + $reg->mapSimple(Blocks::ELEMENT_HELIUM(), Ids::ELEMENT_2); + $reg->mapSimple(Blocks::ELEMENT_HOLMIUM(), Ids::ELEMENT_67); + $reg->mapSimple(Blocks::ELEMENT_HYDROGEN(), Ids::ELEMENT_1); + $reg->mapSimple(Blocks::ELEMENT_INDIUM(), Ids::ELEMENT_49); + $reg->mapSimple(Blocks::ELEMENT_IODINE(), Ids::ELEMENT_53); + $reg->mapSimple(Blocks::ELEMENT_IRIDIUM(), Ids::ELEMENT_77); + $reg->mapSimple(Blocks::ELEMENT_IRON(), Ids::ELEMENT_26); + $reg->mapSimple(Blocks::ELEMENT_KRYPTON(), Ids::ELEMENT_36); + $reg->mapSimple(Blocks::ELEMENT_LANTHANUM(), Ids::ELEMENT_57); + $reg->mapSimple(Blocks::ELEMENT_LAWRENCIUM(), Ids::ELEMENT_103); + $reg->mapSimple(Blocks::ELEMENT_LEAD(), Ids::ELEMENT_82); + $reg->mapSimple(Blocks::ELEMENT_LITHIUM(), Ids::ELEMENT_3); + $reg->mapSimple(Blocks::ELEMENT_LIVERMORIUM(), Ids::ELEMENT_116); + $reg->mapSimple(Blocks::ELEMENT_LUTETIUM(), Ids::ELEMENT_71); + $reg->mapSimple(Blocks::ELEMENT_MAGNESIUM(), Ids::ELEMENT_12); + $reg->mapSimple(Blocks::ELEMENT_MANGANESE(), Ids::ELEMENT_25); + $reg->mapSimple(Blocks::ELEMENT_MEITNERIUM(), Ids::ELEMENT_109); + $reg->mapSimple(Blocks::ELEMENT_MENDELEVIUM(), Ids::ELEMENT_101); + $reg->mapSimple(Blocks::ELEMENT_MERCURY(), Ids::ELEMENT_80); + $reg->mapSimple(Blocks::ELEMENT_MOLYBDENUM(), Ids::ELEMENT_42); + $reg->mapSimple(Blocks::ELEMENT_MOSCOVIUM(), Ids::ELEMENT_115); + $reg->mapSimple(Blocks::ELEMENT_NEODYMIUM(), Ids::ELEMENT_60); + $reg->mapSimple(Blocks::ELEMENT_NEON(), Ids::ELEMENT_10); + $reg->mapSimple(Blocks::ELEMENT_NEPTUNIUM(), Ids::ELEMENT_93); + $reg->mapSimple(Blocks::ELEMENT_NICKEL(), Ids::ELEMENT_28); + $reg->mapSimple(Blocks::ELEMENT_NIHONIUM(), Ids::ELEMENT_113); + $reg->mapSimple(Blocks::ELEMENT_NIOBIUM(), Ids::ELEMENT_41); + $reg->mapSimple(Blocks::ELEMENT_NITROGEN(), Ids::ELEMENT_7); + $reg->mapSimple(Blocks::ELEMENT_NOBELIUM(), Ids::ELEMENT_102); + $reg->mapSimple(Blocks::ELEMENT_OGANESSON(), Ids::ELEMENT_118); + $reg->mapSimple(Blocks::ELEMENT_OSMIUM(), Ids::ELEMENT_76); + $reg->mapSimple(Blocks::ELEMENT_OXYGEN(), Ids::ELEMENT_8); + $reg->mapSimple(Blocks::ELEMENT_PALLADIUM(), Ids::ELEMENT_46); + $reg->mapSimple(Blocks::ELEMENT_PHOSPHORUS(), Ids::ELEMENT_15); + $reg->mapSimple(Blocks::ELEMENT_PLATINUM(), Ids::ELEMENT_78); + $reg->mapSimple(Blocks::ELEMENT_PLUTONIUM(), Ids::ELEMENT_94); + $reg->mapSimple(Blocks::ELEMENT_POLONIUM(), Ids::ELEMENT_84); + $reg->mapSimple(Blocks::ELEMENT_POTASSIUM(), Ids::ELEMENT_19); + $reg->mapSimple(Blocks::ELEMENT_PRASEODYMIUM(), Ids::ELEMENT_59); + $reg->mapSimple(Blocks::ELEMENT_PROMETHIUM(), Ids::ELEMENT_61); + $reg->mapSimple(Blocks::ELEMENT_PROTACTINIUM(), Ids::ELEMENT_91); + $reg->mapSimple(Blocks::ELEMENT_RADIUM(), Ids::ELEMENT_88); + $reg->mapSimple(Blocks::ELEMENT_RADON(), Ids::ELEMENT_86); + $reg->mapSimple(Blocks::ELEMENT_RHENIUM(), Ids::ELEMENT_75); + $reg->mapSimple(Blocks::ELEMENT_RHODIUM(), Ids::ELEMENT_45); + $reg->mapSimple(Blocks::ELEMENT_ROENTGENIUM(), Ids::ELEMENT_111); + $reg->mapSimple(Blocks::ELEMENT_RUBIDIUM(), Ids::ELEMENT_37); + $reg->mapSimple(Blocks::ELEMENT_RUTHENIUM(), Ids::ELEMENT_44); + $reg->mapSimple(Blocks::ELEMENT_RUTHERFORDIUM(), Ids::ELEMENT_104); + $reg->mapSimple(Blocks::ELEMENT_SAMARIUM(), Ids::ELEMENT_62); + $reg->mapSimple(Blocks::ELEMENT_SCANDIUM(), Ids::ELEMENT_21); + $reg->mapSimple(Blocks::ELEMENT_SEABORGIUM(), Ids::ELEMENT_106); + $reg->mapSimple(Blocks::ELEMENT_SELENIUM(), Ids::ELEMENT_34); + $reg->mapSimple(Blocks::ELEMENT_SILICON(), Ids::ELEMENT_14); + $reg->mapSimple(Blocks::ELEMENT_SILVER(), Ids::ELEMENT_47); + $reg->mapSimple(Blocks::ELEMENT_SODIUM(), Ids::ELEMENT_11); + $reg->mapSimple(Blocks::ELEMENT_STRONTIUM(), Ids::ELEMENT_38); + $reg->mapSimple(Blocks::ELEMENT_SULFUR(), Ids::ELEMENT_16); + $reg->mapSimple(Blocks::ELEMENT_TANTALUM(), Ids::ELEMENT_73); + $reg->mapSimple(Blocks::ELEMENT_TECHNETIUM(), Ids::ELEMENT_43); + $reg->mapSimple(Blocks::ELEMENT_TELLURIUM(), Ids::ELEMENT_52); + $reg->mapSimple(Blocks::ELEMENT_TENNESSINE(), Ids::ELEMENT_117); + $reg->mapSimple(Blocks::ELEMENT_TERBIUM(), Ids::ELEMENT_65); + $reg->mapSimple(Blocks::ELEMENT_THALLIUM(), Ids::ELEMENT_81); + $reg->mapSimple(Blocks::ELEMENT_THORIUM(), Ids::ELEMENT_90); + $reg->mapSimple(Blocks::ELEMENT_THULIUM(), Ids::ELEMENT_69); + $reg->mapSimple(Blocks::ELEMENT_TIN(), Ids::ELEMENT_50); + $reg->mapSimple(Blocks::ELEMENT_TITANIUM(), Ids::ELEMENT_22); + $reg->mapSimple(Blocks::ELEMENT_TUNGSTEN(), Ids::ELEMENT_74); + $reg->mapSimple(Blocks::ELEMENT_URANIUM(), Ids::ELEMENT_92); + $reg->mapSimple(Blocks::ELEMENT_VANADIUM(), Ids::ELEMENT_23); + $reg->mapSimple(Blocks::ELEMENT_XENON(), Ids::ELEMENT_54); + $reg->mapSimple(Blocks::ELEMENT_YTTERBIUM(), Ids::ELEMENT_70); + $reg->mapSimple(Blocks::ELEMENT_YTTRIUM(), Ids::ELEMENT_39); + $reg->mapSimple(Blocks::ELEMENT_ZERO(), Ids::ELEMENT_0); + $reg->mapSimple(Blocks::ELEMENT_ZINC(), Ids::ELEMENT_30); + $reg->mapSimple(Blocks::ELEMENT_ZIRCONIUM(), Ids::ELEMENT_40); + $reg->mapSimple(Blocks::EMERALD(), Ids::EMERALD_BLOCK); + $reg->mapSimple(Blocks::EMERALD_ORE(), Ids::EMERALD_ORE); + $reg->mapSimple(Blocks::ENCHANTING_TABLE(), Ids::ENCHANTING_TABLE); + $reg->mapSimple(Blocks::END_STONE(), Ids::END_STONE); + $reg->mapSimple(Blocks::END_STONE_BRICKS(), Ids::END_BRICKS); + $reg->mapSimple(Blocks::FERN(), Ids::FERN); + $reg->mapSimple(Blocks::FLETCHING_TABLE(), Ids::FLETCHING_TABLE); + $reg->mapSimple(Blocks::GILDED_BLACKSTONE(), Ids::GILDED_BLACKSTONE); + $reg->mapSimple(Blocks::GLASS(), Ids::GLASS); + $reg->mapSimple(Blocks::GLASS_PANE(), Ids::GLASS_PANE); + $reg->mapSimple(Blocks::GLOWING_OBSIDIAN(), Ids::GLOWINGOBSIDIAN); + $reg->mapSimple(Blocks::GLOWSTONE(), Ids::GLOWSTONE); + $reg->mapSimple(Blocks::GOLD(), Ids::GOLD_BLOCK); + $reg->mapSimple(Blocks::GOLD_ORE(), Ids::GOLD_ORE); + $reg->mapSimple(Blocks::GRANITE(), Ids::GRANITE); + $reg->mapSimple(Blocks::GRASS(), Ids::GRASS_BLOCK); + $reg->mapSimple(Blocks::GRASS_PATH(), Ids::GRASS_PATH); + $reg->mapSimple(Blocks::GRAVEL(), Ids::GRAVEL); + $reg->mapSimple(Blocks::HANGING_ROOTS(), Ids::HANGING_ROOTS); + $reg->mapSimple(Blocks::HARDENED_CLAY(), Ids::HARDENED_CLAY); + $reg->mapSimple(Blocks::HARDENED_GLASS(), Ids::HARD_GLASS); + $reg->mapSimple(Blocks::HARDENED_GLASS_PANE(), Ids::HARD_GLASS_PANE); + $reg->mapSimple(Blocks::HONEYCOMB(), Ids::HONEYCOMB_BLOCK); + $reg->mapSimple(Blocks::ICE(), Ids::ICE); + $reg->mapSimple(Blocks::INFESTED_CHISELED_STONE_BRICK(), Ids::INFESTED_CHISELED_STONE_BRICKS); + $reg->mapSimple(Blocks::INFESTED_COBBLESTONE(), Ids::INFESTED_COBBLESTONE); + $reg->mapSimple(Blocks::INFESTED_CRACKED_STONE_BRICK(), Ids::INFESTED_CRACKED_STONE_BRICKS); + $reg->mapSimple(Blocks::INFESTED_MOSSY_STONE_BRICK(), Ids::INFESTED_MOSSY_STONE_BRICKS); + $reg->mapSimple(Blocks::INFESTED_STONE(), Ids::INFESTED_STONE); + $reg->mapSimple(Blocks::INFESTED_STONE_BRICK(), Ids::INFESTED_STONE_BRICKS); + $reg->mapSimple(Blocks::INFO_UPDATE(), Ids::INFO_UPDATE); + $reg->mapSimple(Blocks::INFO_UPDATE2(), Ids::INFO_UPDATE2); + $reg->mapSimple(Blocks::INVISIBLE_BEDROCK(), Ids::INVISIBLE_BEDROCK); + $reg->mapSimple(Blocks::IRON(), Ids::IRON_BLOCK); + $reg->mapSimple(Blocks::IRON_BARS(), Ids::IRON_BARS); + $reg->mapSimple(Blocks::IRON_ORE(), Ids::IRON_ORE); + $reg->mapSimple(Blocks::JUKEBOX(), Ids::JUKEBOX); + $reg->mapSimple(Blocks::LAPIS_LAZULI(), Ids::LAPIS_BLOCK); + $reg->mapSimple(Blocks::LAPIS_LAZULI_ORE(), Ids::LAPIS_ORE); + $reg->mapSimple(Blocks::LEGACY_STONECUTTER(), Ids::STONECUTTER); + $reg->mapSimple(Blocks::LILY_PAD(), Ids::WATERLILY); + $reg->mapSimple(Blocks::MAGMA(), Ids::MAGMA); + $reg->mapSimple(Blocks::MANGROVE_ROOTS(), Ids::MANGROVE_ROOTS); + $reg->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK); + $reg->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER); + $reg->mapSimple(Blocks::MOSSY_COBBLESTONE(), Ids::MOSSY_COBBLESTONE); + $reg->mapSimple(Blocks::MOSSY_STONE_BRICKS(), Ids::MOSSY_STONE_BRICKS); + $reg->mapSimple(Blocks::MUD(), Ids::MUD); + $reg->mapSimple(Blocks::MUD_BRICKS(), Ids::MUD_BRICKS); + $reg->mapSimple(Blocks::MYCELIUM(), Ids::MYCELIUM); + $reg->mapSimple(Blocks::NETHERITE(), Ids::NETHERITE_BLOCK); + $reg->mapSimple(Blocks::NETHERRACK(), Ids::NETHERRACK); + $reg->mapSimple(Blocks::NETHER_BRICKS(), Ids::NETHER_BRICK); + $reg->mapSimple(Blocks::NETHER_BRICK_FENCE(), Ids::NETHER_BRICK_FENCE); + $reg->mapSimple(Blocks::NETHER_GOLD_ORE(), Ids::NETHER_GOLD_ORE); + $reg->mapSimple(Blocks::NETHER_QUARTZ_ORE(), Ids::QUARTZ_ORE); + $reg->mapSimple(Blocks::NETHER_REACTOR_CORE(), Ids::NETHERREACTOR); + $reg->mapSimple(Blocks::NETHER_WART_BLOCK(), Ids::NETHER_WART_BLOCK); + $reg->mapSimple(Blocks::NOTE_BLOCK(), Ids::NOTEBLOCK); + $reg->mapSimple(Blocks::OBSIDIAN(), Ids::OBSIDIAN); + $reg->mapSimple(Blocks::PACKED_ICE(), Ids::PACKED_ICE); + $reg->mapSimple(Blocks::PACKED_MUD(), Ids::PACKED_MUD); + $reg->mapSimple(Blocks::PODZOL(), Ids::PODZOL); + $reg->mapSimple(Blocks::POLISHED_ANDESITE(), Ids::POLISHED_ANDESITE); + $reg->mapSimple(Blocks::POLISHED_BLACKSTONE(), Ids::POLISHED_BLACKSTONE); + $reg->mapSimple(Blocks::POLISHED_BLACKSTONE_BRICKS(), Ids::POLISHED_BLACKSTONE_BRICKS); + $reg->mapSimple(Blocks::POLISHED_DEEPSLATE(), Ids::POLISHED_DEEPSLATE); + $reg->mapSimple(Blocks::POLISHED_DIORITE(), Ids::POLISHED_DIORITE); + $reg->mapSimple(Blocks::POLISHED_GRANITE(), Ids::POLISHED_GRANITE); + $reg->mapSimple(Blocks::POLISHED_TUFF(), Ids::POLISHED_TUFF); + $reg->mapSimple(Blocks::PRISMARINE(), Ids::PRISMARINE); + $reg->mapSimple(Blocks::PRISMARINE_BRICKS(), Ids::PRISMARINE_BRICKS); + $reg->mapSimple(Blocks::QUARTZ_BRICKS(), Ids::QUARTZ_BRICKS); + $reg->mapSimple(Blocks::RAW_COPPER(), Ids::RAW_COPPER_BLOCK); + $reg->mapSimple(Blocks::RAW_GOLD(), Ids::RAW_GOLD_BLOCK); + $reg->mapSimple(Blocks::RAW_IRON(), Ids::RAW_IRON_BLOCK); + $reg->mapSimple(Blocks::REDSTONE(), Ids::REDSTONE_BLOCK); + $reg->mapSimple(Blocks::RED_MUSHROOM(), Ids::RED_MUSHROOM); + $reg->mapSimple(Blocks::RED_NETHER_BRICKS(), Ids::RED_NETHER_BRICK); + $reg->mapSimple(Blocks::RED_SAND(), Ids::RED_SAND); + $reg->mapSimple(Blocks::RED_SANDSTONE(), Ids::RED_SANDSTONE); + $reg->mapSimple(Blocks::REINFORCED_DEEPSLATE(), Ids::REINFORCED_DEEPSLATE); + $reg->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6); + $reg->mapSimple(Blocks::RESIN(), Ids::RESIN_BLOCK); + $reg->mapSimple(Blocks::RESIN_BRICKS(), Ids::RESIN_BRICKS); + $reg->mapSimple(Blocks::SAND(), Ids::SAND); + $reg->mapSimple(Blocks::SANDSTONE(), Ids::SANDSTONE); + $reg->mapSimple(Blocks::SCULK(), Ids::SCULK); + $reg->mapSimple(Blocks::SEA_LANTERN(), Ids::SEA_LANTERN); + $reg->mapSimple(Blocks::SHROOMLIGHT(), Ids::SHROOMLIGHT); + $reg->mapSimple(Blocks::SHULKER_BOX(), Ids::UNDYED_SHULKER_BOX); + $reg->mapSimple(Blocks::SLIME(), Ids::SLIME); + $reg->mapSimple(Blocks::SMITHING_TABLE(), Ids::SMITHING_TABLE); + $reg->mapSimple(Blocks::SMOOTH_BASALT(), Ids::SMOOTH_BASALT); + $reg->mapSimple(Blocks::SMOOTH_RED_SANDSTONE(), Ids::SMOOTH_RED_SANDSTONE); + $reg->mapSimple(Blocks::SMOOTH_SANDSTONE(), Ids::SMOOTH_SANDSTONE); + $reg->mapSimple(Blocks::SMOOTH_STONE(), Ids::SMOOTH_STONE); + $reg->mapSimple(Blocks::SNOW(), Ids::SNOW); + $reg->mapSimple(Blocks::SOUL_SAND(), Ids::SOUL_SAND); + $reg->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL); + $reg->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM); + $reg->mapSimple(Blocks::STONE(), Ids::STONE); + $reg->mapSimple(Blocks::STONE_BRICKS(), Ids::STONE_BRICKS); + $reg->mapSimple(Blocks::TALL_GRASS(), Ids::SHORT_GRASS); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( + $reg->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS); + $reg->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER); + $reg->mapSimple(Blocks::TUFF(), Ids::TUFF); + $reg->mapSimple(Blocks::TUFF_BRICKS(), Ids::TUFF_BRICKS); + $reg->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK); + $reg->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS); + $reg->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE); + + $reg->mapSimple(Blocks::ALLIUM(), Ids::ALLIUM); + $reg->mapSimple(Blocks::CORNFLOWER(), Ids::CORNFLOWER); + $reg->mapSimple(Blocks::AZURE_BLUET(), Ids::AZURE_BLUET); + $reg->mapSimple(Blocks::LILY_OF_THE_VALLEY(), Ids::LILY_OF_THE_VALLEY); + $reg->mapSimple(Blocks::BLUE_ORCHID(), Ids::BLUE_ORCHID); + $reg->mapSimple(Blocks::OXEYE_DAISY(), Ids::OXEYE_DAISY); + $reg->mapSimple(Blocks::POPPY(), Ids::POPPY); + $reg->mapSimple(Blocks::ORANGE_TULIP(), Ids::ORANGE_TULIP); + $reg->mapSimple(Blocks::PINK_TULIP(), Ids::PINK_TULIP); + $reg->mapSimple(Blocks::RED_TULIP(), Ids::RED_TULIP); + $reg->mapSimple(Blocks::WHITE_TULIP(), Ids::WHITE_TULIP); + } + + private static function registerColoredMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapColored(Blocks::STAINED_HARDENED_GLASS(), "minecraft:hard_", "_stained_glass"); + $reg->mapColored(Blocks::STAINED_HARDENED_GLASS_PANE(), "minecraft:hard_", "_stained_glass_pane"); + + $reg->mapColored(Blocks::CARPET(), "minecraft:", "_carpet"); + $reg->mapColored(Blocks::CONCRETE(), "minecraft:", "_concrete"); + $reg->mapColored(Blocks::CONCRETE_POWDER(), "minecraft:", "_concrete_powder"); + $reg->mapColored(Blocks::DYED_SHULKER_BOX(), "minecraft:", "_shulker_box"); + $reg->mapColored(Blocks::STAINED_CLAY(), "minecraft:", "_terracotta"); + $reg->mapColored(Blocks::STAINED_GLASS(), "minecraft:", "_stained_glass"); + $reg->mapColored(Blocks::STAINED_GLASS_PANE(), "minecraft:", "_stained_glass_pane"); + $reg->mapColored(Blocks::WOOL(), "minecraft:", "_wool"); + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::GLAZED_TERRACOTTA()) + ->idComponents([ + "minecraft:", + new ValueFromStringProperty("color", ValueMappings::getInstance()->dyeColorWithSilver, fn(GlazedTerracotta $b) => $b->getColor(), fn(GlazedTerracotta $b, DyeColor $v) => $b->setColor($v)), + "_glazed_terracotta" + ]) + ->properties([$commonProperties->horizontalFacingClassic]) + ); + } + + private static function registerCandleMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $candleProperties = [ + $commonProperties->lit, + new IntProperty(StateNames::CANDLES, 0, 3, fn(Candle $b) => $b->getCount(), fn(Candle $b, int $v) => $b->setCount($v), offset: 1), + ]; + $cakeWithCandleProperties = [$commonProperties->lit]; + $reg->mapModel(Model::create(Blocks::CANDLE(), Ids::CANDLE)->properties($candleProperties)); + $reg->mapModel(Model::create(Blocks::CAKE_WITH_CANDLE(), Ids::CANDLE_CAKE)->properties($cakeWithCandleProperties)); + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DYED_CANDLE()) + ->idComponents([ + "minecraft:", + $commonProperties->dyeColorIdInfix, + "_candle" + ]) + ->properties($candleProperties) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CAKE_WITH_DYED_CANDLE()) + ->idComponents([ + "minecraft:", + $commonProperties->dyeColorIdInfix, + "_candle_cake" + ]) + ->properties($cakeWithCandleProperties) + ); + } + + private static function registerLeavesMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $properties = [ + new BoolProperty(StateNames::PERSISTENT_BIT, fn(Leaves $b) => $b->isNoDecay(), fn(Leaves $b, bool $v) => $b->setNoDecay($v)), + new BoolProperty(StateNames::UPDATE_BIT, fn(Leaves $b) => $b->isCheckDecay(), fn(Leaves $b, bool $v) => $b->setCheckDecay($v)), + ]; + foreach([ + Ids::ACACIA_LEAVES => Blocks::ACACIA_LEAVES(), + Ids::AZALEA_LEAVES => Blocks::AZALEA_LEAVES(), + Ids::AZALEA_LEAVES_FLOWERED => Blocks::FLOWERING_AZALEA_LEAVES(), + Ids::BIRCH_LEAVES => Blocks::BIRCH_LEAVES(), + Ids::CHERRY_LEAVES => Blocks::CHERRY_LEAVES(), + Ids::DARK_OAK_LEAVES => Blocks::DARK_OAK_LEAVES(), + Ids::JUNGLE_LEAVES => Blocks::JUNGLE_LEAVES(), + Ids::MANGROVE_LEAVES => Blocks::MANGROVE_LEAVES(), + Ids::OAK_LEAVES => Blocks::OAK_LEAVES(), + Ids::PALE_OAK_LEAVES => Blocks::PALE_OAK_LEAVES(), + Ids::SPRUCE_LEAVES => Blocks::SPRUCE_LEAVES() + ] as $id => $block){ + $reg->mapModel(Model::create($block, $id)->properties($properties)); + } + } + + private static function registerSaplingMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $properties = [ + new BoolProperty(StateNames::AGE_BIT, fn(Sapling $b) => $b->isReady(), fn(Sapling $b, bool $v) => $b->setReady($v)), + ]; + foreach([ + Ids::ACACIA_SAPLING => Blocks::ACACIA_SAPLING(), + Ids::BIRCH_SAPLING => Blocks::BIRCH_SAPLING(), + Ids::DARK_OAK_SAPLING => Blocks::DARK_OAK_SAPLING(), + Ids::JUNGLE_SAPLING => Blocks::JUNGLE_SAPLING(), + Ids::OAK_SAPLING => Blocks::OAK_SAPLING(), + Ids::SPRUCE_SAPLING => Blocks::SPRUCE_SAPLING(), + ] as $id => $block){ + $reg->mapModel(Model::create($block, $id)->properties($properties)); + } + } + + private static function registerPlantMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapModel(Model::create(Blocks::BEETROOTS(), Ids::BEETROOT)->properties([$commonProperties->cropAgeMax7])); + $reg->mapModel(Model::create(Blocks::CARROTS(), Ids::CARROTS)->properties([$commonProperties->cropAgeMax7])); + $reg->mapModel(Model::create(Blocks::POTATOES(), Ids::POTATOES)->properties([$commonProperties->cropAgeMax7])); + $reg->mapModel(Model::create(Blocks::WHEAT(), Ids::WHEAT)->properties([$commonProperties->cropAgeMax7])); + + $reg->mapModel(Model::create(Blocks::MELON_STEM(), Ids::MELON_STEM)->properties($commonProperties->stemProperties)); + $reg->mapModel(Model::create(Blocks::PUMPKIN_STEM(), Ids::PUMPKIN_STEM)->properties($commonProperties->stemProperties)); + + foreach([ + [Blocks::DOUBLE_TALLGRASS(), Ids::TALL_GRASS], + [Blocks::LARGE_FERN(), Ids::LARGE_FERN], + [Blocks::LILAC(), Ids::LILAC], + [Blocks::PEONY(), Ids::PEONY], + [Blocks::ROSE_BUSH(), Ids::ROSE_BUSH], + [Blocks::SUNFLOWER(), Ids::SUNFLOWER], + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->doublePlantHalf])); + } + + foreach([ + [Blocks::BROWN_MUSHROOM_BLOCK(), Ids::BROWN_MUSHROOM_BLOCK], + [Blocks::RED_MUSHROOM_BLOCK(), Ids::RED_MUSHROOM_BLOCK] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([ + new ValueFromIntProperty(StateNames::HUGE_MUSHROOM_BITS, ValueMappings::getInstance()->mushroomBlockType, fn(RedMushroomBlock $b) => $b->getMushroomBlockType(), fn(RedMushroomBlock $b, MushroomBlockType $v) => $b->setMushroomBlockType($v)), + ])); + } + + $reg->mapModel(Model::create(Blocks::GLOW_LICHEN(), Ids::GLOW_LICHEN)->properties([$commonProperties->multiFacingFlags])); + $reg->mapModel(Model::create(Blocks::RESIN_CLUMP(), Ids::RESIN_CLUMP)->properties([$commonProperties->multiFacingFlags])); + + $reg->mapModel(Model::create(Blocks::VINES(), Ids::VINE)->properties([ + new OptionSetFromIntProperty( + StateNames::VINE_DIRECTION_BITS, + IntFromRawStateMap::int([ + Facing::NORTH => BlockLegacyMetadata::VINE_FLAG_NORTH, + Facing::SOUTH => BlockLegacyMetadata::VINE_FLAG_SOUTH, + Facing::WEST => BlockLegacyMetadata::VINE_FLAG_WEST, + Facing::EAST => BlockLegacyMetadata::VINE_FLAG_EAST, + ]), + fn(Vine $b) => $b->getFaces(), + fn(Vine $b, array $v) => $b->setFaces($v) + ) + ])); + + $reg->mapModel(Model::create(Blocks::SWEET_BERRY_BUSH(), Ids::SWEET_BERRY_BUSH)->properties([ + //TODO: berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7 + new IntProperty(StateNames::GROWTH, 0, 7, fn(SweetBerryBush $b) => $b->getAge(), fn(SweetBerryBush $b, int $v) => $b->setAge(min($v, SweetBerryBush::STAGE_MATURE))) + ])); + $reg->mapModel(Model::create(Blocks::TORCHFLOWER_CROP(), Ids::TORCHFLOWER_CROP)->properties([ + //TODO: this property can have values 0-7, but only 0-1 are valid + new IntProperty(StateNames::GROWTH, 0, 7, fn(TorchflowerCrop $b) => $b->isReady() ? 1 : 0, fn(TorchflowerCrop $b, int $v) => $b->setReady($v !== 0)) + ])); + } + + private static function registerCoralMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL())->idComponents([...$commonProperties->coralIdPrefixes, "_coral"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL_BLOCK())->idComponents([...$commonProperties->coralIdPrefixes, "_coral_block"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL_FAN()) + ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_fan"]) + ->properties([ + new ValueFromIntProperty(StateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis, fn(FloorCoralFan $b) => $b->getAxis(), fn(FloorCoralFan $b, int $v) => $b->setAxis($v)) + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::WALL_CORAL_FAN()) + ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_wall_fan"]) + ->properties([ + new ValueFromIntProperty(StateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, fn(HorizontalFacing $b) => $b->getFacing(), fn(HorizontalFacing $b, int $v) => $b->setFacing($v)), + ]) + ); + } + + private static function registerCopperMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_BULB()) + ->idComponents([...$commonProperties->copperIdPrefixes, "copper_bulb"]) + ->properties([ + $commonProperties->lit, + new BoolProperty(StateNames::POWERED_BIT, fn(PoweredByRedstone $b) => $b->isPowered(), fn(PoweredByRedstone $b, bool $v) => $b->setPowered($v)), + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER()) + ->idComponents([ + ...$commonProperties->copperIdPrefixes, + "copper", + //HACK: the non-waxed, non-oxidised variant has a _block suffix, but none of the others do + new BoolFromStringProperty("bruhhhh", "", "_block", fn(Copper $b) => !$b->isWaxed() && $b->getOxidation() === CopperOxidation::NONE, fn() => null) + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CHISELED_COPPER())->idComponents([...$commonProperties->copperIdPrefixes, "chiseled_copper"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_GRATE())->idComponents([...$commonProperties->copperIdPrefixes, "copper_grate"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER())->idComponents([...$commonProperties->copperIdPrefixes, "cut_copper"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER_STAIRS()) + ->idComponents([...$commonProperties->copperIdPrefixes, "cut_copper_stairs"]) + ->properties($commonProperties->stairProperties) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_TRAPDOOR()) + ->idComponents([...$commonProperties->copperIdPrefixes, "copper_trapdoor"]) + ->properties($commonProperties->trapdoorProperties) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_DOOR()) + ->idComponents([...$commonProperties->copperIdPrefixes, "copper_door"]) + ->properties($commonProperties->doorProperties) + ); + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER_SLAB()) + ->idComponents([ + ...$commonProperties->copperIdPrefixes, + $commonProperties->slabIdInfix, + "cut_copper_slab" + ]) + ->properties([$commonProperties->slabPositionProperty]) + ); + } + + private static function registerFlattenedEnumMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + //A + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::ANVIL()) + ->idComponents([ + new ValueFromStringProperty("id", IntFromRawStateMap::string([ + 0 => Ids::ANVIL, + 1 => Ids::CHIPPED_ANVIL, + 2 => Ids::DAMAGED_ANVIL, + ]), fn(Anvil $b) => $b->getDamage(), fn(Anvil $b, int $v) => $b->setDamage($v)) + ]) + ->properties([$commonProperties->horizontalFacingCardinal]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::AMETHYST_CLUSTER()) + ->idComponents([ + new ValueFromStringProperty("id", IntFromRawStateMap::string([ + AmethystCluster::STAGE_SMALL_BUD => Ids::SMALL_AMETHYST_BUD, + AmethystCluster::STAGE_MEDIUM_BUD => Ids::MEDIUM_AMETHYST_BUD, + AmethystCluster::STAGE_LARGE_BUD => Ids::LARGE_AMETHYST_BUD, + AmethystCluster::STAGE_CLUSTER => Ids::AMETHYST_CLUSTER + ]), fn(AmethystCluster $b) => $b->getStage(), fn(AmethystCluster $b, int $v) => $b->setStage($v)) + ]) + ->properties([$commonProperties->blockFace]) + ); + + //C + //This one is a special offender :< + //I have no idea why this only has 3 IDs - there are 4 in Java and 4 visually distinct states in Bedrock + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CAVE_VINES()) + ->idComponents([ + "minecraft:cave_vines", + new ValueFromStringProperty( + "variant", + EnumFromRawStateMap::string(FlattenedCaveVinesVariant::class, fn(FlattenedCaveVinesVariant $case) => $case->value), + fn(CaveVines $b) => $b->hasBerries() ? + ($b->isHead() ? + FlattenedCaveVinesVariant::HEAD_WITH_BERRIES : + FlattenedCaveVinesVariant::BODY_WITH_BERRIES) : + FlattenedCaveVinesVariant::NO_BERRIES, + fn(CaveVines $b, FlattenedCaveVinesVariant $v) => match($v){ + FlattenedCaveVinesVariant::HEAD_WITH_BERRIES => $b->setBerries(true)->setHead(true), + FlattenedCaveVinesVariant::BODY_WITH_BERRIES => $b->setBerries(true)->setHead(false), + FlattenedCaveVinesVariant::NO_BERRIES => $b->setBerries(false)->setHead(false), //assume this isn't a head, since we don't have enough information + } + ) + ]) + ->properties([ + new IntProperty(StateNames::GROWING_PLANT_AGE, 0, 25, fn(CaveVines $b) => $b->getAge(), fn(CaveVines $b, int $v) => $b->setAge($v)), + ]) + ); + + //D + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DIRT()) + ->idComponents([ + new ValueFromStringProperty("id", EnumFromRawStateMap::string(DirtType::class, fn(DirtType $case) => match ($case) { + DirtType::NORMAL => Ids::DIRT, + DirtType::COARSE => Ids::COARSE_DIRT, + DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, + }), fn(Dirt $b) => $b->getDirtType(), fn(Dirt $b, DirtType $v) => $b->setDirtType($v)) + ]) + ); + + //F + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::FROGLIGHT()) + ->idComponents([ + new ValueFromStringProperty("id", ValueMappings::getInstance()->froglightType, fn(Froglight $b) => $b->getFroglightType(), fn(Froglight $b, FroglightType $v) => $b->setFroglightType($v)), + ]) + ->properties([$commonProperties->pillarAxis]) + ); + + //L + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::LIGHT()) + ->idComponents([ + "minecraft:light_block_", + //this is a bit shit but it's easier than adapting IntProperty to support flattening :D + new ValueFromStringProperty( + "light_level", + IntFromRawStateMap::string(array_map(strval(...), range(0, 15))), + fn(Light $b) => $b->getLightLevel(), + fn(Light $b, int $v) => $b->setLightLevel($v) + ) + ]) + ); + + //M + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::MOB_HEAD()) + ->idComponents([ + new ValueFromStringProperty("id", ValueMappings::getInstance()->mobHeadType, fn(MobHead $b) => $b->getMobHeadType(), fn(MobHead $b, MobHeadType $v) => $b->setMobHeadType($v)), + ]) + ->properties([ + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptDown, fn(MobHead $b) => $b->getFacing(), fn(MobHead $b, int $v) => $b->setFacing($v)) + ]) + ); + + foreach([ + [Blocks::LAVA(), "lava"], + [Blocks::WATER(), "water"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([...$commonProperties->liquidIdPrefixes, $idSuffix]) + ->properties([$commonProperties->liquidData]) + ); + } + } + + private static function registerFlattenedBoolMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + [Blocks::BLAST_FURNACE(), "blast_furnace"], + [Blocks::FURNACE(), "furnace"], + [Blocks::SMOKER(), "smoker"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([...$commonProperties->furnaceIdPrefixes, $idSuffix]) + ->properties([$commonProperties->horizontalFacingCardinal]) + ); + } + + foreach([ + [Blocks::REDSTONE_LAMP(), "redstone_lamp"], + [Blocks::REDSTONE_ORE(), "redstone_ore"], + [Blocks::DEEPSLATE_REDSTONE_ORE(), "deepslate_redstone_ore"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block)->idComponents(["minecraft:", $commonProperties->litIdInfix, $idSuffix])); + } + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DAYLIGHT_SENSOR()) + ->idComponents([ + "minecraft:daylight_detector", + new BoolFromStringProperty("inverted", "", "_inverted", fn(DaylightSensor $b) => $b->isInverted(), fn(DaylightSensor $b, bool $v) => $b->setInverted($v)) + ]) + ->properties([$commonProperties->analogRedstoneSignal]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_REPEATER()) + ->idComponents([ + "minecraft:", + new BoolFromStringProperty("powered", "un", "", fn(RedstoneRepeater $b) => $b->isPowered(), fn(RedstoneRepeater $b, bool $v) => $b->setPowered($v)), + "powered_repeater" + ]) + ->properties([ + $commonProperties->horizontalFacingCardinal, + new IntProperty(StateNames::REPEATER_DELAY, 0, 3, fn(RedstoneRepeater $b) => $b->getDelay(), fn(RedstoneRepeater $b, int $v) => $b->setDelay($v), offset: 1), + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_COMPARATOR()) + ->idComponents([ + "minecraft:", + //this property also appears in the state, so we ignore it in the ID + //this is baked here purely to keep minecraft happy + new BoolFromStringProperty("dummy_powered", "un", "", fn(RedstoneComparator $b) => $b->isPowered(), fn() => null), + "powered_comparator" + ]) + ->properties([ + $commonProperties->horizontalFacingCardinal, + new BoolProperty(StateNames::OUTPUT_LIT_BIT, fn(RedstoneComparator $b) => $b->isPowered(), fn(RedstoneComparator $b, bool $v) => $b->setPowered($v)), + new BoolProperty(StateNames::OUTPUT_SUBTRACT_BIT, fn(RedstoneComparator $b) => $b->isSubtractMode(), fn(RedstoneComparator $b, bool $v) => $b->setSubtractMode($v)), + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_TORCH()) + ->idComponents([ + "minecraft:", + new BoolFromStringProperty("lit", "unlit_", "", fn(RedstoneTorch $b) => $b->isLit(), fn(RedstoneTorch $b, bool $v) => $b->setLit($v)), + "redstone_torch" + ]) + ->properties([$commonProperties->torchFacing]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::SPONGE())->idComponents([ + "minecraft:", + new BoolFromStringProperty("wet", "", "wet_", fn(Sponge $b) => $b->isWet(), fn(Sponge $b, bool $v) => $b->setWet($v)), + "sponge" + ])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::TNT()) + ->idComponents([ + "minecraft:", + new BoolFromStringProperty("underwater", "", "underwater_", fn(TNT $b) => $b->worksUnderwater(), fn(TNT $b, bool $v) => $b->setWorksUnderwater($v)), + "tnt" + ]) + ->properties([ + new BoolProperty(StateNames::EXPLODE_BIT, fn(TNT $b) => $b->isUnstable(), fn(TNT $b, bool $v) => $b->setUnstable($v)), + ]) + ); + } + + private static function registerStoneLikeSlabMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $reg->mapSlab(Blocks::ANDESITE_SLAB(), "andesite"); + $reg->mapSlab(Blocks::BLACKSTONE_SLAB(), "blackstone"); + $reg->mapSlab(Blocks::BRICK_SLAB(), "brick"); + $reg->mapSlab(Blocks::COBBLED_DEEPSLATE_SLAB(), "cobbled_deepslate"); + $reg->mapSlab(Blocks::COBBLESTONE_SLAB(), "cobblestone"); + $reg->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), "cut_red_sandstone"); + $reg->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), "cut_sandstone"); + $reg->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), "dark_prismarine"); + $reg->mapSlab(Blocks::DEEPSLATE_BRICK_SLAB(), "deepslate_brick"); + $reg->mapSlab(Blocks::DEEPSLATE_TILE_SLAB(), "deepslate_tile"); + $reg->mapSlab(Blocks::DIORITE_SLAB(), "diorite"); + $reg->mapSlab(Blocks::END_STONE_BRICK_SLAB(), "end_stone_brick"); + $reg->mapSlab(Blocks::FAKE_WOODEN_SLAB(), "petrified_oak"); + $reg->mapSlab(Blocks::GRANITE_SLAB(), "granite"); + $reg->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), "mossy_cobblestone"); + $reg->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), "mossy_stone_brick"); + $reg->mapSlab(Blocks::MUD_BRICK_SLAB(), "mud_brick"); + $reg->mapSlab(Blocks::NETHER_BRICK_SLAB(), "nether_brick"); + $reg->mapSlab(Blocks::POLISHED_ANDESITE_SLAB(), "polished_andesite"); + $reg->mapSlab(Blocks::POLISHED_BLACKSTONE_BRICK_SLAB(), "polished_blackstone_brick"); + $reg->mapSlab(Blocks::POLISHED_BLACKSTONE_SLAB(), "polished_blackstone"); + $reg->mapSlab(Blocks::POLISHED_DEEPSLATE_SLAB(), "polished_deepslate"); + $reg->mapSlab(Blocks::POLISHED_DIORITE_SLAB(), "polished_diorite"); + $reg->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), "polished_granite"); + $reg->mapSlab(Blocks::POLISHED_TUFF_SLAB(), "polished_tuff"); + $reg->mapSlab(Blocks::PRISMARINE_BRICKS_SLAB(), "prismarine_brick"); + $reg->mapSlab(Blocks::PRISMARINE_SLAB(), "prismarine"); + $reg->mapSlab(Blocks::PURPUR_SLAB(), "purpur"); + $reg->mapSlab(Blocks::QUARTZ_SLAB(), "quartz"); + $reg->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), "red_nether_brick"); + $reg->mapSlab(Blocks::RED_SANDSTONE_SLAB(), "red_sandstone"); + $reg->mapSlab(Blocks::RESIN_BRICK_SLAB(), "resin_brick"); + $reg->mapSlab(Blocks::SANDSTONE_SLAB(), "sandstone"); + $reg->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), "smooth_quartz"); + $reg->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), "smooth_red_sandstone"); + $reg->mapSlab(Blocks::SMOOTH_SANDSTONE_SLAB(), "smooth_sandstone"); + $reg->mapSlab(Blocks::SMOOTH_STONE_SLAB(), "smooth_stone"); + $reg->mapSlab(Blocks::STONE_BRICK_SLAB(), "stone_brick"); + $reg->mapSlab(Blocks::STONE_SLAB(), "normal_stone"); + $reg->mapSlab(Blocks::TUFF_BRICK_SLAB(), "tuff_brick"); + $reg->mapSlab(Blocks::TUFF_SLAB(), "tuff"); + } + + private static function registerStoneLikeStairMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $reg->mapStairs(Blocks::ANDESITE_STAIRS(), Ids::ANDESITE_STAIRS); + $reg->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS); + $reg->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS); + $reg->mapStairs(Blocks::COBBLED_DEEPSLATE_STAIRS(), Ids::COBBLED_DEEPSLATE_STAIRS); + $reg->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS); + $reg->mapStairs(Blocks::DARK_PRISMARINE_STAIRS(), Ids::DARK_PRISMARINE_STAIRS); + $reg->mapStairs(Blocks::DEEPSLATE_BRICK_STAIRS(), Ids::DEEPSLATE_BRICK_STAIRS); + $reg->mapStairs(Blocks::DEEPSLATE_TILE_STAIRS(), Ids::DEEPSLATE_TILE_STAIRS); + $reg->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS); + $reg->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS); + $reg->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS); + $reg->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS); + $reg->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS); + $reg->mapStairs(Blocks::MUD_BRICK_STAIRS(), Ids::MUD_BRICK_STAIRS); + $reg->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); + $reg->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_BLACKSTONE_BRICK_STAIRS(), Ids::POLISHED_BLACKSTONE_BRICK_STAIRS); + $reg->mapStairs(Blocks::POLISHED_BLACKSTONE_STAIRS(), Ids::POLISHED_BLACKSTONE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_DEEPSLATE_STAIRS(), Ids::POLISHED_DEEPSLATE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_TUFF_STAIRS(), Ids::POLISHED_TUFF_STAIRS); + $reg->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS); + $reg->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS); + $reg->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS); + $reg->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS); + $reg->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS); + $reg->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::RESIN_BRICK_STAIRS(), Ids::RESIN_BRICK_STAIRS); + $reg->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS); + $reg->mapStairs(Blocks::SMOOTH_RED_SANDSTONE_STAIRS(), Ids::SMOOTH_RED_SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::SMOOTH_SANDSTONE_STAIRS(), Ids::SMOOTH_SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS); + $reg->mapStairs(Blocks::STONE_STAIRS(), Ids::NORMAL_STONE_STAIRS); + $reg->mapStairs(Blocks::TUFF_BRICK_STAIRS(), Ids::TUFF_BRICK_STAIRS); + $reg->mapStairs(Blocks::TUFF_STAIRS(), Ids::TUFF_STAIRS); + } + + private static function registerStoneLikeWallMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + Ids::ANDESITE_WALL => Blocks::ANDESITE_WALL(), + Ids::BLACKSTONE_WALL => Blocks::BLACKSTONE_WALL(), + Ids::BRICK_WALL => Blocks::BRICK_WALL(), + Ids::COBBLED_DEEPSLATE_WALL => Blocks::COBBLED_DEEPSLATE_WALL(), + Ids::COBBLESTONE_WALL => Blocks::COBBLESTONE_WALL(), + Ids::DEEPSLATE_BRICK_WALL => Blocks::DEEPSLATE_BRICK_WALL(), + Ids::DEEPSLATE_TILE_WALL => Blocks::DEEPSLATE_TILE_WALL(), + Ids::DIORITE_WALL => Blocks::DIORITE_WALL(), + Ids::END_STONE_BRICK_WALL => Blocks::END_STONE_BRICK_WALL(), + Ids::GRANITE_WALL => Blocks::GRANITE_WALL(), + Ids::MOSSY_COBBLESTONE_WALL => Blocks::MOSSY_COBBLESTONE_WALL(), + Ids::MOSSY_STONE_BRICK_WALL => Blocks::MOSSY_STONE_BRICK_WALL(), + Ids::MUD_BRICK_WALL => Blocks::MUD_BRICK_WALL(), + Ids::NETHER_BRICK_WALL => Blocks::NETHER_BRICK_WALL(), + Ids::POLISHED_BLACKSTONE_BRICK_WALL => Blocks::POLISHED_BLACKSTONE_BRICK_WALL(), + Ids::POLISHED_BLACKSTONE_WALL => Blocks::POLISHED_BLACKSTONE_WALL(), + Ids::POLISHED_DEEPSLATE_WALL => Blocks::POLISHED_DEEPSLATE_WALL(), + Ids::POLISHED_TUFF_WALL => Blocks::POLISHED_TUFF_WALL(), + Ids::PRISMARINE_WALL => Blocks::PRISMARINE_WALL(), + Ids::RED_NETHER_BRICK_WALL => Blocks::RED_NETHER_BRICK_WALL(), + Ids::RED_SANDSTONE_WALL => Blocks::RED_SANDSTONE_WALL(), + Ids::RESIN_BRICK_WALL => Blocks::RESIN_BRICK_WALL(), + Ids::SANDSTONE_WALL => Blocks::SANDSTONE_WALL(), + Ids::STONE_BRICK_WALL => Blocks::STONE_BRICK_WALL(), + Ids::TUFF_BRICK_WALL => Blocks::TUFF_BRICK_WALL(), + Ids::TUFF_WALL => Blocks::TUFF_WALL() + ] as $id => $block){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->wallProperties)); + } + } + + private static function registerWoodMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + //buttons + foreach([ + [Blocks::ACACIA_BUTTON(), Ids::ACACIA_BUTTON], + [Blocks::BIRCH_BUTTON(), Ids::BIRCH_BUTTON], + [Blocks::CHERRY_BUTTON(), Ids::CHERRY_BUTTON], + [Blocks::CRIMSON_BUTTON(), Ids::CRIMSON_BUTTON], + [Blocks::DARK_OAK_BUTTON(), Ids::DARK_OAK_BUTTON], + [Blocks::JUNGLE_BUTTON(), Ids::JUNGLE_BUTTON], + [Blocks::MANGROVE_BUTTON(), Ids::MANGROVE_BUTTON], + [Blocks::OAK_BUTTON(), Ids::WOODEN_BUTTON], + [Blocks::PALE_OAK_BUTTON(), Ids::PALE_OAK_BUTTON], + [Blocks::SPRUCE_BUTTON(), Ids::SPRUCE_BUTTON], + [Blocks::WARPED_BUTTON(), Ids::WARPED_BUTTON] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->buttonProperties)); + } + + //doors + foreach([ + [Blocks::ACACIA_DOOR(), Ids::ACACIA_DOOR], + [Blocks::BIRCH_DOOR(), Ids::BIRCH_DOOR], + [Blocks::CHERRY_DOOR(), Ids::CHERRY_DOOR], + [Blocks::CRIMSON_DOOR(), Ids::CRIMSON_DOOR], + [Blocks::DARK_OAK_DOOR(), Ids::DARK_OAK_DOOR], + [Blocks::JUNGLE_DOOR(), Ids::JUNGLE_DOOR], + [Blocks::MANGROVE_DOOR(), Ids::MANGROVE_DOOR], + [Blocks::OAK_DOOR(), Ids::WOODEN_DOOR], + [Blocks::PALE_OAK_DOOR(), Ids::PALE_OAK_DOOR], + [Blocks::SPRUCE_DOOR(), Ids::SPRUCE_DOOR], + [Blocks::WARPED_DOOR(), Ids::WARPED_DOOR] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->doorProperties)); + } + + //fences + foreach([ + [Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE], + [Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE], + [Blocks::CHERRY_FENCE(), Ids::CHERRY_FENCE], + [Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE], + [Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE], + [Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE], + [Blocks::OAK_FENCE(), Ids::OAK_FENCE], + [Blocks::PALE_OAK_FENCE(), Ids::PALE_OAK_FENCE], + [Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE], + [Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE], + [Blocks::WARPED_FENCE(), Ids::WARPED_FENCE] + ] as [$block, $id]){ + $reg->mapSimple($block, $id); + } + + foreach([ + [Blocks::ACACIA_FENCE_GATE(), Ids::ACACIA_FENCE_GATE], + [Blocks::BIRCH_FENCE_GATE(), Ids::BIRCH_FENCE_GATE], + [Blocks::CHERRY_FENCE_GATE(), Ids::CHERRY_FENCE_GATE], + [Blocks::DARK_OAK_FENCE_GATE(), Ids::DARK_OAK_FENCE_GATE], + [Blocks::JUNGLE_FENCE_GATE(), Ids::JUNGLE_FENCE_GATE], + [Blocks::MANGROVE_FENCE_GATE(), Ids::MANGROVE_FENCE_GATE], + [Blocks::OAK_FENCE_GATE(), Ids::FENCE_GATE], + [Blocks::PALE_OAK_FENCE_GATE(), Ids::PALE_OAK_FENCE_GATE], + [Blocks::SPRUCE_FENCE_GATE(), Ids::SPRUCE_FENCE_GATE], + [Blocks::CRIMSON_FENCE_GATE(), Ids::CRIMSON_FENCE_GATE], + [Blocks::WARPED_FENCE_GATE(), Ids::WARPED_FENCE_GATE] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->fenceGateProperties)); + } + + foreach([ + [Blocks::ACACIA_SIGN(), Ids::ACACIA_STANDING_SIGN], + [Blocks::BIRCH_SIGN(), Ids::BIRCH_STANDING_SIGN], + [Blocks::CHERRY_SIGN(), Ids::CHERRY_STANDING_SIGN], + [Blocks::DARK_OAK_SIGN(), Ids::DARKOAK_STANDING_SIGN], + [Blocks::JUNGLE_SIGN(), Ids::JUNGLE_STANDING_SIGN], + [Blocks::MANGROVE_SIGN(), Ids::MANGROVE_STANDING_SIGN], + [Blocks::OAK_SIGN(), Ids::STANDING_SIGN], + [Blocks::PALE_OAK_SIGN(), Ids::PALE_OAK_STANDING_SIGN], + [Blocks::SPRUCE_SIGN(), Ids::SPRUCE_STANDING_SIGN], + [Blocks::CRIMSON_SIGN(), Ids::CRIMSON_STANDING_SIGN], + [Blocks::WARPED_SIGN(), Ids::WARPED_STANDING_SIGN] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->floorSignLikeRotation])); + } + + //logs + foreach([ + [Blocks::ACACIA_LOG(), "acacia_log"], + [Blocks::BIRCH_LOG(), "birch_log"], + [Blocks::CHERRY_LOG(), "cherry_log"], + [Blocks::DARK_OAK_LOG(), "dark_oak_log"], + [Blocks::JUNGLE_LOG(), "jungle_log"], + [Blocks::MANGROVE_LOG(), "mangrove_log"], + [Blocks::OAK_LOG(), "oak_log"], + [Blocks::PALE_OAK_LOG(), "pale_oak_log"], + [Blocks::SPRUCE_LOG(), "spruce_log"], + [Blocks::CRIMSON_STEM(), "crimson_stem"], + [Blocks::WARPED_STEM(), "warped_stem"], + + //all-sided logs + [Blocks::ACACIA_WOOD(), "acacia_wood"], + [Blocks::BIRCH_WOOD(), "birch_wood"], + [Blocks::CHERRY_WOOD(), "cherry_wood"], + [Blocks::DARK_OAK_WOOD(), "dark_oak_wood"], + [Blocks::JUNGLE_WOOD(), "jungle_wood"], + [Blocks::MANGROVE_WOOD(), "mangrove_wood"], + [Blocks::OAK_WOOD(), "oak_wood"], + [Blocks::PALE_OAK_WOOD(), "pale_oak_wood"], + [Blocks::SPRUCE_WOOD(), "spruce_wood"], + [Blocks::CRIMSON_HYPHAE(), "crimson_hyphae"], + [Blocks::WARPED_HYPHAE(), "warped_hyphae"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([...$commonProperties->woodIdPrefixes, $idSuffix]) + ->properties([$commonProperties->pillarAxis]) + ); + } + + //planks + foreach([ + [Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS], + [Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS], + [Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS], + [Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS], + [Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS], + [Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS], + [Blocks::OAK_PLANKS(), Ids::OAK_PLANKS], + [Blocks::PALE_OAK_PLANKS(), Ids::PALE_OAK_PLANKS], + [Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS], + [Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS], + [Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS] + ] as [$block, $id]){ + $reg->mapSimple($block, $id); + } + + //pressure plates + foreach([ + [Blocks::ACACIA_PRESSURE_PLATE(), Ids::ACACIA_PRESSURE_PLATE], + [Blocks::BIRCH_PRESSURE_PLATE(), Ids::BIRCH_PRESSURE_PLATE], + [Blocks::CHERRY_PRESSURE_PLATE(), Ids::CHERRY_PRESSURE_PLATE], + [Blocks::DARK_OAK_PRESSURE_PLATE(), Ids::DARK_OAK_PRESSURE_PLATE], + [Blocks::JUNGLE_PRESSURE_PLATE(), Ids::JUNGLE_PRESSURE_PLATE], + [Blocks::MANGROVE_PRESSURE_PLATE(), Ids::MANGROVE_PRESSURE_PLATE], + [Blocks::OAK_PRESSURE_PLATE(), Ids::WOODEN_PRESSURE_PLATE], + [Blocks::PALE_OAK_PRESSURE_PLATE(), Ids::PALE_OAK_PRESSURE_PLATE], + [Blocks::SPRUCE_PRESSURE_PLATE(), Ids::SPRUCE_PRESSURE_PLATE], + [Blocks::CRIMSON_PRESSURE_PLATE(), Ids::CRIMSON_PRESSURE_PLATE], + [Blocks::WARPED_PRESSURE_PLATE(), Ids::WARPED_PRESSURE_PLATE] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->simplePressurePlateProperties)); + } + + //slabs + foreach([ + [Blocks::ACACIA_SLAB(), "acacia"], + [Blocks::BIRCH_SLAB(), "birch"], + [Blocks::CHERRY_SLAB(), "cherry"], + [Blocks::DARK_OAK_SLAB(), "dark_oak"], + [Blocks::JUNGLE_SLAB(), "jungle"], + [Blocks::MANGROVE_SLAB(), "mangrove"], + [Blocks::OAK_SLAB(), "oak"], + [Blocks::PALE_OAK_SLAB(), "pale_oak"], + [Blocks::SPRUCE_SLAB(), "spruce"], + [Blocks::CRIMSON_SLAB(), "crimson"], + [Blocks::WARPED_SLAB(), "warped"] + ] as [$block, $type]){ + $reg->mapSlab($block, $type); + } + + //stairs + foreach([ + [Blocks::ACACIA_STAIRS(), Ids::ACACIA_STAIRS], + [Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS], + [Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS], + [Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS], + [Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS], + [Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS], + [Blocks::OAK_STAIRS(), Ids::OAK_STAIRS], + [Blocks::PALE_OAK_STAIRS(), Ids::PALE_OAK_STAIRS], + [Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS], + [Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS], + [Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS] + ] as [$block, $id]){ + $reg->mapStairs($block, $id); + } + + //trapdoors + foreach([ + [Blocks::ACACIA_TRAPDOOR(), Ids::ACACIA_TRAPDOOR], + [Blocks::BIRCH_TRAPDOOR(), Ids::BIRCH_TRAPDOOR], + [Blocks::CHERRY_TRAPDOOR(), Ids::CHERRY_TRAPDOOR], + [Blocks::DARK_OAK_TRAPDOOR(), Ids::DARK_OAK_TRAPDOOR], + [Blocks::JUNGLE_TRAPDOOR(), Ids::JUNGLE_TRAPDOOR], + [Blocks::MANGROVE_TRAPDOOR(), Ids::MANGROVE_TRAPDOOR], + [Blocks::OAK_TRAPDOOR(), Ids::TRAPDOOR], + [Blocks::PALE_OAK_TRAPDOOR(), Ids::PALE_OAK_TRAPDOOR], + [Blocks::SPRUCE_TRAPDOOR(), Ids::SPRUCE_TRAPDOOR], + [Blocks::CRIMSON_TRAPDOOR(), Ids::CRIMSON_TRAPDOOR], + [Blocks::WARPED_TRAPDOOR(), Ids::WARPED_TRAPDOOR] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->trapdoorProperties)); + } + + //wall signs + foreach([ + [Blocks::ACACIA_WALL_SIGN(), Ids::ACACIA_WALL_SIGN], + [Blocks::BIRCH_WALL_SIGN(), Ids::BIRCH_WALL_SIGN], + [Blocks::CHERRY_WALL_SIGN(), Ids::CHERRY_WALL_SIGN], + [Blocks::DARK_OAK_WALL_SIGN(), Ids::DARKOAK_WALL_SIGN], + [Blocks::JUNGLE_WALL_SIGN(), Ids::JUNGLE_WALL_SIGN], + [Blocks::MANGROVE_WALL_SIGN(), Ids::MANGROVE_WALL_SIGN], + [Blocks::OAK_WALL_SIGN(), Ids::WALL_SIGN], + [Blocks::PALE_OAK_WALL_SIGN(), Ids::PALE_OAK_WALL_SIGN], + [Blocks::SPRUCE_WALL_SIGN(), Ids::SPRUCE_WALL_SIGN], + [Blocks::CRIMSON_WALL_SIGN(), Ids::CRIMSON_WALL_SIGN], + [Blocks::WARPED_WALL_SIGN(), Ids::WARPED_WALL_SIGN] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->horizontalFacingClassic])); + } + } + + private static function registerTorchMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + [Blocks::BLUE_TORCH(), Ids::COLORED_TORCH_BLUE], + [Blocks::GREEN_TORCH(), Ids::COLORED_TORCH_GREEN], + [Blocks::PURPLE_TORCH(), Ids::COLORED_TORCH_PURPLE], + [Blocks::RED_TORCH(), Ids::COLORED_TORCH_RED], + [Blocks::SOUL_TORCH(), Ids::SOUL_TORCH], + [Blocks::TORCH(), Ids::TORCH], + [Blocks::UNDERWATER_TORCH(), Ids::UNDERWATER_TORCH] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->torchFacing])); + } + } + + private static function registerChemistryMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + [Blocks::COMPOUND_CREATOR(), Ids::COMPOUND_CREATOR], + [Blocks::ELEMENT_CONSTRUCTOR(), Ids::ELEMENT_CONSTRUCTOR], + [Blocks::LAB_TABLE(), Ids::LAB_TABLE], + [Blocks::MATERIAL_REDUCER(), Ids::MATERIAL_REDUCER], + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->horizontalFacingSWNEInverted])); + } + } + + private static function register1to1CustomMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + //TODO: some of these have repeated accessor refs, we might be able to deduplicate them + //A + $reg->mapModel(Model::create(Blocks::ACTIVATOR_RAIL(), Ids::ACTIVATOR_RAIL)->properties([ + new BoolProperty(StateNames::RAIL_DATA_BIT, fn(ActivatorRail $b) => $b->isPowered(), fn(ActivatorRail $b, bool $v) => $b->setPowered($v)), + new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(ActivatorRail $b) => $b->getShape(), fn(ActivatorRail $b, int $v) => $b->setShape($v)) + ])); + + //B + $reg->mapModel(Model::create(Blocks::BAMBOO(), Ids::BAMBOO)->properties([ + new ValueFromStringProperty(StateNames::BAMBOO_LEAF_SIZE, ValueMappings::getInstance()->bambooLeafSize, fn(Bamboo $b) => $b->getLeafSize(), fn(Bamboo $b, int $v) => $b->setLeafSize($v)), + new BoolProperty(StateNames::AGE_BIT, fn(Bamboo $b) => $b->isReady(), fn(Bamboo $b, bool $v) => $b->setReady($v)), + new BoolFromStringProperty(StateNames::BAMBOO_STALK_THICKNESS, StringValues::BAMBOO_STALK_THICKNESS_THIN, StringValues::BAMBOO_STALK_THICKNESS_THICK, fn(Bamboo $b) => $b->isThick(), fn(Bamboo $b, bool $v) => $b->setThick($v)) + ])); + $reg->mapModel(Model::create(Blocks::BAMBOO_SAPLING(), Ids::BAMBOO_SAPLING)->properties([ + new BoolProperty(StateNames::AGE_BIT, fn(BambooSapling $b) => $b->isReady(), fn(BambooSapling $b, bool $v) => $b->setReady($v)) + ])); + $reg->mapModel(Model::create(Blocks::BANNER(), Ids::STANDING_BANNER)->properties([$commonProperties->floorSignLikeRotation])); + $reg->mapModel(Model::create(Blocks::BARREL(), Ids::BARREL)->properties([ + $commonProperties->anyFacingClassic, + new BoolProperty(StateNames::OPEN_BIT, fn(Barrel $b) => $b->isOpen(), fn(Barrel $b, bool $v) => $b->setOpen($v)) + ])); + $reg->mapModel(Model::create(Blocks::BASALT(), Ids::BASALT)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::BED(), Ids::BED)->properties([ + new BoolProperty(StateNames::HEAD_PIECE_BIT, fn(Bed $b) => $b->isHeadPart(), fn(Bed $b, bool $v) => $b->setHead($v)), + new BoolProperty(StateNames::OCCUPIED_BIT, fn(Bed $b) => $b->isOccupied(), fn(Bed $b, bool $v) => $b->setOccupied($v)), + $commonProperties->horizontalFacingSWNE + ])); + $reg->mapModel(Model::create(Blocks::BEDROCK(), Ids::BEDROCK)->properties([ + new BoolProperty(StateNames::INFINIBURN_BIT, fn(Bedrock $b) => $b->burnsForever(), fn(Bedrock $b, bool $v) => $b->setBurnsForever($v)) + ])); + $reg->mapModel(Model::create(Blocks::BELL(), Ids::BELL)->properties([ + BoolProperty::unused(StateNames::TOGGLE_BIT, false), + new ValueFromStringProperty(StateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, fn(Bell $b) => $b->getAttachmentType(), fn(Bell $b, BellAttachmentType $v) => $b->setAttachmentType($v)), + $commonProperties->horizontalFacingSWNE + ])); + $reg->mapModel(Model::create(Blocks::BONE_BLOCK(), Ids::BONE_BLOCK)->properties([ + IntProperty::unused(StateNames::DEPRECATED, 0), + $commonProperties->pillarAxis + ])); + + $reg->mapModel(Model::create(Blocks::BREWING_STAND(), Ids::BREWING_STAND)->properties(array_map(fn(BrewingStandSlot $slot) => new BoolProperty(match ($slot) { + BrewingStandSlot::EAST => StateNames::BREWING_STAND_SLOT_A_BIT, + BrewingStandSlot::SOUTHWEST => StateNames::BREWING_STAND_SLOT_B_BIT, + BrewingStandSlot::NORTHWEST => StateNames::BREWING_STAND_SLOT_C_BIT + }, fn(BrewingStand $b) => $b->hasSlot($slot), fn(BrewingStand $b, bool $v) => $b->setSlot($slot, $v)), BrewingStandSlot::cases()))); + + //C + $reg->mapModel(Model::create(Blocks::CACTUS(), Ids::CACTUS)->properties([ + new IntProperty(StateNames::AGE, 0, 15, fn(Cactus $b) => $b->getAge(), fn(Cactus $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::CAKE(), Ids::CAKE)->properties([ + new IntProperty(StateNames::BITE_COUNTER, 0, 6, fn(Cake $b) => $b->getBites(), fn(Cake $b, int $v) => $b->setBites($v)) + ])); + $reg->mapModel(Model::create(Blocks::CAMPFIRE(), Ids::CAMPFIRE)->properties($commonProperties->campfireProperties)); + $reg->mapModel(Model::create(Blocks::CARVED_PUMPKIN(), Ids::CARVED_PUMPKIN)->properties([ + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::CHAIN(), Ids::CHAIN)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::CHISELED_BOOKSHELF(), Ids::CHISELED_BOOKSHELF)->properties([ + $commonProperties->horizontalFacingSWNE, + new OptionSetFromIntProperty( + StateNames::BOOKS_STORED, + EnumFromRawStateMap::int(ChiseledBookshelfSlot::class, fn(ChiseledBookshelfSlot $case) => match($case){ + //these are (currently) the same as the internal values, but it's best not to rely on those in case Mojang mess with the flags + ChiseledBookshelfSlot::TOP_LEFT => 1 << 0, + ChiseledBookshelfSlot::TOP_MIDDLE => 1 << 1, + ChiseledBookshelfSlot::TOP_RIGHT => 1 << 2, + ChiseledBookshelfSlot::BOTTOM_LEFT => 1 << 3, + ChiseledBookshelfSlot::BOTTOM_MIDDLE => 1 << 4, + ChiseledBookshelfSlot::BOTTOM_RIGHT => 1 << 5 + }), + fn(ChiseledBookshelf $b) => $b->getSlots(), + fn(ChiseledBookshelf $b, array $v) => $b->setSlots($v) + ) + ])); + $reg->mapModel(Model::create(Blocks::CHISELED_QUARTZ(), Ids::CHISELED_QUARTZ_BLOCK)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::CHEST(), Ids::CHEST)->properties([$commonProperties->horizontalFacingCardinal])); + $reg->mapModel(Model::create(Blocks::CHORUS_FLOWER(), Ids::CHORUS_FLOWER)->properties([ + new IntProperty(StateNames::AGE, ChorusFlower::MIN_AGE, ChorusFlower::MAX_AGE, fn(ChorusFlower $b) => $b->getAge(), fn(ChorusFlower $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::COCOA_POD(), Ids::COCOA)->properties([ + new IntProperty(StateNames::AGE, 0, 2, fn(CocoaBlock $b) => $b->getAge(), fn(CocoaBlock $b, int $v) => $b->setAge($v)), + $commonProperties->horizontalFacingSWNEInverted + ])); + + //D + $reg->mapModel(Model::create(Blocks::DEEPSLATE(), Ids::DEEPSLATE)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::DETECTOR_RAIL(), Ids::DETECTOR_RAIL)->properties([ + new BoolProperty(StateNames::RAIL_DATA_BIT, fn(DetectorRail $b) => $b->isActivated(), fn(DetectorRail $b, bool $v) => $b->setActivated($v)), + new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail + ])); + + //E + $reg->mapModel(Model::create(Blocks::ENDER_CHEST(), Ids::ENDER_CHEST)->properties([$commonProperties->horizontalFacingCardinal])); + $reg->mapModel(Model::create(Blocks::END_PORTAL_FRAME(), Ids::END_PORTAL_FRAME)->properties([ + new BoolProperty(StateNames::END_PORTAL_EYE_BIT, fn(EndPortalFrame $b) => $b->hasEye(), fn(EndPortalFrame $b, bool $v) => $b->setEye($v)), + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::END_ROD(), Ids::END_ROD)->properties([ + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingEndRod, fn(EndRod $b) => $b->getFacing(), fn(EndRod $b, int $v) => $b->setFacing($v)), + ])); + + //F + $reg->mapModel(Model::create(Blocks::FARMLAND(), Ids::FARMLAND)->properties([ + new IntProperty(StateNames::MOISTURIZED_AMOUNT, 0, 7, fn(Farmland $b) => $b->getWetness(), fn(Farmland $b, int $v) => $b->setWetness($v)) + ])); + $reg->mapModel(Model::create(Blocks::FIRE(), Ids::FIRE)->properties([ + new IntProperty(StateNames::AGE, 0, 15, fn(Fire $b) => $b->getAge(), fn(Fire $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::FLOWER_POT(), Ids::FLOWER_POT)->properties([ + BoolProperty::unused(StateNames::UPDATE_BIT, false) + ])); + $reg->mapModel(Model::create(Blocks::FROSTED_ICE(), Ids::FROSTED_ICE)->properties([ + new IntProperty(StateNames::AGE, 0, 3, fn(FrostedIce $b) => $b->getAge(), fn(FrostedIce $b, int $v) => $b->setAge($v)) + ])); + + //G + $reg->mapModel(Model::create(Blocks::GLOWING_ITEM_FRAME(), Ids::GLOW_FRAME)->properties($commonProperties->itemFrameProperties)); + + //H + $reg->mapModel(Model::create(Blocks::HAY_BALE(), Ids::HAY_BLOCK)->properties([ + IntProperty::unused(StateNames::DEPRECATED, 0), + $commonProperties->pillarAxis + ])); + $reg->mapModel(Model::create(Blocks::HOPPER(), Ids::HOPPER)->properties([ + //kinda weird this doesn't use powered_bit? + new BoolProperty(StateNames::TOGGLE_BIT, fn(PoweredByRedstone $b) => $b->isPowered(), fn(PoweredByRedstone $b, bool $v) => $b->setPowered($v)), + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptUp, fn(Hopper $b) => $b->getFacing(), fn(Hopper $b, int $v) => $b->setFacing($v)), + ])); + + //I + $reg->mapModel(Model::create(Blocks::IRON_DOOR(), Ids::IRON_DOOR)->properties($commonProperties->doorProperties)); + $reg->mapModel(Model::create(Blocks::IRON_TRAPDOOR(), Ids::IRON_TRAPDOOR)->properties($commonProperties->trapdoorProperties)); + $reg->mapModel(Model::create(Blocks::ITEM_FRAME(), Ids::FRAME)->properties($commonProperties->itemFrameProperties)); + + //L + $reg->mapModel(Model::create(Blocks::LADDER(), Ids::LADDER)->properties([$commonProperties->horizontalFacingClassic])); + $reg->mapModel(Model::create(Blocks::LANTERN(), Ids::LANTERN)->properties([ + new BoolProperty(StateNames::HANGING, fn(Lantern $b) => $b->isHanging(), fn(Lantern $b, bool $v) => $b->setHanging($v)) + ])); + $reg->mapModel(Model::create(Blocks::LECTERN(), Ids::LECTERN)->properties([ + new BoolProperty(StateNames::POWERED_BIT, fn(Lectern $b) => $b->isProducingSignal(), fn(Lectern $b, bool $v) => $b->setProducingSignal($v)), + $commonProperties->horizontalFacingCardinal, + ])); + $reg->mapModel(Model::create(Blocks::LEVER(), Ids::LEVER)->properties([ + new ValueFromStringProperty(StateNames::LEVER_DIRECTION, ValueMappings::getInstance()->leverFacing, fn(Lever $b) => $b->getFacing(), fn(Lever $b, LeverFacing $v) => $b->setFacing($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(Lever $b) => $b->isActivated(), fn(Lever $b, bool $v) => $b->setActivated($v)), + ])); + $reg->mapModel(Model::create(Blocks::LIGHTNING_ROD(), Ids::LIGHTNING_ROD)->properties([$commonProperties->anyFacingClassic])); + $reg->mapModel(Model::create(Blocks::LIT_PUMPKIN(), Ids::LIT_PUMPKIN)->properties([$commonProperties->horizontalFacingCardinal])); + $reg->mapModel(Model::create(Blocks::LOOM(), Ids::LOOM)->properties([$commonProperties->horizontalFacingSWNE])); + + //M + $reg->mapModel(Model::create(Blocks::MUDDY_MANGROVE_ROOTS(), Ids::MUDDY_MANGROVE_ROOTS)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::NETHER_WART(), Ids::NETHER_WART)->properties([ + new IntProperty(StateNames::AGE, 0, 3, fn(NetherWartPlant $b) => $b->getAge(), fn(NetherWartPlant $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::NETHER_PORTAL(), Ids::PORTAL)->properties([ + new ValueFromStringProperty(StateNames::PORTAL_AXIS, ValueMappings::getInstance()->portalAxis, fn(NetherPortal $b) => $b->getAxis(), fn(NetherPortal $b, int $v) => $b->setAxis($v)) + ])); + + //P + $reg->mapModel(Model::create(Blocks::PINK_PETALS(), Ids::PINK_PETALS)->properties([ + //Pink petals only uses 0-3, but GROWTH state can go up to 7 + new IntProperty(StateNames::GROWTH, 0, 7, fn(PinkPetals $b) => $b->getCount(), fn(PinkPetals $b, int $v) => $b->setCount(min($v, PinkPetals::MAX_COUNT)), offset: 1), + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::POWERED_RAIL(), Ids::GOLDEN_RAIL)->properties([ + new BoolProperty(StateNames::RAIL_DATA_BIT, fn(PoweredRail $b) => $b->isPowered(), fn(PoweredRail $b, bool $v) => $b->setPowered($v)), //TODO: shared with ActivatorRail + new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail + ])); + $reg->mapModel(Model::create(Blocks::PITCHER_PLANT(), Ids::PITCHER_PLANT)->properties([ + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePlant $b) => $b->isTop(), fn(DoublePlant $b, bool $v) => $b->setTop($v)), //TODO: don't we have helpers for this? + ])); + $reg->mapModel(Model::create(Blocks::POLISHED_BASALT(), Ids::POLISHED_BASALT)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::POLISHED_BLACKSTONE_BUTTON(), Ids::POLISHED_BLACKSTONE_BUTTON)->properties($commonProperties->buttonProperties)); + $reg->mapModel(Model::create(Blocks::POLISHED_BLACKSTONE_PRESSURE_PLATE(), Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE)->properties($commonProperties->simplePressurePlateProperties)); + $reg->mapModel(Model::create(Blocks::PUMPKIN(), Ids::PUMPKIN)->properties([ + //not used, has no visible effect + $commonProperties->dummyCardinalDirection + ])); + $reg->mapModel(Model::create(Blocks::PURPUR(), Ids::PURPUR_BLOCK)->properties([ + $commonProperties->dummyPillarAxis + ])); + $reg->mapModel(Model::create(Blocks::PURPUR_PILLAR(), Ids::PURPUR_PILLAR)->properties([$commonProperties->pillarAxis])); + + //Q + $reg->mapModel(Model::create(Blocks::QUARTZ(), Ids::QUARTZ_BLOCK)->properties([ + $commonProperties->dummyPillarAxis + ])); + $reg->mapModel(Model::create(Blocks::QUARTZ_PILLAR(), Ids::QUARTZ_PILLAR)->properties([$commonProperties->pillarAxis])); + + //R + $reg->mapModel(Model::create(Blocks::RAIL(), Ids::RAIL)->properties([ + new IntProperty(StateNames::RAIL_DIRECTION, 0, 9, fn(Rail $b) => $b->getShape(), fn(Rail $b, int $v) => $b->setShape($v)) + ])); + $reg->mapModel(Model::create(Blocks::REDSTONE_WIRE(), Ids::REDSTONE_WIRE)->properties([$commonProperties->analogRedstoneSignal])); + $reg->mapModel(Model::create(Blocks::RESPAWN_ANCHOR(), Ids::RESPAWN_ANCHOR)->properties([ + new IntProperty(StateNames::RESPAWN_ANCHOR_CHARGE, 0, 4, fn(RespawnAnchor $b) => $b->getCharges(), fn(RespawnAnchor $b, int $v) => $b->setCharges($v)) + ])); + + //S + $reg->mapModel(Model::create(Blocks::SEA_PICKLE(), Ids::SEA_PICKLE)->properties([ + new IntProperty(StateNames::CLUSTER_COUNT, 0, 3, fn(SeaPickle $b) => $b->getCount(), fn(SeaPickle $b, int $v) => $b->setCount($v), offset: 1), + new BoolProperty(StateNames::DEAD_BIT, fn(SeaPickle $b) => $b->isUnderwater(), fn(SeaPickle $b, bool $v) => $b->setUnderwater($v), inverted: true) + ])); + $reg->mapModel(Model::create(Blocks::SMALL_DRIPLEAF(), Ids::SMALL_DRIPLEAF_BLOCK)->properties([ + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(SmallDripleaf $b) => $b->isTop(), fn(SmallDripleaf $b, bool $v) => $b->setTop($v)), + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::SMOOTH_QUARTZ(), Ids::SMOOTH_QUARTZ)->properties([ + $commonProperties->dummyPillarAxis + ])); + $reg->mapModel(Model::create(Blocks::SNOW_LAYER(), Ids::SNOW_LAYER)->properties([ + new DummyProperty(StateNames::COVERED_BIT, false), + new IntProperty(StateNames::HEIGHT, 0, 7, fn(SnowLayer $b) => $b->getLayers(), fn(SnowLayer $b, int $v) => $b->setLayers($v), offset: 1) + ])); + $reg->mapModel(Model::create(Blocks::SOUL_CAMPFIRE(), Ids::SOUL_CAMPFIRE)->properties($commonProperties->campfireProperties)); + $reg->mapModel(Model::create(Blocks::SOUL_FIRE(), Ids::SOUL_FIRE)->properties([ + new DummyProperty(StateNames::AGE, 0) //this is useless for soul fire, since it doesn't have the logic associated + ])); + $reg->mapModel(Model::create(Blocks::SOUL_LANTERN(), Ids::SOUL_LANTERN)->properties([ + new BoolProperty(StateNames::HANGING, fn(Lantern $b) => $b->isHanging(), fn(Lantern $b, bool $v) => $b->setHanging($v)) //TODO: repeated + ])); + $reg->mapModel(Model::create(Blocks::STONE_BUTTON(), Ids::STONE_BUTTON)->properties($commonProperties->buttonProperties)); + $reg->mapModel(Model::create(Blocks::STONE_PRESSURE_PLATE(), Ids::STONE_PRESSURE_PLATE)->properties($commonProperties->simplePressurePlateProperties)); + $reg->mapModel(Model::create(Blocks::STONECUTTER(), Ids::STONECUTTER_BLOCK)->properties([ + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::SUGARCANE(), Ids::REEDS)->properties([ + new IntProperty(StateNames::AGE, 0, 15, fn(Sugarcane $b) => $b->getAge(), fn(Sugarcane $b, int $v) => $b->setAge($v)) + ])); + + //T + $reg->mapModel(Model::create(Blocks::TRAPPED_CHEST(), Ids::TRAPPED_CHEST)->properties([ + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::TRIPWIRE(), Ids::TRIP_WIRE)->properties([ + new BoolProperty(StateNames::ATTACHED_BIT, fn(Tripwire $b) => $b->isConnected(), fn(Tripwire $b, bool $v) => $b->setConnected($v)), + new BoolProperty(StateNames::DISARMED_BIT, fn(Tripwire $b) => $b->isDisarmed(), fn(Tripwire $b, bool $v) => $b->setDisarmed($v)), + new BoolProperty(StateNames::SUSPENDED_BIT, fn(Tripwire $b) => $b->isSuspended(), fn(Tripwire $b, bool $v) => $b->setSuspended($v)), + new BoolProperty(StateNames::POWERED_BIT, fn(Tripwire $b) => $b->isTriggered(), fn(Tripwire $b, bool $v) => $b->setTriggered($v)), + ])); + $reg->mapModel(Model::create(Blocks::TRIPWIRE_HOOK(), Ids::TRIPWIRE_HOOK)->properties([ + new BoolProperty(StateNames::ATTACHED_BIT, fn(TripwireHook $b) => $b->isConnected(), fn(TripwireHook $b, bool $v) => $b->setConnected($v)), + new BoolProperty(StateNames::POWERED_BIT, fn(TripwireHook $b) => $b->isPowered(), fn(TripwireHook $b, bool $v) => $b->setPowered($v)), + $commonProperties->horizontalFacingSWNE + ])); + + $reg->mapModel(Model::create(Blocks::TWISTING_VINES(), Ids::TWISTING_VINES)->properties([ + new IntProperty(StateNames::TWISTING_VINES_AGE, 0, 25, fn(NetherVines $b) => $b->getAge(), fn(NetherVines $b, int $v) => $b->setAge($v)) + ])); + + //W + $reg->mapModel(Model::create(Blocks::WALL_BANNER(), Ids::WALL_BANNER)->properties([$commonProperties->horizontalFacingClassic])); + $reg->mapModel(Model::create(Blocks::WEEPING_VINES(), Ids::WEEPING_VINES)->properties([ + new IntProperty(StateNames::WEEPING_VINES_AGE, 0, 25, fn(NetherVines $b) => $b->getAge(), fn(NetherVines $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_HEAVY(), Ids::HEAVY_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal])); + $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), Ids::LIGHT_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal])); + } + + /** + * All mappings that still use the split form of serializer/deserializer registration + * This is typically only used by blocks with one ID but multiple PM types (split by property) + * These currently can't be registered in a unified way, and due to their small number it may not be worth the + * effort to implement a unified way to deal with them + */ + private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + //big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state + $reg->serializer->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ + return Writer::create(Ids::BIG_DRIPLEAF) + ->writeCardinalHorizontalFacing($block->getFacing()) + ->writeUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState, $block->getLeafState()) + ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true); + }); + $reg->serializer->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{ + return Writer::create(Ids::BIG_DRIPLEAF) + ->writeCardinalHorizontalFacing($block->getFacing()) + ->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE) + ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false); + }); + $reg->deserializer->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{ + if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){ + return Blocks::BIG_DRIPLEAF_HEAD() + ->setFacing($in->readCardinalHorizontalFacing()) + ->setLeafState($in->readUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState)); + }else{ + $in->ignored(StateNames::BIG_DRIPLEAF_TILT); + return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readCardinalHorizontalFacing()); + } + }); + + //cauldrons - split into liquid variants, as each have different behaviour + $reg->serializer->map(Blocks::CAULDRON(), Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); + $reg->serializer->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel())); + //potion cauldrons store their real information in the block actor data + $reg->serializer->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); + $reg->serializer->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); + $reg->deserializer->map(Ids::CAULDRON, function(Reader $in) : Block{ + $level = $in->readBoundedInt(StateNames::FILL_LEVEL, 0, 6); + if($level === 0){ + $in->ignored(StateNames::CAULDRON_LIQUID); + return Blocks::CAULDRON(); + } + + return (match ($liquid = $in->readString(StateNames::CAULDRON_LIQUID)) { + StringValues::CAULDRON_LIQUID_WATER => Blocks::WATER_CAULDRON(), + StringValues::CAULDRON_LIQUID_LAVA => Blocks::LAVA_CAULDRON(), + StringValues::CAULDRON_LIQUID_POWDER_SNOW => throw new UnsupportedBlockStateException("Powder snow is not supported yet"), + default => throw $in->badValueException(StateNames::CAULDRON_LIQUID, $liquid) + })->setFillLevel($level); + }); + + //mushroom stems, split for consistency with all-sided logs vs normal logs + $reg->serializer->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) + ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); + $reg->serializer->map(Blocks::MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) + ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); + $reg->deserializer->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match ($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)) { + BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(), + BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(), + default => throw new BlockStateDeserializeException("This state does not exist"), + }); + + //pitcher crop, split into single and double variants as double has different properties and behaviour + //this will probably be the most annoying to unify + $reg->serializer->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{ + return Writer::create(Ids::PITCHER_CROP) + ->writeInt(StateNames::GROWTH, $block->getAge()) + ->writeBool(StateNames::UPPER_BLOCK_BIT, false); + }); + $reg->serializer->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{ + return Writer::create(Ids::PITCHER_CROP) + ->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE) + ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); + }); + $reg->deserializer->map(Ids::PITCHER_CROP, function(Reader $in) : Block{ + $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); + $top = $in->readBool(StateNames::UPPER_BLOCK_BIT); + if($growth <= PitcherCrop::MAX_AGE){ + //top pitcher crop with age 0-2 is an invalid state + //only the bottom half should exist in this case + return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth); + } + return Blocks::DOUBLE_PITCHER_CROP() + ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) + ->setTop($top); + }); + } +} diff --git a/src/data/bedrock/block/convert/property/BoolFromStringProperty.php b/src/data/bedrock/block/convert/property/BoolFromStringProperty.php new file mode 100644 index 000000000..89c64188d --- /dev/null +++ b/src/data/bedrock/block/convert/property/BoolFromStringProperty.php @@ -0,0 +1,78 @@ + + */ +final class BoolFromStringProperty implements StringProperty{ + + /** + * @param \Closure(TBlock) : bool $getter + * @param \Closure(TBlock, bool) : mixed $setter + */ + public function __construct( + private string $name, + private string $falseValue, + private string $trueValue, + private \Closure $getter, + private \Closure $setter + ){} + + public function getName() : string{ + return $this->name; + } + + public function getPossibleValues() : array{ + return [$this->falseValue, $this->trueValue]; + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $this->deserializePlain($block, $in->readString($this->name)); + } + + public function deserializePlain(object $block, string $raw) : void{ + $value = match($raw){ + $this->falseValue => false, + $this->trueValue => true, + default => throw new BlockStateSerializeException("Invalid value for {$this->name}: $raw"), + }; + + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $out->writeString($this->name, $this->serializePlain($block)); + } + + public function serializePlain(object $block) : string{ + $value = ($this->getter)($block); + return $value ? $this->trueValue : $this->falseValue; + } +} diff --git a/src/data/bedrock/block/convert/property/BoolProperty.php b/src/data/bedrock/block/convert/property/BoolProperty.php new file mode 100644 index 000000000..299ec4076 --- /dev/null +++ b/src/data/bedrock/block/convert/property/BoolProperty.php @@ -0,0 +1,71 @@ + + */ +final class BoolProperty implements Property{ + /** + * @phpstan-param \Closure(TBlock) : bool $getter + * @phpstan-param \Closure(TBlock, bool) : mixed $setter + */ + public function __construct( + private string $name, + private \Closure $getter, + private \Closure $setter, + private bool $inverted = false //we don't *need* this, but it avoids accidentally forgetting a ! in the getter/setter closures (and makes it analysable) + ){} + + /** + * @phpstan-return self + */ + public static function unused(string $name, bool $serializedValue) : self{ + return new self($name, fn() => $serializedValue, fn() => null); + } + + public function getName() : string{ return $this->name; } + + /** + * @phpstan-param TBlock $block + */ + public function deserialize(object $block, BlockStateReader $in) : void{ + $raw = $in->readBool($this->name); + $value = $raw !== $this->inverted; + ($this->setter)($block, $value); + } + + /** + * @phpstan-param TBlock $block + */ + public function serialize(object $block, BlockStateWriter $out) : void{ + $value = ($this->getter)($block); + $raw = $value !== $this->inverted; + $out->writeBool($this->name, $raw); + } +} diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php new file mode 100644 index 000000000..71b87139c --- /dev/null +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -0,0 +1,429 @@ + */ + public readonly ValueFromStringProperty $blockFace; + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $pillarAxis; + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $torchFacing; + + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $horizontalFacingCardinal; + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $horizontalFacingSWNE; + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $horizontalFacingSWNEInverted; + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $horizontalFacingClassic; + + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $anyFacingClassic; + + /** @phpstan-var OptionSetFromIntProperty */ + public readonly OptionSetFromIntProperty $multiFacingFlags; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $floorSignLikeRotation; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $analogRedstoneSignal; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $cropAgeMax7; + /** @phpstan-var BoolProperty */ + public readonly BoolProperty $doublePlantHalf; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $liquidData; + + /** @phpstan-var BoolProperty */ + public readonly BoolProperty $lit; + + public readonly DummyProperty $dummyCardinalDirection; + public readonly DummyProperty $dummyPillarAxis; + + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $dyeColorIdInfix; + + /** @phpstan-var BoolFromStringProperty */ + public readonly BoolFromStringProperty $litIdInfix; + + /** @phpstan-var BoolFromStringProperty */ + public readonly BoolFromStringProperty $slabIdInfix; + /** @phpstan-var BoolFromStringProperty */ + public readonly BoolFromStringProperty $slabPositionProperty; + + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $coralIdPrefixes; + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $copperIdPrefixes; + + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $furnaceIdPrefixes; + + /** + * @var StringProperty[]|string[] + * @phpstan-var non-empty-list> + */ + public readonly array $liquidIdPrefixes; + + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $woodIdPrefixes; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $buttonProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $campfireProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $doorProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $fenceGateProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $itemFrameProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $simplePressurePlateProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $stairProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $stemProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $trapdoorProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $wallProperties; + + private function __construct(){ + $vm = ValueMappings::getInstance(); + + $hfGet = fn(HorizontalFacing $v) => $v->getFacing(); + $hfSet = fn(HorizontalFacing $v, int $facing) => $v->setFacing($facing); + $this->horizontalFacingCardinal = new ValueFromStringProperty(StateNames::MC_CARDINAL_DIRECTION, $vm->cardinalDirection, $hfGet, $hfSet); + + $this->blockFace = new ValueFromStringProperty( + StateNames::MC_BLOCK_FACE, + $vm->blockFace, + fn(AnyFacing $b) => $b->getFacing(), + fn(AnyFacing $b, int $v) => $b->setFacing($v) + ); + + $this->pillarAxis = new ValueFromStringProperty( + StateNames::PILLAR_AXIS, + $vm->pillarAxis, + fn(PillarRotation $b) => $b->getAxis(), + fn(PillarRotation $b, int $v) => $b->setAxis($v) + ); + + $this->torchFacing = new ValueFromStringProperty( + StateNames::TORCH_FACING_DIRECTION, + $vm->torchFacing, + fn(Torch $b) => $b->getFacing(), + fn(Torch $b, int $v) => $b->setFacing($v) + ); + + $this->horizontalFacingSWNE = new ValueFromIntProperty(StateNames::DIRECTION, $vm->horizontalFacingSWNE, $hfGet, $hfSet); + $this->horizontalFacingSWNEInverted = new ValueFromIntProperty(StateNames::DIRECTION, $vm->horizontalFacingSWNEInverted, $hfGet, $hfSet); + $this->horizontalFacingClassic = new ValueFromIntProperty(StateNames::FACING_DIRECTION, $vm->horizontalFacingClassic, $hfGet, $hfSet); + + $this->anyFacingClassic = new ValueFromIntProperty( + StateNames::FACING_DIRECTION, + $vm->facing, + fn(AnyFacing $b) => $b->getFacing(), + fn(AnyFacing $b, int $v) => $b->setFacing($v) + ); + + $this->multiFacingFlags = new OptionSetFromIntProperty( + StateNames::MULTI_FACE_DIRECTION_BITS, + IntFromRawStateMap::int([ + Facing::DOWN => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN, + Facing::UP => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP, + Facing::NORTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH, + Facing::SOUTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH, + Facing::WEST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST, + Facing::EAST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST + ]), + fn(MultiFacing $b) => $b->getFaces(), + fn(MultiFacing $b, array $v) => $b->setFaces($v) + ); + + $this->floorSignLikeRotation = new IntProperty(StateNames::GROUND_SIGN_DIRECTION, 0, 15, fn(SignLikeRotation $b) => $b->getRotation(), fn(SignLikeRotation $b, int $v) => $b->setRotation($v)); + + $this->analogRedstoneSignal = new IntProperty(StateNames::REDSTONE_SIGNAL, 0, 15, fn(AnalogRedstoneSignalEmitter $b) => $b->getOutputSignalStrength(), fn(AnalogRedstoneSignalEmitter $b, int $v) => $b->setOutputSignalStrength($v)); + + $this->cropAgeMax7 = new IntProperty(StateNames::GROWTH, 0, 7, fn(Ageable $b) => $b->getAge(), fn(Ageable $b, int $v) => $b->setAge($v)); + $this->doublePlantHalf = new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePlant $b) => $b->isTop(), fn(DoublePlant $b, bool $v) => $b->setTop($v)); + + $fallingFlag = BlockLegacyMetadata::LIQUID_FALLING_FLAG; + $this->liquidData = new IntProperty( + StateNames::LIQUID_DEPTH, + 0, + 15, + fn(Liquid $b) => $b->getDecay() | ($b->isFalling() ? $fallingFlag : 0), + fn(Liquid $b, int $v) => $b->setDecay($v & ~$fallingFlag)->setFalling(($v & $fallingFlag) !== 0) + ); + + $this->lit = new BoolProperty(StateNames::LIT, fn(Lightable $b) => $b->isLit(), fn(Lightable $b, bool $v) => $b->setLit($v)); + + $this->dummyCardinalDirection = new DummyProperty(StateNames::MC_CARDINAL_DIRECTION, BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH); + $this->dummyPillarAxis = new DummyProperty(StateNames::PILLAR_AXIS, BlockStateStringValues::PILLAR_AXIS_Y); + + $this->dyeColorIdInfix = new ValueFromStringProperty("color", $vm->dyeColor, fn(Colored $b) => $b->getColor(), fn(Colored $b, DyeColor $v) => $b->setColor($v)); + $this->litIdInfix = new BoolFromStringProperty("lit", "", "lit_", fn(Lightable $b) => $b->isLit(), fn(Lightable $b, bool $v) => $b->setLit($v)); + + $this->slabIdInfix = new BoolFromStringProperty( + "double", + "", + "double_", + fn(Slab $b) => $b->getSlabType() === SlabType::DOUBLE, + + //we don't know this is actually a bottom slab yet but we don't have enough information to set the + //correct type in this handler + //BOTTOM serves as a signal value for the state deserializer to decide whether to ignore the + //upper_block_bit property + fn(Slab $b, bool $v) => $b->setSlabType($v ? SlabType::DOUBLE : SlabType::BOTTOM) + ); + $this->slabPositionProperty = new BoolFromStringProperty( + StateNames::MC_VERTICAL_HALF, + BlockStateStringValues::MC_VERTICAL_HALF_BOTTOM, + BlockStateStringValues::MC_VERTICAL_HALF_TOP, + fn(Slab $b) => $b->getSlabType() === SlabType::TOP, + + //Ignore the value for double slabs (should be set by ID component before this is reached) + fn(Slab $b, bool $v) => $b->getSlabType() !== SlabType::DOUBLE ? $b->setSlabType($v ? SlabType::TOP : SlabType::BOTTOM) : null + ); + + $this->coralIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("dead", "", "dead_", fn(CoralMaterial $b) => $b->isDead(), fn(CoralMaterial $b, bool $v) => $b->setDead($v)), + new ValueFromStringProperty("type", EnumFromRawStateMap::string(CoralType::class, fn(CoralType $case) => match ($case) { + CoralType::BRAIN => "brain", + CoralType::BUBBLE => "bubble", + CoralType::FIRE => "fire", + CoralType::HORN => "horn", + CoralType::TUBE => "tube" + }), fn(CoralMaterial $b) => $b->getCoralType(), fn(CoralMaterial $b, CoralType $v) => $b->setCoralType($v)), + ]; + $this->copperIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("waxed", "", "waxed_", fn(CopperMaterial $b) => $b->isWaxed(), fn(CopperMaterial $b, bool $v) => $b->setWaxed($v)), + new ValueFromStringProperty("oxidation", EnumFromRawStateMap::string(CopperOxidation::class, fn(CopperOxidation $case) => match ($case) { + CopperOxidation::NONE => "", + CopperOxidation::EXPOSED => "exposed_", + CopperOxidation::WEATHERED => "weathered_", + CopperOxidation::OXIDIZED => "oxidized_", + }), fn(CopperMaterial $b) => $b->getOxidation(), fn(CopperMaterial $b, CopperOxidation $v) => $b->setOxidation($v)) + ]; + + $this->furnaceIdPrefixes = ["minecraft:", $this->litIdInfix]; + + $this->liquidIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("still", "flowing_", "", fn(Liquid $b) => $b->isStill(), fn(Liquid $b, bool $v) => $b->setStill($v)) + ]; + + $this->woodIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("stripped", "", "stripped_", fn(Wood $b) => $b->isStripped(), fn(Wood $b, bool $v) => $b->setStripped($v)), + ]; + + $this->buttonProperties = [ + $this->anyFacingClassic, + new BoolProperty(StateNames::BUTTON_PRESSED_BIT, fn(Button $b) => $b->isPressed(), fn(Button $b, bool $v) => $b->setPressed($v)), + ]; + + $this->campfireProperties = [ + $this->horizontalFacingCardinal, + new BoolProperty(StateNames::EXTINGUISHED, fn(Lightable $b) => $b->isLit(), fn(Lightable $b, bool $v) => $b->setLit($v), inverted: true), + ]; + + //TODO: check if these need any special treatment to get the appropriate data to both halves of the door + $this->doorProperties = [ + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(Door $b) => $b->isTop(), fn(Door $b, bool $v) => $b->setTop($v)), + new BoolProperty(StateNames::DOOR_HINGE_BIT, fn(Door $b) => $b->isHingeRight(), fn(Door $b, bool $v) => $b->setHingeRight($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(Door $b) => $b->isOpen(), fn(Door $b, bool $v) => $b->setOpen($v)), + new ValueFromStringProperty( + StateNames::MC_CARDINAL_DIRECTION, + IntFromRawStateMap::string([ + //a door facing "east" is actually facing north - thanks mojang + Facing::NORTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_EAST, + Facing::EAST => BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH, + Facing::SOUTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_WEST, + Facing::WEST => BlockStateStringValues::MC_CARDINAL_DIRECTION_NORTH + ]), + fn(HorizontalFacing $b) => $b->getFacing(), + fn(HorizontalFacing $b, int $v) => $b->setFacing($v) + ) + ]; + + $this->fenceGateProperties = [ + new BoolProperty(StateNames::IN_WALL_BIT, fn(FenceGate $b) => $b->isInWall(), fn(FenceGate $b, bool $v) => $b->setInWall($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(FenceGate $b) => $b->isOpen(), fn(FenceGate $b, bool $v) => $b->setOpen($v)), + $this->horizontalFacingCardinal, + ]; + + $this->itemFrameProperties = [ + new DummyProperty(StateNames::ITEM_FRAME_PHOTO_BIT, false), //TODO: not sure what the point of this is + new BoolProperty(StateNames::ITEM_FRAME_MAP_BIT, fn(ItemFrame $b) => $b->hasMap(), fn(ItemFrame $b, bool $v) => $b->setHasMap($v)), + $this->anyFacingClassic + ]; + + $this->simplePressurePlateProperties = [ + //TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation? + //best to keep this separate from weighted plates anyway... + new IntProperty( + StateNames::REDSTONE_SIGNAL, + 0, + 15, + fn(SimplePressurePlate $b) => $b->isPressed() ? 15 : 0, + fn(SimplePressurePlate $b, int $v) => $b->setPressed($v !== 0) + ) + ]; + + $this->stairProperties = [ + new BoolProperty(StateNames::UPSIDE_DOWN_BIT, fn(Stair $b) => $b->isUpsideDown(), fn(Stair $b, bool $v) => $b->setUpsideDown($v)), + new ValueFromIntProperty(StateNames::WEIRDO_DIRECTION, $vm->horizontalFacing5Minus, $hfGet, $hfSet), + ]; + + $this->stemProperties = [ + new ValueFromIntProperty(StateNames::FACING_DIRECTION, $vm->facingStem, fn(Stem $b) => $b->getFacing(), fn(Stem $b, int $v) => $b->setFacing($v)), + $this->cropAgeMax7 + ]; + + $this->trapdoorProperties = [ + //this uses the same values as stairs, but the state is named differently + new ValueFromIntProperty(StateNames::DIRECTION, $vm->horizontalFacing5Minus, $hfGet, $hfSet), + + new BoolProperty(StateNames::UPSIDE_DOWN_BIT, fn(Trapdoor $b) => $b->isTop(), fn(Trapdoor $b, bool $v) => $b->setTop($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(Trapdoor $b) => $b->isOpen(), fn(Trapdoor $b, bool $v) => $b->setOpen($v)), + ]; + + $wallProperties = [ + new BoolProperty(StateNames::WALL_POST_BIT, fn(Wall $b) => $b->isPost(), fn(Wall $b, bool $v) => $b->setPost($v)), + ]; + foreach([ + Facing::NORTH => StateNames::WALL_CONNECTION_TYPE_NORTH, + Facing::SOUTH => StateNames::WALL_CONNECTION_TYPE_SOUTH, + Facing::WEST => StateNames::WALL_CONNECTION_TYPE_WEST, + Facing::EAST => StateNames::WALL_CONNECTION_TYPE_EAST + ] as $facing => $stateName){ + $wallProperties[] = new ValueFromStringProperty( + $stateName, + EnumFromRawStateMap::string(WallConnectionTypeShim::class, fn(WallConnectionTypeShim $case) => $case->getValue()), + fn(Wall $b) => WallConnectionTypeShim::serialize($b->getConnection($facing)), + fn(Wall $b, WallConnectionTypeShim $v) => $b->setConnection($facing, $v->deserialize()) + ); + } + $this->wallProperties = $wallProperties; + } +} diff --git a/src/data/bedrock/block/convert/property/DummyProperty.php b/src/data/bedrock/block/convert/property/DummyProperty.php new file mode 100644 index 000000000..a9d32b417 --- /dev/null +++ b/src/data/bedrock/block/convert/property/DummyProperty.php @@ -0,0 +1,61 @@ + + */ +final class DummyProperty implements Property{ + public function __construct( + private string $name, + private bool|int|string $value + ){} + + public function getName() : string{ + return $this->name; + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $in->ignored($this->name); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + if(is_bool($this->value)){ + $out->writeBool($this->name, $this->value); + }elseif(is_int($this->value)){ + $out->writeInt($this->name, $this->value); + }elseif(is_string($this->value)){ + $out->writeString($this->name, $this->value); + }else{ + throw new AssumptionFailedError(); + } + } +} diff --git a/src/data/bedrock/block/convert/property/EnumFromRawStateMap.php b/src/data/bedrock/block/convert/property/EnumFromRawStateMap.php new file mode 100644 index 000000000..c6f4d0516 --- /dev/null +++ b/src/data/bedrock/block/convert/property/EnumFromRawStateMap.php @@ -0,0 +1,109 @@ + + */ +class EnumFromRawStateMap implements StateMap{ + /** + * @var int[] + * @phpstan-var array + */ + private array $enumToValue = []; + + /** + * @var \UnitEnum[] + * @phpstan-var array + */ + private array $valueToEnum = []; + + /** + * @phpstan-param class-string $class + * @phpstan-param \Closure(TEnum) : TRaw $mapper + * @phpstan-param ?\Closure(TEnum) : list $aliasMapper + */ + public function __construct( + string $class, + \Closure $mapper, + ?\Closure $aliasMapper = null + ){ + foreach($class::cases() as $case){ + $int = $mapper($case); + $this->valueToEnum[$int] = $case; + $this->enumToValue[spl_object_id($case)] = $int; + + if($aliasMapper !== null){ + $aliases = $aliasMapper($case); + foreach($aliases as $alias){ + $this->valueToEnum[$alias] = $case; + } + } + } + } + + /** + * Workaround PHPStan too-specific literal type inference - if it ever gets fixed we can get rid of these functions + * + * @phpstan-template TEnum_ of \UnitEnum + * @phpstan-param class-string $class + * @param \Closure(TEnum_) : string $mapper + * @param ?\Closure(TEnum_) : list $aliasMapper + * + * @phpstan-return EnumFromRawStateMap + */ + public static function string(string $class, \Closure $mapper, ?\Closure $aliasMapper = null) : self{ return new self($class, $mapper, $aliasMapper); } + + /** + * Workaround PHPStan too-specific literal type inference - if it ever gets fixed we can get rid of these functions + * + * @phpstan-template TEnum_ of \UnitEnum + * @phpstan-param class-string $class + * @param \Closure(TEnum_) : int $mapper + * @param ?\Closure(TEnum_) : list $aliasMapper + * + * @phpstan-return EnumFromRawStateMap + */ + public static function int(string $class, \Closure $mapper, ?\Closure $aliasMapper = null) : self{ return new self($class, $mapper, $aliasMapper); } + + public function getRawToValueMap() : array{ + return $this->valueToEnum; + } + + public function valueToRaw(mixed $value) : int|string{ + return $this->enumToValue[spl_object_id($value)]; + } + + public function rawToValue(int|string $raw) : ?\UnitEnum{ + return $this->valueToEnum[$raw] ?? null; + } + + public function printableValue(mixed $value) : string{ + return $value::class . "::" . $value->name; + } +} diff --git a/src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php b/src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php new file mode 100644 index 000000000..979fc4751 --- /dev/null +++ b/src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php @@ -0,0 +1,35 @@ + + */ +class IntFromRawStateMap implements StateMap{ + + /** + * @var int[] + * @phpstan-var array + */ + private array $deserializeMap; + + /** + * Constructs a bidirectional mapping, given a mapping of internal values -> serialized values, and an optional set + * of aliases per internal value (used for deserializing invalid serialized values). + * + * @param (int|string)[] $serializeMap + * @param (int|int[])|(string|string[]) $deserializeAliases + * + * @phpstan-param array $serializeMap + * @phpstan-param array> $deserializeAliases + */ + public function __construct( + private array $serializeMap, + array $deserializeAliases = [] + ){ + $this->deserializeMap = array_flip($this->serializeMap); + foreach($deserializeAliases as $pmValue => $mcValues){ + if(!is_array($mcValues)){ + $this->deserializeMap[$mcValues] = $pmValue; + }else{ + foreach($mcValues as $mcValue){ + $this->deserializeMap[$mcValue] = $pmValue; + } + } + } + } + + /** + * @param int[] $serializeMap + * @param (int|int[]) $deserializeAliases + * + * @phpstan-param array $serializeMap + * @phpstan-param array> $deserializeAliases + * + * @phpstan-return self + */ + public static function int(array $serializeMap, array $deserializeAliases = []) : self{ return new self($serializeMap, $deserializeAliases); } + + /** + * @param string[] $serializeMap + * @param (string|string[]) $deserializeAliases + * + * @phpstan-param array $serializeMap + * @phpstan-param array> $deserializeAliases + * + * @phpstan-return self + */ + public static function string(array $serializeMap, array $deserializeAliases = []) : self{ return new self($serializeMap, $deserializeAliases); } + + public function getRawToValueMap() : array{ + return $this->deserializeMap; + } + + public function valueToRaw(mixed $value) : int|string{ + return $this->serializeMap[$value]; + } + + public function rawToValue(int|string $raw) : mixed{ + return $this->deserializeMap[$raw] ?? null; + } + + public function printableValue(mixed $value) : string{ + return "$value"; + } +} diff --git a/src/data/bedrock/block/convert/property/IntProperty.php b/src/data/bedrock/block/convert/property/IntProperty.php new file mode 100644 index 000000000..bab865522 --- /dev/null +++ b/src/data/bedrock/block/convert/property/IntProperty.php @@ -0,0 +1,70 @@ + + */ +final class IntProperty implements Property{ + /** + * @phpstan-param \Closure(TBlock) : int $getter + * @phpstan-param \Closure(TBlock, int) : mixed $setter + */ + public function __construct( + private string $name, + private int $min, + private int $max, + private \Closure $getter, + private \Closure $setter, + private int $offset = 0 + ){ + if($min > $max){ + throw new \InvalidArgumentException("Min value cannot be greater than max value"); + } + } + + public function getName() : string{ return $this->name; } + + /** + * @phpstan-return self + */ + public static function unused(string $name, int $serializedValue) : self{ + return new self($name, Limits::INT32_MIN, Limits::INT32_MAX, fn() => $serializedValue, fn() => null); + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $value = $in->readBoundedInt($this->name, $this->min, $this->max); + ($this->setter)($block, $value + $this->offset); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $value = ($this->getter)($block); + $out->writeInt($this->name, $value - $this->offset); + } +} diff --git a/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php b/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php new file mode 100644 index 000000000..a91c681b8 --- /dev/null +++ b/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php @@ -0,0 +1,93 @@ + + */ +class OptionSetFromIntProperty implements Property{ + + private int $maxValue = 0; + + /** + * @phpstan-param StateMap $map + * @phpstan-param \Closure(TBlock) : array $getter + * @phpstan-param \Closure(TBlock, array) : mixed $setter + */ + public function __construct( + private string $name, + private StateMap $map, + private \Closure $getter, + private \Closure $setter + ){ + $flagsToCases = $this->map->getRawToValueMap(); + foreach($flagsToCases as $possibleFlag => $option){ + if(($this->maxValue & $possibleFlag) !== 0){ + foreach($flagsToCases as $otherFlag => $otherOption){ + if(($possibleFlag & $otherFlag) === $otherFlag && $otherOption !== $option){ + $printableOption = $this->map->printableValue($option); + $printableOtherOption = $this->map->printableValue($otherOption); + throw new \InvalidArgumentException("Flag for option $printableOption overlaps with flag for option $printableOtherOption in property $this->name"); + } + } + + throw new AssumptionFailedError("Unreachable"); + } + + $this->maxValue |= $possibleFlag; + } + } + + public function getName() : string{ return $this->name; } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $flags = $in->readBoundedInt($this->name, 0, $this->maxValue); + + $value = []; + foreach($this->map->getRawToValueMap() as $possibleFlag => $option){ + if(($flags & $possibleFlag) === $possibleFlag){ + $value[] = $option; + } + } + + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $flags = 0; + + $value = ($this->getter)($block); + foreach($value as $option){ + $flags |= $this->map->valueToRaw($option); + } + + $out->writeInt($this->name, $flags); + } +} diff --git a/src/data/bedrock/block/convert/property/Property.php b/src/data/bedrock/block/convert/property/Property.php new file mode 100644 index 000000000..30868dcd1 --- /dev/null +++ b/src/data/bedrock/block/convert/property/Property.php @@ -0,0 +1,44 @@ + + */ + public function getRawToValueMap() : array; + + /** + * @phpstan-param TValue $value + * @phpstan-return TRaw + */ + public function valueToRaw(mixed $value) : int|string; + + /** + * @phpstan-param TRaw $raw + * @phpstan-return TValue|null + */ + public function rawToValue(int|string $raw) : mixed; + + /** + * @phpstan-param TValue $value + */ + public function printableValue(mixed $value) : string; +} diff --git a/src/data/bedrock/block/convert/property/StringProperty.php b/src/data/bedrock/block/convert/property/StringProperty.php new file mode 100644 index 000000000..14ef1beff --- /dev/null +++ b/src/data/bedrock/block/convert/property/StringProperty.php @@ -0,0 +1,50 @@ + + */ +interface StringProperty extends Property{ + /** + * @return string[] + * @phpstan-return list + */ + public function getPossibleValues() : array; + + /** + * TODO: These are only used for flattened IDs for now, we should expand their use to all properties + * in the future and remove the dependencies on BlockStateReader and BlockStateWriter + * @phpstan-param TBlock $block + */ + public function deserializePlain(object $block, string $raw) : void; + + /** + * TODO: These are only used for flattened IDs for now, we should expand their use to all properties + * in the future and remove the dependencies on BlockStateReader and BlockStateWriter + * @phpstan-param TBlock $block + */ + public function serializePlain(object $block) : string; +} diff --git a/src/data/bedrock/block/convert/property/ValueFromIntProperty.php b/src/data/bedrock/block/convert/property/ValueFromIntProperty.php new file mode 100644 index 000000000..46b721255 --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueFromIntProperty.php @@ -0,0 +1,75 @@ + + */ +final class ValueFromIntProperty implements Property{ + + /** + * @phpstan-param StateMap $map + * @phpstan-param \Closure(TBlock) : TValue $getter + * @phpstan-param \Closure(TBlock, TValue) : mixed $setter + */ + public function __construct( + private string $name, + private StateMap $map, + private \Closure $getter, + private \Closure $setter + ){} + + public function getName() : string{ return $this->name; } + + /** + * @return int[] + * @phpstan-return list + */ + public function getPossibleValues() : array{ + return array_keys($this->map->getRawToValueMap()); + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $raw = $in->readInt($this->name); + $value = $this->map->rawToValue($raw); + + if($value === null){ + throw $in->badValueException($this->name, (string) $raw); + } + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $value = ($this->getter)($block); + $raw = $this->map->valueToRaw($value); + + $out->writeInt($this->name, $raw); + } +} diff --git a/src/data/bedrock/block/convert/property/ValueFromStringProperty.php b/src/data/bedrock/block/convert/property/ValueFromStringProperty.php new file mode 100644 index 000000000..b33634ae9 --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueFromStringProperty.php @@ -0,0 +1,81 @@ + + */ +final class ValueFromStringProperty implements StringProperty{ + + /** + * @phpstan-param StateMap $map + * @phpstan-param \Closure(TBlock) : TValue $getter + * @phpstan-param \Closure(TBlock, TValue) : mixed $setter + */ + public function __construct( + private string $name, + private StateMap $map, + private \Closure $getter, + private \Closure $setter + ){} + + public function getName() : string{ return $this->name; } + + /** + * @return string[] + * @phpstan-return list + */ + public function getPossibleValues() : array{ + //PHP sucks + return array_map(strval(...), array_keys($this->map->getRawToValueMap())); + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $this->deserializePlain($block, $in->readString($this->name)); + } + + public function deserializePlain(object $block, string $raw) : void{ + //TODO: duplicated code from BlockStateReader :( + $value = $this->map->rawToValue($raw) ?? throw new BlockStateDeserializeException("Property \"$this->name\" has invalid value \"$raw\""); + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $out->writeString($this->name, $this->serializePlain($block)); + } + + public function serializePlain(object $block) : string{ + $value = ($this->getter)($block); + return $this->map->valueToRaw($value); + } +} diff --git a/src/data/bedrock/block/convert/property/ValueMappings.php b/src/data/bedrock/block/convert/property/ValueMappings.php new file mode 100644 index 000000000..22e9803a5 --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueMappings.php @@ -0,0 +1,305 @@ + */ + public readonly EnumFromRawStateMap $dyeColor; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $dyeColorWithSilver; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $mobHeadType; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $froglightType; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $dirtType; + + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $dripleafState; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $bellAttachmentType; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $leverFacing; + + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $mushroomBlockType; + + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $cardinalDirection; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $blockFace; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $pillarAxis; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $torchFacing; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $portalAxis; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $bambooLeafSize; + + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacing5Minus; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingSWNE; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingSWNEInverted; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingCoral; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingClassic; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facing; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingEndRod; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $coralAxis; + + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingExceptDown; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingExceptUp; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingStem; + + public function __construct(){ + //flattened ID components - we can't generate constants for these + $this->dyeColor = EnumFromRawStateMap::string(DyeColor::class, fn(DyeColor $case) => match ($case) { + DyeColor::BLACK => "black", + DyeColor::BLUE => "blue", + DyeColor::BROWN => "brown", + DyeColor::CYAN => "cyan", + DyeColor::GRAY => "gray", + DyeColor::GREEN => "green", + DyeColor::LIGHT_BLUE => "light_blue", + DyeColor::LIGHT_GRAY => "light_gray", + DyeColor::LIME => "lime", + DyeColor::MAGENTA => "magenta", + DyeColor::ORANGE => "orange", + DyeColor::PINK => "pink", + DyeColor::PURPLE => "purple", + DyeColor::RED => "red", + DyeColor::WHITE => "white", + DyeColor::YELLOW => "yellow" + }); + $this->dyeColorWithSilver = EnumFromRawStateMap::string(DyeColor::class, fn(DyeColor $case) => match ($case) { + DyeColor::LIGHT_GRAY => "silver", + default => $this->dyeColor->valueToRaw($case) + }); + + $this->mobHeadType = EnumFromRawStateMap::string(MobHeadType::class, fn(MobHeadType $case) => match ($case) { + MobHeadType::CREEPER => Ids::CREEPER_HEAD, + MobHeadType::DRAGON => Ids::DRAGON_HEAD, + MobHeadType::PIGLIN => Ids::PIGLIN_HEAD, + MobHeadType::PLAYER => Ids::PLAYER_HEAD, + MobHeadType::SKELETON => Ids::SKELETON_SKULL, + MobHeadType::WITHER_SKELETON => Ids::WITHER_SKELETON_SKULL, + MobHeadType::ZOMBIE => Ids::ZOMBIE_HEAD + }); + $this->froglightType = EnumFromRawStateMap::string(FroglightType::class, fn(FroglightType $case) => match ($case) { + FroglightType::OCHRE => Ids::OCHRE_FROGLIGHT, + FroglightType::PEARLESCENT => Ids::PEARLESCENT_FROGLIGHT, + FroglightType::VERDANT => Ids::VERDANT_FROGLIGHT, + }); + $this->dirtType = EnumFromRawStateMap::string(DirtType::class, fn(DirtType $case) => match ($case) { + DirtType::NORMAL => Ids::DIRT, + DirtType::COARSE => Ids::COARSE_DIRT, + DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, + }); + + //state value mappings + $this->dripleafState = EnumFromRawStateMap::string(DripleafState::class, fn(DripleafState $case) => match ($case) { + DripleafState::STABLE => StringValues::BIG_DRIPLEAF_TILT_NONE, + DripleafState::UNSTABLE => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE, + DripleafState::PARTIAL_TILT => StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT, + DripleafState::FULL_TILT => StringValues::BIG_DRIPLEAF_TILT_FULL_TILT + }); + $this->bellAttachmentType = EnumFromRawStateMap::string(BellAttachmentType::class, fn(BellAttachmentType $case) => match ($case) { + BellAttachmentType::FLOOR => StringValues::ATTACHMENT_STANDING, + BellAttachmentType::CEILING => StringValues::ATTACHMENT_HANGING, + BellAttachmentType::ONE_WALL => StringValues::ATTACHMENT_SIDE, + BellAttachmentType::TWO_WALLS => StringValues::ATTACHMENT_MULTIPLE, + }); + $this->leverFacing = EnumFromRawStateMap::string(LeverFacing::class, fn(LeverFacing $case) => match ($case) { + LeverFacing::DOWN_AXIS_Z => StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH, + LeverFacing::DOWN_AXIS_X => StringValues::LEVER_DIRECTION_DOWN_EAST_WEST, + LeverFacing::UP_AXIS_Z => StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH, + LeverFacing::UP_AXIS_X => StringValues::LEVER_DIRECTION_UP_EAST_WEST, + LeverFacing::NORTH => StringValues::LEVER_DIRECTION_NORTH, + LeverFacing::SOUTH => StringValues::LEVER_DIRECTION_SOUTH, + LeverFacing::WEST => StringValues::LEVER_DIRECTION_WEST, + LeverFacing::EAST => StringValues::LEVER_DIRECTION_EAST + }); + + $this->mushroomBlockType = EnumFromRawStateMap::int( + MushroomBlockType::class, + fn(MushroomBlockType $case) => match ($case) { + MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, + MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, + MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, + MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, + MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, + MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, + MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, + MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, + MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, + MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, + MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, + }, + fn(MushroomBlockType $case) => match ($case) { + MushroomBlockType::ALL_CAP => [11, 12, 13], + default => [] + } + ); + + $this->cardinalDirection = IntFromRawStateMap::string([ + Facing::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH, + Facing::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH, + Facing::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST, + Facing::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST, + ]); + $this->blockFace = IntFromRawStateMap::string([ + Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN, + Facing::UP => StringValues::MC_BLOCK_FACE_UP, + Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH, + Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH, + Facing::WEST => StringValues::MC_BLOCK_FACE_WEST, + Facing::EAST => StringValues::MC_BLOCK_FACE_EAST, + ]); + $this->pillarAxis = IntFromRawStateMap::string([ + Axis::X => StringValues::PILLAR_AXIS_X, + Axis::Y => StringValues::PILLAR_AXIS_Y, + Axis::Z => StringValues::PILLAR_AXIS_Z + ]); + $this->torchFacing = IntFromRawStateMap::string([ + //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) + Facing::WEST => StringValues::TORCH_FACING_DIRECTION_EAST, + Facing::SOUTH => StringValues::TORCH_FACING_DIRECTION_NORTH, + Facing::NORTH => StringValues::TORCH_FACING_DIRECTION_SOUTH, + Facing::UP => StringValues::TORCH_FACING_DIRECTION_TOP, + Facing::EAST => StringValues::TORCH_FACING_DIRECTION_WEST, + ], deserializeAliases: [ + Facing::UP => StringValues::TORCH_FACING_DIRECTION_UNKNOWN //should be illegal, but still supported + ]); + $this->portalAxis = IntFromRawStateMap::string([ + Axis::X => StringValues::PORTAL_AXIS_X, + Axis::Z => StringValues::PORTAL_AXIS_Z, + ], deserializeAliases: [ + Axis::X => StringValues::PORTAL_AXIS_UNKNOWN, + ]); + $this->bambooLeafSize = IntFromRawStateMap::string([ + Bamboo::NO_LEAVES => StringValues::BAMBOO_LEAF_SIZE_NO_LEAVES, + Bamboo::SMALL_LEAVES => StringValues::BAMBOO_LEAF_SIZE_SMALL_LEAVES, + Bamboo::LARGE_LEAVES => StringValues::BAMBOO_LEAF_SIZE_LARGE_LEAVES, + ]); + + $this->horizontalFacing5Minus = IntFromRawStateMap::int([ + Facing::EAST => 0, + Facing::WEST => 1, + Facing::SOUTH => 2, + Facing::NORTH => 3 + ]); + $this->horizontalFacingSWNE = IntFromRawStateMap::int([ + Facing::SOUTH => 0, + Facing::WEST => 1, + Facing::NORTH => 2, + Facing::EAST => 3 + ]); + $this->horizontalFacingSWNEInverted = IntFromRawStateMap::int([ + Facing::NORTH => 0, + Facing::EAST => 1, + Facing::SOUTH => 2, + Facing::WEST => 3, + ]); + $this->horizontalFacingCoral = IntFromRawStateMap::int([ + Facing::WEST => 0, + Facing::EAST => 1, + Facing::NORTH => 2, + Facing::SOUTH => 3 + ]); + $horizontalFacingClassicTable = [ + Facing::NORTH => 2, + Facing::SOUTH => 3, + Facing::WEST => 4, + Facing::EAST => 5 + ]; + $this->horizontalFacingClassic = IntFromRawStateMap::int($horizontalFacingClassicTable, deserializeAliases: [ + Facing::NORTH => [0, 1] //should be illegal but still technically possible + ]); + + $this->facing = IntFromRawStateMap::int([ + Facing::DOWN => 0, + Facing::UP => 1 + ] + $horizontalFacingClassicTable); + + //end rods have all the horizontal facing values opposite to classic facing + $this->facingEndRod = IntFromRawStateMap::int([ + Facing::DOWN => 0, + Facing::UP => 1, + Facing::SOUTH => 2, + Facing::NORTH => 3, + Facing::EAST => 4, + Facing::WEST => 5, + ]); + + $this->coralAxis = IntFromRawStateMap::int([ + Axis::X => 0, + Axis::Z => 1, + ]); + + //TODO: shitty copy pasta job, we can do this better but this is good enough for now + $this->facingExceptDown = IntFromRawStateMap::int( + [Facing::UP => 1] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP => 0]); + $this->facingExceptUp = IntFromRawStateMap::int( + [Facing::DOWN => 0] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::DOWN => 1] + ); + + //In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the + //most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which + //is absurd, and I refuse to make our API similarly absurd. + $this->facingStem = IntFromRawStateMap::int( + [Facing::UP => 0] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP => 1] + ); + } +} diff --git a/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php new file mode 100644 index 000000000..bdd878b52 --- /dev/null +++ b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php @@ -0,0 +1,66 @@ + BlockStateStringValues::WALL_CONNECTION_TYPE_EAST_NONE, + self::SHORT => BlockStateStringValues::WALL_CONNECTION_TYPE_EAST_SHORT, + self::TALL => BlockStateStringValues::WALL_CONNECTION_TYPE_EAST_TALL, + }; + } + + public function deserialize() : ?WallConnectionType{ + return match($this){ + self::NONE => null, + self::SHORT => WallConnectionType::SHORT, + self::TALL => WallConnectionType::TALL, + }; + } + + public static function serialize(?WallConnectionType $value) : self{ + return match($value){ + null => self::NONE, + WallConnectionType::SHORT => self::SHORT, + WallConnectionType::TALL => self::TALL, + }; + } +} diff --git a/src/world/format/io/GlobalBlockStateHandlers.php b/src/world/format/io/GlobalBlockStateHandlers.php index c1d3934cf..731b15f74 100644 --- a/src/world/format/io/GlobalBlockStateHandlers.php +++ b/src/world/format/io/GlobalBlockStateHandlers.php @@ -26,7 +26,9 @@ namespace pocketmine\world\format\io; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockTypeNames; use pocketmine\data\bedrock\block\convert\BlockObjectToStateSerializer; +use pocketmine\data\bedrock\block\convert\BlockSerializerDeserializerRegistrar; use pocketmine\data\bedrock\block\convert\BlockStateToObjectDeserializer; +use pocketmine\data\bedrock\block\convert\VanillaBlockMappings; use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockIdMetaUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader; @@ -44,20 +46,28 @@ use const pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH; * benefits for now. */ final class GlobalBlockStateHandlers{ - private static ?BlockObjectToStateSerializer $blockStateSerializer = null; - - private static ?BlockStateToObjectDeserializer $blockStateDeserializer = null; - private static ?BlockDataUpgrader $blockDataUpgrader = null; private static ?BlockStateData $unknownBlockStateData = null; + private static ?BlockSerializerDeserializerRegistrar $registrar = null; + + public static function getRegistrar() : BlockSerializerDeserializerRegistrar{ + if(self::$registrar === null){ + $deserializer = new BlockStateToObjectDeserializer(); + $serializer = new BlockObjectToStateSerializer(); + self::$registrar = new BlockSerializerDeserializerRegistrar($deserializer, $serializer); + VanillaBlockMappings::init(self::$registrar); + } + return self::$registrar; + } + public static function getDeserializer() : BlockStateToObjectDeserializer{ - return self::$blockStateDeserializer ??= new BlockStateToObjectDeserializer(); + return self::getRegistrar()->deserializer; } public static function getSerializer() : BlockObjectToStateSerializer{ - return self::$blockStateSerializer ??= new BlockObjectToStateSerializer(); + return self::getRegistrar()->serializer; } public static function getUpgrader() : BlockDataUpgrader{ diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 2d609ae2c..06abe7fee 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -600,6 +600,12 @@ parameters: count: 1 path: ../../../src/crash/CrashDumpRenderer.php + - + message: '#^Parameter \#1 \$faces of method pocketmine\\block\\Vine\:\:setFaces\(\) expects list\<2\|3\|4\|5\>, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/data/bedrock/block/convert/VanillaBlockMappings.php + - message: '#^Parameter \#1 \$blockToItemId of class pocketmine\\data\\bedrock\\item\\BlockItemIdMap constructor expects array\, array\ given\.$#' identifier: argument.type diff --git a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php index a47a9b155..674ae8152 100644 --- a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php +++ b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php @@ -42,6 +42,8 @@ final class BlockSerializerDeserializerTest extends TestCase{ public function setUp() : void{ $this->deserializer = new BlockStateToObjectDeserializer(); $this->serializer = new BlockObjectToStateSerializer(); + $registrar = new BlockSerializerDeserializerRegistrar($this->deserializer, $this->serializer); + VanillaBlockMappings::init($registrar); } public function testAllKnownBlockStatesSerializableAndDeserializable() : void{ @@ -49,12 +51,12 @@ final class BlockSerializerDeserializerTest extends TestCase{ try{ $blockStateData = $this->serializer->serializeBlock($block); }catch(BlockStateSerializeException $e){ - self::fail($e->getMessage()); + self::fail("Failed to serialize " . $block->getName() . ": " . $e->getMessage()); } try{ $newBlock = $this->deserializer->deserializeBlock($blockStateData); }catch(BlockStateDeserializeException $e){ - self::fail($e->getMessage()); + self::fail("Failed to deserialize " . $blockStateData->getName() . ": " . $e->getMessage() . " with data " . $blockStateData->toNbt()); } if($block->getTypeId() === BlockTypeIds::POTION_CAULDRON){ From 8f9478e82fec72a8b9f75a90882a278af0ab3a08 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 15:31:10 +0100 Subject: [PATCH 103/140] Illager banners finally working closes #2951 --- src/block/BaseBanner.php | 6 ++ src/block/BaseOminousBanner.php | 85 +++++++++++++++++++ src/block/BlockTypeIds.php | 4 +- src/block/FloorBanner.php | 4 + src/block/OminousFloorBanner.php | 53 ++++++++++++ src/block/OminousWallBanner.php | 49 +++++++++++ src/block/VanillaBlocks.php | 4 + src/block/WallBanner.php | 4 + src/block/tile/Banner.php | 14 +++ .../block/convert/VanillaBlockMappings.php | 19 ++++- .../ItemSerializerDeserializerRegistrar.php | 25 ++++-- src/item/ItemTypeIds.php | 3 +- src/item/VanillaItems.php | 2 + 13 files changed, 260 insertions(+), 12 deletions(-) create mode 100644 src/block/BaseOminousBanner.php create mode 100644 src/block/OminousFloorBanner.php create mode 100644 src/block/OminousWallBanner.php diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index 376f1f9dc..71a892c20 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -50,6 +50,10 @@ abstract class BaseBanner extends Transparent implements Colored{ parent::readStateFromWorld(); $tile = $this->position->getWorld()->getTile($this->position); if($tile instanceof TileBanner){ + if($tile->getType() === TileBanner::TYPE_OMINOUS){ + //illager banner is implemented as a separate block, as it doesn't support base color or custom patterns + return $this->getOminousVersion(); + } $this->color = $tile->getBaseColor(); $this->setPatterns($tile->getPatterns()); } @@ -57,6 +61,8 @@ abstract class BaseBanner extends Transparent implements Colored{ return $this; } + abstract protected function getOminousVersion() : Block; + public function writeStateToWorld() : void{ parent::writeStateToWorld(); $tile = $this->position->getWorld()->getTile($this->position); diff --git a/src/block/BaseOminousBanner.php b/src/block/BaseOminousBanner.php new file mode 100644 index 000000000..ef2ef9c9a --- /dev/null +++ b/src/block/BaseOminousBanner.php @@ -0,0 +1,85 @@ +position->getWorld()->getTile($this->position); + assert($tile instanceof TileBanner); + $tile->setBaseColor(DyeColor::WHITE); + $tile->setPatterns([]); + $tile->setType(TileBanner::TYPE_OMINOUS); + } + + public function isSolid() : bool{ + return false; + } + + public function getMaxStackSize() : int{ + return 16; + } + + public function getFuelTime() : int{ + return 300; + } + + protected function recalculateCollisionBoxes() : array{ + return []; + } + + public function getSupportType(int $facing) : SupportType{ + return SupportType::NONE; + } + + private function canBeSupportedBy(Block $block) : bool{ + return $block->isSolid(); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){ + return false; + } + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + abstract protected function getSupportingFace() : int; + + public function onNearbyBlockChange() : void{ + if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){ + $this->position->getWorld()->useBreakOn($this->position); + } + } +} diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index 4af1894bd..52b141bcf 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -787,8 +787,10 @@ final class BlockTypeIds{ public const RESIN_CLUMP = 10757; public const CHISELED_RESIN_BRICKS = 10758; public const RESPAWN_ANCHOR = 10759; + public const OMINOUS_BANNER = 10760; + public const OMINOUS_WALL_BANNER = 10761; - public const FIRST_UNUSED_BLOCK_ID = 10760; + public const FIRST_UNUSED_BLOCK_ID = 10762; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/FloorBanner.php b/src/block/FloorBanner.php index ba089b6c0..ff57b5973 100644 --- a/src/block/FloorBanner.php +++ b/src/block/FloorBanner.php @@ -34,6 +34,10 @@ use pocketmine\world\BlockTransaction; final class FloorBanner extends BaseBanner implements SignLikeRotation{ use SignLikeRotationTrait; + protected function getOminousVersion() : Block{ + return VanillaBlocks::OMINOUS_BANNER()->setRotation($this->rotation); + } + protected function getSupportingFace() : int{ return Facing::DOWN; } diff --git a/src/block/OminousFloorBanner.php b/src/block/OminousFloorBanner.php new file mode 100644 index 000000000..240aeccfc --- /dev/null +++ b/src/block/OminousFloorBanner.php @@ -0,0 +1,53 @@ +rotation = self::getRotationFromYaw($player->getLocation()->getYaw()); + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/OminousWallBanner.php b/src/block/OminousWallBanner.php new file mode 100644 index 000000000..1eb5ba753 --- /dev/null +++ b/src/block/OminousWallBanner.php @@ -0,0 +1,49 @@ +facing); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(Facing::axis($face) === Axis::Y){ + return false; + } + $this->facing = $face; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 54ec27fc2..3255c6f6f 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -587,6 +587,8 @@ use function strtolower; * @method static WallSign OAK_WALL_SIGN() * @method static Wood OAK_WOOD() * @method static Opaque OBSIDIAN() + * @method static OminousFloorBanner OMINOUS_BANNER() + * @method static OminousWallBanner OMINOUS_WALL_BANNER() * @method static Flower ORANGE_TULIP() * @method static Flower OXEYE_DAISY() * @method static PackedIce PACKED_ICE() @@ -873,6 +875,8 @@ final class VanillaBlocks{ $bannerBreakInfo = new Info(BreakInfo::axe(1.0)); self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class); self::register("wall_banner", fn(BID $id) => new WallBanner($id, "Wall Banner", $bannerBreakInfo), TileBanner::class); + self::register("ominous_banner", fn(BID $id) => new OminousFloorBanner($id, "Ominous Banner", $bannerBreakInfo), TileBanner::class); + self::register("ominous_wall_banner", fn(BID $id) => new OminousWallBanner($id, "Ominous Wall Banner", $bannerBreakInfo), TileBanner::class); self::register("barrel", fn(BID $id) => new Barrel($id, "Barrel", new Info(BreakInfo::axe(2.5))), TileBarrel::class); self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible()))); self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class); diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php index ddb157cda..b631e0c81 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -35,6 +35,10 @@ use pocketmine\world\BlockTransaction; final class WallBanner extends BaseBanner implements HorizontalFacing{ use HorizontalFacingTrait; + protected function getOminousVersion() : Block{ + return VanillaBlocks::OMINOUS_WALL_BANNER()->setFacing($this->facing); + } + protected function getSupportingFace() : int{ return Facing::opposite($this->facing); } diff --git a/src/block/tile/Banner.php b/src/block/tile/Banner.php index 08a560707..97ffe630d 100644 --- a/src/block/tile/Banner.php +++ b/src/block/tile/Banner.php @@ -41,6 +41,10 @@ class Banner extends Spawnable{ public const TAG_PATTERNS = "Patterns"; public const TAG_PATTERN_COLOR = "Color"; public const TAG_PATTERN_NAME = "Pattern"; + public const TAG_TYPE = "Type"; + + public const TYPE_NORMAL = 0; + public const TYPE_OMINOUS = 1; private DyeColor $baseColor = DyeColor::BLACK; @@ -50,6 +54,8 @@ class Banner extends Spawnable{ */ private array $patterns = []; + private int $type = self::TYPE_NORMAL; + public function readSaveData(CompoundTag $nbt) : void{ $colorIdMap = DyeColorIdMap::getInstance(); if( @@ -75,6 +81,8 @@ class Banner extends Spawnable{ $this->patterns[] = new BannerPatternLayer($patternType, $patternColor); } } + + $this->type = $nbt->getInt(self::TAG_TYPE); } protected function writeSaveData(CompoundTag $nbt) : void{ @@ -89,6 +97,7 @@ class Banner extends Spawnable{ ); } $nbt->setTag(self::TAG_PATTERNS, $patterns); + $nbt->setInt(self::TAG_TYPE, $this->type); } protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ @@ -103,6 +112,7 @@ class Banner extends Spawnable{ ); } $nbt->setTag(self::TAG_PATTERNS, $patterns); + $nbt->setInt(self::TAG_TYPE, $this->type); } /** @@ -136,6 +146,10 @@ class Banner extends Spawnable{ $this->patterns = $patterns; } + public function getType() : int{ return $this->type; } + + public function setType(int $type) : void{ $this->type = $type; } + public function getDefaultName() : string{ return "Banner"; } diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index 16ae1e244..3dfa81644 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -55,6 +55,7 @@ use pocketmine\block\Farmland; use pocketmine\block\FillableCauldron; use pocketmine\block\Fire; use pocketmine\block\FloorCoralFan; +use pocketmine\block\OminousFloorBanner; use pocketmine\block\Froglight; use pocketmine\block\FrostedIce; use pocketmine\block\GlazedTerracotta; @@ -103,6 +104,7 @@ use pocketmine\block\utils\MushroomBlockType; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Vine; +use pocketmine\block\OminousWallBanner; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; @@ -154,7 +156,7 @@ final class VanillaBlockMappings{ self::registerChemistryMappings($reg, $commonProperties); self::register1to1CustomMappings($reg, $commonProperties); - self::registerSplitMappings($reg); + self::registerSplitMappings($reg, $commonProperties); } private static function registerSimpleIdOnlyMappings(BlockSerializerDeserializerRegistrar $reg) : void{ @@ -1476,7 +1478,7 @@ final class VanillaBlockMappings{ * These currently can't be registered in a unified way, and due to their small number it may not be worth the * effort to implement a unified way to deal with them */ - private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ //big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state $reg->serializer->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ return Writer::create(Ids::BIG_DRIPLEAF) @@ -1557,5 +1559,18 @@ final class VanillaBlockMappings{ ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) ->setTop($top); }); + + //these only exist within PM (mapped from tile properties) as they don't support the same properties as a + //normal banner + $reg->serializer->map(Blocks::OMINOUS_BANNER(), function(OminousFloorBanner $block) use ($commonProperties) : Writer{ + $writer = Writer::create(Ids::STANDING_BANNER); + $commonProperties->floorSignLikeRotation->serialize($block, $writer); + return $writer; + }); + $reg->serializer->map(Blocks::OMINOUS_WALL_BANNER(), function(OminousWallBanner $block) use ($commonProperties) : Writer{ + $writer = Writer::create(Ids::WALL_BANNER); + $commonProperties->horizontalFacingClassic->serialize($block, $writer); + return $writer; + }); } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 771154d46..f176351b7 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -26,6 +26,7 @@ namespace pocketmine\data\bedrock\item; use pocketmine\block\Bed; use pocketmine\block\Block; use pocketmine\block\CopperDoor; +use pocketmine\block\tile\Banner as TileBanner; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\DyeColor; use pocketmine\block\VanillaBlocks as Blocks; @@ -46,6 +47,7 @@ use pocketmine\item\Potion; use pocketmine\item\SplashPotion; use pocketmine\item\SuspiciousStew; use pocketmine\item\VanillaItems as Items; +use pocketmine\nbt\tag\CompoundTag; final class ItemSerializerDeserializerRegistrar{ @@ -487,14 +489,6 @@ final class ItemSerializerDeserializerRegistrar{ * in a unified manner. */ private function register1to1ItemWithMetaMappings() : void{ - $this->map1to1ItemWithMeta( - Ids::BANNER, - Items::BANNER(), - function(Banner $item, int $meta) : void{ - $item->setColor(DyeColorIdMap::getInstance()->fromInvertedId($meta) ?? throw new ItemTypeDeserializeException("Unknown banner meta $meta")); - }, - fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor()) - ); $this->map1to1ItemWithMeta( Ids::GOAT_HORN, Items::GOAT_HORN(), @@ -550,6 +544,21 @@ final class ItemSerializerDeserializerRegistrar{ $this->deserializer?->map($id, fn() => Items::DYE()->setColor($color)); } $this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor()))); + + $this->deserializer?->map(Ids::BANNER, function(Data $data) : Item{ + $type = $data->getTag()?->getInt(TileBanner::TAG_TYPE) ?? TileBanner::TYPE_NORMAL; + if($type === TileBanner::TYPE_OMINOUS){ + return Items::OMINOUS_BANNER(); + } + $color = DyeColorIdMap::getInstance()->fromInvertedId($data->getMeta()) ?? throw new ItemTypeDeserializeException("Unknown banner meta " . $data->getMeta()); + return Items::BANNER()->setColor($color); + }); + $this->serializer?->map(Items::OMINOUS_BANNER(), fn() => new Data(Ids::BANNER, tag: CompoundTag::create() + ->setInt(TileBanner::TAG_TYPE, TileBanner::TYPE_OMINOUS)) + ); + $this->serializer?->map(Items::BANNER(), function(Banner $item) : Data{ + return new Data(Ids::BANNER, DyeColorIdMap::getInstance()->toInvertedId($item->getColor())); + }); } /** diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index c63046c6b..37be3ab9e 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -334,8 +334,9 @@ final class ItemTypeIds{ public const RECORD_CREATOR = 20295; public const RECORD_CREATOR_MUSIC_BOX = 20296; public const RECORD_PRECIPICE = 20297; + public const OMINOUS_BANNER = 20298; - public const FIRST_UNUSED_ITEM_ID = 20298; + public const FIRST_UNUSED_ITEM_ID = 20299; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index f76cf369f..7103d8878 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -242,6 +242,7 @@ use function strtolower; * @method static Item NETHER_STAR() * @method static Boat OAK_BOAT() * @method static ItemBlockWallOrFloor OAK_SIGN() + * @method static ItemBlockWallOrFloor OMINOUS_BANNER() * @method static PaintingItem PAINTING() * @method static ItemBlockWallOrFloor PALE_OAK_SIGN() * @method static Item PAPER() @@ -540,6 +541,7 @@ final class VanillaItems{ public function isFireProof() : bool{ return true; } }); self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); + self::register("ominous_banner", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OMINOUS_BANNER(), Blocks::OMINOUS_WALL_BANNER())); self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting")); self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN())); self::register("paper", fn(IID $id) => new Item($id, "Paper")); From ef53676a596dd581efa1792e7252bdc43214ffb3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 15:38:07 +0100 Subject: [PATCH 104/140] Fix unit tests --- src/block/BaseOminousBanner.php | 5 +++++ .../bedrock/item/ItemSerializerDeserializerRegistrar.php | 2 +- tests/phpunit/block/block_factory_consistency_check.json | 4 ++++ .../block/convert/BlockSerializerDeserializerTest.php | 9 +++++++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/block/BaseOminousBanner.php b/src/block/BaseOminousBanner.php index ef2ef9c9a..192e6fac2 100644 --- a/src/block/BaseOminousBanner.php +++ b/src/block/BaseOminousBanner.php @@ -27,6 +27,7 @@ use pocketmine\block\tile\Banner as TileBanner; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -82,4 +83,8 @@ abstract class BaseOminousBanner extends Transparent{ $this->position->getWorld()->useBreakOn($this->position); } } + + public function asItem() : Item{ + return VanillaItems::OMINOUS_BANNER(); + } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index f176351b7..fa6f88483 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -546,7 +546,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor()))); $this->deserializer?->map(Ids::BANNER, function(Data $data) : Item{ - $type = $data->getTag()?->getInt(TileBanner::TAG_TYPE) ?? TileBanner::TYPE_NORMAL; + $type = $data->getTag()?->getInt(TileBanner::TAG_TYPE, TileBanner::TYPE_NORMAL) ?? TileBanner::TYPE_NORMAL; if($type === TileBanner::TYPE_OMINOUS){ return Items::OMINOUS_BANNER(); } diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index c7629656d..86a44113f 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -508,6 +508,8 @@ "OAK_WALL_SIGN": 4, "OAK_WOOD": 6, "OBSIDIAN": 1, + "OMINOUS_BANNER": 16, + "OMINOUS_WALL_BANNER": 4, "ORANGE_TULIP": 1, "OXEYE_DAISY": 1, "PACKED_ICE": 1, @@ -776,6 +778,8 @@ "NOTE_BLOCK": "pocketmine\\block\\tile\\Note", "OAK_SIGN": "pocketmine\\block\\tile\\Sign", "OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "OMINOUS_BANNER": "pocketmine\\block\\tile\\Banner", + "OMINOUS_WALL_BANNER": "pocketmine\\block\\tile\\Banner", "PALE_OAK_SIGN": "pocketmine\\block\\tile\\Sign", "PALE_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "POTION_CAULDRON": "pocketmine\\block\\tile\\Cauldron", diff --git a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php index 674ae8152..c0f850027 100644 --- a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php +++ b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php @@ -59,8 +59,13 @@ final class BlockSerializerDeserializerTest extends TestCase{ self::fail("Failed to deserialize " . $blockStateData->getName() . ": " . $e->getMessage() . " with data " . $blockStateData->toNbt()); } - if($block->getTypeId() === BlockTypeIds::POTION_CAULDRON){ - //this pretends to be a water cauldron in the blockstate, and stores its actual data in the blockentity + if(match ($block->getTypeId()) { + BlockTypeIds::POTION_CAULDRON, + BlockTypeIds::OMINOUS_BANNER, + BlockTypeIds::OMINOUS_WALL_BANNER => true, + default => false + }){ + //these pretend to be something else in the blockstate, and the variant switching is done via block entity data continue; } From 5bf0cbec875f8cbc7f6f2ef37433837041e7f3c4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 15:39:23 +0100 Subject: [PATCH 105/140] ... --- src/data/bedrock/block/convert/VanillaBlockMappings.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index 3dfa81644..4438b01bf 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -55,7 +55,6 @@ use pocketmine\block\Farmland; use pocketmine\block\FillableCauldron; use pocketmine\block\Fire; use pocketmine\block\FloorCoralFan; -use pocketmine\block\OminousFloorBanner; use pocketmine\block\Froglight; use pocketmine\block\FrostedIce; use pocketmine\block\GlazedTerracotta; @@ -69,6 +68,8 @@ use pocketmine\block\MobHead; use pocketmine\block\NetherPortal; use pocketmine\block\NetherVines; use pocketmine\block\NetherWartPlant; +use pocketmine\block\OminousFloorBanner; +use pocketmine\block\OminousWallBanner; use pocketmine\block\PinkPetals; use pocketmine\block\PitcherCrop; use pocketmine\block\PoweredRail; @@ -104,7 +105,6 @@ use pocketmine\block\utils\MushroomBlockType; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Vine; -use pocketmine\block\OminousWallBanner; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; From 4cdf064344a6aba4d0136865508ffadb3c9bce03 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 16:37:42 +0100 Subject: [PATCH 106/140] VanillaBlockMappings: Use some model mappings this way there are some minor symmetry benefits, and the only asymmetric parts are the code that selects which model to use. it also has the added benefit of removing some duplicated code paths (e.g. now it's possible to get rid of readUnitEnum() and such). --- .../block/convert/VanillaBlockMappings.php | 185 ++++++++++-------- 1 file changed, 106 insertions(+), 79 deletions(-) diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index 4438b01bf..f70de9f4c 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -96,6 +96,7 @@ use pocketmine\block\utils\BrewingStandSlot; use pocketmine\block\utils\ChiseledBookshelfSlot; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\DirtType; +use pocketmine\block\utils\DripleafState; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\FroglightType; use pocketmine\block\utils\HorizontalFacing; @@ -1472,6 +1473,35 @@ final class VanillaBlockMappings{ $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), Ids::LIGHT_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal])); } + /** + * @phpstan-template TBlock of Block + * @phpstan-param Model $model + */ + private static function mapAsymmetricSerializer(BlockSerializerDeserializerRegistrar $reg, Model $model) : void{ + $id = $model->getId(); + $properties = $model->getProperties(); + $reg->serializer->map($model->getBlock(), function(Block $block) use ($id, $properties) : Writer{ + $writer = new Writer($id); + foreach($properties as $property){ + $property->serialize($block, $writer); + } + return $writer; + }); + } + + /** + * @phpstan-template TBlock of Block + * @phpstan-param Model $model + * @phpstan-return TBlock + */ + private static function deserializeAsymmetric(Model $model, Reader $in) : Block{ + $block = clone $model->getBlock(); + foreach($model->getProperties() as $property){ + $property->deserialize($block, $in); + } + return $block; + } + /** * All mappings that still use the split form of serializer/deserializer registration * This is typically only used by blocks with one ID but multiple PM types (split by property) @@ -1480,97 +1510,94 @@ final class VanillaBlockMappings{ */ private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ //big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state - $reg->serializer->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState, $block->getLeafState()) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true); - }); - $reg->serializer->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false); - }); - $reg->deserializer->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{ - if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){ - return Blocks::BIG_DRIPLEAF_HEAD() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLeafState($in->readUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState)); - }else{ - $in->ignored(StateNames::BIG_DRIPLEAF_TILT); - return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readCardinalHorizontalFacing()); - } - }); + $bigDripleafHeadModel = Model::create(Blocks::BIG_DRIPLEAF_HEAD(), Ids::BIG_DRIPLEAF)->properties([ + $commonProperties->horizontalFacingCardinal, + new ValueFromStringProperty(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState, fn(BigDripleafHead $b) => $b->getLeafState(), fn(BigDripleafHead $b, DripleafState $v) => $b->setLeafState($v)), + new DummyProperty(StateNames::BIG_DRIPLEAF_HEAD, true) + ]); + $bigDripleafStemModel = Model::create(Blocks::BIG_DRIPLEAF_STEM(), Ids::BIG_DRIPLEAF)->properties([ + $commonProperties->horizontalFacingCardinal, + new DummyProperty(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE), + new DummyProperty(StateNames::BIG_DRIPLEAF_HEAD, false) + ]); + self::mapAsymmetricSerializer($reg, $bigDripleafHeadModel); + self::mapAsymmetricSerializer($reg, $bigDripleafStemModel); + $reg->deserializer->map(Ids::BIG_DRIPLEAF, fn(Reader $in) => $in->readBool(StateNames::BIG_DRIPLEAF_HEAD) ? + self::deserializeAsymmetric($bigDripleafHeadModel, $in) : + self::deserializeAsymmetric($bigDripleafStemModel, $in) + ); - //cauldrons - split into liquid variants, as each have different behaviour - $reg->serializer->map(Blocks::CAULDRON(), Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); - $reg->serializer->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel())); - //potion cauldrons store their real information in the block actor data - $reg->serializer->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - $reg->serializer->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - $reg->deserializer->map(Ids::CAULDRON, function(Reader $in) : Block{ - $level = $in->readBoundedInt(StateNames::FILL_LEVEL, 0, 6); - if($level === 0){ - $in->ignored(StateNames::CAULDRON_LIQUID); - return Blocks::CAULDRON(); - } + $fillLevelProperty = new IntProperty(StateNames::FILL_LEVEL, 1, 6, fn(FillableCauldron $b) => $b->getFillLevel(), fn(FillableCauldron $b, int $v) => $b->setFillLevel($v)); - return (match ($liquid = $in->readString(StateNames::CAULDRON_LIQUID)) { - StringValues::CAULDRON_LIQUID_WATER => Blocks::WATER_CAULDRON(), - StringValues::CAULDRON_LIQUID_LAVA => Blocks::LAVA_CAULDRON(), + //this pretends to be a water cauldron on disk and stores its real information in the block actor data, therefore only a serializer is needed + self::mapAsymmetricSerializer($reg, Model::create(Blocks::POTION_CAULDRON(), Ids::CAULDRON)->properties([$fillLevelProperty, new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_WATER)])); + + $lavaCauldronModel = Model::create(Blocks::LAVA_CAULDRON(), Ids::CAULDRON)->properties([ + $fillLevelProperty, + new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_LAVA) + ]); + $waterCauldronModel = Model::create(Blocks::WATER_CAULDRON(), Ids::CAULDRON)->properties([ + $fillLevelProperty, + new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_WATER) + ]); + $emptyCauldronModel = Model::create(Blocks::CAULDRON(), Ids::CAULDRON)->properties([ + new DummyProperty(StateNames::FILL_LEVEL, 0), + new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_WATER) + ]); + self::mapAsymmetricSerializer($reg, $lavaCauldronModel); + self::mapAsymmetricSerializer($reg, $waterCauldronModel); + self::mapAsymmetricSerializer($reg, $emptyCauldronModel); + $reg->deserializer->map(Ids::CAULDRON, fn(Reader $in) => $in->readInt(StateNames::FILL_LEVEL) === 0 ? + self::deserializeAsymmetric($emptyCauldronModel, $in) : + match ($liquid = $in->readString(StateNames::CAULDRON_LIQUID)) { + StringValues::CAULDRON_LIQUID_WATER => self::deserializeAsymmetric($waterCauldronModel, $in), + StringValues::CAULDRON_LIQUID_LAVA => self::deserializeAsymmetric($lavaCauldronModel, $in), StringValues::CAULDRON_LIQUID_POWDER_SNOW => throw new UnsupportedBlockStateException("Powder snow is not supported yet"), default => throw $in->badValueException(StateNames::CAULDRON_LIQUID, $liquid) - })->setFillLevel($level); - }); + } + ); //mushroom stems, split for consistency with all-sided logs vs normal logs - $reg->serializer->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); - $reg->serializer->map(Blocks::MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); - $reg->deserializer->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match ($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)) { - BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(), - BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(), + $allSidedMushroomStemModel = Model::create(Blocks::ALL_SIDED_MUSHROOM_STEM(), Ids::MUSHROOM_STEM)->properties([new DummyProperty(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)]); + $mushroomStemModel = Model::create(Blocks::MUSHROOM_STEM(), Ids::MUSHROOM_STEM)->properties([new DummyProperty(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)]); + self::mapAsymmetricSerializer($reg, $allSidedMushroomStemModel); + self::mapAsymmetricSerializer($reg, $mushroomStemModel); + $reg->deserializer->map(Ids::MUSHROOM_STEM, fn(Reader $in) : Block => match ($in->readInt(StateNames::HUGE_MUSHROOM_BITS)) { + BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => self::deserializeAsymmetric($allSidedMushroomStemModel, $in), + BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => self::deserializeAsymmetric($mushroomStemModel, $in), default => throw new BlockStateDeserializeException("This state does not exist"), }); //pitcher crop, split into single and double variants as double has different properties and behaviour //this will probably be the most annoying to unify - $reg->serializer->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge()) - ->writeBool(StateNames::UPPER_BLOCK_BIT, false); - }); - $reg->serializer->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $reg->deserializer->map(Ids::PITCHER_CROP, function(Reader $in) : Block{ - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - $top = $in->readBool(StateNames::UPPER_BLOCK_BIT); - if($growth <= PitcherCrop::MAX_AGE){ - //top pitcher crop with age 0-2 is an invalid state - //only the bottom half should exist in this case - return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth); - } - return Blocks::DOUBLE_PITCHER_CROP() - ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) - ->setTop($top); - }); + $pitcherCropModel = Model::create(Blocks::PITCHER_CROP(), Ids::PITCHER_CROP)->properties([ + new IntProperty(StateNames::GROWTH, 0, PitcherCrop::MAX_AGE, fn(PitcherCrop $b) => $b->getAge(), fn(PitcherCrop $b, int $v) => $b->setAge($v)), + new DummyProperty(StateNames::UPPER_BLOCK_BIT, false) + ]); + $doublePitcherCropAgeOffset = PitcherCrop::MAX_AGE + 1; + $doublePitcherCropModel = Model::create(Blocks::DOUBLE_PITCHER_CROP(), Ids::PITCHER_CROP)->properties([ + new IntProperty( + StateNames::GROWTH, + $doublePitcherCropAgeOffset, //TODO: it would be a bit less awkward if the bounds applied _after_ applying the offset, instead of before + 7, + fn(DoublePitcherCrop $b) => $b->getAge(), + fn(DoublePitcherCrop $b, int $v) => $b->setAge(min($v, DoublePitcherCrop::MAX_AGE)), //state may give up to 7, but only up to 4 is valid + offset: -$doublePitcherCropAgeOffset + ), + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePitcherCrop $b) => $b->isTop(), fn(DoublePitcherCrop $b, bool $v) => $b->setTop($v)) + ]); + self::mapAsymmetricSerializer($reg, $pitcherCropModel); + self::mapAsymmetricSerializer($reg, $doublePitcherCropModel); + $reg->deserializer->map(Ids::PITCHER_CROP, fn(Reader $in) => $in->readInt(StateNames::GROWTH) <= PitcherCrop::MAX_AGE ? + ($in->readBool(StateNames::UPPER_BLOCK_BIT) ? + Blocks::AIR() : + self::deserializeAsymmetric($pitcherCropModel, $in) + ) : self::deserializeAsymmetric($doublePitcherCropModel, $in) + ); //these only exist within PM (mapped from tile properties) as they don't support the same properties as a - //normal banner - $reg->serializer->map(Blocks::OMINOUS_BANNER(), function(OminousFloorBanner $block) use ($commonProperties) : Writer{ - $writer = Writer::create(Ids::STANDING_BANNER); - $commonProperties->floorSignLikeRotation->serialize($block, $writer); - return $writer; - }); - $reg->serializer->map(Blocks::OMINOUS_WALL_BANNER(), function(OminousWallBanner $block) use ($commonProperties) : Writer{ - $writer = Writer::create(Ids::WALL_BANNER); - $commonProperties->horizontalFacingClassic->serialize($block, $writer); - return $writer; - }); + //normal banner, therefore no deserializer is needed + self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_BANNER(), Ids::STANDING_BANNER)->properties([$commonProperties->floorSignLikeRotation])); + self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_WALL_BANNER(), Ids::WALL_BANNER)->properties([$commonProperties->horizontalFacingClassic])); } } From 93e33dad8e2b607efba3ef8a84caeb1b75420451 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 16:42:05 +0100 Subject: [PATCH 107/140] tidy CS --- src/data/bedrock/block/convert/VanillaBlockMappings.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index f70de9f4c..e276ed6e7 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -33,7 +33,6 @@ use pocketmine\block\Bed; use pocketmine\block\Bedrock; use pocketmine\block\Bell; use pocketmine\block\BigDripleafHead; -use pocketmine\block\BigDripleafStem; use pocketmine\block\Block; use pocketmine\block\BrewingStand; use pocketmine\block\Cactus; @@ -68,8 +67,6 @@ use pocketmine\block\MobHead; use pocketmine\block\NetherPortal; use pocketmine\block\NetherVines; use pocketmine\block\NetherWartPlant; -use pocketmine\block\OminousFloorBanner; -use pocketmine\block\OminousWallBanner; use pocketmine\block\PinkPetals; use pocketmine\block\PitcherCrop; use pocketmine\block\PoweredRail; @@ -112,7 +109,6 @@ use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; use pocketmine\data\bedrock\block\BlockTypeNames as Ids; use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader; -use pocketmine\data\bedrock\block\convert\BlockStateSerializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer; use pocketmine\data\bedrock\block\convert\property\BoolFromStringProperty; use pocketmine\data\bedrock\block\convert\property\BoolProperty; @@ -1590,6 +1586,7 @@ final class VanillaBlockMappings{ self::mapAsymmetricSerializer($reg, $doublePitcherCropModel); $reg->deserializer->map(Ids::PITCHER_CROP, fn(Reader $in) => $in->readInt(StateNames::GROWTH) <= PitcherCrop::MAX_AGE ? ($in->readBool(StateNames::UPPER_BLOCK_BIT) ? + //top pitcher crop with age 0-2 is an invalid state, only the bottom half should exist in this case Blocks::AIR() : self::deserializeAsymmetric($pitcherCropModel, $in) ) : self::deserializeAsymmetric($doublePitcherCropModel, $in) From 17ecf11a1bae60600fbf56637e50aea714255e5b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 16:49:49 +0100 Subject: [PATCH 108/140] Remove stupid thing PhpStorm keeps doing --- src/data/bedrock/block/convert/property/CommonProperties.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php index 71b87139c..1a3224270 100644 --- a/src/data/bedrock/block/convert/property/CommonProperties.php +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -55,7 +55,6 @@ use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateStringValues; -use pocketmine\data\bedrock\block\convert\non; use pocketmine\math\Facing; use pocketmine\utils\SingletonTrait; From be90c6c399b4fa40ae86bcdd9fd8c3623a0ef78c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 17:01:59 +0100 Subject: [PATCH 109/140] World: trigger readStateFromWorld on tile blocks immediately on load this ensures that the state IDs reflect the actual PM block type, which would probably be important for a bunch of different async things. --- src/world/World.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 8602c5803..bd790c299 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2978,7 +2978,7 @@ class World implements ChunkManager{ unset($this->blockCache[$chunkHash]); unset($this->blockCollisionBoxCache[$chunkHash]); - $this->initChunk($x, $z, $chunkData); + $this->initChunk($x, $z, $chunkData, $chunk); if(ChunkLoadEvent::hasHandlers()){ (new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call(); @@ -2998,7 +2998,7 @@ class World implements ChunkManager{ return $this->chunks[$chunkHash]; } - private function initChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void{ + private function initChunk(int $chunkX, int $chunkZ, ChunkData $chunkData, Chunk $chunk) : void{ $logger = new \PrefixedLogger($this->logger, "Loading chunk $chunkX $chunkZ"); if(count($chunkData->getEntityNBT()) !== 0){ @@ -3063,6 +3063,16 @@ class World implements ChunkManager{ }else{ $this->addTile($tile); } + $expectedStateId = $chunk->getBlockStateId($tilePosition->getFloorX() & Chunk::COORD_MASK, $tilePosition->getFloorY(), $tilePosition->getFloorZ() & Chunk::COORD_MASK); + $actualStateId = $this->getBlock($tilePosition)->getStateId(); + if($expectedStateId !== $actualStateId){ + //state ID was updated by readStateFromWorld - typically because the block pulled some data from the tile + //make sure this is synced to the chunk + //TODO: in the future we should pull tile reading logic out of readStateFromWorld() and do it only + //when the tile is loaded - this would be cleaner and faster + $chunk->setBlockStateId($tilePosition->getFloorX() & Chunk::COORD_MASK, $tilePosition->getFloorY(), $tilePosition->getFloorZ() & Chunk::COORD_MASK, $actualStateId); + $this->logger->debug("Tile " . $tile::class . " at x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z updated block state ID from $expectedStateId to $actualStateId"); + } } foreach(Utils::promoteKeys($deletedTiles) as $saveId => $count){ From 00d617146355815549b0f86fd9a355166fbd5fe7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 20:07:59 +0100 Subject: [PATCH 110/140] Implement hanging signs --- src/block/BaseSign.php | 19 +++++- src/block/BlockTypeIds.php | 35 +++++++++- src/block/CeilingCenterHangingSign.php | 52 +++++++++++++++ src/block/CeilingEdgesHangingSign.php | 51 ++++++++++++++ src/block/VanillaBlocks.php | 51 ++++++++++++++ src/block/WallHangingSign.php | 63 ++++++++++++++++++ src/block/tile/HangingSign.php | 33 ++++++++++ src/block/tile/TileFactory.php | 2 + .../block/convert/VanillaBlockMappings.php | 46 +++++++++++++ .../ItemSerializerDeserializerRegistrar.php | 11 ++++ src/item/HangingSign.php | 66 +++++++++++++++++++ src/item/Item.php | 4 ++ src/item/ItemTypeIds.php | 13 +++- src/item/StringToItemParser.php | 11 ++++ src/item/VanillaItems.php | 22 +++++++ src/world/World.php | 2 +- .../block_factory_consistency_check.json | 66 +++++++++++++++++++ 17 files changed, 543 insertions(+), 4 deletions(-) create mode 100644 src/block/CeilingCenterHangingSign.php create mode 100644 src/block/CeilingEdgesHangingSign.php create mode 100644 src/block/WallHangingSign.php create mode 100644 src/block/tile/HangingSign.php create mode 100644 src/item/HangingSign.php diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 0efaa603c..b01157343 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -103,10 +103,27 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ return SupportType::NONE; } + /** + * @deprecated + */ abstract protected function getSupportingFace() : int; + /** + * @return int[] + */ + protected function getSupportingFaceOptions() : array{ + return [$this->getSupportingFace()]; + } + public function onNearbyBlockChange() : void{ - if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){ + $foundSupport = false; + foreach($this->getSupportingFaceOptions() as $face){ + if($this->getSide($face)->getTypeId() !== BlockTypeIds::AIR){ + $foundSupport = true; + break; + } + } + if(!$foundSupport){ $this->position->getWorld()->useBreakOn($this->position); } } diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index 52b141bcf..e4d49746f 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -789,8 +789,41 @@ final class BlockTypeIds{ public const RESPAWN_ANCHOR = 10759; public const OMINOUS_BANNER = 10760; public const OMINOUS_WALL_BANNER = 10761; + public const ACACIA_CEILING_CENTER_HANGING_SIGN = 10762; + public const ACACIA_CEILING_EDGES_HANGING_SIGN = 10763; + public const ACACIA_WALL_HANGING_SIGN = 10764; + public const BIRCH_CEILING_CENTER_HANGING_SIGN = 10765; + public const BIRCH_CEILING_EDGES_HANGING_SIGN = 10766; + public const BIRCH_WALL_HANGING_SIGN = 10767; + public const CHERRY_CEILING_CENTER_HANGING_SIGN = 10768; + public const CHERRY_CEILING_EDGES_HANGING_SIGN = 10769; + public const CHERRY_WALL_HANGING_SIGN = 10770; + public const CRIMSON_CEILING_CENTER_HANGING_SIGN = 10771; + public const CRIMSON_CEILING_EDGES_HANGING_SIGN = 10772; + public const CRIMSON_WALL_HANGING_SIGN = 10773; + public const DARK_OAK_CEILING_CENTER_HANGING_SIGN = 10774; + public const DARK_OAK_CEILING_EDGES_HANGING_SIGN = 10775; + public const DARK_OAK_WALL_HANGING_SIGN = 10776; + public const JUNGLE_CEILING_CENTER_HANGING_SIGN = 10777; + public const JUNGLE_CEILING_EDGES_HANGING_SIGN = 10778; + public const JUNGLE_WALL_HANGING_SIGN = 10779; + public const MANGROVE_CEILING_CENTER_HANGING_SIGN = 10780; + public const MANGROVE_CEILING_EDGES_HANGING_SIGN = 10781; + public const MANGROVE_WALL_HANGING_SIGN = 10782; + public const OAK_CEILING_CENTER_HANGING_SIGN = 10783; + public const OAK_CEILING_EDGES_HANGING_SIGN = 10784; + public const OAK_WALL_HANGING_SIGN = 10785; + public const PALE_OAK_CEILING_CENTER_HANGING_SIGN = 10786; + public const PALE_OAK_CEILING_EDGES_HANGING_SIGN = 10787; + public const PALE_OAK_WALL_HANGING_SIGN = 10788; + public const SPRUCE_CEILING_CENTER_HANGING_SIGN = 10789; + public const SPRUCE_CEILING_EDGES_HANGING_SIGN = 10790; + public const SPRUCE_WALL_HANGING_SIGN = 10791; + public const WARPED_CEILING_CENTER_HANGING_SIGN = 10792; + public const WARPED_CEILING_EDGES_HANGING_SIGN = 10793; + public const WARPED_WALL_HANGING_SIGN = 10794; - public const FIRST_UNUSED_BLOCK_ID = 10762; + public const FIRST_UNUSED_BLOCK_ID = 10795; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/CeilingCenterHangingSign.php b/src/block/CeilingCenterHangingSign.php new file mode 100644 index 000000000..7078f38d8 --- /dev/null +++ b/src/block/CeilingCenterHangingSign.php @@ -0,0 +1,52 @@ +rotation = self::getRotationFromYaw($player->getLocation()->getYaw()); + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/CeilingEdgesHangingSign.php b/src/block/CeilingEdgesHangingSign.php new file mode 100644 index 000000000..5dafe6932 --- /dev/null +++ b/src/block/CeilingEdgesHangingSign.php @@ -0,0 +1,51 @@ +facing = Facing::opposite($player->getHorizontalFacing()); + } + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 3255c6f6f..bf9f7e5f5 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -45,6 +45,7 @@ use pocketmine\block\tile\EnchantTable as TileEnchantingTable; use pocketmine\block\tile\EnderChest as TileEnderChest; use pocketmine\block\tile\FlowerPot as TileFlowerPot; use pocketmine\block\tile\GlowingItemFrame as TileGlowingItemFrame; +use pocketmine\block\tile\HangingSign as TileHangingSign; use pocketmine\block\tile\Hopper as TileHopper; use pocketmine\block\tile\ItemFrame as TileItemFrame; use pocketmine\block\tile\Jukebox as TileJukebox; @@ -80,6 +81,8 @@ use function strtolower; * @generate-registry-docblock * * @method static WoodenButton ACACIA_BUTTON() + * @method static CeilingCenterHangingSign ACACIA_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign ACACIA_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor ACACIA_DOOR() * @method static WoodenFence ACACIA_FENCE() * @method static FenceGate ACACIA_FENCE_GATE() @@ -92,6 +95,7 @@ use function strtolower; * @method static WoodenSlab ACACIA_SLAB() * @method static WoodenStairs ACACIA_STAIRS() * @method static WoodenTrapdoor ACACIA_TRAPDOOR() + * @method static WallHangingSign ACACIA_WALL_HANGING_SIGN() * @method static WallSign ACACIA_WALL_SIGN() * @method static Wood ACACIA_WOOD() * @method static ActivatorRail ACTIVATOR_RAIL() @@ -122,6 +126,8 @@ use function strtolower; * @method static BigDripleafHead BIG_DRIPLEAF_HEAD() * @method static BigDripleafStem BIG_DRIPLEAF_STEM() * @method static WoodenButton BIRCH_BUTTON() + * @method static CeilingCenterHangingSign BIRCH_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign BIRCH_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor BIRCH_DOOR() * @method static WoodenFence BIRCH_FENCE() * @method static FenceGate BIRCH_FENCE_GATE() @@ -134,6 +140,7 @@ use function strtolower; * @method static WoodenSlab BIRCH_SLAB() * @method static WoodenStairs BIRCH_STAIRS() * @method static WoodenTrapdoor BIRCH_TRAPDOOR() + * @method static WallHangingSign BIRCH_WALL_HANGING_SIGN() * @method static WallSign BIRCH_WALL_SIGN() * @method static Wood BIRCH_WOOD() * @method static Opaque BLACKSTONE() @@ -170,6 +177,8 @@ use function strtolower; * @method static Chain CHAIN() * @method static ChemicalHeat CHEMICAL_HEAT() * @method static WoodenButton CHERRY_BUTTON() + * @method static CeilingCenterHangingSign CHERRY_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign CHERRY_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor CHERRY_DOOR() * @method static WoodenFence CHERRY_FENCE() * @method static FenceGate CHERRY_FENCE_GATE() @@ -181,6 +190,7 @@ use function strtolower; * @method static WoodenSlab CHERRY_SLAB() * @method static WoodenStairs CHERRY_STAIRS() * @method static WoodenTrapdoor CHERRY_TRAPDOOR() + * @method static WallHangingSign CHERRY_WALL_HANGING_SIGN() * @method static WallSign CHERRY_WALL_SIGN() * @method static Wood CHERRY_WOOD() * @method static Chest CHEST() @@ -231,6 +241,8 @@ use function strtolower; * @method static Opaque CRACKED_STONE_BRICKS() * @method static CraftingTable CRAFTING_TABLE() * @method static WoodenButton CRIMSON_BUTTON() + * @method static CeilingCenterHangingSign CRIMSON_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign CRIMSON_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor CRIMSON_DOOR() * @method static WoodenFence CRIMSON_FENCE() * @method static FenceGate CRIMSON_FENCE_GATE() @@ -243,6 +255,7 @@ use function strtolower; * @method static WoodenStairs CRIMSON_STAIRS() * @method static Wood CRIMSON_STEM() * @method static WoodenTrapdoor CRIMSON_TRAPDOOR() + * @method static WallHangingSign CRIMSON_WALL_HANGING_SIGN() * @method static WallSign CRIMSON_WALL_SIGN() * @method static Opaque CRYING_OBSIDIAN() * @method static Copper CUT_COPPER() @@ -254,6 +267,8 @@ use function strtolower; * @method static Slab CUT_SANDSTONE_SLAB() * @method static Flower DANDELION() * @method static WoodenButton DARK_OAK_BUTTON() + * @method static CeilingCenterHangingSign DARK_OAK_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign DARK_OAK_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor DARK_OAK_DOOR() * @method static WoodenFence DARK_OAK_FENCE() * @method static FenceGate DARK_OAK_FENCE_GATE() @@ -266,6 +281,7 @@ use function strtolower; * @method static WoodenSlab DARK_OAK_SLAB() * @method static WoodenStairs DARK_OAK_STAIRS() * @method static WoodenTrapdoor DARK_OAK_TRAPDOOR() + * @method static WallHangingSign DARK_OAK_WALL_HANGING_SIGN() * @method static WallSign DARK_OAK_WALL_SIGN() * @method static Wood DARK_OAK_WOOD() * @method static Opaque DARK_PRISMARINE() @@ -488,6 +504,8 @@ use function strtolower; * @method static ItemFrame ITEM_FRAME() * @method static Jukebox JUKEBOX() * @method static WoodenButton JUNGLE_BUTTON() + * @method static CeilingCenterHangingSign JUNGLE_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign JUNGLE_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor JUNGLE_DOOR() * @method static WoodenFence JUNGLE_FENCE() * @method static FenceGate JUNGLE_FENCE_GATE() @@ -500,6 +518,7 @@ use function strtolower; * @method static WoodenSlab JUNGLE_SLAB() * @method static WoodenStairs JUNGLE_STAIRS() * @method static WoodenTrapdoor JUNGLE_TRAPDOOR() + * @method static WallHangingSign JUNGLE_WALL_HANGING_SIGN() * @method static WallSign JUNGLE_WALL_SIGN() * @method static Wood JUNGLE_WOOD() * @method static ChemistryTable LAB_TABLE() @@ -522,6 +541,8 @@ use function strtolower; * @method static Loom LOOM() * @method static Magma MAGMA() * @method static WoodenButton MANGROVE_BUTTON() + * @method static CeilingCenterHangingSign MANGROVE_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign MANGROVE_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor MANGROVE_DOOR() * @method static WoodenFence MANGROVE_FENCE() * @method static FenceGate MANGROVE_FENCE_GATE() @@ -534,6 +555,7 @@ use function strtolower; * @method static WoodenSlab MANGROVE_SLAB() * @method static WoodenStairs MANGROVE_STAIRS() * @method static WoodenTrapdoor MANGROVE_TRAPDOOR() + * @method static WallHangingSign MANGROVE_WALL_HANGING_SIGN() * @method static WallSign MANGROVE_WALL_SIGN() * @method static Wood MANGROVE_WOOD() * @method static ChemistryTable MATERIAL_REDUCER() @@ -572,6 +594,8 @@ use function strtolower; * @method static Opaque NETHER_WART_BLOCK() * @method static Note NOTE_BLOCK() * @method static WoodenButton OAK_BUTTON() + * @method static CeilingCenterHangingSign OAK_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign OAK_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor OAK_DOOR() * @method static WoodenFence OAK_FENCE() * @method static FenceGate OAK_FENCE_GATE() @@ -584,6 +608,7 @@ use function strtolower; * @method static WoodenSlab OAK_SLAB() * @method static WoodenStairs OAK_STAIRS() * @method static WoodenTrapdoor OAK_TRAPDOOR() + * @method static WallHangingSign OAK_WALL_HANGING_SIGN() * @method static WallSign OAK_WALL_SIGN() * @method static Wood OAK_WOOD() * @method static Opaque OBSIDIAN() @@ -594,6 +619,8 @@ use function strtolower; * @method static PackedIce PACKED_ICE() * @method static Opaque PACKED_MUD() * @method static WoodenButton PALE_OAK_BUTTON() + * @method static CeilingCenterHangingSign PALE_OAK_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign PALE_OAK_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor PALE_OAK_DOOR() * @method static WoodenFence PALE_OAK_FENCE() * @method static FenceGate PALE_OAK_FENCE_GATE() @@ -605,6 +632,7 @@ use function strtolower; * @method static WoodenSlab PALE_OAK_SLAB() * @method static WoodenStairs PALE_OAK_STAIRS() * @method static WoodenTrapdoor PALE_OAK_TRAPDOOR() + * @method static WallHangingSign PALE_OAK_WALL_HANGING_SIGN() * @method static WallSign PALE_OAK_WALL_SIGN() * @method static Wood PALE_OAK_WOOD() * @method static DoublePlant PEONY() @@ -735,6 +763,8 @@ use function strtolower; * @method static Sponge SPONGE() * @method static SporeBlossom SPORE_BLOSSOM() * @method static WoodenButton SPRUCE_BUTTON() + * @method static CeilingCenterHangingSign SPRUCE_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign SPRUCE_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor SPRUCE_DOOR() * @method static WoodenFence SPRUCE_FENCE() * @method static FenceGate SPRUCE_FENCE_GATE() @@ -747,6 +777,7 @@ use function strtolower; * @method static WoodenSlab SPRUCE_SLAB() * @method static WoodenStairs SPRUCE_STAIRS() * @method static WoodenTrapdoor SPRUCE_TRAPDOOR() + * @method static WallHangingSign SPRUCE_WALL_HANGING_SIGN() * @method static WallSign SPRUCE_WALL_SIGN() * @method static Wood SPRUCE_WOOD() * @method static StainedHardenedClay STAINED_CLAY() @@ -790,6 +821,8 @@ use function strtolower; * @method static WallBanner WALL_BANNER() * @method static WallCoralFan WALL_CORAL_FAN() * @method static WoodenButton WARPED_BUTTON() + * @method static CeilingCenterHangingSign WARPED_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign WARPED_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor WARPED_DOOR() * @method static WoodenFence WARPED_FENCE() * @method static FenceGate WARPED_FENCE_GATE() @@ -802,6 +835,7 @@ use function strtolower; * @method static WoodenStairs WARPED_STAIRS() * @method static Wood WARPED_STEM() * @method static WoodenTrapdoor WARPED_TRAPDOOR() + * @method static WallHangingSign WARPED_WALL_HANGING_SIGN() * @method static WallSign WARPED_WALL_SIGN() * @method static Opaque WARPED_WART_BLOCK() * @method static Water WATER() @@ -1396,6 +1430,23 @@ final class VanillaBlocks{ }; self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); + + $hangingSignAsItem = match($woodType){ + WoodType::OAK => VanillaItems::OAK_HANGING_SIGN(...), + WoodType::SPRUCE => VanillaItems::SPRUCE_HANGING_SIGN(...), + WoodType::BIRCH => VanillaItems::BIRCH_HANGING_SIGN(...), + WoodType::JUNGLE => VanillaItems::JUNGLE_HANGING_SIGN(...), + WoodType::ACACIA => VanillaItems::ACACIA_HANGING_SIGN(...), + WoodType::DARK_OAK => VanillaItems::DARK_OAK_HANGING_SIGN(...), + WoodType::MANGROVE => VanillaItems::MANGROVE_HANGING_SIGN(...), + WoodType::CRIMSON => VanillaItems::CRIMSON_HANGING_SIGN(...), + WoodType::WARPED => VanillaItems::WARPED_HANGING_SIGN(...), + WoodType::CHERRY => VanillaItems::CHERRY_HANGING_SIGN(...), + WoodType::PALE_OAK => VanillaItems::PALE_OAK_HANGING_SIGN(...), + }; + self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . "Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . "Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); } } diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php new file mode 100644 index 000000000..c167036b1 --- /dev/null +++ b/src/block/WallHangingSign.php @@ -0,0 +1,63 @@ +facing, clockwise: true); + } + + protected function getSupportingFaceOptions() : array{ + //wall hanging signs can be supported from either end of the post + return [ + Facing::rotateY($this->facing, clockwise: true), + Facing::rotateY($this->facing, clockwise: false) + ]; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(Facing::axis($face) === Axis::Y){ + return false; + } + + $this->facing = Facing::rotateY($face, clockwise: true); + //the front should always face the player if possible + if($player !== null && $this->facing === $player->getHorizontalFacing()){ + $this->facing = Facing::opposite($this->facing); + } + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/tile/HangingSign.php b/src/block/tile/HangingSign.php new file mode 100644 index 000000000..9bf088f47 --- /dev/null +++ b/src/block/tile/HangingSign.php @@ -0,0 +1,33 @@ +register(SporeBlossom::class, ["SporeBlossom", "minecraft:spore_blossom"]); $this->register(MobHead::class, ["Skull", "minecraft:skull"]); $this->register(GlowingItemFrame::class, ["GlowItemFrame"]); + $this->register(HangingSign::class, ["HangingSign", "minecraft:hanging_sign"]); //TODO: ChalkboardBlock //TODO: ChemistryTable diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index e276ed6e7..fc1c6acb6 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -1596,5 +1596,51 @@ final class VanillaBlockMappings{ //normal banner, therefore no deserializer is needed self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_BANNER(), Ids::STANDING_BANNER)->properties([$commonProperties->floorSignLikeRotation])); self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_WALL_BANNER(), Ids::WALL_BANNER)->properties([$commonProperties->horizontalFacingClassic])); + + foreach([ + Ids::ACACIA_HANGING_SIGN => [Blocks::ACACIA_CEILING_CENTER_HANGING_SIGN(), Blocks::ACACIA_CEILING_EDGES_HANGING_SIGN(), Blocks::ACACIA_WALL_HANGING_SIGN()], + Ids::BIRCH_HANGING_SIGN => [Blocks::BIRCH_CEILING_CENTER_HANGING_SIGN(), Blocks::BIRCH_CEILING_EDGES_HANGING_SIGN(), Blocks::BIRCH_WALL_HANGING_SIGN()], + Ids::CHERRY_HANGING_SIGN => [Blocks::CHERRY_CEILING_CENTER_HANGING_SIGN(), Blocks::CHERRY_CEILING_EDGES_HANGING_SIGN(), Blocks::CHERRY_WALL_HANGING_SIGN()], + Ids::CRIMSON_HANGING_SIGN => [Blocks::CRIMSON_CEILING_CENTER_HANGING_SIGN(), Blocks::CRIMSON_CEILING_EDGES_HANGING_SIGN(), Blocks::CRIMSON_WALL_HANGING_SIGN()], + Ids::DARK_OAK_HANGING_SIGN => [Blocks::DARK_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::DARK_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::DARK_OAK_WALL_HANGING_SIGN()], + Ids::JUNGLE_HANGING_SIGN => [Blocks::JUNGLE_CEILING_CENTER_HANGING_SIGN(), Blocks::JUNGLE_CEILING_EDGES_HANGING_SIGN(), Blocks::JUNGLE_WALL_HANGING_SIGN()], + Ids::MANGROVE_HANGING_SIGN => [Blocks::MANGROVE_CEILING_CENTER_HANGING_SIGN(), Blocks::MANGROVE_CEILING_EDGES_HANGING_SIGN(), Blocks::MANGROVE_WALL_HANGING_SIGN()], + Ids::OAK_HANGING_SIGN => [Blocks::OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::OAK_WALL_HANGING_SIGN()], + Ids::PALE_OAK_HANGING_SIGN => [Blocks::PALE_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::PALE_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::PALE_OAK_WALL_HANGING_SIGN()], + Ids::SPRUCE_HANGING_SIGN => [Blocks::SPRUCE_CEILING_CENTER_HANGING_SIGN(), Blocks::SPRUCE_CEILING_EDGES_HANGING_SIGN(), Blocks::SPRUCE_WALL_HANGING_SIGN()], + Ids::WARPED_HANGING_SIGN => [Blocks::WARPED_CEILING_CENTER_HANGING_SIGN(), Blocks::WARPED_CEILING_EDGES_HANGING_SIGN(), Blocks::WARPED_WALL_HANGING_SIGN()], + ] as $id => [$center, $edges, $wall]){ + //attached_bit - true for ceiling center signs, false for ceiling edges signs and wall signs + //hanging - true for all ceiling signs, false for wall signs + //facing_direction - used for ceiling edges signs and wall signs + //ground_sign_direction - used by ceiling center signs only + $centerModel = Model::create($center, $id)->properties([ + $commonProperties->floorSignLikeRotation, + new DummyProperty(StateNames::ATTACHED_BIT, true), + new DummyProperty(StateNames::HANGING, true), + new DummyProperty(StateNames::FACING_DIRECTION, 2) + ]); + $edgesModel = Model::create($edges, $id)->properties([ + new DummyProperty(StateNames::GROUND_SIGN_DIRECTION, 0), + new DummyProperty(StateNames::ATTACHED_BIT, false), + new DummyProperty(StateNames::HANGING, true), + $commonProperties->horizontalFacingClassic, + ]); + $wallModel = Model::create($wall, $id)->properties([ + new DummyProperty(StateNames::GROUND_SIGN_DIRECTION, 0), + new DummyProperty(StateNames::ATTACHED_BIT, false), + new DummyProperty(StateNames::HANGING, false), + $commonProperties->horizontalFacingClassic + ]); + self::mapAsymmetricSerializer($reg, $centerModel); + self::mapAsymmetricSerializer($reg, $edgesModel); + self::mapAsymmetricSerializer($reg, $wallModel); + $reg->deserializer->map($id, fn(Reader $in) => $in->readBool(StateNames::HANGING) ? + ($in->readBool(StateNames::ATTACHED_BIT) ? + self::deserializeAsymmetric($centerModel, $in) : + self::deserializeAsymmetric($edgesModel, $in) + ) : + self::deserializeAsymmetric($wallModel, $in)); + } } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index fa6f88483..f5c03dbeb 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -167,6 +167,7 @@ final class ItemSerializerDeserializerRegistrar{ */ private function register1to1ItemMappings() : void{ $this->map1to1Item(Ids::ACACIA_BOAT, Items::ACACIA_BOAT()); + $this->map1to1Item(Ids::ACACIA_HANGING_SIGN, Items::ACACIA_HANGING_SIGN()); $this->map1to1Item(Ids::ACACIA_SIGN, Items::ACACIA_SIGN()); $this->map1to1Item(Ids::AMETHYST_SHARD, Items::AMETHYST_SHARD()); $this->map1to1Item(Ids::APPLE, Items::APPLE()); @@ -176,6 +177,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::BEETROOT_SEEDS, Items::BEETROOT_SEEDS()); $this->map1to1Item(Ids::BEETROOT_SOUP, Items::BEETROOT_SOUP()); $this->map1to1Item(Ids::BIRCH_BOAT, Items::BIRCH_BOAT()); + $this->map1to1Item(Ids::BIRCH_HANGING_SIGN, Items::BIRCH_HANGING_SIGN()); $this->map1to1Item(Ids::BIRCH_SIGN, Items::BIRCH_SIGN()); $this->map1to1Item(Ids::BLAZE_POWDER, Items::BLAZE_POWDER()); $this->map1to1Item(Ids::BLAZE_ROD, Items::BLAZE_ROD()); @@ -194,6 +196,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::CHAINMAIL_HELMET, Items::CHAINMAIL_HELMET()); $this->map1to1Item(Ids::CHAINMAIL_LEGGINGS, Items::CHAINMAIL_LEGGINGS()); $this->map1to1Item(Ids::CHARCOAL, Items::CHARCOAL()); + $this->map1to1Item(Ids::CHERRY_HANGING_SIGN, Items::CHERRY_HANGING_SIGN()); $this->map1to1Item(Ids::CHERRY_SIGN, Items::CHERRY_SIGN()); $this->map1to1Item(Ids::CHICKEN, Items::RAW_CHICKEN()); $this->map1to1Item(Ids::CHORUS_FRUIT, Items::CHORUS_FRUIT()); @@ -213,8 +216,10 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::COOKED_SALMON, Items::COOKED_SALMON()); $this->map1to1Item(Ids::COOKIE, Items::COOKIE()); $this->map1to1Item(Ids::COPPER_INGOT, Items::COPPER_INGOT()); + $this->map1to1Item(Ids::CRIMSON_HANGING_SIGN, Items::CRIMSON_HANGING_SIGN()); $this->map1to1Item(Ids::CRIMSON_SIGN, Items::CRIMSON_SIGN()); $this->map1to1Item(Ids::DARK_OAK_BOAT, Items::DARK_OAK_BOAT()); + $this->map1to1Item(Ids::DARK_OAK_HANGING_SIGN, Items::DARK_OAK_HANGING_SIGN()); $this->map1to1Item(Ids::DARK_OAK_SIGN, Items::DARK_OAK_SIGN()); $this->map1to1Item(Ids::DIAMOND, Items::DIAMOND()); $this->map1to1Item(Ids::DIAMOND_AXE, Items::DIAMOND_AXE()); @@ -283,6 +288,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::IRON_SHOVEL, Items::IRON_SHOVEL()); $this->map1to1Item(Ids::IRON_SWORD, Items::IRON_SWORD()); $this->map1to1Item(Ids::JUNGLE_BOAT, Items::JUNGLE_BOAT()); + $this->map1to1Item(Ids::JUNGLE_HANGING_SIGN, Items::JUNGLE_HANGING_SIGN()); $this->map1to1Item(Ids::JUNGLE_SIGN, Items::JUNGLE_SIGN()); $this->map1to1Item(Ids::LAPIS_LAZULI, Items::LAPIS_LAZULI()); $this->map1to1Item(Ids::LAVA_BUCKET, Items::LAVA_BUCKET()); @@ -293,6 +299,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::LEATHER_LEGGINGS, Items::LEATHER_PANTS()); $this->map1to1Item(Ids::MAGMA_CREAM, Items::MAGMA_CREAM()); $this->map1to1Item(Ids::MANGROVE_BOAT, Items::MANGROVE_BOAT()); + $this->map1to1Item(Ids::MANGROVE_HANGING_SIGN, Items::MANGROVE_HANGING_SIGN()); $this->map1to1Item(Ids::MANGROVE_SIGN, Items::MANGROVE_SIGN()); $this->map1to1Item(Ids::MELON_SEEDS, Items::MELON_SEEDS()); $this->map1to1Item(Ids::MELON_SLICE, Items::MELON()); @@ -336,8 +343,10 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::NETHERITE_SWORD, Items::NETHERITE_SWORD()); $this->map1to1Item(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items::NETHERITE_UPGRADE_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::OAK_BOAT, Items::OAK_BOAT()); + $this->map1to1Item(Ids::OAK_HANGING_SIGN, Items::OAK_HANGING_SIGN()); $this->map1to1Item(Ids::OAK_SIGN, Items::OAK_SIGN()); $this->map1to1Item(Ids::PAINTING, Items::PAINTING()); + $this->map1to1Item(Ids::PALE_OAK_HANGING_SIGN, Items::PALE_OAK_HANGING_SIGN()); $this->map1to1Item(Ids::PALE_OAK_SIGN, Items::PALE_OAK_SIGN()); $this->map1to1Item(Ids::PAPER, Items::PAPER()); $this->map1to1Item(Ids::PHANTOM_MEMBRANE, Items::PHANTOM_MEMBRANE()); @@ -378,6 +387,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::SPIDER_EYE, Items::SPIDER_EYE()); $this->map1to1Item(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::SPRUCE_BOAT, Items::SPRUCE_BOAT()); + $this->map1to1Item(Ids::SPRUCE_HANGING_SIGN, Items::SPRUCE_HANGING_SIGN()); $this->map1to1Item(Ids::SPRUCE_SIGN, Items::SPRUCE_SIGN()); $this->map1to1Item(Ids::SPYGLASS, Items::SPYGLASS()); $this->map1to1Item(Ids::SQUID_SPAWN_EGG, Items::SQUID_SPAWN_EGG()); @@ -398,6 +408,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE, Items::VEX_ARMOR_TRIM_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::VILLAGER_SPAWN_EGG, Items::VILLAGER_SPAWN_EGG()); $this->map1to1Item(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WARD_ARMOR_TRIM_SMITHING_TEMPLATE()); + $this->map1to1Item(Ids::WARPED_HANGING_SIGN, Items::WARPED_HANGING_SIGN()); $this->map1to1Item(Ids::WARPED_SIGN, Items::WARPED_SIGN()); $this->map1to1Item(Ids::WATER_BUCKET, Items::WATER_BUCKET()); $this->map1to1Item(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php new file mode 100644 index 000000000..e3e95d68a --- /dev/null +++ b/src/item/HangingSign.php @@ -0,0 +1,66 @@ +getSide(Facing::UP)->getSupportType(Facing::DOWN) === SupportType::CENTER ? + $this->centerPointCeilingVariant : + $this->edgePointCeilingVariant + : $this->wallVariant; + return clone $result; + } + + public function getBlock(?int $clickedFace = null) : Block{ + //we don't have enough information here to decide which ceiling type to use + return $clickedFace === Facing::DOWN ? clone $this->centerPointCeilingVariant : clone $this->wallVariant; + } + + public function getMaxStackSize() : int{ + return 16; + } + + public function getFuelTime() : int{ + return 200; + } +} diff --git a/src/item/Item.php b/src/item/Item.php index 205f15e13..6786238b0 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -485,6 +485,10 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } + public function getPlacementBlock(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ + return $this->getBlock($face); + } + /** * Returns the block corresponding to this Item. */ diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index 37be3ab9e..af32cbcc2 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -335,8 +335,19 @@ final class ItemTypeIds{ public const RECORD_CREATOR_MUSIC_BOX = 20296; public const RECORD_PRECIPICE = 20297; public const OMINOUS_BANNER = 20298; + public const ACACIA_HANGING_SIGN = 20299; + public const BIRCH_HANGING_SIGN = 20300; + public const CHERRY_HANGING_SIGN = 20301; + public const CRIMSON_HANGING_SIGN = 20302; + public const DARK_OAK_HANGING_SIGN = 20303; + public const JUNGLE_HANGING_SIGN = 20304; + public const MANGROVE_HANGING_SIGN = 20305; + public const OAK_HANGING_SIGN = 20306; + public const PALE_OAK_HANGING_SIGN = 20307; + public const SPRUCE_HANGING_SIGN = 20308; + public const WARPED_HANGING_SIGN = 20309; - public const FIRST_UNUSED_ITEM_ID = 20299; + public const FIRST_UNUSED_ITEM_ID = 20310; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 7a90babed..2f316f66b 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1232,6 +1232,7 @@ final class StringToItemParser extends StringToTParser{ private static function registerItems(self $result) : void{ $result->register("acacia_boat", fn() => Items::ACACIA_BOAT()); + $result->register("acacia_hanging_sign", fn() => Items::ACACIA_HANGING_SIGN()); $result->register("amethyst_shard", fn() => Items::AMETHYST_SHARD()); $result->register("antidote", fn() => Items::MEDICINE()->setType(MedicineType::ANTIDOTE)); $result->register("apple", fn() => Items::APPLE()); @@ -1246,6 +1247,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("beetroot_seeds", fn() => Items::BEETROOT_SEEDS()); $result->register("beetroot_soup", fn() => Items::BEETROOT_SOUP()); $result->register("birch_boat", fn() => Items::BIRCH_BOAT()); + $result->register("birch_hanging_sign", fn() => Items::BIRCH_HANGING_SIGN()); $result->register("blaze_powder", fn() => Items::BLAZE_POWDER()); $result->register("blaze_rod", fn() => Items::BLAZE_ROD()); $result->register("bleach", fn() => Items::BLEACH()); @@ -1307,6 +1309,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("chemical_sulphate", fn() => Items::CHEMICAL_SULPHATE()); $result->register("chemical_tungsten_chloride", fn() => Items::CHEMICAL_TUNGSTEN_CHLORIDE()); $result->register("chemical_water", fn() => Items::CHEMICAL_WATER()); + $result->register("cherry_hanging_sign", fn() => Items::CHERRY_HANGING_SIGN()); $result->register("chicken", fn() => Items::RAW_CHICKEN()); $result->register("chorus_fruit", fn() => Items::CHORUS_FRUIT()); $result->register("chorus_fruit_popped", fn() => Items::POPPED_CHORUS_FRUIT()); @@ -1331,7 +1334,9 @@ final class StringToItemParser extends StringToTParser{ $result->register("cooked_salmon", fn() => Items::COOKED_SALMON()); $result->register("cookie", fn() => Items::COOKIE()); $result->register("copper_ingot", fn() => Items::COPPER_INGOT()); + $result->register("crimson_hanging_sign", fn() => Items::CRIMSON_HANGING_SIGN()); $result->register("dark_oak_boat", fn() => Items::DARK_OAK_BOAT()); + $result->register("dark_oak_hanging_sign", fn() => Items::DARK_OAK_HANGING_SIGN()); $result->register("diamond", fn() => Items::DIAMOND()); $result->register("diamond_axe", fn() => Items::DIAMOND_AXE()); $result->register("diamond_boots", fn() => Items::DIAMOND_BOOTS()); @@ -1416,6 +1421,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("iron_shovel", fn() => Items::IRON_SHOVEL()); $result->register("iron_sword", fn() => Items::IRON_SWORD()); $result->register("jungle_boat", fn() => Items::JUNGLE_BOAT()); + $result->register("jungle_hanging_sign", fn() => Items::JUNGLE_HANGING_SIGN()); $result->register("lapis_lazuli", fn() => Items::LAPIS_LAZULI()); $result->register("lava_bucket", fn() => Items::LAVA_BUCKET()); $result->register("leather", fn() => Items::LEATHER()); @@ -1427,6 +1433,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("leather_pants", fn() => Items::LEATHER_PANTS()); $result->register("leather_tunic", fn() => Items::LEATHER_TUNIC()); $result->register("magma_cream", fn() => Items::MAGMA_CREAM()); + $result->register("mangrove_hanging_sign", fn() => Items::MANGROVE_HANGING_SIGN()); $result->register("melon", fn() => Items::MELON()); $result->register("melon_seeds", fn() => Items::MELON_SEEDS()); $result->register("melon_slice", fn() => Items::MELON()); @@ -1458,7 +1465,9 @@ final class StringToItemParser extends StringToTParser{ $result->register("netherstar", fn() => Items::NETHER_STAR()); $result->register("netherite_upgrade_smithing_template", fn() => Items::NETHERITE_UPGRADE_SMITHING_TEMPLATE()); $result->register("oak_boat", fn() => Items::OAK_BOAT()); + $result->register("oak_hanging_sign", fn() => Items::OAK_HANGING_SIGN()); $result->register("painting", fn() => Items::PAINTING()); + $result->register("pale_oak_hanging_sign", fn() => Items::PALE_OAK_HANGING_SIGN()); $result->register("paper", fn() => Items::PAPER()); $result->register("phantom_membrane", fn() => Items::PHANTOM_MEMBRANE()); $result->register("pitcher_pod", fn() => Items::PITCHER_POD()); @@ -1532,6 +1541,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("spire_armor_trim_smithing_template", fn() => Items::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE()); $result->register("splash_potion", fn() => Items::SPLASH_POTION()); $result->register("spruce_boat", fn() => Items::SPRUCE_BOAT()); + $result->register("spruce_hanging_sign", fn() => Items::SPRUCE_HANGING_SIGN()); $result->register("spyglass", fn() => Items::SPYGLASS()); $result->register("squid_spawn_egg", fn() => Items::SQUID_SPAWN_EGG()); $result->register("steak", fn() => Items::STEAK()); @@ -1555,6 +1565,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("turtle_shell_piece", fn() => Items::SCUTE()); $result->register("villager_spawn_egg", fn() => Items::VILLAGER_SPAWN_EGG()); $result->register("ward_armor_trim_smithing_template", fn() => Items::WARD_ARMOR_TRIM_SMITHING_TEMPLATE()); + $result->register("warped_hanging_sign", fn() => Items::WARPED_HANGING_SIGN()); $result->register("water_bucket", fn() => Items::WATER_BUCKET()); $result->register("wayfinder_armor_trim_smithing_template", fn() => Items::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE()); $result->register("wheat", fn() => Items::WHEAT()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 7103d8878..e4eeffc1d 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -48,6 +48,7 @@ use function strtolower; * @generate-registry-docblock * * @method static Boat ACACIA_BOAT() + * @method static HangingSign ACACIA_HANGING_SIGN() * @method static ItemBlockWallOrFloor ACACIA_SIGN() * @method static ItemBlock AIR() * @method static Item AMETHYST_SHARD() @@ -60,6 +61,7 @@ use function strtolower; * @method static BeetrootSeeds BEETROOT_SEEDS() * @method static BeetrootSoup BEETROOT_SOUP() * @method static Boat BIRCH_BOAT() + * @method static HangingSign BIRCH_HANGING_SIGN() * @method static ItemBlockWallOrFloor BIRCH_SIGN() * @method static Item BLAZE_POWDER() * @method static BlazeRod BLAZE_ROD() @@ -116,6 +118,7 @@ use function strtolower; * @method static Item CHEMICAL_SULPHATE() * @method static Item CHEMICAL_TUNGSTEN_CHLORIDE() * @method static Item CHEMICAL_WATER() + * @method static HangingSign CHERRY_HANGING_SIGN() * @method static ItemBlockWallOrFloor CHERRY_SIGN() * @method static ChorusFruit CHORUS_FRUIT() * @method static Item CLAY() @@ -134,8 +137,10 @@ use function strtolower; * @method static Cookie COOKIE() * @method static Item COPPER_INGOT() * @method static CoralFan CORAL_FAN() + * @method static HangingSign CRIMSON_HANGING_SIGN() * @method static ItemBlockWallOrFloor CRIMSON_SIGN() * @method static Boat DARK_OAK_BOAT() + * @method static HangingSign DARK_OAK_HANGING_SIGN() * @method static ItemBlockWallOrFloor DARK_OAK_SIGN() * @method static Item DIAMOND() * @method static Axe DIAMOND_AXE() @@ -206,6 +211,7 @@ use function strtolower; * @method static Shovel IRON_SHOVEL() * @method static Sword IRON_SWORD() * @method static Boat JUNGLE_BOAT() + * @method static HangingSign JUNGLE_HANGING_SIGN() * @method static ItemBlockWallOrFloor JUNGLE_SIGN() * @method static Item LAPIS_LAZULI() * @method static LiquidBucket LAVA_BUCKET() @@ -216,6 +222,7 @@ use function strtolower; * @method static Armor LEATHER_TUNIC() * @method static Item MAGMA_CREAM() * @method static Boat MANGROVE_BOAT() + * @method static HangingSign MANGROVE_HANGING_SIGN() * @method static ItemBlockWallOrFloor MANGROVE_SIGN() * @method static Medicine MEDICINE() * @method static Melon MELON() @@ -241,9 +248,11 @@ use function strtolower; * @method static Item NETHER_QUARTZ() * @method static Item NETHER_STAR() * @method static Boat OAK_BOAT() + * @method static HangingSign OAK_HANGING_SIGN() * @method static ItemBlockWallOrFloor OAK_SIGN() * @method static ItemBlockWallOrFloor OMINOUS_BANNER() * @method static PaintingItem PAINTING() + * @method static HangingSign PALE_OAK_HANGING_SIGN() * @method static ItemBlockWallOrFloor PALE_OAK_SIGN() * @method static Item PAPER() * @method static Item PHANTOM_MEMBRANE() @@ -308,6 +317,7 @@ use function strtolower; * @method static Item SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static SplashPotion SPLASH_POTION() * @method static Boat SPRUCE_BOAT() + * @method static HangingSign SPRUCE_HANGING_SIGN() * @method static ItemBlockWallOrFloor SPRUCE_SIGN() * @method static Spyglass SPYGLASS() * @method static SpawnEgg SQUID_SPAWN_EGG() @@ -329,6 +339,7 @@ use function strtolower; * @method static Item VEX_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static SpawnEgg VILLAGER_SPAWN_EGG() * @method static Item WARD_ARMOR_TRIM_SMITHING_TEMPLATE() + * @method static HangingSign WARPED_HANGING_SIGN() * @method static ItemBlockWallOrFloor WARPED_SIGN() * @method static LiquidBucket WATER_BUCKET() * @method static Item WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE() @@ -398,6 +409,7 @@ final class VanillaItems{ self::_registryRegister("air", Blocks::AIR()->asItem()->setCount(0)); self::register("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN())); + self::register("acacia_hanging_sign", fn(IID $id) => new HangingSign($id, "Acacia Hanging Sign", Blocks::ACACIA_CEILING_CENTER_HANGING_SIGN(), Blocks::ACACIA_CEILING_EDGES_HANGING_SIGN(), Blocks::ACACIA_WALL_HANGING_SIGN())); self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard")); self::register("apple", fn(IID $id) => new Apple($id, "Apple")); self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow")); @@ -408,6 +420,7 @@ final class VanillaItems{ self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds")); self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup")); self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN())); + self::register("birch_hanging_sign", fn(IID $id) => new HangingSign($id, "Birch Hanging Sign", Blocks::BIRCH_CEILING_CENTER_HANGING_SIGN(), Blocks::BIRCH_CEILING_EDGES_HANGING_SIGN(), Blocks::BIRCH_WALL_HANGING_SIGN())); self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder")); self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod")); self::register("bleach", fn(IID $id) => new Item($id, "Bleach")); @@ -422,6 +435,7 @@ final class VanillaItems{ self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot")); self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal")); self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN())); + self::register("cherry_hanging_sign", fn(IID $id) => new HangingSign($id, "Cherry Hanging Sign", Blocks::CHERRY_CEILING_CENTER_HANGING_SIGN(), Blocks::CHERRY_CEILING_EDGES_HANGING_SIGN(), Blocks::CHERRY_WALL_HANGING_SIGN())); self::register("chemical_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide")); self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia")); self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate")); @@ -477,7 +491,9 @@ final class VanillaItems{ self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot")); self::register("coral_fan", fn(IID $id) => new CoralFan($id)); self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN())); + self::register("crimson_hanging_sign", fn(IID $id) => new HangingSign($id, "Crimson Hanging Sign", Blocks::CRIMSON_CEILING_CENTER_HANGING_SIGN(), Blocks::CRIMSON_CEILING_EDGES_HANGING_SIGN(), Blocks::CRIMSON_WALL_HANGING_SIGN())); self::register("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN())); + self::register("dark_oak_hanging_sign", fn(IID $id) => new HangingSign($id, "Dark Oak Hanging Sign", Blocks::DARK_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::DARK_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::DARK_OAK_WALL_HANGING_SIGN())); self::register("diamond", fn(IID $id) => new Item($id, "Diamond")); self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)")); self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath")); @@ -518,11 +534,13 @@ final class VanillaItems{ self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot")); self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget")); self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN())); + self::register("jungle_hanging_sign", fn(IID $id) => new HangingSign($id, "Jungle Hanging Sign", Blocks::JUNGLE_CEILING_CENTER_HANGING_SIGN(), Blocks::JUNGLE_CEILING_EDGES_HANGING_SIGN(), Blocks::JUNGLE_WALL_HANGING_SIGN())); self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli")); self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA())); self::register("leather", fn(IID $id) => new Item($id, "Leather")); self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream")); self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); + self::register("mangrove_hanging_sign", fn(IID $id) => new HangingSign($id, "Mangrove Hanging Sign", Blocks::MANGROVE_CEILING_CENTER_HANGING_SIGN(), Blocks::MANGROVE_CEILING_EDGES_HANGING_SIGN(), Blocks::MANGROVE_WALL_HANGING_SIGN())); self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine")); self::register("melon", fn(IID $id) => new Melon($id, "Melon")); self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds")); @@ -541,9 +559,11 @@ final class VanillaItems{ public function isFireProof() : bool{ return true; } }); self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); + self::register("oak_hanging_sign", fn(IID $id) => new HangingSign($id, "Oak Hanging Sign", Blocks::OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::OAK_WALL_HANGING_SIGN())); self::register("ominous_banner", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OMINOUS_BANNER(), Blocks::OMINOUS_WALL_BANNER())); self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting")); self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN())); + self::register("pale_oak_hanging_sign", fn(IID $id) => new HangingSign($id, "Pale Oak Hanging Sign", Blocks::PALE_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::PALE_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::PALE_OAK_WALL_HANGING_SIGN())); self::register("paper", fn(IID $id) => new Item($id, "Paper")); self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane")); self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod")); @@ -600,6 +620,7 @@ final class VanillaItems{ self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye")); self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion")); self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN())); + self::register("spruce_hanging_sign", fn(IID $id) => new HangingSign($id, "Spruce Hanging Sign", Blocks::SPRUCE_CEILING_CENTER_HANGING_SIGN(), Blocks::SPRUCE_CEILING_EDGES_HANGING_SIGN(), Blocks::SPRUCE_WALL_HANGING_SIGN())); self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass")); self::register("steak", fn(IID $id) => new Steak($id, "Steak")); self::register("stick", fn(IID $id) => new Stick($id, "Stick")); @@ -610,6 +631,7 @@ final class VanillaItems{ self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds")); self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying")); self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN())); + self::register("warped_hanging_sign", fn(IID $id) => new HangingSign($id, "Warped Hanging Sign", Blocks::WARPED_CEILING_CENTER_HANGING_SIGN(), Blocks::WARPED_CEILING_EDGES_HANGING_SIGN(), Blocks::WARPED_WALL_HANGING_SIGN())); self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER())); self::register("wheat", fn(IID $id) => new Item($id, "Wheat")); self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds")); diff --git a/src/world/World.php b/src/world/World.php index bd790c299..4f2e222ca 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2298,7 +2298,7 @@ class World implements ChunkManager{ if($item->isNull() || !$item->canBePlaced()){ return false; } - $hand = $item->getBlock($face); + $hand = $item->getPlacementBlock($blockReplace, $blockClicked, $face, $clickVector); $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 86a44113f..b96607c0b 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -1,6 +1,8 @@ { "stateCounts": { "ACACIA_BUTTON": 12, + "ACACIA_CEILING_CENTER_HANGING_SIGN": 16, + "ACACIA_CEILING_EDGES_HANGING_SIGN": 4, "ACACIA_DOOR": 32, "ACACIA_FENCE": 1, "ACACIA_FENCE_GATE": 16, @@ -13,6 +15,7 @@ "ACACIA_SLAB": 3, "ACACIA_STAIRS": 8, "ACACIA_TRAPDOOR": 16, + "ACACIA_WALL_HANGING_SIGN": 4, "ACACIA_WALL_SIGN": 4, "ACACIA_WOOD": 6, "ACTIVATOR_RAIL": 12, @@ -43,6 +46,8 @@ "BIG_DRIPLEAF_HEAD": 16, "BIG_DRIPLEAF_STEM": 4, "BIRCH_BUTTON": 12, + "BIRCH_CEILING_CENTER_HANGING_SIGN": 16, + "BIRCH_CEILING_EDGES_HANGING_SIGN": 4, "BIRCH_DOOR": 32, "BIRCH_FENCE": 1, "BIRCH_FENCE_GATE": 16, @@ -55,6 +60,7 @@ "BIRCH_SLAB": 3, "BIRCH_STAIRS": 8, "BIRCH_TRAPDOOR": 16, + "BIRCH_WALL_HANGING_SIGN": 4, "BIRCH_WALL_SIGN": 4, "BIRCH_WOOD": 6, "BLACKSTONE": 1, @@ -91,6 +97,8 @@ "CHAIN": 3, "CHEMICAL_HEAT": 1, "CHERRY_BUTTON": 12, + "CHERRY_CEILING_CENTER_HANGING_SIGN": 16, + "CHERRY_CEILING_EDGES_HANGING_SIGN": 4, "CHERRY_DOOR": 32, "CHERRY_FENCE": 1, "CHERRY_FENCE_GATE": 16, @@ -102,6 +110,7 @@ "CHERRY_SLAB": 3, "CHERRY_STAIRS": 8, "CHERRY_TRAPDOOR": 16, + "CHERRY_WALL_HANGING_SIGN": 4, "CHERRY_WALL_SIGN": 4, "CHERRY_WOOD": 6, "CHEST": 4, @@ -152,6 +161,8 @@ "CRACKED_STONE_BRICKS": 1, "CRAFTING_TABLE": 1, "CRIMSON_BUTTON": 12, + "CRIMSON_CEILING_CENTER_HANGING_SIGN": 16, + "CRIMSON_CEILING_EDGES_HANGING_SIGN": 4, "CRIMSON_DOOR": 32, "CRIMSON_FENCE": 1, "CRIMSON_FENCE_GATE": 16, @@ -164,6 +175,7 @@ "CRIMSON_STAIRS": 8, "CRIMSON_STEM": 6, "CRIMSON_TRAPDOOR": 16, + "CRIMSON_WALL_HANGING_SIGN": 4, "CRIMSON_WALL_SIGN": 4, "CRYING_OBSIDIAN": 1, "CUT_COPPER": 8, @@ -175,6 +187,8 @@ "CUT_SANDSTONE_SLAB": 3, "DANDELION": 1, "DARK_OAK_BUTTON": 12, + "DARK_OAK_CEILING_CENTER_HANGING_SIGN": 16, + "DARK_OAK_CEILING_EDGES_HANGING_SIGN": 4, "DARK_OAK_DOOR": 32, "DARK_OAK_FENCE": 1, "DARK_OAK_FENCE_GATE": 16, @@ -187,6 +201,7 @@ "DARK_OAK_SLAB": 3, "DARK_OAK_STAIRS": 8, "DARK_OAK_TRAPDOOR": 16, + "DARK_OAK_WALL_HANGING_SIGN": 4, "DARK_OAK_WALL_SIGN": 4, "DARK_OAK_WOOD": 6, "DARK_PRISMARINE": 1, @@ -409,6 +424,8 @@ "ITEM_FRAME": 12, "JUKEBOX": 1, "JUNGLE_BUTTON": 12, + "JUNGLE_CEILING_CENTER_HANGING_SIGN": 16, + "JUNGLE_CEILING_EDGES_HANGING_SIGN": 4, "JUNGLE_DOOR": 32, "JUNGLE_FENCE": 1, "JUNGLE_FENCE_GATE": 16, @@ -421,6 +438,7 @@ "JUNGLE_SLAB": 3, "JUNGLE_STAIRS": 8, "JUNGLE_TRAPDOOR": 16, + "JUNGLE_WALL_HANGING_SIGN": 4, "JUNGLE_WALL_SIGN": 4, "JUNGLE_WOOD": 6, "LAB_TABLE": 4, @@ -443,6 +461,8 @@ "LOOM": 4, "MAGMA": 1, "MANGROVE_BUTTON": 12, + "MANGROVE_CEILING_CENTER_HANGING_SIGN": 16, + "MANGROVE_CEILING_EDGES_HANGING_SIGN": 4, "MANGROVE_DOOR": 32, "MANGROVE_FENCE": 1, "MANGROVE_FENCE_GATE": 16, @@ -455,6 +475,7 @@ "MANGROVE_SLAB": 3, "MANGROVE_STAIRS": 8, "MANGROVE_TRAPDOOR": 16, + "MANGROVE_WALL_HANGING_SIGN": 4, "MANGROVE_WALL_SIGN": 4, "MANGROVE_WOOD": 6, "MATERIAL_REDUCER": 4, @@ -493,6 +514,8 @@ "NETHER_WART_BLOCK": 1, "NOTE_BLOCK": 1, "OAK_BUTTON": 12, + "OAK_CEILING_CENTER_HANGING_SIGN": 16, + "OAK_CEILING_EDGES_HANGING_SIGN": 4, "OAK_DOOR": 32, "OAK_FENCE": 1, "OAK_FENCE_GATE": 16, @@ -505,6 +528,7 @@ "OAK_SLAB": 3, "OAK_STAIRS": 8, "OAK_TRAPDOOR": 16, + "OAK_WALL_HANGING_SIGN": 4, "OAK_WALL_SIGN": 4, "OAK_WOOD": 6, "OBSIDIAN": 1, @@ -515,6 +539,8 @@ "PACKED_ICE": 1, "PACKED_MUD": 1, "PALE_OAK_BUTTON": 12, + "PALE_OAK_CEILING_CENTER_HANGING_SIGN": 16, + "PALE_OAK_CEILING_EDGES_HANGING_SIGN": 4, "PALE_OAK_DOOR": 32, "PALE_OAK_FENCE": 1, "PALE_OAK_FENCE_GATE": 16, @@ -526,6 +552,7 @@ "PALE_OAK_SLAB": 3, "PALE_OAK_STAIRS": 8, "PALE_OAK_TRAPDOOR": 16, + "PALE_OAK_WALL_HANGING_SIGN": 4, "PALE_OAK_WALL_SIGN": 4, "PALE_OAK_WOOD": 6, "PEONY": 2, @@ -656,6 +683,8 @@ "SPONGE": 2, "SPORE_BLOSSOM": 1, "SPRUCE_BUTTON": 12, + "SPRUCE_CEILING_CENTER_HANGING_SIGN": 16, + "SPRUCE_CEILING_EDGES_HANGING_SIGN": 4, "SPRUCE_DOOR": 32, "SPRUCE_FENCE": 1, "SPRUCE_FENCE_GATE": 16, @@ -668,6 +697,7 @@ "SPRUCE_SLAB": 3, "SPRUCE_STAIRS": 8, "SPRUCE_TRAPDOOR": 16, + "SPRUCE_WALL_HANGING_SIGN": 4, "SPRUCE_WALL_SIGN": 4, "SPRUCE_WOOD": 6, "STAINED_CLAY": 16, @@ -711,6 +741,8 @@ "WALL_BANNER": 64, "WALL_CORAL_FAN": 40, "WARPED_BUTTON": 12, + "WARPED_CEILING_CENTER_HANGING_SIGN": 16, + "WARPED_CEILING_EDGES_HANGING_SIGN": 4, "WARPED_DOOR": 32, "WARPED_FENCE": 1, "WARPED_FENCE_GATE": 16, @@ -723,6 +755,7 @@ "WARPED_STAIRS": 8, "WARPED_STEM": 6, "WARPED_TRAPDOOR": 16, + "WARPED_WALL_HANGING_SIGN": 4, "WARPED_WALL_SIGN": 4, "WARPED_WART_BLOCK": 1, "WATER": 32, @@ -736,26 +769,41 @@ "WOOL": 16 }, "tiles": { + "ACACIA_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "ACACIA_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "ACACIA_SIGN": "pocketmine\\block\\tile\\Sign", + "ACACIA_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "ACACIA_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "BANNER": "pocketmine\\block\\tile\\Banner", "BARREL": "pocketmine\\block\\tile\\Barrel", "BEACON": "pocketmine\\block\\tile\\Beacon", "BED": "pocketmine\\block\\tile\\Bed", "BELL": "pocketmine\\block\\tile\\Bell", + "BIRCH_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "BIRCH_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "BIRCH_SIGN": "pocketmine\\block\\tile\\Sign", + "BIRCH_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "BIRCH_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "BLAST_FURNACE": "pocketmine\\block\\tile\\BlastFurnace", "BREWING_STAND": "pocketmine\\block\\tile\\BrewingStand", "CAMPFIRE": "pocketmine\\block\\tile\\Campfire", "CAULDRON": "pocketmine\\block\\tile\\Cauldron", + "CHERRY_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "CHERRY_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CHERRY_SIGN": "pocketmine\\block\\tile\\Sign", + "CHERRY_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CHERRY_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "CHEST": "pocketmine\\block\\tile\\Chest", "CHISELED_BOOKSHELF": "pocketmine\\block\\tile\\ChiseledBookshelf", + "CRIMSON_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "CRIMSON_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CRIMSON_SIGN": "pocketmine\\block\\tile\\Sign", + "CRIMSON_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CRIMSON_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "DARK_OAK_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "DARK_OAK_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "DARK_OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "DARK_OAK_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "DARK_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "DAYLIGHT_SENSOR": "pocketmine\\block\\tile\\DaylightSensor", "DYED_SHULKER_BOX": "pocketmine\\block\\tile\\ShulkerBox", @@ -767,31 +815,49 @@ "HOPPER": "pocketmine\\block\\tile\\Hopper", "ITEM_FRAME": "pocketmine\\block\\tile\\ItemFrame", "JUKEBOX": "pocketmine\\block\\tile\\Jukebox", + "JUNGLE_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "JUNGLE_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "JUNGLE_SIGN": "pocketmine\\block\\tile\\Sign", + "JUNGLE_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "JUNGLE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "LAVA_CAULDRON": "pocketmine\\block\\tile\\Cauldron", "LECTERN": "pocketmine\\block\\tile\\Lectern", + "MANGROVE_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "MANGROVE_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "MANGROVE_SIGN": "pocketmine\\block\\tile\\Sign", + "MANGROVE_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "MANGROVE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "MOB_HEAD": "pocketmine\\block\\tile\\MobHead", "MONSTER_SPAWNER": "pocketmine\\block\\tile\\MonsterSpawner", "NOTE_BLOCK": "pocketmine\\block\\tile\\Note", + "OAK_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "OAK_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "OAK_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "OMINOUS_BANNER": "pocketmine\\block\\tile\\Banner", "OMINOUS_WALL_BANNER": "pocketmine\\block\\tile\\Banner", + "PALE_OAK_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "PALE_OAK_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "PALE_OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "PALE_OAK_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "PALE_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "POTION_CAULDRON": "pocketmine\\block\\tile\\Cauldron", "REDSTONE_COMPARATOR": "pocketmine\\block\\tile\\Comparator", "SHULKER_BOX": "pocketmine\\block\\tile\\ShulkerBox", "SMOKER": "pocketmine\\block\\tile\\Smoker", "SOUL_CAMPFIRE": "pocketmine\\block\\tile\\Campfire", + "SPRUCE_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "SPRUCE_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "SPRUCE_SIGN": "pocketmine\\block\\tile\\Sign", + "SPRUCE_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "SPRUCE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "TRAPPED_CHEST": "pocketmine\\block\\tile\\Chest", "WALL_BANNER": "pocketmine\\block\\tile\\Banner", + "WARPED_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "WARPED_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "WARPED_SIGN": "pocketmine\\block\\tile\\Sign", + "WARPED_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "WARPED_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "WATER_CAULDRON": "pocketmine\\block\\tile\\Cauldron" } From 0e498720bddc34407dc2b48526ad9c6783624c13 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 20:10:34 +0100 Subject: [PATCH 111/140] Regenerate phpstan-bugs baseline --- tests/phpstan/configs/phpstan-bugs.neon | 78 +++++++++++++++++++++---- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index cb92bf968..e67f5768e 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -18,78 +18,138 @@ parameters: count: 1 path: ../../../src/Server.php - - - message: '#^Method pocketmine\\block\\Block\:\:readStateFromWorld\(\) is marked as impure but does not have any side effects\.$#' - identifier: impureMethod.pure - count: 1 - path: ../../../src/block/Block.php - - message: '#^Method pocketmine\\block\\DoubleTallGrass\:\:traitGetDropsForIncompatibleTool\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue count: 1 path: ../../../src/block/DoubleTallGrass.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:ACACIA_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:ACACIA_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:BIRCH_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:BIRCH_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CHERRY_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CHERRY_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CRIMSON_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CRIMSON_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:DARK_OAK_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:DARK_OAK_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:JUNGLE_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:JUNGLE_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:MANGROVE_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:MANGROVE_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:OAK_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:OAK_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:PALE_OAK_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:PALE_OAK_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:SPRUCE_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:SPRUCE_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:WARPED_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:WARPED_SIGN\(\)\.$#' identifier: callable.nonNativeMethod @@ -252,9 +312,3 @@ parameters: count: 2 path: ../../phpunit/promise/PromiseTest.php - - - message: '#^Strict comparison using \=\=\= between 0 and 0 will always evaluate to true\.$#' - identifier: identical.alwaysTrue - count: 1 - path: ../rules/UnsafeForeachArrayWithStringKeysRule.php - From 31f6f5d2522f088b78c57188de8ecb967df2cdb6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 20:13:15 +0100 Subject: [PATCH 112/140] CS again --- src/block/tile/HangingSign.php | 2 -- src/block/tile/TileFactory.php | 1 - src/item/HangingSign.php | 1 - 3 files changed, 4 deletions(-) diff --git a/src/block/tile/HangingSign.php b/src/block/tile/HangingSign.php index 9bf088f47..a5be9ba5c 100644 --- a/src/block/tile/HangingSign.php +++ b/src/block/tile/HangingSign.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace pocketmine\block\tile; -use pocketmine\nbt\tag\CompoundTag; - /** * @deprecated */ diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php index 6cab78b2c..108483894 100644 --- a/src/block/tile/TileFactory.php +++ b/src/block/tile/TileFactory.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\block\tile; -use pocketmine\block\HangingRoots; use pocketmine\data\SavedDataLoadingException; use pocketmine\math\Vector3; use pocketmine\nbt\NbtException; diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php index e3e95d68a..7143637ba 100644 --- a/src/item/HangingSign.php +++ b/src/item/HangingSign.php @@ -27,7 +27,6 @@ use pocketmine\block\Block; use pocketmine\block\utils\SupportType; use pocketmine\math\Facing; use pocketmine\math\Vector3; -use pocketmine\player\Player; final class HangingSign extends Item{ From 36211a96c12c8539b7cb895c6bdfec9dad0365d2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 23:24:38 +0100 Subject: [PATCH 113/140] Strip deprecated leftovers from #6769 --- src/data/bedrock/MushroomBlockTypeIdMap.php | 44 --- .../convert/BlockObjectToStateSerializer.php | 33 -- .../BlockSerializerDeserializerRegistrar.php | 4 +- .../convert/BlockStateDeserializerHelper.php | 359 ------------------ .../block/convert/BlockStateReader.php | 225 ----------- .../convert/BlockStateSerializerHelper.php | 264 ------------- .../BlockStateToObjectDeserializer.php | 38 -- .../block/convert/BlockStateWriter.php | 225 ----------- 8 files changed, 2 insertions(+), 1190 deletions(-) delete mode 100644 src/data/bedrock/MushroomBlockTypeIdMap.php delete mode 100644 src/data/bedrock/block/convert/BlockStateDeserializerHelper.php delete mode 100644 src/data/bedrock/block/convert/BlockStateSerializerHelper.php diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php deleted file mode 100644 index 6357d3e14..000000000 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ /dev/null @@ -1,44 +0,0 @@ - */ - use IntSaveIdMapTrait; - - public function __construct(){ - $newMapping = ValueMappings::getInstance()->mushroomBlockType; - foreach(MushroomBlockType::cases() as $case){ - $this->register($newMapping->valueToRaw($case), $case); - } - } -} diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 90f6af97a..d007900e0 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -25,14 +25,9 @@ namespace pocketmine\data\bedrock\block\convert; use pocketmine\block\Block; use pocketmine\block\RuntimeBlockStateRegistry; -use pocketmine\block\Slab; -use pocketmine\block\Stair; -use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateSerializeException; use pocketmine\data\bedrock\block\BlockStateSerializer; -use pocketmine\data\bedrock\block\BlockTypeNames as Ids; -use pocketmine\data\bedrock\block\convert\BlockStateSerializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer; use function get_class; @@ -75,34 +70,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->serializers[$block->getTypeId()] = $serializer instanceof Writer ? $serializer->getBlockStateData() : $serializer; } - /** - * @deprecated - */ - public function mapSimple(Block $block, string $id) : void{ - $this->map($block, BlockStateData::current($id, [])); - } - - /** - * @deprecated - */ - public function mapSlab(Slab $block, string $singleId, string $doubleId) : void{ - $this->map($block, fn(Slab $block) => Helper::encodeSlab($block, $singleId, $doubleId)); - } - - /** - * @deprecated - */ - public function mapStairs(Stair $block, string $id) : void{ - $this->map($block, fn(Stair $block) => Helper::encodeStairs($block, Writer::create($id))); - } - - /** - * @deprecated - */ - public function mapLog(Wood $block, string $unstrippedId, string $strippedId) : void{ - $this->map($block, fn(Wood $block) => Helper::encodeLog($block, $unstrippedId, $strippedId)); - } - /** * @phpstan-template TBlockType of Block * @phpstan-param TBlockType $blockState diff --git a/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php index 02491bae6..5fc1d3eef 100644 --- a/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php @@ -121,8 +121,8 @@ final class BlockSerializerDeserializerRegistrar{ } public function mapSimple(Block $block, string $id) : void{ - $this->deserializer->mapSimple($id, fn() => clone $block); - $this->serializer->mapSimple($block, $id); + $this->deserializer->map($id, fn() => clone $block); + $this->serializer->map($block, BlockStateData::current($id, [])); } /** diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php deleted file mode 100644 index 1d48ec76f..000000000 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ /dev/null @@ -1,359 +0,0 @@ -setFacing($in->readFacingDirection()) - ->setPressed($in->readBool(BlockStateNames::BUTTON_PRESSED_BIT)); - } - - /** - * @deprecated - * @phpstan-template TCandle of Candle - * @phpstan-param TCandle $block - * @phpstan-return TCandle - * - * @throws BlockStateDeserializeException - */ - public static function decodeCandle(Candle $block, BlockStateReader $in) : Candle{ - return $block - ->setCount($in->readBoundedInt(StateNames::CANDLES, 0, 3) + 1) - ->setLit($in->readBool(StateNames::LIT)); - } - - /** - * @phpstan-template TCrops of Crops - * @phpstan-param TCrops $block - * @phpstan-return TCrops - * - * @throws BlockStateDeserializeException - */ - public static function decodeCrops(Crops $block, BlockStateReader $in) : Crops{ - return $block->setAge($in->readBoundedInt(BlockStateNames::GROWTH, 0, 7)); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeComparator(RedstoneComparator $block, BlockStateReader $in) : RedstoneComparator{ - return $block - ->setFacing($in->readCardinalHorizontalFacing()) - ->setPowered($in->readBool(BlockStateNames::OUTPUT_LIT_BIT)) - ->setSubtractMode($in->readBool(BlockStateNames::OUTPUT_SUBTRACT_BIT)); - } - - /** - * @deprecated - * @phpstan-template TBlock of CopperMaterial - * - * @phpstan-param TBlock $block - * @phpstan-return TBlock - */ - public static function decodeCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{ - $block->setOxidation($oxidation); - $block->setWaxed(false); - return $block; - } - - /** - * @deprecated - * @phpstan-template TBlock of CopperMaterial - * - * @phpstan-param TBlock $block - * @phpstan-return TBlock - */ - public static function decodeWaxedCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{ - $block->setOxidation($oxidation); - $block->setWaxed(true); - return $block; - } - - /** @throws BlockStateDeserializeException */ - public static function decodeDaylightSensor(DaylightSensor $block, BlockStateReader $in) : DaylightSensor{ - return $block - ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); - } - - /** - * @deprecated - * @phpstan-template TDoor of Door - * @phpstan-param TDoor $block - * @phpstan-return TDoor - * - * @throws BlockStateDeserializeException - */ - public static function decodeDoor(Door $block, BlockStateReader $in) : Door{ - //TODO: check if these need any special treatment to get the appropriate data to both halves of the door - return $block - ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)) - //a door facing "east" is actually facing north - thanks mojang - ->setFacing(Facing::rotateY($in->readCardinalHorizontalFacing(), clockwise: false)) - ->setHingeRight($in->readBool(BlockStateNames::DOOR_HINGE_BIT)) - ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeDoublePlant(DoublePlant $block, BlockStateReader $in) : DoublePlant{ - return $block - ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{ - return $block - ->setFacing($in->readCardinalHorizontalFacing()) - ->setInWall($in->readBool(BlockStateNames::IN_WALL_BIT)) - ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{ - return $block - ->setAxis($in->mapIntFromInt(BlockStateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis)); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public static function decodeFloorSign(FloorSign $block, BlockStateReader $in) : FloorSign{ - return $block - ->setRotation($in->readBoundedInt(BlockStateNames::GROUND_SIGN_DIRECTION, 0, 15)); - } - - public static function decodeItemFrame(ItemFrame $block, BlockStateReader $in) : ItemFrame{ - $in->todo(StateNames::ITEM_FRAME_PHOTO_BIT); //TODO: not sure what the point of this is - return $block - ->setFacing($in->readFacingDirection()) - ->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT)); - } - - /** - * @throws BlockStateDeserializeException - * @deprecated - */ - public static function decodeLeaves(Leaves $block, BlockStateReader $in) : Leaves{ - return $block - ->setNoDecay($in->readBool(StateNames::PERSISTENT_BIT)) - ->setCheckDecay($in->readBool(StateNames::UPDATE_BIT)); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeLiquid(Liquid $block, BlockStateReader $in, bool $still) : Liquid{ - $fluidHeightState = $in->readBoundedInt(BlockStateNames::LIQUID_DEPTH, 0, 15); - return $block - ->setDecay($fluidHeightState & 0x7) - ->setFalling(($fluidHeightState & 0x8) !== 0) - ->setStill($still); - } - - public static function decodeFlowingLiquid(Liquid $block, BlockStateReader $in) : Liquid{ - return self::decodeLiquid($block, $in, false); - } - - public static function decodeStillLiquid(Liquid $block, BlockStateReader $in) : Liquid{ - return self::decodeLiquid($block, $in, true); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeLog(Wood $block, bool $stripped, BlockStateReader $in) : Wood{ - return $block - ->setAxis($in->readPillarAxis()) - ->setStripped($stripped); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeMushroomBlock(RedMushroomBlock $block, BlockStateReader $in) : Block{ - switch($type = $in->readBoundedInt(BlockStateNames::HUGE_MUSHROOM_BITS, 0, 15)){ - case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM: - case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: throw new BlockStateDeserializeException("This state does not exist"); - default: - //invalid types get left as default - $type = MushroomBlockTypeIdMap::getInstance()->fromId($type); - return $type !== null ? $block->setMushroomBlockType($type) : $block; - } - } - - /** @throws BlockStateDeserializeException */ - public static function decodeRepeater(RedstoneRepeater $block, BlockStateReader $in) : RedstoneRepeater{ - return $block - ->setFacing($in->readCardinalHorizontalFacing()) - ->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1); - } - - /** - * @throws BlockStateDeserializeException - * @deprecated - */ - public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{ - return $block - ->setReady($in->readBool(BlockStateNames::AGE_BIT)); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeSimplePressurePlate(SimplePressurePlate $block, BlockStateReader $in) : SimplePressurePlate{ - //TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation? - //best to keep this separate from weighted plates anyway... - return $block->setPressed($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15) !== 0); - } - - /** - * @phpstan-template TSlab of Slab - * @phpstan-param TSlab $block - * @phpstan-return TSlab - * - * @throws BlockStateDeserializeException - */ - public static function decodeSingleSlab(Slab $block, BlockStateReader $in) : Slab{ - return $block->setSlabType($in->readSlabPosition()); - } - - /** - * @phpstan-template TSlab of Slab - * @phpstan-param TSlab $block - * @phpstan-return TSlab - * - * @throws BlockStateDeserializeException - */ - public static function decodeDoubleSlab(Slab $block, BlockStateReader $in) : Slab{ - $in->ignored(StateNames::MC_VERTICAL_HALF); - return $block->setSlabType(SlabType::DOUBLE); - } - - /** - * @deprecated - * @phpstan-template TStair of Stair - * @phpstan-param TStair $block - * @phpstan-return TStair - * - * @throws BlockStateDeserializeException - */ - public static function decodeStairs(Stair $block, BlockStateReader $in) : Stair{ - return $block - ->setUpsideDown($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT)) - ->setFacing($in->readWeirdoHorizontalFacing()); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeStem(Stem $block, BlockStateReader $in) : Stem{ - //In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the - //most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which - //is absurd, and I refuse to make our API similarly absurd. - $facing = $in->readFacingWithoutUp(); - return self::decodeCrops($block, $in) - ->setFacing($facing === Facing::DOWN ? Facing::UP : $facing); - } - - /** - * @deprecated - * @phpstan-template TTrapdoor of Trapdoor - * @phpstan-param TTrapdoor $block - * @phpstan-return TTrapdoor - * - * @throws BlockStateDeserializeException - */ - public static function decodeTrapdoor(Trapdoor $block, BlockStateReader $in) : Trapdoor{ - return $block - ->setFacing($in->read5MinusHorizontalFacing()) - ->setTop($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT)) - ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); - } - - /** @throws BlockStateDeserializeException */ - public static function decodeWall(Wall $block, BlockStateReader $in) : Wall{ - $block->setPost($in->readBool(BlockStateNames::WALL_POST_BIT)); - $block->setConnection(Facing::NORTH, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_NORTH)); - $block->setConnection(Facing::SOUTH, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_SOUTH)); - $block->setConnection(Facing::WEST, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST)); - $block->setConnection(Facing::EAST, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_EAST)); - - return $block; - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{ - return $block - ->setFacing($in->readHorizontalFacing()); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public static function decodeWeightedPressurePlate(WeightedPressurePlate $block, BlockStateReader $in) : WeightedPressurePlate{ - return $block - ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); - } -} diff --git a/src/data/bedrock/block/convert/BlockStateReader.php b/src/data/bedrock/block/convert/BlockStateReader.php index 4d09d2f85..adcdf9709 100644 --- a/src/data/bedrock/block/convert/BlockStateReader.php +++ b/src/data/bedrock/block/convert/BlockStateReader.php @@ -23,19 +23,8 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\convert; -use pocketmine\block\utils\BellAttachmentType; -use pocketmine\block\utils\SlabType; -use pocketmine\block\utils\WallConnectionType; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateDeserializeException; -use pocketmine\data\bedrock\block\BlockStateNames; -use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; -use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap; -use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap; -use pocketmine\data\bedrock\block\convert\property\ValueMappings; -use pocketmine\math\Axis; -use pocketmine\math\Facing; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; @@ -114,220 +103,6 @@ final class BlockStateReader{ throw $this->missingOrWrongTypeException($name, $tag); } - /** - * @deprecated - * @phpstan-param IntFromRawStateMap $map - * @throws BlockStateDeserializeException - */ - public function mapIntFromString(string $name, IntFromRawStateMap $map) : int{ - $raw = $this->readString($name); - - return $map->rawToValue($raw) ?? throw $this->badValueException($name, $raw); - } - - /** - * @deprecated - * @phpstan-param IntFromRawStateMap $map - * @throws BlockStateDeserializeException - */ - public function mapIntFromInt(string $name, IntFromRawStateMap $map) : int{ - $raw = $this->readInt($name); - - return $map->rawToValue($raw) ?? throw $this->badValueException($name, (string) $raw); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readFacingDirection() : int{ - return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readBlockFace() : int{ - return $this->mapIntFromString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace); - } - - /** - * @deprecated - * @return int[] - * @phpstan-return array - */ - public function readFacingFlags() : array{ - $result = []; - $flags = $this->readBoundedInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, 0, 63); - foreach([ - BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN => Facing::DOWN, - BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP => Facing::UP, - BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH => Facing::NORTH, - BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH => Facing::SOUTH, - BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST => Facing::WEST, - BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST => Facing::EAST - ] as $flag => $facing){ - if(($flags & $flag) !== 0){ - $result[$facing] = $facing; - } - } - - return $result; - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readEndRodFacingDirection() : int{ - $result = $this->readFacingDirection(); - return Facing::axis($result) !== Axis::Y ? Facing::opposite($result) : $result; - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readHorizontalFacing() : int{ - return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readWeirdoHorizontalFacing() : int{ - return $this->mapIntFromInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readLegacyHorizontalFacing() : int{ - return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE); - } - - /** - * @deprecated - * This is for trapdoors, because Mojang botched the conversion in 1.13 - * @throws BlockStateDeserializeException - */ - public function read5MinusHorizontalFacing() : int{ - return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus); - } - - /** - * @deprecated - * Used by pumpkins as of 1.20.0.23 beta - * @throws BlockStateDeserializeException - */ - public function readCardinalHorizontalFacing() : int{ - return $this->mapIntFromString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readCoralFacing() : int{ - return $this->mapIntFromInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readFacingWithoutDown() : int{ - $result = $this->readFacingDirection(); - if($result === Facing::DOWN){ //shouldn't be legal, but 1.13 allows it - $result = Facing::UP; - } - return $result; - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readFacingWithoutUp() : int{ - $result = $this->readFacingDirection(); - if($result === Facing::UP){ - $result = Facing::DOWN; //shouldn't be legal, but 1.13 allows it - } - return $result; - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readPillarAxis() : int{ - return $this->mapIntFromString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readSlabPosition() : SlabType{ - return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){ - StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM, - StringValues::MC_VERTICAL_HALF_TOP => SlabType::TOP, - default => throw $this->badValueException(BlockStateNames::MC_VERTICAL_HALF, $rawValue, "Invalid slab position"), - }; - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readTorchFacing() : int{ - return $this->mapIntFromString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readBellAttachmentType() : BellAttachmentType{ - return $this->readUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType); - } - - /** - * @deprecated - * @throws BlockStateDeserializeException - */ - public function readWallConnectionType(string $name) : ?WallConnectionType{ - return match($type = $this->readString($name)){ - //TODO: this looks a bit confusing due to use of EAST, but the values are the same for all connections - //we need to find a better way to auto-generate the constant names when they are reused - //for now, using these constants is better than nothing since it still gives static analysability - StringValues::WALL_CONNECTION_TYPE_EAST_NONE => null, - StringValues::WALL_CONNECTION_TYPE_EAST_SHORT => WallConnectionType::SHORT, - StringValues::WALL_CONNECTION_TYPE_EAST_TALL => WallConnectionType::TALL, - default => throw $this->badValueException($name, $type), - }; - } - - /** - * @deprecated - * @phpstan-template TEnum of \UnitEnum - * @phpstan-param EnumFromRawStateMap $map - * @phpstan-return TEnum - * @throws BlockStateDeserializeException - */ - public function readUnitEnum(string $name, EnumFromRawStateMap $map) : \UnitEnum{ - $value = $this->readString($name); - - $mapped = $map->rawToValue($value); - if($mapped === null){ - throw $this->badValueException($name, $value); - } - return $mapped; - } - /** * Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled */ diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php deleted file mode 100644 index da3dbb387..000000000 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ /dev/null @@ -1,264 +0,0 @@ -writeFacingDirection($block->getFacing()) - ->writeBool(BlockStateNames::BUTTON_PRESSED_BIT, $block->isPressed()); - } - - public static function encodeCandle(Candle $block, Writer $out) : Writer{ - return $out - ->writeBool(StateNames::LIT, $block->isLit()) - ->writeInt(StateNames::CANDLES, $block->getCount() - 1); - } - - public static function encodeChemistryTable(ChemistryTable $block, Writer $out) : Writer{ - return $out - ->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing())); - } - - public static function encodeCrops(Crops $block, Writer $out) : Writer{ - return $out->writeInt(BlockStateNames::GROWTH, $block->getAge()); - } - - /** - * @deprecated - */ - public static function encodeTorch(Torch $block, Writer $out) : Writer{ - return $out - ->writeTorchFacing($block->getFacing()); - } - - public static function encodeCauldron(string $liquid, int $fillLevel) : Writer{ - return Writer::create(Ids::CAULDRON) - ->writeString(BlockStateNames::CAULDRON_LIQUID, $liquid) - ->writeInt(BlockStateNames::FILL_LEVEL, $fillLevel); - } - - public static function selectCopperId(CopperOxidation $oxidation, string $noneId, string $exposedId, string $weatheredId, string $oxidizedId) : string{ - return match($oxidation){ - CopperOxidation::NONE => $noneId, - CopperOxidation::EXPOSED => $exposedId, - CopperOxidation::WEATHERED => $weatheredId, - CopperOxidation::OXIDIZED => $oxidizedId, - }; - } - - /** - * @deprecated - */ - public static function encodeDoor(Door $block, Writer $out) : Writer{ - return $out - ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()) - //a door facing north is encoded as "east" - ->writeCardinalHorizontalFacing(Facing::rotateY($block->getFacing(), clockwise: true)) - ->writeBool(BlockStateNames::DOOR_HINGE_BIT, $block->isHingeRight()) - ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); - } - - public static function encodeDoublePlant(DoublePlant $block, Writer $out) : Writer{ - return $out - ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()); - } - - /** - * @deprecated - */ - public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{ - return $out - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeBool(BlockStateNames::IN_WALL_BIT, $block->isInWall()) - ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); - } - - /** - * @deprecated - */ - public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{ - return $out - ->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation()); - } - - public static function encodeFurnace(Furnace $block, string $unlitId, string $litId) : Writer{ - return Writer::create($block->isLit() ? $litId : $unlitId) - ->writeCardinalHorizontalFacing($block->getFacing()); - } - - public static function encodeItemFrame(ItemFrame $block, string $id) : Writer{ - return Writer::create($id) - ->writeBool(StateNames::ITEM_FRAME_MAP_BIT, $block->hasMap()) - ->writeBool(StateNames::ITEM_FRAME_PHOTO_BIT, false) - ->writeFacingDirection($block->getFacing()); - } - - /** - * @deprecated - */ - public static function encodeLeaves(Leaves $block, Writer $out) : Writer{ - return $out - ->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay()) - ->writeBool(BlockStateNames::UPDATE_BIT, $block->isCheckDecay()); - } - - public static function encodeLiquid(Liquid $block, string $stillId, string $flowingId) : Writer{ - return Writer::create($block->isStill() ? $stillId : $flowingId) - ->writeInt(BlockStateNames::LIQUID_DEPTH, $block->getDecay() | ($block->isFalling() ? 0x8 : 0)); - } - - public static function encodeLog(Wood $block, string $unstrippedId, string $strippedId) : Writer{ - $out = $block->isStripped() ? - Writer::create($strippedId) : - Writer::create($unstrippedId); - return $out - ->writePillarAxis($block->getAxis()); - } - - public static function encodeMushroomBlock(RedMushroomBlock $block, Writer $out) : Writer{ - return $out - ->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType())); - } - - /** - * @deprecated - */ - public static function encodeQuartz(int $axis, Writer $out) : Writer{ - return $out - ->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway - } - - /** - * @deprecated - */ - public static function encodeSapling(Sapling $block, Writer $out) : Writer{ - return $out - ->writeBool(BlockStateNames::AGE_BIT, $block->isReady()); - } - - public static function encodeSimplePressurePlate(SimplePressurePlate $block, Writer $out) : Writer{ - //TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation? - //best to keep this separate from weighted plates anyway... - return $out - ->writeInt(BlockStateNames::REDSTONE_SIGNAL, $block->isPressed() ? 15 : 0); - } - - private static function encodeSingleSlab(Slab $block, string $id) : Writer{ - return Writer::create($id) - ->writeSlabPosition($block->getSlabType()); - } - - private static function encodeDoubleSlab(Slab $block, string $id) : Writer{ - return Writer::create($id) - //this is (intentionally) also written for double slabs (as zero) to maintain bug parity with MCPE - ->writeSlabPosition(SlabType::BOTTOM); - } - - public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : Writer{ - return $block->getSlabType() === SlabType::DOUBLE ? - self::encodeDoubleSlab($block, $doubleId) : - self::encodeSingleSlab($block, $singleId); - } - - /** - * @deprecated - */ - public static function encodeStairs(Stair $block, Writer $out) : Writer{ - return $out - ->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown()) - ->writeWeirdoHorizontalFacing($block->getFacing()); - } - - public static function encodeStem(Stem $block, Writer $out) : Writer{ - //In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the - //most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which - //is absurd, and I refuse to make our API similarly absurd. - $facing = $block->getFacing(); - return self::encodeCrops($block, $out) - ->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing); - } - - /** - * @deprecated - */ - public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{ - return $out - ->write5MinusHorizontalFacing($block->getFacing()) - ->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isTop()) - ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); - } - - public static function encodeWall(Wall $block, Writer $out) : Writer{ - return $out - ->writeBool(BlockStateNames::WALL_POST_BIT, $block->isPost()) - ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_EAST, $block->getConnection(Facing::EAST)) - ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_NORTH, $block->getConnection(Facing::NORTH)) - ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_SOUTH, $block->getConnection(Facing::SOUTH)) - ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST)); - } - - /** - * @deprecated - */ - public static function encodeWallSign(WallSign $block, Writer $out) : Writer{ - return $out - ->writeHorizontalFacing($block->getFacing()); - } -} diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index ca5c12412..18d549774 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -25,13 +25,9 @@ namespace pocketmine\data\bedrock\block\convert; use pocketmine\block\Block; use pocketmine\block\RuntimeBlockStateRegistry; -use pocketmine\block\Slab; -use pocketmine\block\Stair; -use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializer; -use pocketmine\data\bedrock\block\convert\BlockStateDeserializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader; use function array_key_exists; use function count; @@ -86,40 +82,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return $this->deserializeFuncs[$id] ?? null; } - /** - * @deprecated - * @phpstan-param \Closure() : Block $getBlock - */ - public function mapSimple(string $id, \Closure $getBlock) : void{ - $this->map($id, $getBlock); - } - - /** - * @deprecated - * @phpstan-param \Closure(Reader) : Slab $getBlock - */ - public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock) : void{ - $this->map($singleId, fn(Reader $in) => Helper::decodeSingleSlab($getBlock($in), $in)); - $this->map($doubleId, fn(Reader $in) => Helper::decodeDoubleSlab($getBlock($in), $in)); - } - - /** - * @deprecated - * @phpstan-param \Closure() : Stair $getBlock - */ - public function mapStairs(string $id, \Closure $getBlock) : void{ - $this->map($id, fn(Reader $in) : Stair => Helper::decodeStairs($getBlock(), $in)); - } - - /** - * @deprecated - * @phpstan-param \Closure() : Wood $getBlock - */ - public function mapLog(string $unstrippedId, string $strippedId, \Closure $getBlock) : void{ - $this->map($unstrippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), false, $in)); - $this->map($strippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), true, $in)); - } - /** @throws BlockStateDeserializeException */ public function deserializeBlock(BlockStateData $blockStateData) : Block{ $id = $blockStateData->getName(); diff --git a/src/data/bedrock/block/convert/BlockStateWriter.php b/src/data/bedrock/block/convert/BlockStateWriter.php index 0119bd02e..d58bbd7c3 100644 --- a/src/data/bedrock/block/convert/BlockStateWriter.php +++ b/src/data/bedrock/block/convert/BlockStateWriter.php @@ -23,24 +23,11 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\convert; -use pocketmine\block\utils\BellAttachmentType; -use pocketmine\block\utils\SlabType; -use pocketmine\block\utils\WallConnectionType; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateData; -use pocketmine\data\bedrock\block\BlockStateNames; -use pocketmine\data\bedrock\block\BlockStateSerializeException; -use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; -use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap; -use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap; -use pocketmine\data\bedrock\block\convert\property\ValueMappings; -use pocketmine\math\Axis; -use pocketmine\math\Facing; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\Tag; -use pocketmine\utils\AssumptionFailedError; final class BlockStateWriter{ @@ -76,218 +63,6 @@ final class BlockStateWriter{ return $this; } - /** - * @deprecated - * @phpstan-param IntFromRawStateMap $map - * @return $this - */ - public function mapIntToString(string $name, IntFromRawStateMap $map, int $value) : self{ - $raw = $map->valueToRaw($value); - $this->writeString($name, $raw); - return $this; - } - - /** - * @deprecated - * @phpstan-param IntFromRawStateMap $map - * @return $this - */ - public function mapIntToInt(string $name, IntFromRawStateMap $map, int $value) : self{ - $raw = $map->valueToRaw($value); - $this->writeInt($name, $raw); - return $this; - } - - /** - * @deprecated - * @return $this - */ - public function writeFacingDirection(int $value) : self{ - return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing, $value); - } - - /** - * @deprecated - * @return $this - */ - public function writeBlockFace(int $value) : self{ - $this->mapIntToString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace, $value); - return $this; - } - - /** - * @deprecated - * @param int[] $faces - * @phpstan-param array $faces - * @return $this - */ - public function writeFacingFlags(array $faces) : self{ - $result = 0; - foreach($faces as $face){ - $result |= match($face){ - Facing::DOWN => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN, - Facing::UP => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP, - Facing::NORTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH, - Facing::SOUTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH, - Facing::WEST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST, - Facing::EAST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST, - default => throw new AssumptionFailedError("Unhandled face $face") - }; - } - - return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result); - } - - /** - * @deprecated - * @return $this - */ - public function writeEndRodFacingDirection(int $value) : self{ - //end rods are stupid in bedrock and have everything except up/down the wrong way round - return $this->writeFacingDirection(Facing::axis($value) !== Axis::Y ? Facing::opposite($value) : $value); - } - - /** - * @deprecated - * @return $this - */ - public function writeHorizontalFacing(int $value) : self{ - return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic, $value); - } - - /** - * @deprecated - * @return $this - */ - public function writeWeirdoHorizontalFacing(int $value) : self{ - return $this->mapIntToInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value); - } - - /** - * @deprecated - * @return $this - */ - public function writeLegacyHorizontalFacing(int $value) : self{ - return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE, $value); - } - - /** - * @deprecated - * This is for trapdoors, because Mojang botched the conversion in 1.13 - * @return $this - */ - public function write5MinusHorizontalFacing(int $value) : self{ - return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value); - } - - /** - * @deprecated - * Used by pumpkins as of 1.20.0.23 beta - * @return $this - */ - public function writeCardinalHorizontalFacing(int $value) : self{ - return $this->mapIntToString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection, $value); - } - - /** - * @deprecated - * @return $this - */ - public function writeCoralFacing(int $value) : self{ - return $this->mapIntToInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, $value); - } - - /** - * @deprecated - * @return $this - */ - public function writeFacingWithoutDown(int $value) : self{ - if($value === Facing::DOWN){ - throw new BlockStateSerializeException("Invalid facing DOWN"); - } - $this->writeFacingDirection($value); - return $this; - } - - /** - * @deprecated - * @return $this - */ - public function writeFacingWithoutUp(int $value) : self{ - if($value === Facing::UP){ - throw new BlockStateSerializeException("Invalid facing UP"); - } - $this->writeFacingDirection($value); - return $this; - } - - /** - * @deprecated - * @return $this - */ - public function writePillarAxis(int $axis) : self{ - $this->mapIntToString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis, $axis); - return $this; - } - - /** - * @deprecated - * @return $this - */ - public function writeSlabPosition(SlabType $slabType) : self{ - $this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){ - SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP, - SlabType::BOTTOM => StringValues::MC_VERTICAL_HALF_BOTTOM, - default => throw new BlockStateSerializeException("Invalid slab type " . $slabType->name) - }); - return $this; - } - - /** - * @deprecated - * @return $this - */ - public function writeTorchFacing(int $facing) : self{ - $this->mapIntToString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing, $facing); - return $this; - } - - /** - * @deprecated - * @return $this - */ - public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{ - return $this->writeUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, $attachmentType); - } - - /** - * @deprecated - * @return $this - */ - public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{ - $this->writeString($name, match($wallConnectionType){ - null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE, - WallConnectionType::SHORT => StringValues::WALL_CONNECTION_TYPE_EAST_SHORT, - WallConnectionType::TALL => StringValues::WALL_CONNECTION_TYPE_EAST_TALL, - }); - return $this; - } - - /** - * @deprecated - * @phpstan-template TEnum of \UnitEnum - * @phpstan-param EnumFromRawStateMap $map - * @phpstan-param TEnum $case - * - * @return $this - */ - public function writeUnitEnum(string $name, EnumFromRawStateMap $map, \UnitEnum $case) : self{ - $value = $map->valueToRaw($case); - $this->writeString($name, $value); - - return $this; - } - public function getBlockStateData() : BlockStateData{ return BlockStateData::current($this->id, $this->states); } From 1ebd7d3960d713d56f77f610fe0c15cdee201069 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 23:29:55 +0100 Subject: [PATCH 114/140] Remove unused deprecated stuff --- src/block/tile/Sign.php | 11 ----------- src/crafting/CraftingManager.php | 11 ----------- src/utils/Process.php | 10 ++-------- src/utils/Utils.php | 10 ---------- 4 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index 0bb21a6d3..662cf9eab 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -32,9 +32,6 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\utils\Binary; use pocketmine\world\World; -use function array_pad; -use function array_slice; -use function explode; use function implode; use function mb_scrub; use function sprintf; @@ -60,14 +57,6 @@ class Sign extends Spawnable{ public const TAG_WAXED = "IsWaxed"; //TAG_Byte public const TAG_LOCKED_FOR_EDITING_BY = "LockedForEditingBy"; //TAG_Long - /** - * @return string[] - * @deprecated - */ - public static function fixTextBlob(string $blob) : array{ - return array_slice(array_pad(explode("\n", $blob, limit: 5), 4, ""), 0, 4); - } - protected SignText $text; private bool $waxed = false; diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php index 93d6e1838..32188f7e5 100644 --- a/src/crafting/CraftingManager.php +++ b/src/crafting/CraftingManager.php @@ -102,17 +102,6 @@ class CraftingManager{ /** @phpstan-return ObjectSet<\Closure() : void> */ public function getRecipeRegisteredCallbacks() : ObjectSet{ return $this->recipeRegisteredCallbacks; } - /** - * Function used to arrange Shapeless Recipe ingredient lists into a consistent order. - * @deprecated - */ - public static function sort(Item $i1, Item $i2) : int{ - //Use spaceship operator to compare each property, then try the next one if they are equivalent. - ($retval = $i1->getStateId() <=> $i2->getStateId()) === 0 && ($retval = $i1->getCount() <=> $i2->getCount()) === 0; - - return $retval; - } - private static function hashOutput(Item $output) : string{ $write = new BinaryStream(); $write->putVarInt($output->getStateId()); diff --git a/src/utils/Process.php b/src/utils/Process.php index 90149870a..897f60626 100644 --- a/src/utils/Process.php +++ b/src/utils/Process.php @@ -125,24 +125,18 @@ final class Process{ return Thread::getRunningCount() + 1; //pmmpthread doesn't count the main thread } - /** - * @param bool $subprocesses @deprecated - */ - public static function kill(int $pid, bool $subprocesses = false) : void{ + public static function kill(int $pid) : void{ $logger = \GlobalLogger::get(); if($logger instanceof MainLogger){ $logger->syncFlushBuffer(); } switch(Utils::getOS()){ case Utils::OS_WINDOWS: - exec("taskkill.exe /F " . ($subprocesses ? "/T " : "") . "/PID $pid > NUL 2> NUL"); + exec("taskkill.exe /F /PID $pid > NUL 2> NUL"); break; case Utils::OS_MACOS: case Utils::OS_LINUX: default: - if($subprocesses){ - $pid = -$pid; - } if(function_exists("posix_kill")){ posix_kill($pid, 9); //SIGKILL }else{ diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 800bd0183..c132e6636 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -165,16 +165,6 @@ final class Utils{ return $reflect->getName(); } - /** - * @phpstan-return \Closure(object) : object - * @deprecated - */ - public static function cloneCallback() : \Closure{ - return static function(object $o){ - return clone $o; - }; - } - /** * @phpstan-template TKey of array-key * @phpstan-template TValue of object From 5c0a109f1849a148731c8adf0e53d5b2a98d16f3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 01:48:29 +0100 Subject: [PATCH 115/140] Sign: Strip trailing newlines from text blobs fixes sign editor always putting the cursor on the last line when right-clicking to edit --- src/block/tile/Sign.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index 0bb21a6d3..aef83e3cc 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -37,6 +37,7 @@ use function array_slice; use function explode; use function implode; use function mb_scrub; +use function rtrim; use function sprintf; /** @@ -117,7 +118,7 @@ class Sign extends Spawnable{ protected function writeSaveData(CompoundTag $nbt) : void{ $nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create() - ->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines())) + ->setString(self::TAG_TEXT_BLOB, rtrim(implode("\n", $this->text->getLines()), "\n")) ->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) @@ -162,7 +163,7 @@ class Sign extends Spawnable{ protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ $nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create() - ->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines())) + ->setString(self::TAG_TEXT_BLOB, rtrim(implode("\n", $this->text->getLines()), "\n")) ->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) //TODO: not sure what this is used for From 4a2c7dc684f110cab3c37a75f1f79af4246c9ee7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 02:15:24 +0100 Subject: [PATCH 116/140] Apparently hanging signs are self supporting --- src/block/BaseSign.php | 19 +------------------ src/block/WallHangingSign.php | 14 ++++++++------ 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index b01157343..5a7df4663 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -103,27 +103,10 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ return SupportType::NONE; } - /** - * @deprecated - */ abstract protected function getSupportingFace() : int; - /** - * @return int[] - */ - protected function getSupportingFaceOptions() : array{ - return [$this->getSupportingFace()]; - } - public function onNearbyBlockChange() : void{ - $foundSupport = false; - foreach($this->getSupportingFaceOptions() as $face){ - if($this->getSide($face)->getTypeId() !== BlockTypeIds::AIR){ - $foundSupport = true; - break; - } - } - if(!$foundSupport){ + if($this->getSide($this->getSupportingFace())->getTypeId() !== BlockTypeIds::AIR){ $this->position->getWorld()->useBreakOn($this->position); } } diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php index c167036b1..2332f8e4f 100644 --- a/src/block/WallHangingSign.php +++ b/src/block/WallHangingSign.php @@ -27,6 +27,7 @@ use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; +use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -39,12 +40,13 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ return Facing::rotateY($this->facing, clockwise: true); } - protected function getSupportingFaceOptions() : array{ - //wall hanging signs can be supported from either end of the post - return [ - Facing::rotateY($this->facing, clockwise: true), - Facing::rotateY($this->facing, clockwise: false) - ]; + public function onNearbyBlockChange() : void{ + //NOOP - disable default self-destruct behaviour + } + + protected function recalculateCollisionBoxes() : array{ + //only the cross bar is collidable + return [AxisAlignedBB::one()->trim(Facing::DOWN, 7 / 8)->squash(Facing::axis($this->facing), 3 / 4)]; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ From c54892311612b2786e964c9bc7f416fbdf4c2d64 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 02:16:38 +0100 Subject: [PATCH 117/140] ... --- src/block/BaseSign.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 5a7df4663..0efaa603c 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -106,7 +106,7 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ abstract protected function getSupportingFace() : int; public function onNearbyBlockChange() : void{ - if($this->getSide($this->getSupportingFace())->getTypeId() !== BlockTypeIds::AIR){ + if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){ $this->position->getWorld()->useBreakOn($this->position); } } From ec56d65bcc3b2696f759867d30c5e2f6d3d99082 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 02:17:45 +0100 Subject: [PATCH 118/140] Fix BC break in BaseBanner --- src/block/BaseBanner.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index 71a892c20..e8bf187ee 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -61,7 +61,12 @@ abstract class BaseBanner extends Transparent implements Colored{ return $this; } - abstract protected function getOminousVersion() : Block; + /** + * TODO: make this abstract in PM6 (BC break) + */ + protected function getOminousVersion() : Block{ + return VanillaBlocks::AIR(); + } public function writeStateToWorld() : void{ parent::writeStateToWorld(); From ac2c07c3fe7e90585cce4fc2b215aa0e20b687d0 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Mon, 25 Aug 2025 19:00:41 +0300 Subject: [PATCH 119/140] Added a space after hanging sign wood type (#6776) --- src/block/VanillaBlocks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index bf9f7e5f5..e3b7a5f06 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -1444,8 +1444,8 @@ final class VanillaBlocks{ WoodType::CHERRY => VanillaItems::CHERRY_HANGING_SIGN(...), WoodType::PALE_OAK => VanillaItems::PALE_OAK_HANGING_SIGN(...), }; - self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . "Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); - self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . "Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . " Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . " Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); } } From db54c481aa5248a01ae59435cf3a7d954f3e28f8 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:27:17 +0300 Subject: [PATCH 120/140] Fixed hanging signs placement criteria (#6775) --- src/block/BlockTypeTags.php | 1 + src/block/CeilingCenterHangingSign.php | 9 ++++++++ src/block/CeilingEdgesHangingSign.php | 17 +++++++++++++++ src/block/VanillaBlocks.php | 7 ++++--- src/block/WallHangingSign.php | 22 ++++++++++++++++--- src/item/HangingSign.php | 29 ++++++++++++++++++++------ src/item/Item.php | 2 +- src/world/World.php | 2 +- 8 files changed, 75 insertions(+), 14 deletions(-) diff --git a/src/block/BlockTypeTags.php b/src/block/BlockTypeTags.php index 19a4825d9..531f3bcb3 100644 --- a/src/block/BlockTypeTags.php +++ b/src/block/BlockTypeTags.php @@ -31,4 +31,5 @@ final class BlockTypeTags{ public const SAND = self::PREFIX . "sand"; public const POTTABLE_PLANTS = self::PREFIX . "pottable"; public const FIRE = self::PREFIX . "fire"; + public const HANGING_SIGN = self::PREFIX . "hanging_sign"; } diff --git a/src/block/CeilingCenterHangingSign.php b/src/block/CeilingCenterHangingSign.php index 7078f38d8..1125de553 100644 --- a/src/block/CeilingCenterHangingSign.php +++ b/src/block/CeilingCenterHangingSign.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SignLikeRotationTrait; +use pocketmine\block\utils\StaticSupportTrait; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -33,6 +34,7 @@ use pocketmine\world\BlockTransaction; final class CeilingCenterHangingSign extends BaseSign implements SignLikeRotation{ use SignLikeRotationTrait; + use StaticSupportTrait; protected function getSupportingFace() : int{ return Facing::UP; @@ -49,4 +51,11 @@ final class CeilingCenterHangingSign extends BaseSign implements SignLikeRotatio } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } + + private function canBeSupportedAt(Block $block) : bool{ + $supportBlock = $block->getSide(Facing::UP); + return + $supportBlock->getSupportType(Facing::DOWN)->hasCenterSupport() || + $supportBlock->hasTypeTag(BlockTypeTags::HANGING_SIGN); + } } diff --git a/src/block/CeilingEdgesHangingSign.php b/src/block/CeilingEdgesHangingSign.php index 5dafe6932..3f7b6489b 100644 --- a/src/block/CeilingEdgesHangingSign.php +++ b/src/block/CeilingEdgesHangingSign.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -45,7 +46,23 @@ final class CeilingEdgesHangingSign extends BaseSign implements HorizontalFacing if($player !== null){ $this->facing = Facing::opposite($player->getHorizontalFacing()); } + if(!$this->canBeSupportedAt($blockReplace)){ + return false; + } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } + + public function onNearbyBlockChange() : void{ + if(!$this->canBeSupportedAt($this)){ + $this->position->getWorld()->useBreakOn($this->position); + } + } + + private function canBeSupportedAt(Block $block) : bool{ + $supportBlock = $block->getSide(Facing::UP); + return + $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || + (($supportBlock instanceof WallHangingSign || $supportBlock instanceof CeilingEdgesHangingSign) && Facing::axis($supportBlock->getFacing()) === Facing::axis($this->facing)); + } } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index e3b7a5f06..36074c606 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -1391,6 +1391,7 @@ final class VanillaBlocks{ private static function registerWoodenBlocks() : void{ $planksBreakInfo = new Info(BreakInfo::axe(2.0, null, 15.0)); $signBreakInfo = new Info(BreakInfo::axe(1.0)); + $hangingSignBreakInfo = new Info(BreakInfo::axe(1.0), [Tags::HANGING_SIGN]); $logBreakInfo = new Info(BreakInfo::axe(2.0)); $woodenDoorBreakInfo = new Info(BreakInfo::axe(3.0, null, 15.0)); $woodenButtonBreakInfo = new Info(BreakInfo::axe(0.5)); @@ -1444,9 +1445,9 @@ final class VanillaBlocks{ WoodType::CHERRY => VanillaItems::CHERRY_HANGING_SIGN(...), WoodType::PALE_OAK => VanillaItems::PALE_OAK_HANGING_SIGN(...), }; - self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . " Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); - self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . " Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); - self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . " Center Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . " Edges Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); } } diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php index 2332f8e4f..df959c720 100644 --- a/src/block/WallHangingSign.php +++ b/src/block/WallHangingSign.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; @@ -50,16 +51,31 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(Facing::axis($face) === Axis::Y){ + if($player === null){ + return false; + } + $attachFace = Facing::axis($face) === Axis::Y ? Facing::rotateY($player->getHorizontalFacing(), clockwise: true) : $face; + + if($this->canBeSupportedAt($blockReplace->getSide($attachFace), $attachFace)){ + $direction = $attachFace; + }elseif($this->canBeSupportedAt($blockReplace->getSide($opposite = Facing::opposite($attachFace)), $opposite)){ + $direction = $opposite; + }else{ return false; } - $this->facing = Facing::rotateY($face, clockwise: true); + $this->facing = Facing::rotateY(Facing::opposite($direction), clockwise: true); //the front should always face the player if possible - if($player !== null && $this->facing === $player->getHorizontalFacing()){ + if($this->facing === $player->getHorizontalFacing()){ $this->facing = Facing::opposite($this->facing); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } + + private function canBeSupportedAt(Block $block, int $face) : bool{ + return + ($block instanceof WallHangingSign && Facing::axis(Facing::rotateY($block->getFacing(), clockwise: true)) === Facing::axis($face)) || + $block->getSupportType(Facing::opposite($face)) === SupportType::FULL; + } } diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php index 7143637ba..5e3bd068a 100644 --- a/src/item/HangingSign.php +++ b/src/item/HangingSign.php @@ -24,9 +24,13 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\block\Block; +use pocketmine\block\CeilingCenterHangingSign; +use pocketmine\block\CeilingEdgesHangingSign; use pocketmine\block\utils\SupportType; +use pocketmine\block\WallHangingSign; use pocketmine\math\Facing; use pocketmine\math\Vector3; +use pocketmine\player\Player; final class HangingSign extends Item{ @@ -40,13 +44,26 @@ final class HangingSign extends Item{ parent::__construct($identifier, $name); } - public function getPlacementBlock(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ + public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ //we don't verify valid placement conditions here, only decide which block to return - $result = $face === Facing::DOWN ? - $blockReplace->getSide(Facing::UP)->getSupportType(Facing::DOWN) === SupportType::CENTER ? - $this->centerPointCeilingVariant : - $this->edgePointCeilingVariant - : $this->wallVariant; + if($face === Facing::DOWN){ + if($player !== null && $player->isSneaking()){ + return clone $this->centerPointCeilingVariant; + } + + //we select the center variant when support is edge/wall sign with perpendicular player facing, + //support is a center sign itself, or support provides center support. + //otherwise use the edge variant. + $support = $blockReplace->getSide(Facing::UP); + $result = + (($support instanceof CeilingEdgesHangingSign || $support instanceof WallHangingSign) && ($player === null || Facing::axis($player->getHorizontalFacing()) !== Facing::axis($support->getFacing()))) || + $support instanceof CeilingCenterHangingSign || + $support->getSupportType(Facing::DOWN) === SupportType::CENTER ? + $this->centerPointCeilingVariant : + $this->edgePointCeilingVariant; + }else{ + $result = $this->wallVariant; + } return clone $result; } diff --git a/src/item/Item.php b/src/item/Item.php index 6786238b0..c286a2bff 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -485,7 +485,7 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } - public function getPlacementBlock(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ + public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ return $this->getBlock($face); } diff --git a/src/world/World.php b/src/world/World.php index 4f2e222ca..bd79ae083 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2298,7 +2298,7 @@ class World implements ChunkManager{ if($item->isNull() || !$item->canBePlaced()){ return false; } - $hand = $item->getPlacementBlock($blockReplace, $blockClicked, $face, $clickVector); + $hand = $item->getPlacementBlock($player, $blockReplace, $blockClicked, $face, $clickVector); $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ From de234d1f382ca4b4c8bf489e016ae59013693910 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 26 Aug 2025 00:10:50 +0100 Subject: [PATCH 121/140] Improved placement logic --- src/item/HangingSign.php | 33 +++++++++------------------------ src/item/Item.php | 15 +++++++++++++-- src/world/World.php | 35 +++++++++++++++-------------------- 3 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php index 5e3bd068a..a6752087a 100644 --- a/src/item/HangingSign.php +++ b/src/item/HangingSign.php @@ -24,13 +24,10 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\block\Block; -use pocketmine\block\CeilingCenterHangingSign; -use pocketmine\block\CeilingEdgesHangingSign; -use pocketmine\block\utils\SupportType; -use pocketmine\block\WallHangingSign; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\world\BlockTransaction; final class HangingSign extends Item{ @@ -44,27 +41,15 @@ final class HangingSign extends Item{ parent::__construct($identifier, $name); } - public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ - //we don't verify valid placement conditions here, only decide which block to return - if($face === Facing::DOWN){ - if($player !== null && $player->isSneaking()){ - return clone $this->centerPointCeilingVariant; - } - - //we select the center variant when support is edge/wall sign with perpendicular player facing, - //support is a center sign itself, or support provides center support. - //otherwise use the edge variant. - $support = $blockReplace->getSide(Facing::UP); - $result = - (($support instanceof CeilingEdgesHangingSign || $support instanceof WallHangingSign) && ($player === null || Facing::axis($player->getHorizontalFacing()) !== Facing::axis($support->getFacing()))) || - $support instanceof CeilingCenterHangingSign || - $support->getSupportType(Facing::DOWN) === SupportType::CENTER ? - $this->centerPointCeilingVariant : - $this->edgePointCeilingVariant; - }else{ - $result = $this->wallVariant; + public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ + if($face !== Facing::DOWN){ + return $this->tryPlacementTransaction(clone $this->wallVariant, $blockReplace, $blockClicked, $face, $clickVector, $player); } - return clone $result; + //ceiling edges sign has stricter placement conditions than ceiling center sign, so try that first + $ceilingEdgeTx = $player === null || !$player->isSneaking() ? + $this->tryPlacementTransaction(clone $this->edgePointCeilingVariant, $blockReplace, $blockClicked, $face, $clickVector, $player) : + null; + return $ceilingEdgeTx ?? $this->tryPlacementTransaction(clone $this->centerPointCeilingVariant, $blockReplace, $blockClicked, $face, $clickVector, $player); } public function getBlock(?int $clickedFace = null) : Block{ diff --git a/src/item/Item.php b/src/item/Item.php index c286a2bff..af7cab433 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -48,6 +48,7 @@ use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\TreeRoot; use pocketmine\player\Player; use pocketmine\utils\Utils; +use pocketmine\world\BlockTransaction; use pocketmine\world\format\io\GlobalItemDataHandlers; use function base64_decode; use function base64_encode; @@ -485,8 +486,18 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } - public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ - return $this->getBlock($face); + protected final function tryPlacementTransaction(Block $blockPlace, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player) : ?BlockTransaction{ + $position = $blockReplace->getPosition(); + $blockPlace->position($position->getWorld(), $position->getFloorX(), $position->getFloorY(), $position->getFloorZ()); + if(!$blockPlace->canBePlacedAt($blockReplace, $clickVector, $face, $blockReplace->getPosition()->equals($blockClicked->getPosition()))){ + return null; + } + $transaction = new BlockTransaction($position->getWorld()); + return $blockPlace->place($transaction, $this, $blockReplace, $blockClicked, $face, $clickVector, $player) ? $transaction : null; + } + + public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ + return $this->tryPlacementTransaction($this->getBlock($face), $blockReplace, $blockClicked, $face, $clickVector, $player); } /** diff --git a/src/world/World.php b/src/world/World.php index bd79ae083..236fd6e56 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2298,22 +2298,15 @@ class World implements ChunkManager{ if($item->isNull() || !$item->canBePlaced()){ return false; } - $hand = $item->getPlacementBlock($player, $blockReplace, $blockClicked, $face, $clickVector); - $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); - if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ - $blockReplace = $blockClicked; - //TODO: while this mimics the vanilla behaviour with replaceable blocks, we should really pass some other - //value like NULL and let place() deal with it. This will look like a bug to anyone who doesn't know about - //the vanilla behaviour. - $face = Facing::UP; - $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); - }elseif(!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)){ - return false; - } - - $tx = new BlockTransaction($this); - if(!$hand->place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ + //TODO: while passing Facing::UP mimics the vanilla behaviour with replaceable blocks, we should really pass + //some other value like NULL and let place() deal with it. This will look like a bug to anyone who doesn't know + //about the vanilla behaviour. + $tx = + $item->getPlacementTransaction($blockClicked, $blockClicked, Facing::UP, $clickVector, $player) ?? + $item->getPlacementTransaction($blockReplace, $blockClicked, $face, $clickVector, $player); + if($tx === null){ + //no placement options available return false; } @@ -2357,6 +2350,7 @@ class World implements ChunkManager{ if(!$tx->apply()){ return false; } + $first = true; foreach($tx->getBlocks() as [$x, $y, $z, $_]){ $tile = $this->getTileAt($x, $y, $z); if($tile !== null){ @@ -2364,11 +2358,12 @@ class World implements ChunkManager{ $tile->copyDataFromItem($item); } - $this->getBlockAt($x, $y, $z)->onPostPlace(); - } - - if($playSound){ - $this->addSound($hand->getPosition(), new BlockPlaceSound($hand)); + $placed = $this->getBlockAt($x, $y, $z); + $placed->onPostPlace(); + if($first && $playSound){ + $this->addSound($placed->getPosition(), new BlockPlaceSound($placed)); + } + $first = false; } $item->pop(); From dd9030f1f5e81cc7e72b567db392f06323744635 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 28 Aug 2025 21:15:09 +0100 Subject: [PATCH 122/140] tools/generate-bedrock-data-from-packets: generate less noise for items if we have only a name (the majority case), we can just return the name directly instead of an object. this massively reduces the amount of noise in the files as seen in pmmp/BedrockData@f814036229a3a8a71450159ed2aaea17832f5abf --- .../CraftingManagerFromDataHelper.php | 2 +- src/crafting/json/ItemStackData.php | 15 ++++++++++++++- tests/phpstan/configs/phpstan-bugs.neon | 6 ++++++ tools/generate-bedrock-data-from-packets.php | 19 +++++++++++++------ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php index 3df6bfb62..7c9cdd58b 100644 --- a/src/crafting/CraftingManagerFromDataHelper.php +++ b/src/crafting/CraftingManagerFromDataHelper.php @@ -162,7 +162,7 @@ final class CraftingManagerFromDataHelper{ } $mapper = new \JsonMapper(); - $mapper->bStrictObjectTypeChecking = true; + $mapper->bStrictObjectTypeChecking = false; //to allow hydrating ItemStackData - since this is only used for offline data it's safe $mapper->bExceptionOnUndefinedProperty = true; $mapper->bExceptionOnMissingData = true; diff --git a/src/crafting/json/ItemStackData.php b/src/crafting/json/ItemStackData.php index 032c7da7d..bf079e920 100644 --- a/src/crafting/json/ItemStackData.php +++ b/src/crafting/json/ItemStackData.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\crafting\json; -final class ItemStackData{ +use function count; + +final class ItemStackData implements \JsonSerializable{ /** @required */ public string $name; @@ -40,4 +42,15 @@ final class ItemStackData{ public function __construct(string $name){ $this->name = $name; } + + /** + * @return mixed[]|string + */ + public function jsonSerialize() : array|string{ + $result = (array) $this; + if(count($result) === 1 && isset($result["name"])){ + return $this->name; + } + return $result; + } } diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index e67f5768e..a80050020 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -168,6 +168,12 @@ parameters: count: 1 path: ../../../src/crafting/ShapedRecipe.php + - + message: '#^Offset ''name'' on \*NEVER\* in isset\(\) always exists and is not nullable\.$#' + identifier: isset.offset + count: 1 + path: ../../../src/crafting/json/ItemStackData.php + - message: '#^Property pocketmine\\crash\\CrashDumpData\:\:\$parameters \(list\\) does not accept array\.$#' identifier: assign.propertyType diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index b0aae57df..f40029365 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -209,11 +209,18 @@ class ParserPacketHandler extends PacketHandler{ return $data; } - /** - * @return mixed[] - */ - private static function objectToOrderedArray(object $object) : array{ - $result = (array) ($object instanceof \JsonSerializable ? $object->jsonSerialize() : $object); + private static function objectToOrderedArray(object $object) : mixed{ + if($object instanceof \JsonSerializable){ + $result = $object->jsonSerialize(); + if(is_object($result)){ + $result = (array) $result; + }elseif(!is_array($result)){ + return $result; + } + }else{ + $result = (array) $object; + } + ksort($result, SORT_STRING); foreach(Utils::promoteKeys($result) as $property => $value){ @@ -280,7 +287,7 @@ class ParserPacketHandler extends PacketHandler{ file_put_contents($this->bedrockDataPath . '/required_item_list.json', json_encode($table, JSON_PRETTY_PRINT) . "\n"); echo "updating item registry\n"; - $items = array_map(function(ItemTypeEntry $entry) : array{ + $items = array_map(function(ItemTypeEntry $entry) : mixed{ return self::objectToOrderedArray($entry); }, $packet->getEntries()); file_put_contents($this->bedrockDataPath . '/item_registry.json', json_encode($items, JSON_PRETTY_PRINT) . "\n"); From 2404d63b1fcc9a7b65a839b752dd52e69cb503ee Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 12:24:24 +0100 Subject: [PATCH 123/140] Ageable: added getMaxAge() we'll probably need this... --- src/block/utils/Ageable.php | 3 +++ src/block/utils/AgeableTrait.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/block/utils/Ageable.php b/src/block/utils/Ageable.php index 31fe3f459..180392ef3 100644 --- a/src/block/utils/Ageable.php +++ b/src/block/utils/Ageable.php @@ -27,7 +27,10 @@ interface Ageable{ public function getAge() : int; + public function getMaxAge() : int; + /** + * Must be in range 0 - getMaxAge() * @return $this */ public function setAge(int $age) : self; diff --git a/src/block/utils/AgeableTrait.php b/src/block/utils/AgeableTrait.php index dc1369c87..7a83ad66e 100644 --- a/src/block/utils/AgeableTrait.php +++ b/src/block/utils/AgeableTrait.php @@ -38,6 +38,8 @@ trait AgeableTrait{ public function getAge() : int{ return $this->age; } + public function getMaxAge() : int{ return self::MAX_AGE; } + /** * @return $this */ From 0be15a7403fb88f0fdc7887abd949057a87ddc1a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 12:33:04 +0100 Subject: [PATCH 124/140] Rename MultiFacing -> MultiAnyFacing to match the trait name --- src/block/GlowLichen.php | 4 ++-- src/block/ResinClump.php | 4 ++-- src/block/utils/{MultiFacing.php => MultiAnyFacing.php} | 2 +- .../bedrock/block/convert/property/CommonProperties.php | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/block/utils/{MultiFacing.php => MultiAnyFacing.php} (97%) diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index 6ad8d3748..c9dfad7f4 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -25,7 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\MultiAnySupportTrait; -use pocketmine\block\utils\MultiFacing; +use pocketmine\block\utils\MultiAnyFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Fertilizer; use pocketmine\item\Item; @@ -36,7 +36,7 @@ use pocketmine\world\World; use function count; use function shuffle; -class GlowLichen extends Transparent implements MultiFacing{ +class GlowLichen extends Transparent implements MultiAnyFacing{ use MultiAnySupportTrait; public function getLightLevel() : int{ diff --git a/src/block/ResinClump.php b/src/block/ResinClump.php index 2855a7cc3..56b42fa4a 100644 --- a/src/block/ResinClump.php +++ b/src/block/ResinClump.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\MultiAnySupportTrait; -use pocketmine\block\utils\MultiFacing; +use pocketmine\block\utils\MultiAnyFacing; use pocketmine\block\utils\SupportType; -final class ResinClump extends Transparent implements MultiFacing{ +final class ResinClump extends Transparent implements MultiAnyFacing{ use MultiAnySupportTrait; public function isSolid() : bool{ diff --git a/src/block/utils/MultiFacing.php b/src/block/utils/MultiAnyFacing.php similarity index 97% rename from src/block/utils/MultiFacing.php rename to src/block/utils/MultiAnyFacing.php index 9bdb3640d..dafe041e4 100644 --- a/src/block/utils/MultiFacing.php +++ b/src/block/utils/MultiAnyFacing.php @@ -25,7 +25,7 @@ namespace pocketmine\block\utils; use pocketmine\math\Facing; -interface MultiFacing{ +interface MultiAnyFacing{ /** * @return int[] diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php index 1a3224270..35d7e21b6 100644 --- a/src/data/bedrock/block/convert/property/CommonProperties.php +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -46,7 +46,7 @@ use pocketmine\block\utils\CoralType; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\Lightable; -use pocketmine\block\utils\MultiFacing; +use pocketmine\block\utils\MultiAnyFacing; use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SlabType; @@ -80,7 +80,7 @@ final class CommonProperties{ /** @phpstan-var ValueFromIntProperty */ public readonly ValueFromIntProperty $anyFacingClassic; - /** @phpstan-var OptionSetFromIntProperty */ + /** @phpstan-var OptionSetFromIntProperty */ public readonly OptionSetFromIntProperty $multiFacingFlags; /** @phpstan-var IntProperty */ @@ -252,8 +252,8 @@ final class CommonProperties{ Facing::WEST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST, Facing::EAST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST ]), - fn(MultiFacing $b) => $b->getFaces(), - fn(MultiFacing $b, array $v) => $b->setFaces($v) + fn(MultiAnyFacing $b) => $b->getFaces(), + fn(MultiAnyFacing $b, array $v) => $b->setFaces($v) ); $this->floorSignLikeRotation = new IntProperty(StateNames::GROUND_SIGN_DIRECTION, 0, 15, fn(SignLikeRotation $b) => $b->getRotation(), fn(SignLikeRotation $b, int $v) => $b->setRotation($v)); From 48ba3342187a5ff0602fbc44b55c2958d25d8877 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 12:33:50 +0100 Subject: [PATCH 125/140] CS again :< --- src/block/GlowLichen.php | 2 +- src/block/ResinClump.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index c9dfad7f4..4588d647d 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; -use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\MultiAnyFacing; +use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Fertilizer; use pocketmine\item\Item; diff --git a/src/block/ResinClump.php b/src/block/ResinClump.php index 56b42fa4a..a56a386d4 100644 --- a/src/block/ResinClump.php +++ b/src/block/ResinClump.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\MultiAnyFacing; +use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\SupportType; final class ResinClump extends Transparent implements MultiAnyFacing{ From beaedc3627458804bb822f64e44ae37975e07f4f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 13:07:09 +0100 Subject: [PATCH 126/140] Tidy up in block properties aisle --- src/data/bedrock/block/convert/VanillaBlockMappings.php | 6 +++--- .../bedrock/block/convert/property/CommonProperties.php | 6 +++--- ...onSetFromIntProperty.php => ValueSetFromIntProperty.php} | 2 +- .../block/convert/property/WallConnectionTypeShim.php | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) rename src/data/bedrock/block/convert/property/{OptionSetFromIntProperty.php => ValueSetFromIntProperty.php} (98%) diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index fc1c6acb6..f339ce3ef 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -118,10 +118,10 @@ use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap; use pocketmine\data\bedrock\block\convert\property\FlattenedCaveVinesVariant; use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap; use pocketmine\data\bedrock\block\convert\property\IntProperty; -use pocketmine\data\bedrock\block\convert\property\OptionSetFromIntProperty; use pocketmine\data\bedrock\block\convert\property\ValueFromIntProperty; use pocketmine\data\bedrock\block\convert\property\ValueFromStringProperty; use pocketmine\data\bedrock\block\convert\property\ValueMappings; +use pocketmine\data\bedrock\block\convert\property\ValueSetFromIntProperty; use pocketmine\math\Facing; use function array_map; use function min; @@ -581,7 +581,7 @@ final class VanillaBlockMappings{ $reg->mapModel(Model::create(Blocks::RESIN_CLUMP(), Ids::RESIN_CLUMP)->properties([$commonProperties->multiFacingFlags])); $reg->mapModel(Model::create(Blocks::VINES(), Ids::VINE)->properties([ - new OptionSetFromIntProperty( + new ValueSetFromIntProperty( StateNames::VINE_DIRECTION_BITS, IntFromRawStateMap::int([ Facing::NORTH => BlockLegacyMetadata::VINE_FLAG_NORTH, @@ -1267,7 +1267,7 @@ final class VanillaBlockMappings{ $reg->mapModel(Model::create(Blocks::CHAIN(), Ids::CHAIN)->properties([$commonProperties->pillarAxis])); $reg->mapModel(Model::create(Blocks::CHISELED_BOOKSHELF(), Ids::CHISELED_BOOKSHELF)->properties([ $commonProperties->horizontalFacingSWNE, - new OptionSetFromIntProperty( + new ValueSetFromIntProperty( StateNames::BOOKS_STORED, EnumFromRawStateMap::int(ChiseledBookshelfSlot::class, fn(ChiseledBookshelfSlot $case) => match($case){ //these are (currently) the same as the internal values, but it's best not to rely on those in case Mojang mess with the flags diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php index 35d7e21b6..666637027 100644 --- a/src/data/bedrock/block/convert/property/CommonProperties.php +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -80,8 +80,8 @@ final class CommonProperties{ /** @phpstan-var ValueFromIntProperty */ public readonly ValueFromIntProperty $anyFacingClassic; - /** @phpstan-var OptionSetFromIntProperty */ - public readonly OptionSetFromIntProperty $multiFacingFlags; + /** @phpstan-var ValueSetFromIntProperty */ + public readonly ValueSetFromIntProperty $multiFacingFlags; /** @phpstan-var IntProperty */ public readonly IntProperty $floorSignLikeRotation; @@ -242,7 +242,7 @@ final class CommonProperties{ fn(AnyFacing $b, int $v) => $b->setFacing($v) ); - $this->multiFacingFlags = new OptionSetFromIntProperty( + $this->multiFacingFlags = new ValueSetFromIntProperty( StateNames::MULTI_FACE_DIRECTION_BITS, IntFromRawStateMap::int([ Facing::DOWN => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN, diff --git a/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php b/src/data/bedrock/block/convert/property/ValueSetFromIntProperty.php similarity index 98% rename from src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php rename to src/data/bedrock/block/convert/property/ValueSetFromIntProperty.php index a91c681b8..89913d78b 100644 --- a/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php +++ b/src/data/bedrock/block/convert/property/ValueSetFromIntProperty.php @@ -32,7 +32,7 @@ use pocketmine\utils\AssumptionFailedError; * @phpstan-template TOption of int|\UnitEnum * @phpstan-implements Property */ -class OptionSetFromIntProperty implements Property{ +class ValueSetFromIntProperty implements Property{ private int $maxValue = 0; diff --git a/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php index bdd878b52..c7d4913cf 100644 --- a/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php +++ b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php @@ -30,6 +30,7 @@ use pocketmine\data\bedrock\block\BlockStateStringValues; * Internally we use null for no connections, but accepting this in the mapping code would require a fair amount of * extra complexity for this one case. This shim allows us to use the regular systems for handling walls. * TODO: get rid of this in PM6 and make the internal enum have a NONE case + * @internal */ enum WallConnectionTypeShim{ case NONE; From 8f7e16a9adc697b4dd9dea6a97b6f570961c831c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 14:11:50 +0100 Subject: [PATCH 127/140] Prepare 5.33.0 release --- changelogs/5.33.md | 124 ++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 +- 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.33.md diff --git a/changelogs/5.33.md b/changelogs/5.33.md new file mode 100644 index 000000000..dc174cd76 --- /dev/null +++ b/changelogs/5.33.md @@ -0,0 +1,124 @@ +# 5.33.0 +Released 29th August 2025. + +This is a minor feature release containing internals improvements, API improvements and new gameplay features. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## Performance +- Worlds now remember when a chunk isn't generated. This reduces world I/O during world generation. +- `BlockObjectToStateSerializer` now creates fewer objects in certain cases. + +## Gameplay +- The following blocks have been added and/or are now properly supported: + - Hanging signs + - Illager banners + +## Tools +- `generate-bedrock-data-from-packets.php` now represents items as strings directly when only an ID is present. This significantly improves readability in `BedrockData` and reduces file sizes. + +## API +### `pocketmine\block` +- Added (and implemented) interfaces for many common block properties, to allow `instanceof` to be used: + - `Ageable`: for blocks with age, such as crops + - `AnyFacing`: for blocks which can face up, down, and horizontal directions (not the same as `HorizontalFacing`!) + - `Colored`: for blocks with 16 `DyeColor` variants + - `CoralMaterial`: for coral blocks, provides access to coral type and dead/alive + - `HorizontalFacing`: for blocks which can **only** face horizontal directions (not the same as `AnyFacing`!) + - `Lightable`: for light-source blocks which can be turned on and off, e.g. redstone lamp + - `MultiAnyFacing`: for blocks which can appear in multiple faces of the same block (including up, down, and horizontal faces), e.g. glow lichen + - `PillarRotation`: for blocks which can be oriented on an axis, e.g. logs + - `PoweredByRedstone`: for blocks which receive power from a redstone component, e.g. redstone lamp + - `SignLikeRotation`: for blocks which can be rotated 16 ways, e.g. signs, banners + - `WoodMaterial`: for blocks made from wood + - These interfaces have been implemented on many blocks. For the sake of brevity, they are not listed here, but you can expect to see them wherever the corresponding traits were used. +- The following classes have been added: + - `BaseOminousBanner` + - `CeilingCenterHangingSign` - both chains connected to the same point on the block above, can face 16 directions + - `CeilingEdgesHangingSign` - each chain connected to separate edges of the block above, can face 4 directions + - `OminousFloorBanner` - floor version of illager banner, can face 16 directions + - `OminousWallBanner` - wall version of illager banner, can face 4 directions + - `WallHangingSign` - hangs from a horizontal beam, can face 4 directions +- The following API methods have been added: + - `public ChiseledBookshelf->setSlots(list $slots) : $this` + - `public static VanillaBlocks` methods: + - `ACACIA_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `ACACIA_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `ACACIA_WALL_HANGING_SIGN() : WallHangingSign` + - `BIRCH_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `BIRCH_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `BIRCH_WALL_HANGING_SIGN() : WallHangingSign` + - `CHERRY_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `CHERRY_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `CHERRY_WALL_HANGING_SIGN() : WallHangingSign` + - `CRIMSON_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `CRIMSON_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `CRIMSON_WALL_HANGING_SIGN() : WallHangingSign` + - `DARK_OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `DARK_OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `DARK_OAK_WALL_HANGING_SIGN() : WallHangingSign` + - `JUNGLE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `JUNGLE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `JUNGLE_WALL_HANGING_SIGN() : WallHangingSign` + - `MANGROVE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `MANGROVE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `MANGROVE_WALL_HANGING_SIGN() : WallHangingSign` + - `OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `OAK_WALL_HANGING_SIGN() : WallHangingSign` + - `OMINOUS_FLOOR_BANNER() : OminousFloorBanner` + - `OMINOUS_WALL_BANNER() : OminousWallBanner` + - `PALE_OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `PALE_OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `PALE_OAK_WALL_HANGING_SIGN() : WallHangingSign` + - `SPRUCE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `SPRUCE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `SPRUCE_WALL_HANGING_SIGN() : WallHangingSign` + - `WARPED_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` + - `WARPED_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` + - `WARPED_WALL_HANGING_SIGN() : WallHangingSign` + +### `pocketmine\data\bedrock\block\convert` +- A new system for symmetric block serializers and deserializers has been introduced. + - This allows registering both a serializer and a deserializer with the same code, meaning way less code + - It also eliminates information duplication and potential inconsistencies, improving maintainability. + - A proper way to deal with flattened IDs (e.g. color blocks) has been introduced which _doesn't_ require hardcoding a giant mess of IDs + - This symmetric system covers 99% of blocks which have a 1:1 association between PM and vanilla blocks, or 1:N where IDs are flattened + - However, there are still some special cases which require registering separate serializers and deserializers (usually in cases where the PM implementation deviates from Mojang where Mojang's implementation sucks, such as hanging signs or big dripleaf). + - No backwards compatibility breaks are expected as a result of this change. However, it's recommended to migrate old code to this new system for maintainability. + - The following new classes have been added: + - `BlockSerializerDeserializerRegistrar` - handles unified registration of block serializers and deserializers, based on a provided block model + - `FlattenedIdModel` - represents a block with some properties baked into its Minecraft ID, e.g. coral or color blocks + - `Model` - represents a regular block with all properties in its `states` NBT + - `property\BoolFromStringProperty` - property mapping a bool value from a string NBT state + - `property\BoolProperty` + - `property\CommonProperties` - singleton containing commonly-used block property definitions and groups, e.g. facing, stair properties + - `property\EnumFromRawStateMap` - maps a raw NBT value to a PHP `enum` and vice versa + - `property\IntFromRawStateMap` - maps a raw NBT value to PM integer constants and vice versa + - `property\IntProperty` - an integer range property with a min, max, and optional offset + - `property\Property` - interface implemented by all property definitions accepted by a `Model` or `FlattenedIdModel` + - `property\StateMap` - interface implemented by classes accepted by mapping properties, e.g. `BoolFromStringProperty` + - `property\StringProperty` - interface implemented by properties whose raw outputs are strings - these can be used as ID components in `FlattenedIdModel` + - `property\ValueFromIntProperty` - property mapping a generic PM value from an int NBT state + - `property\ValueFromStringProperty` - same as above, but for a string NBT state + - `property\ValueSetFromIntProperty` - a property mapping an `int[]` or `enum[]` from a set of flags in NBT states + - `property\ValueMappings` - singleton containing commonly-needed `StateMap`s + - The following classes have been deprecated: + - `BlockStateDeserializerHelper` + - `BlockStateSerializerHelper` + - The following methods have been deprecated: + - All methods for decoding mapped property types in `BlockStateReader`, e.g. `readFacingDirection()` + - All methods for encoding mapped property types in `BlockStateWriter`, e.g. `writeFacingDirection()` + - All specific blocktype mapping functions in `BlockStateToObjectDeserializer`, e.g. `mapStairs()` + - All specific blocktype mapping functions in `BlockObjectToStateSerializer`, e.g. `mapStairs()` + +### `pocketmine\world` +- `World->setChunk()` now verifies that blockstate IDs in the provided chunk are all registered in `RuntimeBlockStateRegistry`. This should provide earlier detection for custom block registration errors by plugins. + +## Internals +- `BlockStateUpgrader` is now almost entirely independent from `BlockStateData`. It's anticipated that the upgrader library will be separable from the core in the future. +- `Block->readStateFromWorld()` is now triggered on chunk load for any position containing a tile. This should allow more effective updating of blocks with properties in their tiles. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index b3f37677b..dd8bed4b6 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.32.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.33.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 23d612f1af73e84f3dc26844d64df9565551fe54 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 18:49:08 +0100 Subject: [PATCH 128/140] Suggested additions --- changelogs/5.33.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelogs/5.33.md b/changelogs/5.33.md index dc174cd76..8c61e90bc 100644 --- a/changelogs/5.33.md +++ b/changelogs/5.33.md @@ -81,6 +81,7 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - `WARPED_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign` - `WARPED_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign` - `WARPED_WALL_HANGING_SIGN() : WallHangingSign` + - `public AgeableTrait->getMaxAge() : int` (included by all growable plant-like blocks, e.g. crops) ### `pocketmine\data\bedrock\block\convert` - A new system for symmetric block serializers and deserializers has been introduced. @@ -116,6 +117,10 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - All specific blocktype mapping functions in `BlockStateToObjectDeserializer`, e.g. `mapStairs()` - All specific blocktype mapping functions in `BlockObjectToStateSerializer`, e.g. `mapStairs()` +### `pocketmine\item` +- The following hooks have been added: + - `public Item->getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction` - allows more complex logic for itemblocks to place blocks, without duplicating their placement conditions (used for hanging signs) + ### `pocketmine\world` - `World->setChunk()` now verifies that blockstate IDs in the provided chunk are all registered in `RuntimeBlockStateRegistry`. This should provide earlier detection for custom block registration errors by plugins. From f1b1e1977edd1c1ae7560ef12d7f4ce9e4df19bd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 20:37:29 +0100 Subject: [PATCH 129/140] Harden validation for server auth block breaking --- src/network/mcpe/handler/InGamePacketHandler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index b5990d38f..6aae60745 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -719,6 +719,7 @@ class InGamePacketHandler extends PacketHandler{ case PlayerAction::CREATIVE_PLAYER_DESTROY_BLOCK: //TODO: do we need to handle this? case PlayerAction::PREDICT_DESTROY_BLOCK: + self::validateFacing($face); if(!$this->player->breakBlock($pos)){ $this->syncBlocksNearby($pos, $face); } From 6f6b23d4e46c07260f6970fe9d5e1064d81c74dc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 21:47:20 +0100 Subject: [PATCH 130/140] Integrate dev-major-next version of pocketmine/math this is a reduced version compared to the original, due to the difficulty of getting rid of Facing values internally. --- composer.json | 2 +- composer.lock | 35 +++-- src/block/AmethystCluster.php | 10 +- src/block/Anvil.php | 8 +- src/block/Bamboo.php | 6 +- src/block/BambooSapling.php | 2 +- src/block/Barrel.php | 6 +- src/block/BaseBanner.php | 7 +- src/block/BaseBigDripleaf.php | 6 +- src/block/BaseCake.php | 4 +- src/block/BaseCoral.php | 3 +- src/block/BaseOminousBanner.php | 7 +- src/block/BaseRail.php | 30 +++-- src/block/BaseSign.php | 9 +- src/block/Bed.php | 10 +- src/block/Bell.php | 24 ++-- src/block/BigDripleafHead.php | 8 +- src/block/Block.php | 27 ++-- src/block/BrewingStand.php | 12 +- src/block/Button.php | 8 +- src/block/Cactus.php | 4 +- src/block/Cake.php | 8 +- src/block/CakeWithCandle.php | 6 +- src/block/Campfire.php | 8 +- src/block/Candle.php | 32 ++--- src/block/Carpet.php | 2 +- src/block/CartographyTable.php | 3 +- src/block/Cauldron.php | 8 +- src/block/CaveVines.php | 6 +- src/block/CeilingCenterHangingSign.php | 4 +- src/block/CeilingEdgesHangingSign.php | 4 +- src/block/Chain.php | 4 +- src/block/ChemistryTable.php | 3 +- src/block/Chest.php | 6 +- src/block/ChiseledBookshelf.php | 2 +- src/block/ChorusFlower.php | 10 +- src/block/ChorusPlant.php | 8 +- src/block/CocoaBlock.php | 14 +- src/block/CopperDoor.php | 2 +- src/block/CopperTrapdoor.php | 3 +- src/block/CraftingTable.php | 3 +- src/block/Crops.php | 2 +- src/block/DaylightSensor.php | 6 +- src/block/Dirt.php | 2 +- src/block/Door.php | 8 +- src/block/DoublePitcherCrop.php | 10 +- src/block/DoublePlant.php | 2 +- src/block/DragonEgg.php | 7 +- src/block/EnchantingTable.php | 6 +- src/block/EndPortalFrame.php | 2 +- src/block/EndRod.php | 4 +- src/block/EnderChest.php | 6 +- src/block/Farmland.php | 2 +- src/block/Fence.php | 34 ++--- src/block/FenceGate.php | 8 +- src/block/FillableCauldron.php | 6 +- src/block/FloorBanner.php | 4 +- src/block/FloorCoralFan.php | 8 +- src/block/FloorSign.php | 4 +- src/block/Flowable.php | 5 +- src/block/FlowerPot.php | 4 +- src/block/Furnace.php | 3 +- src/block/GlowLichen.php | 20 +-- src/block/Grass.php | 2 +- src/block/GrassPath.php | 2 +- src/block/Hopper.php | 16 +-- src/block/ItemFrame.php | 10 +- src/block/Jukebox.php | 3 +- src/block/Ladder.php | 8 +- src/block/Lantern.php | 14 +- src/block/LavaCauldron.php | 3 +- src/block/Leaves.php | 4 +- src/block/Lectern.php | 8 +- src/block/Lever.php | 8 +- src/block/Light.php | 5 +- src/block/LightningRod.php | 4 +- src/block/Liquid.php | 6 +- src/block/Loom.php | 3 +- src/block/MobHead.php | 16 +-- src/block/MonsterSpawner.php | 3 +- src/block/NetherPortal.php | 9 +- src/block/NetherVines.php | 10 +- src/block/OminousFloorBanner.php | 4 +- src/block/OminousWallBanner.php | 4 +- src/block/PinkPetals.php | 6 +- src/block/PitcherCrop.php | 10 +- src/block/PotionCauldron.php | 3 +- src/block/PressurePlate.php | 10 +- src/block/Pumpkin.php | 2 +- src/block/Rail.php | 4 +- src/block/RedMushroom.php | 2 +- src/block/RedstoneComparator.php | 6 +- src/block/RedstoneOre.php | 3 +- src/block/RedstoneRepeater.php | 6 +- src/block/ResinClump.php | 5 +- src/block/RespawnAnchor.php | 3 +- src/block/Sapling.php | 2 +- src/block/SeaPickle.php | 9 +- src/block/ShulkerBox.php | 7 +- src/block/Slab.php | 8 +- src/block/SmallDripleaf.php | 6 +- src/block/SmithingTable.php | 3 +- src/block/SnowLayer.php | 6 +- src/block/SoulSand.php | 2 +- src/block/Stair.php | 20 +-- src/block/Stem.php | 6 +- src/block/Stonecutter.php | 6 +- src/block/Sugarcane.php | 4 +- src/block/SweetBerryBush.php | 2 +- src/block/TNT.php | 3 +- src/block/Thin.php | 32 ++--- src/block/Torch.php | 10 +- src/block/TorchflowerCrop.php | 2 +- src/block/Trapdoor.php | 8 +- src/block/TripwireHook.php | 2 +- src/block/Vine.php | 29 ++-- src/block/Wall.php | 40 +++--- src/block/WallBanner.php | 4 +- src/block/WallCoralFan.php | 4 +- src/block/WallHangingSign.php | 8 +- src/block/WallSign.php | 4 +- src/block/WaterCauldron.php | 3 +- src/block/WaterLily.php | 4 +- src/block/Wood.php | 3 +- src/block/tile/Bell.php | 18 ++- src/block/tile/ShulkerBox.php | 16 ++- src/block/utils/AnyFacing.php | 10 +- src/block/utils/AnyFacingTrait.php | 9 +- src/block/utils/CandleTrait.php | 4 +- src/block/utils/CopperTrait.php | 4 +- .../utils/FacesOppositePlacingPlayerTrait.php | 2 +- src/block/utils/HorizontalFacing.php | 14 +- src/block/utils/HorizontalFacingTrait.php | 7 +- src/block/utils/LeverFacing.php | 2 +- src/block/utils/MinimumCostFlowCalculator.php | 15 ++- src/block/utils/MultiAnyFacing.php | 21 +-- src/block/utils/MultiAnyFacingTrait.php | 20 ++- src/block/utils/MultiAnySupportTrait.php | 15 ++- src/block/utils/PillarRotation.php | 11 +- src/block/utils/PillarRotationTrait.php | 10 +- src/block/utils/RailConnectionInfo.php | 40 +++--- src/block/utils/StaticSupportTrait.php | 3 +- .../block/convert/VanillaBlockMappings.php | 29 ++-- .../convert/property/CommonProperties.php | 52 ++++---- .../block/convert/property/ValueMappings.php | 125 +++++++++--------- src/data/runtime/RuntimeDataDescriber.php | 17 +-- src/data/runtime/RuntimeDataReader.php | 43 ++---- .../runtime/RuntimeDataSizeCalculator.php | 19 +-- src/data/runtime/RuntimeDataWriter.php | 56 +++----- src/entity/Entity.php | 29 ++-- src/entity/Living.php | 2 +- src/entity/Location.php | 2 +- src/entity/object/Painting.php | 38 +++--- src/entity/projectile/IceBomb.php | 2 +- src/entity/projectile/Projectile.php | 2 +- src/event/player/PlayerBucketEvent.php | 5 +- src/event/player/PlayerInteractEvent.php | 5 +- src/item/Bamboo.php | 3 +- src/item/BeetrootSeeds.php | 3 +- src/item/Bucket.php | 3 +- src/item/Carrot.php | 3 +- src/item/CocoaBeans.php | 3 +- src/item/CoralFan.php | 2 +- src/item/EndCrystal.php | 6 +- src/item/FireCharge.php | 3 +- src/item/FlintSteel.php | 3 +- src/item/GlassBottle.php | 3 +- src/item/GlowBerries.php | 3 +- src/item/HangingSign.php | 4 +- src/item/Item.php | 9 +- src/item/ItemBlock.php | 3 +- src/item/ItemBlockWallOrFloor.php | 2 +- src/item/LiquidBucket.php | 3 +- src/item/MelonSeeds.php | 3 +- src/item/PaintingItem.php | 2 +- src/item/PitcherPod.php | 3 +- src/item/Potato.php | 3 +- src/item/PumpkinSeeds.php | 3 +- src/item/Redstone.php | 3 +- src/item/SpawnEgg.php | 3 +- src/item/StringItem.php | 3 +- src/item/SweetBerries.php | 3 +- src/item/TorchflowerSeeds.php | 3 +- src/item/WheatSeeds.php | 3 +- .../mcpe/handler/InGamePacketHandler.php | 25 ++-- src/player/Player.php | 20 ++- src/player/SurvivalBlockBreakHandler.php | 7 +- src/world/Position.php | 9 +- src/world/World.php | 8 +- src/world/generator/object/AcaciaTree.php | 2 +- src/world/light/LightPropagationContext.php | 4 +- src/world/light/LightUpdate.php | 5 +- src/world/particle/BlockPunchParticle.php | 9 +- tests/phpstan/configs/actual-problems.neon | 2 +- 194 files changed, 909 insertions(+), 873 deletions(-) diff --git a/composer.json b/composer.json index ff7f6c81e..67c4f16a4 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ "pocketmine/errorhandler": "^0.7.0", "pocketmine/locale-data": "~2.25.0", "pocketmine/log": "^0.4.0", - "pocketmine/math": "~1.0.0", + "pocketmine/math": "dev-major-next as 1.0.0", "pocketmine/nbt": "~1.1.0", "pocketmine/raklib": "~1.2.0", "pocketmine/raklib-ipc": "~1.0.0", diff --git a/composer.lock b/composer.lock index d3d57ea4a..c95c2ce8b 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": "6aac4fb3990be02fdbbe03c8dd33a6fa", + "content-hash": "1c81eefad19096d505bd201ff6546367", "packages": [ { "name": "adhocore/json-comment", @@ -535,27 +535,27 @@ }, { "name": "pocketmine/math", - "version": "1.0.0", + "version": "dev-major-next", "source": { "type": "git", "url": "https://github.com/pmmp/Math.git", - "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf" + "reference": "7513b9504e4a9d3ebde2f9d4de50266cb1dafd23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Math/zipball/dc132d93595b32e9f210d78b3c8d43c662a5edbf", - "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf", + "url": "https://api.github.com/repos/pmmp/Math/zipball/7513b9504e4a9d3ebde2f9d4de50266cb1dafd23", + "reference": "7513b9504e4a9d3ebde2f9d4de50266cb1dafd23", "shasum": "" }, "require": { - "php": "^8.0", + "php": "^8.2", "php-64bit": "*" }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "~1.10.3", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^8.5 || ^9.5" + "phpstan/phpstan": "2.1.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^10.0 || ^11.0" }, "type": "library", "autoload": { @@ -570,9 +570,9 @@ "description": "PHP library containing math related code used in PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Math/issues", - "source": "https://github.com/pmmp/Math/tree/1.0.0" + "source": "https://github.com/pmmp/Math/tree/major-next" }, - "time": "2023-08-03T12:56:33+00:00" + "time": "2025-08-29T18:29:53+00:00" }, { "name": "pocketmine/nbt", @@ -2724,9 +2724,18 @@ "time": "2024-03-03T12:36:25+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "pocketmine/math", + "version": "dev-major-next", + "alias": "1.0.0", + "alias_normalized": "1.0.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "pocketmine/math": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/block/AmethystCluster.php b/src/block/AmethystCluster.php index 8a750e974..d939994a2 100644 --- a/src/block/AmethystCluster.php +++ b/src/block/AmethystCluster.php @@ -82,22 +82,22 @@ final class AmethystCluster extends Transparent implements AnyFacing{ if($axis === $myAxis){ continue; } - $box->squash($axis, $this->stage === self::STAGE_SMALL_BUD ? 4 / 16 : 3 / 16); + $box = $box->squashedCopy($axis, $this->stage === self::STAGE_SMALL_BUD ? 4 / 16 : 3 / 16); } - $box->trim($this->facing, 1 - ($this->stage === self::STAGE_CLUSTER ? 7 / 16 : ($this->stage + 3) / 16)); + $box = $box->trimmedCopy($this->facing, 1 - ($this->stage === self::STAGE_CLUSTER ? 7 / 16 : ($this->stage + 3) / 16)); return [$box]; } - private function canBeSupportedAt(Block $block, int $facing) : bool{ + private function canBeSupportedAt(Block $block, Facing $facing) : bool{ return $block->getAdjacentSupportType($facing) === SupportType::FULL; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ return false; } diff --git a/src/block/Anvil.php b/src/block/Anvil.php index fcb4d045c..1d08e53ae 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -72,14 +72,14 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->squash(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)]; + return [AxisAlignedBB::one()->squashedCopy(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $player->setCurrentWindow(new AnvilInventory($this->position)); } @@ -87,7 +87,7 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{ return true; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->facing = Facing::rotateY($player->getHorizontalFacing(), false); } diff --git a/src/block/Bamboo.php b/src/block/Bamboo.php index fd64e10ef..015db6a18 100644 --- a/src/block/Bamboo.php +++ b/src/block/Bamboo.php @@ -90,10 +90,10 @@ class Bamboo extends Transparent{ protected function recalculateCollisionBoxes() : array{ //this places the BB at the northwest corner, not the center $inset = 1 - (($this->thick ? 3 : 2) / 16); - return [AxisAlignedBB::one()->trim(Facing::SOUTH, $inset)->trim(Facing::EAST, $inset)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::SOUTH, $inset)->trimmedCopy(Facing::EAST, $inset)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -138,7 +138,7 @@ class Bamboo extends Transparent{ return $top; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer){ $top = $this->seekToTop(); if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){ diff --git a/src/block/BambooSapling.php b/src/block/BambooSapling.php index 67c8a24e0..ddcde9981 100644 --- a/src/block/BambooSapling.php +++ b/src/block/BambooSapling.php @@ -61,7 +61,7 @@ final class BambooSapling extends Flowable{ $supportBlock->hasTypeTag(BlockTypeTags::SAND); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer || $item instanceof ItemBamboo){ if($this->grow($player)){ $item->pop(); diff --git a/src/block/Barrel.php b/src/block/Barrel.php index 7b2ea356e..68f584f10 100644 --- a/src/block/Barrel.php +++ b/src/block/Barrel.php @@ -40,7 +40,7 @@ class Barrel extends Opaque implements AnyFacing{ protected bool $open = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facing($this->facing); + $w->enum($this->facing); $w->bool($this->open); } @@ -54,7 +54,7 @@ class Barrel extends Opaque implements AnyFacing{ return $this; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ if(abs($player->getPosition()->x - $this->position->x) < 2 && abs($player->getPosition()->z - $this->position->z) < 2){ $y = $player->getEyePos()->y; @@ -74,7 +74,7 @@ class Barrel extends Opaque implements AnyFacing{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $barrel = $this->position->getWorld()->getTile($this->position); if($barrel instanceof TileBarrel){ diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index e8bf187ee..dcdc36c60 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -31,6 +31,7 @@ use pocketmine\block\utils\SupportType; use pocketmine\item\Banner as ItemBanner; use pocketmine\item\Item; use pocketmine\item\VanillaItems; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -112,7 +113,7 @@ abstract class BaseBanner extends Transparent implements Colored{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -120,7 +121,7 @@ abstract class BaseBanner extends Transparent implements Colored{ return $block->isSolid(); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){ return false; } @@ -132,7 +133,7 @@ abstract class BaseBanner extends Transparent implements Colored{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - abstract protected function getSupportingFace() : int; + abstract protected function getSupportingFace() : Facing; public function onNearbyBlockChange() : void{ if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){ diff --git a/src/block/BaseBigDripleaf.php b/src/block/BaseBigDripleaf.php index 94e2c12a2..452184d74 100644 --- a/src/block/BaseBigDripleaf.php +++ b/src/block/BaseBigDripleaf.php @@ -57,7 +57,7 @@ abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $block = $blockReplace->getSide(Facing::DOWN); if(!$this->canBeSupportedBy($block, true)){ return false; @@ -72,7 +72,7 @@ abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); return true; @@ -131,7 +131,7 @@ abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{ return 100; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/BaseCake.php b/src/block/BaseCake.php index 4b3903840..47a5f60f9 100644 --- a/src/block/BaseCake.php +++ b/src/block/BaseCake.php @@ -36,7 +36,7 @@ use pocketmine\player\Player; abstract class BaseCake extends Transparent implements FoodSource{ use StaticSupportTrait; - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -44,7 +44,7 @@ abstract class BaseCake extends Transparent implements FoodSource{ return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ return $player->consumeObject($this); } diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php index c1cc9bb25..936d068ed 100644 --- a/src/block/BaseCoral.php +++ b/src/block/BaseCoral.php @@ -28,6 +28,7 @@ use pocketmine\block\utils\CoralMaterial; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; +use pocketmine\math\Facing; use function mt_rand; abstract class BaseCoral extends Transparent implements CoralMaterial{ @@ -72,7 +73,7 @@ abstract class BaseCoral extends Transparent implements CoralMaterial{ protected function recalculateCollisionBoxes() : array{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/BaseOminousBanner.php b/src/block/BaseOminousBanner.php index 192e6fac2..a16e133a7 100644 --- a/src/block/BaseOminousBanner.php +++ b/src/block/BaseOminousBanner.php @@ -28,6 +28,7 @@ use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\item\VanillaItems; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -60,7 +61,7 @@ abstract class BaseOminousBanner extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -68,7 +69,7 @@ abstract class BaseOminousBanner extends Transparent{ return $block->isSolid(); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){ return false; } @@ -76,7 +77,7 @@ abstract class BaseOminousBanner extends Transparent{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - abstract protected function getSupportingFace() : int; + abstract protected function getSupportingFace() : Facing; public function onNearbyBlockChange() : void{ if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){ diff --git a/src/block/BaseRail.php b/src/block/BaseRail.php index 0bcb2f340..15b7d2bec 100644 --- a/src/block/BaseRail.php +++ b/src/block/BaseRail.php @@ -37,7 +37,7 @@ use function in_array; abstract class BaseRail extends Flowable{ - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($blockReplace->getAdjacentSupportType(Facing::DOWN)->hasEdgeSupport()){ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -89,8 +89,9 @@ abstract class BaseRail extends Flowable{ /** @var int $connection */ foreach($this->getCurrentShapeConnections() as $connection){ - $other = $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND); - $otherConnection = Facing::opposite($connection & ~RailConnectionInfo::FLAG_ASCEND); + $connectionFace = Facing::from($connection & ~RailConnectionInfo::FLAG_ASCEND); + $other = $this->getSide($connectionFace); + $otherConnection = Facing::opposite($connectionFace)->value; if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0){ $other = $other->getSide(Facing::UP); @@ -122,10 +123,10 @@ abstract class BaseRail extends Flowable{ case 0: //No constraints, can connect in any direction $possible = [ - Facing::NORTH => true, - Facing::SOUTH => true, - Facing::WEST => true, - Facing::EAST => true + Facing::NORTH->value => true, + Facing::SOUTH->value => true, + Facing::WEST->value => true, + Facing::EAST->value => true ]; foreach($possible as $p => $_){ $possible[$p | RailConnectionInfo::FLAG_ASCEND] = true; @@ -146,13 +147,13 @@ abstract class BaseRail extends Flowable{ * @phpstan-return array */ protected function getPossibleConnectionDirectionsOneConstraint(int $constraint) : array{ - $opposite = Facing::opposite($constraint & ~RailConnectionInfo::FLAG_ASCEND); + $opposite = Facing::opposite(Facing::from($constraint & ~RailConnectionInfo::FLAG_ASCEND)); - $possible = [$opposite => true]; + $possible = [$opposite->value => true]; if(($constraint & RailConnectionInfo::FLAG_ASCEND) === 0){ //We can slope the other way if this connection isn't already a slope - $possible[$opposite | RailConnectionInfo::FLAG_ASCEND] = true; + $possible[$opposite->value | RailConnectionInfo::FLAG_ASCEND] = true; } return $possible; @@ -168,9 +169,10 @@ abstract class BaseRail extends Flowable{ $continue = false; foreach($possible as $thisSide => $_){ - $otherSide = Facing::opposite($thisSide & ~RailConnectionInfo::FLAG_ASCEND); + $thisSideEnum = Facing::from($thisSide & ~RailConnectionInfo::FLAG_ASCEND); + $otherSide = Facing::opposite($thisSideEnum)->value; - $other = $this->getSide($thisSide & ~RailConnectionInfo::FLAG_ASCEND); + $other = $this->getSide($thisSideEnum); if(($thisSide & RailConnectionInfo::FLAG_ASCEND) !== 0){ $other = $other->getSide(Facing::UP); @@ -212,7 +214,7 @@ abstract class BaseRail extends Flowable{ */ private function setConnections(array $connections) : void{ if(count($connections) === 1){ - $connections[] = Facing::opposite($connections[0] & ~RailConnectionInfo::FLAG_ASCEND); + $connections[] = Facing::opposite(Facing::from($connections[0] & ~RailConnectionInfo::FLAG_ASCEND))->value; }elseif(count($connections) !== 2){ throw new \InvalidArgumentException("Expected exactly 2 connections, got " . count($connections)); } @@ -226,7 +228,7 @@ abstract class BaseRail extends Flowable{ $world->useBreakOn($this->position); }else{ foreach($this->getCurrentShapeConnections() as $connection){ - if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && !$this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->getSupportType(Facing::UP)->hasEdgeSupport()){ + if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && !$this->getSide(Facing::from($connection & ~RailConnectionInfo::FLAG_ASCEND))->getSupportType(Facing::UP)->hasEdgeSupport()){ $world->useBreakOn($this->position); break; } diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 0efaa603c..f631ff60a 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -35,6 +35,7 @@ use pocketmine\event\block\SignChangeEvent; use pocketmine\item\Dye; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\utils\TextFormat; @@ -99,11 +100,11 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - abstract protected function getSupportingFace() : int; + abstract protected function getSupportingFace() : Facing; public function onNearbyBlockChange() : void{ if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){ @@ -111,7 +112,7 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->editorEntityRuntimeId = $player->getId(); } @@ -160,7 +161,7 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ return true; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player === null){ return false; } diff --git a/src/block/Bed.php b/src/block/Bed.php index 21bd3a172..84589664a 100644 --- a/src/block/Bed.php +++ b/src/block/Bed.php @@ -79,10 +79,10 @@ class Bed extends Transparent implements Colored, HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 7 / 16)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -106,7 +106,7 @@ class Bed extends Transparent implements Colored, HorizontalFacing{ return $this; } - private function getOtherHalfSide() : int{ + private function getOtherHalfSide() : Facing{ return $this->head ? Facing::opposite($this->facing) : $this->facing; } @@ -119,7 +119,7 @@ class Bed extends Transparent implements Colored, HorizontalFacing{ return null; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $other = $this->getOtherHalf(); $playerPos = $player->getPosition(); @@ -172,7 +172,7 @@ class Bed extends Transparent implements Colored, HorizontalFacing{ return $entity->getMotion()->y * -3 / 4; // 2/3 in Java, according to the wiki } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($this->canBeSupportedAt($blockReplace)){ $this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH; diff --git a/src/block/Bell.php b/src/block/Bell.php index 258abc628..cf93f4501 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -52,26 +52,26 @@ final class Bell extends Transparent implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ if($this->attachmentType === BellAttachmentType::FLOOR){ return [ - AxisAlignedBB::one()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16) + AxisAlignedBB::one()->squashedCopy(Facing::axis($this->facing), 1 / 4)->trimmedCopy(Facing::UP, 3 / 16) ]; } if($this->attachmentType === BellAttachmentType::CEILING){ return [ - AxisAlignedBB::one()->contract(1 / 4, 0, 1 / 4)->trim(Facing::DOWN, 1 / 4) + AxisAlignedBB::one()->contractedCopy(1 / 4, 0, 1 / 4)->trimmedCopy(Facing::DOWN, 1 / 4) ]; } $box = AxisAlignedBB::one() - ->squash(Facing::axis(Facing::rotateY($this->facing, true)), 1 / 4) - ->trim(Facing::UP, 1 / 16) - ->trim(Facing::DOWN, 1 / 4); + ->squashedCopy(Facing::axis(Facing::rotateY($this->facing, true)), 1 / 4) + ->trimmedCopy(Facing::UP, 1 / 16) + ->trimmedCopy(Facing::DOWN, 1 / 4); return [ - $this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trim($this->facing, 3 / 16) : $box + $this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trimmedCopy($this->facing, 3 / 16) : $box ]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -83,11 +83,11 @@ final class Bell extends Transparent implements HorizontalFacing{ return $this; } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType($face) !== SupportType::NONE; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ return false; } @@ -123,7 +123,7 @@ final class Bell extends Transparent implements HorizontalFacing{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $faceHit = Facing::opposite($player->getHorizontalFacing()); if($this->isValidFaceToRing($faceHit)){ @@ -142,7 +142,7 @@ final class Bell extends Transparent implements HorizontalFacing{ } } - public function ring(int $faceHit) : void{ + public function ring(Facing $faceHit) : void{ $world = $this->position->getWorld(); $world->addSound($this->position, new BellRingSound()); $tile = $world->getTile($this->position); @@ -155,7 +155,7 @@ final class Bell extends Transparent implements HorizontalFacing{ return [$this->asItem()]; } - private function isValidFaceToRing(int $faceHit) : bool{ + private function isValidFaceToRing(Facing $faceHit) : bool{ return match($this->attachmentType){ BellAttachmentType::CEILING => true, BellAttachmentType::FLOOR => Facing::axis($faceHit) === Facing::axis($this->facing), diff --git a/src/block/BigDripleafHead.php b/src/block/BigDripleafHead.php index a9b87bf7f..1244aff1c 100644 --- a/src/block/BigDripleafHead.php +++ b/src/block/BigDripleafHead.php @@ -80,8 +80,8 @@ class BigDripleafHead extends BaseBigDripleaf{ if(!$entity instanceof Projectile && $this->leafState === DripleafState::STABLE){ //the entity must be standing on top of the leaf - do not collapse if the entity is standing underneath $intersection = AxisAlignedBB::one() - ->offset($this->position->x, $this->position->y, $this->position->z) - ->trim(Facing::DOWN, 1 - $this->getLeafTopOffset()); + ->offsetCopy($this->position->x, $this->position->y, $this->position->z) + ->trimmedCopy(Facing::DOWN, 1 - $this->getLeafTopOffset()); if($entity->getBoundingBox()->intersectsWith($intersection)){ $this->setTiltAndScheduleTick(DripleafState::UNSTABLE); return false; @@ -116,8 +116,8 @@ class BigDripleafHead extends BaseBigDripleaf{ if($this->leafState !== DripleafState::FULL_TILT){ return [ AxisAlignedBB::one() - ->trim(Facing::DOWN, 11 / 16) - ->trim(Facing::UP, $this->getLeafTopOffset()) + ->trimmedCopy(Facing::DOWN, 11 / 16) + ->trimmedCopy(Facing::UP, $this->getLeafTopOffset()) ]; } return []; diff --git a/src/block/Block.php b/src/block/Block.php index 36e08fc0b..7dd4723b0 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -424,7 +424,7 @@ class Block{ * Returns whether this block can replace the given block in the given placement conditions. * This is used to allow slabs of the same type to combine into double slabs. */ - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ return $blockReplace->canBeReplaced(); } @@ -436,13 +436,13 @@ class Block{ * @param Item $item Item used to place the block * @param Block $blockReplace Block expected to be replaced * @param Block $blockClicked Block that was clicked using the item - * @param int $face Face of the clicked block which was clicked + * @param Facing $face Face of the clicked block which was clicked * @param Vector3 $clickVector Exact position inside the clicked block where the click occurred, relative to the block's position * @param Player|null $player Player who placed the block, or null if it was not a player * * @return bool whether the placement should go ahead */ - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $tx->addBlock($blockReplace->position, $this); return true; } @@ -524,7 +524,7 @@ class Block{ * @param Vector3 $clickVector Exact position where the click occurred, relative to the block's integer position * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ return false; } @@ -533,7 +533,7 @@ class Block{ * * @return bool if an action took place, prevents starting to break the block if true. */ - public function onAttack(Item $item, int $face, ?Player $player = null) : bool{ + public function onAttack(Item $item, Facing $face, ?Player $player = null) : bool{ return false; } @@ -770,10 +770,10 @@ class Block{ * * @return Block */ - public function getSide(int $side, int $step = 1){ + public function getSide(Facing $side, int $step = 1){ $position = $this->position; if($position->isValid()){ - [$dx, $dy, $dz] = Facing::OFFSET[$side] ?? [0, 0, 0]; + [$dx, $dy, $dz] = Facing::OFFSET[$side->value] ?? [0, 0, 0]; return $position->getWorld()->getBlockAt( $position->x + ($dx * $step), $position->y + ($dy * $step), @@ -793,7 +793,7 @@ class Block{ public function getHorizontalSides() : \Generator{ $world = $this->position->getWorld(); foreach(Facing::HORIZONTAL as $facing){ - [$dx, $dy, $dz] = Facing::OFFSET[$facing]; + [$dx, $dy, $dz] = Facing::OFFSET[$facing->value]; //TODO: yield Facing as the key? yield $world->getBlockAt( $this->position->x + $dx, @@ -914,11 +914,12 @@ class Block{ */ final public function getCollisionBoxes() : array{ if($this->collisionBoxes === null){ - $this->collisionBoxes = $this->recalculateCollisionBoxes(); + $collisionBoxes = $this->recalculateCollisionBoxes(); $extraOffset = $this->getModelPositionOffset(); $offset = $extraOffset !== null ? $this->position->addVector($extraOffset) : $this->position; - foreach($this->collisionBoxes as $bb){ - $bb->offset($offset->x, $offset->y, $offset->z); + $this->collisionBoxes = []; + foreach($collisionBoxes as $bb){ + $this->collisionBoxes[] = $bb->offsetCopy($offset->x, $offset->y, $offset->z); } } @@ -945,11 +946,11 @@ class Block{ * Returns the type of support that the block can provide on the given face. This is used to determine whether * blocks placed on the given face can be supported by this block. */ - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::FULL; } - protected function getAdjacentSupportType(int $facing) : SupportType{ + protected function getAdjacentSupportType(Facing $facing) : SupportType{ return $this->getSide($facing)->getSupportType(Facing::opposite($facing)); } diff --git a/src/block/BrewingStand.php b/src/block/BrewingStand.php index 039693164..2248f702b 100644 --- a/src/block/BrewingStand.php +++ b/src/block/BrewingStand.php @@ -51,17 +51,17 @@ class BrewingStand extends Transparent{ protected function recalculateCollisionBoxes() : array{ return [ //bottom slab part - in PC this is also inset on X/Z by 1/16, but Bedrock sucks - AxisAlignedBB::one()->trim(Facing::UP, 7 / 8), + AxisAlignedBB::one()->trimmedCopy(Facing::UP, 7 / 8), //center post AxisAlignedBB::one() - ->squash(Axis::X, 7 / 16) - ->squash(Axis::Z, 7 / 16) - ->trim(Facing::UP, 1 / 8) + ->squashedCopy(Axis::X, 7 / 16) + ->squashedCopy(Axis::Z, 7 / 16) + ->trimmedCopy(Facing::UP, 1 / 8) ]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -95,7 +95,7 @@ class BrewingStand extends Transparent{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $stand = $this->position->getWorld()->getTile($this->position); if($stand instanceof TileBrewingStand && $stand->canOpenWith($item->getCustomName())){ diff --git a/src/block/Button.php b/src/block/Button.php index 4d1f1bbc5..04f7716f2 100644 --- a/src/block/Button.php +++ b/src/block/Button.php @@ -40,7 +40,7 @@ abstract class Button extends Flowable implements AnyFacing{ protected bool $pressed = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facing($this->facing); + $w->enum($this->facing); $w->bool($this->pressed); } @@ -52,7 +52,7 @@ abstract class Button extends Flowable implements AnyFacing{ return $this; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($this->canBeSupportedAt($blockReplace, $face)){ $this->facing = $face; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); @@ -62,7 +62,7 @@ abstract class Button extends Flowable implements AnyFacing{ abstract protected function getActivationTime() : int; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->pressed){ $this->pressed = true; $world = $this->position->getWorld(); @@ -89,7 +89,7 @@ abstract class Button extends Flowable implements AnyFacing{ } } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType(Facing::opposite($face))->hasCenterSupport(); } } diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 51c98786b..0e4d7c8ed 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -46,10 +46,10 @@ class Cactus extends Transparent implements Ageable{ protected function recalculateCollisionBoxes() : array{ $shrinkSize = 1 / 16; - return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)]; + return [AxisAlignedBB::one()->contractedCopy($shrinkSize, 0, $shrinkSize)->trimmedCopy(Facing::UP, $shrinkSize)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } diff --git a/src/block/Cake.php b/src/block/Cake.php index e8c6dc93e..3f4f9db92 100644 --- a/src/block/Cake.php +++ b/src/block/Cake.php @@ -43,9 +43,9 @@ class Cake extends BaseCake{ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() - ->contract(1 / 16, 0, 1 / 16) - ->trim(Facing::UP, 0.5) - ->trim(Facing::WEST, $this->bites / 8) + ->contractedCopy(1 / 16, 0, 1 / 16) + ->trimmedCopy(Facing::UP, 0.5) + ->trimmedCopy(Facing::WEST, $this->bites / 8) ]; } @@ -60,7 +60,7 @@ class Cake extends BaseCake{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->bites === 0 && $item instanceof ItemBlock){ $block = $item->getBlock(); $resultBlock = null; diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php index 1462776c2..09a1c1bfa 100644 --- a/src/block/CakeWithCandle.php +++ b/src/block/CakeWithCandle.php @@ -40,8 +40,8 @@ class CakeWithCandle extends BaseCake implements Lightable{ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() - ->contract(1 / 16, 0, 1 / 16) - ->trim(Facing::UP, 0.5) //TODO: not sure if the candle affects height + ->contractedCopy(1 / 16, 0, 1 / 16) + ->trimmedCopy(Facing::UP, 0.5) //TODO: not sure if the candle affects height ]; } @@ -49,7 +49,7 @@ class CakeWithCandle extends BaseCake implements Lightable{ return VanillaBlocks::CANDLE(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->lit && $face !== Facing::UP){ return true; } diff --git a/src/block/Campfire.php b/src/block/Campfire.php index edfc87ecb..5c81af949 100644 --- a/src/block/Campfire.php +++ b/src/block/Campfire.php @@ -127,12 +127,12 @@ class Campfire extends Transparent implements Lightable, HorizontalFacing{ ]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 9 / 16)]; } /** @@ -171,7 +171,7 @@ class Campfire extends Transparent implements Lightable, HorizontalFacing{ return $this->cookingTimes[$slot] ?? 0; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($this->getSide(Facing::DOWN) instanceof Campfire){ return false; } @@ -182,7 +182,7 @@ class Campfire extends Transparent implements Lightable, HorizontalFacing{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->lit){ if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){ $item->pop(); diff --git a/src/block/Candle.php b/src/block/Candle.php index 977f13a09..df129d961 100644 --- a/src/block/Candle.php +++ b/src/block/Candle.php @@ -71,27 +71,27 @@ class Candle extends Transparent implements Lightable{ return [ (match($this->count){ 1 => AxisAlignedBB::one() - ->squash(Axis::X, 7 / 16) - ->squash(Axis::Z, 7 / 16), + ->squashedCopy(Axis::X, 7 / 16) + ->squashedCopy(Axis::Z, 7 / 16), 2 => AxisAlignedBB::one() - ->squash(Axis::X, 5 / 16) - ->trim(Facing::NORTH, 7 / 16) //0.3 thick on the Z axis - ->trim(Facing::SOUTH, 6 / 16), + ->squashedCopy(Axis::X, 5 / 16) + ->trimmedCopy(Facing::NORTH, 7 / 16) //0.3 thick on the Z axis + ->trimmedCopy(Facing::SOUTH, 6 / 16), 3 => AxisAlignedBB::one() - ->trim(Facing::WEST, 5 / 16) - ->trim(Facing::EAST, 6 / 16) - ->trim(Facing::NORTH, 6 / 16) - ->trim(Facing::SOUTH, 5 / 16), + ->trimmedCopy(Facing::WEST, 5 / 16) + ->trimmedCopy(Facing::EAST, 6 / 16) + ->trimmedCopy(Facing::NORTH, 6 / 16) + ->trimmedCopy(Facing::SOUTH, 5 / 16), 4 => AxisAlignedBB::one() - ->squash(Axis::X, 5 / 16) - ->trim(Facing::NORTH, 5 / 16) - ->trim(Facing::SOUTH, 6 / 16), + ->squashedCopy(Axis::X, 5 / 16) + ->trimmedCopy(Facing::NORTH, 5 / 16) + ->trimmedCopy(Facing::SOUTH, 6 / 16), default => throw new AssumptionFailedError("Unreachable") - })->trim(Facing::UP, 10 / 16) + })->trimmedCopy(Facing::UP, 10 / 16) ]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -99,12 +99,12 @@ class Candle extends Transparent implements Lightable{ return $block instanceof Candle && $block->hasSameTypeId($this) ? $block : null; } - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ $candle = $this->getCandleIfCompatibleType($blockReplace); return $candle !== null ? $candle->count < self::MAX_COUNT : parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$blockReplace->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport()){ return false; } diff --git a/src/block/Carpet.php b/src/block/Carpet.php index fd8b42a09..772a93eea 100644 --- a/src/block/Carpet.php +++ b/src/block/Carpet.php @@ -38,7 +38,7 @@ class Carpet extends Flowable implements Colored{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 15 / 16)]; } private function canBeSupportedAt(Block $block) : bool{ diff --git a/src/block/CartographyTable.php b/src/block/CartographyTable.php index 67d950c5a..df9041448 100644 --- a/src/block/CartographyTable.php +++ b/src/block/CartographyTable.php @@ -25,12 +25,13 @@ namespace pocketmine\block; use pocketmine\block\inventory\CartographyTableInventory; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; final class CartographyTable extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new CartographyTableInventory($this->position)); } diff --git a/src/block/Cauldron.php b/src/block/Cauldron.php index 772583a5a..c44da1682 100644 --- a/src/block/Cauldron.php +++ b/src/block/Cauldron.php @@ -51,16 +51,16 @@ final class Cauldron extends Transparent{ protected function recalculateCollisionBoxes() : array{ $result = [ - AxisAlignedBB::one()->trim(Facing::UP, 11 / 16) //bottom of the cauldron + AxisAlignedBB::one()->trimmedCopy(Facing::UP, 11 / 16) //bottom of the cauldron ]; foreach(Facing::HORIZONTAL as $f){ //add the frame parts around the bowl - $result[] = AxisAlignedBB::one()->trim($f, 14 / 16); + $result[] = AxisAlignedBB::one()->trimmedCopy($f, 14 / 16); } return $result; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE; } @@ -75,7 +75,7 @@ final class Cauldron extends Transparent{ $returnedItems[] = $returnedItem; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item->getTypeId() === ItemTypeIds::WATER_BUCKET){ $this->fill(FillableCauldron::MAX_FILL_LEVEL, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::BUCKET(), $returnedItems); }elseif($item->getTypeId() === ItemTypeIds::LAVA_BUCKET){ diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php index 84d7d3169..1e42f2ac0 100644 --- a/src/block/CaveVines.php +++ b/src/block/CaveVines.php @@ -84,12 +84,12 @@ class CaveVines extends Flowable implements Ageable{ return $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || $supportBlock->hasSameTypeId($this); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->age = mt_rand(0, self::MAX_AGE); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->berries){ $this->position->getWorld()->dropItem($this->position, $this->asItem()); $this->position->getWorld()->addSound($this->position, new GlowBerriesPickSound()); @@ -159,7 +159,7 @@ class CaveVines extends Flowable implements Ageable{ return VanillaItems::GLOW_BERRIES(); } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/CeilingCenterHangingSign.php b/src/block/CeilingCenterHangingSign.php index 1125de553..4d9803617 100644 --- a/src/block/CeilingCenterHangingSign.php +++ b/src/block/CeilingCenterHangingSign.php @@ -36,12 +36,12 @@ final class CeilingCenterHangingSign extends BaseSign implements SignLikeRotatio use SignLikeRotationTrait; use StaticSupportTrait; - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::UP; } //TODO: duplicated code :( - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face !== Facing::DOWN){ return false; } diff --git a/src/block/CeilingEdgesHangingSign.php b/src/block/CeilingEdgesHangingSign.php index 3f7b6489b..e8a347033 100644 --- a/src/block/CeilingEdgesHangingSign.php +++ b/src/block/CeilingEdgesHangingSign.php @@ -35,11 +35,11 @@ use pocketmine\world\BlockTransaction; final class CeilingEdgesHangingSign extends BaseSign implements HorizontalFacing{ use HorizontalFacingTrait; - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::UP; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face !== Facing::DOWN){ return false; } diff --git a/src/block/Chain.php b/src/block/Chain.php index 5ec3b1e2a..12b379852 100644 --- a/src/block/Chain.php +++ b/src/block/Chain.php @@ -33,7 +33,7 @@ use pocketmine\math\Facing; final class Chain extends Transparent implements PillarRotation{ use PillarRotationTrait; - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE; } @@ -41,7 +41,7 @@ final class Chain extends Transparent implements PillarRotation{ $bb = AxisAlignedBB::one(); foreach([Axis::Y, Axis::Z, Axis::X] as $axis){ if($axis !== $this->axis){ - $bb->squash($axis, 13 / 32); + $bb = $bb->squashedCopy($axis, 13 / 32); } } return [$bb]; diff --git a/src/block/ChemistryTable.php b/src/block/ChemistryTable.php index 9b5e78f92..c266bd513 100644 --- a/src/block/ChemistryTable.php +++ b/src/block/ChemistryTable.php @@ -26,13 +26,14 @@ namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; final class ChemistryTable extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ //TODO return false; } diff --git a/src/block/Chest.php b/src/block/Chest.php index c0dc09d9e..58a7463f5 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -39,10 +39,10 @@ class Chest extends Transparent implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ //these are slightly bigger than in PC - return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)]; + return [AxisAlignedBB::one()->contractedCopy(0.025, 0, 0.025)->trimmedCopy(Facing::UP, 0.05)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -70,7 +70,7 @@ class Chest extends Transparent implements HorizontalFacing{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $chest = $this->position->getWorld()->getTile($this->position); diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php index cbe22a94b..1d8d83ed0 100644 --- a/src/block/ChiseledBookshelf.php +++ b/src/block/ChiseledBookshelf.php @@ -143,7 +143,7 @@ class ChiseledBookshelf extends Opaque implements HorizontalFacing{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($face !== $this->facing){ return false; } diff --git a/src/block/ChorusFlower.php b/src/block/ChorusFlower.php index ef48d5a89..14cce40ac 100644 --- a/src/block/ChorusFlower.php +++ b/src/block/ChorusFlower.php @@ -106,9 +106,9 @@ final class ChorusFlower extends Flowable implements Ageable{ return [$stemHeight, $endStoneBelow]; } - private function allHorizontalBlocksEmpty(World $world, Vector3 $position, ?int $except) : bool{ + private function allHorizontalBlocksEmpty(World $world, Vector3 $position, ?Facing $except) : bool{ foreach($position->sidesAroundAxis(Axis::Y) as $facing => $sidePosition){ - if($facing === $except){ + if($facing === $except?->value){ continue; } if($world->getBlock($sidePosition)->getTypeId() !== BlockTypeIds::AIR){ @@ -149,7 +149,7 @@ final class ChorusFlower extends Flowable implements Ageable{ return $this->allHorizontalBlocksEmpty($world, $up, null); } - private function grow(int $facing, int $ageChange, ?BlockTransaction $tx) : BlockTransaction{ + private function grow(Facing $facing, int $ageChange, ?BlockTransaction $tx) : BlockTransaction{ if($tx === null){ $tx = new BlockTransaction($this->position->getWorld()); } @@ -176,10 +176,10 @@ final class ChorusFlower extends Flowable implements Ageable{ $facingVisited = []; for($attempts = 0, $maxAttempts = mt_rand(0, $endStoneBelow ? 4 : 3); $attempts < $maxAttempts; $attempts++){ $facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]; - if(isset($facingVisited[$facing])){ + if(isset($facingVisited[$facing->value])){ continue; } - $facingVisited[$facing] = true; + $facingVisited[$facing->value] = true; $sidePosition = $this->position->getSide($facing); if( diff --git a/src/block/ChorusPlant.php b/src/block/ChorusPlant.php index 88cf1787c..96a203a10 100644 --- a/src/block/ChorusPlant.php +++ b/src/block/ChorusPlant.php @@ -43,8 +43,8 @@ final class ChorusPlant extends Flowable{ protected function recalculateCollisionBoxes() : array{ $bb = AxisAlignedBB::one(); foreach(Facing::ALL as $facing){ - if(!isset($this->connections[$facing])){ - $bb->trim($facing, 2 / 16); + if(!isset($this->connections[$facing->value])){ + $bb = $bb->trimmedCopy($facing, 2 / 16); } } @@ -62,9 +62,9 @@ final class ChorusPlant extends Flowable{ BlockTypeIds::END_STONE, BlockTypeIds::CHORUS_FLOWER, $this->getTypeId() => true, default => false }){ - $this->connections[$facing] = true; + $this->connections[$facing->value] = true; }else{ - unset($this->connections[$facing]); + unset($this->connections[$facing->value]); } } diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index ae09ccb0a..eec10db2d 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -55,11 +55,11 @@ class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() - ->squash(Facing::axis(Facing::rotateY($this->facing, true)), (6 - $this->age) / 16) //sides - ->trim(Facing::DOWN, (7 - $this->age * 2) / 16) - ->trim(Facing::UP, 0.25) - ->trim(Facing::opposite($this->facing), 1 / 16) //gap between log and pod - ->trim($this->facing, (11 - $this->age * 2) / 16) //outward face + ->squashedCopy(Facing::axis(Facing::rotateY($this->facing, true)), (6 - $this->age) / 16) //sides + ->trimmedCopy(Facing::DOWN, (7 - $this->age * 2) / 16) + ->trimmedCopy(Facing::UP, 0.25) + ->trimmedCopy(Facing::opposite($this->facing), 1 / 16) //gap between log and pod + ->trimmedCopy($this->facing, (11 - $this->age * 2) / 16) //outward face ]; } @@ -67,7 +67,7 @@ class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(Facing::axis($face) !== Axis::Y && $this->canAttachTo($blockClicked)){ $this->facing = $face; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); @@ -76,7 +76,7 @@ class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); diff --git a/src/block/CopperDoor.php b/src/block/CopperDoor.php index 82a611206..6a690a109 100644 --- a/src/block/CopperDoor.php +++ b/src/block/CopperDoor.php @@ -35,7 +35,7 @@ class CopperDoor extends Door implements CopperMaterial{ onInteract as onInteractCopper; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) { //copy copper properties to other half $other = $this->getSide($this->top ? Facing::DOWN : Facing::UP); diff --git a/src/block/CopperTrapdoor.php b/src/block/CopperTrapdoor.php index e7d56fa0c..16a7d4ec0 100644 --- a/src/block/CopperTrapdoor.php +++ b/src/block/CopperTrapdoor.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperTrait; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -34,7 +35,7 @@ class CopperTrapdoor extends Trapdoor implements CopperMaterial{ onInteract as onInteractCopper; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) { return true; } diff --git a/src/block/CraftingTable.php b/src/block/CraftingTable.php index dcd9edce2..6d43dd18f 100644 --- a/src/block/CraftingTable.php +++ b/src/block/CraftingTable.php @@ -25,12 +25,13 @@ namespace pocketmine\block; use pocketmine\block\inventory\CraftingTableInventory; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; class CraftingTable extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $player->setCurrentWindow(new CraftingTableInventory($this->position)); } diff --git a/src/block/Crops.php b/src/block/Crops.php index b0488d150..85cddcbed 100644 --- a/src/block/Crops.php +++ b/src/block/Crops.php @@ -45,7 +45,7 @@ abstract class Crops extends Flowable implements Ageable{ return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->age < self::MAX_AGE && $item instanceof Fertilizer){ $block = clone $this; $tempAge = $block->age + mt_rand(2, 5); diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php index ed56d2f44..ac3833753 100644 --- a/src/block/DaylightSensor.php +++ b/src/block/DaylightSensor.php @@ -64,14 +64,14 @@ class DaylightSensor extends Transparent implements AnalogRedstoneSignalEmitter{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 10 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 10 / 16)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->inverted = !$this->inverted; $this->signalStrength = $this->recalculateSignalStrength(); $this->position->getWorld()->setBlock($this->position, $this); diff --git a/src/block/Dirt.php b/src/block/Dirt.php index 104080d31..f106edc48 100644 --- a/src/block/Dirt.php +++ b/src/block/Dirt.php @@ -52,7 +52,7 @@ class Dirt extends Opaque{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $world = $this->position->getWorld(); if($face !== Facing::DOWN && $item instanceof Hoe){ $up = $this->getSide(Facing::UP); diff --git a/src/block/Door.php b/src/block/Door.php index 2ff53645c..514d9dda9 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -98,10 +98,10 @@ class Door extends Transparent implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ //TODO: doors are 0.1825 blocks thick, instead of 0.1875 like JE (https://bugs.mojang.com/browse/MCPE-19214) - return [AxisAlignedBB::one()->trim($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)]; + return [AxisAlignedBB::one()->trimmedCopy($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -111,7 +111,7 @@ class Door extends Transparent implements HorizontalFacing{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face === Facing::UP){ $blockUp = $this->getSide(Facing::UP); if(!$blockUp->canBeReplaced() || !$this->canBeSupportedAt($blockReplace)){ @@ -139,7 +139,7 @@ class Door extends Transparent implements HorizontalFacing{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->open = !$this->open; $other = $this->getSide($this->top ? Facing::DOWN : Facing::UP); diff --git a/src/block/DoublePitcherCrop.php b/src/block/DoublePitcherCrop.php index 89d9e98b5..a4031d2d2 100644 --- a/src/block/DoublePitcherCrop.php +++ b/src/block/DoublePitcherCrop.php @@ -58,10 +58,10 @@ final class DoublePitcherCrop extends DoublePlant implements Ageable{ //the pod exists only in the bottom half of the plant return [ AxisAlignedBB::one() - ->trim(Facing::UP, 11 / 16) - ->squash(Axis::X, 3 / 16) - ->squash(Axis::Z, 3 / 16) - ->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall + ->trimmedCopy(Facing::UP, 11 / 16) + ->squashedCopy(Axis::X, 3 / 16) + ->squashedCopy(Axis::Z, 3 / 16) + ->extendedCopy(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall ]; } @@ -89,7 +89,7 @@ final class DoublePitcherCrop extends DoublePlant implements Ageable{ } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); return true; diff --git a/src/block/DoublePlant.php b/src/block/DoublePlant.php index aab6d5b04..d1f878561 100644 --- a/src/block/DoublePlant.php +++ b/src/block/DoublePlant.php @@ -45,7 +45,7 @@ class DoublePlant extends Flowable{ return $this; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $down = $blockReplace->getSide(Facing::DOWN); if($down->hasTypeTag(BlockTypeTags::DIRT) && $blockReplace->getSide(Facing::UP)->canBeReplaced()){ $top = clone $this; diff --git a/src/block/DragonEgg.php b/src/block/DragonEgg.php index 10fec6394..89ec4237f 100644 --- a/src/block/DragonEgg.php +++ b/src/block/DragonEgg.php @@ -28,6 +28,7 @@ use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\SupportType; use pocketmine\event\block\BlockTeleportEvent; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\GameMode; use pocketmine\player\Player; @@ -44,12 +45,12 @@ class DragonEgg extends Transparent implements Fallable{ return 1; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->teleport(); return true; } - public function onAttack(Item $item, int $face, ?Player $player = null) : bool{ + public function onAttack(Item $item, Facing $face, ?Player $player = null) : bool{ if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){ $this->teleport(); return true; @@ -81,7 +82,7 @@ class DragonEgg extends Transparent implements Fallable{ } } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/EnchantingTable.php b/src/block/EnchantingTable.php index 53573d064..346dde7cd 100644 --- a/src/block/EnchantingTable.php +++ b/src/block/EnchantingTable.php @@ -34,14 +34,14 @@ use pocketmine\player\Player; class EnchantingTable extends Transparent{ protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 0.25)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 0.25)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ //TODO lock diff --git a/src/block/EndPortalFrame.php b/src/block/EndPortalFrame.php index 6c4865fdd..7169e38f2 100644 --- a/src/block/EndPortalFrame.php +++ b/src/block/EndPortalFrame.php @@ -52,6 +52,6 @@ class EndPortalFrame extends Opaque implements HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 3 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 3 / 16)]; } } diff --git a/src/block/EndRod.php b/src/block/EndRod.php index e3d439f6a..b1cd64749 100644 --- a/src/block/EndRod.php +++ b/src/block/EndRod.php @@ -36,7 +36,7 @@ use pocketmine\world\BlockTransaction; class EndRod extends Flowable implements AnyFacing{ use AnyFacingTrait; - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->facing = $face; if($blockClicked instanceof EndRod && $blockClicked->facing === $this->facing){ $this->facing = Facing::opposite($face); @@ -61,7 +61,7 @@ class EndRod extends Flowable implements AnyFacing{ if($axis === $myAxis){ continue; } - $bb->squash($axis, 6 / 16); + $bb->squashedCopy($axis, 6 / 16); } return [$bb]; } diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php index 5dec4f3af..6aa65bdff 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -43,14 +43,14 @@ class EnderChest extends Transparent implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ //these are slightly bigger than in PC - return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)]; + return [AxisAlignedBB::one()->contractedCopy(0.025, 0, 0.025)->trimmedCopy(Facing::UP, 0.05)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $enderChest = $this->position->getWorld()->getTile($this->position); if($enderChest instanceof TileEnderChest && $this->getSide(Facing::UP)->isTransparent()){ diff --git a/src/block/Farmland.php b/src/block/Farmland.php index 83bc34561..d0f92a528 100644 --- a/src/block/Farmland.php +++ b/src/block/Farmland.php @@ -95,7 +95,7 @@ class Farmland extends Transparent{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 1 / 16)]; } public function onNearbyBlockChange() : void{ diff --git a/src/block/Fence.php b/src/block/Fence.php index 52256d9f0..1eb8ef900 100644 --- a/src/block/Fence.php +++ b/src/block/Fence.php @@ -45,9 +45,9 @@ class Fence extends Transparent{ foreach(Facing::HORIZONTAL as $facing){ $block = $this->getSide($facing); if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing)) === SupportType::FULL){ - $this->connections[$facing] = true; + $this->connections[$facing->value] = true; }else{ - unset($this->connections[$facing]); + unset($this->connections[$facing->value]); } } @@ -59,43 +59,43 @@ class Fence extends Transparent{ $bbs = []; - $connectWest = isset($this->connections[Facing::WEST]); - $connectEast = isset($this->connections[Facing::EAST]); + $connectWest = isset($this->connections[Facing::WEST->value]); + $connectEast = isset($this->connections[Facing::EAST->value]); if($connectWest || $connectEast){ //X axis (west/east) $bbs[] = AxisAlignedBB::one() - ->squash(Axis::Z, $inset) - ->extend(Facing::UP, 0.5) - ->trim(Facing::WEST, $connectWest ? 0 : $inset) - ->trim(Facing::EAST, $connectEast ? 0 : $inset); + ->squashedCopy(Axis::Z, $inset) + ->extendedCopy(Facing::UP, 0.5) + ->trimmedCopy(Facing::WEST, $connectWest ? 0 : $inset) + ->trimmedCopy(Facing::EAST, $connectEast ? 0 : $inset); } - $connectNorth = isset($this->connections[Facing::NORTH]); - $connectSouth = isset($this->connections[Facing::SOUTH]); + $connectNorth = isset($this->connections[Facing::NORTH->value]); + $connectSouth = isset($this->connections[Facing::SOUTH->value]); if($connectNorth || $connectSouth){ //Z axis (north/south) $bbs[] = AxisAlignedBB::one() - ->squash(Axis::X, $inset) - ->extend(Facing::UP, 0.5) - ->trim(Facing::NORTH, $connectNorth ? 0 : $inset) - ->trim(Facing::SOUTH, $connectSouth ? 0 : $inset); + ->squashedCopy(Axis::X, $inset) + ->extendedCopy(Facing::UP, 0.5) + ->trimmedCopy(Facing::NORTH, $connectNorth ? 0 : $inset) + ->trimmedCopy(Facing::SOUTH, $connectSouth ? 0 : $inset); } if(count($bbs) === 0){ //centre post AABB (only needed if not connected on any axis - other BBs overlapping will do this if any connections are made) return [ AxisAlignedBB::one() - ->extend(Facing::UP, 0.5) - ->contract($inset, 0, $inset) + ->extendedCopy(Facing::UP, 0.5) + ->contractedCopy($inset, 0, $inset) ]; } return $bbs; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE; } } diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php index d8cdfe4e2..1e6ed6dc8 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -67,10 +67,10 @@ class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ } protected function recalculateCollisionBoxes() : array{ - return $this->open ? [] : [AxisAlignedBB::one()->extend(Facing::UP, 0.5)->squash(Facing::axis($this->facing), 6 / 16)]; + return $this->open ? [] : [AxisAlignedBB::one()->extendedCopy(Facing::UP, 0.5)->squashedCopy(Facing::axis($this->facing), 6 / 16)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -81,7 +81,7 @@ class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ ); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->facing = $player->getHorizontalFacing(); } @@ -99,7 +99,7 @@ class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->open = !$this->open; if($this->open && $player !== null){ $playerFacing = $player->getHorizontalFacing(); diff --git a/src/block/FillableCauldron.php b/src/block/FillableCauldron.php index ceef35299..f6ff3e458 100644 --- a/src/block/FillableCauldron.php +++ b/src/block/FillableCauldron.php @@ -54,16 +54,16 @@ abstract class FillableCauldron extends Transparent{ protected function recalculateCollisionBoxes() : array{ $result = [ - AxisAlignedBB::one()->trim(Facing::UP, 11 / 16) //bottom of the cauldron + AxisAlignedBB::one()->trimmedCopy(Facing::UP, 11 / 16) //bottom of the cauldron ]; foreach(Facing::HORIZONTAL as $f){ //add the frame parts around the bowl - $result[] = AxisAlignedBB::one()->trim($f, 14 / 16); + $result[] = AxisAlignedBB::one()->trimmedCopy($f, 14 / 16); } return $result; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE; } diff --git a/src/block/FloorBanner.php b/src/block/FloorBanner.php index ff57b5973..5df7bfb61 100644 --- a/src/block/FloorBanner.php +++ b/src/block/FloorBanner.php @@ -38,11 +38,11 @@ final class FloorBanner extends BaseBanner implements SignLikeRotation{ return VanillaBlocks::OMINOUS_BANNER()->setRotation($this->rotation); } - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::DOWN; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face !== Facing::UP){ return false; } diff --git a/src/block/FloorCoralFan.php b/src/block/FloorCoralFan.php index 5b74d08af..3adfe0420 100644 --- a/src/block/FloorCoralFan.php +++ b/src/block/FloorCoralFan.php @@ -38,16 +38,16 @@ use function rad2deg; final class FloorCoralFan extends BaseCoral{ use StaticSupportTrait; - private int $axis = Axis::X; + private Axis $axis = Axis::X; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->horizontalAxis($this->axis); } - public function getAxis() : int{ return $this->axis; } + public function getAxis() : Axis{ return $this->axis; } /** @return $this */ - public function setAxis(int $axis) : self{ + public function setAxis(Axis $axis) : self{ if($axis !== Axis::X && $axis !== Axis::Z){ throw new \InvalidArgumentException("Axis must be X or Z only"); } @@ -55,7 +55,7 @@ final class FloorCoralFan extends BaseCoral{ return $this; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $playerBlockPos = $player->getPosition()->floor(); $directionVector = $blockReplace->position->subtractVector($playerBlockPos)->normalize(); diff --git a/src/block/FloorSign.php b/src/block/FloorSign.php index 94e51ffe8..3f2bd6d22 100644 --- a/src/block/FloorSign.php +++ b/src/block/FloorSign.php @@ -34,11 +34,11 @@ use pocketmine\world\BlockTransaction; final class FloorSign extends BaseSign implements SignLikeRotation{ use SignLikeRotationTrait; - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::DOWN; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face !== Facing::UP){ return false; } diff --git a/src/block/Flowable.php b/src/block/Flowable.php index 355c9caea..db1b5745b 100644 --- a/src/block/Flowable.php +++ b/src/block/Flowable.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\SupportType; +use pocketmine\math\Facing; use pocketmine\math\Vector3; /** @@ -40,7 +41,7 @@ abstract class Flowable extends Transparent{ return false; } - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ return (!$this->canBeFlowedInto() || !$blockReplace instanceof Liquid) && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } @@ -49,7 +50,7 @@ abstract class Flowable extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/FlowerPot.php b/src/block/FlowerPot.php index 79fb73b12..88d23380b 100644 --- a/src/block/FlowerPot.php +++ b/src/block/FlowerPot.php @@ -84,14 +84,14 @@ class FlowerPot extends Flowable{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)]; + return [AxisAlignedBB::one()->contractedCopy(3 / 16, 0, 3 / 16)->trimmedCopy(Facing::UP, 5 / 8)]; } private function canBeSupportedAt(Block $block) : bool{ return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $world = $this->position->getWorld(); $plant = $item->getBlock(); if($this->plant !== null){ diff --git a/src/block/Furnace.php b/src/block/Furnace.php index 43ab463a6..808231b18 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -31,6 +31,7 @@ use pocketmine\block\utils\LightableTrait; use pocketmine\crafting\FurnaceType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; @@ -59,7 +60,7 @@ class Furnace extends Opaque implements Lightable, HorizontalFacing{ return $this->lit ? 13 : 0; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $furnace = $this->position->getWorld()->getTile($this->position); if($furnace instanceof TileFurnace && $furnace->canOpenWith($item->getCustomName())){ diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index 4588d647d..0815303a8 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -51,7 +51,7 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -60,13 +60,13 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ } /** - * @return int[] + * @return Facing[] */ protected function getInitialPlaceFaces(Block $blockReplace) : array{ return $blockReplace instanceof GlowLichen ? $blockReplace->faces : []; } - private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{ + private function getSpreadBlock(Block $replace, Facing $spreadFace) : ?Block{ if($replace instanceof self && $replace->hasSameTypeId($this)){ if($replace->hasFace($spreadFace)){ return null; @@ -81,7 +81,7 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ return $result->setFace($spreadFace, true); } - private function spread(World $world, Vector3 $replacePos, int $spreadFace) : bool{ + private function spread(World $world, Vector3 $replacePos, Facing $spreadFace) : bool{ $supportBlock = $world->getBlock($replacePos->getSide($spreadFace)); $supportFace = Facing::opposite($spreadFace); @@ -99,9 +99,9 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ } /** - * @phpstan-return \Generator + * @phpstan-return \Generator */ - private static function getShuffledSpreadFaces(int $sourceFace) : \Generator{ + private static function getShuffledSpreadFaces(Facing $sourceFace) : \Generator{ $skipAxis = Facing::axis($sourceFace); $faces = Facing::ALL; @@ -113,7 +113,7 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ } } - private function spreadAroundSupport(int $sourceFace) : bool{ + private function spreadAroundSupport(Facing $sourceFace) : bool{ $world = $this->position->getWorld(); $supportPos = $this->position->getSide($sourceFace); @@ -127,7 +127,7 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ return false; } - private function spreadAdjacentToSupport(int $sourceFace) : bool{ + private function spreadAdjacentToSupport(Facing $sourceFace) : bool{ $world = $this->position->getWorld(); foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){ @@ -139,7 +139,7 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ return false; } - private function spreadWithinSelf(int $sourceFace) : bool{ + private function spreadWithinSelf(Facing $sourceFace) : bool{ foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){ if(!$this->hasFace($spreadFace) && $this->spread($this->position->getWorld(), $this->position, $spreadFace)){ return true; @@ -149,7 +149,7 @@ class GlowLichen extends Transparent implements MultiAnyFacing{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && count($this->faces) > 0){ $shuffledFaces = $this->faces; shuffle($shuffledFaces); diff --git a/src/block/Grass.php b/src/block/Grass.php index 8a9fea8ea..d8fe435f2 100644 --- a/src/block/Grass.php +++ b/src/block/Grass.php @@ -81,7 +81,7 @@ class Grass extends Opaque{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->getSide(Facing::UP)->getTypeId() !== BlockTypeIds::AIR){ return false; } diff --git a/src/block/GrassPath.php b/src/block/GrassPath.php index ea56e4b95..7a5985e42 100644 --- a/src/block/GrassPath.php +++ b/src/block/GrassPath.php @@ -30,7 +30,7 @@ use pocketmine\math\Facing; class GrassPath extends Transparent{ protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 1 / 16)]; } public function onNearbyBlockChange() : void{ diff --git a/src/block/Hopper.php b/src/block/Hopper.php index 4956b668f..8c727a1d2 100644 --- a/src/block/Hopper.php +++ b/src/block/Hopper.php @@ -38,17 +38,17 @@ use pocketmine\world\BlockTransaction; class Hopper extends Transparent implements PoweredByRedstone{ use PoweredByRedstoneTrait; - private int $facing = Facing::DOWN; + private Facing $facing = Facing::DOWN; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->facingExcept($this->facing, Facing::UP); $w->bool($this->powered); } - public function getFacing() : int{ return $this->facing; } + public function getFacing() : Facing{ return $this->facing; } /** @return $this */ - public function setFacing(int $facing) : self{ + public function setFacing(Facing $facing) : self{ if($facing === Facing::UP){ throw new \InvalidArgumentException("Hopper may not face upward"); } @@ -58,16 +58,16 @@ class Hopper extends Transparent implements PoweredByRedstone{ protected function recalculateCollisionBoxes() : array{ $result = [ - AxisAlignedBB::one()->trim(Facing::UP, 6 / 16) //the empty area around the bottom is currently considered solid + AxisAlignedBB::one()->trimmedCopy(Facing::UP, 6 / 16) //the empty area around the bottom is currently considered solid ]; foreach(Facing::HORIZONTAL as $f){ //add the frame parts around the bowl - $result[] = AxisAlignedBB::one()->trim($f, 14 / 16); + $result[] = AxisAlignedBB::one()->trimmedCopy($f, 14 / 16); } return $result; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return match($facing){ Facing::UP => SupportType::FULL, Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER : SupportType::NONE, @@ -75,13 +75,13 @@ class Hopper extends Transparent implements PoweredByRedstone{ }; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->facing = $face === Facing::DOWN ? Facing::DOWN : Facing::opposite($face); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $tile = $this->position->getWorld()->getTile($this->position); if($tile instanceof TileHopper){ //TODO: find a way to have inventories open on click without this boilerplate in every block diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index 0fda77758..ee0c8c452 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -52,7 +52,7 @@ class ItemFrame extends Flowable implements AnyFacing{ protected float $itemDropChance = 1.0; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facing($this->facing); + $w->enum($this->facing); $w->bool($this->hasMap); } @@ -132,7 +132,7 @@ class ItemFrame extends Flowable implements AnyFacing{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->framedItem !== null){ $this->itemRotation = ($this->itemRotation + 1) % self::ROTATIONS; @@ -150,7 +150,7 @@ class ItemFrame extends Flowable implements AnyFacing{ return true; } - public function onAttack(Item $item, int $face, ?Player $player = null) : bool{ + public function onAttack(Item $item, Facing $face, ?Player $player = null) : bool{ if($this->framedItem === null){ return false; } @@ -164,7 +164,7 @@ class ItemFrame extends Flowable implements AnyFacing{ return true; } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType($face) !== SupportType::NONE; } @@ -174,7 +174,7 @@ class ItemFrame extends Flowable implements AnyFacing{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ return false; } diff --git a/src/block/Jukebox.php b/src/block/Jukebox.php index a61dd06db..d2f39c7d7 100644 --- a/src/block/Jukebox.php +++ b/src/block/Jukebox.php @@ -27,6 +27,7 @@ use pocketmine\block\tile\Jukebox as JukeboxTile; use pocketmine\item\Item; use pocketmine\item\Record; use pocketmine\lang\KnownTranslationFactory; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\RecordSound; @@ -40,7 +41,7 @@ class Jukebox extends Opaque{ return 300; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ if($this->record !== null){ $this->ejectRecord(); diff --git a/src/block/Ladder.php b/src/block/Ladder.php index 6edaebbf2..fe7a4b65f 100644 --- a/src/block/Ladder.php +++ b/src/block/Ladder.php @@ -60,14 +60,14 @@ class Ladder extends Transparent implements HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim($this->facing, 13 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy($this->facing, 13 / 16)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($this->canBeSupportedAt($blockReplace, Facing::opposite($face)) && Facing::axis($face) !== Axis::Y){ $this->facing = $face; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); @@ -82,7 +82,7 @@ class Ladder extends Transparent implements HorizontalFacing{ } } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType($face) === SupportType::FULL; } } diff --git a/src/block/Lantern.php b/src/block/Lantern.php index 302e69fd7..02e310768 100644 --- a/src/block/Lantern.php +++ b/src/block/Lantern.php @@ -62,18 +62,18 @@ class Lantern extends Transparent{ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() - ->trim(Facing::UP, $this->hanging ? 6 / 16 : 8 / 16) - ->trim(Facing::DOWN, $this->hanging ? 2 / 16 : 0) - ->squash(Axis::X, 5 / 16) - ->squash(Axis::Z, 5 / 16) + ->trimmedCopy(Facing::UP, $this->hanging ? 6 / 16 : 8 / 16) + ->trimmedCopy(Facing::DOWN, $this->hanging ? 2 / 16 : 0) + ->squashedCopy(Axis::X, 5 / 16) + ->squashedCopy(Axis::Z, 5 / 16) ]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $downSupport = $this->canBeSupportedAt($blockReplace, Facing::DOWN); if(!$downSupport && !$this->canBeSupportedAt($blockReplace, Facing::UP)){ return false; @@ -90,7 +90,7 @@ class Lantern extends Transparent{ } } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType($face)->hasCenterSupport(); } } diff --git a/src/block/LavaCauldron.php b/src/block/LavaCauldron.php index 3df903e22..f89addd78 100644 --- a/src/block/LavaCauldron.php +++ b/src/block/LavaCauldron.php @@ -31,6 +31,7 @@ use pocketmine\event\entity\EntityDamageEvent; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; use pocketmine\item\VanillaItems; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\CauldronEmptyLavaSound; @@ -61,7 +62,7 @@ final class LavaCauldron extends FillableCauldron{ return new CauldronEmptyLavaSound(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ match($item->getTypeId()){ ItemTypeIds::BUCKET => $this->removeFillLevels(self::MAX_FILL_LEVEL, $item, VanillaItems::LAVA_BUCKET(), $returnedItems), ItemTypeIds::POWDER_SNOW_BUCKET, ItemTypeIds::WATER_BUCKET => $this->mix($item, VanillaItems::BUCKET(), $returnedItems), diff --git a/src/block/Leaves.php b/src/block/Leaves.php index 847536557..8ed99b411 100644 --- a/src/block/Leaves.php +++ b/src/block/Leaves.php @@ -134,7 +134,7 @@ class Leaves extends Transparent{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->noDecay = true; //artificial leaves don't decay return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -188,7 +188,7 @@ class Leaves extends Transparent{ return 60; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/Lectern.php b/src/block/Lectern.php index 9ba01c1c5..bfdbcb3ec 100644 --- a/src/block/Lectern.php +++ b/src/block/Lectern.php @@ -84,10 +84,10 @@ class Lectern extends Transparent implements HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 0.1)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 0.1)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -120,7 +120,7 @@ class Lectern extends Transparent implements HorizontalFacing{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->book === null && $item instanceof WritableBookBase){ $world = $this->position->getWorld(); $world->setBlock($this->position, $this->setBook($item)); @@ -130,7 +130,7 @@ class Lectern extends Transparent implements HorizontalFacing{ return true; } - public function onAttack(Item $item, int $face, ?Player $player = null) : bool{ + public function onAttack(Item $item, Facing $face, ?Player $player = null) : bool{ if($this->book !== null){ $world = $this->position->getWorld(); $world->dropItem($this->position->up(), $this->book); diff --git a/src/block/Lever.php b/src/block/Lever.php index d2b98efc3..24bbfc502 100644 --- a/src/block/Lever.php +++ b/src/block/Lever.php @@ -30,7 +30,6 @@ use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use pocketmine\utils\AssumptionFailedError; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\RedstonePowerOffSound; use pocketmine\world\sound\RedstonePowerOnSound; @@ -60,7 +59,7 @@ class Lever extends Flowable{ return $this; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ return false; } @@ -78,7 +77,6 @@ class Lever extends Flowable{ Facing::SOUTH => LeverFacing::SOUTH, Facing::WEST => LeverFacing::WEST, Facing::EAST => LeverFacing::EAST, - default => throw new AssumptionFailedError("Bad facing value"), }; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); @@ -90,7 +88,7 @@ class Lever extends Flowable{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->activated = !$this->activated; $world = $this->position->getWorld(); $world->setBlock($this->position, $this); @@ -101,7 +99,7 @@ class Lever extends Flowable{ return true; } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType($face)->hasCenterSupport(); } diff --git a/src/block/Light.php b/src/block/Light.php index 29a3a8dfc..27bfbeab4 100644 --- a/src/block/Light.php +++ b/src/block/Light.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -51,12 +52,12 @@ final class Light extends Flowable{ public function canBeReplaced() : bool{ return true; } - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ //light blocks behave like solid blocks when placing them on another light block return $blockReplace->canBeReplaced() && $blockReplace->getTypeId() !== $this->getTypeId(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->level = $this->level === self::MAX_LIGHT_LEVEL ? self::MIN_LIGHT_LEVEL : $this->level + 1; diff --git a/src/block/LightningRod.php b/src/block/LightningRod.php index 9c1971229..f26f8212d 100644 --- a/src/block/LightningRod.php +++ b/src/block/LightningRod.php @@ -42,14 +42,14 @@ final class LightningRod extends Transparent implements AnyFacing{ $result = AxisAlignedBB::one(); foreach([Axis::X, Axis::Y, Axis::Z] as $axis){ if($axis !== $myAxis){ - $result->squash($axis, 6 / 16); + $result = $result->squashedCopy($axis, 6 / 16); } } return [$result]; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->facing = $face; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } diff --git a/src/block/Liquid.php b/src/block/Liquid.php index 813d76904..d47e21035 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -92,7 +92,7 @@ abstract class Liquid extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -170,7 +170,7 @@ abstract class Liquid extends Transparent{ $world = $this->position->getWorld(); foreach(Facing::HORIZONTAL as $j){ - [$dx, $dy, $dz] = Facing::OFFSET[$j]; + [$dx, $dy, $dz] = Facing::OFFSET[$j->value]; $sideX = $x + $dx; $sideY = $y + $dy; @@ -206,7 +206,7 @@ abstract class Liquid extends Transparent{ if($this->falling){ foreach(Facing::HORIZONTAL as $facing){ - [$dx, $dy, $dz] = Facing::OFFSET[$facing]; + [$dx, $dy, $dz] = Facing::OFFSET[$facing->value]; if( !$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy, $z + $dz)) || !$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy + 1, $z + $dz)) diff --git a/src/block/Loom.php b/src/block/Loom.php index a2b9fc235..693dd118e 100644 --- a/src/block/Loom.php +++ b/src/block/Loom.php @@ -27,13 +27,14 @@ use pocketmine\block\inventory\LoomInventory; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; final class Loom extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new LoomInventory($this->position)); return true; diff --git a/src/block/MobHead.php b/src/block/MobHead.php index 41e816c55..a943a9a98 100644 --- a/src/block/MobHead.php +++ b/src/block/MobHead.php @@ -41,7 +41,7 @@ class MobHead extends Flowable{ protected MobHeadType $mobHeadType = MobHeadType::SKELETON; - protected int $facing = Facing::NORTH; + protected Facing $facing = Facing::NORTH; protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling public function describeBlockItemState(RuntimeDataDescriber $w) : void{ @@ -82,10 +82,10 @@ class MobHead extends Flowable{ return $this; } - public function getFacing() : int{ return $this->facing; } + public function getFacing() : Facing{ return $this->facing; } /** @return $this */ - public function setFacing(int $facing) : self{ + public function setFacing(Facing $facing) : self{ if($facing === Facing::DOWN){ throw new \InvalidArgumentException("Skull may not face DOWN"); } @@ -106,17 +106,17 @@ class MobHead extends Flowable{ protected function recalculateCollisionBoxes() : array{ $collisionBox = AxisAlignedBB::one() - ->contract(0.25, 0, 0.25) - ->trim(Facing::UP, 0.5); + ->contractedCopy(0.25, 0, 0.25) + ->trimmedCopy(Facing::UP, 0.5); if($this->facing !== Facing::UP){ $collisionBox = $collisionBox - ->offsetTowards(Facing::opposite($this->facing), 0.25) - ->offsetTowards(Facing::UP, 0.25); + ->offsetTowardsCopy(Facing::opposite($this->facing), 0.25) + ->offsetTowardsCopy(Facing::UP, 0.25); } return [$collisionBox]; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face === Facing::DOWN){ return false; } diff --git a/src/block/MonsterSpawner.php b/src/block/MonsterSpawner.php index 5cbe80e0a..c11327507 100644 --- a/src/block/MonsterSpawner.php +++ b/src/block/MonsterSpawner.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; +use pocketmine\math\Facing; use function mt_rand; class MonsterSpawner extends Transparent{ @@ -41,7 +42,7 @@ class MonsterSpawner extends Transparent{ //TODO } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/NetherPortal.php b/src/block/NetherPortal.php index 1b199c603..2f00b44f2 100644 --- a/src/block/NetherPortal.php +++ b/src/block/NetherPortal.php @@ -28,16 +28,17 @@ use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\item\Item; use pocketmine\math\Axis; +use pocketmine\math\Facing; class NetherPortal extends Transparent{ - protected int $axis = Axis::X; + protected Axis $axis = Axis::X; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->horizontalAxis($this->axis); } - public function getAxis() : int{ + public function getAxis() : Axis{ return $this->axis; } @@ -45,7 +46,7 @@ class NetherPortal extends Transparent{ * @throws \InvalidArgumentException * @return $this */ - public function setAxis(int $axis) : self{ + public function setAxis(Axis $axis) : self{ if($axis !== Axis::X && $axis !== Axis::Z){ throw new \InvalidArgumentException("Invalid axis"); } @@ -65,7 +66,7 @@ class NetherPortal extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } diff --git a/src/block/NetherVines.php b/src/block/NetherVines.php index 67a0b6f94..98464ed58 100644 --- a/src/block/NetherVines.php +++ b/src/block/NetherVines.php @@ -49,9 +49,9 @@ class NetherVines extends Flowable implements Ageable{ public const MAX_AGE = 25; /** Direction the vine grows towards. */ - private int $growthFace; + private Facing $growthFace; - public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $growthFace){ + public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, Facing $growthFace){ $this->growthFace = $growthFace; parent::__construct($idInfo, $name, $typeInfo); } @@ -80,12 +80,12 @@ class NetherVines extends Flowable implements Ageable{ return $top; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->age = mt_rand(0, self::MAX_AGE - 1); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer){ if($this->grow($player, mt_rand(1, 5))){ $item->pop(); @@ -159,7 +159,7 @@ class NetherVines extends Flowable implements Ageable{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/OminousFloorBanner.php b/src/block/OminousFloorBanner.php index 240aeccfc..f4a03664f 100644 --- a/src/block/OminousFloorBanner.php +++ b/src/block/OminousFloorBanner.php @@ -36,11 +36,11 @@ final class OminousFloorBanner extends BaseOminousBanner implements SignLikeRota //TODO: duplicated code :( - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::DOWN; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face !== Facing::UP){ return false; } diff --git a/src/block/OminousWallBanner.php b/src/block/OminousWallBanner.php index 1eb5ba753..cc89b4192 100644 --- a/src/block/OminousWallBanner.php +++ b/src/block/OminousWallBanner.php @@ -35,11 +35,11 @@ use pocketmine\world\BlockTransaction; final class OminousWallBanner extends BaseOminousBanner implements HorizontalFacing{ use HorizontalFacingTrait; - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::opposite($this->facing); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(Facing::axis($face) === Axis::Y){ return false; } diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php index b8b6e248c..83ba482fd 100644 --- a/src/block/PinkPetals.php +++ b/src/block/PinkPetals.php @@ -70,11 +70,11 @@ class PinkPetals extends Flowable implements HorizontalFacing{ return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD); } - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ return ($blockReplace instanceof PinkPetals && $blockReplace->count < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($blockReplace instanceof PinkPetals && $blockReplace->count < self::MAX_COUNT){ $this->count = $blockReplace->count + 1; $this->facing = $blockReplace->facing; @@ -84,7 +84,7 @@ class PinkPetals extends Flowable implements HorizontalFacing{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer){ $grew = false; if($this->count < self::MAX_COUNT){ diff --git a/src/block/PitcherCrop.php b/src/block/PitcherCrop.php index 1c771bb8a..7223dc66d 100644 --- a/src/block/PitcherCrop.php +++ b/src/block/PitcherCrop.php @@ -54,10 +54,10 @@ final class PitcherCrop extends Flowable implements Ageable{ $heightTrim = $this->age === 0 ? 13 : 11; return [ AxisAlignedBB::one() - ->trim(Facing::UP, $heightTrim / 16) - ->squash(Axis::X, $widthTrim / 16) - ->squash(Axis::Z, $widthTrim / 16) - ->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall + ->trimmedCopy(Facing::UP, $heightTrim / 16) + ->squashedCopy(Axis::X, $widthTrim / 16) + ->squashedCopy(Axis::Z, $widthTrim / 16) + ->extendedCopy(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall ]; } @@ -85,7 +85,7 @@ final class PitcherCrop extends Flowable implements Ageable{ return BlockEventHelper::grow($this, (clone $this)->setAge($this->age + 1), $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); return true; diff --git a/src/block/PotionCauldron.php b/src/block/PotionCauldron.php index ca91664e4..92a5507ee 100644 --- a/src/block/PotionCauldron.php +++ b/src/block/PotionCauldron.php @@ -27,6 +27,7 @@ use pocketmine\block\tile\Cauldron as TileCauldron; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; use pocketmine\item\VanillaItems; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\CauldronEmptyPotionSound; @@ -94,7 +95,7 @@ final class PotionCauldron extends FillableCauldron{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ match($item->getTypeId()){ ItemTypeIds::LINGERING_POTION, ItemTypeIds::POTION, ItemTypeIds::SPLASH_POTION => $this->addFillLevelsOrMix(self::POTION_FILL_AMOUNT, $item, VanillaItems::GLASS_BOTTLE(), $returnedItems), ItemTypeIds::GLASS_BOTTLE => $this->potionItem === null ? null : $this->removeFillLevels(self::POTION_FILL_AMOUNT, $item, clone $this->potionItem, $returnedItems), diff --git a/src/block/PressurePlate.php b/src/block/PressurePlate.php index 1dd4b50d9..b96154a15 100644 --- a/src/block/PressurePlate.php +++ b/src/block/PressurePlate.php @@ -57,7 +57,7 @@ abstract class PressurePlate extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -83,10 +83,10 @@ abstract class PressurePlate extends Transparent{ */ protected function getActivationBox() : AxisAlignedBB{ return AxisAlignedBB::one() - ->squash(Axis::X, 1 / 8) - ->squash(Axis::Z, 1 / 8) - ->trim(Facing::UP, 3 / 4) - ->offset($this->position->x, $this->position->y, $this->position->z); + ->squashedCopy(Axis::X, 1 / 8) + ->squashedCopy(Axis::Z, 1 / 8) + ->trimmedCopy(Facing::UP, 3 / 4) + ->offsetCopy($this->position->x, $this->position->y, $this->position->z); } /** diff --git a/src/block/Pumpkin.php b/src/block/Pumpkin.php index 1b7f6a9cd..f268be656 100644 --- a/src/block/Pumpkin.php +++ b/src/block/Pumpkin.php @@ -33,7 +33,7 @@ use function in_array; class Pumpkin extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Shears && in_array($face, Facing::HORIZONTAL, true)){ $item->applyDamage(1); $world = $this->position->getWorld(); diff --git a/src/block/Rail.php b/src/block/Rail.php index f516902f0..faed53c9b 100644 --- a/src/block/Rail.php +++ b/src/block/Rail.php @@ -60,8 +60,8 @@ class Rail extends BaseRail{ Facing::WEST, Facing::EAST ] as $d){ - if($constraint !== $d){ - $possible[$d] = true; + if($constraint !== $d->value){ + $possible[$d->value] = true; } } } diff --git a/src/block/RedMushroom.php b/src/block/RedMushroom.php index 81ab940f5..a012fd4c7 100644 --- a/src/block/RedMushroom.php +++ b/src/block/RedMushroom.php @@ -41,7 +41,7 @@ class RedMushroom extends Flowable{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $down = $this->getSide(Facing::DOWN); $position = $this->position; $lightLevel = $position->getWorld()->getFullLightAt($position->x, $position->y, $position->z); diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php index 88050b85a..49dc52529 100644 --- a/src/block/RedstoneComparator.php +++ b/src/block/RedstoneComparator.php @@ -83,17 +83,17 @@ class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 7 / 8)]; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->facing = Facing::opposite($player->getHorizontalFacing()); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->isSubtractMode = !$this->isSubtractMode; $this->position->getWorld()->setBlock($this->position, $this); return true; diff --git a/src/block/RedstoneOre.php b/src/block/RedstoneOre.php index 3477a3519..52a5f5432 100644 --- a/src/block/RedstoneOre.php +++ b/src/block/RedstoneOre.php @@ -28,6 +28,7 @@ use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\item\Item; use pocketmine\item\VanillaItems; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; @@ -39,7 +40,7 @@ class RedstoneOre extends Opaque implements Lightable{ return $this->lit ? 9 : 0; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->lit){ $this->lit = true; $this->position->getWorld()->setBlock($this->position, $this); //no return here - this shouldn't prevent block placement diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php index 4059ff1cc..e7cf9187c 100644 --- a/src/block/RedstoneRepeater.php +++ b/src/block/RedstoneRepeater.php @@ -65,10 +65,10 @@ class RedstoneRepeater extends Flowable implements PoweredByRedstone, Horizontal } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 7 / 8)]; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->facing = Facing::opposite($player->getHorizontalFacing()); } @@ -76,7 +76,7 @@ class RedstoneRepeater extends Flowable implements PoweredByRedstone, Horizontal return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(++$this->delay > self::MAX_DELAY){ $this->delay = self::MIN_DELAY; } diff --git a/src/block/ResinClump.php b/src/block/ResinClump.php index a56a386d4..044128cf8 100644 --- a/src/block/ResinClump.php +++ b/src/block/ResinClump.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\utils\MultiAnyFacing; use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\SupportType; +use pocketmine\math\Facing; final class ResinClump extends Transparent implements MultiAnyFacing{ use MultiAnySupportTrait; @@ -34,7 +35,7 @@ final class ResinClump extends Transparent implements MultiAnyFacing{ return false; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -43,7 +44,7 @@ final class ResinClump extends Transparent implements MultiAnyFacing{ } /** - * @return int[] + * @return Facing[] */ protected function getInitialPlaceFaces(Block $blockReplace) : array{ return $blockReplace instanceof ResinClump ? $blockReplace->faces : []; diff --git a/src/block/RespawnAnchor.php b/src/block/RespawnAnchor.php index e19ea8f6d..b57d851fe 100644 --- a/src/block/RespawnAnchor.php +++ b/src/block/RespawnAnchor.php @@ -29,6 +29,7 @@ use pocketmine\event\player\PlayerRespawnAnchorUseEvent; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; use pocketmine\lang\KnownTranslationFactory; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\utils\TextFormat; @@ -64,7 +65,7 @@ final class RespawnAnchor extends Opaque{ return $this->charges > 0 ? ($this->charges * 4) - 1 : 0; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item->getTypeId() === ItemTypeIds::fromBlockTypeId(BlockTypeIds::GLOWSTONE) && $this->charges < self::MAX_CHARGES){ $this->position->getWorld()->setBlock($this->position, $this->setCharges($this->charges + 1)); $this->position->getWorld()->addSound($this->position, new RespawnAnchorChargeSound()); diff --git a/src/block/Sapling.php b/src/block/Sapling.php index b3fdf59af..313d8b7d4 100644 --- a/src/block/Sapling.php +++ b/src/block/Sapling.php @@ -65,7 +65,7 @@ class Sapling extends Flowable{ return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); diff --git a/src/block/SeaPickle.php b/src/block/SeaPickle.php index 34f5c3e9e..51d450645 100644 --- a/src/block/SeaPickle.php +++ b/src/block/SeaPickle.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -73,16 +74,16 @@ class SeaPickle extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ //TODO: proper placement logic (needs a supporting face below) return ($blockReplace instanceof SeaPickle && $blockReplace->count < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->underwater = false; //TODO: implement this once we have new water logic in place if($blockReplace instanceof SeaPickle && $blockReplace->count < self::MAX_COUNT){ $this->count = $blockReplace->count + 1; @@ -91,7 +92,7 @@ class SeaPickle extends Transparent{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ //TODO: bonemeal logic (requires coral) return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); } diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php index 52a3b83cd..2d46c5712 100644 --- a/src/block/ShulkerBox.php +++ b/src/block/ShulkerBox.php @@ -29,6 +29,7 @@ use pocketmine\block\utils\AnyFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -62,7 +63,7 @@ class ShulkerBox extends Opaque implements AnyFacing{ return 1; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->facing = $face; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); @@ -94,7 +95,7 @@ class ShulkerBox extends Opaque implements AnyFacing{ return $result; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $shulker = $this->position->getWorld()->getTile($this->position); @@ -113,7 +114,7 @@ class ShulkerBox extends Opaque implements AnyFacing{ return true; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/Slab.php b/src/block/Slab.php index 2bbb7528c..0ecee2571 100644 --- a/src/block/Slab.php +++ b/src/block/Slab.php @@ -63,7 +63,7 @@ class Slab extends Transparent{ return $this; } - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ if(parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock)){ return true; } @@ -79,7 +79,7 @@ class Slab extends Transparent{ return false; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($blockReplace instanceof Slab && $blockReplace->slabType !== SlabType::DOUBLE && $blockReplace->hasSameTypeId($this) && ( ($blockReplace->slabType === SlabType::TOP && ($clickVector->y <= 0.5 || $face === Facing::UP)) || ($blockReplace->slabType === SlabType::BOTTOM && ($clickVector->y >= 0.5 || $face === Facing::DOWN)) @@ -97,10 +97,10 @@ class Slab extends Transparent{ if($this->slabType === SlabType::DOUBLE){ return [AxisAlignedBB::one()]; } - return [AxisAlignedBB::one()->trim($this->slabType === SlabType::TOP ? Facing::DOWN : Facing::UP, 0.5)]; + return [AxisAlignedBB::one()->trimmedCopy($this->slabType === SlabType::TOP ? Facing::DOWN : Facing::UP, 0.5)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ if($this->slabType === SlabType::DOUBLE){ return SupportType::FULL; }elseif(($facing === Facing::UP && $this->slabType === SlabType::TOP) || ($facing === Facing::DOWN && $this->slabType === SlabType::BOTTOM)){ diff --git a/src/block/SmallDripleaf.php b/src/block/SmallDripleaf.php index 846be5c93..76031fd9b 100644 --- a/src/block/SmallDripleaf.php +++ b/src/block/SmallDripleaf.php @@ -75,7 +75,7 @@ class SmallDripleaf extends Transparent implements HorizontalFacing{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $block = $blockReplace->getSide(Facing::UP); if($block->getTypeId() !== BlockTypeIds::AIR || !$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){ return false; @@ -91,7 +91,7 @@ class SmallDripleaf extends Transparent implements HorizontalFacing{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); return true; @@ -161,7 +161,7 @@ class SmallDripleaf extends Transparent implements HorizontalFacing{ return 100; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } diff --git a/src/block/SmithingTable.php b/src/block/SmithingTable.php index 741e9c02f..818bedf4d 100644 --- a/src/block/SmithingTable.php +++ b/src/block/SmithingTable.php @@ -25,12 +25,13 @@ namespace pocketmine\block; use pocketmine\block\inventory\SmithingTableInventory; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; final class SmithingTable extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new SmithingTableInventory($this->position)); } diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php index 8549f0b31..8669bdd65 100644 --- a/src/block/SnowLayer.php +++ b/src/block/SnowLayer.php @@ -67,10 +67,10 @@ class SnowLayer extends Flowable implements Fallable{ protected function recalculateCollisionBoxes() : array{ //TODO: this zero-height BB is intended to stay in lockstep with a MCPE bug - return [AxisAlignedBB::one()->trim(Facing::UP, $this->layers >= 4 ? 0.5 : 1)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, $this->layers >= 4 ? 0.5 : 1)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ if(!$this->canBeReplaced()){ return SupportType::FULL; } @@ -81,7 +81,7 @@ class SnowLayer extends Flowable implements Fallable{ return $block->getAdjacentSupportType(Facing::DOWN) === SupportType::FULL; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($blockReplace instanceof SnowLayer){ if($blockReplace->layers >= self::MAX_LAYERS){ return false; diff --git a/src/block/SoulSand.php b/src/block/SoulSand.php index e1285d095..e1b30a90d 100644 --- a/src/block/SoulSand.php +++ b/src/block/SoulSand.php @@ -29,6 +29,6 @@ use pocketmine\math\Facing; class SoulSand extends Opaque{ protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 8)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 1 / 8)]; } } diff --git a/src/block/Stair.php b/src/block/Stair.php index 56101de13..72e62b6eb 100644 --- a/src/block/Stair.php +++ b/src/block/Stair.php @@ -83,21 +83,21 @@ class Stair extends Transparent implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ $topStepFace = $this->upsideDown ? Facing::DOWN : Facing::UP; $bbs = [ - AxisAlignedBB::one()->trim($topStepFace, 0.5) + AxisAlignedBB::one()->trimmedCopy($topStepFace, 0.5) ]; $topStep = AxisAlignedBB::one() - ->trim(Facing::opposite($topStepFace), 0.5) - ->trim(Facing::opposite($this->facing), 0.5); + ->trimmedCopy(Facing::opposite($topStepFace), 0.5) + ->trimmedCopy(Facing::opposite($this->facing), 0.5); if($this->shape === StairShape::OUTER_LEFT || $this->shape === StairShape::OUTER_RIGHT){ - $topStep->trim(Facing::rotateY($this->facing, $this->shape === StairShape::OUTER_LEFT), 0.5); + $topStep = $topStep->trimmedCopy(Facing::rotateY($this->facing, $this->shape === StairShape::OUTER_LEFT), 0.5); }elseif($this->shape === StairShape::INNER_LEFT || $this->shape === StairShape::INNER_RIGHT){ //add an extra cube $bbs[] = AxisAlignedBB::one() - ->trim(Facing::opposite($topStepFace), 0.5) - ->trim($this->facing, 0.5) //avoid overlapping with main step - ->trim(Facing::rotateY($this->facing, $this->shape === StairShape::INNER_LEFT), 0.5); + ->trimmedCopy(Facing::opposite($topStepFace), 0.5) + ->trimmedCopy($this->facing, 0.5) //avoid overlapping with main step + ->trimmedCopy(Facing::rotateY($this->facing, $this->shape === StairShape::INNER_LEFT), 0.5); } $bbs[] = $topStep; @@ -105,7 +105,7 @@ class Stair extends Transparent implements HorizontalFacing{ return $bbs; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ if( $facing === Facing::UP && $this->upsideDown || $facing === Facing::DOWN && !$this->upsideDown || @@ -118,7 +118,7 @@ class Stair extends Transparent implements HorizontalFacing{ return SupportType::NONE; } - private function getPossibleCornerFacing(bool $oppositeFacing) : ?int{ + private function getPossibleCornerFacing(bool $oppositeFacing) : ?Facing{ $side = $this->getSide($oppositeFacing ? Facing::opposite($this->facing) : $this->facing); return ( $side instanceof Stair && @@ -127,7 +127,7 @@ class Stair extends Transparent implements HorizontalFacing{ ) ? $side->facing : null; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->facing = $player->getHorizontalFacing(); } diff --git a/src/block/Stem.php b/src/block/Stem.php index 2b6f2150c..8dbe915f1 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -33,17 +33,17 @@ use pocketmine\math\Facing; use function array_rand; abstract class Stem extends Crops{ - protected int $facing = Facing::UP; + protected Facing $facing = Facing::UP; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ parent::describeBlockOnlyState($w); $w->facingExcept($this->facing, Facing::DOWN); } - public function getFacing() : int{ return $this->facing; } + public function getFacing() : Facing{ return $this->facing; } /** @return $this */ - public function setFacing(int $facing) : self{ + public function setFacing(Facing $facing) : self{ if($facing === Facing::DOWN){ throw new \InvalidArgumentException("DOWN is not a valid facing for this block"); } diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php index 0fd259326..3f955b785 100644 --- a/src/block/Stonecutter.php +++ b/src/block/Stonecutter.php @@ -36,7 +36,7 @@ use pocketmine\player\Player; class Stonecutter extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new StonecutterInventory($this->position)); } @@ -44,10 +44,10 @@ class Stonecutter extends Transparent implements HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 7 / 16)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 0874413c5..0f8d7e13c 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -75,7 +75,7 @@ class Sugarcane extends Flowable implements Ageable{ return $grew; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer){ if($this->grow($this->seekToBottom(), $player)){ $item->pop(); @@ -116,7 +116,7 @@ class Sugarcane extends Flowable implements Ageable{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $down = $blockReplace->getSide(Facing::DOWN); if($down->hasSameTypeId($this)){ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index baebb7588..1b7ce5bad 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -65,7 +65,7 @@ class SweetBerryBush extends Flowable implements Ageable{ ($supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD)); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $world = $this->position->getWorld(); if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){ $block = clone $this; diff --git a/src/block/TNT.php b/src/block/TNT.php index a0256bb67..20543dcbd 100644 --- a/src/block/TNT.php +++ b/src/block/TNT.php @@ -32,6 +32,7 @@ use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\FlintSteel; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; +use pocketmine\math\Facing; use pocketmine\math\RayTraceResult; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -77,7 +78,7 @@ class TNT extends Opaque{ return parent::onBreak($item, $player, $returnedItems); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){ $item->pop(); $this->ignite(); diff --git a/src/block/Thin.php b/src/block/Thin.php index 82010697a..585f70a80 100644 --- a/src/block/Thin.php +++ b/src/block/Thin.php @@ -44,9 +44,9 @@ class Thin extends Transparent{ foreach(Facing::HORIZONTAL as $facing){ $side = $this->getSide($facing); if($side instanceof Thin || $side instanceof Wall || $side->getSupportType(Facing::opposite($facing)) === SupportType::FULL){ - $this->connections[$facing] = true; + $this->connections[$facing->value] = true; }else{ - unset($this->connections[$facing]); + unset($this->connections[$facing->value]); } } @@ -58,24 +58,24 @@ class Thin extends Transparent{ $bbs = []; - if(isset($this->connections[Facing::WEST]) || isset($this->connections[Facing::EAST])){ - $bb = AxisAlignedBB::one()->squash(Axis::Z, $inset); + if(isset($this->connections[Facing::WEST->value]) || isset($this->connections[Facing::EAST->value])){ + $bb = AxisAlignedBB::one()->squashedCopy(Axis::Z, $inset); - if(!isset($this->connections[Facing::WEST])){ - $bb->trim(Facing::WEST, $inset); - }elseif(!isset($this->connections[Facing::EAST])){ - $bb->trim(Facing::EAST, $inset); + if(!isset($this->connections[Facing::WEST->value])){ + $bb = $bb->trimmedCopy(Facing::WEST, $inset); + }elseif(!isset($this->connections[Facing::EAST->value])){ + $bb = $bb->trimmedCopy(Facing::EAST, $inset); } $bbs[] = $bb; } - if(isset($this->connections[Facing::NORTH]) || isset($this->connections[Facing::SOUTH])){ - $bb = AxisAlignedBB::one()->squash(Axis::X, $inset); + if(isset($this->connections[Facing::NORTH->value]) || isset($this->connections[Facing::SOUTH->value])){ + $bb = AxisAlignedBB::one()->squashedCopy(Axis::X, $inset); - if(!isset($this->connections[Facing::NORTH])){ - $bb->trim(Facing::NORTH, $inset); - }elseif(!isset($this->connections[Facing::SOUTH])){ - $bb->trim(Facing::SOUTH, $inset); + if(!isset($this->connections[Facing::NORTH->value])){ + $bb = $bb->trimmedCopy(Facing::NORTH, $inset); + }elseif(!isset($this->connections[Facing::SOUTH->value])){ + $bb = $bb->trimmedCopy(Facing::SOUTH, $inset); } $bbs[] = $bb; } @@ -83,14 +83,14 @@ class Thin extends Transparent{ if(count($bbs) === 0){ //centre post AABB (only needed if not connected on any axis - other BBs overlapping will do this if any connections are made) return [ - AxisAlignedBB::one()->contract($inset, 0, $inset) + AxisAlignedBB::one()->contractedCopy($inset, 0, $inset) ]; } return $bbs; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } diff --git a/src/block/Torch.php b/src/block/Torch.php index aee4da32a..3d108a816 100644 --- a/src/block/Torch.php +++ b/src/block/Torch.php @@ -33,16 +33,16 @@ use pocketmine\world\BlockTransaction; class Torch extends Flowable{ - protected int $facing = Facing::UP; + protected Facing $facing = Facing::UP; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->facingExcept($this->facing, Facing::DOWN); } - public function getFacing() : int{ return $this->facing; } + public function getFacing() : Facing{ return $this->facing; } /** @return $this */ - public function setFacing(int $facing) : self{ + public function setFacing(Facing $facing) : self{ if($facing === Facing::DOWN){ throw new \InvalidArgumentException("Torch may not face DOWN"); } @@ -60,7 +60,7 @@ class Torch extends Flowable{ } } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face !== Facing::DOWN && $this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ $this->facing = $face; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); @@ -81,7 +81,7 @@ class Torch extends Flowable{ return false; } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $face === Facing::DOWN ? $block->getAdjacentSupportType($face)->hasCenterSupport() : $block->getAdjacentSupportType($face) === SupportType::FULL; diff --git a/src/block/TorchflowerCrop.php b/src/block/TorchflowerCrop.php index 033b08552..a5dc2dcbb 100644 --- a/src/block/TorchflowerCrop.php +++ b/src/block/TorchflowerCrop.php @@ -62,7 +62,7 @@ final class TorchflowerCrop extends Flowable{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer){ if(BlockEventHelper::grow($this, $this->getNextState(), $player)){ $item->pop(); diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php index 5e8a7dc3f..8b9a10d41 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -64,14 +64,14 @@ class Trapdoor extends Transparent implements HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->facing = Facing::opposite($player->getHorizontalFacing()); } @@ -82,7 +82,7 @@ class Trapdoor extends Transparent implements HorizontalFacing{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->open = !$this->open; $world = $this->position->getWorld(); $world->setBlock($this->position, $this); diff --git a/src/block/TripwireHook.php b/src/block/TripwireHook.php index 4e40cf62e..0fe43da56 100644 --- a/src/block/TripwireHook.php +++ b/src/block/TripwireHook.php @@ -61,7 +61,7 @@ class TripwireHook extends Flowable implements HorizontalFacing{ return $this; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(Facing::axis($face) !== Axis::Y){ //TODO: check face is valid $this->facing = $face; diff --git a/src/block/Vine.php b/src/block/Vine.php index cc516bbca..9ac4a3ade 100644 --- a/src/block/Vine.php +++ b/src/block/Vine.php @@ -36,22 +36,22 @@ use function count; class Vine extends Flowable{ - /** @var int[] */ + /** @var Facing[] */ protected array $faces = []; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->horizontalFacingFlags($this->faces); } - /** @return int[] */ + /** @return Facing[] */ public function getFaces() : array{ return $this->faces; } - public function hasFace(int $face) : bool{ - return isset($this->faces[$face]); + public function hasFace(Facing $face) : bool{ + return isset($this->faces[$face->value]); } /** - * @param int[] $faces + * @param Facing[] $faces * @phpstan-param list $faces * @return $this */ @@ -61,21 +61,21 @@ class Vine extends Flowable{ if($face !== Facing::NORTH && $face !== Facing::SOUTH && $face !== Facing::WEST && $face !== Facing::EAST){ throw new \InvalidArgumentException("Facing can only be north, east, south or west"); } - $uniqueFaces[$face] = $face; + $uniqueFaces[$face->value] = $face; } $this->faces = $uniqueFaces; return $this; } /** @return $this */ - public function setFace(int $face, bool $value) : self{ + public function setFace(Facing $face, bool $value) : self{ if($face !== Facing::NORTH && $face !== Facing::SOUTH && $face !== Facing::WEST && $face !== Facing::EAST){ throw new \InvalidArgumentException("Facing can only be north, east, south or west"); } if($value){ - $this->faces[$face] = $face; + $this->faces[$face->value] = $face; }else{ - unset($this->faces[$face]); + unset($this->faces[$face->value]); } return $this; } @@ -101,13 +101,14 @@ class Vine extends Flowable{ return []; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(!$blockReplace->getSide(Facing::opposite($face))->isFullCube() || Facing::axis($face) === Axis::Y){ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $opposite = Facing::opposite($face); + if(!$blockReplace->getSide($opposite)->isFullCube() || Facing::axis($face) === Axis::Y){ return false; } $this->faces = $blockReplace instanceof Vine ? $blockReplace->faces : []; - $this->faces[Facing::opposite($face)] = Facing::opposite($face); + $this->faces[$opposite->value] = $opposite; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -120,8 +121,8 @@ class Vine extends Flowable{ $supportedFaces = $up instanceof Vine ? array_intersect_key($this->faces, $up->faces) : []; foreach($this->faces as $face){ - if(!isset($supportedFaces[$face]) && !$this->getSide($face)->isSolid()){ - unset($this->faces[$face]); + if(!isset($supportedFaces[$face->value]) && !$this->getSide($face)->isSolid()){ + unset($this->faces[$face->value]); $changed = true; } } diff --git a/src/block/Wall.php b/src/block/Wall.php index 520ced8eb..b39e35c4b 100644 --- a/src/block/Wall.php +++ b/src/block/Wall.php @@ -31,7 +31,7 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; /** - * @phpstan-type WallConnectionSet array + * @phpstan-type WallConnectionSet array, WallConnectionType> */ class Wall extends Transparent{ @@ -53,8 +53,8 @@ class Wall extends Transparent{ */ public function getConnections() : array{ return $this->connections; } - public function getConnection(int $face) : ?WallConnectionType{ - return $this->connections[$face] ?? null; + public function getConnection(Facing $face) : ?WallConnectionType{ + return $this->connections[$face->value] ?? null; } /** @@ -68,14 +68,14 @@ class Wall extends Transparent{ } /** @return $this */ - public function setConnection(int $face, ?WallConnectionType $type) : self{ + public function setConnection(Facing $face, ?WallConnectionType $type) : self{ if($face !== Facing::NORTH && $face !== Facing::SOUTH && $face !== Facing::WEST && $face !== Facing::EAST){ throw new \InvalidArgumentException("Facing can only be north, east, south or west"); } if($type !== null){ - $this->connections[$face] = $type; + $this->connections[$face->value] = $type; }else{ - unset($this->connections[$face]); + unset($this->connections[$face->value]); } return $this; } @@ -102,12 +102,12 @@ class Wall extends Transparent{ foreach(Facing::HORIZONTAL as $facing){ $block = $this->getSide($facing); if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || $block->getSupportType(Facing::opposite($facing)) === SupportType::FULL){ - if(!isset($this->connections[$facing])){ - $this->connections[$facing] = WallConnectionType::SHORT; + if(!isset($this->connections[$facing->value])){ + $this->connections[$facing->value] = WallConnectionType::SHORT; $changed++; } - }elseif(isset($this->connections[$facing])){ - unset($this->connections[$facing]); + }elseif(isset($this->connections[$facing->value])){ + unset($this->connections[$facing->value]); $changed++; } } @@ -124,10 +124,10 @@ class Wall extends Transparent{ protected function recalculateCollisionBoxes() : array{ //walls don't have any special collision boxes like fences do - $north = isset($this->connections[Facing::NORTH]); - $south = isset($this->connections[Facing::SOUTH]); - $west = isset($this->connections[Facing::WEST]); - $east = isset($this->connections[Facing::EAST]); + $north = isset($this->connections[Facing::NORTH->value]); + $south = isset($this->connections[Facing::SOUTH->value]); + $west = isset($this->connections[Facing::WEST->value]); + $east = isset($this->connections[Facing::EAST->value]); $inset = 0.25; if( @@ -143,15 +143,15 @@ class Wall extends Transparent{ return [ AxisAlignedBB::one() - ->extend(Facing::UP, 0.5) - ->trim(Facing::NORTH, $north ? 0 : $inset) - ->trim(Facing::SOUTH, $south ? 0 : $inset) - ->trim(Facing::WEST, $west ? 0 : $inset) - ->trim(Facing::EAST, $east ? 0 : $inset) + ->extendedCopy(Facing::UP, 0.5) + ->trimmedCopy(Facing::NORTH, $north ? 0 : $inset) + ->trimmedCopy(Facing::SOUTH, $south ? 0 : $inset) + ->trimmedCopy(Facing::WEST, $west ? 0 : $inset) + ->trimmedCopy(Facing::EAST, $east ? 0 : $inset) ]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE; } } diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php index b631e0c81..9159c2485 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -39,11 +39,11 @@ final class WallBanner extends BaseBanner implements HorizontalFacing{ return VanillaBlocks::OMINOUS_WALL_BANNER()->setFacing($this->facing); } - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::opposite($this->facing); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(Facing::axis($face) === Axis::Y){ return false; } diff --git a/src/block/WallCoralFan.php b/src/block/WallCoralFan.php index 67745a537..74ec3dcc4 100644 --- a/src/block/WallCoralFan.php +++ b/src/block/WallCoralFan.php @@ -41,7 +41,7 @@ final class WallCoralFan extends BaseCoral implements HorizontalFacing{ $w->horizontalFacing($this->facing); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $axis = Facing::axis($face); if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ return false; @@ -62,7 +62,7 @@ final class WallCoralFan extends BaseCoral implements HorizontalFacing{ } } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType($face)->hasCenterSupport(); } diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php index df959c720..f03ff5927 100644 --- a/src/block/WallHangingSign.php +++ b/src/block/WallHangingSign.php @@ -37,7 +37,7 @@ use pocketmine\world\BlockTransaction; final class WallHangingSign extends BaseSign implements HorizontalFacing{ use HorizontalFacingTrait; - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::rotateY($this->facing, clockwise: true); } @@ -47,10 +47,10 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ //only the cross bar is collidable - return [AxisAlignedBB::one()->trim(Facing::DOWN, 7 / 8)->squash(Facing::axis($this->facing), 3 / 4)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::DOWN, 7 / 8)->squashedCopy(Facing::axis($this->facing), 3 / 4)]; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player === null){ return false; } @@ -73,7 +73,7 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return ($block instanceof WallHangingSign && Facing::axis(Facing::rotateY($block->getFacing(), clockwise: true)) === Facing::axis($face)) || $block->getSupportType(Facing::opposite($face)) === SupportType::FULL; diff --git a/src/block/WallSign.php b/src/block/WallSign.php index c6b42608d..8a7bd8eb3 100644 --- a/src/block/WallSign.php +++ b/src/block/WallSign.php @@ -35,11 +35,11 @@ use pocketmine\world\BlockTransaction; final class WallSign extends BaseSign implements HorizontalFacing{ use HorizontalFacingTrait; - protected function getSupportingFace() : int{ + protected function getSupportingFace() : Facing{ return Facing::opposite($this->facing); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if(Facing::axis($face) === Axis::Y){ return false; } diff --git a/src/block/WaterCauldron.php b/src/block/WaterCauldron.php index 8129f2960..60bdc132e 100644 --- a/src/block/WaterCauldron.php +++ b/src/block/WaterCauldron.php @@ -37,6 +37,7 @@ use pocketmine\item\Potion; use pocketmine\item\PotionType; use pocketmine\item\SplashPotion; use pocketmine\item\VanillaItems; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\CauldronAddDyeSound; @@ -108,7 +109,7 @@ final class WaterCauldron extends FillableCauldron{ return new CauldronEmptyWaterSound(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $world = $this->position->getWorld(); if(($dyeColor = match($item->getTypeId()){ ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE, diff --git a/src/block/WaterLily.php b/src/block/WaterLily.php index b04b1baed..d71a9d85b 100644 --- a/src/block/WaterLily.php +++ b/src/block/WaterLily.php @@ -34,10 +34,10 @@ class WaterLily extends Flowable{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)]; + return [AxisAlignedBB::one()->contractedCopy(1 / 16, 0, 1 / 16)->trimmedCopy(Facing::UP, 63 / 64)]; } - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ return !$blockReplace instanceof Water && $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } diff --git a/src/block/Wood.php b/src/block/Wood.php index 7aa667bc8..6dc27efbe 100644 --- a/src/block/Wood.php +++ b/src/block/Wood.php @@ -30,6 +30,7 @@ use pocketmine\block\utils\WoodTypeTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Axe; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\ItemUseOnBlockSound; @@ -64,7 +65,7 @@ class Wood extends Opaque implements PillarRotation, WoodMaterial{ return $this->woodType->isFlammable() ? 5 : 0; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->stripped && $item instanceof Axe){ $item->applyDamage(1); $this->stripped = true; diff --git a/src/block/tile/Bell.php b/src/block/tile/Bell.php index 7a1e784e3..370964e9e 100644 --- a/src/block/tile/Bell.php +++ b/src/block/tile/Bell.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block\tile; +use pocketmine\data\SavedDataLoadingException; use pocketmine\math\Facing; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\BlockActorDataPacket; @@ -36,16 +37,16 @@ final class Bell extends Spawnable{ public const TAG_TICKS = "Ticks"; //TAG_Int private bool $ringing = false; - private int $facing = Facing::NORTH; + private Facing $facing = Facing::NORTH; private int $ticks = 0; public function isRinging() : bool{ return $this->ringing; } public function setRinging(bool $ringing) : void{ $this->ringing = $ringing; } - public function getFacing() : int{ return $this->facing; } + public function getFacing() : Facing{ return $this->facing; } - public function setFacing(int $facing) : void{ $this->facing = $facing; } + public function setFacing(Facing $facing) : void{ $this->facing = $facing; } public function getTicks() : int{ return $this->ticks; } @@ -53,19 +54,22 @@ final class Bell extends Spawnable{ protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ $nbt->setByte(self::TAG_RINGING, $this->ringing ? 1 : 0); - $nbt->setInt(self::TAG_DIRECTION, $this->facing); + //TODO: suspicious use of internal Facing value for network + $nbt->setInt(self::TAG_DIRECTION, $this->facing->value); $nbt->setInt(self::TAG_TICKS, $this->ticks); } public function readSaveData(CompoundTag $nbt) : void{ $this->ringing = $nbt->getByte(self::TAG_RINGING, 0) !== 0; - $this->facing = $nbt->getInt(self::TAG_DIRECTION, Facing::NORTH); + //TODO: suspicious use of internal Facing value for storage + $this->facing = Facing::tryFrom($nbt->getInt(self::TAG_DIRECTION, Facing::NORTH->value)) ?? throw new SavedDataLoadingException("Invalid facing value"); $this->ticks = $nbt->getInt(self::TAG_TICKS, 0); } protected function writeSaveData(CompoundTag $nbt) : void{ $nbt->setByte(self::TAG_RINGING, $this->ringing ? 1 : 0); - $nbt->setInt(self::TAG_DIRECTION, $this->facing); + //TODO: suspicious use of internal Facing value for storage + $nbt->setInt(self::TAG_DIRECTION, $this->facing->value); $nbt->setInt(self::TAG_TICKS, $this->ticks); } @@ -77,7 +81,7 @@ final class Bell extends Spawnable{ * simpler as a BlockEventPacket. It's simpler to implement bells with this hack than to follow Mojang's complicated * mess. */ - public function createFakeUpdatePacket(int $bellHitFace) : BlockActorDataPacket{ + public function createFakeUpdatePacket(Facing $bellHitFace) : BlockActorDataPacket{ $nbt = $this->getSpawnCompound(); $nbt->setByte(self::TAG_RINGING, 1); $nbt->setInt(self::TAG_DIRECTION, match($bellHitFace){ diff --git a/src/block/tile/ShulkerBox.php b/src/block/tile/ShulkerBox.php index a30b75c4e..87221b97a 100644 --- a/src/block/tile/ShulkerBox.php +++ b/src/block/tile/ShulkerBox.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block\tile; use pocketmine\block\inventory\ShulkerBoxInventory; +use pocketmine\data\SavedDataLoadingException; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -38,7 +39,7 @@ class ShulkerBox extends Spawnable implements Container, Nameable{ public const TAG_FACING = "facing"; - protected int $facing = Facing::NORTH; + protected Facing $facing = Facing::NORTH; protected ShulkerBoxInventory $inventory; @@ -50,13 +51,15 @@ class ShulkerBox extends Spawnable implements Container, Nameable{ public function readSaveData(CompoundTag $nbt) : void{ $this->loadName($nbt); $this->loadItems($nbt); - $this->facing = $nbt->getByte(self::TAG_FACING, $this->facing); + //TODO: suspicious use of internal Facing value for storage + $this->facing = Facing::tryFrom($nbt->getByte(self::TAG_FACING, $this->facing->value)) ?? throw new SavedDataLoadingException("Invalid facing value"); } protected function writeSaveData(CompoundTag $nbt) : void{ $this->saveName($nbt); $this->saveItems($nbt); - $nbt->setByte(self::TAG_FACING, $this->facing); + //TODO: suspicious use of internal Facing value for storage + $nbt->setByte(self::TAG_FACING, $this->facing->value); } public function copyDataFromItem(Item $item) : void{ @@ -85,11 +88,11 @@ class ShulkerBox extends Spawnable implements Container, Nameable{ return $nbt; } - public function getFacing() : int{ + public function getFacing() : Facing{ return $this->facing; } - public function setFacing(int $facing) : void{ + public function setFacing(Facing $facing) : void{ $this->facing = $facing; } @@ -106,7 +109,8 @@ class ShulkerBox extends Spawnable implements Container, Nameable{ } protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ - $nbt->setByte(self::TAG_FACING, $this->facing); + //TODO: suspicious use of internal Facing value for network + $nbt->setByte(self::TAG_FACING, $this->facing->value); $this->addNameSpawnData($nbt); } } diff --git a/src/block/utils/AnyFacing.php b/src/block/utils/AnyFacing.php index 8b0885ba8..0cf71632e 100644 --- a/src/block/utils/AnyFacing.php +++ b/src/block/utils/AnyFacing.php @@ -26,16 +26,10 @@ namespace pocketmine\block\utils; use pocketmine\math\Facing; interface AnyFacing{ - - /** - * @see Facing - */ - public function getFacing() : int; + public function getFacing() : Facing; /** * @return $this - * - * @see Facing */ - public function setFacing(int $facing) : self; + public function setFacing(Facing $facing) : self; } diff --git a/src/block/utils/AnyFacingTrait.php b/src/block/utils/AnyFacingTrait.php index 4805b7b7c..a54e0c01e 100644 --- a/src/block/utils/AnyFacingTrait.php +++ b/src/block/utils/AnyFacingTrait.php @@ -27,17 +27,16 @@ use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\math\Facing; trait AnyFacingTrait{ - protected int $facing = Facing::DOWN; + protected Facing $facing = Facing::DOWN; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facing($this->facing); + $w->enum($this->facing); } - public function getFacing() : int{ return $this->facing; } + public function getFacing() : Facing{ return $this->facing; } /** @return $this */ - public function setFacing(int $facing) : self{ - Facing::validate($this->facing); + public function setFacing(Facing $facing) : self{ $this->facing = $facing; return $this; } diff --git a/src/block/utils/CandleTrait.php b/src/block/utils/CandleTrait.php index 0cbd13044..3f8e164bf 100644 --- a/src/block/utils/CandleTrait.php +++ b/src/block/utils/CandleTrait.php @@ -29,6 +29,7 @@ use pocketmine\item\Durable; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; +use pocketmine\math\Facing; use pocketmine\math\RayTraceResult; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -45,9 +46,10 @@ trait CandleTrait{ /** * @param Item[] &$returnedItems + * * @see Block::onInteract() */ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE || $item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){ if($this->lit){ return true; diff --git a/src/block/utils/CopperTrait.php b/src/block/utils/CopperTrait.php index 2ed06b798..1b74331cb 100644 --- a/src/block/utils/CopperTrait.php +++ b/src/block/utils/CopperTrait.php @@ -28,6 +28,7 @@ use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Axe; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\CopperWaxApplySound; @@ -61,9 +62,10 @@ trait CopperTrait{ /** * @param Item[] &$returnedItems + * * @see Block::onInteract() */ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->waxed && $item->getTypeId() === ItemTypeIds::HONEYCOMB){ $this->waxed = true; $this->position->getWorld()->setBlock($this->position, $this); diff --git a/src/block/utils/FacesOppositePlacingPlayerTrait.php b/src/block/utils/FacesOppositePlacingPlayerTrait.php index 0bca482b5..e1fa05206 100644 --- a/src/block/utils/FacesOppositePlacingPlayerTrait.php +++ b/src/block/utils/FacesOppositePlacingPlayerTrait.php @@ -36,7 +36,7 @@ trait FacesOppositePlacingPlayerTrait{ /** * @see Block::place() */ - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $this->facing = Facing::opposite($player->getHorizontalFacing()); } diff --git a/src/block/utils/HorizontalFacing.php b/src/block/utils/HorizontalFacing.php index 4d0d8cfd6..b94d1a1bd 100644 --- a/src/block/utils/HorizontalFacing.php +++ b/src/block/utils/HorizontalFacing.php @@ -26,16 +26,8 @@ namespace pocketmine\block\utils; use pocketmine\math\Facing; interface HorizontalFacing{ + public function getFacing() : Facing; - /** - * @see Facing - */ - public function getFacing() : int; - - /** - * @return $this - * - * @see Facing - */ - public function setFacing(int $facing) : self; + /** @return $this */ + public function setFacing(Facing $facing) : self; } diff --git a/src/block/utils/HorizontalFacingTrait.php b/src/block/utils/HorizontalFacingTrait.php index a5bd6dcf4..f4f231d33 100644 --- a/src/block/utils/HorizontalFacingTrait.php +++ b/src/block/utils/HorizontalFacingTrait.php @@ -28,16 +28,17 @@ use pocketmine\math\Axis; use pocketmine\math\Facing; trait HorizontalFacingTrait{ - protected int $facing = Facing::NORTH; + //TODO: this really needs a proper HorizontalFacing enum + protected Facing $facing = Facing::NORTH; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->horizontalFacing($this->facing); } - public function getFacing() : int{ return $this->facing; } + public function getFacing() : Facing{ return $this->facing; } /** @return $this */ - public function setFacing(int $facing) : self{ + public function setFacing(Facing $facing) : self{ $axis = Facing::axis($facing); if($axis !== Axis::X && $axis !== Axis::Z){ throw new \InvalidArgumentException("Facing must be horizontal"); diff --git a/src/block/utils/LeverFacing.php b/src/block/utils/LeverFacing.php index 6edb29376..73728d08f 100644 --- a/src/block/utils/LeverFacing.php +++ b/src/block/utils/LeverFacing.php @@ -35,7 +35,7 @@ enum LeverFacing{ case SOUTH; case WEST; - public function getFacing() : int{ + public function getFacing() : Facing{ return match($this){ self::UP_AXIS_X, self::UP_AXIS_Z => Facing::UP, self::DOWN_AXIS_X, self::DOWN_AXIS_Z => Facing::DOWN, diff --git a/src/block/utils/MinimumCostFlowCalculator.php b/src/block/utils/MinimumCostFlowCalculator.php index a690a7487..c8e55ded1 100644 --- a/src/block/utils/MinimumCostFlowCalculator.php +++ b/src/block/utils/MinimumCostFlowCalculator.php @@ -27,6 +27,7 @@ use pocketmine\block\Block; use pocketmine\math\Facing; use pocketmine\world\World; use function array_fill_keys; +use function array_map; use function intdiv; use function min; @@ -51,14 +52,14 @@ final class MinimumCostFlowCalculator{ private \Closure $canFlowInto ){} - private function calculateFlowCost(int $blockX, int $blockY, int $blockZ, int $accumulatedCost, int $maxCost, int $originOpposite, int $lastOpposite) : int{ + private function calculateFlowCost(int $blockX, int $blockY, int $blockZ, int $accumulatedCost, int $maxCost, Facing $originOpposite, Facing $lastOpposite) : int{ $cost = 1000; foreach(Facing::HORIZONTAL as $j){ if($j === $originOpposite || $j === $lastOpposite){ continue; } - [$dx, $dy, $dz] = Facing::OFFSET[$j]; + [$dx, $dy, $dz] = Facing::OFFSET[$j->value]; $x = $blockX + $dx; $y = $blockY + $dy; $z = $blockZ + $dz; @@ -99,10 +100,10 @@ final class MinimumCostFlowCalculator{ * @return int[] */ public function getOptimalFlowDirections(int $originX, int $originY, int $originZ) : array{ - $flowCost = array_fill_keys(Facing::HORIZONTAL, 1000); + $flowCost = array_fill_keys(array_map(fn(Facing $f) => $f->value, Facing::HORIZONTAL), 1000); $maxCost = intdiv(4, $this->flowDecayPerBlock); foreach(Facing::HORIZONTAL as $j){ - [$dx, $dy, $dz] = Facing::OFFSET[$j]; + [$dx, $dy, $dz] = Facing::OFFSET[$j->value]; $x = $originX + $dx; $y = $originY + $dy; $z = $originZ + $dz; @@ -111,12 +112,12 @@ final class MinimumCostFlowCalculator{ $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::BLOCKED; }elseif($this->world->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){ $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::CAN_FLOW_DOWN; - $flowCost[$j] = $maxCost = 0; + $flowCost[$j->value] = $maxCost = 0; }elseif($maxCost > 0){ $this->flowCostVisited[World::blockHash($x, $y, $z)] = self::CAN_FLOW; $opposite = Facing::opposite($j); - $flowCost[$j] = $this->calculateFlowCost($x, $y, $z, 1, $maxCost, $opposite, $opposite); - $maxCost = min($maxCost, $flowCost[$j]); + $flowCost[$j->value] = $this->calculateFlowCost($x, $y, $z, 1, $maxCost, $opposite, $opposite); + $maxCost = min($maxCost, $flowCost[$j->value]); } } diff --git a/src/block/utils/MultiAnyFacing.php b/src/block/utils/MultiAnyFacing.php index dafe041e4..c50e36ff0 100644 --- a/src/block/utils/MultiAnyFacing.php +++ b/src/block/utils/MultiAnyFacing.php @@ -27,27 +27,18 @@ use pocketmine\math\Facing; interface MultiAnyFacing{ - /** - * @return int[] - * @see Facing - */ + /** @return Facing[] */ public function getFaces() : array; - public function hasFace(int $face) : bool; + public function hasFace(Facing $face) : bool; + + /** @return $this */ + public function setFace(Facing $face, bool $value) : self; /** - * @return $this - * - * @see Facing - */ - public function setFace(int $face, bool $value) : self; - - /** - * @param int[] $faces + * @param Facing[] $faces * * @return $this - * - * @see Facing */ public function setFaces(array $faces) : self; diff --git a/src/block/utils/MultiAnyFacingTrait.php b/src/block/utils/MultiAnyFacingTrait.php index 66f26d980..b7cd77901 100644 --- a/src/block/utils/MultiAnyFacingTrait.php +++ b/src/block/utils/MultiAnyFacingTrait.php @@ -31,41 +31,39 @@ use pocketmine\math\Facing; */ trait MultiAnyFacingTrait{ - /** @var int[] */ + /** @var Facing[] */ protected array $faces = []; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->facingFlags($this->faces); } - /** @return int[] */ + /** @return Facing[] */ public function getFaces() : array{ return $this->faces; } - public function hasFace(int $face) : bool{ - return isset($this->faces[$face]); + public function hasFace(Facing $face) : bool{ + return isset($this->faces[$face->value]); } /** - * @param int[] $faces + * @param Facing[] $faces * @return $this */ public function setFaces(array $faces) : self{ $uniqueFaces = []; foreach($faces as $face){ - Facing::validate($face); - $uniqueFaces[$face] = $face; + $uniqueFaces[$face->value] = $face; } $this->faces = $uniqueFaces; return $this; } /** @return $this */ - public function setFace(int $face, bool $value) : self{ - Facing::validate($face); + public function setFace(Facing $face, bool $value) : self{ if($value){ - $this->faces[$face] = $face; + $this->faces[$face->value] = $face; }else{ - unset($this->faces[$face]); + unset($this->faces[$face->value]); } return $this; } diff --git a/src/block/utils/MultiAnySupportTrait.php b/src/block/utils/MultiAnySupportTrait.php index ae1da7bef..55164b6f0 100644 --- a/src/block/utils/MultiAnySupportTrait.php +++ b/src/block/utils/MultiAnySupportTrait.php @@ -42,11 +42,11 @@ trait MultiAnySupportTrait{ /** * Returns a list of faces that block should already have when placed. * - * @return int[] + * @return Facing[] */ abstract protected function getInitialPlaceFaces(Block $blockReplace) : array; - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->faces = $this->getInitialPlaceFaces($blockReplace); $availableFaces = $this->getAvailableFaces(); @@ -55,8 +55,8 @@ trait MultiAnySupportTrait{ } $opposite = Facing::opposite($face); - $placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces); - $this->faces[$placedFace] = $placedFace; + $placedFace = isset($availableFaces[$opposite->value]) ? $opposite : $availableFaces[array_key_first($availableFaces)]; + $this->faces[$placedFace->value] = $placedFace; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -66,7 +66,7 @@ trait MultiAnySupportTrait{ foreach($this->faces as $face){ if($this->getAdjacentSupportType($face) !== SupportType::FULL){ - unset($this->faces[$face]); + unset($this->faces[$face->value]); $changed = true; } } @@ -82,13 +82,14 @@ trait MultiAnySupportTrait{ } /** - * @return array $faces + * @return Facing[] + * @phpstan-return array */ private function getAvailableFaces() : array{ $faces = []; foreach(Facing::ALL as $face){ if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){ - $faces[$face] = $face; + $faces[$face->value] = $face; } } return $faces; diff --git a/src/block/utils/PillarRotation.php b/src/block/utils/PillarRotation.php index f016ce50c..cfba744f1 100644 --- a/src/block/utils/PillarRotation.php +++ b/src/block/utils/PillarRotation.php @@ -27,13 +27,8 @@ use pocketmine\math\Axis; interface PillarRotation{ - /** @see Axis */ - public function getAxis() : int; + public function getAxis() : Axis; - /** - * @return $this - * - * @see Axis - */ - public function setAxis(int $axis) : self; + /** @return $this */ + public function setAxis(Axis $axis) : self; } diff --git a/src/block/utils/PillarRotationTrait.php b/src/block/utils/PillarRotationTrait.php index 3f0117dec..d789ec75c 100644 --- a/src/block/utils/PillarRotationTrait.php +++ b/src/block/utils/PillarRotationTrait.php @@ -33,17 +33,17 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; trait PillarRotationTrait{ - protected int $axis = Axis::Y; + protected Axis $axis = Axis::Y; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->axis($this->axis); + $w->enum($this->axis); } /** @see Axis */ - public function getAxis() : int{ return $this->axis; } + public function getAxis() : Axis{ return $this->axis; } /** @return $this */ - public function setAxis(int $axis) : self{ + public function setAxis(Axis $axis) : self{ $this->axis = $axis; return $this; } @@ -51,7 +51,7 @@ trait PillarRotationTrait{ /** * @see Block::place() */ - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ $this->axis = Facing::axis($face); /** @see Block::place() */ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); diff --git a/src/block/utils/RailConnectionInfo.php b/src/block/utils/RailConnectionInfo.php index f343bb726..af13a51c9 100644 --- a/src/block/utils/RailConnectionInfo.php +++ b/src/block/utils/RailConnectionInfo.php @@ -33,50 +33,50 @@ final class RailConnectionInfo{ public const CONNECTIONS = [ //straights BlockLegacyMetadata::RAIL_STRAIGHT_NORTH_SOUTH => [ - Facing::NORTH, - Facing::SOUTH + Facing::NORTH->value, + Facing::SOUTH->value ], BlockLegacyMetadata::RAIL_STRAIGHT_EAST_WEST => [ - Facing::EAST, - Facing::WEST + Facing::EAST->value, + Facing::WEST->value ], //ascending BlockLegacyMetadata::RAIL_ASCENDING_EAST => [ - Facing::WEST, - Facing::EAST | self::FLAG_ASCEND + Facing::WEST->value, + Facing::EAST->value | self::FLAG_ASCEND ], BlockLegacyMetadata::RAIL_ASCENDING_WEST => [ - Facing::EAST, - Facing::WEST | self::FLAG_ASCEND + Facing::EAST->value, + Facing::WEST->value | self::FLAG_ASCEND ], BlockLegacyMetadata::RAIL_ASCENDING_NORTH => [ - Facing::SOUTH, - Facing::NORTH | self::FLAG_ASCEND + Facing::SOUTH->value, + Facing::NORTH->value | self::FLAG_ASCEND ], BlockLegacyMetadata::RAIL_ASCENDING_SOUTH => [ - Facing::NORTH, - Facing::SOUTH | self::FLAG_ASCEND + Facing::NORTH->value, + Facing::SOUTH->value | self::FLAG_ASCEND ] ]; /* extended meta values for regular rails, to allow curving */ public const CURVE_CONNECTIONS = [ BlockLegacyMetadata::RAIL_CURVE_SOUTHEAST => [ - Facing::SOUTH, - Facing::EAST + Facing::SOUTH->value, + Facing::EAST->value ], BlockLegacyMetadata::RAIL_CURVE_SOUTHWEST => [ - Facing::SOUTH, - Facing::WEST + Facing::SOUTH->value, + Facing::WEST->value ], BlockLegacyMetadata::RAIL_CURVE_NORTHWEST => [ - Facing::NORTH, - Facing::WEST + Facing::NORTH->value, + Facing::WEST->value ], BlockLegacyMetadata::RAIL_CURVE_NORTHEAST => [ - Facing::NORTH, - Facing::EAST + Facing::NORTH->value, + Facing::EAST->value ] ]; } diff --git a/src/block/utils/StaticSupportTrait.php b/src/block/utils/StaticSupportTrait.php index 0e40230cc..a725b74c3 100644 --- a/src/block/utils/StaticSupportTrait.php +++ b/src/block/utils/StaticSupportTrait.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block\utils; use pocketmine\block\Block; +use pocketmine\math\Facing; use pocketmine\math\Vector3; /** @@ -40,7 +41,7 @@ trait StaticSupportTrait{ /** * @see Block::canBePlacedAt() */ - public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, Facing $face, bool $isClickedBlock) : bool{ return $this->canBeSupportedAt($blockReplace) && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index f339ce3ef..cb42ff2e2 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -122,6 +122,7 @@ use pocketmine\data\bedrock\block\convert\property\ValueFromIntProperty; use pocketmine\data\bedrock\block\convert\property\ValueFromStringProperty; use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\data\bedrock\block\convert\property\ValueSetFromIntProperty; +use pocketmine\math\Axis; use pocketmine\math\Facing; use function array_map; use function min; @@ -584,13 +585,14 @@ final class VanillaBlockMappings{ new ValueSetFromIntProperty( StateNames::VINE_DIRECTION_BITS, IntFromRawStateMap::int([ - Facing::NORTH => BlockLegacyMetadata::VINE_FLAG_NORTH, - Facing::SOUTH => BlockLegacyMetadata::VINE_FLAG_SOUTH, - Facing::WEST => BlockLegacyMetadata::VINE_FLAG_WEST, - Facing::EAST => BlockLegacyMetadata::VINE_FLAG_EAST, + Facing::NORTH->value => BlockLegacyMetadata::VINE_FLAG_NORTH, + Facing::SOUTH->value => BlockLegacyMetadata::VINE_FLAG_SOUTH, + Facing::WEST->value => BlockLegacyMetadata::VINE_FLAG_WEST, + Facing::EAST->value => BlockLegacyMetadata::VINE_FLAG_EAST, ]), - fn(Vine $b) => $b->getFaces(), - fn(Vine $b, array $v) => $b->setFaces($v) + //TODO: hack for lack of HorizontalFacing enum :( + fn(Vine $b) => array_map(fn(Facing $facing) => $facing->value, $b->getFaces()), + fn(Vine $b, array $v) => $b->setFaces(array_map(Facing::from(...), $v)) ) ])); @@ -610,13 +612,15 @@ final class VanillaBlockMappings{ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL_FAN()) ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_fan"]) ->properties([ - new ValueFromIntProperty(StateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis, fn(FloorCoralFan $b) => $b->getAxis(), fn(FloorCoralFan $b, int $v) => $b->setAxis($v)) + //TODO: hack for lack of horizontal axis enum :( + new ValueFromIntProperty(StateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis, fn(FloorCoralFan $b) => $b->getAxis()->value, fn(FloorCoralFan $b, int $v) => $b->setAxis(Axis::from($v))) ]) ); $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::WALL_CORAL_FAN()) ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_wall_fan"]) ->properties([ - new ValueFromIntProperty(StateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, fn(HorizontalFacing $b) => $b->getFacing(), fn(HorizontalFacing $b, int $v) => $b->setFacing($v)), + //TODO: hack for lack of horizontal facing enum :( + new ValueFromIntProperty(StateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, fn(HorizontalFacing $b) => $b->getFacing()->value, fn(HorizontalFacing $b, int $v) => $b->setFacing(Facing::from($v))), ]) ); } @@ -752,7 +756,7 @@ final class VanillaBlockMappings{ new ValueFromStringProperty("id", ValueMappings::getInstance()->mobHeadType, fn(MobHead $b) => $b->getMobHeadType(), fn(MobHead $b, MobHeadType $v) => $b->setMobHeadType($v)), ]) ->properties([ - new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptDown, fn(MobHead $b) => $b->getFacing(), fn(MobHead $b, int $v) => $b->setFacing($v)) + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptDown, fn(MobHead $b) => $b->getFacing()->value, fn(MobHead $b, int $v) => $b->setFacing(Facing::from($v))) ]) ); @@ -1306,7 +1310,7 @@ final class VanillaBlockMappings{ $commonProperties->horizontalFacingCardinal ])); $reg->mapModel(Model::create(Blocks::END_ROD(), Ids::END_ROD)->properties([ - new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingEndRod, fn(EndRod $b) => $b->getFacing(), fn(EndRod $b, int $v) => $b->setFacing($v)), + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingEndRod, fn(EndRod $b) => $b->getFacing(), fn(EndRod $b, Facing $v) => $b->setFacing($v)), ])); //F @@ -1334,7 +1338,7 @@ final class VanillaBlockMappings{ $reg->mapModel(Model::create(Blocks::HOPPER(), Ids::HOPPER)->properties([ //kinda weird this doesn't use powered_bit? new BoolProperty(StateNames::TOGGLE_BIT, fn(PoweredByRedstone $b) => $b->isPowered(), fn(PoweredByRedstone $b, bool $v) => $b->setPowered($v)), - new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptUp, fn(Hopper $b) => $b->getFacing(), fn(Hopper $b, int $v) => $b->setFacing($v)), + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptUp, fn(Hopper $b) => $b->getFacing()->value, fn(Hopper $b, int $v) => $b->setFacing(Facing::from($v))), ])); //I @@ -1365,7 +1369,8 @@ final class VanillaBlockMappings{ new IntProperty(StateNames::AGE, 0, 3, fn(NetherWartPlant $b) => $b->getAge(), fn(NetherWartPlant $b, int $v) => $b->setAge($v)) ])); $reg->mapModel(Model::create(Blocks::NETHER_PORTAL(), Ids::PORTAL)->properties([ - new ValueFromStringProperty(StateNames::PORTAL_AXIS, ValueMappings::getInstance()->portalAxis, fn(NetherPortal $b) => $b->getAxis(), fn(NetherPortal $b, int $v) => $b->setAxis($v)) + //TODO: hack for lack of horizontal axis enum :( + new ValueFromStringProperty(StateNames::PORTAL_AXIS, ValueMappings::getInstance()->portalAxis, fn(NetherPortal $b) => $b->getAxis()->value, fn(NetherPortal $b, int $v) => $b->setAxis(Axis::from($v))) ])); //P diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php index 666637027..54618690b 100644 --- a/src/data/bedrock/block/convert/property/CommonProperties.php +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -55,15 +55,16 @@ use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateStringValues; +use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\utils\SingletonTrait; final class CommonProperties{ use SingletonTrait; - /** @phpstan-var ValueFromStringProperty */ + /** @phpstan-var ValueFromStringProperty */ public readonly ValueFromStringProperty $blockFace; - /** @phpstan-var ValueFromStringProperty */ + /** @phpstan-var ValueFromStringProperty */ public readonly ValueFromStringProperty $pillarAxis; /** @phpstan-var ValueFromStringProperty */ public readonly ValueFromStringProperty $torchFacing; @@ -77,10 +78,10 @@ final class CommonProperties{ /** @phpstan-var ValueFromIntProperty */ public readonly ValueFromIntProperty $horizontalFacingClassic; - /** @phpstan-var ValueFromIntProperty */ + /** @phpstan-var ValueFromIntProperty */ public readonly ValueFromIntProperty $anyFacingClassic; - /** @phpstan-var ValueSetFromIntProperty */ + /** @phpstan-var ValueSetFromIntProperty */ public readonly ValueSetFromIntProperty $multiFacingFlags; /** @phpstan-var IntProperty */ @@ -206,29 +207,30 @@ final class CommonProperties{ private function __construct(){ $vm = ValueMappings::getInstance(); - $hfGet = fn(HorizontalFacing $v) => $v->getFacing(); - $hfSet = fn(HorizontalFacing $v, int $facing) => $v->setFacing($facing); + //TODO: crude hack here - since we have no HorizontalFacing enum we need to use ints and convert to enum in the accessors + $hfGet = fn(HorizontalFacing $v) => $v->getFacing()->value; + $hfSet = fn(HorizontalFacing $v, int $facing) => $v->setFacing(Facing::from($facing)); $this->horizontalFacingCardinal = new ValueFromStringProperty(StateNames::MC_CARDINAL_DIRECTION, $vm->cardinalDirection, $hfGet, $hfSet); $this->blockFace = new ValueFromStringProperty( StateNames::MC_BLOCK_FACE, $vm->blockFace, fn(AnyFacing $b) => $b->getFacing(), - fn(AnyFacing $b, int $v) => $b->setFacing($v) + fn(AnyFacing $b, Facing $v) => $b->setFacing($v) ); $this->pillarAxis = new ValueFromStringProperty( StateNames::PILLAR_AXIS, $vm->pillarAxis, fn(PillarRotation $b) => $b->getAxis(), - fn(PillarRotation $b, int $v) => $b->setAxis($v) + fn(PillarRotation $b, Axis $v) => $b->setAxis($v) ); $this->torchFacing = new ValueFromStringProperty( StateNames::TORCH_FACING_DIRECTION, $vm->torchFacing, - fn(Torch $b) => $b->getFacing(), - fn(Torch $b, int $v) => $b->setFacing($v) + fn(Torch $b) => $b->getFacing()->value, + fn(Torch $b, int $v) => $b->setFacing(Facing::from($v)) ); $this->horizontalFacingSWNE = new ValueFromIntProperty(StateNames::DIRECTION, $vm->horizontalFacingSWNE, $hfGet, $hfSet); @@ -239,19 +241,19 @@ final class CommonProperties{ StateNames::FACING_DIRECTION, $vm->facing, fn(AnyFacing $b) => $b->getFacing(), - fn(AnyFacing $b, int $v) => $b->setFacing($v) + fn(AnyFacing $b, Facing $v) => $b->setFacing($v) ); $this->multiFacingFlags = new ValueSetFromIntProperty( StateNames::MULTI_FACE_DIRECTION_BITS, - IntFromRawStateMap::int([ + EnumFromRawStateMap::int(Facing::class, fn(Facing $case) => match ($case) { Facing::DOWN => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN, Facing::UP => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP, Facing::NORTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH, Facing::SOUTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH, Facing::WEST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST, Facing::EAST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST - ]), + }), fn(MultiAnyFacing $b) => $b->getFaces(), fn(MultiAnyFacing $b, array $v) => $b->setFaces($v) ); @@ -355,13 +357,13 @@ final class CommonProperties{ StateNames::MC_CARDINAL_DIRECTION, IntFromRawStateMap::string([ //a door facing "east" is actually facing north - thanks mojang - Facing::NORTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_EAST, - Facing::EAST => BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH, - Facing::SOUTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_WEST, - Facing::WEST => BlockStateStringValues::MC_CARDINAL_DIRECTION_NORTH + Facing::NORTH->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_EAST, + Facing::EAST->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH, + Facing::SOUTH->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_WEST, + Facing::WEST->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_NORTH ]), - fn(HorizontalFacing $b) => $b->getFacing(), - fn(HorizontalFacing $b, int $v) => $b->setFacing($v) + fn(HorizontalFacing $b) => $b->getFacing()->value, + fn(HorizontalFacing $b, int $v) => $b->setFacing(Facing::from($v)) ) ]; @@ -395,7 +397,7 @@ final class CommonProperties{ ]; $this->stemProperties = [ - new ValueFromIntProperty(StateNames::FACING_DIRECTION, $vm->facingStem, fn(Stem $b) => $b->getFacing(), fn(Stem $b, int $v) => $b->setFacing($v)), + new ValueFromIntProperty(StateNames::FACING_DIRECTION, $vm->facingStem, fn(Stem $b) => $b->getFacing()->value, fn(Stem $b, int $v) => $b->setFacing(Facing::from($v))), $this->cropAgeMax7 ]; @@ -411,11 +413,11 @@ final class CommonProperties{ new BoolProperty(StateNames::WALL_POST_BIT, fn(Wall $b) => $b->isPost(), fn(Wall $b, bool $v) => $b->setPost($v)), ]; foreach([ - Facing::NORTH => StateNames::WALL_CONNECTION_TYPE_NORTH, - Facing::SOUTH => StateNames::WALL_CONNECTION_TYPE_SOUTH, - Facing::WEST => StateNames::WALL_CONNECTION_TYPE_WEST, - Facing::EAST => StateNames::WALL_CONNECTION_TYPE_EAST - ] as $facing => $stateName){ + [Facing::NORTH, StateNames::WALL_CONNECTION_TYPE_NORTH], + [Facing::SOUTH, StateNames::WALL_CONNECTION_TYPE_SOUTH], + [Facing::WEST, StateNames::WALL_CONNECTION_TYPE_WEST], + [Facing::EAST, StateNames::WALL_CONNECTION_TYPE_EAST] + ] as [$facing, $stateName]){ $wallProperties[] = new ValueFromStringProperty( $stateName, EnumFromRawStateMap::string(WallConnectionTypeShim::class, fn(WallConnectionTypeShim $case) => $case->getValue()), diff --git a/src/data/bedrock/block/convert/property/ValueMappings.php b/src/data/bedrock/block/convert/property/ValueMappings.php index 22e9803a5..f51c4fc5b 100644 --- a/src/data/bedrock/block/convert/property/ValueMappings.php +++ b/src/data/bedrock/block/convert/property/ValueMappings.php @@ -65,10 +65,10 @@ final class ValueMappings{ /** @phpstan-var IntFromRawStateMap */ public readonly IntFromRawStateMap $cardinalDirection; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $blockFace; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $pillarAxis; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $blockFace; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $pillarAxis; /** @phpstan-var IntFromRawStateMap */ public readonly IntFromRawStateMap $torchFacing; /** @phpstan-var IntFromRawStateMap */ @@ -86,10 +86,10 @@ final class ValueMappings{ public readonly IntFromRawStateMap $horizontalFacingCoral; /** @phpstan-var IntFromRawStateMap */ public readonly IntFromRawStateMap $horizontalFacingClassic; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $facing; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $facingEndRod; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $facing; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $facingEndRod; /** @phpstan-var IntFromRawStateMap */ public readonly IntFromRawStateMap $coralAxis; @@ -190,40 +190,41 @@ final class ValueMappings{ } ); + //TODO: this can't use EnumFromRawStateMap until we have a dedicated HorizontalFacing enum $this->cardinalDirection = IntFromRawStateMap::string([ - Facing::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH, - Facing::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH, - Facing::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST, - Facing::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST, + Facing::NORTH->value => StringValues::MC_CARDINAL_DIRECTION_NORTH, + Facing::SOUTH->value => StringValues::MC_CARDINAL_DIRECTION_SOUTH, + Facing::WEST->value => StringValues::MC_CARDINAL_DIRECTION_WEST, + Facing::EAST->value => StringValues::MC_CARDINAL_DIRECTION_EAST, ]); - $this->blockFace = IntFromRawStateMap::string([ + $this->blockFace = EnumFromRawStateMap::string(Facing::class, fn(Facing $case) => match ($case) { Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN, Facing::UP => StringValues::MC_BLOCK_FACE_UP, Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH, Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH, Facing::WEST => StringValues::MC_BLOCK_FACE_WEST, Facing::EAST => StringValues::MC_BLOCK_FACE_EAST, - ]); - $this->pillarAxis = IntFromRawStateMap::string([ + }); + $this->pillarAxis = EnumFromRawStateMap::string(Axis::class, fn(Axis $case) => match ($case) { Axis::X => StringValues::PILLAR_AXIS_X, Axis::Y => StringValues::PILLAR_AXIS_Y, Axis::Z => StringValues::PILLAR_AXIS_Z - ]); + }); $this->torchFacing = IntFromRawStateMap::string([ //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) - Facing::WEST => StringValues::TORCH_FACING_DIRECTION_EAST, - Facing::SOUTH => StringValues::TORCH_FACING_DIRECTION_NORTH, - Facing::NORTH => StringValues::TORCH_FACING_DIRECTION_SOUTH, - Facing::UP => StringValues::TORCH_FACING_DIRECTION_TOP, - Facing::EAST => StringValues::TORCH_FACING_DIRECTION_WEST, + Facing::WEST->value => StringValues::TORCH_FACING_DIRECTION_EAST, + Facing::SOUTH->value => StringValues::TORCH_FACING_DIRECTION_NORTH, + Facing::NORTH->value => StringValues::TORCH_FACING_DIRECTION_SOUTH, + Facing::UP->value => StringValues::TORCH_FACING_DIRECTION_TOP, + Facing::EAST->value => StringValues::TORCH_FACING_DIRECTION_WEST, ], deserializeAliases: [ - Facing::UP => StringValues::TORCH_FACING_DIRECTION_UNKNOWN //should be illegal, but still supported + Facing::UP->value => StringValues::TORCH_FACING_DIRECTION_UNKNOWN //should be illegal, but still supported ]); $this->portalAxis = IntFromRawStateMap::string([ - Axis::X => StringValues::PORTAL_AXIS_X, - Axis::Z => StringValues::PORTAL_AXIS_Z, + Axis::X->value => StringValues::PORTAL_AXIS_X, + Axis::Z->value => StringValues::PORTAL_AXIS_Z, ], deserializeAliases: [ - Axis::X => StringValues::PORTAL_AXIS_UNKNOWN, + Axis::X->value => StringValues::PORTAL_AXIS_UNKNOWN, ]); $this->bambooLeafSize = IntFromRawStateMap::string([ Bamboo::NO_LEAVES => StringValues::BAMBOO_LEAF_SIZE_NO_LEAVES, @@ -232,74 +233,78 @@ final class ValueMappings{ ]); $this->horizontalFacing5Minus = IntFromRawStateMap::int([ - Facing::EAST => 0, - Facing::WEST => 1, - Facing::SOUTH => 2, - Facing::NORTH => 3 + Facing::EAST->value => 0, + Facing::WEST->value => 1, + Facing::SOUTH->value => 2, + Facing::NORTH->value => 3 ]); $this->horizontalFacingSWNE = IntFromRawStateMap::int([ - Facing::SOUTH => 0, - Facing::WEST => 1, - Facing::NORTH => 2, - Facing::EAST => 3 + Facing::SOUTH->value => 0, + Facing::WEST->value => 1, + Facing::NORTH->value => 2, + Facing::EAST->value => 3 ]); $this->horizontalFacingSWNEInverted = IntFromRawStateMap::int([ - Facing::NORTH => 0, - Facing::EAST => 1, - Facing::SOUTH => 2, - Facing::WEST => 3, + Facing::NORTH->value => 0, + Facing::EAST->value => 1, + Facing::SOUTH->value => 2, + Facing::WEST->value => 3, ]); $this->horizontalFacingCoral = IntFromRawStateMap::int([ - Facing::WEST => 0, - Facing::EAST => 1, - Facing::NORTH => 2, - Facing::SOUTH => 3 + Facing::WEST->value => 0, + Facing::EAST->value => 1, + Facing::NORTH->value => 2, + Facing::SOUTH->value => 3 ]); $horizontalFacingClassicTable = [ + Facing::NORTH->value => 2, + Facing::SOUTH->value => 3, + Facing::WEST->value => 4, + Facing::EAST->value => 5 + ]; + $this->horizontalFacingClassic = IntFromRawStateMap::int($horizontalFacingClassicTable, deserializeAliases: [ + Facing::NORTH->value => [0, 1] //should be illegal but still technically possible + ]); + + $this->facing = EnumFromRawStateMap::int(Facing::class, fn(Facing $case) => match ($case) { + Facing::DOWN => 0, + Facing::UP => 1, Facing::NORTH => 2, Facing::SOUTH => 3, Facing::WEST => 4, Facing::EAST => 5 - ]; - $this->horizontalFacingClassic = IntFromRawStateMap::int($horizontalFacingClassicTable, deserializeAliases: [ - Facing::NORTH => [0, 1] //should be illegal but still technically possible - ]); - - $this->facing = IntFromRawStateMap::int([ - Facing::DOWN => 0, - Facing::UP => 1 - ] + $horizontalFacingClassicTable); + }); //end rods have all the horizontal facing values opposite to classic facing - $this->facingEndRod = IntFromRawStateMap::int([ + $this->facingEndRod = EnumFromRawStateMap::int(Facing::class, fn(Facing $case) => match ($case) { Facing::DOWN => 0, Facing::UP => 1, Facing::SOUTH => 2, Facing::NORTH => 3, Facing::EAST => 4, Facing::WEST => 5, - ]); + }); $this->coralAxis = IntFromRawStateMap::int([ - Axis::X => 0, - Axis::Z => 1, + Axis::X->value => 0, + Axis::Z->value => 1, ]); //TODO: shitty copy pasta job, we can do this better but this is good enough for now $this->facingExceptDown = IntFromRawStateMap::int( - [Facing::UP => 1] + $horizontalFacingClassicTable, - deserializeAliases: [Facing::UP => 0]); + [Facing::UP->value => 1] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP->value => 0]); $this->facingExceptUp = IntFromRawStateMap::int( - [Facing::DOWN => 0] + $horizontalFacingClassicTable, - deserializeAliases: [Facing::DOWN => 1] + [Facing::DOWN->value => 0] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::DOWN->value => 1] ); //In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the //most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which //is absurd, and I refuse to make our API similarly absurd. $this->facingStem = IntFromRawStateMap::int( - [Facing::UP => 0] + $horizontalFacingClassicTable, - deserializeAliases: [Facing::UP => 1] + [Facing::UP->value => 0] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP->value => 1] ); } } diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 8df675f70..b06133022 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\data\runtime; use pocketmine\block\utils\WallConnectionType; +use pocketmine\math\Axis; use pocketmine\math\Facing; /** @@ -45,29 +46,25 @@ interface RuntimeDataDescriber{ public function bool(bool &$value) : void; - public function horizontalFacing(int &$facing) : void; + public function horizontalFacing(Facing &$facing) : void; /** - * @param int[] $faces + * @param Facing[] $faces */ public function facingFlags(array &$faces) : void; /** - * @param int[] $faces + * @param Facing[] $faces */ public function horizontalFacingFlags(array &$faces) : void; - public function facing(int &$facing) : void; + public function facingExcept(Facing &$facing, Facing $except) : void; - public function facingExcept(int &$facing, int $except) : void; - - public function axis(int &$axis) : void; - - public function horizontalAxis(int &$axis) : void; + public function horizontalAxis(Axis &$axis) : void; /** * @param WallConnectionType[] $connections - * @phpstan-param array $connections + * @phpstan-param array, WallConnectionType> $connections */ public function wallConnections(array &$connections) : void; diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php index c230a52ae..64e5e0f80 100644 --- a/src/data/runtime/RuntimeDataReader.php +++ b/src/data/runtime/RuntimeDataReader.php @@ -77,7 +77,7 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $value = $this->readBool(); } - public function horizontalFacing(int &$facing) : void{ + public function horizontalFacing(Facing &$facing) : void{ $facing = match($this->readInt(2)){ 0 => Facing::NORTH, 1 => Facing::EAST, @@ -88,13 +88,13 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ } /** - * @param int[] $faces + * @param Facing[] $faces */ public function facingFlags(array &$faces) : void{ $result = []; foreach(Facing::ALL as $facing){ if($this->readBool()){ - $result[$facing] = $facing; + $result[$facing->value] = $facing; } } @@ -102,34 +102,22 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ } /** - * @param int[] $faces + * @param Facing[] $faces */ public function horizontalFacingFlags(array &$faces) : void{ $result = []; foreach(Facing::HORIZONTAL as $facing){ if($this->readBool()){ - $result[$facing] = $facing; + $result[$facing->value] = $facing; } } $faces = $result; } - public function facing(int &$facing) : void{ - $facing = match($this->readInt(3)){ - 0 => Facing::DOWN, - 1 => Facing::UP, - 2 => Facing::NORTH, - 3 => Facing::SOUTH, - 4 => Facing::WEST, - 5 => Facing::EAST, - default => throw new InvalidSerializedRuntimeDataException("Invalid facing value") - }; - } - - public function facingExcept(int &$facing, int $except) : void{ - $result = 0; - $this->facing($result); + public function facingExcept(Facing &$facing, Facing $except) : void{ + $result = Facing::DOWN; + $this->enum($result); if($result === $except){ throw new InvalidSerializedRuntimeDataException("Illegal facing value"); } @@ -137,16 +125,7 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $facing = $result; } - public function axis(int &$axis) : void{ - $axis = match($this->readInt(2)){ - 0 => Axis::X, - 1 => Axis::Z, - 2 => Axis::Y, - default => throw new InvalidSerializedRuntimeDataException("Invalid axis value") - }; - } - - public function horizontalAxis(int &$axis) : void{ + public function horizontalAxis(Axis &$axis) : void{ $axis = match($this->readInt(1)){ 0 => Axis::X, 1 => Axis::Z, @@ -156,7 +135,7 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ /** * @param WallConnectionType[] $connections - * @phpstan-param array $connections + * @phpstan-param array, WallConnectionType> $connections */ public function wallConnections(array &$connections) : void{ $result = []; @@ -165,7 +144,7 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ foreach(Facing::HORIZONTAL as $facing){ $type = intdiv($packed, (3 ** $offset)) % 3; if($type !== 0){ - $result[$facing] = match($type){ + $result[$facing->value] = match($type){ 1 => WallConnectionType::SHORT, 2 => WallConnectionType::TALL, default => throw new AssumptionFailedError("Unreachable") diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php index 6725aace6..213279cb1 100644 --- a/src/data/runtime/RuntimeDataSizeCalculator.php +++ b/src/data/runtime/RuntimeDataSizeCalculator.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\runtime; +use pocketmine\math\Axis; use pocketmine\math\Facing; use function count; use function log; @@ -50,31 +51,23 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ $this->addBits(1); } - public function horizontalFacing(int &$facing) : void{ + public function horizontalFacing(Facing &$facing) : void{ $this->addBits(2); } public function facingFlags(array &$faces) : void{ - $this->addBits(count(Facing::ALL)); + $this->addBits(count(Facing::cases())); } public function horizontalFacingFlags(array &$faces) : void{ $this->addBits(count(Facing::HORIZONTAL)); } - public function facing(int &$facing) : void{ - $this->addBits(3); + public function facingExcept(Facing &$facing, Facing $except) : void{ + $this->enum($facing); } - public function facingExcept(int &$facing, int $except) : void{ - $this->facing($facing); - } - - public function axis(int &$axis) : void{ - $this->addBits(2); - } - - public function horizontalAxis(int &$axis) : void{ + public function horizontalAxis(Axis &$axis) : void{ $this->addBits(1); } diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php index 382aa9bf6..c36a436d2 100644 --- a/src/data/runtime/RuntimeDataWriter.php +++ b/src/data/runtime/RuntimeDataWriter.php @@ -26,7 +26,6 @@ namespace pocketmine\data\runtime; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; -use function array_flip; use function log; use function spl_object_id; @@ -74,78 +73,63 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->writeBool($value); } - public function horizontalFacing(int &$facing) : void{ + public function horizontalFacing(Facing &$facing) : void{ $this->writeInt(2, match($facing){ Facing::NORTH => 0, Facing::EAST => 1, Facing::SOUTH => 2, Facing::WEST => 3, - default => throw new \InvalidArgumentException("Invalid horizontal facing $facing") + default => throw new \InvalidArgumentException("Invalid horizontal facing $facing->name") }); } /** - * @param int[] $faces + * @param Facing[] $faces */ public function facingFlags(array &$faces) : void{ - $uniqueFaces = array_flip($faces); + $uniqueFaces = []; + foreach($faces as $face){ + $uniqueFaces[$face->value] = true; + } foreach(Facing::ALL as $facing){ - $this->writeBool(isset($uniqueFaces[$facing])); + $this->writeBool(isset($uniqueFaces[$facing->value])); } } /** - * @param int[] $faces + * @param Facing[] $faces */ public function horizontalFacingFlags(array &$faces) : void{ - $uniqueFaces = array_flip($faces); + $uniqueFaces = []; + foreach($faces as $face){ + $uniqueFaces[$face->value] = true; + } foreach(Facing::HORIZONTAL as $facing){ - $this->writeBool(isset($uniqueFaces[$facing])); + $this->writeBool(isset($uniqueFaces[$facing->value])); } } - public function facing(int &$facing) : void{ - $this->writeInt(3, match($facing){ - 0 => Facing::DOWN, - 1 => Facing::UP, - 2 => Facing::NORTH, - 3 => Facing::SOUTH, - 4 => Facing::WEST, - 5 => Facing::EAST, - default => throw new \InvalidArgumentException("Invalid facing $facing") - }); + public function facingExcept(Facing &$facing, Facing $except) : void{ + $this->enum($facing); } - public function facingExcept(int &$facing, int $except) : void{ - $this->facing($facing); - } - - public function axis(int &$axis) : void{ - $this->writeInt(2, match($axis){ - Axis::X => 0, - Axis::Z => 1, - Axis::Y => 2, - default => throw new \InvalidArgumentException("Invalid axis $axis") - }); - } - - public function horizontalAxis(int &$axis) : void{ + public function horizontalAxis(Axis &$axis) : void{ $this->writeInt(1, match($axis){ Axis::X => 0, Axis::Z => 1, - default => throw new \InvalidArgumentException("Invalid horizontal axis $axis") + default => throw new \InvalidArgumentException("Invalid horizontal axis $axis->name") }); } /** * @param WallConnectionType[] $connections - * @phpstan-param array $connections + * @phpstan-param array, WallConnectionType> $connections */ public function wallConnections(array &$connections) : void{ $packed = 0; $offset = 0; foreach(Facing::HORIZONTAL as $facing){ - $packed += match($connections[$facing] ?? null){ + $packed += match($connections[$facing->value] ?? null){ null => 0, WallConnectionType::SHORT => 1, WallConnectionType::TALL => 2, diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 18cd75306..582df4b94 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -939,7 +939,7 @@ abstract class Entity{ return false; } - public function getHorizontalFacing() : int{ + public function getHorizontalFacing() : Facing{ $angle = fmod($this->location->yaw, 360); if($angle < 0){ $angle += 360.0; @@ -1161,7 +1161,7 @@ abstract class Entity{ $wantedZ = $dz; if($this->keepMovement){ - $this->boundingBox->offset($dx, $dy, $dz); + $this->boundingBox = $this->boundingBox->offsetCopy($dx, $dy, $dz); }else{ $this->ySize *= self::STEP_CLIP_MULTIPLIER; @@ -1175,7 +1175,7 @@ abstract class Entity{ $dy = $bb->calculateYOffset($moveBB, $dy); } - $moveBB->offset(0, $dy, 0); + $moveBB = $moveBB->offsetCopy(0, $dy, 0); $fallingFlag = ($this->onGround || ($dy !== $wantedY && $wantedY < 0)); @@ -1183,13 +1183,13 @@ abstract class Entity{ $dx = $bb->calculateXOffset($moveBB, $dx); } - $moveBB->offset($dx, 0, 0); + $moveBB = $moveBB->offsetCopy($dx, 0, 0); foreach($list as $bb){ $dz = $bb->calculateZOffset($moveBB, $dz); } - $moveBB->offset(0, 0, $dz); + $moveBB = $moveBB->offsetCopy(0, 0, $dz); $stepHeight = $this->getStepHeight(); @@ -1208,26 +1208,26 @@ abstract class Entity{ $dy = $bb->calculateYOffset($stepBB, $dy); } - $stepBB->offset(0, $dy, 0); + $stepBB = $stepBB->offsetCopy(0, $dy, 0); foreach($list as $bb){ $dx = $bb->calculateXOffset($stepBB, $dx); } - $stepBB->offset($dx, 0, 0); + $stepBB = $stepBB->offsetCopy($dx, 0, 0); foreach($list as $bb){ $dz = $bb->calculateZOffset($stepBB, $dz); } - $stepBB->offset(0, 0, $dz); + $stepBB = $stepBB->offsetCopy(0, 0, $dz); $reverseDY = -$dy; foreach($list as $bb){ $reverseDY = $bb->calculateYOffset($stepBB, $reverseDY); } $dy += $reverseDY; - $stepBB->offset(0, $reverseDY, 0); + $stepBB = $stepBB->offsetCopy(0, $reverseDY, 0); if(($cx ** 2 + $cz ** 2) >= ($dx ** 2 + $dz ** 2)){ $dx = $cx; @@ -1401,8 +1401,15 @@ abstract class Entity{ public function setRotation(float $yaw, float $pitch) : void{ Utils::checkFloatNotInfOrNaN("yaw", $yaw); Utils::checkFloatNotInfOrNaN("pitch", $pitch); - $this->location->yaw = $yaw; - $this->location->pitch = $pitch; + //TODO: maybe it's time to think about pulling rotation into a separate structure? + $this->location = new Location( + $this->location->x, + $this->location->y, + $this->location->z, + $this->location->world, + $yaw, + $pitch + ); $this->scheduleUpdate(); } diff --git a/src/entity/Living.php b/src/entity/Living.php index 6d62c85d2..888567c08 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -741,7 +741,7 @@ abstract class Living extends Entity{ if( !$block->isSameState($liquid) || $world->getBlockAt($x, $y + 1, $z)->getTypeId() !== BlockTypeIds::AIR || - count($world->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z))) !== 0 + count($world->getNearbyEntities(AxisAlignedBB::one()->offsetCopy($x, $y, $z))) !== 0 ){ continue; } diff --git a/src/entity/Location.php b/src/entity/Location.php index d9c101882..cfd3ebc01 100644 --- a/src/entity/Location.php +++ b/src/entity/Location.php @@ -27,7 +27,7 @@ use pocketmine\math\Vector3; use pocketmine\world\Position; use pocketmine\world\World; -class Location extends Position{ +readonly class Location extends Position{ public float $yaw; public float $pitch; diff --git a/src/entity/object/Painting.php b/src/entity/object/Painting.php index 78a006d77..f09dcc231 100644 --- a/src/entity/object/Painting.php +++ b/src/entity/object/Painting.php @@ -58,17 +58,17 @@ class Painting extends Entity{ 3 => Facing::EAST ]; private const FACING_TO_DATA = [ - Facing::SOUTH => 0, - Facing::WEST => 1, - Facing::NORTH => 2, - Facing::EAST => 3 + Facing::SOUTH->value => 0, + Facing::WEST->value => 1, + Facing::NORTH->value => 2, + Facing::EAST->value => 3 ]; protected Vector3 $blockIn; - protected int $facing; + protected Facing $facing; protected PaintingMotive $motive; - public function __construct(Location $location, Vector3 $blockIn, int $facing, PaintingMotive $motive, ?CompoundTag $nbt = null){ + public function __construct(Location $location, Vector3 $blockIn, Facing $facing, PaintingMotive $motive, ?CompoundTag $nbt = null){ $this->motive = $motive; $this->blockIn = $blockIn->asVector3(); $this->facing = $facing; @@ -96,8 +96,8 @@ class Painting extends Entity{ $nbt->setInt(self::TAG_TILE_Y, (int) $this->blockIn->y); $nbt->setInt(self::TAG_TILE_Z, (int) $this->blockIn->z); - $nbt->setByte(self::TAG_FACING_JE, self::FACING_TO_DATA[$this->facing]); - $nbt->setByte(self::TAG_DIRECTION_BE, self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility + $nbt->setByte(self::TAG_FACING_JE, self::FACING_TO_DATA[$this->facing->value]); + $nbt->setByte(self::TAG_DIRECTION_BE, self::FACING_TO_DATA[$this->facing->value]); //Save both for full compatibility $nbt->setString(self::TAG_MOTIVE, $this->motive->getName()); @@ -125,7 +125,7 @@ class Painting extends Entity{ protected function recalculateBoundingBox() : void{ $side = $this->blockIn->getSide($this->facing); - $this->boundingBox = self::getPaintingBB($this->facing, $this->getMotive())->offset($side->x, $side->y, $side->z); + $this->boundingBox = self::getPaintingBB($this->facing, $this->getMotive())->offsetCopy($side->x, $side->y, $side->z); } public function onNearbyBlockChange() : void{ @@ -161,7 +161,7 @@ class Painting extends Entity{ ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2 ), - self::FACING_TO_DATA[$this->facing], + self::FACING_TO_DATA[$this->facing->value], $this->motive->getName() )); } @@ -177,14 +177,14 @@ class Painting extends Entity{ return $this->motive; } - public function getFacing() : int{ + public function getFacing() : Facing{ return $this->facing; } /** * Returns the bounding-box a painting with the specified motive would have at the given position and direction. */ - private static function getPaintingBB(int $facing, PaintingMotive $motive) : AxisAlignedBB{ + private static function getPaintingBB(Facing $facing, PaintingMotive $motive) : AxisAlignedBB{ $width = $motive->getWidth(); $height = $motive->getHeight(); @@ -192,17 +192,17 @@ class Painting extends Entity{ $verticalStart = (int) (ceil($height / 2) - 1); return AxisAlignedBB::one() - ->trim($facing, 15 / 16) - ->extend(Facing::rotateY($facing, true), $horizontalStart) - ->extend(Facing::rotateY($facing, false), -$horizontalStart + $width - 1) - ->extend(Facing::DOWN, $verticalStart) - ->extend(Facing::UP, -$verticalStart + $height - 1); + ->trimmedCopy($facing, 15 / 16) + ->extendedCopy(Facing::rotateY($facing, true), $horizontalStart) + ->extendedCopy(Facing::rotateY($facing, false), -$horizontalStart + $width - 1) + ->extendedCopy(Facing::DOWN, $verticalStart) + ->extendedCopy(Facing::UP, -$verticalStart + $height - 1); } /** * Returns whether a painting with the specified motive can be placed at the given position. */ - public static function canFit(World $world, Vector3 $blockIn, int $facing, bool $checkOverlap, PaintingMotive $motive) : bool{ + public static function canFit(World $world, Vector3 $blockIn, Facing $facing, bool $checkOverlap, PaintingMotive $motive) : bool{ $width = $motive->getWidth(); $height = $motive->getHeight(); @@ -227,7 +227,7 @@ class Painting extends Entity{ } if($checkOverlap){ - $bb = self::getPaintingBB($facing, $motive)->offset($blockIn->x, $blockIn->y, $blockIn->z); + $bb = self::getPaintingBB($facing, $motive)->offsetCopy($blockIn->x, $blockIn->y, $blockIn->z); foreach($world->getNearbyEntities($bb) as $entity){ if($entity instanceof self){ diff --git a/src/entity/projectile/IceBomb.php b/src/entity/projectile/IceBomb.php index 5a3525c74..db4393a52 100644 --- a/src/entity/projectile/IceBomb.php +++ b/src/entity/projectile/IceBomb.php @@ -46,7 +46,7 @@ class IceBomb extends Throwable{ if($block->getTypeId() === BlockTypeIds::WATER){ $pos = $block->getPosition(); - return AxisAlignedBB::one()->offset($pos->x, $pos->y, $pos->z)->calculateIntercept($start, $end); + return AxisAlignedBB::one()->offsetCopy($pos->x, $pos->y, $pos->z)->calculateIntercept($start, $end); } return parent::calculateInterceptWithBlock($block, $start, $end); diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index 68b6c4763..a6735b3fe 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -186,7 +186,7 @@ abstract class Projectile extends Entity{ $entityDistance = PHP_INT_MAX; $newDiff = $end->subtractVector($start); - foreach($world->getCollidingEntities($this->boundingBox->addCoord($newDiff->x, $newDiff->y, $newDiff->z)->expand(1, 1, 1), $this) as $entity){ + foreach($world->getCollidingEntities($this->boundingBox->addCoord($newDiff->x, $newDiff->y, $newDiff->z)->expandedCopy(1, 1, 1), $this) as $entity){ if($entity->getId() === $this->getOwningEntityId() && $this->ticksLived < 5){ continue; } diff --git a/src/event/player/PlayerBucketEvent.php b/src/event/player/PlayerBucketEvent.php index 30a55a472..d80de0dce 100644 --- a/src/event/player/PlayerBucketEvent.php +++ b/src/event/player/PlayerBucketEvent.php @@ -27,6 +27,7 @@ use pocketmine\block\Block; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\player\Player; /** @@ -38,7 +39,7 @@ abstract class PlayerBucketEvent extends PlayerEvent implements Cancellable{ public function __construct( Player $who, private Block $blockClicked, - private int $blockFace, + private Facing $blockFace, private Item $bucket, private Item $itemInHand ){ @@ -67,7 +68,7 @@ abstract class PlayerBucketEvent extends PlayerEvent implements Cancellable{ return $this->blockClicked; } - public function getBlockFace() : int{ + public function getBlockFace() : Facing{ return $this->blockFace; } } diff --git a/src/event/player/PlayerInteractEvent.php b/src/event/player/PlayerInteractEvent.php index 46daf7081..395e95b53 100644 --- a/src/event/player/PlayerInteractEvent.php +++ b/src/event/player/PlayerInteractEvent.php @@ -27,6 +27,7 @@ use pocketmine\block\Block; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -50,7 +51,7 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{ protected Item $item, protected Block $blockTouched, ?Vector3 $touchVector, - protected int $blockFace, + protected Facing $blockFace, protected int $action = PlayerInteractEvent::RIGHT_CLICK_BLOCK ){ $this->player = $player; @@ -73,7 +74,7 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{ return $this->touchVector; } - public function getFace() : int{ + public function getFace() : Facing{ return $this->blockFace; } diff --git a/src/item/Bamboo.php b/src/item/Bamboo.php index d928000e0..4247f5015 100644 --- a/src/item/Bamboo.php +++ b/src/item/Bamboo.php @@ -25,6 +25,7 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; final class Bamboo extends Item{ @@ -32,7 +33,7 @@ final class Bamboo extends Item{ return 50; } - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::BAMBOO_SAPLING(); } } diff --git a/src/item/BeetrootSeeds.php b/src/item/BeetrootSeeds.php index 64c140086..f4be6f591 100644 --- a/src/item/BeetrootSeeds.php +++ b/src/item/BeetrootSeeds.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class BeetrootSeeds extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::BEETROOTS(); } } diff --git a/src/item/Bucket.php b/src/item/Bucket.php index ee69a0a8a..0fce40ef6 100644 --- a/src/item/Bucket.php +++ b/src/item/Bucket.php @@ -28,6 +28,7 @@ use pocketmine\block\BlockTypeIds; use pocketmine\block\Liquid; use pocketmine\block\VanillaBlocks; use pocketmine\event\player\PlayerBucketFillEvent; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -37,7 +38,7 @@ class Bucket extends Item{ return 16; } - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ //TODO: move this to generic placement logic if($blockClicked instanceof Liquid && $blockClicked->isSource()){ $stack = clone $this; diff --git a/src/item/Carrot.php b/src/item/Carrot.php index fd4a93f2f..b55e0167d 100644 --- a/src/item/Carrot.php +++ b/src/item/Carrot.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class Carrot extends Food{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::CARROTS(); } diff --git a/src/item/CocoaBeans.php b/src/item/CocoaBeans.php index 57053cd8d..fb7be46fe 100644 --- a/src/item/CocoaBeans.php +++ b/src/item/CocoaBeans.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class CocoaBeans extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::COCOA_POD(); } } diff --git a/src/item/CoralFan.php b/src/item/CoralFan.php index 7fdfc9114..a5cb6b774 100644 --- a/src/item/CoralFan.php +++ b/src/item/CoralFan.php @@ -46,7 +46,7 @@ final class CoralFan extends Item{ $this->encodeCoralType($w); } - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ $block = $clickedFace !== null && Facing::axis($clickedFace) !== Axis::Y ? VanillaBlocks::WALL_CORAL_FAN() : VanillaBlocks::CORAL_FAN(); return $block->setCoralType($this->coralType)->setDead($this->dead); diff --git a/src/item/EndCrystal.php b/src/item/EndCrystal.php index 320d657e6..e39d16ae0 100644 --- a/src/item/EndCrystal.php +++ b/src/item/EndCrystal.php @@ -35,13 +35,13 @@ use function count; class EndCrystal extends Item{ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if($blockClicked->getTypeId() === BlockTypeIds::OBSIDIAN || $blockClicked->getTypeId() === BlockTypeIds::BEDROCK){ $pos = $blockClicked->getPosition(); $world = $pos->getWorld(); $bb = AxisAlignedBB::one() - ->offset($pos->getX(), $pos->getY(), $pos->getZ()) - ->extend(Facing::UP, 1); + ->offsetCopy($pos->getX(), $pos->getY(), $pos->getZ()) + ->extendedCopy(Facing::UP, 1); if( count($world->getNearbyEntities($bb)) === 0 && $blockClicked->getSide(Facing::UP)->getTypeId() === BlockTypeIds::AIR && diff --git a/src/item/FireCharge.php b/src/item/FireCharge.php index a612e0897..56b192d99 100644 --- a/src/item/FireCharge.php +++ b/src/item/FireCharge.php @@ -26,13 +26,14 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\BlockTypeIds; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\BlazeShootSound; class FireCharge extends Item{ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if($blockReplace->getTypeId() === BlockTypeIds::AIR){ $world = $player->getWorld(); $world->setBlock($blockReplace->getPosition(), VanillaBlocks::FIRE()); diff --git a/src/item/FlintSteel.php b/src/item/FlintSteel.php index 3e694eb0d..8345a8b19 100644 --- a/src/item/FlintSteel.php +++ b/src/item/FlintSteel.php @@ -26,13 +26,14 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\BlockTypeIds; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\FlintSteelSound; class FlintSteel extends Tool{ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if($blockReplace->getTypeId() === BlockTypeIds::AIR){ $world = $player->getWorld(); $world->setBlock($blockReplace->getPosition(), VanillaBlocks::FIRE()); diff --git a/src/item/GlassBottle.php b/src/item/GlassBottle.php index c638b109f..38a1e0ba1 100644 --- a/src/item/GlassBottle.php +++ b/src/item/GlassBottle.php @@ -25,12 +25,13 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\BlockTypeIds; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; class GlassBottle extends Item{ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if($blockClicked->getTypeId() === BlockTypeIds::WATER){ $this->pop(); $returnedItems[] = VanillaItems::POTION()->setType(PotionType::WATER); diff --git a/src/item/GlowBerries.php b/src/item/GlowBerries.php index e959b7b9e..60d1626b3 100644 --- a/src/item/GlowBerries.php +++ b/src/item/GlowBerries.php @@ -25,6 +25,7 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class GlowBerries extends Food{ @@ -36,7 +37,7 @@ class GlowBerries extends Food{ return 0.4; } - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::CAVE_VINES(); } } diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php index a6752087a..58c58d627 100644 --- a/src/item/HangingSign.php +++ b/src/item/HangingSign.php @@ -41,7 +41,7 @@ final class HangingSign extends Item{ parent::__construct($identifier, $name); } - public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ + public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ if($face !== Facing::DOWN){ return $this->tryPlacementTransaction(clone $this->wallVariant, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -52,7 +52,7 @@ final class HangingSign extends Item{ return $ceilingEdgeTx ?? $this->tryPlacementTransaction(clone $this->centerPointCeilingVariant, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ //we don't have enough information here to decide which ceiling type to use return $clickedFace === Facing::DOWN ? clone $this->centerPointCeilingVariant : clone $this->wallVariant; } diff --git a/src/item/Item.php b/src/item/Item.php index 1a9202fc8..d9b9d835b 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -38,6 +38,7 @@ use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\Entity; use pocketmine\entity\Living; use pocketmine\item\enchantment\EnchantmentInstance; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NBT; @@ -489,7 +490,7 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } - protected final function tryPlacementTransaction(Block $blockPlace, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player) : ?BlockTransaction{ + protected final function tryPlacementTransaction(Block $blockPlace, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player) : ?BlockTransaction{ $position = $blockReplace->getPosition(); $blockPlace->position($position->getWorld(), $position->getFloorX(), $position->getFloorY(), $position->getFloorZ()); if(!$blockPlace->canBePlacedAt($blockReplace, $clickVector, $face, $blockReplace->getPosition()->equals($blockClicked->getPosition()))){ @@ -499,14 +500,14 @@ class Item implements \JsonSerializable{ return $blockPlace->place($transaction, $this, $blockReplace, $blockClicked, $face, $clickVector, $player) ? $transaction : null; } - public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ + public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ return $this->tryPlacementTransaction($this->getBlock($face), $blockReplace, $blockClicked, $face, $clickVector, $player); } /** * Returns the block corresponding to this Item. */ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::AIR(); } @@ -605,7 +606,7 @@ class Item implements \JsonSerializable{ * * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ return ItemUseResult::NONE; } diff --git a/src/item/ItemBlock.php b/src/item/ItemBlock.php index 015c78471..9d10f5e87 100644 --- a/src/item/ItemBlock.php +++ b/src/item/ItemBlock.php @@ -26,6 +26,7 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\BlockTypeIds; use pocketmine\data\runtime\RuntimeDataDescriber; +use pocketmine\math\Facing; /** * Class used for Items that directly represent blocks, such as stone, dirt, wood etc. @@ -44,7 +45,7 @@ final class ItemBlock extends Item{ $this->block->describeBlockItemState($w); } - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return clone $this->block; } diff --git a/src/item/ItemBlockWallOrFloor.php b/src/item/ItemBlockWallOrFloor.php index c20c73a1d..963b3cfb9 100644 --- a/src/item/ItemBlockWallOrFloor.php +++ b/src/item/ItemBlockWallOrFloor.php @@ -38,7 +38,7 @@ class ItemBlockWallOrFloor extends Item{ $this->wallVariant = $wallVariant->getStateId(); } - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ if($clickedFace !== null && Facing::axis($clickedFace) !== Axis::Y){ return RuntimeBlockStateRegistry::getInstance()->fromStateId($this->wallVariant); } diff --git a/src/item/LiquidBucket.php b/src/item/LiquidBucket.php index eb2cb18ed..c2f0d143e 100644 --- a/src/item/LiquidBucket.php +++ b/src/item/LiquidBucket.php @@ -27,6 +27,7 @@ use pocketmine\block\Block; use pocketmine\block\Lava; use pocketmine\block\Liquid; use pocketmine\event\player\PlayerBucketEmptyEvent; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -54,7 +55,7 @@ class LiquidBucket extends Item{ return VanillaItems::BUCKET(); } - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if(!$blockReplace->canBeReplaced()){ return ItemUseResult::NONE; } diff --git a/src/item/MelonSeeds.php b/src/item/MelonSeeds.php index feecf09c7..f9604712b 100644 --- a/src/item/MelonSeeds.php +++ b/src/item/MelonSeeds.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class MelonSeeds extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::MELON_STEM(); } } diff --git a/src/item/PaintingItem.php b/src/item/PaintingItem.php index a83c8dba8..48589e3e8 100644 --- a/src/item/PaintingItem.php +++ b/src/item/PaintingItem.php @@ -37,7 +37,7 @@ use function count; class PaintingItem extends Item{ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if(Facing::axis($face) === Axis::Y){ return ItemUseResult::NONE; } diff --git a/src/item/PitcherPod.php b/src/item/PitcherPod.php index be9393515..0a82c5f84 100644 --- a/src/item/PitcherPod.php +++ b/src/item/PitcherPod.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; final class PitcherPod extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::PITCHER_CROP(); } } diff --git a/src/item/Potato.php b/src/item/Potato.php index 8fe7fc8c7..61ba36b6f 100644 --- a/src/item/Potato.php +++ b/src/item/Potato.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class Potato extends Food{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::POTATOES(); } diff --git a/src/item/PumpkinSeeds.php b/src/item/PumpkinSeeds.php index 3502500cc..bb31a17f7 100644 --- a/src/item/PumpkinSeeds.php +++ b/src/item/PumpkinSeeds.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class PumpkinSeeds extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::PUMPKIN_STEM(); } } diff --git a/src/item/Redstone.php b/src/item/Redstone.php index ffa58167c..92a7fc303 100644 --- a/src/item/Redstone.php +++ b/src/item/Redstone.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class Redstone extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::REDSTONE_WIRE(); } } diff --git a/src/item/SpawnEgg.php b/src/item/SpawnEgg.php index ab4f0e149..c6756e4f9 100644 --- a/src/item/SpawnEgg.php +++ b/src/item/SpawnEgg.php @@ -25,6 +25,7 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\entity\Entity; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\utils\Utils; @@ -34,7 +35,7 @@ abstract class SpawnEgg extends Item{ abstract protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity; - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ $entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), Utils::getRandomFloat() * 360, 0); if($this->hasCustomName()){ diff --git a/src/item/StringItem.php b/src/item/StringItem.php index 2474f9ac9..7a1fc6367 100644 --- a/src/item/StringItem.php +++ b/src/item/StringItem.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class StringItem extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::TRIPWIRE(); } } diff --git a/src/item/SweetBerries.php b/src/item/SweetBerries.php index e377175c2..e1ebbf933 100644 --- a/src/item/SweetBerries.php +++ b/src/item/SweetBerries.php @@ -25,6 +25,7 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class SweetBerries extends Food{ @@ -36,7 +37,7 @@ class SweetBerries extends Food{ return 1.2; } - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::SWEET_BERRY_BUSH(); } } diff --git a/src/item/TorchflowerSeeds.php b/src/item/TorchflowerSeeds.php index 123af35a0..22725c733 100644 --- a/src/item/TorchflowerSeeds.php +++ b/src/item/TorchflowerSeeds.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; final class TorchflowerSeeds extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::TORCHFLOWER_CROP(); } } diff --git a/src/item/WheatSeeds.php b/src/item/WheatSeeds.php index 775cc59c2..6657681fe 100644 --- a/src/item/WheatSeeds.php +++ b/src/item/WheatSeeds.php @@ -25,10 +25,11 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\VanillaBlocks; +use pocketmine\math\Facing; class WheatSeeds extends Item{ - public function getBlock(?int $clickedFace = null) : Block{ + public function getBlock(?Facing $clickedFace = null) : Block{ return VanillaBlocks::WHEAT(); } } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index b5990d38f..8503f4eec 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -110,7 +110,6 @@ use function count; use function fmod; use function get_debug_type; use function implode; -use function in_array; use function is_infinite; use function is_nan; use function json_decode; @@ -280,7 +279,7 @@ class InGamePacketHandler extends PacketHandler{ foreach(Utils::promoteKeys($blockActions) as $k => $blockAction){ $actionHandled = false; if($blockAction instanceof PlayerBlockActionStopBreak){ - $actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), new BlockPosition(0, 0, 0), Facing::DOWN); + $actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), new BlockPosition(0, 0, 0), 0); }elseif($blockAction instanceof PlayerBlockActionWithBlockInfo){ $actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), $blockAction->getBlockPosition(), $blockAction->getFace()); } @@ -493,16 +492,16 @@ class InGamePacketHandler extends PacketHandler{ } //TODO: end hack for client spam bug - self::validateFacing($data->getFace()); + $face = self::deserializeFacing($data->getFace()); $blockPos = $data->getBlockPosition(); $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); - $this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos); + $this->player->interactBlock($vBlockPos, $face, $clickPos); //always sync this in case plugins caused a different result than the client expected //we *could* try to enhance detection of plugin-altered behaviour, but this would require propagating //more information up the stack. For now I think this is good enough. //if only the client would tell us what blocks it thinks changed... - $this->syncBlocksNearby($vBlockPos, $data->getFace()); + $this->syncBlocksNearby($vBlockPos, $face); return true; case UseItemTransactionData::ACTION_CLICK_AIR: if($this->player->isUsingItem()){ @@ -522,16 +521,19 @@ class InGamePacketHandler extends PacketHandler{ /** * @throws PacketHandlingException */ - private static function validateFacing(int $facing) : void{ - if(!in_array($facing, Facing::ALL, true)){ + private static function deserializeFacing(int $facing) : Facing{ + //TODO: dodgy use of network facing values as internal values here - they may not be the same in the future + $case = Facing::tryFrom($facing); + if($case === null){ throw new PacketHandlingException("Invalid facing value $facing"); } + return $case; } /** * Syncs blocks nearby to ensure that the client and server agree on the world's blocks after a block interaction. */ - private function syncBlocksNearby(Vector3 $blockPos, ?int $face) : void{ + private function syncBlocksNearby(Vector3 $blockPos, ?Facing $face) : void{ if($blockPos->distanceSquared($this->player->getLocation()) < 10000){ $blocks = $blockPos->sidesArray(); if($face !== null){ @@ -676,13 +678,13 @@ class InGamePacketHandler extends PacketHandler{ return $this->handlePlayerActionFromData($packet->action, $packet->blockPosition, $packet->face); } - private function handlePlayerActionFromData(int $action, BlockPosition $blockPosition, int $face) : bool{ + private function handlePlayerActionFromData(int $action, BlockPosition $blockPosition, int $extraData) : bool{ $pos = new Vector3($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ()); switch($action){ case PlayerAction::START_BREAK: case PlayerAction::CONTINUE_DESTROY_BLOCK: //destroy the next block while holding down left click - self::validateFacing($face); + $face = self::deserializeFacing($extraData); if($this->lastBlockAttacked !== null && $blockPosition->equals($this->lastBlockAttacked)){ //the client will send CONTINUE_DESTROY_BLOCK for the currently targeted block directly before it //sends PREDICT_DESTROY_BLOCK, but also when it starts to break the block @@ -710,7 +712,7 @@ class InGamePacketHandler extends PacketHandler{ $this->player->stopSleep(); break; case PlayerAction::CRACK_BREAK: - self::validateFacing($face); + $face = self::deserializeFacing($extraData); $this->player->continueBreakBlock($pos, $face); $this->lastBlockAttacked = $blockPosition; break; @@ -720,6 +722,7 @@ class InGamePacketHandler extends PacketHandler{ //TODO: do we need to handle this? case PlayerAction::PREDICT_DESTROY_BLOCK: if(!$this->player->breakBlock($pos)){ + $face = self::deserializeFacing($extraData); $this->syncBlocksNearby($pos, $face); } $this->lastBlockAttacked = null; diff --git a/src/player/Player.php b/src/player/Player.php index 762187b24..20ef235d9 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -105,6 +105,8 @@ use pocketmine\item\Releasable; use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\Language; use pocketmine\lang\Translatable; +use pocketmine\math\AxisAlignedBB; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; @@ -1311,9 +1313,15 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($this->gamemode === GameMode::SPECTATOR){ $this->onGround = false; }else{ - $bb = clone $this->boundingBox; - $bb->minY = $this->location->y - 0.2; - $bb->maxY = $this->location->y + 0.2; + //TODO: AxisAlignedBB::withComponents() would be nice here + $bb = new AxisAlignedBB( + $this->boundingBox->minX, + $this->location->y - 0.2, + $this->boundingBox->minZ, + $this->boundingBox->maxX, + $this->location->y + 0.2, + $this->boundingBox->maxZ + ); //we're already at the new position at this point; check if there are blocks we might have landed on between //the old and new positions (running down stairs necessitates this) @@ -1853,7 +1861,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ * * @return bool if an action took place successfully */ - public function attackBlock(Vector3 $pos, int $face) : bool{ + public function attackBlock(Vector3 $pos, Facing $face) : bool{ if($pos->distanceSquared($this->location) > 10000){ return false; //TODO: maybe this should throw an exception instead? } @@ -1887,7 +1895,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return true; } - public function continueBreakBlock(Vector3 $pos, int $face) : void{ + public function continueBreakBlock(Vector3 $pos, Facing $face) : void{ if($this->blockBreakHandler !== null && $this->blockBreakHandler->getBlockPos()->distanceSquared($pos) < 0.0001){ $this->blockBreakHandler->setTargetedFace($face); } @@ -1930,7 +1938,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ * * @return bool if it did something */ - public function interactBlock(Vector3 $pos, int $face, Vector3 $clickOffset) : bool{ + public function interactBlock(Vector3 $pos, Facing $face, Vector3 $clickOffset) : bool{ $this->setUsingItem(false); if($this->canInteract($pos->add(0.5, 0.5, 0.5), $this->isCreative() ? self::MAX_REACH_DISTANCE_CREATIVE : self::MAX_REACH_DISTANCE_SURVIVAL)){ diff --git a/src/player/SurvivalBlockBreakHandler.php b/src/player/SurvivalBlockBreakHandler.php index 57c2842ce..ee52fde0e 100644 --- a/src/player/SurvivalBlockBreakHandler.php +++ b/src/player/SurvivalBlockBreakHandler.php @@ -47,7 +47,7 @@ final class SurvivalBlockBreakHandler{ private Player $player, private Vector3 $blockPos, private Block $block, - private int $targetedFace, + private Facing $targetedFace, private int $maxPlayerDistance, private int $fxTickInterval = self::DEFAULT_FX_INTERVAL_TICKS ){ @@ -123,12 +123,11 @@ final class SurvivalBlockBreakHandler{ return $this->blockPos; } - public function getTargetedFace() : int{ + public function getTargetedFace() : Facing{ return $this->targetedFace; } - public function setTargetedFace(int $face) : void{ - Facing::validate($face); + public function setTargetedFace(Facing $face) : void{ $this->targetedFace = $face; } diff --git a/src/world/Position.php b/src/world/Position.php index 62a4b51f5..79bbefdd9 100644 --- a/src/world/Position.php +++ b/src/world/Position.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace pocketmine\world; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\utils\AssumptionFailedError; use function assert; -class Position extends Vector3{ - public ?World $world = null; +readonly class Position extends Vector3{ + public ?World $world; public function __construct(float|int $x, float|int $y, float|int $z, ?World $world){ parent::__construct($x, $y, $z); @@ -71,8 +72,6 @@ class Position extends Vector3{ */ public function isValid() : bool{ if($this->world !== null && !$this->world->isLoaded()){ - $this->world = null; - return false; } @@ -84,7 +83,7 @@ class Position extends Vector3{ * * @return Position */ - public function getSide(int $side, int $step = 1){ + public function getSide(Facing $side, int $step = 1){ assert($this->isValid()); return Position::fromObject(parent::getSide($side, $step), $this->world); diff --git a/src/world/World.php b/src/world/World.php index 951eb53f8..ee70bc99b 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -983,7 +983,7 @@ class World implements ChunkManager{ continue; } } - foreach($this->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z)) as $entity){ + foreach($this->getNearbyEntities(AxisAlignedBB::one()->offsetCopy($x, $y, $z)) as $entity){ $entity->onNearbyBlockChange(); } $block->onNearbyBlockChange(); @@ -1611,7 +1611,7 @@ class World implements ChunkManager{ $stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo); $boxes = match($stateCollisionInfo){ RuntimeBlockStateRegistry::COLLISION_NONE => [], - RuntimeBlockStateRegistry::COLLISION_CUBE => [AxisAlignedBB::one()->offset($x, $y, $z)], + RuntimeBlockStateRegistry::COLLISION_CUBE => [AxisAlignedBB::one()->offsetCopy($x, $y, $z)], default => $this->getBlockAt($x, $y, $z)->getCollisionBoxes() }; @@ -1625,7 +1625,7 @@ class World implements ChunkManager{ $stateCollisionInfo = $this->getBlockCollisionInfo($offsetX, $offsetY, $offsetZ, $collisionInfo); if($stateCollisionInfo === RuntimeBlockStateRegistry::COLLISION_MAY_OVERFLOW){ //avoid allocating this unless it's needed - $cellBB ??= AxisAlignedBB::one()->offset($x, $y, $z); + $cellBB ??= AxisAlignedBB::one()->offsetCopy($x, $y, $z); $extraBoxes = $this->getBlockAt($offsetX, $offsetY, $offsetZ)->getCollisionBoxes(); foreach($extraBoxes as $extraBox){ if($extraBox->intersectsWith($cellBB)){ @@ -2219,7 +2219,7 @@ class World implements ChunkManager{ * @param bool $playSound Whether to play a block-place sound if the block was placed successfully. * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped if the inventory is full) */ - public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $clickVector = null, ?Player $player = null, bool $playSound = false, array &$returnedItems = []) : bool{ + public function useItemOn(Vector3 $vector, Item &$item, Facing $face, ?Vector3 $clickVector = null, ?Player $player = null, bool $playSound = false, array &$returnedItems = []) : bool{ $blockClicked = $this->getBlock($vector); $blockReplace = $blockClicked->getSide($face); diff --git a/src/world/generator/object/AcaciaTree.php b/src/world/generator/object/AcaciaTree.php index 75e58a9b3..f5ab5665c 100644 --- a/src/world/generator/object/AcaciaTree.php +++ b/src/world/generator/object/AcaciaTree.php @@ -84,7 +84,7 @@ final class AcaciaTree extends Tree{ } } - protected function placeBranch(BlockTransaction $transaction, Vector3 $start, int $branchFacing, int $maxDiagonal, int $length) : Vector3{ + protected function placeBranch(BlockTransaction $transaction, Vector3 $start, Facing $branchFacing, int $maxDiagonal, int $length) : Vector3{ $diagonalPlaced = 0; $nextBlockPos = $start; diff --git a/src/world/light/LightPropagationContext.php b/src/world/light/LightPropagationContext.php index c3d45b3c6..7f363c785 100644 --- a/src/world/light/LightPropagationContext.php +++ b/src/world/light/LightPropagationContext.php @@ -23,13 +23,15 @@ declare(strict_types=1); namespace pocketmine\world\light; +use pocketmine\math\Facing; + final class LightPropagationContext{ /** @phpstan-var \SplQueue */ public \SplQueue $spreadQueue; /** * @var int[]|true[] - * @phpstan-var array + * @phpstan-var array */ public array $spreadVisited = []; diff --git a/src/world/light/LightUpdate.php b/src/world/light/LightUpdate.php index b7455c6cc..039d4d991 100644 --- a/src/world/light/LightUpdate.php +++ b/src/world/light/LightUpdate.php @@ -156,11 +156,12 @@ abstract class LightUpdate{ continue; } - foreach(Facing::OFFSET as $side => [$ox, $oy, $oz]){ + foreach(Facing::ALL as $side){ if($from === $side){ //don't check the side that this node received its initial light from continue; } + [$ox, $oy, $oz] = Facing::OFFSET[$side->value]; $cx = $x + $ox; $cy = $y + $oy; $cz = $z + $oz; @@ -204,7 +205,7 @@ abstract class LightUpdate{ } } - protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context, LightArray $lightArray, SubChunk $subChunk, int $side) : void{ + protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context, LightArray $lightArray, SubChunk $subChunk, Facing $side) : void{ $lx = $x & SubChunk::COORD_MASK; $ly = $y & SubChunk::COORD_MASK; $lz = $z & SubChunk::COORD_MASK; diff --git a/src/world/particle/BlockPunchParticle.php b/src/world/particle/BlockPunchParticle.php index 74478ca95..f023501ea 100644 --- a/src/world/particle/BlockPunchParticle.php +++ b/src/world/particle/BlockPunchParticle.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\world\particle; use pocketmine\block\Block; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\protocol\LevelEventPacket; @@ -35,10 +36,14 @@ use pocketmine\network\mcpe\protocol\types\LevelEvent; class BlockPunchParticle implements Particle{ public function __construct( private Block $block, - private int $face + private Facing $face ){} public function encode(Vector3 $pos) : array{ - return [LevelEventPacket::create(LevelEvent::PARTICLE_PUNCH_BLOCK, TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()) | ($this->face << 24), $pos)]; + return [LevelEventPacket::create( + LevelEvent::PARTICLE_PUNCH_BLOCK, + //TODO: dodgy use of internal value for protocol - this should be properly translated + TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()) | ($this->face->value << 24), $pos) + ]; } } diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 06abe7fee..04e476e96 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -601,7 +601,7 @@ parameters: path: ../../../src/crash/CrashDumpRenderer.php - - message: '#^Parameter \#1 \$faces of method pocketmine\\block\\Vine\:\:setFaces\(\) expects list\<2\|3\|4\|5\>, array\ given\.$#' + message: '#^Parameter \#1 \$faces of method pocketmine\\block\\Vine\:\:setFaces\(\) expects list\, array\ given\.$#' identifier: argument.type count: 1 path: ../../../src/data/bedrock/block/convert/VanillaBlockMappings.php From 6aaf6b336a6cc6b45e7ee0c852a4d3a6508778c5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 23:11:17 +0100 Subject: [PATCH 131/140] Make an enum for horizontal facing blocks awkward that the interface is also called HorizontalFacing, so I had to improvise for the naming --- src/block/Anvil.php | 7 +- src/block/BaseBigDripleaf.php | 3 +- src/block/Bed.php | 9 +- src/block/Bell.php | 22 ++-- src/block/Campfire.php | 3 +- src/block/CeilingEdgesHangingSign.php | 5 +- src/block/Chest.php | 2 +- src/block/ChiseledBookshelf.php | 4 +- src/block/CocoaBlock.php | 16 +-- src/block/Door.php | 13 ++- src/block/EndPortalFrame.php | 2 +- src/block/FenceGate.php | 16 +-- src/block/Furnace.php | 2 +- src/block/Ladder.php | 9 +- src/block/Lectern.php | 2 +- src/block/OminousWallBanner.php | 8 +- src/block/PinkPetals.php | 5 +- src/block/Pumpkin.php | 5 +- src/block/RedstoneComparator.php | 12 +- src/block/RedstoneRepeater.php | 14 +-- src/block/SmallDripleaf.php | 5 +- src/block/Stair.php | 30 ++--- src/block/Trapdoor.php | 7 +- src/block/TripwireHook.php | 9 +- src/block/Vine.php | 29 +++-- src/block/WallBanner.php | 8 +- src/block/WallCoralFan.php | 11 +- src/block/WallHangingSign.php | 14 ++- src/block/WallSign.php | 8 +- .../utils/FacesOppositePlacingPlayerTrait.php | 2 +- src/block/utils/HorizontalFacing.php | 6 +- src/block/utils/HorizontalFacingOption.php | 56 +++++++++ src/block/utils/HorizontalFacingTrait.php | 13 +-- .../block/convert/VanillaBlockMappings.php | 20 ++-- .../convert/property/CommonProperties.php | 29 ++--- .../block/convert/property/ValueMappings.php | 107 +++++++++--------- src/data/runtime/RuntimeDataDescriber.php | 5 +- src/data/runtime/RuntimeDataReader.php | 15 +-- .../runtime/RuntimeDataSizeCalculator.php | 7 +- src/data/runtime/RuntimeDataWriter.php | 15 +-- 40 files changed, 305 insertions(+), 250 deletions(-) create mode 100644 src/block/utils/HorizontalFacingOption.php diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 1d08e53ae..a8a95492a 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -27,6 +27,7 @@ use pocketmine\block\inventory\AnvilInventory; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -57,7 +58,7 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{ } protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); } public function getDamage() : int{ return $this->damage; } @@ -72,7 +73,7 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->squashedCopy(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)]; + return [AxisAlignedBB::one()->squashedCopy(Facing::axis(Facing::rotateY($this->facing->toFacing(), false)), 1 / 8)]; } public function getSupportType(Facing $facing) : SupportType{ @@ -89,7 +90,7 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ - $this->facing = Facing::rotateY($player->getHorizontalFacing(), false); + $this->facing = HorizontalFacingOption::fromFacing(Facing::rotateY($player->getHorizontalFacing(), false)); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } diff --git a/src/block/BaseBigDripleaf.php b/src/block/BaseBigDripleaf.php index 452184d74..c94300959 100644 --- a/src/block/BaseBigDripleaf.php +++ b/src/block/BaseBigDripleaf.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\event\block\StructureGrowEvent; @@ -63,7 +64,7 @@ abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{ return false; } if($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); + $this->facing = HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing())); } if($block instanceof BaseBigDripleaf){ $this->facing = $block->facing; diff --git a/src/block/Bed.php b/src/block/Bed.php index 84589664a..8e7bf5a4a 100644 --- a/src/block/Bed.php +++ b/src/block/Bed.php @@ -28,6 +28,7 @@ use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -51,7 +52,7 @@ class Bed extends Transparent implements Colored, HorizontalFacing{ protected bool $head = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->occupied); $w->bool($this->head); } @@ -107,7 +108,7 @@ class Bed extends Transparent implements Colored, HorizontalFacing{ } private function getOtherHalfSide() : Facing{ - return $this->head ? Facing::opposite($this->facing) : $this->facing; + return $this->head ? Facing::opposite($this->facing->toFacing()) : $this->facing->toFacing(); } public function getOtherHalf() : ?Bed{ @@ -174,7 +175,9 @@ class Bed extends Transparent implements Colored, HorizontalFacing{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($this->canBeSupportedAt($blockReplace)){ - $this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH; + if($player !== null){ + $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing()); + } $next = $this->getSide($this->getOtherHalfSide()); if($next->canBeReplaced() && $this->canBeSupportedAt($next)){ diff --git a/src/block/Bell.php b/src/block/Bell.php index cf93f4501..976793720 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Bell as TileBell; use pocketmine\block\utils\BellAttachmentType; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -46,13 +47,14 @@ final class Bell extends Transparent implements HorizontalFacing{ protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->enum($this->attachmentType); - $w->horizontalFacing($this->facing); + $w->enum($this->facing); } protected function recalculateCollisionBoxes() : array{ + $realFacing = $this->facing->toFacing(); if($this->attachmentType === BellAttachmentType::FLOOR){ return [ - AxisAlignedBB::one()->squashedCopy(Facing::axis($this->facing), 1 / 4)->trimmedCopy(Facing::UP, 3 / 16) + AxisAlignedBB::one()->squashedCopy(Facing::axis($realFacing), 1 / 4)->trimmedCopy(Facing::UP, 3 / 16) ]; } if($this->attachmentType === BellAttachmentType::CEILING){ @@ -62,12 +64,12 @@ final class Bell extends Transparent implements HorizontalFacing{ } $box = AxisAlignedBB::one() - ->squashedCopy(Facing::axis(Facing::rotateY($this->facing, true)), 1 / 4) + ->squashedCopy(Facing::axis(Facing::rotateY($realFacing, true)), 1 / 4) ->trimmedCopy(Facing::UP, 1 / 16) ->trimmedCopy(Facing::DOWN, 1 / 4); return [ - $this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trimmedCopy($this->facing, 3 / 16) : $box + $this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trimmedCopy($realFacing, 3 / 16) : $box ]; } @@ -93,13 +95,13 @@ final class Bell extends Transparent implements HorizontalFacing{ } if($face === Facing::UP){ if($player !== null){ - $this->setFacing(Facing::opposite($player->getHorizontalFacing())); + $this->setFacing(HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing()))); } $this->setAttachmentType(BellAttachmentType::FLOOR); }elseif($face === Facing::DOWN){ $this->setAttachmentType(BellAttachmentType::CEILING); }else{ - $this->setFacing($face); + $this->setFacing(HorizontalFacingOption::fromFacing($face)); $this->setAttachmentType( $this->canBeSupportedAt($blockReplace, $face) ? BellAttachmentType::TWO_WALLS : @@ -113,8 +115,8 @@ final class Bell extends Transparent implements HorizontalFacing{ foreach(match($this->attachmentType){ BellAttachmentType::CEILING => [Facing::UP], BellAttachmentType::FLOOR => [Facing::DOWN], - BellAttachmentType::ONE_WALL => [Facing::opposite($this->facing)], - BellAttachmentType::TWO_WALLS => [$this->facing, Facing::opposite($this->facing)] + BellAttachmentType::ONE_WALL => [Facing::opposite($this->facing->toFacing())], + BellAttachmentType::TWO_WALLS => [$this->facing->toFacing(), Facing::opposite($this->facing->toFacing())] } as $supportBlockDirection){ if(!$this->canBeSupportedAt($this, $supportBlockDirection)){ $this->position->getWorld()->useBreakOn($this->position); @@ -158,8 +160,8 @@ final class Bell extends Transparent implements HorizontalFacing{ private function isValidFaceToRing(Facing $faceHit) : bool{ return match($this->attachmentType){ BellAttachmentType::CEILING => true, - BellAttachmentType::FLOOR => Facing::axis($faceHit) === Facing::axis($this->facing), - BellAttachmentType::ONE_WALL, BellAttachmentType::TWO_WALLS => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true), + BellAttachmentType::FLOOR => Facing::axis($faceHit) === Facing::axis($this->facing->toFacing()), + BellAttachmentType::ONE_WALL, BellAttachmentType::TWO_WALLS => $faceHit === Facing::rotateY($this->facing->toFacing(), false) || $faceHit === Facing::rotateY($this->facing->toFacing(), true), }; } } diff --git a/src/block/Campfire.php b/src/block/Campfire.php index 5c81af949..61b67a869 100644 --- a/src/block/Campfire.php +++ b/src/block/Campfire.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\CampfireInventory; use pocketmine\block\tile\Campfire as TileCampfire; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; @@ -176,7 +177,7 @@ class Campfire extends Transparent implements Lightable, HorizontalFacing{ return false; } if($player !== null){ - $this->facing = $player->getHorizontalFacing(); + $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing()); } $this->lit = true; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); diff --git a/src/block/CeilingEdgesHangingSign.php b/src/block/CeilingEdgesHangingSign.php index e8a347033..8a7271863 100644 --- a/src/block/CeilingEdgesHangingSign.php +++ b/src/block/CeilingEdgesHangingSign.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; @@ -44,7 +45,7 @@ final class CeilingEdgesHangingSign extends BaseSign implements HorizontalFacing return false; } if($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); + $this->facing = HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing())); } if(!$this->canBeSupportedAt($blockReplace)){ return false; @@ -63,6 +64,6 @@ final class CeilingEdgesHangingSign extends BaseSign implements HorizontalFacing $supportBlock = $block->getSide(Facing::UP); return $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || - (($supportBlock instanceof WallHangingSign || $supportBlock instanceof CeilingEdgesHangingSign) && Facing::axis($supportBlock->getFacing()) === Facing::axis($this->facing)); + (($supportBlock instanceof WallHangingSign || $supportBlock instanceof CeilingEdgesHangingSign) && Facing::axis($supportBlock->getFacing()->toFacing()) === Facing::axis($this->facing->toFacing())); } } diff --git a/src/block/Chest.php b/src/block/Chest.php index 58a7463f5..6251aa13f 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -51,7 +51,7 @@ class Chest extends Transparent implements HorizontalFacing{ $tile = $world->getTile($this->position); if($tile instanceof TileChest){ foreach([false, true] as $clockwise){ - $side = Facing::rotateY($this->facing, $clockwise); + $side = Facing::rotateY($this->facing->toFacing(), $clockwise); $c = $this->getSide($side); if($c instanceof Chest && $c->hasSameTypeId($this) && $c->facing === $this->facing){ $pair = $world->getTile($c->position); diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php index 1d8d83ed0..d35848331 100644 --- a/src/block/ChiseledBookshelf.php +++ b/src/block/ChiseledBookshelf.php @@ -52,7 +52,7 @@ class ChiseledBookshelf extends Opaque implements HorizontalFacing{ private ?ChiseledBookshelfSlot $lastInteractedSlot = null; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->enumSet($this->slots, ChiseledBookshelfSlot::cases()); } @@ -144,7 +144,7 @@ class ChiseledBookshelf extends Opaque implements HorizontalFacing{ } public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ - if($face !== $this->facing){ + if($face !== $this->facing->toFacing()){ return false; } diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index eec10db2d..f149dd153 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -27,6 +27,7 @@ use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\WoodType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -48,18 +49,19 @@ class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ public const MAX_AGE = 2; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->boundedIntAuto(0, self::MAX_AGE, $this->age); } protected function recalculateCollisionBoxes() : array{ + $realFacing = $this->facing->toFacing(); return [ AxisAlignedBB::one() - ->squashedCopy(Facing::axis(Facing::rotateY($this->facing, true)), (6 - $this->age) / 16) //sides + ->squashedCopy(Facing::axis(Facing::rotateY($realFacing, true)), (6 - $this->age) / 16) //sides ->trimmedCopy(Facing::DOWN, (7 - $this->age * 2) / 16) ->trimmedCopy(Facing::UP, 0.25) - ->trimmedCopy(Facing::opposite($this->facing), 1 / 16) //gap between log and pod - ->trimmedCopy($this->facing, (11 - $this->age * 2) / 16) //outward face + ->trimmedCopy(Facing::opposite($realFacing), 1 / 16) //gap between log and pod + ->trimmedCopy($realFacing, (11 - $this->age * 2) / 16) //outward face ]; } @@ -68,8 +70,8 @@ class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(Facing::axis($face) !== Axis::Y && $this->canAttachTo($blockClicked)){ - $this->facing = $face; + if(($hzFacing = HorizontalFacingOption::tryFromFacing($face)) !== null && $this->canAttachTo($blockClicked)){ + $this->facing = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -87,7 +89,7 @@ class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ } public function onNearbyBlockChange() : void{ - if(!$this->canAttachTo($this->getSide(Facing::opposite($this->facing)))){ + if(!$this->canAttachTo($this->getSide(Facing::opposite($this->facing->toFacing())))){ $this->position->getWorld()->useBreakOn($this->position); } } diff --git a/src/block/Door.php b/src/block/Door.php index 514d9dda9..559c71575 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -43,7 +44,7 @@ class Door extends Transparent implements HorizontalFacing{ protected bool $open = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->top); $w->bool($this->hingeRight); $w->bool($this->open); @@ -98,7 +99,7 @@ class Door extends Transparent implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ //TODO: doors are 0.1825 blocks thick, instead of 0.1875 like JE (https://bugs.mojang.com/browse/MCPE-19214) - return [AxisAlignedBB::one()->trimmedCopy($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)]; + return [AxisAlignedBB::one()->trimmedCopy($this->open ? Facing::rotateY($this->facing->toFacing(), !$this->hingeRight) : $this->facing->toFacing(), 327 / 400)]; } public function getSupportType(Facing $facing) : SupportType{ @@ -119,11 +120,13 @@ class Door extends Transparent implements HorizontalFacing{ } if($player !== null){ - $this->facing = $player->getHorizontalFacing(); + //TODO: not sure if entities should use HorizontalFacingOption too + $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing()); } - $next = $this->getSide(Facing::rotateY($this->facing, false)); - $next2 = $this->getSide(Facing::rotateY($this->facing, true)); + $realFacing = $this->facing->toFacing(); + $next = $this->getSide(Facing::rotateY($realFacing, false)); + $next2 = $this->getSide(Facing::rotateY($realFacing, true)); if($next->hasSameTypeId($this) || (!$next2->isTransparent() && $next->isTransparent())){ //Door hinge $this->hingeRight = true; diff --git a/src/block/EndPortalFrame.php b/src/block/EndPortalFrame.php index 7169e38f2..52111090c 100644 --- a/src/block/EndPortalFrame.php +++ b/src/block/EndPortalFrame.php @@ -35,7 +35,7 @@ class EndPortalFrame extends Opaque implements HorizontalFacing{ protected bool $eye = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->eye); } diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php index 1e6ed6dc8..a067bb71a 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\block\utils\WoodMaterial; @@ -45,7 +46,7 @@ class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ protected bool $inWall = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->open); $w->bool($this->inWall); } @@ -67,7 +68,7 @@ class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ } protected function recalculateCollisionBoxes() : array{ - return $this->open ? [] : [AxisAlignedBB::one()->extendedCopy(Facing::UP, 0.5)->squashedCopy(Facing::axis($this->facing), 6 / 16)]; + return $this->open ? [] : [AxisAlignedBB::one()->extendedCopy(Facing::UP, 0.5)->squashedCopy(Facing::axis($this->facing->toFacing()), 6 / 16)]; } public function getSupportType(Facing $facing) : SupportType{ @@ -75,15 +76,16 @@ class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ } private function checkInWall() : bool{ + $realFacing = $this->facing->toFacing(); return ( - $this->getSide(Facing::rotateY($this->facing, false)) instanceof Wall || - $this->getSide(Facing::rotateY($this->facing, true)) instanceof Wall + $this->getSide(Facing::rotateY($realFacing, false)) instanceof Wall || + $this->getSide(Facing::rotateY($realFacing, true)) instanceof Wall ); } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ - $this->facing = $player->getHorizontalFacing(); + $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing()); } $this->inWall = $this->checkInWall(); @@ -103,8 +105,8 @@ class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ $this->open = !$this->open; if($this->open && $player !== null){ $playerFacing = $player->getHorizontalFacing(); - if($playerFacing === Facing::opposite($this->facing)){ - $this->facing = $playerFacing; + if($playerFacing === Facing::opposite($this->facing->toFacing())){ + $this->facing = HorizontalFacingOption::fromFacing($playerFacing); } } diff --git a/src/block/Furnace.php b/src/block/Furnace.php index 808231b18..bd395cb1a 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -48,7 +48,7 @@ class Furnace extends Opaque implements Lightable, HorizontalFacing{ } protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->lit); } diff --git a/src/block/Ladder.php b/src/block/Ladder.php index fe7a4b65f..40d234051 100644 --- a/src/block/Ladder.php +++ b/src/block/Ladder.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\entity\Entity; @@ -60,7 +61,7 @@ class Ladder extends Transparent implements HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trimmedCopy($this->facing, 13 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy($this->facing->toFacing(), 13 / 16)]; } public function getSupportType(Facing $facing) : SupportType{ @@ -68,8 +69,8 @@ class Ladder extends Transparent implements HorizontalFacing{ } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if($this->canBeSupportedAt($blockReplace, Facing::opposite($face)) && Facing::axis($face) !== Axis::Y){ - $this->facing = $face; + if(($hzFacing = HorizontalFacingOption::tryFromFacing($face)) !== null && $this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ + $this->facing = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -77,7 +78,7 @@ class Ladder extends Transparent implements HorizontalFacing{ } public function onNearbyBlockChange() : void{ - if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){ //Replace with common break method + if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing->toFacing()))){ //Replace with common break method $this->position->getWorld()->useBreakOn($this->position); } } diff --git a/src/block/Lectern.php b/src/block/Lectern.php index bfdbcb3ec..43c28f32b 100644 --- a/src/block/Lectern.php +++ b/src/block/Lectern.php @@ -46,7 +46,7 @@ class Lectern extends Transparent implements HorizontalFacing{ protected bool $producingSignal = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->producingSignal); } diff --git a/src/block/OminousWallBanner.php b/src/block/OminousWallBanner.php index cc89b4192..3f59d82fb 100644 --- a/src/block/OminousWallBanner.php +++ b/src/block/OminousWallBanner.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -36,14 +37,15 @@ final class OminousWallBanner extends BaseOminousBanner implements HorizontalFac use HorizontalFacingTrait; protected function getSupportingFace() : Facing{ - return Facing::opposite($this->facing); + return Facing::opposite($this->facing->toFacing()); } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(Facing::axis($face) === Axis::Y){ + $hzFacing = HorizontalFacingOption::tryFromFacing($face); + if($hzFacing === null){ return false; } - $this->facing = $face; + $this->facing = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } } diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php index 83ba482fd..d772d5df8 100644 --- a/src/block/PinkPetals.php +++ b/src/block/PinkPetals.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -47,7 +48,7 @@ class PinkPetals extends Flowable implements HorizontalFacing{ protected int $count = self::MIN_COUNT; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count); } @@ -79,7 +80,7 @@ class PinkPetals extends Flowable implements HorizontalFacing{ $this->count = $blockReplace->count + 1; $this->facing = $blockReplace->facing; }elseif($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); + $this->facing = HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing())); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } diff --git a/src/block/Pumpkin.php b/src/block/Pumpkin.php index f268be656..8a21b66de 100644 --- a/src/block/Pumpkin.php +++ b/src/block/Pumpkin.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\item\Item; use pocketmine\item\Shears; use pocketmine\item\VanillaItems; @@ -34,10 +35,10 @@ use function in_array; class Pumpkin extends Opaque{ public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ - if($item instanceof Shears && in_array($face, Facing::HORIZONTAL, true)){ + if($item instanceof Shears && ($hzFacing = HorizontalFacingOption::tryFromFacing($face)) !== null){ $item->applyDamage(1); $world = $this->position->getWorld(); - $world->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($face)); + $world->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($hzFacing)); $world->dropItem($this->position->add(0.5, 0.5, 0.5), VanillaItems::PUMPKIN_SEEDS()->setCount(1)); return true; } diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php index 49dc52529..18300982c 100644 --- a/src/block/RedstoneComparator.php +++ b/src/block/RedstoneComparator.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Comparator; use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; +use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\PoweredByRedstone; @@ -42,7 +43,7 @@ use pocketmine\world\BlockTransaction; use function assert; class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter, PoweredByRedstone, HorizontalFacing{ - use HorizontalFacingTrait; + use FacesOppositePlacingPlayerTrait; use AnalogRedstoneSignalEmitterTrait; use PoweredByRedstoneTrait; use StaticSupportTrait; @@ -50,7 +51,7 @@ class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter protected bool $isSubtractMode = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->isSubtractMode); $w->bool($this->powered); } @@ -86,13 +87,6 @@ class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 7 / 8)]; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); - } - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->isSubtractMode = !$this->isSubtractMode; $this->position->getWorld()->setBlock($this->position, $this); diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php index e7cf9187c..3e65d3090 100644 --- a/src/block/RedstoneRepeater.php +++ b/src/block/RedstoneRepeater.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; @@ -38,7 +40,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; class RedstoneRepeater extends Flowable implements PoweredByRedstone, HorizontalFacing{ - use HorizontalFacingTrait; + use FacesOppositePlacingPlayerTrait; use PoweredByRedstoneTrait; use StaticSupportTrait; @@ -48,7 +50,7 @@ class RedstoneRepeater extends Flowable implements PoweredByRedstone, Horizontal protected int $delay = self::MIN_DELAY; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->boundedIntAuto(self::MIN_DELAY, self::MAX_DELAY, $this->delay); $w->bool($this->powered); } @@ -68,14 +70,6 @@ class RedstoneRepeater extends Flowable implements PoweredByRedstone, Horizontal return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 7 / 8)]; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); - } - - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(++$this->delay > self::MAX_DELAY){ $this->delay = self::MIN_DELAY; diff --git a/src/block/SmallDripleaf.php b/src/block/SmallDripleaf.php index 76031fd9b..abf428ce5 100644 --- a/src/block/SmallDripleaf.php +++ b/src/block/SmallDripleaf.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -43,7 +44,7 @@ class SmallDripleaf extends Transparent implements HorizontalFacing{ protected bool $top = false; public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->top); } @@ -81,7 +82,7 @@ class SmallDripleaf extends Transparent implements HorizontalFacing{ return false; } if($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); + $this->facing = HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing())); } $tx->addBlock($block->position, VanillaBlocks::SMALL_DRIPLEAF() diff --git a/src/block/Stair.php b/src/block/Stair.php index 72e62b6eb..6e58dd985 100644 --- a/src/block/Stair.php +++ b/src/block/Stair.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\StairShape; use pocketmine\block\utils\SupportType; @@ -43,7 +44,7 @@ class Stair extends Transparent implements HorizontalFacing{ protected StairShape $shape = StairShape::STRAIGHT; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->upsideDown); } @@ -52,7 +53,7 @@ class Stair extends Transparent implements HorizontalFacing{ $this->collisionBoxes = null; - $clockwise = Facing::rotateY($this->facing, true); + $clockwise = HorizontalFacingOption::fromFacing(Facing::rotateY($this->facing->toFacing(), true)); if(($backFacing = $this->getPossibleCornerFacing(false)) !== null){ $this->shape = $backFacing === $clockwise ? StairShape::OUTER_RIGHT : StairShape::OUTER_LEFT; }elseif(($frontFacing = $this->getPossibleCornerFacing(true)) !== null){ @@ -86,18 +87,19 @@ class Stair extends Transparent implements HorizontalFacing{ AxisAlignedBB::one()->trimmedCopy($topStepFace, 0.5) ]; + $realFacing = $this->facing->toFacing(); $topStep = AxisAlignedBB::one() ->trimmedCopy(Facing::opposite($topStepFace), 0.5) - ->trimmedCopy(Facing::opposite($this->facing), 0.5); + ->trimmedCopy(Facing::opposite($realFacing), 0.5); if($this->shape === StairShape::OUTER_LEFT || $this->shape === StairShape::OUTER_RIGHT){ - $topStep = $topStep->trimmedCopy(Facing::rotateY($this->facing, $this->shape === StairShape::OUTER_LEFT), 0.5); + $topStep = $topStep->trimmedCopy(Facing::rotateY($realFacing, $this->shape === StairShape::OUTER_LEFT), 0.5); }elseif($this->shape === StairShape::INNER_LEFT || $this->shape === StairShape::INNER_RIGHT){ //add an extra cube $bbs[] = AxisAlignedBB::one() ->trimmedCopy(Facing::opposite($topStepFace), 0.5) - ->trimmedCopy($this->facing, 0.5) //avoid overlapping with main step - ->trimmedCopy(Facing::rotateY($this->facing, $this->shape === StairShape::INNER_LEFT), 0.5); + ->trimmedCopy($realFacing, 0.5) //avoid overlapping with main step + ->trimmedCopy(Facing::rotateY($realFacing, $this->shape === StairShape::INNER_LEFT), 0.5); } $bbs[] = $topStep; @@ -106,30 +108,32 @@ class Stair extends Transparent implements HorizontalFacing{ } public function getSupportType(Facing $facing) : SupportType{ + $realFacing = $this->facing->toFacing(); if( $facing === Facing::UP && $this->upsideDown || $facing === Facing::DOWN && !$this->upsideDown || - ($facing === $this->facing && $this->shape !== StairShape::OUTER_LEFT && $this->shape !== StairShape::OUTER_RIGHT) || - ($facing === Facing::rotate($this->facing, Axis::Y, false) && $this->shape === StairShape::INNER_LEFT) || - ($facing === Facing::rotate($this->facing, Axis::Y, true) && $this->shape === StairShape::INNER_RIGHT) + ($facing === $realFacing && $this->shape !== StairShape::OUTER_LEFT && $this->shape !== StairShape::OUTER_RIGHT) || + ($facing === Facing::rotate($realFacing, Axis::Y, false) && $this->shape === StairShape::INNER_LEFT) || + ($facing === Facing::rotate($realFacing, Axis::Y, true) && $this->shape === StairShape::INNER_RIGHT) ){ return SupportType::FULL; } return SupportType::NONE; } - private function getPossibleCornerFacing(bool $oppositeFacing) : ?Facing{ - $side = $this->getSide($oppositeFacing ? Facing::opposite($this->facing) : $this->facing); + private function getPossibleCornerFacing(bool $oppositeFacing) : ?HorizontalFacingOption{ + $realFacing = $this->facing->toFacing(); + $side = $this->getSide($oppositeFacing ? Facing::opposite($realFacing) : $realFacing); return ( $side instanceof Stair && $side->upsideDown === $this->upsideDown && - Facing::axis($side->facing) !== Facing::axis($this->facing) //perpendicular + Facing::axis($side->facing->toFacing()) !== Facing::axis($realFacing) //perpendicular ) ? $side->facing : null; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ - $this->facing = $player->getHorizontalFacing(); + $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing()); } $this->upsideDown = (($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN); diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php index 8b9a10d41..81c165840 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -42,7 +43,7 @@ class Trapdoor extends Transparent implements HorizontalFacing{ protected bool $top = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->top); $w->bool($this->open); } @@ -64,7 +65,7 @@ class Trapdoor extends Transparent implements HorizontalFacing{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trimmedCopy($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy($this->open ? $this->facing->toFacing() : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)]; } public function getSupportType(Facing $facing) : SupportType{ @@ -73,7 +74,7 @@ class Trapdoor extends Transparent implements HorizontalFacing{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); + $this->facing = HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing())); } if(($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN){ $this->top = true; diff --git a/src/block/TripwireHook.php b/src/block/TripwireHook.php index 0fe43da56..89ceea60c 100644 --- a/src/block/TripwireHook.php +++ b/src/block/TripwireHook.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -40,7 +41,7 @@ class TripwireHook extends Flowable implements HorizontalFacing{ protected bool $powered = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->connected); $w->bool($this->powered); } @@ -62,9 +63,9 @@ class TripwireHook extends Flowable implements HorizontalFacing{ } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(Facing::axis($face) !== Axis::Y){ - //TODO: check face is valid - $this->facing = $face; + $hzFacing = HorizontalFacingOption::tryFromFacing($face); + if($hzFacing !== null){ + $this->facing = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } return false; diff --git a/src/block/Vine.php b/src/block/Vine.php index 9ac4a3ade..8a83ba28f 100644 --- a/src/block/Vine.php +++ b/src/block/Vine.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\item\Item; @@ -36,30 +38,29 @@ use function count; class Vine extends Flowable{ - /** @var Facing[] */ + /** @var HorizontalFacingOption[] */ protected array $faces = []; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->horizontalFacingFlags($this->faces); } - /** @return Facing[] */ + /** @return HorizontalFacingOption[] */ public function getFaces() : array{ return $this->faces; } - public function hasFace(Facing $face) : bool{ + public function hasFace(HorizontalFacingOption $face) : bool{ return isset($this->faces[$face->value]); } /** - * @param Facing[] $faces - * @phpstan-param list $faces + * @param HorizontalFacingOption[] $faces * @return $this */ public function setFaces(array $faces) : self{ $uniqueFaces = []; foreach($faces as $face){ - if($face !== Facing::NORTH && $face !== Facing::SOUTH && $face !== Facing::WEST && $face !== Facing::EAST){ - throw new \InvalidArgumentException("Facing can only be north, east, south or west"); + if(!$face instanceof HorizontalFacingOption){ + throw new \InvalidArgumentException("Expected array of HorizontalFacingOption"); } $uniqueFaces[$face->value] = $face; } @@ -68,10 +69,7 @@ class Vine extends Flowable{ } /** @return $this */ - public function setFace(Facing $face, bool $value) : self{ - if($face !== Facing::NORTH && $face !== Facing::SOUTH && $face !== Facing::WEST && $face !== Facing::EAST){ - throw new \InvalidArgumentException("Facing can only be north, east, south or west"); - } + public function setFace(HorizontalFacingOption $face, bool $value) : self{ if($value){ $this->faces[$face->value] = $face; }else{ @@ -102,13 +100,14 @@ class Vine extends Flowable{ } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - $opposite = Facing::opposite($face); - if(!$blockReplace->getSide($opposite)->isFullCube() || Facing::axis($face) === Axis::Y){ + $oppositeFace = Facing::opposite($face); + $hzFacing = HorizontalFacingOption::tryFromFacing($oppositeFace); + if($hzFacing === null || !$blockReplace->getSide($oppositeFace)->isFullCube()){ return false; } $this->faces = $blockReplace instanceof Vine ? $blockReplace->faces : []; - $this->faces[$opposite->value] = $opposite; + $this->faces[$hzFacing->value] = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -121,7 +120,7 @@ class Vine extends Flowable{ $supportedFaces = $up instanceof Vine ? array_intersect_key($this->faces, $up->faces) : []; foreach($this->faces as $face){ - if(!isset($supportedFaces[$face->value]) && !$this->getSide($face)->isSolid()){ + if(!isset($supportedFaces[$face->value]) && !$this->getSide($face->toFacing())->isSolid()){ unset($this->faces[$face->value]); $changed = true; } diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php index 9159c2485..b367ab3d9 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -40,14 +41,15 @@ final class WallBanner extends BaseBanner implements HorizontalFacing{ } protected function getSupportingFace() : Facing{ - return Facing::opposite($this->facing); + return Facing::opposite($this->facing->toFacing()); } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(Facing::axis($face) === Axis::Y){ + $hzFacing = HorizontalFacingOption::tryFromFacing($face); + if($hzFacing === null){ return false; } - $this->facing = $face; + $this->facing = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } } diff --git a/src/block/WallCoralFan.php b/src/block/WallCoralFan.php index 74ec3dcc4..89d76cf17 100644 --- a/src/block/WallCoralFan.php +++ b/src/block/WallCoralFan.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -38,15 +39,15 @@ final class WallCoralFan extends BaseCoral implements HorizontalFacing{ use HorizontalFacingTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - $axis = Facing::axis($face); - if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ + $hzFacing = HorizontalFacingOption::tryFromFacing($face); + if($hzFacing === null || !$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ return false; } - $this->facing = $face; + $this->facing = $hzFacing; $this->dead = !$this->isCoveredWithWater(); @@ -55,7 +56,7 @@ final class WallCoralFan extends BaseCoral implements HorizontalFacing{ public function onNearbyBlockChange() : void{ $world = $this->position->getWorld(); - if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){ + if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing->toFacing()))){ $world->useBreakOn($this->position); }else{ parent::onNearbyBlockChange(); diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php index f03ff5927..44cdba2eb 100644 --- a/src/block/WallHangingSign.php +++ b/src/block/WallHangingSign.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; @@ -38,7 +39,7 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ use HorizontalFacingTrait; protected function getSupportingFace() : Facing{ - return Facing::rotateY($this->facing, clockwise: true); + return Facing::rotateY($this->facing->toFacing(), clockwise: true); } public function onNearbyBlockChange() : void{ @@ -47,7 +48,7 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ protected function recalculateCollisionBoxes() : array{ //only the cross bar is collidable - return [AxisAlignedBB::one()->trimmedCopy(Facing::DOWN, 7 / 8)->squashedCopy(Facing::axis($this->facing), 3 / 4)]; + return [AxisAlignedBB::one()->trimmedCopy(Facing::DOWN, 7 / 8)->squashedCopy(Facing::axis($this->facing->toFacing()), 3 / 4)]; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ @@ -64,18 +65,19 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ return false; } - $this->facing = Facing::rotateY(Facing::opposite($direction), clockwise: true); + $facing = Facing::rotateY(Facing::opposite($direction), clockwise: true); //the front should always face the player if possible - if($this->facing === $player->getHorizontalFacing()){ - $this->facing = Facing::opposite($this->facing); + if($facing === $player->getHorizontalFacing()){ + $facing = Facing::opposite($facing); } + $this->facing = HorizontalFacingOption::fromFacing($facing); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } private function canBeSupportedAt(Block $block, Facing $face) : bool{ return - ($block instanceof WallHangingSign && Facing::axis(Facing::rotateY($block->getFacing(), clockwise: true)) === Facing::axis($face)) || + ($block instanceof WallHangingSign && Facing::axis(Facing::rotateY($block->getFacing()->toFacing(), clockwise: true)) === Facing::axis($face)) || $block->getSupportType(Facing::opposite($face)) === SupportType::FULL; } } diff --git a/src/block/WallSign.php b/src/block/WallSign.php index 8a7bd8eb3..007c504a5 100644 --- a/src/block/WallSign.php +++ b/src/block/WallSign.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -36,14 +37,15 @@ final class WallSign extends BaseSign implements HorizontalFacing{ use HorizontalFacingTrait; protected function getSupportingFace() : Facing{ - return Facing::opposite($this->facing); + return Facing::opposite($this->facing->toFacing()); } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(Facing::axis($face) === Axis::Y){ + $hzFacing = HorizontalFacingOption::tryFromFacing($face); + if($hzFacing === null){ return false; } - $this->facing = $face; + $this->facing = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } } diff --git a/src/block/utils/FacesOppositePlacingPlayerTrait.php b/src/block/utils/FacesOppositePlacingPlayerTrait.php index e1fa05206..25ed984e6 100644 --- a/src/block/utils/FacesOppositePlacingPlayerTrait.php +++ b/src/block/utils/FacesOppositePlacingPlayerTrait.php @@ -38,7 +38,7 @@ trait FacesOppositePlacingPlayerTrait{ */ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ - $this->facing = Facing::opposite($player->getHorizontalFacing()); + $this->facing = HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing())); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } diff --git a/src/block/utils/HorizontalFacing.php b/src/block/utils/HorizontalFacing.php index b94d1a1bd..a4c1fed1d 100644 --- a/src/block/utils/HorizontalFacing.php +++ b/src/block/utils/HorizontalFacing.php @@ -23,11 +23,9 @@ declare(strict_types=1); namespace pocketmine\block\utils; -use pocketmine\math\Facing; - interface HorizontalFacing{ - public function getFacing() : Facing; + public function getFacing() : HorizontalFacingOption; /** @return $this */ - public function setFacing(Facing $facing) : self; + public function setFacing(HorizontalFacingOption $facing) : self; } diff --git a/src/block/utils/HorizontalFacingOption.php b/src/block/utils/HorizontalFacingOption.php new file mode 100644 index 000000000..b48aa3269 --- /dev/null +++ b/src/block/utils/HorizontalFacingOption.php @@ -0,0 +1,56 @@ +value; + case SOUTH = Facing::SOUTH->value; + case WEST = Facing::WEST->value; + case EAST = Facing::EAST->value; + + public static function tryFromFacing(Facing $facing) : ?self{ + return match($facing){ + Facing::NORTH => self::NORTH, + Facing::SOUTH => self::SOUTH, + Facing::WEST => self::WEST, + Facing::EAST => self::EAST, + default => null, + }; + } + + public static function fromFacing(Facing $facing) : self{ + return self::tryFromFacing($facing) ?? throw new \InvalidArgumentException("Facing $facing->name cannot be converted to a horizontal facing"); + } + + public function toFacing() : Facing{ + return match($this){ + self::NORTH => Facing::NORTH, + self::SOUTH => Facing::SOUTH, + self::WEST => Facing::WEST, + self::EAST => Facing::EAST, + }; + } +} diff --git a/src/block/utils/HorizontalFacingTrait.php b/src/block/utils/HorizontalFacingTrait.php index f4f231d33..d10eb0d3b 100644 --- a/src/block/utils/HorizontalFacingTrait.php +++ b/src/block/utils/HorizontalFacingTrait.php @@ -28,21 +28,16 @@ use pocketmine\math\Axis; use pocketmine\math\Facing; trait HorizontalFacingTrait{ - //TODO: this really needs a proper HorizontalFacing enum - protected Facing $facing = Facing::NORTH; + protected HorizontalFacingOption $facing = HorizontalFacingOption::NORTH; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); } - public function getFacing() : Facing{ return $this->facing; } + public function getFacing() : HorizontalFacingOption{ return $this->facing; } /** @return $this */ - public function setFacing(Facing $facing) : self{ - $axis = Facing::axis($facing); - if($axis !== Axis::X && $axis !== Axis::Z){ - throw new \InvalidArgumentException("Facing must be horizontal"); - } + public function setFacing(HorizontalFacingOption $facing) : self{ $this->facing = $facing; return $this; } diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index cb42ff2e2..8bcee6e0b 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -97,6 +97,7 @@ use pocketmine\block\utils\DripleafState; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\FroglightType; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\LeverFacing; use pocketmine\block\utils\MobHeadType; use pocketmine\block\utils\MushroomBlockType; @@ -584,15 +585,15 @@ final class VanillaBlockMappings{ $reg->mapModel(Model::create(Blocks::VINES(), Ids::VINE)->properties([ new ValueSetFromIntProperty( StateNames::VINE_DIRECTION_BITS, - IntFromRawStateMap::int([ - Facing::NORTH->value => BlockLegacyMetadata::VINE_FLAG_NORTH, - Facing::SOUTH->value => BlockLegacyMetadata::VINE_FLAG_SOUTH, - Facing::WEST->value => BlockLegacyMetadata::VINE_FLAG_WEST, - Facing::EAST->value => BlockLegacyMetadata::VINE_FLAG_EAST, - ]), + EnumFromRawStateMap::int(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match($case) { + HorizontalFacingOption::NORTH => BlockLegacyMetadata::VINE_FLAG_NORTH, + HorizontalFacingOption::SOUTH => BlockLegacyMetadata::VINE_FLAG_SOUTH, + HorizontalFacingOption::WEST => BlockLegacyMetadata::VINE_FLAG_WEST, + HorizontalFacingOption::EAST => BlockLegacyMetadata::VINE_FLAG_EAST, + }), //TODO: hack for lack of HorizontalFacing enum :( - fn(Vine $b) => array_map(fn(Facing $facing) => $facing->value, $b->getFaces()), - fn(Vine $b, array $v) => $b->setFaces(array_map(Facing::from(...), $v)) + fn(Vine $b) => $b->getFaces(), + fn(Vine $b, array $v) => $b->setFaces($v) ) ])); @@ -619,8 +620,7 @@ final class VanillaBlockMappings{ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::WALL_CORAL_FAN()) ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_wall_fan"]) ->properties([ - //TODO: hack for lack of horizontal facing enum :( - new ValueFromIntProperty(StateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, fn(HorizontalFacing $b) => $b->getFacing()->value, fn(HorizontalFacing $b, int $v) => $b->setFacing(Facing::from($v))), + new ValueFromIntProperty(StateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, fn(HorizontalFacing $b) => $b->getFacing(), fn(HorizontalFacing $b, HorizontalFacingOption $v) => $b->setFacing($v)), ]) ); } diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php index 54618690b..4c039d9f4 100644 --- a/src/data/bedrock/block/convert/property/CommonProperties.php +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -45,6 +45,7 @@ use pocketmine\block\utils\CoralMaterial; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\Lightable; use pocketmine\block\utils\MultiAnyFacing; use pocketmine\block\utils\PillarRotation; @@ -69,13 +70,13 @@ final class CommonProperties{ /** @phpstan-var ValueFromStringProperty */ public readonly ValueFromStringProperty $torchFacing; - /** @phpstan-var ValueFromStringProperty */ + /** @phpstan-var ValueFromStringProperty */ public readonly ValueFromStringProperty $horizontalFacingCardinal; - /** @phpstan-var ValueFromIntProperty */ + /** @phpstan-var ValueFromIntProperty */ public readonly ValueFromIntProperty $horizontalFacingSWNE; - /** @phpstan-var ValueFromIntProperty */ + /** @phpstan-var ValueFromIntProperty */ public readonly ValueFromIntProperty $horizontalFacingSWNEInverted; - /** @phpstan-var ValueFromIntProperty */ + /** @phpstan-var ValueFromIntProperty */ public readonly ValueFromIntProperty $horizontalFacingClassic; /** @phpstan-var ValueFromIntProperty */ @@ -208,8 +209,8 @@ final class CommonProperties{ $vm = ValueMappings::getInstance(); //TODO: crude hack here - since we have no HorizontalFacing enum we need to use ints and convert to enum in the accessors - $hfGet = fn(HorizontalFacing $v) => $v->getFacing()->value; - $hfSet = fn(HorizontalFacing $v, int $facing) => $v->setFacing(Facing::from($facing)); + $hfGet = fn(HorizontalFacing $v) => $v->getFacing(); + $hfSet = fn(HorizontalFacing $v, HorizontalFacingOption $facing) => $v->setFacing($facing); $this->horizontalFacingCardinal = new ValueFromStringProperty(StateNames::MC_CARDINAL_DIRECTION, $vm->cardinalDirection, $hfGet, $hfSet); $this->blockFace = new ValueFromStringProperty( @@ -355,15 +356,15 @@ final class CommonProperties{ new BoolProperty(StateNames::OPEN_BIT, fn(Door $b) => $b->isOpen(), fn(Door $b, bool $v) => $b->setOpen($v)), new ValueFromStringProperty( StateNames::MC_CARDINAL_DIRECTION, - IntFromRawStateMap::string([ + EnumFromRawStateMap::string(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { //a door facing "east" is actually facing north - thanks mojang - Facing::NORTH->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_EAST, - Facing::EAST->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH, - Facing::SOUTH->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_WEST, - Facing::WEST->value => BlockStateStringValues::MC_CARDINAL_DIRECTION_NORTH - ]), - fn(HorizontalFacing $b) => $b->getFacing()->value, - fn(HorizontalFacing $b, int $v) => $b->setFacing(Facing::from($v)) + HorizontalFacingOption::NORTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_EAST, + HorizontalFacingOption::EAST => BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH, + HorizontalFacingOption::SOUTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_WEST, + HorizontalFacingOption::WEST => BlockStateStringValues::MC_CARDINAL_DIRECTION_NORTH + }), + fn(HorizontalFacing $b) => $b->getFacing(), + fn(HorizontalFacing $b, HorizontalFacingOption $v) => $b->setFacing($v) ) ]; diff --git a/src/data/bedrock/block/convert/property/ValueMappings.php b/src/data/bedrock/block/convert/property/ValueMappings.php index f51c4fc5b..ef2bf45cf 100644 --- a/src/data/bedrock/block/convert/property/ValueMappings.php +++ b/src/data/bedrock/block/convert/property/ValueMappings.php @@ -29,6 +29,7 @@ use pocketmine\block\utils\DirtType; use pocketmine\block\utils\DripleafState; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\FroglightType; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\LeverFacing; use pocketmine\block\utils\MobHeadType; use pocketmine\block\utils\MushroomBlockType; @@ -63,8 +64,8 @@ final class ValueMappings{ /** @phpstan-var EnumFromRawStateMap */ public readonly EnumFromRawStateMap $mushroomBlockType; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $cardinalDirection; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $cardinalDirection; /** @phpstan-var EnumFromRawStateMap */ public readonly EnumFromRawStateMap $blockFace; /** @phpstan-var EnumFromRawStateMap */ @@ -76,16 +77,16 @@ final class ValueMappings{ /** @phpstan-var IntFromRawStateMap */ public readonly IntFromRawStateMap $bambooLeafSize; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $horizontalFacing5Minus; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $horizontalFacingSWNE; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $horizontalFacingSWNEInverted; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $horizontalFacingCoral; - /** @phpstan-var IntFromRawStateMap */ - public readonly IntFromRawStateMap $horizontalFacingClassic; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $horizontalFacing5Minus; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $horizontalFacingSWNE; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $horizontalFacingSWNEInverted; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $horizontalFacingCoral; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $horizontalFacingClassic; /** @phpstan-var EnumFromRawStateMap */ public readonly EnumFromRawStateMap $facing; /** @phpstan-var EnumFromRawStateMap */ @@ -191,12 +192,12 @@ final class ValueMappings{ ); //TODO: this can't use EnumFromRawStateMap until we have a dedicated HorizontalFacing enum - $this->cardinalDirection = IntFromRawStateMap::string([ - Facing::NORTH->value => StringValues::MC_CARDINAL_DIRECTION_NORTH, - Facing::SOUTH->value => StringValues::MC_CARDINAL_DIRECTION_SOUTH, - Facing::WEST->value => StringValues::MC_CARDINAL_DIRECTION_WEST, - Facing::EAST->value => StringValues::MC_CARDINAL_DIRECTION_EAST, - ]); + $this->cardinalDirection = EnumFromRawStateMap::string(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { + HorizontalFacingOption::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH, + HorizontalFacingOption::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH, + HorizontalFacingOption::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST, + HorizontalFacingOption::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST, + }); $this->blockFace = EnumFromRawStateMap::string(Facing::class, fn(Facing $case) => match ($case) { Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN, Facing::UP => StringValues::MC_BLOCK_FACE_UP, @@ -232,39 +233,36 @@ final class ValueMappings{ Bamboo::LARGE_LEAVES => StringValues::BAMBOO_LEAF_SIZE_LARGE_LEAVES, ]); - $this->horizontalFacing5Minus = IntFromRawStateMap::int([ - Facing::EAST->value => 0, - Facing::WEST->value => 1, - Facing::SOUTH->value => 2, - Facing::NORTH->value => 3 - ]); - $this->horizontalFacingSWNE = IntFromRawStateMap::int([ - Facing::SOUTH->value => 0, - Facing::WEST->value => 1, - Facing::NORTH->value => 2, - Facing::EAST->value => 3 - ]); - $this->horizontalFacingSWNEInverted = IntFromRawStateMap::int([ - Facing::NORTH->value => 0, - Facing::EAST->value => 1, - Facing::SOUTH->value => 2, - Facing::WEST->value => 3, - ]); - $this->horizontalFacingCoral = IntFromRawStateMap::int([ - Facing::WEST->value => 0, - Facing::EAST->value => 1, - Facing::NORTH->value => 2, - Facing::SOUTH->value => 3 - ]); - $horizontalFacingClassicTable = [ - Facing::NORTH->value => 2, - Facing::SOUTH->value => 3, - Facing::WEST->value => 4, - Facing::EAST->value => 5 - ]; - $this->horizontalFacingClassic = IntFromRawStateMap::int($horizontalFacingClassicTable, deserializeAliases: [ - Facing::NORTH->value => [0, 1] //should be illegal but still technically possible - ]); + $this->horizontalFacing5Minus = EnumFromRawStateMap::int(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { + HorizontalFacingOption::EAST => 0, + HorizontalFacingOption::WEST => 1, + HorizontalFacingOption::SOUTH => 2, + HorizontalFacingOption::NORTH => 3 + }); + $this->horizontalFacingSWNE = EnumFromRawStateMap::int(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { + HorizontalFacingOption::SOUTH => 0, + HorizontalFacingOption::WEST => 1, + HorizontalFacingOption::NORTH => 2, + HorizontalFacingOption::EAST => 3 + }); + $this->horizontalFacingSWNEInverted = EnumFromRawStateMap::int(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { + HorizontalFacingOption::NORTH => 0, + HorizontalFacingOption::EAST => 1, + HorizontalFacingOption::SOUTH => 2, + HorizontalFacingOption::WEST => 3, + }); + $this->horizontalFacingCoral = EnumFromRawStateMap::int(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { + HorizontalFacingOption::WEST => 0, + HorizontalFacingOption::EAST => 1, + HorizontalFacingOption::NORTH => 2, + HorizontalFacingOption::SOUTH => 3 + }); + $this->horizontalFacingClassic = EnumFromRawStateMap::int(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { + HorizontalFacingOption::NORTH => 2, + HorizontalFacingOption::SOUTH => 3, + HorizontalFacingOption::WEST => 4, + HorizontalFacingOption::EAST => 5 + }, aliasMapper: fn(HorizontalFacingOption $case) => $case === HorizontalFacingOption::NORTH ? [0, 1] : []); //should be illegal but still technically possible $this->facing = EnumFromRawStateMap::int(Facing::class, fn(Facing $case) => match ($case) { Facing::DOWN => 0, @@ -285,6 +283,13 @@ final class ValueMappings{ Facing::WEST => 5, }); + //these can't be migrated to enums yet + $horizontalFacingClassicTable = [ + Facing::NORTH->value => 2, + Facing::SOUTH->value => 3, + Facing::WEST->value => 4, + Facing::EAST->value => 5 + ]; $this->coralAxis = IntFromRawStateMap::int([ Axis::X->value => 0, Axis::Z->value => 1, diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index b06133022..addcc4164 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\runtime; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; @@ -46,15 +47,13 @@ interface RuntimeDataDescriber{ public function bool(bool &$value) : void; - public function horizontalFacing(Facing &$facing) : void; - /** * @param Facing[] $faces */ public function facingFlags(array &$faces) : void; /** - * @param Facing[] $faces + * @param HorizontalFacingOption[] $faces */ public function horizontalFacingFlags(array &$faces) : void; diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php index 64e5e0f80..d79aafee4 100644 --- a/src/data/runtime/RuntimeDataReader.php +++ b/src/data/runtime/RuntimeDataReader.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\runtime; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\RailConnectionInfo; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; @@ -77,16 +78,6 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $value = $this->readBool(); } - public function horizontalFacing(Facing &$facing) : void{ - $facing = match($this->readInt(2)){ - 0 => Facing::NORTH, - 1 => Facing::EAST, - 2 => Facing::SOUTH, - 3 => Facing::WEST, - default => throw new AssumptionFailedError("Unreachable") - }; - } - /** * @param Facing[] $faces */ @@ -102,11 +93,11 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ } /** - * @param Facing[] $faces + * @param HorizontalFacingOption[] $faces */ public function horizontalFacingFlags(array &$faces) : void{ $result = []; - foreach(Facing::HORIZONTAL as $facing){ + foreach(HorizontalFacingOption::cases() as $facing){ if($this->readBool()){ $result[$facing->value] = $facing; } diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php index 213279cb1..09551ee1f 100644 --- a/src/data/runtime/RuntimeDataSizeCalculator.php +++ b/src/data/runtime/RuntimeDataSizeCalculator.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\runtime; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\math\Axis; use pocketmine\math\Facing; use function count; @@ -51,16 +52,12 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ $this->addBits(1); } - public function horizontalFacing(Facing &$facing) : void{ - $this->addBits(2); - } - public function facingFlags(array &$faces) : void{ $this->addBits(count(Facing::cases())); } public function horizontalFacingFlags(array &$faces) : void{ - $this->addBits(count(Facing::HORIZONTAL)); + $this->addBits(count(HorizontalFacingOption::cases())); } public function facingExcept(Facing &$facing, Facing $except) : void{ diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php index c36a436d2..1cab259de 100644 --- a/src/data/runtime/RuntimeDataWriter.php +++ b/src/data/runtime/RuntimeDataWriter.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\runtime; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; @@ -73,16 +74,6 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->writeBool($value); } - public function horizontalFacing(Facing &$facing) : void{ - $this->writeInt(2, match($facing){ - Facing::NORTH => 0, - Facing::EAST => 1, - Facing::SOUTH => 2, - Facing::WEST => 3, - default => throw new \InvalidArgumentException("Invalid horizontal facing $facing->name") - }); - } - /** * @param Facing[] $faces */ @@ -97,14 +88,14 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ } /** - * @param Facing[] $faces + * @param HorizontalFacingOption[] $faces */ public function horizontalFacingFlags(array &$faces) : void{ $uniqueFaces = []; foreach($faces as $face){ $uniqueFaces[$face->value] = true; } - foreach(Facing::HORIZONTAL as $facing){ + foreach(HorizontalFacingOption::cases() as $facing){ $this->writeBool(isset($uniqueFaces[$facing->value])); } } From 7449ad5637621661dba33d88b87b8d6f21dcfb1f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 23:12:58 +0100 Subject: [PATCH 132/140] tidy --- src/block/CocoaBlock.php | 1 - src/block/Ladder.php | 1 - src/block/OminousWallBanner.php | 1 - src/block/Pumpkin.php | 1 - src/block/RedstoneComparator.php | 2 -- src/block/RedstoneRepeater.php | 3 --- src/block/TripwireHook.php | 1 - src/block/Vine.php | 2 -- src/block/WallBanner.php | 1 - src/block/WallCoralFan.php | 1 - src/block/WallSign.php | 1 - src/block/utils/HorizontalFacingTrait.php | 2 -- 12 files changed, 17 deletions(-) diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index f149dd153..8cc60872c 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -34,7 +34,6 @@ use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\item\VanillaItems; -use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; diff --git a/src/block/Ladder.php b/src/block/Ladder.php index 40d234051..03c9ea57f 100644 --- a/src/block/Ladder.php +++ b/src/block/Ladder.php @@ -30,7 +30,6 @@ use pocketmine\block\utils\SupportType; use pocketmine\entity\Entity; use pocketmine\entity\Living; use pocketmine\item\Item; -use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; diff --git a/src/block/OminousWallBanner.php b/src/block/OminousWallBanner.php index 3f59d82fb..78b9e9024 100644 --- a/src/block/OminousWallBanner.php +++ b/src/block/OminousWallBanner.php @@ -27,7 +27,6 @@ use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; -use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; diff --git a/src/block/Pumpkin.php b/src/block/Pumpkin.php index 8a21b66de..668b10b98 100644 --- a/src/block/Pumpkin.php +++ b/src/block/Pumpkin.php @@ -30,7 +30,6 @@ use pocketmine\item\VanillaItems; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use function in_array; class Pumpkin extends Opaque{ diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php index 18300982c..ad00fc8b3 100644 --- a/src/block/RedstoneComparator.php +++ b/src/block/RedstoneComparator.php @@ -28,7 +28,6 @@ use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; -use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; @@ -39,7 +38,6 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use pocketmine\world\BlockTransaction; use function assert; class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter, PoweredByRedstone, HorizontalFacing{ diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php index 3e65d3090..1b9ee249b 100644 --- a/src/block/RedstoneRepeater.php +++ b/src/block/RedstoneRepeater.php @@ -25,8 +25,6 @@ namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; -use pocketmine\block\utils\HorizontalFacingOption; -use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; @@ -37,7 +35,6 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use pocketmine\world\BlockTransaction; class RedstoneRepeater extends Flowable implements PoweredByRedstone, HorizontalFacing{ use FacesOppositePlacingPlayerTrait; diff --git a/src/block/TripwireHook.php b/src/block/TripwireHook.php index 89ceea60c..3f82985b0 100644 --- a/src/block/TripwireHook.php +++ b/src/block/TripwireHook.php @@ -28,7 +28,6 @@ use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; -use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; diff --git a/src/block/Vine.php b/src/block/Vine.php index 8a83ba28f..8dfb0520e 100644 --- a/src/block/Vine.php +++ b/src/block/Vine.php @@ -23,12 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\item\Item; -use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php index b367ab3d9..5b3c3dc1c 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -27,7 +27,6 @@ use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; -use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; diff --git a/src/block/WallCoralFan.php b/src/block/WallCoralFan.php index 89d76cf17..b6bfac73f 100644 --- a/src/block/WallCoralFan.php +++ b/src/block/WallCoralFan.php @@ -29,7 +29,6 @@ use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; use pocketmine\item\VanillaItems; -use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; diff --git a/src/block/WallSign.php b/src/block/WallSign.php index 007c504a5..2df05f97f 100644 --- a/src/block/WallSign.php +++ b/src/block/WallSign.php @@ -27,7 +27,6 @@ use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; -use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; diff --git a/src/block/utils/HorizontalFacingTrait.php b/src/block/utils/HorizontalFacingTrait.php index d10eb0d3b..fb950b4f2 100644 --- a/src/block/utils/HorizontalFacingTrait.php +++ b/src/block/utils/HorizontalFacingTrait.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace pocketmine\block\utils; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\math\Axis; -use pocketmine\math\Facing; trait HorizontalFacingTrait{ protected HorizontalFacingOption $facing = HorizontalFacingOption::NORTH; From 8dc4371385558421aeb2ae4b89931ef985df7259 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 29 Aug 2025 23:33:52 +0100 Subject: [PATCH 133/140] Enums for rail shapes, sort of this makes the API more sane, although the internals will need to be rewritten at some point in the future. --- src/block/Rail.php | 19 ++++----- src/block/StraightOnlyRail.php | 19 ++++----- src/block/utils/RailShape.php | 41 +++++++++++++++++++ src/block/utils/StraightOnlyRailShape.php | 33 +++++++++++++++ .../block/convert/VanillaBlockMappings.php | 21 +++++++--- .../convert/property/CommonProperties.php | 13 ++++++ src/data/runtime/RuntimeDataDescriber.php | 4 -- src/data/runtime/RuntimeDataReader.php | 19 --------- .../runtime/RuntimeDataSizeCalculator.php | 8 ---- src/data/runtime/RuntimeDataWriter.php | 8 ---- 10 files changed, 117 insertions(+), 68 deletions(-) create mode 100644 src/block/utils/RailShape.php create mode 100644 src/block/utils/StraightOnlyRailShape.php diff --git a/src/block/Rail.php b/src/block/Rail.php index faed53c9b..f904c238d 100644 --- a/src/block/Rail.php +++ b/src/block/Rail.php @@ -24,18 +24,16 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\RailConnectionInfo; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; +use pocketmine\block\utils\RailShape; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\math\Facing; -use function array_keys; -use function implode; class Rail extends BaseRail{ - private int $railShape = BlockLegacyMetadata::RAIL_STRAIGHT_NORTH_SOUTH; + private RailShape $railShape = RailShape::FLAT_AXIS_Z; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->railShape($this->railShape); + $w->enum($this->railShape); } protected function setShapeFromConnections(array $connections) : void{ @@ -43,11 +41,11 @@ class Rail extends BaseRail{ if($railShape === null){ throw new \InvalidArgumentException("No rail shape matches these connections"); } - $this->railShape = $railShape; + $this->railShape = RailShape::from($railShape); } protected function getCurrentShapeConnections() : array{ - return RailConnectionInfo::CURVE_CONNECTIONS[$this->railShape] ?? RailConnectionInfo::CONNECTIONS[$this->railShape]; + return RailConnectionInfo::CURVE_CONNECTIONS[$this->railShape->value] ?? RailConnectionInfo::CONNECTIONS[$this->railShape->value]; } protected function getPossibleConnectionDirectionsOneConstraint(int $constraint) : array{ @@ -69,13 +67,10 @@ class Rail extends BaseRail{ return $possible; } - public function getShape() : int{ return $this->railShape; } + public function getShape() : RailShape{ return $this->railShape; } /** @return $this */ - public function setShape(int $shape) : self{ - if(!isset(RailConnectionInfo::CONNECTIONS[$shape]) && !isset(RailConnectionInfo::CURVE_CONNECTIONS[$shape])){ - throw new \InvalidArgumentException("Invalid shape, must be one of " . implode(", ", [...array_keys(RailConnectionInfo::CONNECTIONS), ...array_keys(RailConnectionInfo::CURVE_CONNECTIONS)])); - } + public function setShape(RailShape $shape) : self{ $this->railShape = $shape; return $this; } diff --git a/src/block/StraightOnlyRail.php b/src/block/StraightOnlyRail.php index 054983dbc..5a09474a5 100644 --- a/src/block/StraightOnlyRail.php +++ b/src/block/StraightOnlyRail.php @@ -24,20 +24,18 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\RailConnectionInfo; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; +use pocketmine\block\utils\StraightOnlyRailShape; use pocketmine\data\runtime\RuntimeDataDescriber; -use function array_keys; -use function implode; /** * Simple non-curvable rail. */ class StraightOnlyRail extends BaseRail{ - private int $railShape = BlockLegacyMetadata::RAIL_STRAIGHT_NORTH_SOUTH; + private StraightOnlyRailShape $railShape = StraightOnlyRailShape::FLAT_AXIS_Z; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->straightOnlyRailShape($this->railShape); + $w->enum($this->railShape); } protected function setShapeFromConnections(array $connections) : void{ @@ -45,20 +43,17 @@ class StraightOnlyRail extends BaseRail{ if($railShape === null){ throw new \InvalidArgumentException("No rail shape matches these connections"); } - $this->railShape = $railShape; + $this->railShape = StraightOnlyRailShape::from($railShape); } protected function getCurrentShapeConnections() : array{ - return RailConnectionInfo::CONNECTIONS[$this->railShape]; + return RailConnectionInfo::CONNECTIONS[$this->railShape->value]; } - public function getShape() : int{ return $this->railShape; } + public function getShape() : StraightOnlyRailShape{ return $this->railShape; } /** @return $this */ - public function setShape(int $shape) : self{ - if(!isset(RailConnectionInfo::CONNECTIONS[$shape])){ - throw new \InvalidArgumentException("Invalid rail shape, must be one of " . implode(", ", array_keys(RailConnectionInfo::CONNECTIONS))); - } + public function setShape(StraightOnlyRailShape $shape) : self{ $this->railShape = $shape; return $this; diff --git a/src/block/utils/RailShape.php b/src/block/utils/RailShape.php new file mode 100644 index 000000000..dfd0d09a1 --- /dev/null +++ b/src/block/utils/RailShape.php @@ -0,0 +1,41 @@ +value; + case FLAT_AXIS_X = RailShape::FLAT_AXIS_X->value; + case ASCENDING_EAST = RailShape::ASCENDING_EAST->value; + case ASCENDING_WEST = RailShape::ASCENDING_WEST->value; + case ASCENDING_NORTH = RailShape::ASCENDING_NORTH->value; + case ASCENDING_SOUTH = RailShape::ASCENDING_SOUTH->value; +} diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index 8bcee6e0b..15fd07659 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -81,7 +81,6 @@ use pocketmine\block\SeaPickle; use pocketmine\block\SmallDripleaf; use pocketmine\block\SnowLayer; use pocketmine\block\Sponge; -use pocketmine\block\StraightOnlyRail; use pocketmine\block\Sugarcane; use pocketmine\block\SweetBerryBush; use pocketmine\block\TNT; @@ -102,6 +101,7 @@ use pocketmine\block\utils\LeverFacing; use pocketmine\block\utils\MobHeadType; use pocketmine\block\utils\MushroomBlockType; use pocketmine\block\utils\PoweredByRedstone; +use pocketmine\block\utils\RailShape; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Vine; use pocketmine\data\bedrock\block\BlockLegacyMetadata; @@ -1215,7 +1215,7 @@ final class VanillaBlockMappings{ //A $reg->mapModel(Model::create(Blocks::ACTIVATOR_RAIL(), Ids::ACTIVATOR_RAIL)->properties([ new BoolProperty(StateNames::RAIL_DATA_BIT, fn(ActivatorRail $b) => $b->isPowered(), fn(ActivatorRail $b, bool $v) => $b->setPowered($v)), - new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(ActivatorRail $b) => $b->getShape(), fn(ActivatorRail $b, int $v) => $b->setShape($v)) + $commonProperties->straightOnlyRailShape ])); //B @@ -1300,7 +1300,7 @@ final class VanillaBlockMappings{ $reg->mapModel(Model::create(Blocks::DEEPSLATE(), Ids::DEEPSLATE)->properties([$commonProperties->pillarAxis])); $reg->mapModel(Model::create(Blocks::DETECTOR_RAIL(), Ids::DETECTOR_RAIL)->properties([ new BoolProperty(StateNames::RAIL_DATA_BIT, fn(DetectorRail $b) => $b->isActivated(), fn(DetectorRail $b, bool $v) => $b->setActivated($v)), - new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail + $commonProperties->straightOnlyRailShape ])); //E @@ -1381,7 +1381,7 @@ final class VanillaBlockMappings{ ])); $reg->mapModel(Model::create(Blocks::POWERED_RAIL(), Ids::GOLDEN_RAIL)->properties([ new BoolProperty(StateNames::RAIL_DATA_BIT, fn(PoweredRail $b) => $b->isPowered(), fn(PoweredRail $b, bool $v) => $b->setPowered($v)), //TODO: shared with ActivatorRail - new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail + $commonProperties->straightOnlyRailShape ])); $reg->mapModel(Model::create(Blocks::PITCHER_PLANT(), Ids::PITCHER_PLANT)->properties([ new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePlant $b) => $b->isTop(), fn(DoublePlant $b, bool $v) => $b->setTop($v)), //TODO: don't we have helpers for this? @@ -1406,7 +1406,18 @@ final class VanillaBlockMappings{ //R $reg->mapModel(Model::create(Blocks::RAIL(), Ids::RAIL)->properties([ - new IntProperty(StateNames::RAIL_DIRECTION, 0, 9, fn(Rail $b) => $b->getShape(), fn(Rail $b, int $v) => $b->setShape($v)) + new ValueFromIntProperty(StateNames::RAIL_DIRECTION, EnumFromRawStateMap::int(RailShape::class, fn(RailShape $case) => match($case){ + RailShape::FLAT_AXIS_Z => BlockLegacyMetadata::RAIL_STRAIGHT_NORTH_SOUTH, + RailShape::FLAT_AXIS_X => BlockLegacyMetadata::RAIL_STRAIGHT_EAST_WEST, + RailShape::ASCENDING_EAST => BlockLegacyMetadata::RAIL_ASCENDING_EAST, + RailShape::ASCENDING_WEST => BlockLegacyMetadata::RAIL_ASCENDING_WEST, + RailShape::ASCENDING_NORTH => BlockLegacyMetadata::RAIL_ASCENDING_NORTH, + RailShape::ASCENDING_SOUTH => BlockLegacyMetadata::RAIL_ASCENDING_SOUTH, + RailShape::CURVED_NORTHEAST => BlockLegacyMetadata::RAIL_CURVE_NORTHEAST, + RailShape::CURVED_NORTHWEST => BlockLegacyMetadata::RAIL_CURVE_NORTHWEST, + RailShape::CURVED_SOUTHEAST => BlockLegacyMetadata::RAIL_CURVE_SOUTHEAST, + RailShape::CURVED_SOUTHWEST => BlockLegacyMetadata::RAIL_CURVE_SOUTHWEST, + }), fn(Rail $b) => $b->getShape(), fn(Rail $b, RailShape $v) => $b->setShape($v)) ])); $reg->mapModel(Model::create(Blocks::REDSTONE_WIRE(), Ids::REDSTONE_WIRE)->properties([$commonProperties->analogRedstoneSignal])); $reg->mapModel(Model::create(Blocks::RESPAWN_ANCHOR(), Ids::RESPAWN_ANCHOR)->properties([ diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php index 4c039d9f4..beb2a421d 100644 --- a/src/data/bedrock/block/convert/property/CommonProperties.php +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -33,6 +33,7 @@ use pocketmine\block\SimplePressurePlate; use pocketmine\block\Slab; use pocketmine\block\Stair; use pocketmine\block\Stem; +use pocketmine\block\StraightOnlyRail; use pocketmine\block\Torch; use pocketmine\block\Trapdoor; use pocketmine\block\utils\Ageable; @@ -51,6 +52,7 @@ use pocketmine\block\utils\MultiAnyFacing; use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SlabType; +use pocketmine\block\utils\StraightOnlyRailShape; use pocketmine\block\Wall; use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockLegacyMetadata; @@ -95,6 +97,8 @@ final class CommonProperties{ public readonly IntProperty $cropAgeMax7; /** @phpstan-var BoolProperty */ public readonly BoolProperty $doublePlantHalf; + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $straightOnlyRailShape; /** @phpstan-var IntProperty */ public readonly IntProperty $liquidData; @@ -266,6 +270,15 @@ final class CommonProperties{ $this->cropAgeMax7 = new IntProperty(StateNames::GROWTH, 0, 7, fn(Ageable $b) => $b->getAge(), fn(Ageable $b, int $v) => $b->setAge($v)); $this->doublePlantHalf = new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePlant $b) => $b->isTop(), fn(DoublePlant $b, bool $v) => $b->setTop($v)); + $this->straightOnlyRailShape = new ValueFromIntProperty(StateNames::RAIL_DIRECTION, EnumFromRawStateMap::int(StraightOnlyRailShape::class, fn(StraightOnlyRailShape $case) => match($case){ + StraightOnlyRailShape::FLAT_AXIS_Z => BlockLegacyMetadata::RAIL_STRAIGHT_NORTH_SOUTH, + StraightOnlyRailShape::FLAT_AXIS_X => BlockLegacyMetadata::RAIL_STRAIGHT_EAST_WEST, + StraightOnlyRailShape::ASCENDING_EAST => BlockLegacyMetadata::RAIL_ASCENDING_EAST, + StraightOnlyRailShape::ASCENDING_WEST => BlockLegacyMetadata::RAIL_ASCENDING_WEST, + StraightOnlyRailShape::ASCENDING_NORTH => BlockLegacyMetadata::RAIL_ASCENDING_NORTH, + StraightOnlyRailShape::ASCENDING_SOUTH => BlockLegacyMetadata::RAIL_ASCENDING_SOUTH + }), fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, StraightOnlyRailShape $v) => $b->setShape($v)); + $fallingFlag = BlockLegacyMetadata::LIQUID_FALLING_FLAG; $this->liquidData = new IntProperty( StateNames::LIQUID_DEPTH, diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index addcc4164..4fab5fc34 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -67,10 +67,6 @@ interface RuntimeDataDescriber{ */ public function wallConnections(array &$connections) : void; - public function railShape(int &$railShape) : void; - - public function straightOnlyRailShape(int &$railShape) : void; - /** * @phpstan-template T of \UnitEnum * @phpstan-param T &$case diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php index d79aafee4..abc4b2a84 100644 --- a/src/data/runtime/RuntimeDataReader.php +++ b/src/data/runtime/RuntimeDataReader.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; use pocketmine\block\utils\HorizontalFacingOption; -use pocketmine\block\utils\RailConnectionInfo; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; @@ -147,24 +146,6 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $connections = $result; } - public function railShape(int &$railShape) : void{ - $result = $this->readInt(4); - if(!isset(RailConnectionInfo::CONNECTIONS[$result]) && !isset(RailConnectionInfo::CURVE_CONNECTIONS[$result])){ - throw new InvalidSerializedRuntimeDataException("Invalid rail shape $result"); - } - - $railShape = $result; - } - - public function straightOnlyRailShape(int &$railShape) : void{ - $result = $this->readInt(3); - if(!isset(RailConnectionInfo::CONNECTIONS[$result])){ - throw new InvalidSerializedRuntimeDataException("No rail shape matches meta $result"); - } - - $railShape = $result; - } - public function enum(\UnitEnum &$case) : void{ $metadata = RuntimeEnumMetadata::from($case); $raw = $this->readInt($metadata->bits); diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php index 09551ee1f..6efd7ed8a 100644 --- a/src/data/runtime/RuntimeDataSizeCalculator.php +++ b/src/data/runtime/RuntimeDataSizeCalculator.php @@ -72,14 +72,6 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ $this->addBits(7); } - public function railShape(int &$railShape) : void{ - $this->addBits(4); - } - - public function straightOnlyRailShape(int &$railShape) : void{ - $this->addBits(3); - } - public function enum(\UnitEnum &$case) : void{ $metadata = RuntimeEnumMetadata::from($case); $this->addBits($metadata->bits); diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php index 1cab259de..e469ac26e 100644 --- a/src/data/runtime/RuntimeDataWriter.php +++ b/src/data/runtime/RuntimeDataWriter.php @@ -130,14 +130,6 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->writeBoundedIntAuto(0, (3 ** 4) - 1, $packed); } - public function railShape(int &$railShape) : void{ - $this->int(4, $railShape); - } - - public function straightOnlyRailShape(int &$railShape) : void{ - $this->int(3, $railShape); - } - public function enum(\UnitEnum &$case) : void{ $metadata = RuntimeEnumMetadata::from($case); $this->writeInt($metadata->bits, $metadata->enumToInt($case)); From 4e82482a80335594c8b7460c0c2287fce3bbdeae Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Aug 2025 18:10:44 +0100 Subject: [PATCH 134/140] Use generic enumSet() for blocks with facing flags --- src/block/Vine.php | 17 +++++------ src/block/utils/MultiAnyFacingTrait.php | 11 ++++---- src/data/runtime/RuntimeDataDescriber.php | 10 ------- src/data/runtime/RuntimeDataReader.php | 28 ------------------- .../runtime/RuntimeDataSizeCalculator.php | 8 ------ src/data/runtime/RuntimeDataWriter.php | 26 ----------------- 6 files changed, 15 insertions(+), 85 deletions(-) diff --git a/src/block/Vine.php b/src/block/Vine.php index 8dfb0520e..0e6f83b12 100644 --- a/src/block/Vine.php +++ b/src/block/Vine.php @@ -33,6 +33,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function array_intersect_key; use function count; +use function spl_object_id; class Vine extends Flowable{ @@ -40,14 +41,14 @@ class Vine extends Flowable{ protected array $faces = []; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacingFlags($this->faces); + $w->enumSet($this->faces, HorizontalFacingOption::cases()); } /** @return HorizontalFacingOption[] */ public function getFaces() : array{ return $this->faces; } public function hasFace(HorizontalFacingOption $face) : bool{ - return isset($this->faces[$face->value]); + return isset($this->faces[spl_object_id($face)]); } /** @@ -60,7 +61,7 @@ class Vine extends Flowable{ if(!$face instanceof HorizontalFacingOption){ throw new \InvalidArgumentException("Expected array of HorizontalFacingOption"); } - $uniqueFaces[$face->value] = $face; + $uniqueFaces[spl_object_id($face)] = $face; } $this->faces = $uniqueFaces; return $this; @@ -69,9 +70,9 @@ class Vine extends Flowable{ /** @return $this */ public function setFace(HorizontalFacingOption $face, bool $value) : self{ if($value){ - $this->faces[$face->value] = $face; + $this->faces[spl_object_id($face)] = $face; }else{ - unset($this->faces[$face->value]); + unset($this->faces[spl_object_id($face)]); } return $this; } @@ -105,7 +106,7 @@ class Vine extends Flowable{ } $this->faces = $blockReplace instanceof Vine ? $blockReplace->faces : []; - $this->faces[$hzFacing->value] = $hzFacing; + $this->faces[spl_object_id($hzFacing)] = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -118,8 +119,8 @@ class Vine extends Flowable{ $supportedFaces = $up instanceof Vine ? array_intersect_key($this->faces, $up->faces) : []; foreach($this->faces as $face){ - if(!isset($supportedFaces[$face->value]) && !$this->getSide($face->toFacing())->isSolid()){ - unset($this->faces[$face->value]); + if(!isset($supportedFaces[spl_object_id($face)]) && !$this->getSide($face->toFacing())->isSolid()){ + unset($this->faces[spl_object_id($face)]); $changed = true; } } diff --git a/src/block/utils/MultiAnyFacingTrait.php b/src/block/utils/MultiAnyFacingTrait.php index b7cd77901..b465a7d50 100644 --- a/src/block/utils/MultiAnyFacingTrait.php +++ b/src/block/utils/MultiAnyFacingTrait.php @@ -25,6 +25,7 @@ namespace pocketmine\block\utils; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\math\Facing; +use function spl_object_id; /** * Used by blocks that can have multiple target faces in the area of one solid block, such as covering three sides of a corner. @@ -35,14 +36,14 @@ trait MultiAnyFacingTrait{ protected array $faces = []; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facingFlags($this->faces); + $w->enumSet($this->faces, Facing::cases()); } /** @return Facing[] */ public function getFaces() : array{ return $this->faces; } public function hasFace(Facing $face) : bool{ - return isset($this->faces[$face->value]); + return isset($this->faces[spl_object_id($face)]); } /** @@ -52,7 +53,7 @@ trait MultiAnyFacingTrait{ public function setFaces(array $faces) : self{ $uniqueFaces = []; foreach($faces as $face){ - $uniqueFaces[$face->value] = $face; + $uniqueFaces[spl_object_id($face)] = $face; } $this->faces = $uniqueFaces; return $this; @@ -61,9 +62,9 @@ trait MultiAnyFacingTrait{ /** @return $this */ public function setFace(Facing $face, bool $value) : self{ if($value){ - $this->faces[$face->value] = $face; + $this->faces[spl_object_id($face)] = $face; }else{ - unset($this->faces[$face->value]); + unset($this->faces[spl_object_id($face)]); } return $this; } diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 4fab5fc34..cf6d93135 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -47,16 +47,6 @@ interface RuntimeDataDescriber{ public function bool(bool &$value) : void; - /** - * @param Facing[] $faces - */ - public function facingFlags(array &$faces) : void; - - /** - * @param HorizontalFacingOption[] $faces - */ - public function horizontalFacingFlags(array &$faces) : void; - public function facingExcept(Facing &$facing, Facing $except) : void; public function horizontalAxis(Axis &$axis) : void; diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php index abc4b2a84..58845c49e 100644 --- a/src/data/runtime/RuntimeDataReader.php +++ b/src/data/runtime/RuntimeDataReader.php @@ -77,34 +77,6 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $value = $this->readBool(); } - /** - * @param Facing[] $faces - */ - public function facingFlags(array &$faces) : void{ - $result = []; - foreach(Facing::ALL as $facing){ - if($this->readBool()){ - $result[$facing->value] = $facing; - } - } - - $faces = $result; - } - - /** - * @param HorizontalFacingOption[] $faces - */ - public function horizontalFacingFlags(array &$faces) : void{ - $result = []; - foreach(HorizontalFacingOption::cases() as $facing){ - if($this->readBool()){ - $result[$facing->value] = $facing; - } - } - - $faces = $result; - } - public function facingExcept(Facing &$facing, Facing $except) : void{ $result = Facing::DOWN; $this->enum($result); diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php index 6efd7ed8a..ba6f7afd1 100644 --- a/src/data/runtime/RuntimeDataSizeCalculator.php +++ b/src/data/runtime/RuntimeDataSizeCalculator.php @@ -52,14 +52,6 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ $this->addBits(1); } - public function facingFlags(array &$faces) : void{ - $this->addBits(count(Facing::cases())); - } - - public function horizontalFacingFlags(array &$faces) : void{ - $this->addBits(count(HorizontalFacingOption::cases())); - } - public function facingExcept(Facing &$facing, Facing $except) : void{ $this->enum($facing); } diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php index e469ac26e..a2be0c0f2 100644 --- a/src/data/runtime/RuntimeDataWriter.php +++ b/src/data/runtime/RuntimeDataWriter.php @@ -74,32 +74,6 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->writeBool($value); } - /** - * @param Facing[] $faces - */ - public function facingFlags(array &$faces) : void{ - $uniqueFaces = []; - foreach($faces as $face){ - $uniqueFaces[$face->value] = true; - } - foreach(Facing::ALL as $facing){ - $this->writeBool(isset($uniqueFaces[$facing->value])); - } - } - - /** - * @param HorizontalFacingOption[] $faces - */ - public function horizontalFacingFlags(array &$faces) : void{ - $uniqueFaces = []; - foreach($faces as $face){ - $uniqueFaces[$face->value] = true; - } - foreach(HorizontalFacingOption::cases() as $facing){ - $this->writeBool(isset($uniqueFaces[$facing->value])); - } - } - public function facingExcept(Facing &$facing, Facing $except) : void{ $this->enum($facing); } From 95679b5a297fe55291d8a3cfbda81879b40e0b23 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Aug 2025 18:36:35 +0100 Subject: [PATCH 135/140] Update BedrockData and some transient deps --- composer.json | 2 +- composer.lock | 99 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/composer.json b/composer.json index e66269b40..17271955e 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", - "pocketmine/bedrock-data": "~5.3.0+bedrock-1.21.100", + "pocketmine/bedrock-data": "~6.0.0+bedrock-1.21.100", "pocketmine/bedrock-item-upgrade-schema": "~1.15.0+bedrock-1.21.100", "pocketmine/bedrock-protocol": "~40.0.0+bedrock-1.21.100", "pocketmine/binaryutils": "^0.2.1", diff --git a/composer.lock b/composer.lock index 9a69e6e14..330f002d7 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": "402ad5667b1e636a8ec6acf2f1b5f055", + "content-hash": "27fee330bdcb6ea2373c57cdfb3bc22f", "packages": [ { "name": "adhocore/json-comment", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "5.3.0+bedrock-1.21.100", + "version": "6.0.0+bedrock-1.21.100", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "5279e76261df158d5af187cfaafc1618c1da9e3f" + "reference": "edc0d829175e5e1e57c87001acfd03526c63fd34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/5279e76261df158d5af187cfaafc1618c1da9e3f", - "reference": "5279e76261df158d5af187cfaafc1618c1da9e3f", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/edc0d829175e5e1e57c87001acfd03526c63fd34", + "reference": "edc0d829175e5e1e57c87001acfd03526c63fd34", "shasum": "" }, "type": "library", @@ -224,9 +224,9 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/5.3.0+bedrock-1.21.100" + "source": "https://github.com/pmmp/BedrockData/tree/6.0.0+bedrock-1.21.100" }, - "time": "2025-07-30T22:07:56+00:00" + "time": "2025-08-30T17:25:42+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", @@ -896,16 +896,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", + "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", "shasum": "" }, "require": { @@ -942,7 +942,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/filesystem/tree/v6.4.24" }, "funding": [ { @@ -953,27 +953,31 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2025-07-10T08:14:14+00:00" } ], "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -1012,7 +1016,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -1020,20 +1024,20 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -1052,7 +1056,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -1076,9 +1080,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "phar-io/manifest", @@ -1680,16 +1684,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.47", + "version": "10.5.53", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3" + "reference": "32768472ebfb6969e6c7399f1c7b09009723f653" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", - "reference": "3637b3e50d32ab3a0d1a33b3b6177169ec3d95a3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32768472ebfb6969e6c7399f1c7b09009723f653", + "reference": "32768472ebfb6969e6c7399f1c7b09009723f653", "shasum": "" }, "require": { @@ -1699,7 +1703,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -1716,7 +1720,7 @@ "sebastian/exporter": "^5.1.2", "sebastian/global-state": "^6.0.2", "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", "sebastian/type": "^4.0.0", "sebastian/version": "^4.0.1" }, @@ -1761,7 +1765,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.47" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.53" }, "funding": [ { @@ -1785,7 +1789,7 @@ "type": "tidelift" } ], - "time": "2025-06-20T11:29:11+00:00" + "time": "2025-08-20T14:40:06+00:00" }, { "name": "sebastian/cli-parser", @@ -2533,23 +2537,23 @@ }, { "name": "sebastian/recursion-context", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -2584,15 +2588,28 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-08-10T07:50:56+00:00" }, { "name": "sebastian/type", From 5c363965f0950516b4967b7fc00cb1f6edf4fbe6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Aug 2025 18:43:18 +0100 Subject: [PATCH 136/140] Fix build date we really need a better way to deal with this --- changelogs/5.33.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.33.md b/changelogs/5.33.md index 8c61e90bc..45f03f521 100644 --- a/changelogs/5.33.md +++ b/changelogs/5.33.md @@ -1,5 +1,5 @@ # 5.33.0 -Released 29th August 2025. +Released 30th August 2025. This is a minor feature release containing internals improvements, API improvements and new gameplay features. From f673159471b21097f15c6abb45e4022912496c7c Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 17:46:07 +0000 Subject: [PATCH 137/140] 5.33.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/17346780638 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index dd8bed4b6..1d4179f7d 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.33.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.33.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 851ac29f7195530ff23135ccb9870c3ce262e7a7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Aug 2025 18:56:56 +0100 Subject: [PATCH 138/140] CS --- src/data/runtime/RuntimeDataDescriber.php | 1 - src/data/runtime/RuntimeDataReader.php | 1 - src/data/runtime/RuntimeDataSizeCalculator.php | 1 - src/data/runtime/RuntimeDataWriter.php | 1 - 4 files changed, 4 deletions(-) diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index cf6d93135..df9da2891 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php index 58845c49e..1f05c31e9 100644 --- a/src/data/runtime/RuntimeDataReader.php +++ b/src/data/runtime/RuntimeDataReader.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php index ba6f7afd1..bb30634f1 100644 --- a/src/data/runtime/RuntimeDataSizeCalculator.php +++ b/src/data/runtime/RuntimeDataSizeCalculator.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\math\Axis; use pocketmine\math\Facing; use function count; diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php index a2be0c0f2..05a177706 100644 --- a/src/data/runtime/RuntimeDataWriter.php +++ b/src/data/runtime/RuntimeDataWriter.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; From 06b48d97e9a2562bff5255b0b3d089d3da774c2b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Aug 2025 19:01:14 +0100 Subject: [PATCH 139/140] Fix merge error --- src/network/mcpe/handler/InGamePacketHandler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 5d33f501f..8503f4eec 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -721,7 +721,6 @@ class InGamePacketHandler extends PacketHandler{ case PlayerAction::CREATIVE_PLAYER_DESTROY_BLOCK: //TODO: do we need to handle this? case PlayerAction::PREDICT_DESTROY_BLOCK: - self::validateFacing($face); if(!$this->player->breakBlock($pos)){ $face = self::deserializeFacing($extraData); $this->syncBlocksNearby($pos, $face); From 9a9506b7936cfa9055239adcda59a2de8c2cd067 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Aug 2025 19:23:38 +0100 Subject: [PATCH 140/140] Upgrade CallbackValidator closes #6343 --- composer.json | 2 +- composer.lock | 23 ++++++++++++----------- src/entity/EntityFactory.php | 9 +-------- src/item/enchantment/Enchantment.php | 8 +------- src/scheduler/ClosureTask.php | 4 +--- src/utils/Utils.php | 22 ++++++++-------------- src/world/BlockTransaction.php | 2 +- 7 files changed, 25 insertions(+), 45 deletions(-) diff --git a/composer.json b/composer.json index 859292b39..5d0bcbd05 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "pocketmine/bedrock-item-upgrade-schema": "~1.15.0+bedrock-1.21.100", "pocketmine/bedrock-protocol": "~40.0.0+bedrock-1.21.100", "pocketmine/binaryutils": "^0.2.1", - "pocketmine/callback-validator": "^1.0.2", + "pocketmine/callback-validator": "dev-rewrite", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.7.0", "pocketmine/locale-data": "~2.25.0", diff --git a/composer.lock b/composer.lock index f605564fe..6dd977435 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": "326e0882230a0614d3dd2f0dce8e2efc", + "content-hash": "deb7c003ba4a6101153256d5d590da42", "packages": [ { "name": "adhocore/json-comment", @@ -344,30 +344,30 @@ }, { "name": "pocketmine/callback-validator", - "version": "1.0.3", + "version": "dev-rewrite", "source": { "type": "git", "url": "https://github.com/pmmp/CallbackValidator.git", - "reference": "64787469766bcaa7e5885242e85c23c25e8c55a2" + "reference": "4b9b375590872cdd98a2a07c5aa0e1e99af5ceda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/CallbackValidator/zipball/64787469766bcaa7e5885242e85c23c25e8c55a2", - "reference": "64787469766bcaa7e5885242e85c23c25e8c55a2", + "url": "https://api.github.com/repos/pmmp/CallbackValidator/zipball/4b9b375590872cdd98a2a07c5aa0e1e99af5ceda", + "reference": "4b9b375590872cdd98a2a07c5aa0e1e99af5ceda", "shasum": "" }, "require": { "ext-reflection": "*", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "replace": { "daverandom/callback-validator": "*" }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.59", - "phpstan/phpstan-strict-rules": "^0.12.4", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.0" + "phpstan/phpstan": "2.1.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.0 || ^10.0 || ^11.0" }, "type": "library", "autoload": { @@ -388,9 +388,9 @@ "description": "Fork of daverandom/callback-validator - Tools for validating callback signatures", "support": { "issues": "https://github.com/pmmp/CallbackValidator/issues", - "source": "https://github.com/pmmp/CallbackValidator/tree/1.0.3" + "source": "https://github.com/pmmp/CallbackValidator/tree/rewrite" }, - "time": "2020-12-11T01:45:37+00:00" + "time": "2025-01-03T17:50:24+00:00" }, { "name": "pocketmine/color", @@ -2734,6 +2734,7 @@ ], "minimum-stability": "stable", "stability-flags": { + "pocketmine/callback-validator": 20, "pocketmine/math": 20 }, "prefer-stable": false, diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index 970fd986f..c41f76d64 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -23,9 +23,6 @@ declare(strict_types=1); namespace pocketmine\entity; -use DaveRandom\CallbackValidator\CallbackType; -use DaveRandom\CallbackValidator\ParameterType; -use DaveRandom\CallbackValidator\ReturnType; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\data\bedrock\LegacyEntityIdToStringIdMap; use pocketmine\data\bedrock\PotionTypeIdMap; @@ -206,11 +203,7 @@ final class EntityFactory{ throw new \InvalidArgumentException("At least one save name must be provided"); } Utils::testValidInstance($className, Entity::class); - Utils::validateCallableSignature(new CallbackType( - new ReturnType(Entity::class), - new ParameterType("world", World::class), - new ParameterType("nbt", CompoundTag::class) - ), $creationFunc); + Utils::validateCallableSignature(fn(World $world, CompoundTag $nbt) : Entity => die(), $creationFunc); foreach($saveNames as $name){ $this->creationFuncs[$name] = $creationFunc; diff --git a/src/item/enchantment/Enchantment.php b/src/item/enchantment/Enchantment.php index e8335d5f3..f36943b57 100644 --- a/src/item/enchantment/Enchantment.php +++ b/src/item/enchantment/Enchantment.php @@ -23,9 +23,6 @@ declare(strict_types=1); namespace pocketmine\item\enchantment; -use DaveRandom\CallbackValidator\CallbackType; -use DaveRandom\CallbackValidator\ParameterType; -use DaveRandom\CallbackValidator\ReturnType; use pocketmine\lang\Translatable; use pocketmine\utils\NotCloneable; use pocketmine\utils\NotSerializable; @@ -55,10 +52,7 @@ class Enchantment{ ){ $this->minEnchantingPower = $minEnchantingPower ?? fn(int $level) : int => 1; - Utils::validateCallableSignature(new CallbackType( - new ReturnType("int"), - new ParameterType("level", "int") - ), $this->minEnchantingPower); + Utils::validateCallableSignature(fn(int $level) : int => die(), $this->minEnchantingPower); } /** diff --git a/src/scheduler/ClosureTask.php b/src/scheduler/ClosureTask.php index 4b8166f22..97e12183d 100644 --- a/src/scheduler/ClosureTask.php +++ b/src/scheduler/ClosureTask.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace pocketmine\scheduler; -use DaveRandom\CallbackValidator\CallbackType; -use DaveRandom\CallbackValidator\ReturnType; use pocketmine\utils\Utils; /** @@ -46,7 +44,7 @@ class ClosureTask extends Task{ public function __construct( private \Closure $closure ){ - Utils::validateCallableSignature(new CallbackType(new ReturnType()), $closure); + Utils::validateCallableSignature(function() : void{}, $closure); } public function getName() : string{ diff --git a/src/utils/Utils.php b/src/utils/Utils.php index c132e6636..b3f995213 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -27,7 +27,7 @@ declare(strict_types=1); namespace pocketmine\utils; -use DaveRandom\CallbackValidator\CallbackType; +use DaveRandom\CallbackValidator\Prototype; use pocketmine\entity\Location; use pocketmine\errorhandler\ErrorTypeToStringMap; use pocketmine\math\Vector3; @@ -554,20 +554,14 @@ final class Utils{ * Verifies that the given callable is compatible with the desired signature. Throws a TypeError if they are * incompatible. * - * @param callable|CallbackType $signature Dummy callable with the required parameters and return type - * @param callable $subject Callable to check the signature of - * @phpstan-param anyCallable|CallbackType $signature - * @phpstan-param anyCallable $subject - * - * @throws \DaveRandom\CallbackValidator\InvalidCallbackException - * @throws \TypeError + * @param \Closure $signature Dummy callable with the required parameters and return type + * @param \Closure $subject Callable to check the signature of + * @phpstan-param anyClosure $signature + * @phpstan-param anyClosure $subject */ - public static function validateCallableSignature(callable|CallbackType $signature, callable $subject) : void{ - if(!($signature instanceof CallbackType)){ - $signature = CallbackType::createFromCallable($signature); - } - if(!$signature->isSatisfiedBy($subject)){ - throw new \TypeError("Declaration of callable `" . CallbackType::createFromCallable($subject) . "` must be compatible with `" . $signature . "`"); + public static function validateCallableSignature(\Closure $signature, \Closure $subject) : void{ + if(!Prototype::isSatisfiedBy($signature, $subject)){ + throw new \TypeError("Declaration of callable `" . Prototype::print($subject) . "` must be compatible with `" . Prototype::print($signature) . "`"); } } diff --git a/src/world/BlockTransaction.php b/src/world/BlockTransaction.php index 46cbc7903..103b8cd88 100644 --- a/src/world/BlockTransaction.php +++ b/src/world/BlockTransaction.php @@ -127,7 +127,7 @@ class BlockTransaction{ * @phpstan-param \Closure(ChunkManager $world, int $x, int $y, int $z) : bool $validator */ public function addValidator(\Closure $validator) : void{ - Utils::validateCallableSignature([$this, 'dummyValidator'], $validator); + Utils::validateCallableSignature($this->dummyValidator(...), $validator); $this->validators[] = $validator; }