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 diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 83d568878..acfc3d3a7 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: @@ -28,16 +33,28 @@ 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 - - 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 @@ -53,7 +70,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.18.0 with: push: true context: ./pocketmine-mp @@ -66,33 +83,33 @@ 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.18.0 with: 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 }} - 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.18.0 with: 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 }} - 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.18.0 with: push: true context: ./pocketmine-mp diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..c644876e0 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,47 @@ +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@v5 + + - name: Setup PHP + uses: pmmp/setup-php-action@3.2.0 + with: + php-version: 8.3 + install-path: "./bin" + pm-version-major: 5 + + - name: Restore Composer package cache + uses: actions/cache@v4 + with: + path: | + ~/.cache/composer/files + ~/.cache/composer/vcs + key: "composer-v2-cache-8.3-${{ hashFiles('./composer.lock') }}" + restore-keys: | + composer-v2-cache- + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-interaction + + - name: Clone extension stubs + 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 fde5e3099..c0aed1320 100644 --- a/.github/workflows/discord-release-notify.yml +++ b/.github/workflows/discord-release-notify.yml @@ -4,18 +4,23 @@ on: release: types: - published + workflow_dispatch: + inputs: + release: + description: 'Release to make notification for' + required: true jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.32.0 + uses: shivammathur/setup-php@2.35.3 with: - php-version: 8.2 + php-version: 8.3 - name: Restore Composer package cache uses: actions/cache@v4 @@ -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/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml index 303f61ccf..4410d4dee 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,12 +46,12 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP - uses: shivammathur/setup-php@2.32.0 + uses: shivammathur/setup-php@2.35.3 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 02cdeec6f..c72dd5688 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: @@ -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 }} @@ -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.32.0 + uses: shivammathur/setup-php@2.35.3 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 @@ -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." 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 051a3a790..44093b707 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: @@ -25,13 +25,13 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.32.0 + uses: shivammathur/setup-php@2.35.3 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 }} @@ -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/pr-remove-waiting-label.yml b/.github/workflows/pr-remove-waiting-label.yml index eb46043bd..b7cd85acd 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; + async 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"); 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 }} diff --git a/.github/workflows/update-updater-api.yml b/.github/workflows/update-updater-api.yml index 3f42062fd..841fa7d44 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: @@ -14,14 +19,22 @@ 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 }} - - 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 diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 2effafe17..81221c11c 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -5,6 +5,13 @@ $finder = PhpCsFixer\Finder::create() ->in(__DIR__ . '/build') ->in(__DIR__ . '/tests') ->in(__DIR__ . '/tools') + + //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/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
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/build/php b/build/php index 154943379..ce1b095a9 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 15494337976e645499e2e3e8c8b491227522be91 +Subproject commit ce1b095a9c6f47dadc7b5812da4e469d52f272bc diff --git a/changelogs/5.28.md b/changelogs/5.28.md new file mode 100644 index 000000000..f378031f7 --- /dev/null +++ b/changelogs/5.28.md @@ -0,0 +1,34 @@ +# 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 +- Added support for Minecraft: Bedrock Edition 1.21.80. +- 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. + +# 5.28.1 +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/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/changelogs/5.30.md b/changelogs/5.30.md new file mode 100644 index 000000000..cc2ecbc1f --- /dev/null +++ b/changelogs/5.30.md @@ -0,0 +1,73 @@ +# 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\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` + - `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). + +# 5.30.1 +Released 23rd June 2025. + +## Fixes +- 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. 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/changelogs/5.32.md b/changelogs/5.32.md new file mode 100644 index 000000000..414330351 --- /dev/null +++ b/changelogs/5.32.md @@ -0,0 +1,25 @@ +# 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). + +# 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/changelogs/5.33.md b/changelogs/5.33.md new file mode 100644 index 000000000..45f03f521 --- /dev/null +++ b/changelogs/5.33.md @@ -0,0 +1,129 @@ +# 5.33.0 +Released 30th 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` + - `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. + - 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\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. + +## 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/composer.json b/composer.json index 583116c0f..5d0bcbd05 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", @@ -34,28 +34,32 @@ "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-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~37.0.0+bedrock-1.21.70", + "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", - "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.24.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.1.2", + "pocketmine/raklib": "~1.2.0", "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", - "ramsey/uuid": "~4.7.0", - "symfony/filesystem": "~6.4.0" + "ramsey/uuid": "~4.9.0", + "symfony/filesystem": "~7.3.0" }, "require-dev": { - "phpstan/phpstan": "2.1.11", + "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": "*", + "symfony/polyfill-mbstring": "*" }, "autoload": { "psr-4": { @@ -73,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 23f312317..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": "818c679a25da8e6b466bc454ad48dec3", + "content-hash": "deb7c003ba4a6101153256d5d590da42", "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", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "4.1.0+bedrock-1.21.70", + "version": "6.0.0+bedrock-1.21.100", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "d53fe98cb3b596ac016e275df5bd5e89b04a4817" + "reference": "edc0d829175e5e1e57c87001acfd03526c63fd34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d53fe98cb3b596ac016e275df5bd5e89b04a4817", - "reference": "d53fe98cb3b596ac016e275df5bd5e89b04a4817", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/edc0d829175e5e1e57c87001acfd03526c63fd34", + "reference": "edc0d829175e5e1e57c87001acfd03526c63fd34", "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.70" + "source": "https://github.com/pmmp/BedrockData/tree/6.0.0+bedrock-1.21.100" }, - "time": "2025-03-25T19:43:31+00:00" + "time": "2025-08-30T17:25:42+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": "37.0.0+bedrock-1.21.70", + "version": "40.0.0+bedrock-1.21.100", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "7091dad2c12ed4a4106432df21fc698960c6be9e" + "reference": "5e95cab3a6e6c24920e0c25ca4aaf887ed4b70ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/7091dad2c12ed4a4106432df21fc698960c6be9e", - "reference": "7091dad2c12ed4a4106432df21fc698960c6be9e", + "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/37.0.0+bedrock-1.21.70" + "source": "https://github.com/pmmp/BedrockProtocol/tree/40.0.0+bedrock-1.21.100" }, - "time": "2025-03-27T15:19:36+00:00" + "time": "2025-08-06T15:13:45+00:00" }, { "name": "pocketmine/binaryutils", @@ -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", @@ -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", @@ -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", @@ -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,21 +818,20 @@ }, { "name": "ramsey/uuid", - "version": "4.7.6", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "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", - "ext-json": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -840,26 +839,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,41 +890,31 @@ ], "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.9.0" }, - "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-25T14:20:11+00:00" }, { "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": { @@ -956,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/v7.3.0" }, "funding": [ { @@ -972,181 +958,22 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.31.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.31.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" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", - "shasum": "" - }, - "require": { - "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.31.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" + "time": "2024-10-25T15:15:23+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 +1012,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,20 +1020,20 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { "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": { @@ -1249,9 +1076,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", @@ -1373,16 +1200,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.11", + "version": "2.1.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30" + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30", - "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", "shasum": "" }, "require": { @@ -1427,7 +1254,7 @@ "type": "github" } ], - "time": "2025-03-24T13:45:00+00:00" + "time": "2025-05-21T20:55:28+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -1484,16 +1311,16 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.4", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a" + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094" }, "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/f9f77efa9de31992a832ff77ea52eb42d675b094", + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094", "shasum": "" }, "require": { @@ -1526,41 +1353,40 @@ "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.6" }, - "time": "2025-03-18T11:42:40+00:00" + "time": "2025-07-21T12:19:29+00:00" }, { "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", @@ -1569,7 +1395,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1.x-dev" + "dev-main": "12.3.x-dev" } }, "autoload": { @@ -1598,40 +1424,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": { @@ -1659,7 +1497,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": [ { @@ -1667,28 +1505,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": "*" @@ -1696,7 +1534,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1722,7 +1560,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": [ { @@ -1730,32 +1569,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": { @@ -1782,7 +1621,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": [ { @@ -1790,32 +1629,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": { @@ -1841,7 +1680,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": [ { @@ -1849,20 +1689,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.45", + "version": "12.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" + "reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f09fda04e7caea93cff50b4e90319184f3e6ee3", + "reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3", "shasum": "" }, "require": { @@ -1872,29 +1712,25 @@ "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", - "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" @@ -1902,7 +1738,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "12.2-dev" } }, "autoload": { @@ -1934,7 +1770,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/12.2.1" }, "funding": [ { @@ -1945,37 +1781,45 @@ "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-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": { @@ -1999,7 +1843,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": [ { @@ -2007,147 +1851,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": { @@ -2187,7 +1923,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": [ { @@ -2195,33 +1931,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": { @@ -2245,7 +1981,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": [ { @@ -2253,33 +1989,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": { @@ -2312,7 +2048,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": [ { @@ -2320,27 +2056,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": "*" @@ -2348,7 +2084,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2376,42 +2112,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": { @@ -2454,7 +2202,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": [ { @@ -2462,35 +2210,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": { @@ -2516,7 +2264,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": [ { @@ -2524,33 +2272,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": { @@ -2574,7 +2322,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": [ { @@ -2582,34 +2330,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": { @@ -2631,7 +2379,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": [ { @@ -2639,32 +2388,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": { @@ -2686,7 +2435,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": [ { @@ -2694,32 +2444,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": { @@ -2749,7 +2499,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": [ { @@ -2757,32 +2508,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": { @@ -2805,7 +2556,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": [ { @@ -2813,29 +2565,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": { @@ -2858,7 +2610,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": [ { @@ -2866,7 +2619,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", @@ -2919,13 +2724,23 @@ "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/callback-validator": 20, + "pocketmine/math": 20 + }, "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", @@ -2952,9 +2767,9 @@ "ext-zlib": ">=1.2.11", "composer-runtime-api": "^2.0" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { - "php": "8.1.0" + "php": "8.3.0" }, "plugin-api-version": "2.6.0" } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4ac7b4410..5bbbf982b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -13,7 +13,7 @@ rules: - pocketmine\phpstan\rules\DisallowDynamicNewRule - pocketmine\phpstan\rules\DisallowForeachByReferenceRule - pocketmine\phpstan\rules\ExplodeLimitRule - - pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule + - pocketmine\phpstan\rules\UnsafeForeachRule # - pocketmine\phpstan\rules\ThreadedSupportedTypesRule parameters: 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 diff --git a/src/Server.php b/src/Server.php index 0d58bd579..a3b00f461 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1624,7 +1624,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)){ diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 44238dba3..1d4179f7d 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,7 +31,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.27.2"; + public const BASE_VERSION = "5.33.1"; public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; 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..d939994a2 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; @@ -81,22 +82,22 @@ final class AmethystCluster extends Transparent{ 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 c98f20b5e..dc9a75a18 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -26,6 +26,8 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\AnvilInventoryWindow; 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; @@ -41,7 +43,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; @@ -56,7 +58,7 @@ class Anvil extends Transparent implements Fallable{ } protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); } public function getDamage() : int{ return $this->damage; } @@ -71,14 +73,14 @@ class Anvil extends Transparent implements Fallable{ } 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->toFacing(), 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 AnvilInventoryWindow($player, $this->position)); } @@ -86,9 +88,9 @@ class Anvil extends Transparent implements Fallable{ 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); + $this->facing = HorizontalFacingOption::fromFacing(Facing::rotateY($player->getHorizontalFacing(), false)); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } 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 1b64e814c..b17c583e8 100644 --- a/src/block/Barrel.php +++ b/src/block/Barrel.php @@ -27,6 +27,7 @@ use pocketmine\block\inventory\window\BlockInventoryWindow; use pocketmine\block\tile\Barrel as TileBarrel; use pocketmine\block\utils\AnimatedContainer; use pocketmine\block\utils\AnimatedContainerTrait; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -40,14 +41,14 @@ use pocketmine\world\sound\BarrelOpenSound; use pocketmine\world\sound\Sound; use function abs; -class Barrel extends Opaque implements AnimatedContainer{ +class Barrel extends Opaque implements AnimatedContainer, AnyFacing{ use AnimatedContainerTrait; use AnyFacingTrait; protected bool $open = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facing($this->facing); + $w->enum($this->facing); $w->bool($this->open); } @@ -61,7 +62,7 @@ class Barrel extends Opaque implements AnimatedContainer{ 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; @@ -81,7 +82,7 @@ class Barrel extends Opaque implements AnimatedContainer{ 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 b56323453..dcdc36c60 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -25,18 +25,20 @@ 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; use pocketmine\item\Item; use pocketmine\item\VanillaItems; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function assert; use function count; -abstract class BaseBanner extends Transparent{ +abstract class BaseBanner extends Transparent implements Colored{ use ColoredTrait; /** @@ -49,6 +51,10 @@ abstract class BaseBanner extends Transparent{ 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()); } @@ -56,6 +62,13 @@ abstract class BaseBanner extends Transparent{ return $this; } + /** + * TODO: make this abstract in PM6 (BC break) + */ + protected function getOminousVersion() : Block{ + return VanillaBlocks::AIR(); + } + public function writeStateToWorld() : void{ parent::writeStateToWorld(); $tile = $this->position->getWorld()->getTile($this->position); @@ -100,7 +113,7 @@ abstract class BaseBanner extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -108,7 +121,7 @@ abstract class BaseBanner 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; } @@ -120,7 +133,7 @@ abstract class BaseBanner 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/BaseBigDripleaf.php b/src/block/BaseBigDripleaf.php index f0ff59cf0..c94300959 100644 --- a/src/block/BaseBigDripleaf.php +++ b/src/block/BaseBigDripleaf.php @@ -23,6 +23,8 @@ 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; @@ -33,7 +35,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; @@ -56,13 +58,13 @@ abstract class BaseBigDripleaf 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{ $block = $blockReplace->getSide(Facing::DOWN); if(!$this->canBeSupportedBy($block, true)){ 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; @@ -71,7 +73,7 @@ abstract class BaseBigDripleaf 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{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); return true; @@ -130,7 +132,7 @@ abstract class BaseBigDripleaf extends Transparent{ 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 b9c595a97..936d068ed 100644 --- a/src/block/BaseCoral.php +++ b/src/block/BaseCoral.php @@ -24,12 +24,14 @@ 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 pocketmine\math\Facing; use function mt_rand; -abstract class BaseCoral extends Transparent{ +abstract class BaseCoral extends Transparent implements CoralMaterial{ use CoralTypeTrait; public function onNearbyBlockChange() : void{ @@ -71,7 +73,7 @@ abstract class BaseCoral extends Transparent{ 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 new file mode 100644 index 000000000..a16e133a7 --- /dev/null +++ b/src/block/BaseOminousBanner.php @@ -0,0 +1,91 @@ +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(Facing $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, Facing $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() : Facing; + + public function onNearbyBlockChange() : void{ + if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){ + $this->position->getWorld()->useBreakOn($this->position); + } + } + + public function asItem() : Item{ + return VanillaItems::OMINOUS_BANNER(); + } +} 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 0f5d77d58..f631ff60a 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; @@ -34,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; @@ -44,7 +46,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; @@ -98,11 +100,11 @@ abstract class BaseSign extends Transparent{ 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){ @@ -110,7 +112,7 @@ abstract class BaseSign 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{ if($player !== null){ $this->editorEntityRuntimeId = $player->getId(); } @@ -159,7 +161,7 @@ abstract class BaseSign extends Transparent{ 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 133c4a9cc..8e7bf5a4a 100644 --- a/src/block/Bed.php +++ b/src/block/Bed.php @@ -24,8 +24,11 @@ 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\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -41,7 +44,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; @@ -49,7 +52,7 @@ class Bed extends Transparent{ 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); } @@ -77,10 +80,10 @@ class Bed extends Transparent{ } 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; } @@ -104,8 +107,8 @@ class Bed extends Transparent{ return $this; } - private function getOtherHalfSide() : int{ - return $this->head ? Facing::opposite($this->facing) : $this->facing; + private function getOtherHalfSide() : Facing{ + return $this->head ? Facing::opposite($this->facing->toFacing()) : $this->facing->toFacing(); } public function getOtherHalf() : ?Bed{ @@ -117,7 +120,7 @@ class Bed extends Transparent{ 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(); @@ -170,9 +173,11 @@ class Bed extends Transparent{ 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; + 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 53a6fc7fb..976793720 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -25,6 +25,8 @@ 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; @@ -38,39 +40,40 @@ 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; 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()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16) + AxisAlignedBB::one()->squashedCopy(Facing::axis($realFacing), 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($realFacing, 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($realFacing, 3 / 16) : $box ]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -82,23 +85,23 @@ final class Bell extends Transparent{ 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; } 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 : @@ -112,8 +115,8 @@ final class Bell extends Transparent{ 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); @@ -122,7 +125,7 @@ final class Bell extends Transparent{ } } - 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)){ @@ -141,7 +144,7 @@ final class Bell extends Transparent{ } } - 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); @@ -154,11 +157,11 @@ final class Bell extends Transparent{ 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), - 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/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/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/BlockTypeIds.php b/src/block/BlockTypeIds.php index c440cefdc..e4d49746f 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -786,8 +786,44 @@ 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 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 = 10759; + public const FIRST_UNUSED_BLOCK_ID = 10795; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; 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/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/BrewingStand.php b/src/block/BrewingStand.php index 59e439b91..0184de199 100644 --- a/src/block/BrewingStand.php +++ b/src/block/BrewingStand.php @@ -52,17 +52,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; } @@ -96,7 +96,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 73bd1d556..04f7716f2 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,13 +34,13 @@ 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; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facing($this->facing); + $w->enum($this->facing); $w->bool($this->pressed); } @@ -51,7 +52,7 @@ abstract class Button 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, $face)){ $this->facing = $face; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); @@ -61,7 +62,7 @@ abstract class Button extends Flowable{ 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(); @@ -88,7 +89,7 @@ abstract class Button extends Flowable{ } } - 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 67b15b946..0e4d7c8ed 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; @@ -45,10 +46,10 @@ class Cactus extends Transparent{ 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 546843d6c..09a1c1bfa 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; } @@ -39,8 +40,8 @@ class CakeWithCandle extends BaseCake{ 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 ]; } @@ -48,7 +49,7 @@ class CakeWithCandle extends BaseCake{ 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/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 bb1a001c6..e8ea9913b 100644 --- a/src/block/Campfire.php +++ b/src/block/Campfire.php @@ -24,7 +24,10 @@ declare(strict_types=1); namespace pocketmine\block; 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; use pocketmine\block\utils\SupportType; use pocketmine\crafting\FurnaceRecipe; @@ -59,7 +62,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; } @@ -126,12 +129,12 @@ class Campfire extends Transparent{ ]; } - 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)]; } /** @@ -170,18 +173,18 @@ class Campfire extends Transparent{ 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; } 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); } - 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 7f22641e1..df129d961 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; @@ -70,27 +71,27 @@ class Candle extends Transparent{ 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; } @@ -98,12 +99,12 @@ class Candle extends Transparent{ 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 2d8e7ea47..772a93eea 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; @@ -37,7 +38,7 @@ class Carpet extends Flowable{ } 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 1c3e94096..28ca59f7c 100644 --- a/src/block/CartographyTable.php +++ b/src/block/CartographyTable.php @@ -25,12 +25,13 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\CartographyTableInventoryWindow; 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 CartographyTableInventoryWindow($player, $this->position)); } 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/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 daa973507..1e42f2ac0 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; @@ -83,12 +84,12 @@ class CaveVines extends Flowable{ 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()); @@ -158,7 +159,7 @@ class CaveVines extends Flowable{ 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 new file mode 100644 index 000000000..4d9803617 --- /dev/null +++ b/src/block/CeilingCenterHangingSign.php @@ -0,0 +1,61 @@ +rotation = self::getRotationFromYaw($player->getLocation()->getYaw()); + } + 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 new file mode 100644 index 000000000..8a7271863 --- /dev/null +++ b/src/block/CeilingEdgesHangingSign.php @@ -0,0 +1,69 @@ +facing = HorizontalFacingOption::fromFacing(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()->toFacing()) === Facing::axis($this->facing->toFacing())); + } +} diff --git a/src/block/Chain.php b/src/block/Chain.php index e9cc2c9be..12b379852 100644 --- a/src/block/Chain.php +++ b/src/block/Chain.php @@ -23,16 +23,17 @@ 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{ + public function getSupportType(Facing $facing) : SupportType{ return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE; } @@ -40,7 +41,7 @@ final class Chain extends Transparent{ $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 058e40288..c266bd513 100644 --- a/src/block/ChemistryTable.php +++ b/src/block/ChemistryTable.php @@ -24,14 +24,16 @@ declare(strict_types=1); 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{ +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 414648d37..c5ae39f26 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -29,6 +29,7 @@ use pocketmine\block\tile\Chest as TileChest; use pocketmine\block\utils\AnimatedContainer; use pocketmine\block\utils\AnimatedContainerTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\event\block\ChestPairEvent; use pocketmine\item\Item; @@ -43,16 +44,16 @@ use pocketmine\world\sound\ChestCloseSound; use pocketmine\world\sound\ChestOpenSound; use pocketmine\world\sound\Sound; -class Chest extends Transparent implements AnimatedContainer{ +class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{ use AnimatedContainerTrait; use FacesOppositePlacingPlayerTrait; 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; } @@ -64,7 +65,7 @@ class Chest extends Transparent implements AnimatedContainer{ $tile = $world->getTile($position); if($tile instanceof TileChest){ foreach([false, true] as $clockwise){ - $side = Facing::rotateY($this->facing, $clockwise); + $side = Facing::rotateY($this->facing->toFacing(), $clockwise); $c = $position->getSide($side); $pair = $world->getTile($c); if($pair instanceof TileChest && $pair->isPaired() && $pair->getPair() === $tile){ @@ -80,7 +81,7 @@ class Chest extends Transparent implements AnimatedContainer{ $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); @@ -99,7 +100,7 @@ class Chest extends Transparent implements AnimatedContainer{ } } - 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){ $world = $this->position->getWorld(); $chest = $world->getTile($this->position); diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php index 73c4861bf..d35848331 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; @@ -51,7 +52,7 @@ class ChiseledBookshelf extends Opaque{ private ?ChiseledBookshelfSlot $lastInteractedSlot = null; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->enumSet($this->slots, ChiseledBookshelfSlot::cases()); } @@ -113,6 +114,18 @@ class ChiseledBookshelf extends Opaque{ 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. */ @@ -130,8 +143,8 @@ class ChiseledBookshelf extends Opaque{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ - if($face !== $this->facing){ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($face !== $this->facing->toFacing()){ return false; } diff --git a/src/block/ChorusFlower.php b/src/block/ChorusFlower.php index cc3c606d9..14cce40ac 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; @@ -105,9 +106,9 @@ final class ChorusFlower extends Flowable{ 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){ @@ -148,7 +149,7 @@ final class ChorusFlower extends Flowable{ 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()); } @@ -175,10 +176,10 @@ final class ChorusFlower extends Flowable{ $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 83e1de34b..8cc60872c 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -23,15 +23,17 @@ 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\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\WoodType; 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; @@ -39,25 +41,26 @@ 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; 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() - ->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($realFacing, true)), (6 - $this->age) / 16) //sides + ->trimmedCopy(Facing::DOWN, (7 - $this->age * 2) / 16) + ->trimmedCopy(Facing::UP, 0.25) + ->trimmedCopy(Facing::opposite($realFacing), 1 / 16) //gap between log and pod + ->trimmedCopy($realFacing, (11 - $this->age * 2) / 16) //outward face ]; } @@ -65,16 +68,16 @@ class CocoaBlock extends Flowable{ 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{ - if(Facing::axis($face) !== Axis::Y && $this->canAttachTo($blockClicked)){ - $this->facing = $face; + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(($hzFacing = HorizontalFacingOption::tryFromFacing($face)) !== null && $this->canAttachTo($blockClicked)){ + $this->facing = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } 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(); @@ -85,7 +88,7 @@ class CocoaBlock extends Flowable{ } 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/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/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/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/CraftingTable.php b/src/block/CraftingTable.php index 2b73d221a..99d509660 100644 --- a/src/block/CraftingTable.php +++ b/src/block/CraftingTable.php @@ -25,12 +25,13 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\CraftingTableInventoryWindow; 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 CraftingTableInventoryWindow($player, $this->position)); } diff --git a/src/block/Crops.php b/src/block/Crops.php index e90ac6236..85cddcbed 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; @@ -44,7 +45,7 @@ abstract class Crops extends Flowable{ 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 5720af529..ac3833753 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; @@ -63,14 +64,14 @@ class DaylightSensor extends Transparent{ } 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 fa88267e1..559c71575 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -23,6 +23,8 @@ 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; @@ -34,7 +36,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; @@ -42,7 +44,7 @@ class Door extends Transparent{ 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); @@ -97,10 +99,10 @@ class Door extends Transparent{ 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->toFacing(), !$this->hingeRight) : $this->facing->toFacing(), 327 / 400)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -110,7 +112,7 @@ class Door 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{ if($face === Facing::UP){ $blockUp = $this->getSide(Facing::UP); if(!$blockUp->canBeReplaced() || !$this->canBeSupportedAt($blockReplace)){ @@ -118,11 +120,13 @@ class Door extends Transparent{ } 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; @@ -138,7 +142,7 @@ class Door extends Transparent{ 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 1233ed05d..a4031d2d2 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; } @@ -57,10 +58,10 @@ final class DoublePitcherCrop extends DoublePlant{ //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 ]; } @@ -88,7 +89,7 @@ final class DoublePitcherCrop extends DoublePlant{ } - 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/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/EnchantingTable.php b/src/block/EnchantingTable.php index a5ac989d4..2344ae793 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 ed5b77433..52111090c 100644 --- a/src/block/EndPortalFrame.php +++ b/src/block/EndPortalFrame.php @@ -24,17 +24,18 @@ 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; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->eye); } @@ -51,6 +52,6 @@ class EndPortalFrame extends Opaque{ } 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 a6770f370..b1cd64749 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,10 +33,10 @@ 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{ + 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); @@ -60,7 +61,7 @@ class EndRod extends Flowable{ 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 0d1d7fd5c..70eb739ff 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -28,6 +28,7 @@ use pocketmine\block\tile\EnderChest as TileEnderChest; use pocketmine\block\utils\AnimatedContainer; use pocketmine\block\utils\AnimatedContainerTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -41,7 +42,7 @@ use pocketmine\world\sound\EnderChestCloseSound; use pocketmine\world\sound\EnderChestOpenSound; use pocketmine\world\sound\Sound; -class EnderChest extends Transparent implements AnimatedContainer{ +class EnderChest extends Transparent implements AnimatedContainer, HorizontalFacing{ use AnimatedContainerTrait { onContainerOpen as private traitOnContainerOpen; onContainerClose as private traitOnContainerClose; @@ -54,14 +55,14 @@ class EnderChest extends Transparent implements AnimatedContainer{ 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 2bbfdf892..a067bb71a 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -23,8 +23,11 @@ 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; use pocketmine\block\utils\WoodTypeTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -35,7 +38,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; @@ -43,7 +46,7 @@ class FenceGate extends Transparent{ 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); } @@ -65,23 +68,24 @@ class FenceGate extends Transparent{ } 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->toFacing()), 6 / 16)]; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } 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, 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(); + $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing()); } $this->inWall = $this->checkInWall(); @@ -97,12 +101,12 @@ class FenceGate extends Transparent{ } } - 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(); - 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/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/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..5df7bfb61 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,14 +31,18 @@ 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{ + protected function getOminousVersion() : Block{ + return VanillaBlocks::OMINOUS_BANNER()->setRotation($this->rotation); + } + + 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 5615d15d8..3f2bd6d22 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,14 +31,14 @@ 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{ + 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/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 2c4433413..9a315dc1c 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -26,15 +26,18 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\FurnaceInventoryWindow; 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; 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; -class Furnace extends Opaque{ +class Furnace extends Opaque implements Lightable, HorizontalFacing{ use FacesOppositePlacingPlayerTrait; use LightableTrait; @@ -46,7 +49,7 @@ class Furnace extends Opaque{ } protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->lit); } @@ -58,7 +61,7 @@ class Furnace extends Opaque{ 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/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..0815303a8 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\MultiAnyFacing; use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Fertilizer; @@ -35,7 +36,7 @@ use pocketmine\world\World; use function count; use function shuffle; -class GlowLichen extends Transparent{ +class GlowLichen extends Transparent implements MultiAnyFacing{ use MultiAnySupportTrait; public function getLightLevel() : int{ @@ -50,7 +51,7 @@ class GlowLichen extends Transparent{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -59,13 +60,13 @@ class GlowLichen extends Transparent{ } /** - * @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; @@ -80,7 +81,7 @@ class GlowLichen extends Transparent{ 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); @@ -98,9 +99,9 @@ class GlowLichen extends Transparent{ } /** - * @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; @@ -112,7 +113,7 @@ class GlowLichen extends Transparent{ } } - private function spreadAroundSupport(int $sourceFace) : bool{ + private function spreadAroundSupport(Facing $sourceFace) : bool{ $world = $this->position->getWorld(); $supportPos = $this->position->getSide($sourceFace); @@ -126,7 +127,7 @@ class GlowLichen extends Transparent{ return false; } - private function spreadAdjacentToSupport(int $sourceFace) : bool{ + private function spreadAdjacentToSupport(Facing $sourceFace) : bool{ $world = $this->position->getWorld(); foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){ @@ -138,7 +139,7 @@ class GlowLichen extends Transparent{ 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; @@ -148,7 +149,7 @@ class GlowLichen extends Transparent{ 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/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 8c65e836c..e0a0f6dc7 100644 --- a/src/block/Hopper.php +++ b/src/block/Hopper.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\HopperInventoryWindow; 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; @@ -35,20 +36,20 @@ 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; + 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 +59,16 @@ class Hopper extends Transparent{ 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 +76,13 @@ class Hopper 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->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 c03806a3b..ee0c8c452 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; @@ -51,7 +52,7 @@ class ItemFrame extends Flowable{ protected float $itemDropChance = 1.0; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facing($this->facing); + $w->enum($this->facing); $w->bool($this->hasMap); } @@ -131,7 +132,7 @@ class ItemFrame extends Flowable{ 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; @@ -149,7 +150,7 @@ class ItemFrame extends Flowable{ 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; } @@ -163,7 +164,7 @@ class ItemFrame extends Flowable{ return true; } - private function canBeSupportedAt(Block $block, int $face) : bool{ + private function canBeSupportedAt(Block $block, Facing $face) : bool{ return $block->getAdjacentSupportType($face) !== SupportType::NONE; } @@ -173,7 +174,7 @@ class ItemFrame 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(!$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 09c0b8f6b..03c9ea57f 100644 --- a/src/block/Ladder.php +++ b/src/block/Ladder.php @@ -23,19 +23,20 @@ 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; use pocketmine\entity\Living; use pocketmine\item\Item; -use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; 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{ @@ -59,16 +60,16 @@ class Ladder extends Transparent{ } protected function recalculateCollisionBoxes() : array{ - return [AxisAlignedBB::one()->trim($this->facing, 13 / 16)]; + return [AxisAlignedBB::one()->trimmedCopy($this->facing->toFacing(), 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{ - if($this->canBeSupportedAt($blockReplace, Facing::opposite($face)) && Facing::axis($face) !== Axis::Y){ - $this->facing = $face; + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ + 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); } @@ -76,12 +77,12 @@ class Ladder extends Transparent{ } 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); } } - 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 03880b3c5..43c28f32b 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; @@ -45,7 +46,7 @@ class Lectern extends Transparent{ protected bool $producingSignal = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->producingSignal); } @@ -83,10 +84,10 @@ class Lectern extends Transparent{ } 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; } @@ -119,7 +120,7 @@ class Lectern 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($this->book === null && $item instanceof WritableBookBase){ $world = $this->position->getWorld(); $world->setBlock($this->position, $this->setBook($item)); @@ -129,7 +130,7 @@ class Lectern extends Transparent{ 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 a0dd50542..f26f8212d 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{ @@ -41,14 +42,14 @@ final class LightningRod extends Transparent{ $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 d19fc9449..3c7cf2cc8 100644 --- a/src/block/Loom.php +++ b/src/block/Loom.php @@ -25,14 +25,16 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\LoomInventoryWindow; 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{ +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 LoomInventoryWindow($player, $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 e8729c00f..98464ed58 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,16 +42,16 @@ 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; 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); } @@ -79,12 +80,12 @@ class NetherVines extends Flowable{ 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(); @@ -158,7 +159,7 @@ class NetherVines extends Flowable{ return []; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } } 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/OminousFloorBanner.php b/src/block/OminousFloorBanner.php new file mode 100644 index 000000000..f4a03664f --- /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..78b9e9024 --- /dev/null +++ b/src/block/OminousWallBanner.php @@ -0,0 +1,50 @@ +facing->toFacing()); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $hzFacing = HorizontalFacingOption::tryFromFacing($face); + if($hzFacing === null){ + return false; + } + $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 17bc4c50a..d772d5df8 100644 --- a/src/block/PinkPetals.php +++ b/src/block/PinkPetals.php @@ -24,6 +24,8 @@ declare(strict_types=1); 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; @@ -34,7 +36,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; @@ -46,7 +48,7 @@ class PinkPetals extends Flowable{ 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); } @@ -69,21 +71,21 @@ class PinkPetals extends Flowable{ 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; }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); } - 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 d41aed284..7223dc66d 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; @@ -53,10 +54,10 @@ final class PitcherCrop extends Flowable{ $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 ]; } @@ -84,7 +85,7 @@ final class PitcherCrop extends Flowable{ 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/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/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/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/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..668b10b98 100644 --- a/src/block/Pumpkin.php +++ b/src/block/Pumpkin.php @@ -23,21 +23,21 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\item\Item; use pocketmine\item\Shears; use pocketmine\item\VanillaItems; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use function in_array; class Pumpkin extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ - if($item instanceof Shears && in_array($face, Facing::HORIZONTAL, true)){ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + 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/Rail.php b/src/block/Rail.php index f516902f0..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{ @@ -60,8 +58,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; } } } @@ -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/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 40e1ef510..ad00fc8b3 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\HorizontalFacingTrait; +use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\block\utils\SupportType; @@ -35,11 +38,10 @@ 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{ - use HorizontalFacingTrait; +class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter, PoweredByRedstone, HorizontalFacing{ + use FacesOppositePlacingPlayerTrait; use AnalogRedstoneSignalEmitterTrait; use PoweredByRedstoneTrait; use StaticSupportTrait; @@ -47,7 +49,7 @@ class RedstoneComparator extends Flowable{ 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); } @@ -80,17 +82,10 @@ class RedstoneComparator extends Flowable{ } 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{ - 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/RedstoneLamp.php b/src/block/RedstoneLamp.php index 58098c395..530fa2410 100644 --- a/src/block/RedstoneLamp.php +++ b/src/block/RedstoneLamp.php @@ -23,10 +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{ +class RedstoneLamp extends Opaque implements PoweredByRedstone, Lightable{ use PoweredByRedstoneTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ @@ -36,4 +38,14 @@ class RedstoneLamp extends Opaque{ 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; + } } diff --git a/src/block/RedstoneOre.php b/src/block/RedstoneOre.php index 10e701a6f..52a5f5432 100644 --- a/src/block/RedstoneOre.php +++ b/src/block/RedstoneOre.php @@ -24,21 +24,23 @@ 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; +use pocketmine\math\Facing; 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{ 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 bf9d0c5da..1b9ee249b 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\HorizontalFacingTrait; +use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\block\utils\SupportType; @@ -33,10 +35,9 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use pocketmine\world\BlockTransaction; -class RedstoneRepeater extends Flowable{ - use HorizontalFacingTrait; +class RedstoneRepeater extends Flowable implements PoweredByRedstone, HorizontalFacing{ + use FacesOppositePlacingPlayerTrait; use PoweredByRedstoneTrait; use StaticSupportTrait; @@ -46,7 +47,7 @@ class RedstoneRepeater extends Flowable{ 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); } @@ -63,18 +64,10 @@ class RedstoneRepeater extends Flowable{ } 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{ - 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{ if(++$this->delay > self::MAX_DELAY){ $this->delay = self::MIN_DELAY; } 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..044128cf8 100644 --- a/src/block/ResinClump.php +++ b/src/block/ResinClump.php @@ -23,17 +23,19 @@ declare(strict_types=1); 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{ +final class ResinClump extends Transparent implements MultiAnyFacing{ use MultiAnySupportTrait; public function isSolid() : bool{ return false; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } @@ -42,7 +44,7 @@ final class ResinClump extends Transparent{ } /** - * @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 new file mode 100644 index 000000000..b57d851fe --- /dev/null +++ b/src/block/RespawnAnchor.php @@ -0,0 +1,124 @@ +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, 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()); + 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 true; + + 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/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 170033963..c29999992 100644 --- a/src/block/ShulkerBox.php +++ b/src/block/ShulkerBox.php @@ -27,10 +27,12 @@ use pocketmine\block\inventory\window\BlockInventoryWindow; use pocketmine\block\tile\ShulkerBox as TileShulkerBox; use pocketmine\block\utils\AnimatedContainer; use pocketmine\block\utils\AnimatedContainerTrait; +use pocketmine\block\utils\AnyFacing; 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\network\mcpe\protocol\BlockEventPacket; use pocketmine\network\mcpe\protocol\types\BlockPosition; @@ -41,7 +43,7 @@ use pocketmine\world\sound\ShulkerBoxCloseSound; use pocketmine\world\sound\ShulkerBoxOpenSound; use pocketmine\world\sound\Sound; -class ShulkerBox extends Opaque implements AnimatedContainer{ +class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{ use AnimatedContainerTrait; use AnyFacingTrait; @@ -71,7 +73,7 @@ class ShulkerBox extends Opaque implements AnimatedContainer{ 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); @@ -103,7 +105,7 @@ class ShulkerBox extends Opaque implements AnimatedContainer{ 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); @@ -122,7 +124,7 @@ class ShulkerBox extends Opaque implements AnimatedContainer{ return true; } - public function getSupportType(int $facing) : SupportType{ + public function getSupportType(Facing $facing) : SupportType{ return SupportType::NONE; } 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/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 d192e43db..abf428ce5 100644 --- a/src/block/SmallDripleaf.php +++ b/src/block/SmallDripleaf.php @@ -23,6 +23,8 @@ 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; @@ -36,13 +38,13 @@ 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; public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->top); } @@ -74,13 +76,13 @@ class SmallDripleaf 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{ $block = $blockReplace->getSide(Facing::UP); if($block->getTypeId() !== BlockTypeIds::AIR || !$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){ 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() @@ -90,7 +92,7 @@ class SmallDripleaf 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{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); return true; @@ -160,7 +162,7 @@ class SmallDripleaf extends Transparent{ 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 b96a582d1..01fdbc189 100644 --- a/src/block/SmithingTable.php +++ b/src/block/SmithingTable.php @@ -25,12 +25,13 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\SmithingTableInventoryWindow; 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 SmithingTableInventoryWindow($player, $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/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..6e58dd985 100644 --- a/src/block/Stair.php +++ b/src/block/Stair.php @@ -23,6 +23,8 @@ 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; @@ -35,14 +37,14 @@ 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; protected StairShape $shape = StairShape::STRAIGHT; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); $w->bool($this->upsideDown); } @@ -51,7 +53,7 @@ class Stair extends Transparent{ $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){ @@ -82,21 +84,22 @@ class Stair extends Transparent{ protected function recalculateCollisionBoxes() : array{ $topStepFace = $this->upsideDown ? Facing::DOWN : Facing::UP; $bbs = [ - AxisAlignedBB::one()->trim($topStepFace, 0.5) + AxisAlignedBB::one()->trimmedCopy($topStepFace, 0.5) ]; + $realFacing = $this->facing->toFacing(); $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($realFacing), 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($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() - ->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($realFacing, 0.5) //avoid overlapping with main step + ->trimmedCopy(Facing::rotateY($realFacing, $this->shape === StairShape::INNER_LEFT), 0.5); } $bbs[] = $topStep; @@ -104,31 +107,33 @@ class Stair extends Transparent{ return $bbs; } - public function getSupportType(int $facing) : SupportType{ + 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) : ?int{ - $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, 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(); + $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing()); } $this->upsideDown = (($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN); diff --git a/src/block/Stem.php b/src/block/Stem.php index 2ac95aa3f..8dbe915f1 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -25,24 +25,25 @@ 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; + 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"); } @@ -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)) ]; } } diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php index 3c22e74a8..f254b4950 100644 --- a/src/block/Stonecutter.php +++ b/src/block/Stonecutter.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\StonecutterInventoryWindow; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -32,10 +33,10 @@ 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{ + public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new StonecutterInventoryWindow($player, $this->position)); } @@ -43,10 +44,10 @@ class Stonecutter extends Transparent{ } 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/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/Sugarcane.php b/src/block/Sugarcane.php index 2da2dc9b9..0f8d7e13c 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; @@ -74,7 +75,7 @@ class Sugarcane extends Flowable{ 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(); @@ -115,7 +116,7 @@ class Sugarcane 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 = $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 5fd7fcaa1..1b7ce5bad 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; @@ -64,7 +65,7 @@ class SweetBerryBush extends Flowable{ ($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 a903e1b5e..81c165840 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -23,6 +23,8 @@ 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; @@ -34,14 +36,14 @@ 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; 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); } @@ -63,16 +65,16 @@ class Trapdoor extends Transparent{ } 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->toFacing() : ($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()); + $this->facing = HorizontalFacingOption::fromFacing(Facing::opposite($player->getHorizontalFacing())); } if(($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN){ $this->top = true; @@ -81,7 +83,7 @@ class Trapdoor 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{ $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 325819825..3f82985b0 100644 --- a/src/block/TripwireHook.php +++ b/src/block/TripwireHook.php @@ -23,23 +23,24 @@ 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; -use pocketmine\math\Axis; use pocketmine\math\Facing; 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; 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); } @@ -60,10 +61,10 @@ class TripwireHook extends Flowable{ return $this; } - 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){ - //TODO: check face is valid - $this->facing = $face; + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $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/VanillaBlocks.php b/src/block/VanillaBlocks.php index f4c318f32..569efe0d3 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,14 +608,19 @@ 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() + * @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() * @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() @@ -603,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() @@ -694,6 +724,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() @@ -732,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() @@ -744,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() @@ -787,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() @@ -799,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() @@ -859,7 +896,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,16 +904,18 @@ 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); 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); 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 +952,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 +969,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 +1001,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 +1009,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 +1043,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 +1102,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 +1165,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 +1184,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 +1194,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 +1277,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 +1313,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 +1365,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 []; } @@ -1346,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)); @@ -1385,6 +1431,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", $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); } } @@ -1537,7 +1600,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 +1644,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 +1665,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 +1685,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 +1703,9 @@ 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)))); + + 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{ @@ -1659,7 +1723,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 +1735,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 +1766,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 +1794,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 +1806,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 +1818,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/src/block/Vine.php b/src/block/Vine.php index cc516bbca..0e6f83b12 100644 --- a/src/block/Vine.php +++ b/src/block/Vine.php @@ -23,59 +23,56 @@ declare(strict_types=1); namespace pocketmine\block; +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; use pocketmine\world\BlockTransaction; use function array_intersect_key; use function count; +use function spl_object_id; class Vine extends Flowable{ - /** @var int[] */ + /** @var HorizontalFacingOption[] */ protected array $faces = []; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->horizontalFacingFlags($this->faces); + $w->enumSet($this->faces, HorizontalFacingOption::cases()); } - /** @return int[] */ + /** @return HorizontalFacingOption[] */ public function getFaces() : array{ return $this->faces; } - public function hasFace(int $face) : bool{ - return isset($this->faces[$face]); + public function hasFace(HorizontalFacingOption $face) : bool{ + return isset($this->faces[spl_object_id($face)]); } /** - * @param int[] $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] = $face; + $uniqueFaces[spl_object_id($face)] = $face; } $this->faces = $uniqueFaces; return $this; } /** @return $this */ - public function setFace(int $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] = $face; + $this->faces[spl_object_id($face)] = $face; }else{ - unset($this->faces[$face]); + unset($this->faces[spl_object_id($face)]); } return $this; } @@ -101,13 +98,15 @@ 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{ + $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[Facing::opposite($face)] = Facing::opposite($face); + $this->faces[spl_object_id($hzFacing)] = $hzFacing; return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } @@ -120,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]) && !$this->getSide($face)->isSolid()){ - unset($this->faces[$face]); + 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/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 5182fe9f8..5b3c3dc1c 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -23,26 +23,32 @@ 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; use pocketmine\math\Facing; 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{ - return Facing::opposite($this->facing); + protected function getOminousVersion() : Block{ + return VanillaBlocks::OMINOUS_WALL_BANNER()->setFacing($this->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){ + protected function getSupportingFace() : 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{ + $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 f9dece1cd..b6bfac73f 100644 --- a/src/block/WallCoralFan.php +++ b/src/block/WallCoralFan.php @@ -23,29 +23,30 @@ 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; use pocketmine\item\VanillaItems; -use pocketmine\math\Axis; use pocketmine\math\Facing; 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{ - $w->horizontalFacing($this->facing); + $w->enum($this->facing); } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - $axis = Facing::axis($face); - if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $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(); @@ -54,14 +55,14 @@ final class WallCoralFan extends BaseCoral{ 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(); } } - 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 new file mode 100644 index 000000000..44cdba2eb --- /dev/null +++ b/src/block/WallHangingSign.php @@ -0,0 +1,83 @@ +facing->toFacing(), clockwise: true); + } + + public function onNearbyBlockChange() : void{ + //NOOP - disable default self-destruct behaviour + } + + protected function recalculateCollisionBoxes() : array{ + //only the cross bar is collidable + 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{ + 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; + } + + $facing = Facing::rotateY(Facing::opposite($direction), clockwise: true); + //the front should always face the player if possible + 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()->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 07826eae7..2df05f97f 100644 --- a/src/block/WallSign.php +++ b/src/block/WallSign.php @@ -23,26 +23,28 @@ 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; use pocketmine\math\Facing; 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{ - return Facing::opposite($this->facing); + protected function getSupportingFace() : Facing{ + return Facing::opposite($this->facing->toFacing()); } - 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){ + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $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/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/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..6dc27efbe 100644 --- a/src/block/Wood.php +++ b/src/block/Wood.php @@ -23,16 +23,19 @@ 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; use pocketmine\item\Item; +use pocketmine\math\Facing; 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; @@ -62,7 +65,7 @@ class Wood extends Opaque{ 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/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/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/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/event/server/ServerEvent.php b/src/block/tile/HangingSign.php similarity index 80% rename from src/event/server/ServerEvent.php rename to src/block/tile/HangingSign.php index 97e79279d..a5be9ba5c 100644 --- a/src/event/server/ServerEvent.php +++ b/src/block/tile/HangingSign.php @@ -21,13 +21,11 @@ declare(strict_types=1); +namespace pocketmine\block\tile; + /** - * Events related to the server core, like networking, stop, console commands + * @deprecated */ -namespace pocketmine\event\server; - -use pocketmine\event\Event; - -abstract class ServerEvent extends Event{ +final class HangingSign extends Sign{ } diff --git a/src/block/tile/ShulkerBox.php b/src/block/tile/ShulkerBox.php index 13ed3980f..858bf4477 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\BlockTypeIds; +use pocketmine\data\SavedDataLoadingException; use pocketmine\inventory\Inventory; use pocketmine\inventory\SimpleInventory; use pocketmine\inventory\transaction\action\validator\CallbackSlotValidator; @@ -43,7 +44,7 @@ class ShulkerBox extends Spawnable implements ContainerTile, Nameable{ public const TAG_FACING = "facing"; - protected int $facing = Facing::NORTH; + protected Facing $facing = Facing::NORTH; protected Inventory $inventory; @@ -64,13 +65,15 @@ class ShulkerBox extends Spawnable implements ContainerTile, 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{ @@ -99,11 +102,11 @@ class ShulkerBox extends Spawnable implements ContainerTile, 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; } @@ -120,7 +123,8 @@ class ShulkerBox extends Spawnable implements ContainerTile, 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/tile/Sign.php b/src/block/tile/Sign.php index 0bb21a6d3..3e68317ba 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -32,11 +32,9 @@ 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 rtrim; use function sprintf; /** @@ -60,14 +58,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; @@ -117,7 +107,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 +152,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 diff --git a/src/block/tile/Spawnable.php b/src/block/tile/Spawnable.php index 956652eac..d801e5324 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; public function clearSpawnCompoundCache() : void{ @@ -59,7 +59,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/block/tile/TileFactory.php b/src/block/tile/TileFactory.php index 26e0af6a5..108483894 100644 --- a/src/block/tile/TileFactory.php +++ b/src/block/tile/TileFactory.php @@ -79,6 +79,7 @@ final class TileFactory{ $this->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/block/utils/Ageable.php b/src/block/utils/Ageable.php new file mode 100644 index 000000000..180392ef3 --- /dev/null +++ b/src/block/utils/Ageable.php @@ -0,0 +1,37 @@ +age; } + public function getMaxAge() : int{ return self::MAX_AGE; } + /** * @return $this */ diff --git a/src/block/utils/AnalogRedstoneSignalEmitter.php b/src/block/utils/AnalogRedstoneSignalEmitter.php new file mode 100644 index 000000000..fa2cc4b11 --- /dev/null +++ b/src/block/utils/AnalogRedstoneSignalEmitter.php @@ -0,0 +1,34 @@ +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/Colored.php b/src/block/utils/Colored.php new file mode 100644 index 000000000..af26d0b5d --- /dev/null +++ b/src/block/utils/Colored.php @@ -0,0 +1,34 @@ +waxed && $item->getTypeId() === ItemTypeIds::HONEYCOMB){ $this->waxed = true; $this->position->getWorld()->setBlock($this->position, $this); diff --git a/src/block/utils/CoralMaterial.php b/src/block/utils/CoralMaterial.php new file mode 100644 index 000000000..0284322ac --- /dev/null +++ b/src/block/utils/CoralMaterial.php @@ -0,0 +1,41 @@ +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 new file mode 100644 index 000000000..a4c1fed1d --- /dev/null +++ b/src/block/utils/HorizontalFacing.php @@ -0,0 +1,31 @@ +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 a5bd6dcf4..fb950b4f2 100644 --- a/src/block/utils/HorizontalFacingTrait.php +++ b/src/block/utils/HorizontalFacingTrait.php @@ -24,24 +24,18 @@ declare(strict_types=1); namespace pocketmine\block\utils; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\math\Axis; -use pocketmine\math\Facing; trait HorizontalFacingTrait{ - protected int $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() : int{ return $this->facing; } + public function getFacing() : HorizontalFacingOption{ return $this->facing; } /** @return $this */ - public function setFacing(int $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/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/Lightable.php b/src/block/utils/Lightable.php new file mode 100644 index 000000000..36ac479f3 --- /dev/null +++ b/src/block/utils/Lightable.php @@ -0,0 +1,34 @@ +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 new file mode 100644 index 000000000..c50e36ff0 --- /dev/null +++ b/src/block/utils/MultiAnyFacing.php @@ -0,0 +1,45 @@ +facingFlags($this->faces); + $w->enumSet($this->faces, Facing::cases()); } - /** @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[spl_object_id($face)]); } /** - * @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[spl_object_id($face)] = $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[spl_object_id($face)] = $face; }else{ - unset($this->faces[$face]); + unset($this->faces[spl_object_id($face)]); } 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 new file mode 100644 index 000000000..cfba744f1 --- /dev/null +++ b/src/block/utils/PillarRotation.php @@ -0,0 +1,34 @@ +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/PoweredByRedstone.php b/src/block/utils/PoweredByRedstone.php new file mode 100644 index 000000000..452de4309 --- /dev/null +++ b/src/block/utils/PoweredByRedstone.php @@ -0,0 +1,34 @@ + [ - 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/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 @@ +canBeSupportedAt($blockReplace) && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } diff --git a/src/block/utils/StraightOnlyRailShape.php b/src/block/utils/StraightOnlyRailShape.php new file mode 100644 index 000000000..36ce3a89f --- /dev/null +++ b/src/block/utils/StraightOnlyRailShape.php @@ -0,0 +1,33 @@ +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/block/utils/WoodMaterial.php b/src/block/utils/WoodMaterial.php new file mode 100644 index 000000000..2fd2bbcca --- /dev/null +++ b/src/block/utils/WoodMaterial.php @@ -0,0 +1,29 @@ + */ 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/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php index a43054e98..b02b965b7 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/src/crash/CrashDump.php b/src/crash/CrashDump.php index 49a587c34..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; @@ -259,10 +258,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/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/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php deleted file mode 100644 index a25336d89..000000000 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ /dev/null @@ -1,52 +0,0 @@ - */ - use IntSaveIdMapTrait; - - public function __construct(){ - 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); - } - } -} diff --git a/src/data/bedrock/WorldDataVersions.php b/src/data/bedrock/WorldDataVersions.php new file mode 100644 index 000000000..e682eb43a --- /dev/null +++ b/src/data/bedrock/WorldDataVersions.php @@ -0,0 +1,66 @@ + + * @var (\Closure|BlockStateData)[] + * @phpstan-var array */ private array $serializers = []; @@ -204,20 +47,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 @@ -231,29 +60,14 @@ 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"); + throw new \InvalidArgumentException("Block type ID " . $block->getTypeId() . " (" . $block->getName() . ") already has a serializer registered"); } - $this->serializers[$block->getTypeId()] = $serializer; - } - - public function mapSimple(Block $block, string $id) : void{ - $this->map($block, fn() => Writer::create($id)); - } - - public function mapSlab(Slab $block, string $singleId, string $doubleId) : void{ - $this->map($block, fn(Slab $block) => Helper::encodeSlab($block, $singleId, $doubleId)); - } - - public function mapStairs(Stair $block, string $id) : void{ - $this->map($block, fn(Stair $block) => Helper::encodeStairs($block, Writer::create($id))); - } - - public function mapLog(Wood $block, string $unstrippedId, string $strippedId) : void{ - $this->map($block, fn(Wood $block) => Helper::encodeLog($block, $unstrippedId, $strippedId)); + //writer accepted for convenience only + $this->serializers[$block->getTypeId()] = $serializer instanceof Writer ? $serializer->getBlockStateData() : $serializer; } /** @@ -270,1616 +84,20 @@ 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(); - } - - 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->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())); - } - - public function registerFlatColorBlockSerializers() : void{ - $this->map(Blocks::STAINED_HARDENED_GLASS(), fn(StainedHardenedGlass $block) => Writer::create(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) => Writer::create(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->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, - 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()); - }); - - $this->map(Blocks::WOOL(), fn(Wool $block) => Writer::create(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->map(Blocks::CARPET(), fn(Carpet $block) => Writer::create(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->map(Blocks::DYED_SHULKER_BOX(), fn(DyedShulkerBox $block) => Writer::create(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->map(Blocks::CONCRETE(), fn(Concrete $block) => Writer::create(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->map(Blocks::CONCRETE_POWDER(), fn(ConcretePowder $block) => Writer::create(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->map(Blocks::STAINED_CLAY(), fn(StainedHardenedClay $block) => Writer::create(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) => Writer::create(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) => Writer::create(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, - })); - } - - 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_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) => 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::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(), fn() => 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) : Writer{ - $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) - ); - }); - $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{ - $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 - ) - ); - }); - $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{ - $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 - ) - ); - }); - $this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{ - $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) - ); - }); - $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(), fn() => 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(), 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::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(), function() : Writer{ - return 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(), 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::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(), fn() => 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(), function() : Writer{ - return 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::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(), fn() => 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::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(), fn() => 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(), 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_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(WeightedPressurePlate $block) : Writer{ - return Writer::create(Ids::HEAVY_WEIGHTED_PRESSURE_PLATE) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), function(WeightedPressurePlate $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))); + return $result instanceof Writer ? $result->getBlockStateData() : $result; } } diff --git a/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php new file mode 100644 index 000000000..5fc1d3eef --- /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->map($id, fn() => clone $block); + $this->serializer->map($block, BlockStateData::current($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 deleted file mode 100644 index 5cf3f7f76..000000000 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ /dev/null @@ -1,327 +0,0 @@ -setFacing($in->readFacingDirection()) - ->setPressed($in->readBool(BlockStateNames::BUTTON_PRESSED_BIT)); - } - - /** @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)); - } - - /** - * @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; - } - - /** - * @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)); - } - - /** - * @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)); - } - - /** @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)); - } - - /** @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"), - }); - } - - /** @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 */ - 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 */ - 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); - } - - /** - * @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); - } - - /** - * @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; - } - - /** @throws BlockStateDeserializeException */ - public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{ - return $block - ->setFacing($in->readHorizontalFacing()); - } - - 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..adcdf9709 100644 --- a/src/data/bedrock/block/convert/BlockStateReader.php +++ b/src/data/bedrock/block/convert/BlockStateReader.php @@ -23,16 +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\math\Axis; -use pocketmine\math\Facing; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; @@ -111,227 +103,6 @@ final class BlockStateReader{ throw $this->missingOrWrongTypeException($name, $tag); } - /** - * @param int[] $mapping - * @phpstan-param array $mapping - * @phpstan-return int - * @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; - } - - /** @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 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; - } - - /** @throws BlockStateDeserializeException */ - public function readEndRodFacingDirection() : int{ - $result = $this->readFacingDirection(); - return Facing::axis($result) !== Axis::Y ? Facing::opposite($result) : $result; - } - - /** @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 - ]); - } - - /** - * 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 - ]); - } - - /** - * 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) - }; - } - - /** @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 - ]); - } - - /** @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; - } - - 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; - } - - /** - * @phpstan-return Axis::* - * @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; - } - - /** @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"), - }; - } - - /** - * @phpstan-return Facing::UP|Facing::NORTH|Facing::SOUTH|Facing::WEST|Facing::EAST - * @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"), - }; - } - - /** @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), - }; - } - - /** @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), - }; - } - /** * 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 a25044153..000000000 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ /dev/null @@ -1,231 +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()); - } - - 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, - }; - } - - 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()); - } - - 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()); - } - - 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()); - } - - 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())); - } - - 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 - } - - 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); - } - - 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); - } - - 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)); - } - - 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 ed45a47d3..18d549774 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -23,48 +23,14 @@ 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\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\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{ @@ -80,21 +46,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 @@ -131,1747 +82,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return $this->deserializeFuncs[$id] ?? null; } - /** @phpstan-param \Closure() : Block $getBlock */ - public function mapSimple(string $id, \Closure $getBlock) : void{ - $this->map($id, $getBlock); - } - - /** - * @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)); - } - - /** - * @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 */ - 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)); - } - - 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)) - ); - } - } - - 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)); - } - - 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)); - } - - 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, - 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()) - ); - } - - 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)); - } - - 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)); - } - - 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)); - } - - 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)); - } - - 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)); - } - - 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)); - } - } - - 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_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()); - }); - $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->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..d58bbd7c3 100644 --- a/src/data/bedrock/block/convert/BlockStateWriter.php +++ b/src/data/bedrock/block/convert/BlockStateWriter.php @@ -23,21 +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\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{ @@ -73,209 +63,6 @@ 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") - }); - return $this; - } - - /** - * @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); - } - - /** @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 */ - 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; - } - - /** - * 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") - }); - } - - /** - * 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 */ - 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 */ - public function writeFacingWithoutDown(int $value) : self{ - if($value === Facing::DOWN){ - throw new BlockStateSerializeException("Invalid facing DOWN"); - } - $this->writeFacingDirection($value); - return $this; - } - - /** @return $this */ - public function writeFacingWithoutUp(int $value) : self{ - if($value === Facing::UP){ - throw new BlockStateSerializeException("Invalid facing UP"); - } - $this->writeFacingDirection($value); - return $this; - } - - /** @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") - }); - return $this; - } - - /** @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; - } - - /** @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") - }); - return $this; - } - - /** @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 */ - 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; - } - 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/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php new file mode 100644 index 000000000..15fd07659 --- /dev/null +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -0,0 +1,1662 @@ +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 ValueSetFromIntProperty( + StateNames::VINE_DIRECTION_BITS, + 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) => $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([ + //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, HorizontalFacingOption $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()->value, fn(MobHead $b, int $v) => $b->setFacing(Facing::from($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)), + $commonProperties->straightOnlyRailShape + ])); + + //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 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 + 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)), + $commonProperties->straightOnlyRailShape + ])); + + //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, Facing $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()->value, fn(Hopper $b, int $v) => $b->setFacing(Facing::from($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([ + //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 + $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 + $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? + ])); + $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 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([ + 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])); + } + + /** + * @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) + * 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, CommonProperties $commonProperties) : void{ + //big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state + $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) + ); + + $fillLevelProperty = new IntProperty(StateNames::FILL_LEVEL, 1, 6, fn(FillableCauldron $b) => $b->getFillLevel(), fn(FillableCauldron $b, int $v) => $b->setFillLevel($v)); + + //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) + } + ); + + //mushroom stems, split for consistency with all-sided logs vs normal logs + $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 + $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) ? + //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) + ); + + //these only exist within PM (mapped from tile properties) as they don't support the same properties as a + //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/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..beb2a421d --- /dev/null +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -0,0 +1,444 @@ + */ + 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 ValueSetFromIntProperty */ + public readonly ValueSetFromIntProperty $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 ValueFromIntProperty */ + public readonly ValueFromIntProperty $straightOnlyRailShape; + + /** @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(); + + //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(); + $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( + StateNames::MC_BLOCK_FACE, + $vm->blockFace, + fn(AnyFacing $b) => $b->getFacing(), + 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, Axis $v) => $b->setAxis($v) + ); + + $this->torchFacing = new ValueFromStringProperty( + StateNames::TORCH_FACING_DIRECTION, + $vm->torchFacing, + 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); + $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, Facing $v) => $b->setFacing($v) + ); + + $this->multiFacingFlags = new ValueSetFromIntProperty( + StateNames::MULTI_FACE_DIRECTION_BITS, + 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) + ); + + $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)); + + $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, + 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, + EnumFromRawStateMap::string(HorizontalFacingOption::class, fn(HorizontalFacingOption $case) => match ($case) { + //a door facing "east" is actually facing north - thanks mojang + 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) + ) + ]; + + $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()->value, fn(Stem $b, int $v) => $b->setFacing(Facing::from($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/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..ef2bf45cf --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueMappings.php @@ -0,0 +1,315 @@ + */ + 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 EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $cardinalDirection; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $blockFace; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $pillarAxis; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $torchFacing; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $portalAxis; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $bambooLeafSize; + + /** @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 */ + public readonly EnumFromRawStateMap $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 => [] + } + ); + + //TODO: this can't use EnumFromRawStateMap until we have a dedicated HorizontalFacing enum + $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, + 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 = 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->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->value => StringValues::TORCH_FACING_DIRECTION_UNKNOWN //should be illegal, but still supported + ]); + $this->portalAxis = IntFromRawStateMap::string([ + Axis::X->value => StringValues::PORTAL_AXIS_X, + Axis::Z->value => StringValues::PORTAL_AXIS_Z, + ], deserializeAliases: [ + Axis::X->value => 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 = 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, + Facing::UP => 1, + Facing::NORTH => 2, + Facing::SOUTH => 3, + Facing::WEST => 4, + Facing::EAST => 5 + }); + + //end rods have all the horizontal facing values opposite to classic facing + $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, + }); + + //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, + ]); + + //TODO: shitty copy pasta job, we can do this better but this is good enough for now + $this->facingExceptDown = IntFromRawStateMap::int( + [Facing::UP->value => 1] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP->value => 0]); + $this->facingExceptUp = IntFromRawStateMap::int( + [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->value => 0] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP->value => 1] + ); + } +} diff --git a/src/data/bedrock/block/convert/property/ValueSetFromIntProperty.php b/src/data/bedrock/block/convert/property/ValueSetFromIntProperty.php new file mode 100644 index 000000000..89913d78b --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueSetFromIntProperty.php @@ -0,0 +1,93 @@ + + */ +class ValueSetFromIntProperty 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/WallConnectionTypeShim.php b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php new file mode 100644 index 000000000..c7d4913cf --- /dev/null +++ b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php @@ -0,0 +1,67 @@ + 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/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; } } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 771154d46..f5c03dbeb 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{ @@ -165,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()); @@ -174,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()); @@ -192,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()); @@ -211,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()); @@ -281,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()); @@ -291,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()); @@ -334,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()); @@ -376,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()); @@ -396,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()); @@ -487,14 +500,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 +555,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) ?? 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/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index ea95d57f0..5c648ff38 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"; @@ -150,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"; @@ -166,6 +180,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 +280,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 +339,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 +354,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"; @@ -361,6 +383,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"; @@ -369,6 +392,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"; @@ -400,6 +424,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 +444,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 +465,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 +482,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 +591,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 +612,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/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 8df675f70..df9da2891 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,36 +46,16 @@ interface RuntimeDataDescriber{ public function bool(bool &$value) : void; - public function horizontalFacing(int &$facing) : void; + public function facingExcept(Facing &$facing, Facing $except) : void; - /** - * @param int[] $faces - */ - public function facingFlags(array &$faces) : void; - - /** - * @param int[] $faces - */ - public function horizontalFacingFlags(array &$faces) : void; - - public function facing(int &$facing) : 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; - 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 c230a52ae..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\RailConnectionInfo; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; @@ -77,59 +76,9 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $value = $this->readBool(); } - public function horizontalFacing(int &$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 int[] $faces - */ - public function facingFlags(array &$faces) : void{ - $result = []; - foreach(Facing::ALL as $facing){ - if($this->readBool()){ - $result[$facing] = $facing; - } - } - - $faces = $result; - } - - /** - * @param int[] $faces - */ - public function horizontalFacingFlags(array &$faces) : void{ - $result = []; - foreach(Facing::HORIZONTAL as $facing){ - if($this->readBool()){ - $result[$facing] = $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 +86,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 +96,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 +105,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") @@ -177,24 +117,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 6725aace6..bb30634f1 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,11 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ $this->addBits(1); } - public function horizontalFacing(int &$facing) : void{ - $this->addBits(2); + public function facingExcept(Facing &$facing, Facing $except) : void{ + $this->enum($facing); } - public function facingFlags(array &$faces) : void{ - $this->addBits(count(Facing::ALL)); - } - - public function horizontalFacingFlags(array &$faces) : void{ - $this->addBits(count(Facing::HORIZONTAL)); - } - - public function facing(int &$facing) : void{ - $this->addBits(3); - } - - 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); } @@ -82,14 +63,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 382aa9bf6..05a177706 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,27 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->writeBool($value); } - public function horizontalFacing(int &$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") - }); + public function facingExcept(Facing &$facing, Facing $except) : void{ + $this->enum($facing); } - /** - * @param int[] $faces - */ - public function facingFlags(array &$faces) : void{ - $uniqueFaces = array_flip($faces); - foreach(Facing::ALL as $facing){ - $this->writeBool(isset($uniqueFaces[$facing])); - } - } - - /** - * @param int[] $faces - */ - public function horizontalFacingFlags(array &$faces) : void{ - $uniqueFaces = array_flip($faces); - foreach(Facing::HORIZONTAL as $facing){ - $this->writeBool(isset($uniqueFaces[$facing])); - } - } - - 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(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, @@ -155,14 +103,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)); diff --git a/src/entity/Entity.php b/src/entity/Entity.php index f89186d19..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,20 +1183,22 @@ 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); - 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; @@ -1206,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; @@ -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); @@ -1391,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/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/entity/Human.php b/src/entity/Human.php index 5f1a46322..279156d7d 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\Hotbar; use pocketmine\inventory\Inventory; @@ -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/Living.php b/src/entity/Living.php index 9a1a28d83..e26ae1e2e 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 + count($world->getNearbyEntities(AxisAlignedBB::one()->offsetCopy($x, $y, $z))) !== 0 ){ continue; } - $world->setBlockAt($x, $y, $z, $frostedIce); + $world->setBlockAt($x, $y, $z, $targetBlock); } } } 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/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/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php index 937a4023f..5ea74bfac 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/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/object/PrimedTNT.php b/src/entity/object/PrimedTNT.php index 29cbf59b9..dea6f70fb 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/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/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/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/event/entity/EntityExplodeEvent.php b/src/event/entity/EntityExplodeEvent.php index 0a0e4f696..779ab879a 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/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; + } +} 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/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/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/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/UpdateNotifyEvent.php b/src/event/server/UpdateNotifyEvent.php index 9a36a8bc8..f9b756546 100644 --- a/src/event/server/UpdateNotifyEvent.php +++ b/src/event/server/UpdateNotifyEvent.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\event\server; +use pocketmine\event\Event; use pocketmine\updater\UpdateChecker; /** * Called when the update checker receives notification of an available PocketMine-MP update. * Plugins may use this event to perform actions when an update notification is received. */ -class UpdateNotifyEvent extends ServerEvent{ +class UpdateNotifyEvent extends Event{ public function __construct(private UpdateChecker $updater){} public function getUpdater() : UpdateChecker{ diff --git a/src/inventory/BaseInventory.php b/src/inventory/BaseInventory.php index adf8d3ff7..3afb3eb2a 100644 --- a/src/inventory/BaseInventory.php +++ b/src/inventory/BaseInventory.php @@ -242,7 +242,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ $slotItem->setCount($slotItem->getCount() + $amount); $this->setItem($i, $slotItem); if($newItem->getCount() <= 0){ - break; + return $newItem; } } } @@ -256,7 +256,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ $slotItem->setCount($amount); $this->setItem($slotIndex, $slotItem); if($newItem->getCount() <= 0){ - break; + return $newItem; } } } 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 new file mode 100644 index 000000000..58c58d627 --- /dev/null +++ b/src/item/HangingSign.php @@ -0,0 +1,67 @@ +tryPlacementTransaction(clone $this->wallVariant, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + //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(?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; + } + + 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..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; @@ -48,6 +49,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; @@ -125,7 +127,7 @@ class Item implements \JsonSerializable{ /** * @return $this */ - public function clearCustomBlockData(){ + public function clearCustomBlockData() : Item{ $this->blockEntityTag = null; return $this; } @@ -202,11 +204,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 +223,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 +238,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; } /** @@ -485,10 +490,24 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } + 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()))){ + 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, 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(); } @@ -587,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/ItemTypeIds.php b/src/item/ItemTypeIds.php index c63046c6b..af32cbcc2 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -334,8 +334,20 @@ 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 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 = 20298; + public const FIRST_UNUSED_ITEM_ID = 20310; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; 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/StringToItemParser.php b/src/item/StringToItemParser.php index a3bd7b872..2f316f66b 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()); @@ -1231,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()); @@ -1245,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()); @@ -1306,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()); @@ -1330,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()); @@ -1415,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()); @@ -1426,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()); @@ -1457,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()); @@ -1531,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()); @@ -1554,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/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/VanillaItems.php b/src/item/VanillaItems.php index f76cf369f..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,8 +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() @@ -307,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() @@ -328,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() @@ -397,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")); @@ -407,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")); @@ -421,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")); @@ -476,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")); @@ -517,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")); @@ -540,8 +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")); @@ -598,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")); @@ -608,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/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/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/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"; diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index c9bdfc6c3..b2393a206 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -251,19 +251,27 @@ class InventoryManager implements InventoryListener{ return null; } - private function addPredictedSlotChange(InventoryWindow $window, int $slot, ItemStack $item) : void{ + private function addPredictedSlotChangeInternal(InventoryWindow $window, int $slot, ItemStack $item) : void{ //TODO: does this need a null check? $entry = $this->getEntry($window->getInventory()) ?? throw new AssumptionFailedError("Assume this should never be null"); $entry->predictions[$slot] = $item; } - public function addTransactionPredictedSlotChanges(InventoryTransaction $tx) : void{ + public function addPredictedSlotChange(InventoryWindow $window, int $slot, Item $item) : void{ $typeConverter = $this->session->getTypeConverter(); + $itemStack = $typeConverter->coreItemStackToNet($item); + $this->addPredictedSlotChangeInternal($window, $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->getInventoryWindow(), $action->getSlot(), $itemStack); + $this->addPredictedSlotChange( + $action->getInventoryWindow(), + $action->getSlot(), + $action->getTargetItem() + ); } } } @@ -292,7 +300,7 @@ class InventoryManager implements InventoryListener{ } [$window, $slot] = $info; - $this->addPredictedSlotChange($window, $slot, $action->newItem->getItemStack()); + $this->addPredictedSlotChangeInternal($window, $slot, $action->newItem->getItemStack()); } } 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); 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/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 e4f5540d6..28fd61f70 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; @@ -111,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; @@ -137,6 +135,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; @@ -212,7 +212,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); @@ -249,26 +249,6 @@ class InGamePacketHandler extends PacketHandler{ $packetHandled = true; - $blockActions = $packet->getBlockActions(); - if($blockActions !== null){ - if(count($blockActions) > 100){ - throw new PacketHandlingException("Too many block actions in PlayerAuthInputPacket"); - } - 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); - }elseif($blockAction instanceof PlayerBlockActionWithBlockInfo){ - $actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), $blockAction->getBlockPosition(), $blockAction->getFace()); - } - - if(!$actionHandled){ - $packetHandled = false; - $this->session->getLogger()->debug("Unhandled player block action at offset $k in PlayerAuthInputPacket"); - } - } - } - $useItemTransaction = $packet->getItemInteractionData(); if($useItemTransaction !== null){ if(count($useItemTransaction->getTransactionData()->getActions()) > 100){ @@ -287,9 +267,33 @@ class InGamePacketHandler extends PacketHandler{ } $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){ + throw new PacketHandlingException("Too many block actions in PlayerAuthInputPacket"); + } + foreach(Utils::promoteKeys($blockActions) as $k => $blockAction){ + $actionHandled = false; + if($blockAction instanceof PlayerBlockActionStopBreak){ + $actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), new BlockPosition(0, 0, 0), 0); + }elseif($blockAction instanceof PlayerBlockActionWithBlockInfo){ + $actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), $blockAction->getBlockPosition(), $blockAction->getFace()); + } + + if(!$actionHandled){ + $packetHandled = false; + $this->session->getLogger()->debug("Unhandled player block action at offset $k in PlayerAuthInputPacket"); + } + } + } + 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; @@ -489,23 +493,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()); - 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); - } + $this->syncBlocksNearby($vBlockPos, $face); return true; case UseItemTransactionData::ACTION_CLICK_AIR: if($this->player->isUsingItem()){ @@ -525,16 +522,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){ @@ -582,7 +582,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 @@ -599,7 +599,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()); @@ -607,10 +611,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{ @@ -620,7 +621,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)); @@ -678,21 +679,32 @@ 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: - self::validateFacing($face); + case PlayerAction::CONTINUE_DESTROY_BLOCK: //destroy the next block while holding down left click + $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 + //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,13 +713,20 @@ 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; 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)){ + $face = self::deserializeFacing($extraData); + $this->syncBlocksNearby($pos, $face); + } + $this->lastBlockAttacked = null; break; case PlayerAction::START_ITEM_USE_ON: case PlayerAction::STOP_ITEM_USE_ON: @@ -782,10 +801,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/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 2e2138c29..d188b8c96 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -32,9 +32,12 @@ use pocketmine\inventory\transaction\EnchantingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\SlotChangeActionBuilder; use pocketmine\inventory\transaction\TransactionBuilder; +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; 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; @@ -46,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; @@ -233,9 +237,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); @@ -360,6 +365,18 @@ 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); + //TODO: this is a bit yucky - maybe we don't need the window to add a predicted slot change? + $inventoryWindow = $this->inventoryManager->getInventoryWindow($inventory) ?? throw new AssumptionFailedError("The player's inventory should always have an inventory window"); + $this->inventoryManager->addPredictedSlotChange($inventoryWindow, $slot, $usedItem); + } }else{ throw new ItemStackRequestProcessException("Unhandled item stack request action"); } @@ -368,7 +385,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); @@ -378,6 +395,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){ @@ -387,12 +407,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/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..4aa8be227 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; @@ -82,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); @@ -99,7 +99,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->server->getMotd(), "", false, - new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false), + new PlayerMovementSettings(0, true), 0, 0, "", @@ -108,6 +108,7 @@ class PreSpawnPacketHandler extends PacketHandler{ Uuid::fromString(Uuid::NIL), false, false, + false, new NetworkPermissions(disableClientSounds: true), [], 0, diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index a1df394da..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 = []; @@ -120,7 +125,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"); } @@ -139,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, "_"); @@ -152,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(), @@ -162,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); 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/player/Player.php b/src/player/Player.php index f6726776c..5f67414f5 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -27,6 +27,7 @@ use DateTimeImmutable; 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; @@ -46,6 +47,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 +62,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; @@ -102,6 +103,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; @@ -135,6 +138,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; @@ -338,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 @@ -1307,9 +1311,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) @@ -1440,9 +1450,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){ @@ -1645,7 +1655,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); @@ -1846,7 +1859,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? } @@ -1880,7 +1893,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); } @@ -1908,7 +1921,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{ @@ -1923,7 +1936,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)){ @@ -2011,7 +2024,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; @@ -2539,6 +2552,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()); @@ -2580,7 +2608,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{ diff --git a/src/player/SurvivalBlockBreakHandler.php b/src/player/SurvivalBlockBreakHandler.php index 3e7fdcfc4..5c012bccf 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/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/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..3b401d44a 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, @@ -67,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), "/") . "/"; @@ -311,14 +309,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/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 1a74b64e7..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)){ @@ -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; 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; } 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/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index e1c9d7c6b..d7d51d40f 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -94,7 +94,19 @@ 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. + //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(); + } + return $this->crashInfo; + } public function start(int $options = NativeThread::INHERIT_NONE) : bool{ ThreadManager::getInstance()->add($this); 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); } } 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..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; @@ -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 @@ -564,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; } 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/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/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 c7a9775fb..ee70bc99b 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; @@ -287,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> @@ -336,11 +344,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 +364,6 @@ class World implements ChunkManager{ private bool $doingTick = false; - /** @phpstan-var class-string<\pocketmine\world\generator\Generator> */ - private string $generator; - private bool $unloaded = false; /** * @var \Closure[] @@ -498,7 +499,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 +551,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 +591,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; } @@ -640,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); @@ -657,7 +649,7 @@ class World implements ChunkManager{ $this->save(); - $this->unregisterGenerator(); + $this->generatorExecutor->shutdown(); $this->provider->close(); $this->blockCache = []; @@ -991,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(); @@ -1619,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() }; @@ -1633,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)){ @@ -2227,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); @@ -2287,22 +2279,15 @@ class World implements ChunkManager{ if($item->isNull() || !$item->canBePlaced()){ return false; } - $hand = $item->getBlock($face); - $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; } @@ -2346,6 +2331,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){ @@ -2353,11 +2339,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(); @@ -2621,6 +2608,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){ @@ -2653,6 +2650,7 @@ class World implements ChunkManager{ } $this->chunks[$chunkHash] = $chunk; + unset($this->knownUngeneratedChunks[$chunkHash]); $this->blockCacheSize -= count($this->blockCache[$chunkHash] ?? []); unset($this->blockCache[$chunkHash]); @@ -2917,6 +2915,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(); @@ -2936,6 +2937,7 @@ class World implements ChunkManager{ if($loadedChunkData === null){ $this->timings->syncChunkLoad->stopTiming(); + $this->knownUngeneratedChunks[$chunkHash] = true; return null; } @@ -2952,7 +2954,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(); @@ -2972,12 +2974,14 @@ 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){ $this->timings->syncChunkLoadEntities->startTiming(); $entityFactory = EntityFactory::getInstance(); + + $deletedEntities = []; foreach($chunkData->getEntityNBT() as $k => $nbt){ try{ $entity = $entityFactory->createFromData($this, $nbt); @@ -2994,18 +2998,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); @@ -3015,7 +3024,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; } @@ -3029,6 +3039,20 @@ 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){ + $logger->warning("Deleted unknown tile entity type $saveId x$count"); } $this->timings->syncChunkLoadTileEntities->stopTiming(); @@ -3324,7 +3348,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), @@ -3371,7 +3395,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); @@ -3456,8 +3480,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); @@ -3467,8 +3490,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, @@ -3481,15 +3504,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/biome/model/BiomeDefinitionEntryData.php b/src/world/biome/model/BiomeDefinitionEntryData.php new file mode 100644 index 000000000..bb63b36e1 --- /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 @@ +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; 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/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index 5b1945739..68eb6dc81 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\format\io\data; +use pocketmine\data\bedrock\WorldDataVersions; use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NbtDataException; use pocketmine\nbt\tag\CompoundTag; @@ -50,15 +51,9 @@ use function time; class BedrockWorldData extends BaseNbtWorldData{ - public const CURRENT_STORAGE_VERSION = 10; - public const CURRENT_STORAGE_NETWORK_VERSION = 786; - public const CURRENT_CLIENT_VERSION_TARGET = [ - 1, //major - 21, //minor - 70, //patch - 3, //revision - 0 //is beta - ]; + public const CURRENT_STORAGE_VERSION = WorldDataVersions::STORAGE; + public const CURRENT_STORAGE_NETWORK_VERSION = WorldDataVersions::NETWORK; + public const CURRENT_CLIENT_VERSION_TARGET = WorldDataVersions::LAST_OPENED_IN; public const GENERATOR_LIMITED = 0; public const GENERATOR_INFINITE = 1; diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index 41c477867..3a64f93f6 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -27,6 +27,7 @@ use pocketmine\block\Block; use pocketmine\data\bedrock\BiomeIds; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\convert\UnsupportedBlockStateException; +use pocketmine\data\bedrock\WorldDataVersions; use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NBT; use pocketmine\nbt\NbtDataException; @@ -35,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; @@ -78,8 +80,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ protected const ENTRY_FLAT_WORLD_LAYERS = "game_flatworldlayers"; - protected const CURRENT_LEVEL_CHUNK_VERSION = ChunkVersion::v1_21_40; - protected const CURRENT_LEVEL_SUBCHUNK_VERSION = SubChunkVersion::PALETTED_MULTI; + protected const CURRENT_LEVEL_CHUNK_VERSION = WorldDataVersions::CHUNK; + protected const CURRENT_LEVEL_SUBCHUNK_VERSION = WorldDataVersions::SUBCHUNK; private const CAVES_CLIFFS_EXPERIMENTAL_SUBCHUNK_KEY_OFFSET = 4; @@ -203,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 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 bad134324..971349a5b 100644 --- a/src/world/generator/PopulationTask.php +++ b/src/world/generator/PopulationTask.php @@ -27,13 +27,18 @@ 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 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{ @@ -71,8 +76,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 +96,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 +115,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]; + } +} 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/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/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 @@ +, 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 @@ -702,12 +708,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 @@ -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 diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index aeb3fae29..a80050020 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 @@ -108,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 @@ -252,9 +318,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 - 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; } } 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} 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/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 0b9150988..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,14 +528,19 @@ "OAK_SLAB": 3, "OAK_STAIRS": 8, "OAK_TRAPDOOR": 16, + "OAK_WALL_HANGING_SIGN": 4, "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, "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, @@ -524,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, @@ -615,6 +644,7 @@ "RESIN_BRICK_STAIRS": 8, "RESIN_BRICK_WALL": 162, "RESIN_CLUMP": 64, + "RESPAWN_ANCHOR": 5, "ROSE_BUSH": 2, "SAND": 1, "SANDSTONE": 1, @@ -653,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, @@ -665,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, @@ -708,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, @@ -720,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, @@ -733,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", @@ -764,29 +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" } 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/convert/BlockSerializerDeserializerTest.php b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php index a47a9b155..c0f850027 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,16 +51,21 @@ 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){ - //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; } 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()); diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index 50639f51d..f40029365 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -54,7 +54,6 @@ use pocketmine\network\mcpe\protocol\PacketPool; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\StartGamePacket; -use pocketmine\network\mcpe\protocol\types\CacheableNbt; use pocketmine\network\mcpe\protocol\types\inventory\CreativeGroupEntry; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData; @@ -76,6 +75,8 @@ use pocketmine\network\PacketHandlingException; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; +use pocketmine\world\biome\model\BiomeDefinitionEntryData; +use pocketmine\world\biome\model\ColorData; use pocketmine\world\format\io\GlobalBlockStateHandlers; use Ramsey\Uuid\Exception\InvalidArgumentException; use Symfony\Component\Filesystem\Path; @@ -100,6 +101,7 @@ use function json_encode; use function ksort; use function mkdir; use function ord; +use function round; use function strlen; use const FILE_IGNORE_NEW_LINES; use const JSON_PRETTY_PRINT; @@ -207,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){ @@ -278,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"); @@ -572,34 +581,34 @@ class ParserPacketHandler extends PacketHandler{ public function handleBiomeDefinitionList(BiomeDefinitionListPacket $packet) : bool{ echo "storing biome definitions" . PHP_EOL; - file_put_contents($this->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; }