diff --git a/.github/ISSUE_TEMPLATE/api-change-request.md b/.github/ISSUE_TEMPLATE/api-change-request.md
deleted file mode 100644
index e3d24ea0f..000000000
--- a/.github/ISSUE_TEMPLATE/api-change-request.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-name: API change request
-about: Suggest a change, addition or removal to the plugin API
-title: ''
-labels: ''
-assignees: ''
-
----
-
-
-## Problem description
-
-
-
-## Proposed solution
-
-
-
-## Alternative solutions that don't require API changes
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
new file mode 100644
index 000000000..209ba8880
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -0,0 +1,87 @@
+name: Bug report
+description: Report a feature of PocketMine-MP not working as expected
+body:
+ - type: markdown
+ attributes:
+ value: |
+ ## Plugin information
+
+ > [!IMPORTANT]
+ > It's strongly recommended to test for bugs without plugins before reporting an issue.
+ > This helps avoid wasting maintainers' time on bugs that are not actually caused by PocketMine-MP.
+ >
+ > If you're not sure whether a plugin might be causing your issue, please seek help on our [Discord](https://discord.gg/bmSAZBG) before writing an issue.
+
+ - type: dropdown
+ attributes:
+ label: Plugin information
+ options:
+ - "I haven't tested without plugins"
+ - Bug happens without plugins
+ - Bug only happens with certain plugins (describe below)
+ validations:
+ required: true
+
+ - type: markdown
+ attributes:
+ value: |
+ ## Bug description
+
+ > [!TIP]
+ > Helpful information to include:
+ > - Steps to reproduce the issue
+ > - Error backtraces
+ > - Crashdumps
+ > - Plugin code that triggers the issue
+ > - List of installed plugins (use /plugins)
+
+ > [!IMPORTANT]
+ > **Steps to reproduce are critical to finding the cause of the problem!**
+ > Without reproducing steps, the issue will probably not be solvable and may be closed.
+
+ - type: textarea
+ attributes:
+ label: Problem description
+ description: Describe the problem, and how you encountered it
+ placeholder: e.g. Steps to reproduce the issue
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Expected behaviour
+ description: What did you expect to happen?
+ validations:
+ required: true
+
+ - type: markdown
+ attributes:
+ value: |
+ ## Version, OS and game info
+ > [!WARNING]
+ > "Latest" is not a valid version.
+ > Failure to fill these fields with valid information may result in your issue being closed.
+
+ - type: input
+ attributes:
+ label: PocketMine-MP version
+ placeholder: Use the /version command in PocketMine-MP
+ validations:
+ required: true
+ - type: input
+ attributes:
+ label: PHP version
+ placeholder: Use the /version command in PocketMine-MP
+ validations:
+ required: true
+ - type: input
+ attributes:
+ label: Server OS
+ placeholder: Use the /version command in PocketMine-MP
+ validations:
+ required: true
+ - type: input
+ attributes:
+ label: Game version (if applicable)
+ placeholder: e.g. Android, iOS, Windows, Xbox, PS4, Switch
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 730d6e811..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,37 +0,0 @@
----
-name: Bug report
-about: Unexpected non-crash behaviour (except missing gameplay features)
-title: ''
-labels: ''
-assignees: ''
-
----
-
-### Issue description
-
-- Expected result: What were you expecting to happen?
-- Actual result: What actually happened?
-
-### Steps to reproduce the issue
-1. ...
-2. ...
-
-### OS and versions
-
-* PocketMine-MP:
-* PHP:
-* Using JIT: yes/no (delete as appropriate)
-* Server OS:
-* Game version: Android/iOS/Win10/Xbox/PS4/Switch (delete as appropriate)
-
-### Plugins
-
-
-- If you remove all plugins, does the issue still occur?
-- If the issue is **not** reproducible without plugins:
- - Have you asked for help on our forums before creating an issue?
- - Can you provide sample, *minimal* reproducing code for the issue? If so, paste it in the bottom section
-
-### Crashdump, backtrace or other files
-
-
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index d13fb4498..d18b277e7 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,9 +3,6 @@ contact_links:
- name: Help & support on Discord
url: https://discord.gg/bmSAZBG
about: We don't accept support requests on the issue tracker. Please try asking on Discord instead.
- - name: Help & support on forums
- url: https://forums.pmmp.io
- about: We don't accept support requests on the issue tracker. Please try asking on forums instead.
- name: Documentation
url: https://pmmp.rtfd.io
about: PocketMine-MP documentation
diff --git a/.github/ISSUE_TEMPLATE/crash.md b/.github/ISSUE_TEMPLATE/crash.md
deleted file mode 100644
index ee91d230e..000000000
--- a/.github/ISSUE_TEMPLATE/crash.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-name: Crash
-about: Report a crash in PocketMine-MP (not plugins)
-title: Server crashed
-labels: ''
-assignees: ''
-
----
-
-
-
-
-Link to crashdump:
-
-
-### Additional comments (optional)
diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml
new file mode 100644
index 000000000..735255de2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/crash.yml
@@ -0,0 +1,25 @@
+name: Crash
+description: Report a crash in PocketMine-MP (not plugins)
+title: Server crashed
+body:
+ - type: markdown
+ attributes:
+ value: |
+ > [!TIP]
+ > Submit crashdump `.log` files to the [Crash Archive](https://crash.pmmp.io/submit).
+ > If you can't submit the crashdump to the Crash Archive, paste it on a site like [GitHub Gist](https://gist.github.com) or [Pastebin](https://pastebin.com).
+
+ > [!CAUTION]
+ > DON'T paste the crashdump data directly into an issue.
+
+ - type: input
+ id: crashdump-url
+ attributes:
+ label: Link to crashdump
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Additional comments (optional)
+ description: Any other information that might help us solve the problem
diff --git a/.github/ISSUE_TEMPLATE/feature-proposal.yml b/.github/ISSUE_TEMPLATE/feature-proposal.yml
new file mode 100644
index 000000000..e0d37ef06
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-proposal.yml
@@ -0,0 +1,19 @@
+name: Feature addition, change, or removal
+description: Propose adding new features, or changing/removing existing ones
+body:
+ - type: textarea
+ attributes:
+ label: Problem description
+ description: Explain why a change is needed
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Proposed solution
+ description: Describe what changes you think should be made
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: "Alternative solutions or workarounds"
+ description: "Describe other ways you've explored to achieve your goal"
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 97607ab8f..ded96ca5d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -12,6 +12,10 @@ updates:
update-types:
- "version-update:semver-major"
- "version-update:semver-minor"
+
+ #since we lock this to exact versions, it causes conflicts with minor-next & major-next in composer.lock
+ #better to just test updates to this locally anyway since almost every version breaks something
+ - dependency-name: phpstan/phpstan
groups:
production-patch-updates:
dependency-type: production
@@ -37,4 +41,7 @@ updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
- interval: weekly
+ interval: monthly
+ groups:
+ github-actions:
+ patterns: ["*"]
diff --git a/.github/workflows/branch-sync-cron-trigger.yml b/.github/workflows/branch-sync-cron-trigger.yml
new file mode 100644
index 000000000..896141d83
--- /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 21 * * *" #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 4325c63f2..acfc3d3a7 100644
--- a/.github/workflows/build-docker-image.yml
+++ b/.github/workflows/build-docker-image.yml
@@ -4,11 +4,16 @@ on:
release:
types:
- published
+ workflow_dispatch:
+ inputs:
+ release:
+ description: 'Tag name to build'
+ required: true
jobs:
build:
name: Update Docker Hub images
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- name: Set up Docker Buildx
@@ -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.9.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.9.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.9.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.9.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 8d0add224..76780d633 100644
--- a/.github/workflows/discord-release-notify.yml
+++ b/.github/workflows/discord-release-notify.yml
@@ -4,16 +4,21 @@ 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.31.1
+ uses: shivammathur/setup-php@2.35.4
with:
php-version: 8.2
@@ -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-from-pr.yml b/.github/workflows/draft-release-from-pr.yml
deleted file mode 100644
index 8a347853b..000000000
--- a/.github/workflows/draft-release-from-pr.yml
+++ /dev/null
@@ -1,65 +0,0 @@
-name: Draft release from PR
-
-on:
- #presume that pull_request_target is safe at this point, since the PR was approved and merged
- #we need write access to prepare the release & create comments
- pull_request_target:
- types:
- - closed
- branches:
- - stable
- - minor-next
- - major-next
- - "legacy/*"
- paths:
- - "src/VersionInfo.php"
-
-jobs:
- check:
- name: Check release
- uses: ./.github/workflows/draft-release-pr-check.yml
-
- draft:
- name: Create GitHub draft release
- needs: [check]
- if: needs.check.outputs.valid == 'true'
-
- uses: ./.github/workflows/draft-release.yml
-
- post-draft-url-comment:
- name: Post draft release URL as comment
- needs: [draft]
-
- runs-on: ubuntu-20.04
-
- steps:
- - name: Post draft release URL on PR
- uses: thollander/actions-comment-pull-request@v3
- with:
- message: "[Draft release ${{ needs.draft.outputs.version }}](${{ needs.draft.outputs.draft-url }}) has been created for commit ${{ github.sha }}. Please review and publish it."
-
- trigger-post-release-workflow:
- name: Trigger post-release RestrictedActions workflow
- # Not sure if needs is actually needed here
- needs: [check]
- if: needs.check.outputs.valid == 'true'
-
- runs-on: ubuntu-20.04
-
- steps:
- - name: Generate access token
- id: generate-token
- uses: actions/create-github-app-token@v1
- with:
- app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
- private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
- owner: ${{ github.repository_owner }}
- repositories: RestrictedActions
-
- - name: Dispatch post-release restricted action
- uses: peter-evans/repository-dispatch@v3
- with:
- token: ${{ steps.generate-token.outputs.token }}
- repository: ${{ github.repository_owner }}/RestrictedActions
- event-type: pocketmine_mp_post_release
- client-payload: '{"branch": "${{ github.ref }}"}'
diff --git a/.github/workflows/draft-release-from-tag.yml b/.github/workflows/draft-release-from-tag.yml
deleted file mode 100644
index f7a5df544..000000000
--- a/.github/workflows/draft-release-from-tag.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-#Allows creating a release by pushing a tag
-#This might be useful for retroactive releases
-name: Draft release from git tag
-
-on:
- push:
- tags: "*"
-
-jobs:
- draft:
- name: Create GitHub draft release
- if: "startsWith(github.event.head_commit.message, 'Release ')"
- uses: ./.github/workflows/draft-release.yml
diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml
index 4c8d0f685..9905d843d 100644
--- a/.github/workflows/draft-release-pr-check.yml
+++ b/.github/workflows/draft-release-pr-check.yml
@@ -24,13 +24,13 @@ permissions:
jobs:
check-intent:
name: Check release trigger
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
outputs:
valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Check IS_DEVELOPMENT_BUILD flag
id: validate
@@ -43,13 +43,13 @@ jobs:
#don't do these checks if this isn't a release - we don't want to generate unnecessary failed statuses
if: needs.check-intent.outputs.valid == 'true'
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Setup PHP
- uses: shivammathur/setup-php@2.31.1
+ uses: shivammathur/setup-php@2.35.4
with:
php-version: 8.2
diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml
index cd1841e4f..6edf05dfb 100644
--- a/.github/workflows/draft-release.yml
+++ b/.github/workflows/draft-release.yml
@@ -1,38 +1,95 @@
name: Draft release
on:
- workflow_call:
- outputs:
- draft-url:
- description: 'The URL of the draft release'
- value: ${{ jobs.draft.outputs.draft-url }}
- version:
- description: 'PocketMine-MP version'
- value: ${{ jobs.draft.outputs.version }}
+ #presume that pull_request_target is safe at this point, since the PR was approved and merged
+ #we need write access to prepare the release & create comments
+ pull_request_target:
+ types:
+ - closed
+ branches:
+ - stable
+ - minor-next
+ - major-next
+ - "legacy/*"
+ paths:
+ - "src/VersionInfo.php"
+ push:
+ tags:
+ - "*"
+
+env:
+ PHP_VERSION: "8.2"
jobs:
- draft:
- name: Create GitHub draft release
-
- runs-on: ubuntu-20.04
- strategy:
- fail-fast: false
- matrix:
- php-version: [8.2]
+ skip:
+ name: Check whether to ignore this tag
+ runs-on: ubuntu-22.04
outputs:
- draft-url: ${{ steps.create-draft.outputs.html_url }}
- version: ${{ steps.get-pm-version.outputs.PM_VERSION }}
+ skip: ${{ steps.exists.outputs.exists == 'true' }}
steps:
- - uses: actions/checkout@v4
+ - name: Check if release already exists
+ id: exists
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ exists=false
+ if [[ "${{ github.ref_type }}" == "tag" ]]; then
+ tag="$(echo "${{ github.ref }}" | cut -d/ -f3-)"
+ if gh release view "$tag" --repo "${{ github.repository }}"; then
+ exists=true
+ fi
+ fi
+ echo exists=$exists >> $GITHUB_OUTPUT
+
+ check:
+ needs: [skip]
+ if: needs.skip.outputs.skip != 'true'
+ name: Check release
+ uses: ./.github/workflows/draft-release-pr-check.yml
+
+ trigger-post-release-workflow:
+ name: Trigger post-release RestrictedActions workflow
+ needs: [check]
+ if: needs.check.outputs.valid == 'true' && github.ref_type != 'tag' #can't do post-commit for a tag
+
+ 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 post-release restricted action
+ uses: peter-evans/repository-dispatch@v3
+ with:
+ token: ${{ steps.generate-token.outputs.token }}
+ repository: ${{ github.repository_owner }}/RestrictedActions
+ event-type: pocketmine_mp_post_release
+ client-payload: '{"branch": "${{ github.ref }}"}'
+
+ draft:
+ name: Create GitHub draft release
+ needs: [check]
+ if: needs.check.outputs.valid == 'true' || github.ref_type == 'tag' #ignore validity check for tags
+
+ runs-on: ubuntu-22.04
+
+ steps:
+ - uses: actions/checkout@v5
with:
submodules: true
- name: Setup PHP
- uses: shivammathur/setup-php@2.31.1
+ uses: shivammathur/setup-php@2.35.4
with:
- php-version: ${{ matrix.php-version }}
+ php-version: ${{ env.PHP_VERSION }}
- name: Restore Composer package cache
uses: actions/cache@v4
@@ -50,7 +107,7 @@ jobs:
- name: Calculate build number
id: build-number
run: |
- BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins
+ BUILD_NUMBER=$((2300+$GITHUB_RUN_NUMBER)) #to stay above jenkins
echo "Build number: $BUILD_NUMBER"
echo BUILD_NUMBER=$BUILD_NUMBER >> $GITHUB_OUTPUT
@@ -63,23 +120,31 @@ jobs:
- name: Get PocketMine-MP release version
id: get-pm-version
run: |
- echo PM_VERSION=$(php build/dump-version-info.php base_version) >> $GITHUB_OUTPUT
+ PM_VERSION=$(php build/dump-version-info.php base_version)
+ echo PM_VERSION=$PM_VERSION >> $GITHUB_OUTPUT
echo PM_MAJOR=$(php build/dump-version-info.php major_version) >> $GITHUB_OUTPUT
echo MCPE_VERSION=$(php build/dump-version-info.php mcpe_version) >> $GITHUB_OUTPUT
echo CHANGELOG_FILE_NAME=$(php build/dump-version-info.php changelog_file_name) >> $GITHUB_OUTPUT
echo CHANGELOG_MD_HEADER=$(php build/dump-version-info.php changelog_md_header) >> $GITHUB_OUTPUT
echo PRERELEASE=$(php build/dump-version-info.php prerelease) >> $GITHUB_OUTPUT
+ if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
+ tag="$(echo "${{ github.ref }}" | cut -d/ -f3-)"
+ else
+ tag="$PM_VERSION"
+ fi
+ echo TAG_NAME=$tag >> $GITHUB_OUTPUT
+
- name: Generate PHP binary download URL
id: php-binary-url
run: |
- echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/pm${{ steps.get-pm-version.outputs.PM_MAJOR }}-php-${{ matrix.php-version }}-latest" >> $GITHUB_OUTPUT
+ echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/pm${{ steps.get-pm-version.outputs.PM_MAJOR }}-php-${{ env.PHP_VERSION }}-latest" >> $GITHUB_OUTPUT
- name: Generate build info
run: |
php build/generate-build-info-json.php \
${{ github.sha }} \
- ${{ steps.get-pm-version.outputs.PM_VERSION }} \
+ ${{ steps.get-pm-version.outputs.TAG_NAME }} \
${{ github.repository }} \
${{ steps.build-number.outputs.BUILD_NUMBER }} \
${{ github.run_id }} \
@@ -100,7 +165,7 @@ jobs:
${{ github.workspace }}/core-permissions.rst
- name: Create draft release
- uses: ncipollo/release-action@v1.14.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
@@ -108,12 +173,19 @@ jobs:
draft: true
prerelease: ${{ steps.get-pm-version.outputs.PRERELEASE }}
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
- tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
+ tag: ${{ steps.get-pm-version.outputs.TAG_NAME }}
token: ${{ secrets.GITHUB_TOKEN }}
- skipIfReleaseExists: true #for release PRs, tags will be created on release publish and trigger the tag release workflow - don't create a second draft
body: |
**For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}**
Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.CHANGELOG_FILE_NAME }}#${{ steps.get-pm-version.outputs.CHANGELOG_MD_HEADER }}) for details.
:information_source: Download the recommended PHP binary [here](${{ steps.php-binary-url.outputs.PHP_BINARY_URL }}).
+
+ :warning: Found a bug? Report it on our [issue tracker](${{ github.server_url }}/${{ github.repository }}/issues). **We can't fix bugs if you don't report them.**
+
+ - name: Post draft release URL on PR
+ if: github.event_name == 'pull_request_target'
+ uses: thollander/actions-comment-pull-request@v3
+ with:
+ 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 e26f7c318..7637a3956 100644
--- a/.github/workflows/main-php-matrix.yml
+++ b/.github/workflows/main-php-matrix.yml
@@ -15,7 +15,7 @@ on:
type: number
image:
description: 'Runner image to use'
- default: 'ubuntu-20.04'
+ default: 'ubuntu-22.04'
type: string
jobs:
@@ -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 b5a9740b5..a7d6eb0fc 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.1", "8.2", "8.3", "8.4"]
uses: ./.github/workflows/main-php-matrix.yml
with:
@@ -20,18 +20,18 @@ jobs:
codestyle:
name: Code Style checks
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
strategy:
fail-fast: false
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Setup PHP and tools
- uses: shivammathur/setup-php@2.31.1
+ uses: shivammathur/setup-php@2.35.4
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 }}
@@ -40,12 +40,12 @@ jobs:
shellcheck:
name: ShellCheck
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
strategy:
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/pr-stale.yml b/.github/workflows/pr-stale.yml
new file mode 100644
index 000000000..23518b2cf
--- /dev/null
+++ b/.github/workflows/pr-stale.yml
@@ -0,0 +1,29 @@
+name: 'Clean up stale PRs'
+on:
+ schedule:
+ - cron: '30 1 * * *'
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@v9
+ with:
+ days-before-issue-stale: -1
+ days-before-issue-close: -1
+ stale-pr-message: |
+ This PR has been marked as "Waiting on Author", but we haven't seen any activity in 7 days.
+
+ If there is no further activity, it will be closed in 28 days.
+
+ Note for maintainers: Adding an assignee to the PR will prevent it from being marked as stale.
+
+ close-pr-message: |
+ As this PR hasn't been updated for a while, unfortunately we'll have to close it.
+
+ days-before-pr-stale: 7
+ days-before-pr-close: 28
+ only-labels: "Status: Waiting on Author"
+ close-pr-label: "Resolution: Abandoned"
+ exempt-all-assignees: true
+
diff --git a/.github/workflows/support.yml b/.github/workflows/support.yml
index 68da365cb..3a65f78a4 100644
--- a/.github/workflows/support.yml
+++ b/.github/workflows/support.yml
@@ -20,10 +20,7 @@ jobs:
- Check our [Documentation](https://doc.pmmp.io) to see if you can find answers there
- - Ask the community on our [Discord server](https://discord.gg/bmSAZBG) or our [Forums](https://forums.pmmp.io)
-
-
- [Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)
+ - Ask the community on our [Discord server](https://discord.gg/bmSAZBG)
close-issue: true
lock-issue: false
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 32af1ef48..5a14b1d35 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -6,6 +6,12 @@ $finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/tests')
->in(__DIR__ . '/tools')
->notPath('plugins/DevTools')
+ //JsonMapper will break if the FQNs in the doc comments for these are shortened :(
+ ->notPath('crafting/json')
+ ->notPath('inventory/json')
+ ->notPath('data/bedrock/block/upgrade/model')
+ ->notPath('data/bedrock/item/upgrade/model')
+
->notName('PocketMine.php');
return (new PhpCsFixer\Config)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3490542c5..799c9d99c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -123,7 +123,7 @@ The following are required as a minimum for pull requests. PRs that don't meet t
- Remember, PRs with small diffs are much easier to review. Small PRs are generally reviewed and merged much faster than large ones.
- **Start small.** Try fixing minor bugs or doing something isolated (e.g. adding a new block or item) before attempting larger changes.
- This helps you get familiar with the codebase, the contribution process, and the expectations of maintainers.
- - Check out the [issues page]() for something that you could tackle without too much effort.
+ - Check out ["Easy task" issues](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue+is%3Aopen+label%3A%22Easy+task%22) on the issues page for something that you could tackle without too much effort.
- **Do not copy-paste other people's code**. Many PRs involve discussion about the changes, and changes are often requested by reviewers. If you don't understand the code you're copy-pasting, your PR is likely to fail.
- **Do not edit code directly on github.com.** We recommend learning how to use [`git`](https://git-scm.com). `git` allows you to "clone" a repository onto your computer, so that you can make changes using an IDE.
- **Use an IDE, not a text editor.** We recommend PhpStorm or VSCode.
diff --git a/README.md b/README.md
index b9e2e1888..352b628b6 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -65,13 +65,15 @@ PocketMine-MP accepts community contributions! The following resources will be u
* [Building and running PocketMine-MP from source](BUILDING.md)
* [Contributing Guidelines](CONTRIBUTING.md)
+New here? Check out [issues with the "Easy task" label](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22Easy%20task%22) for things you could work to familiarise yourself with the codebase.
+
## Donate
PocketMine-MP is free, but it requires a lot of time and effort from unpaid volunteers to develop. Donations enable us to keep delivering support for new versions and adding features your players love.
You can support development using the following methods:
- [Patreon](https://www.patreon.com/pocketminemp)
-- Bitcoin (BTC): `171u8K9e4FtU6j3e5sqNoxKUgEw9qWQdRV`
+- Bitcoin (BTC): `bc1q2v5ngyf8ugyd55kqa9ep35g2rv342ueqm6ks33`
- Stellar Lumens (XLM): `GAAC5WZ33HCTE3BFJFZJXONMEIBNHFLBXM2HJVAZHXXPYA3HP5XPPS7T`
Thanks for your support!
diff --git a/build/dump-version-info.php b/build/dump-version-info.php
index 166264d98..3181acba6 100644
--- a/build/dump-version-info.php
+++ b/build/dump-version-info.php
@@ -31,12 +31,12 @@ 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,
- "major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0],
+ "major_version" => fn() => explode(".", VersionInfo::BASE_VERSION, limit: 2)[0],
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
"changelog_file_name" => function() : string{
diff --git a/build/generate-bedrockdata-path-consts.php b/build/generate-bedrockdata-path-consts.php
index 6ad6d83fd..f74137dd2 100644
--- a/build/generate-bedrockdata-path-consts.php
+++ b/build/generate-bedrockdata-path-consts.php
@@ -28,6 +28,7 @@ use function dirname;
use function fclose;
use function fopen;
use function fwrite;
+use function is_dir;
use function is_file;
use function scandir;
use function str_replace;
@@ -59,7 +60,7 @@ foreach($files as $file){
continue;
}
$path = Path::join(BEDROCK_DATA_PATH, $file);
- if(!is_file($path)){
+ if(!is_file($path) && !is_dir($path)){
continue;
}
@@ -67,6 +68,7 @@ foreach($files as $file){
'README.md',
'LICENSE',
'composer.json',
+ '.github'
] as $ignored){
if($file === $ignored){
continue 2;
diff --git a/build/generate-block-serializer-consts.php b/build/generate-block-serializer-consts.php
index 875729fcf..6dab2ceb2 100644
--- a/build/generate-block-serializer-consts.php
+++ b/build/generate-block-serializer-consts.php
@@ -29,6 +29,9 @@ use pocketmine\data\bedrock\block\BlockStateStringValues;
use pocketmine\data\bedrock\block\BlockTypeNames;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\nbt\NbtException;
+use pocketmine\nbt\tag\ByteTag;
+use pocketmine\nbt\tag\IntTag;
+use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\BlockStateDictionary;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
@@ -78,6 +81,9 @@ function generateBlockPaletteReport(array $states) : BlockPaletteReport{
$name = $stateData->getName();
$result->seenTypes[$name] = $name;
foreach(Utils::stringifyKeys($stateData->getStates()) as $k => $v){
+ if(!$v instanceof ByteTag && !$v instanceof IntTag && !$v instanceof StringTag){
+ throw new AssumptionFailedError("Assumed all state tags should be TAG_Byte, TAG_Int or TAG_String, but found $k ($v) on block $name");
+ }
$result->seenStateValues[$k][$v->getValue()] = $v->getValue();
asort($result->seenStateValues[$k]);
}
diff --git a/build/make-release.php b/build/make-release.php
deleted file mode 100644
index 741f9d787..000000000
--- a/build/make-release.php
+++ /dev/null
@@ -1,174 +0,0 @@
- "Version to insert and tag",
- "next" => "Version to put in the file after tagging",
- "channel" => "Release channel to post this build into"
-];
-
-function systemWrapper(string $command, string $errorMessage) : void{
- system($command, $result);
- if($result !== 0){
- echo "error: $errorMessage; aborting\n";
- exit(1);
- }
-}
-
-function main() : void{
- $filteredOpts = [];
- $postCommitOnly = false;
- foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help", "post"])) as $optName => $optValue){
- if($optName === "help"){
- fwrite(STDOUT, "Options:\n");
-
- $maxLength = max(array_map(fn(string $str) => strlen($str), array_keys(ACCEPTED_OPTS)));
- foreach(ACCEPTED_OPTS as $acceptedName => $description){
- fwrite(STDOUT, str_pad("--$acceptedName", $maxLength + 4, " ", STR_PAD_LEFT) . ": $description\n");
- }
- exit(0);
- }
- if($optName === "post"){
- $postCommitOnly = true;
- continue;
- }
- if(!is_string($optValue)){
- fwrite(STDERR, "--$optName expects exactly 1 value\n");
- exit(1);
- }
- $filteredOpts[$optName] = $optValue;
- }
-
- $channel = $filteredOpts["channel"] ?? null;
- if(isset($filteredOpts["current"])){
- $currentVer = new VersionString($filteredOpts["current"]);
- }else{
- $currentVer = new VersionString(VersionInfo::BASE_VERSION);
- }
-
- $nextVer = isset($filteredOpts["next"]) ? new VersionString($filteredOpts["next"]) : null;
-
- $suffix = $currentVer->getSuffix();
- if($suffix !== ""){
- if($channel === "stable"){
- fwrite(STDERR, "error: cannot release a suffixed build into the stable channel\n");
- exit(1);
- }
- if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){
- echo "error: invalid current version suffix \"$suffix\"; aborting\n";
- exit(1);
- }
- $nextVer ??= new VersionString(sprintf(
- "%u.%u.%u-%s%u",
- $currentVer->getMajor(),
- $currentVer->getMinor(),
- $currentVer->getPatch(),
- $matches[1],
- ((int) $matches[2]) + 1
- ));
- $channel ??= strtolower($matches[1]);
- }else{
- $nextVer ??= new VersionString(sprintf(
- "%u.%u.%u",
- $currentVer->getMajor(),
- $currentVer->getMinor(),
- $currentVer->getPatch() + 1
- ));
- $channel ??= "stable";
- }
-
- $versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
-
- if($postCommitOnly){
- echo "Skipping release commit & tag. Bumping to next version $nextVer directly.\n";
- }else{
- echo "About to tag version $currentVer. Next version will be $nextVer.\n";
- echo "$currentVer will be published on release channel \"$channel\".\n";
- echo "please add appropriate notes to the changelog and press enter...";
- fgets(STDIN);
- systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes");
- system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result);
- if($result === 0){
- echo "error: no changelog changes detected; aborting\n";
- exit(1);
- }
- replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel);
- systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit");
- systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag");
- }
-
- replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel);
- systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit");
- systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit");
-}
-
-main();
diff --git a/build/php b/build/php
index 5016e0a3d..8fe187335 160000
--- a/build/php
+++ b/build/php
@@ -1 +1 @@
-Subproject commit 5016e0a3d54c714c12b331ea0474a6f500ffc0a3
+Subproject commit 8fe187335f666b8fb251927940f66ef372bfd3a6
diff --git a/build/server-phar-stub.php b/build/server-phar-stub.php
index b4018e3a7..c713636c4 100644
--- a/build/server-phar-stub.php
+++ b/build/server-phar-stub.php
@@ -25,6 +25,7 @@ namespace pocketmine\server_phar_stub;
use function clearstatcache;
use function copy;
+use function define;
use function fclose;
use function fflush;
use function flock;
@@ -165,4 +166,5 @@ $start = hrtime(true);
$cacheName = preparePharCache($tmpDir, __FILE__);
echo "Cache ready at $cacheName in " . number_format((hrtime(true) - $start) / 1e9, 2) . "s\n";
+define('pocketmine\ORIGINAL_PHAR_PATH', __FILE__);
require 'phar://' . str_replace(DIRECTORY_SEPARATOR, '/', $cacheName) . '/src/PocketMine.php';
diff --git a/build/server-phar.php b/build/server-phar.php
index f6bb29d51..7560fa5da 100644
--- a/build/server-phar.php
+++ b/build/server-phar.php
@@ -129,7 +129,7 @@ function buildPhar(string $pharPath, string $basePath, array $includedPaths, arr
}
function main() : void{
- if(ini_get("phar.readonly") == 1){
+ if(ini_get("phar.readonly") === "1"){
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
exit(1);
}
diff --git a/changelogs/5.22.md b/changelogs/5.22.md
new file mode 100644
index 000000000..6c9ba6d7e
--- /dev/null
+++ b/changelogs/5.22.md
@@ -0,0 +1,16 @@
+# 5.22.0
+Released 4th December 2024.
+
+**For Minecraft: Bedrock Edition 1.21.50**
+
+This is a support release for Minecraft: Bedrock Edition 1.21.50.
+
+**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.50.
+- Removed support for earlier versions.
diff --git a/changelogs/5.23.md b/changelogs/5.23.md
new file mode 100644
index 000000000..3a287608f
--- /dev/null
+++ b/changelogs/5.23.md
@@ -0,0 +1,139 @@
+# 5.23.0
+Released 5th December 2024.
+
+This is a minor feature release, including new gameplay features, internals improvements, API additions and
+deprecations, and improvements to timings.
+
+**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
+- `/timings` now supports collecting timings from async task workers. These new timings will be shown alongside `Full Server Tick` timings, but will not be counted in total load.
+- Added `/xp` command.
+- `start.sh` will now emit warnings when the server process exits with an unusual exit code. This helps to detect unexpected segmentation faults and other kinds of native errors.
+
+## Gameplay
+- Added the following new items:
+ - End Crystal
+ - Goat Horn (all variants)
+ - Ice Bomb (from Education Edition)
+ - Recovery Compass
+- Added the following enchantments:
+ - Frost Walker
+- Sugarcane now self-destructs when there is no water adjacent to the base block.
+- Added basic support for middle-clicking on entities to get their spawn eggs.
+- Added sounds when drinking potions.
+- Eating food is now allowed in creative mode and in peaceful difficulty.
+
+## API
+### `pocketmine\block`
+- Extracted `MultiAnyFacingTrait` and `MultiAnySupportTrait` from `GlowLichen` to enable reuse in other blocks.
+- The following API methods have been deprecated:
+ - `Campfire->getInventory()` - this was added by mistake and can't be well-supported given the way that blocks work
+
+### `pocketmine\command`
+- The following classes have been added:
+ - `ClosureCommand` - allows registering a closure to execute a command
+
+### `pocketmine\event`
+- Added APIs to `PlayerInteractEvent` to allow toggling item and block interactions.
+ - This allows various customisations, such as allowing interactions when sneaking, selectively disabling item or block reactions, etc.
+ - If both item and block interactions are disabled, the event is **not** cancelled (blocks can still be placed).
+ - The following API methods have been added:
+ - `public PlayerInteractEvent->setUseBlock(bool $useBlock) : void`
+ - `public PlayerInteractEvent->setUseItem(bool $useItem) : void`
+ - `public PlayerInteractEvent->useBlock() : bool` - returns whether the block can respond to the interaction (toggling levers, opening/closing doors, etc).
+ - `public PlayerInteractEvent->useItem() : bool` - returns whether the item can respond to the interaction (spawn eggs, flint & steel, etc).
+- The following new classes have been added:
+ - `player\PlayerEntityPickEvent` - called when a player middle-clicks on an entity
+
+### `pocketmine\inventory\transaction`
+- The following API methods have been deprecated:
+ - `InventoryAction->onAddToTransaction()`
+
+### `pocketmine\permission`
+- The following API methods have been deprecated:
+ - `PermissionManager->getPermissionSubscriptions()`
+ - `PermissionManager->subscribeToPermission()`
+ - `PermissionManager->unsubscribeFromAllPermissions()`
+ - `PermissionManager->unsubscribeFromPermission()`
+
+### `pocketmine\plugin`
+- The following classes have been deprecated:
+ - `DiskResourceProvider`
+ - `ResourceProvider`
+
+### `pocketmine\promise`
+- `Promise::all()` now accepts zero promises. This will return an already-resolved promise with an empty array.
+
+### `pocketmine\scheduler`
+- Added PHPStan generic types to `TaskHandler` and related APIs in `TaskScheduler` and `Task`.
+- The following API methods have been deprecated
+ - `AsyncTask->publishProgress()`
+ - `AsyncTask->onProgressUpdate()`
+
+### `pocketmine\timings`
+- Timings can now notify other code when timings are enabled/disabled, reloaded, or collected.
+ - The intent of this is to facilitate timings usage on other threads, and have the results collected into a single timings report.
+ - Timings cannot directly control timings on other threads, so these callbacks allow plugins to use custom mechanisms to toggle, reset and collect timings.
+ - PocketMine-MP currently uses this to collect timings from async task workers. More internal threads may be supported in the future.
+- The following API methods have been added:
+ - `public static TimingsHandler::getCollectCallbacks() : ObjectSet<\Closure() : list>>` - callbacks for (asynchronously) collecting timings (typically from other threads). The returned promises should be resolved with the result of `TimingsHandler::printCurrentThreadRecords()`.
+ - `public static TimingsHandler::getReloadCallbacks() : ObjectSet<\Closure() : void>` - callbacks called when timings are reset
+ - `public static TimingsHandler::getToggleCallbacks() : ObjectSet<\Closure(bool $enable) : void>` - callbacks called when timings are enabled/disabled
+ - `public static TimingsHandler::requestPrintTimings() : Promise>` - asynchronously collects timing results from all threads and assembles them into a single report
+- The following API methods have been deprecated:
+ - `TimingsHandler::printTimings()` - this function cannot support async timings collection. Use `TimingsHandler::requestPrintTimings()` instead.
+ - `Timings::getAsyncTaskErrorTimings()` - internal method that is no longer needed
+- The following constants have been deprecated:
+ - `Timings::GROUP_BREAKDOWN` - no longer used
+
+### `pocketmine\utils`
+- The following API methods have been added:
+ - `public static Utils::getRandomFloat() : float` - returns a random float between 0 and 1. Drop-in replacement for `lcg_value()` in PHP 8.4.
+
+## Internals
+- Blocks are now always synced with the client during a right-click-block interaction. This clears mispredictions on the client in case the new `PlayerInteractEvent` flags were customized by plugins.
+- `VanillaBlocks` and `VanillaItems` now use reflection to lookup TypeId constants by registration name, instead of requiring TypeIds to be manually specified.
+ - While this is obviously a hack, it prevents incorrect constants from being used when adding new blocks, and guarantees that the names of constants in `BlockTypeIds` and `ItemTypeIds` will match their corresponding entries in `VanillaBlocks` and `VanillaItems` respectively.
+ - It also significantly improves readability of `VanillaBlocks` and `VanillaItems`, as well as eliminating ugly code like `WoodLikeBlockIdHelper`.
+ - In PM6, the team is exploring options to redesign `VanillaBlocks` and `VanillaItems` to eliminate the need for defining separate TypeIds entirely.
+- `ConsoleReader` now uses socket support in `proc_open()` to transmit IPC messages to the server process. Previously, a temporary socket server was used, which was unreliable in some conditions.
+- Event handler tests have been converted to PHPUnit tests by mocking `Server` and `Plugin` instances. Previously, these required integration tests for these dependencies.
+- Fixed various deprecation warnings in PHP 8.4.
+- `netresearch/jsonmapper` is now used at `5.0.0`. The PMMP fork of this library has been removed, as it is no longer needed.
+
+# 5.23.1
+Released 5th December 2024.
+
+## Fixes
+- Fixed signs not creating a tile when placed.
+
+## Internals
+- Improved blockstate consistency check to detect tiles disappearing during refactors.
+
+# 5.23.2
+Released 9th December 2024.
+
+## General
+- Updated translations for Russian and Korean.
+
+## Fixes
+- Fixed server build number.
+- Fixed some crashes being misreported as plugin-involved.
+
+## Internals
+- Removed legacy `build/make-release.php` script. This script is no longer used, as all releases should now follow the PR workflow.
+
+# 5.23.3
+Released 22nd January 2025.
+
+## Fixes
+- Fixed crashes with PHP internal stack frames being flagged as plugin crashes.
+- Fixed note block instrument sounds in 1.21.50.
+
+## Internals
+- Updated GitHub issue templates to use issue forms.
diff --git a/changelogs/5.24.md b/changelogs/5.24.md
new file mode 100644
index 000000000..a159d0e76
--- /dev/null
+++ b/changelogs/5.24.md
@@ -0,0 +1,108 @@
+# 5.24.0
+Released 22nd January 2025.
+
+This is a minor feature release, including new gameplay features, performance improvements, and minor API additions.
+
+**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
+- PHP garbage collection is now managed by the server, instead of being automatically triggered by PHP.
+ - The mechanism for GC triggering is designed to mimic PHP's to avoid behavioural changes. Only the place it's triggered from should be significantly different.
+ - This change also avoids unnecessary GCs during object-heavy operations, such as encoding `CraftingDataPacket`. As such, performance during server join should see an improvement.
+ - Timings is now able to directly measure the impact of GC. Previously, GC would show up as random spikes under random timers, skewing timing results.
+- `ChunkCache` now uses `string` for completed caches directly instead of keeping them wrapped in `CompressBatchPromise`s. This reduces memory usage, improves performance, and reduces GC workload.
+
+## Configuration
+- The following settings have been removed from `pocketmine.yml` and will no longer have any effect:
+ - `memory.garbage-collection.collect-async-worker` (now always `true`)
+ - `memory.garbage-collection.low-memory-trigger` (now always `true`)
+ - `memory.max-chunks.trigger-chunk-collect` (now always `true`)
+ - `memory.world-caches.disable-chunk-cache` (now always `true`)
+ - `memory.world-caches.low-memory-trigger` (now always `true`)
+
+## Gameplay
+- Added the following new blocks:
+ - All types of pale oak wood, and leaves
+ - Resin
+ - Resin Bricks, Slabs, Stairs, and Walls
+ - Resin Clump
+ - Chiseled Resin Bricks
+- Some blocks have had their tool tier requirements adjusted to match latest Bedrock updates.
+- Added the following new items:
+ - Resin Brick
+ - Music Disc - Creator
+ - Music Disc - Creator (Music Box)
+ - Music Disc - Precipice
+ - Music Disc - Relic
+
+## API
+### General
+- Many places had their PHPDoc improved to address issues highlighted by PHPStan 2.x. This may cause new, previously unreported issues to be reported in plugins using PHPStan.
+
+### `pocketmine`
+- The following methods have been deprecated:
+ - `MemoryManager->canUseChunkCache()`
+ - `MemoryManager::dumpMemory()` - relocated to `MemoryDump` class
+
+### `pocketmine\item`
+- The following new enum cases have been added:
+ - `RecordType::DISK_CREATOR`
+ - `RecordType::DISK_CREATOR_MUSIC_BOX`
+ - `RecordType::DISK_PRECIPICE`
+ - `RecordType::DISK_RELIC`
+
+### `pocketmine\player`
+- The following new methods have been added:
+ - `public Player->getFlightSpeedMultiplier() : float` - a base multiplier for player's flight speed
+ - `public Player->setFlightSpeedMultiplier(float $flightSpeedMultiplier) : void` - sets the player's flight speed multiplier
+- The following new constants have been added:
+ - `Player::DEFAULT_FLIGHT_SPEED_MULTIPLIER`
+
+### `pocketmine\utils`
+- The following new methods have been added:
+ - `public static TextFormat::javaToBedrock(string $string) : string` - removes unsupported Java Edition format codes to prevent them being incorrectly displayed on Bedrock
+- The following methods have behavioural changes:
+ - `TextFormat::toHTML()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline). This is because the codes previously used for `STRIKETHROUGH` and `UNDERLINE` conflict with the new material codes introduced by Minecraft Bedrock.
+ - `Terminal::toANSI()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline), as above. However, underline and strikethrough can still be used on the terminal using `Terminal::$FORMAT_UNDERLINE` and `Terminal::$FORMAT_STRIKETHROUGH` respectively.
+- The following new constants have been added:
+ - `TextFormat::MATERIAL_QUARTZ`
+ - `TextFormat::MATERIAL_IRON`
+ - `TextFormat::MATERIAL_NETHERITE`
+ - `TextFormat::MATERIAL_REDSTONE`
+ - `TextFormat::MATERIAL_COPPER`
+ - `TextFormat::MATERIAL_GOLD`
+ - `TextFormat::MATERIAL_EMERALD`
+ - `TextFormat::MATERIAL_DIAMOND`
+ - `TextFormat::MATERIAL_LAPIS`
+ - `TextFormat::MATERIAL_AMETHYST`
+- The following constants have been deprecated:
+ - `TextFormat::STRIKETHROUGH`
+ - `TextFormat::UNDERLINE`
+- The following static properties have been added:
+ - `Terminal::$COLOR_MATERIAL_QUARTZ`
+ - `Terminal::$COLOR_MATERIAL_IRON`
+ - `Terminal::$COLOR_MATERIAL_NETHERITE`
+ - `Terminal::$COLOR_MATERIAL_REDSTONE`
+ - `Terminal::$COLOR_MATERIAL_COPPER`
+ - `Terminal::$COLOR_MATERIAL_GOLD`
+ - `Terminal::$COLOR_MATERIAL_EMERALD`
+ - `Terminal::$COLOR_MATERIAL_DIAMOND`
+ - `Terminal::$COLOR_MATERIAL_LAPIS`
+ - `Terminal::$COLOR_MATERIAL_AMETHYST`
+
+## Tools
+- Fixed some UI issues in `tools/convert-world.php`
+
+## Internals
+- Block cache in `World` is now size-limited. This prevents memory exhaustion when plugins call `getBlock()` many thousands of times with cache misses.
+- `RakLibServer` now disables PHP GC. As RakLib doesn't generate any unmanaged cycles, GC is just a waste of CPU time in this context.
+- `MemoryManager` now has the responsibility for triggering cycle GC. It's checked every tick, but GC won't take place unless the GC threshold is exceeded, similar to PHP.
+ - This mechanism could probably do with alterations to better suit PocketMine-MP, but it was chosen to mimic PHP's own GC to minimize behavioural changes for now.
+- `AsyncTask` now triggers cycle GC after `onRun()` completes. As with `MemoryManager`, this is based on a threshold designed to mimic PHP's own behaviour.
+- `FormatConverter` now performs world provider GC periodically. This is not needed with current active providers, but was found to be a problem while developing custom providers.
+- Various internal adjustments were made to avoid returning incorrectly-keyed arrays in the code. These changes shouldn't affect anything as the arrays should have been properly ordered anyway.
+- Many places that previously used `==` and `!=` have been updated to use strict variants. This kind of change needs to be done carefully to avoid breaking `int|float` comparisons.
diff --git a/changelogs/5.25.md b/changelogs/5.25.md
new file mode 100644
index 000000000..39862cbb7
--- /dev/null
+++ b/changelogs/5.25.md
@@ -0,0 +1,52 @@
+# 5.25.0
+Released 16th February 2025.
+
+This is a support release for Minecraft: Bedrock Edition 1.21.60. It also includes some minor API additions supporting new features in this version.
+
+**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.60.
+- Removed support for earlier versions.
+
+## Documentation
+- Fixed the documentation of `Utils::getOS()`. It now refers to the `Utils::OS_*` constants instead of a list of hardcoded strings.
+
+## API
+### `pocketmine\inventory`
+This release allows plugins to decide which creative tab they want to add their new items to.
+It also allows creating new collapsible groups of items, and modifying or removing existing ones.
+
+- The following new methods have been added:
+ - `public CreativeInventory->getAllEntries() : list` - returns an array of objects, each containing a creative item and information about its category and collapsible group (if applicable).
+ - `public CreativeInventory->getEntry(int $index) : ?CreativeInventoryEntry` - returns the creative item with the specified identifier, or `null` if not found
+- The following methods have signature changes:
+ - `CreativeInventory->add()` now accepts two additional optional parameters: `CreativeCategory $category, ?CreativeGroup $group`. If not specified, the item will be added to the Items tab without a group.
+- The following new classes have been added:
+ - `CreativeCategory` - enum of possible creative inventory categories (each has its own tab on the GUI)
+ - `CreativeGroup` - contains information about a collapsible group of creative items, including tooltip text and icon
+ - `CreativeInventoryEntry` - contains information about a creative inventory item, including its category and collapsible group (if applicable)
+
+## Internals
+- `CreativeContentPacket` is no longer fully cached due to the requirement for translation context during construction. The individual items are still cached (which is the most expensive part), but the packet itself is now constructed on demand, and group entries are constructed on the fly. This may affect performance, but this has not been investigated.
+- `BedrockDataFiles` now includes constants for folders at the top level of `BedrockData` as well as files.
+- The structure of creative data in `BedrockData` was changed to accommodate item category and grouping information. `creativeitems.json` has been replaced by `creative/*.json`, which contain information about item grouping and also segregates item lists per category.
+- New information was added to `required_item_list.json` in `BedrockData`, as the server is now required to send item component NBT data in some cases.
+
+# 5.25.1
+Released 26th February 2025.
+
+## Fixes
+- Fixed confusing exception message when a block-breaking tool has an efficiency value of zero.
+- Fixed incorrect facing of doors since 1.21.60 (resulted in mismatched AABBs between client & server, rendering glitches etc.)
+- Resource pack UUIDs are now validated on load. Previously, invalid UUIDs would be accepted, and potentially cause a server crash on player join.
+
+# 5.25.2
+Released 4th March 2025.
+
+## Fixes
+- Added limits to various `explode()` calls.
diff --git a/changelogs/5.26.md b/changelogs/5.26.md
new file mode 100644
index 000000000..f8adc969f
--- /dev/null
+++ b/changelogs/5.26.md
@@ -0,0 +1,71 @@
+# 5.26.0
+Released 22nd March 2025.
+
+This is a minor feature release focused on performance 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.
+
+## Performance
+- Significantly improved performance of entity movement. Load testing with item entities showed a 3x increase in the number of entities supported without lag.
+- Significantly improved performance of on-ground checks for player movement. This still needs further work, but optimisations implemented in this version should improve performance substantially.
+- Updated `pocketmine/nbt` dependency with performance improvements to `TAG_Compound` and `TAG_List` comparison. This should improve performance of inventory-related actions.
+- `InventoryTransaction` now avoids useless item clones when processing transactions, which should improve performance of inventory-related actions.
+
+## Dependencies
+- `pocketmine/bedrock-protocol` has been updated to `36.2.0`, which adds new functions to access some packet fields.
+- `pocketmine/nbt` has been updated to `1.1.0`, which improves performance when comparing NBT object trees.
+
+## Gameplay
+- Block breaking animation speed now takes into account the following: jumping, being in water, haste, mining fatigue
+
+## Tools
+- `blockstate-upgrade-schema-utils.php` now has a new `dump-table` command, which turns a `.bin` palette table file into human-readable text for debugging.
+
+## API
+### `pocketmine\block`
+- The following methods have been added:
+ - `public RuntimeBlockStateRegistry->hasStateId(int $stateId) : bool` - checks whether the given state ID is registered
+
+### `pocketmine\crafting`
+- The following methods have been deprecated:
+ - `CraftingManager::sort()` - this was implicitly internal anyway
+
+### `pocketmine\utils`
+- The following constants have been added:
+ - `TextFormat::MATERIAL_RESIN`
+- The following static properties have been added:
+ - `Terminal::$COLOR_MATERIAL_RESIN`
+
+### `pocketmine\data\bedrock\block`
+- `BlockStateToObjectDeserializer` now permits overriding **deserializers** for Bedrock IDs. This may be useful to implement custom state handling, or to implement missing block variants (such as snow cauldron).
+ - This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
+ - Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
+ - If you want to make a custom version of a vanilla block, create a custom type ID for it, exactly as you would for a regular custom block.
+- The following methods have been added:
+ - `public BlockStateToObjectDeserializer->getDeserializerForId(string $id) : ?(\Closure(BlockStateReader) : Block)`
+
+### `pocketmine\data\bedrock\item`
+- `ItemDeserializer` now permits overriding **deserializers** for Bedrock IDs. As above, this may be useful to implement custom data handling, or to implement missing variants of existing items.
+ - This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
+ - Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
+ - As above, if you want to make a custom version of a vanilla item, create a custom type ID for it, exactly as you would for a regular custom item.
+- The following methods have been added:
+ - `public ItemDeserializer->getDeserializerForId(string $id) : ?(\Closure(SavedItemData) : Item)`
+
+## Internals
+- `new $class` is now banned on new internals code by a PHPStan rule. Closures or factory objects should be used instead for greater flexibility and better static analysis.
+- `CraftingManager` now uses a more stable hash function for recipe output filtering.
+- `ChunkCache` now accepts `int $dimensionId` in the constructor. This may be useful for plugins which implement the nether.
+- `RuntimeBlockStateRegistry` now precomputes basic collision info about known states for fast paths.
+ - This permits specialization for common shapes like cubes and collisionless blocks, which allows skipping complex logic in entity movement calculation. This vastly improves performance.
+ - Any block whose class overrides `readStateFromWorld()` or `getModelPositionOffset()` will *not* be optimised.
+ - `Block->recalculateCollisionBoxes()` now has a hard requirement not to depend on anything other than available properties. It must not use `World` or its position.
+ - This change was problematic for `ChorusPlant`, which used nearby blocks to calculate its collision boxes.
+ - Blocks which need nearby blocks should override `readStateFromWorld()` and set dynamic state properties, similar to fences.
+ - This design flaw will be corrected with a major change to `Block` internals currently in planning for a future major version.
+- `Block->getCollisionBoxes()` may not be called at all during gameplay for blocks with shapes determined to be simple, like cubes and collisionless blocks.
+- `BlockStateToObjectDeserializer` now checks if the returned blockstate is registered in `RuntimeBlockStateRegistry` to promote earlier error detection (instead of crashing in random code paths).
diff --git a/changelogs/5.27.md b/changelogs/5.27.md
new file mode 100644
index 000000000..07bc8c26e
--- /dev/null
+++ b/changelogs/5.27.md
@@ -0,0 +1,24 @@
+# 5.27.0
+Released 27th March 2025.
+
+This is a support release for Minecraft: Bedrock Edition 1.21.70.
+
+**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.
+
+## Interim releases
+If you're upgrading from 5.25.x directly to 5.27.0, please also read the following changelogs, as the interim releases contain important changes:
+- [5.26.0](https://github.com/pmmp/PocketMine-MP/blob/5.26.0/changelogs/5.26.md#5260) - Performance improvements and other internal improvements
+
+## General
+- Aded support for Minecraft: Bedrock Edition 1.21.70.
+- Removed support for earlier versions.
+
+# 5.27.1
+Released 6th April 2025.
+
+## Fixes
+- Updated RakLib to get ping timestamp handling fixes.
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..65ff6620d
--- /dev/null
+++ b/changelogs/5.33.md
@@ -0,0 +1,147 @@
+# 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.
+
+# 5.33.1
+Released 31st August 2025.
+
+## Fixes
+- Fixed banners placed in prior versions getting their tiles deleted (due to missing `Type` tags).
+
+# 5.33.2
+Released 16th September 2025.
+
+## General
+- PHP 8.4 has now been added to the test matrix.
+
+## Fixes
+- Fixed PHP 8.4 deprecation notice in `AsyncGeneratorExecutor`.
+- Fixed inventory windows breaking if a window is sent while the player has the chat window open (e.g. quickly pressing E followed by / could trigger this).
+- Fixed `BlockBreakEvent` being called twice in creative if cancelled.
+- Reduced block lag when towering and other situations where the placed block might intersect with the player's AABB. Block lag may still appear on higher latency clients - this is still being worked on.
diff --git a/composer.json b/composer.json
index 16bed54b7..9451fd6eb 100644
--- a/composer.json
+++ b/composer.json
@@ -12,6 +12,7 @@
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
+ "ext-encoding": "~1.0.0",
"ext-gmp": "*",
"ext-hash": "*",
"ext-igbinary": "^3.0.1",
@@ -32,31 +33,35 @@
"ext-zlib": ">=1.2.11",
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
- "pocketmine/netresearch-jsonmapper": "~v4.4.999",
- "pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40",
- "pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40",
- "pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40",
- "pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40",
+ "netresearch/jsonmapper": "~v5.0.0",
+ "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60",
+ "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": "~50.0.0+bedrock-1.21.100",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.7.0",
- "pocketmine/locale-data": "~2.22.0",
+ "pocketmine/locale-data": "~2.25.0",
"pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0",
- "pocketmine/nbt": "~1.0.0",
- "pocketmine/raklib": "~1.1.0",
+ "pocketmine/nbt": "~1.2.0",
+ "pocketmine/raklib": "~1.2.0",
"pocketmine/raklib-ipc": "~1.0.0",
"pocketmine/snooze": "^0.5.0",
- "ramsey/uuid": "~4.7.0",
+ "ramsey/uuid": "~4.9.0",
"symfony/filesystem": "~6.4.0"
},
"require-dev": {
- "phpstan/phpstan": "1.11.11",
- "phpstan/phpstan-phpunit": "^1.1.0",
- "phpstan/phpstan-strict-rules": "^1.2.0",
+ "phpstan/phpstan": "2.1.25",
+ "phpstan/phpstan-phpunit": "^2.0.0",
+ "phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^10.5.24"
},
+ "replace": {
+ "symfony/polyfill-ctype": "*",
+ "symfony/polyfill-mbstring": "*"
+ },
"autoload": {
"psr-4": {
"pocketmine\\": "src/"
diff --git a/composer.lock b/composer.lock
index 7eda66b35..ce923690e 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": "c57e8f52250edfd03906219fe14fc240",
+ "content-hash": "0d71d3fba23ba8c4734cac59b9e10129",
"packages": [
{
"name": "adhocore/json-comment",
@@ -67,16 +67,16 @@
},
{
"name": "brick/math",
- "version": "0.12.1",
+ "version": "0.13.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
- "reference": "f510c0a40911935b77b86859eb5223d58d660df1"
+ "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
- "reference": "f510c0a40911935b77b86859eb5223d58d660df1",
+ "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
+ "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
"shasum": ""
},
"require": {
@@ -85,7 +85,7 @@
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.1",
- "vimeo/psalm": "5.16.0"
+ "vimeo/psalm": "6.8.8"
},
"type": "library",
"autoload": {
@@ -115,7 +115,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
- "source": "https://github.com/brick/math/tree/0.12.1"
+ "source": "https://github.com/brick/math/tree/0.13.1"
},
"funding": [
{
@@ -123,20 +123,71 @@
"type": "github"
}
],
- "time": "2023-11-29T23:19:16+00:00"
+ "time": "2025-03-29T13:50:30+00:00"
},
{
- "name": "pocketmine/bedrock-block-upgrade-schema",
- "version": "5.0.0",
+ "name": "netresearch/jsonmapper",
+ "version": "v5.0.0",
"source": {
"type": "git",
- "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
- "reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52"
+ "url": "https://github.com/cweiske/jsonmapper.git",
+ "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
- "reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
+ "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-spl": "*",
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
+ "squizlabs/php_codesniffer": "~3.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "JsonMapper": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "OSL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Christian Weiske",
+ "email": "cweiske@cweiske.de",
+ "homepage": "http://github.com/cweiske/jsonmapper/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Map nested JSON structures onto PHP classes",
+ "support": {
+ "email": "cweiske@cweiske.de",
+ "issues": "https://github.com/cweiske/jsonmapper/issues",
+ "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0"
+ },
+ "time": "2024-09-08T10:20:00+00:00"
+ },
+ {
+ "name": "pocketmine/bedrock-block-upgrade-schema",
+ "version": "5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
+ "reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/2218512e4b91f5bfd09ef55f7a4c4b04e169e41a",
+ "reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a",
"shasum": ""
},
"type": "library",
@@ -147,22 +198,22 @@
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
- "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0"
+ "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.1.0"
},
- "time": "2024-11-03T14:13:50+00:00"
+ "time": "2025-02-11T17:41:44+00:00"
},
{
"name": "pocketmine/bedrock-data",
- "version": "2.14.1+bedrock-1.21.40",
+ "version": "6.0.0+bedrock-1.21.100",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
- "reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4"
+ "reference": "edc0d829175e5e1e57c87001acfd03526c63fd34"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/4a41864ed09613ecec6791e2ae076a8ec7089cc4",
- "reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4",
+ "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/edc0d829175e5e1e57c87001acfd03526c63fd34",
+ "reference": "edc0d829175e5e1e57c87001acfd03526c63fd34",
"shasum": ""
},
"type": "library",
@@ -173,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/2.14.1+bedrock-1.21.40"
+ "source": "https://github.com/pmmp/BedrockData/tree/6.0.0+bedrock-1.21.100"
},
- "time": "2024-11-12T21:36:20+00:00"
+ "time": "2025-08-30T17:25:42+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
- "version": "1.13.1",
+ "version": "1.15.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
- "reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03"
+ "reference": "09e0dbe9743f21a76b1fe04b2b4136785775f52b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1cf81305f2ffcf7dde9577c4f16a55c765192b03",
- "reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03",
+ "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/09e0dbe9743f21a76b1fe04b2b4136785775f52b",
+ "reference": "09e0dbe9743f21a76b1fe04b2b4136785775f52b",
"shasum": ""
},
"type": "library",
@@ -199,25 +250,26 @@
"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.13.1"
+ "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.15.0"
},
- "time": "2024-11-12T21:33:17+00:00"
+ "time": "2025-08-06T15:08:48+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
- "version": "35.0.0+bedrock-1.21.40",
+ "version": "50.0.0+bedrock-1.21.100",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
- "reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459"
+ "reference": "2d7aa27a5537ae593fb1c39158648ea462fef72a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
- "reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459",
+ "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2d7aa27a5537ae593fb1c39158648ea462fef72a",
+ "reference": "2d7aa27a5537ae593fb1c39158648ea462fef72a",
"shasum": ""
},
"require": {
+ "ext-encoding": "~1.0.0",
"ext-json": "*",
"php": "^8.1",
"pocketmine/binaryutils": "^0.2.0",
@@ -227,10 +279,10 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
- "phpstan/phpstan": "1.11.9",
- "phpstan/phpstan-phpunit": "^1.0.0",
- "phpstan/phpstan-strict-rules": "^1.0.0",
- "phpunit/phpunit": "^9.5 || ^10.0"
+ "phpstan/phpstan": "2.1.0",
+ "phpstan/phpstan-phpunit": "^2.0.0",
+ "phpstan/phpstan-strict-rules": "^2.0.0",
+ "phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
},
"type": "library",
"autoload": {
@@ -245,9 +297,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/35.0.0+bedrock-1.21.40"
+ "source": "https://github.com/pmmp/BedrockProtocol/tree/50.0.0+bedrock-1.21.100"
},
- "time": "2024-10-24T15:45:43+00:00"
+ "time": "2025-09-20T23:09:19+00:00"
},
{
"name": "pocketmine/binaryutils",
@@ -420,16 +472,16 @@
},
{
"name": "pocketmine/locale-data",
- "version": "2.22.0",
+ "version": "2.25.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
- "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d"
+ "reference": "8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/Language/zipball/aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
- "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
+ "url": "https://api.github.com/repos/pmmp/Language/zipball/8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd",
+ "reference": "8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd",
"shasum": ""
},
"type": "library",
@@ -437,9 +489,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
- "source": "https://github.com/pmmp/Language/tree/2.22.0"
+ "source": "https://github.com/pmmp/Language/tree/2.25.1"
},
- "time": "2024-11-16T13:28:01+00:00"
+ "time": "2025-04-16T11:15:32+00:00"
},
{
"name": "pocketmine/log",
@@ -525,16 +577,16 @@
},
{
"name": "pocketmine/nbt",
- "version": "1.0.0",
+ "version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
- "reference": "20540271cb59e04672cb163dca73366f207974f1"
+ "reference": "51b8d6a97065fb93e0b4f660b65164b6e1ed2fff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/NBT/zipball/20540271cb59e04672cb163dca73366f207974f1",
- "reference": "20540271cb59e04672cb163dca73366f207974f1",
+ "url": "https://api.github.com/repos/pmmp/NBT/zipball/51b8d6a97065fb93e0b4f660b65164b6e1ed2fff",
+ "reference": "51b8d6a97065fb93e0b4f660b65164b6e1ed2fff",
"shasum": ""
},
"require": {
@@ -544,8 +596,9 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "1.10.25",
- "phpstan/phpstan-strict-rules": "^1.0",
+ "phpstan/phpstan": "2.1.27",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.5"
},
"type": "library",
@@ -561,76 +614,22 @@
"description": "PHP library for working with Named Binary Tags",
"support": {
"issues": "https://github.com/pmmp/NBT/issues",
- "source": "https://github.com/pmmp/NBT/tree/1.0.0"
+ "source": "https://github.com/pmmp/NBT/tree/1.2.0"
},
- "time": "2023-07-14T13:01:49+00:00"
- },
- {
- "name": "pocketmine/netresearch-jsonmapper",
- "version": "v4.4.999",
- "source": {
- "type": "git",
- "url": "https://github.com/pmmp/netresearch-jsonmapper.git",
- "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/9a6610033d56e358e86a3e4fd5f87063c7318833",
- "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833",
- "shasum": ""
- },
- "require": {
- "ext-json": "*",
- "ext-pcre": "*",
- "ext-reflection": "*",
- "ext-spl": "*",
- "php": ">=7.1"
- },
- "replace": {
- "netresearch/jsonmapper": "~4.2.0"
- },
- "require-dev": {
- "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
- "squizlabs/php_codesniffer": "~3.5"
- },
- "type": "library",
- "autoload": {
- "psr-0": {
- "JsonMapper": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "OSL-3.0"
- ],
- "authors": [
- {
- "name": "Christian Weiske",
- "email": "cweiske@cweiske.de",
- "homepage": "http://github.com/cweiske/jsonmapper/",
- "role": "Developer"
- }
- ],
- "description": "Fork of netresearch/jsonmapper with security fixes needed by pocketmine/pocketmine-mp",
- "support": {
- "email": "cweiske@cweiske.de",
- "issues": "https://github.com/cweiske/jsonmapper/issues",
- "source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.4.999"
- },
- "time": "2024-02-23T13:17:01+00:00"
+ "time": "2025-09-19T18:09:30+00:00"
},
{
"name": "pocketmine/raklib",
- "version": "1.1.1",
+ "version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
- "reference": "be2783be516bf6e2872ff5c81fb9048596617b97"
+ "reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97",
- "reference": "be2783be516bf6e2872ff5c81fb9048596617b97",
+ "url": "https://api.github.com/repos/pmmp/RakLib/zipball/a28d05216d34dbd00e8aed827a58df6b4c11510b",
+ "reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b",
"shasum": ""
},
"require": {
@@ -642,8 +641,8 @@
"pocketmine/log": "^0.3.0 || ^0.4.0"
},
"require-dev": {
- "phpstan/phpstan": "1.10.1",
- "phpstan/phpstan-strict-rules": "^1.0"
+ "phpstan/phpstan": "2.1.0",
+ "phpstan/phpstan-strict-rules": "^2.0"
},
"type": "library",
"autoload": {
@@ -658,9 +657,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.1"
+ "source": "https://github.com/pmmp/RakLib/tree/1.2.0"
},
- "time": "2024-03-04T14:02:14+00:00"
+ "time": "2025-06-08T17:36:06+00:00"
},
{
"name": "pocketmine/raklib-ipc",
@@ -745,16 +744,16 @@
},
{
"name": "ramsey/collection",
- "version": "2.0.0",
+ "version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/ramsey/collection.git",
- "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
+ "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
- "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
+ "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
+ "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
"shasum": ""
},
"require": {
@@ -762,25 +761,22 @@
},
"require-dev": {
"captainhook/plugin-composer": "^5.3",
- "ergebnis/composer-normalize": "^2.28.3",
- "fakerphp/faker": "^1.21",
+ "ergebnis/composer-normalize": "^2.45",
+ "fakerphp/faker": "^1.24",
"hamcrest/hamcrest-php": "^2.0",
- "jangregor/phpstan-prophecy": "^1.0",
- "mockery/mockery": "^1.5",
+ "jangregor/phpstan-prophecy": "^2.1",
+ "mockery/mockery": "^1.6",
"php-parallel-lint/php-console-highlighter": "^1.0",
- "php-parallel-lint/php-parallel-lint": "^1.3",
- "phpcsstandards/phpcsutils": "^1.0.0-rc1",
- "phpspec/prophecy-phpunit": "^2.0",
- "phpstan/extension-installer": "^1.2",
- "phpstan/phpstan": "^1.9",
- "phpstan/phpstan-mockery": "^1.1",
- "phpstan/phpstan-phpunit": "^1.3",
- "phpunit/phpunit": "^9.5",
- "psalm/plugin-mockery": "^1.1",
- "psalm/plugin-phpunit": "^0.18.4",
- "ramsey/coding-standard": "^2.0.3",
- "ramsey/conventional-commits": "^1.3",
- "vimeo/psalm": "^5.4"
+ "php-parallel-lint/php-parallel-lint": "^1.4",
+ "phpspec/prophecy-phpunit": "^2.3",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-mockery": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^10.5",
+ "ramsey/coding-standard": "^2.3",
+ "ramsey/conventional-commits": "^1.6",
+ "roave/security-advisories": "dev-latest"
},
"type": "library",
"extra": {
@@ -818,37 +814,26 @@
],
"support": {
"issues": "https://github.com/ramsey/collection/issues",
- "source": "https://github.com/ramsey/collection/tree/2.0.0"
+ "source": "https://github.com/ramsey/collection/tree/2.1.1"
},
- "funding": [
- {
- "url": "https://github.com/ramsey",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
- "type": "tidelift"
- }
- ],
- "time": "2022-12-31T21:50:55+00:00"
+ "time": "2025-03-22T05:38:12+00:00"
},
{
"name": "ramsey/uuid",
- "version": "4.7.6",
+ "version": "4.9.1",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
- "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
+ "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
- "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440",
+ "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440",
"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 || ^0.14",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
},
@@ -856,26 +841,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.",
@@ -910,32 +892,22 @@
],
"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.1"
},
- "funding": [
- {
- "url": "https://github.com/ramsey",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
- "type": "tidelift"
- }
- ],
- "time": "2024-04-27T21:32:50+00:00"
+ "time": "2025-09-04T20:59:21+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v6.4.13",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3"
+ "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3",
- "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/75ae2edb7cdcc0c53766c30b0a2512b8df574bd8",
+ "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8",
"shasum": ""
},
"require": {
@@ -972,7 +944,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v6.4.13"
+ "source": "https://github.com/symfony/filesystem/tree/v6.4.24"
},
"funding": [
{
@@ -984,82 +956,7 @@
"type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "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": {
- "name": "symfony/polyfill",
- "url": "https://github.com/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",
+ "url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
@@ -1067,102 +964,22 @@
"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": {
- "name": "symfony/polyfill",
- "url": "https://github.com/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": "2025-07-10T08:14:14+00:00"
}
],
"packages-dev": [
{
"name": "myclabs/deep-copy",
- "version": "1.12.1",
+ "version": "1.13.4",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
- "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"shasum": ""
},
"require": {
@@ -1201,7 +1018,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
},
"funding": [
{
@@ -1209,20 +1026,20 @@
"type": "tidelift"
}
],
- "time": "2024-11-08T17:47:46+00:00"
+ "time": "2025-08-01T08:46:24+00:00"
},
{
"name": "nikic/php-parser",
- "version": "v5.3.1",
+ "version": "v5.6.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b"
+ "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b",
- "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
+ "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
"shasum": ""
},
"require": {
@@ -1241,7 +1058,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "5.0-dev"
+ "dev-master": "5.x-dev"
}
},
"autoload": {
@@ -1265,9 +1082,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1"
},
- "time": "2024-10-08T18:51:32+00:00"
+ "time": "2025-08-13T20:13:15+00:00"
},
{
"name": "phar-io/manifest",
@@ -1389,20 +1206,20 @@
},
{
"name": "phpstan/phpstan",
- "version": "1.11.11",
+ "version": "2.1.25",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3"
+ "reference": "4087d28bd252895874e174d65e26b2c202ed893a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3",
- "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4087d28bd252895874e174d65e26b2c202ed893a",
+ "reference": "4087d28bd252895874e174d65e26b2c202ed893a",
"shasum": ""
},
"require": {
- "php": "^7.2|^8.0"
+ "php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
@@ -1443,34 +1260,35 @@
"type": "github"
}
],
- "time": "2024-08-19T14:37:29+00:00"
+ "time": "2025-09-12T14:26:42+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
- "version": "1.4.0",
+ "version": "2.0.7",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
- "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11"
+ "reference": "9a9b161baee88a5f5c58d816943cff354ff233dc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11",
- "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9a9b161baee88a5f5c58d816943cff354ff233dc",
+ "reference": "9a9b161baee88a5f5c58d816943cff354ff233dc",
"shasum": ""
},
"require": {
- "php": "^7.2 || ^8.0",
- "phpstan/phpstan": "^1.11"
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.1.18"
},
"conflict": {
"phpunit/phpunit": "<7.0"
},
"require-dev": {
- "nikic/php-parser": "^4.13.0",
+ "nikic/php-parser": "^5",
"php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-strict-rules": "^1.5.1",
- "phpunit/phpunit": "^9.5"
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6"
},
"type": "phpstan-extension",
"extra": {
@@ -1493,34 +1311,33 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
- "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0"
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.7"
},
- "time": "2024-04-20T06:39:00+00:00"
+ "time": "2025-07-13T11:31:46+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
- "version": "1.6.0",
+ "version": "2.0.6",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
- "reference": "363f921dd8441777d4fc137deb99beb486c77df1"
+ "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/363f921dd8441777d4fc137deb99beb486c77df1",
- "reference": "363f921dd8441777d4fc137deb99beb486c77df1",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094",
+ "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094",
"shasum": ""
},
"require": {
- "php": "^7.2 || ^8.0",
- "phpstan/phpstan": "^1.11"
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.0.4"
},
"require-dev": {
- "nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-deprecation-rules": "^1.1",
- "phpstan/phpstan-phpunit": "^1.0",
- "phpunit/phpunit": "^9.5"
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6"
},
"type": "phpstan-extension",
"extra": {
@@ -1542,9 +1359,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
- "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.6.0"
+ "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6"
},
- "time": "2024-04-20T06:37:51+00:00"
+ "time": "2025-07-21T12:19:29+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -1869,16 +1686,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "10.5.38",
+ "version": "10.5.55",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132"
+ "reference": "4b2d546b336876bd9562f24641b08a25335b06b6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132",
- "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4b2d546b336876bd9562f24641b08a25335b06b6",
+ "reference": "4b2d546b336876bd9562f24641b08a25335b06b6",
"shasum": ""
},
"require": {
@@ -1888,7 +1705,7 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.12.0",
+ "myclabs/deep-copy": "^1.13.4",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.1",
@@ -1899,13 +1716,13 @@
"phpunit/php-timer": "^6.0.0",
"sebastian/cli-parser": "^2.0.1",
"sebastian/code-unit": "^2.0.0",
- "sebastian/comparator": "^5.0.3",
+ "sebastian/comparator": "^5.0.4",
"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/recursion-context": "^5.0.1",
"sebastian/type": "^4.0.0",
"sebastian/version": "^4.0.1"
},
@@ -1950,7 +1767,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.38"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.55"
},
"funding": [
{
@@ -1961,12 +1778,20 @@
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
"type": "tidelift"
}
],
- "time": "2024-10-28T13:06:21+00:00"
+ "time": "2025-09-14T06:19:20+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -2138,16 +1963,16 @@
},
{
"name": "sebastian/comparator",
- "version": "5.0.3",
+ "version": "5.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e"
+ "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
- "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e",
+ "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e",
"shasum": ""
},
"require": {
@@ -2203,15 +2028,27 @@
"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/5.0.4"
},
"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/comparator",
+ "type": "tidelift"
}
],
- "time": "2024-10-18T14:56:07+00:00"
+ "time": "2025-09-07T05:25:07+00:00"
},
{
"name": "sebastian/complexity",
@@ -2714,23 +2551,23 @@
},
{
"name": "sebastian/recursion-context",
- "version": "5.0.0",
+ "version": "5.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "05909fb5bc7df4c52992396d0116aed689f93712"
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712",
- "reference": "05909fb5bc7df4c52992396d0116aed689f93712",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a",
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"require-dev": {
- "phpunit/phpunit": "^10.0"
+ "phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
@@ -2765,15 +2602,28 @@
"homepage": "https://github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
- "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0"
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
}
],
- "time": "2023-02-03T07:05:40+00:00"
+ "time": "2025-08-10T07:50:56+00:00"
},
{
"name": "sebastian/type",
@@ -2937,7 +2787,7 @@
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -2948,6 +2798,7 @@
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
+ "ext-encoding": "~1.0.0",
"ext-gmp": "*",
"ext-hash": "*",
"ext-igbinary": "^3.0.1",
@@ -2968,7 +2819,7 @@
"ext-zlib": ">=1.2.11",
"composer-runtime-api": "^2.0"
},
- "platform-dev": [],
+ "platform-dev": {},
"platform-overrides": {
"php": "8.1.0"
},
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 6e8578652..07ca545c4 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -4,6 +4,8 @@ includes:
- tests/phpstan/configs/impossible-generics.neon
- tests/phpstan/configs/php-bugs.neon
- tests/phpstan/configs/phpstan-bugs.neon
+ - tests/phpstan/configs/property-hook-sadness.neon
+ - tests/phpstan/configs/reflection-class-sadness.neon
- tests/phpstan/configs/spl-fixed-array-sucks.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
@@ -11,14 +13,17 @@ includes:
rules:
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
+ - pocketmine\phpstan\rules\DisallowDynamicNewRule
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
- - pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
+ - pocketmine\phpstan\rules\ExplodeLimitRule
+ - pocketmine\phpstan\rules\UnsafeForeachRule
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
parameters:
level: 9
checkMissingCallableSignature: true
+ rememberPossiblyImpureFunctionValues: false #risky to remember these, better for performance to avoid repeated calls anyway
treatPhpDocTypesAsCertain: false
bootstrapFiles:
- tests/phpstan/bootstrap.php
@@ -31,6 +36,7 @@ parameters:
paths:
- build
- src
+ - tests/phpstan/DummyPluginOwned.php
- tests/phpstan/rules
- tests/phpunit
- tests/plugins/TesterPlugin
@@ -44,6 +50,7 @@ parameters:
- pocketmine\DEBUG
- pocketmine\IS_DEVELOPMENT_BUILD
stubFiles:
+ - tests/phpstan/stubs/chunkutils2.stub
- tests/phpstan/stubs/JsonMapper.stub
- tests/phpstan/stubs/leveldb.stub
- tests/phpstan/stubs/pmmpthread.stub
diff --git a/resources/pocketmine.yml b/resources/pocketmine.yml
index 408b5b95b..531924973 100644
--- a/resources/pocketmine.yml
+++ b/resources/pocketmine.yml
@@ -54,12 +54,6 @@ memory:
#This only affects the main thread. Other threads should fire their own collections
period: 36000
- #Fire asynchronous tasks to collect garbage from workers
- collect-async-worker: true
-
- #Trigger on low memory
- low-memory-trigger: true
-
#Settings controlling memory dump handling.
memory-dump:
#Dump memory from async workers as well as the main thread. If you have issues with segfaults when dumping memory, disable this setting.
@@ -69,16 +63,6 @@ memory:
#Cap maximum render distance per player when low memory is triggered. Set to 0 to disable cap.
chunk-radius: 4
- #Do chunk garbage collection on trigger
- trigger-chunk-collect: true
-
- world-caches:
- #Disallow adding to world chunk-packet caches when memory is low
- disable-chunk-cache: true
- #Clear world caches when memory is low
- low-memory-trigger: true
-
-
network:
#Threshold for batching packets, in bytes. Only these packets will be compressed
#Set to 0 to compress everything, -1 to disable.
diff --git a/src/GarbageCollectorManager.php b/src/GarbageCollectorManager.php
new file mode 100644
index 000000000..294403a05
--- /dev/null
+++ b/src/GarbageCollectorManager.php
@@ -0,0 +1,114 @@
+logger = new \PrefixedLogger($logger, "Cyclic Garbage Collector");
+ $this->timings = new TimingsHandler("Cyclic Garbage Collector", $parentTimings);
+ }
+
+ private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{
+ //TODO Very simple heuristic for dynamic GC buffer resizing:
+ //If there are "too few" collections, increase the collection threshold
+ //by a fixed step
+ //Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14
+ if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->threshold){
+ $this->threshold = min(self::GC_THRESHOLD_MAX, $this->threshold + self::GC_THRESHOLD_STEP);
+ }elseif($this->threshold > self::GC_THRESHOLD_DEFAULT){
+ $this->threshold = max(self::GC_THRESHOLD_DEFAULT, $this->threshold - self::GC_THRESHOLD_STEP);
+ }
+ }
+
+ public function getThreshold() : int{ return $this->threshold; }
+
+ public function getCollectionTimeTotalNs() : int{ return $this->collectionTimeTotalNs; }
+
+ public function maybeCollectCycles() : int{
+ $rootsBefore = gc_status()["roots"];
+ if($rootsBefore < $this->threshold){
+ return 0;
+ }
+
+ $this->timings->startTiming();
+
+ $start = hrtime(true);
+ $cycles = gc_collect_cycles();
+ $end = hrtime(true);
+
+ $rootsAfter = gc_status()["roots"];
+ $this->adjustGcThreshold($cycles, $rootsAfter);
+
+ $this->timings->stopTiming();
+
+ $time = $end - $start;
+ $this->collectionTimeTotalNs += $time;
+ $this->runs++;
+ $this->logger->info(sprintf(
+ "Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms",
+ $this->runs,
+ number_format($time / 1_000_000, 2),
+ $rootsBefore,
+ $rootsAfter,
+ $cycles,
+ number_format($this->collectionTimeTotalNs / 1_000_000, 2)
+ ));
+
+ return $cycles;
+ }
+}
diff --git a/src/MemoryDump.php b/src/MemoryDump.php
new file mode 100644
index 000000000..bd1e0fc9a
--- /dev/null
+++ b/src/MemoryDump.php
@@ -0,0 +1,305 @@
+getProperties() as $property){
+ if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){
+ continue;
+ }
+
+ if(!$property->isInitialized()){
+ continue;
+ }
+
+ $staticCount++;
+ $staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+ }
+
+ if(count($staticProperties[$className]) === 0){
+ unset($staticProperties[$className]);
+ }
+
+ foreach($reflection->getMethods() as $method){
+ if($method->getDeclaringClass()->getName() !== $reflection->getName()){
+ continue;
+ }
+ $methodStatics = [];
+ foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){
+ $methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+ }
+ if(count($methodStatics) > 0){
+ $functionStaticVars[$className . "::" . $method->getName()] = $methodStatics;
+ $functionStaticVarsCount += count($functionStaticVars);
+ }
+ }
+ }
+
+ file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
+ $logger->info("Wrote $staticCount static properties");
+
+ $globalVariables = [];
+ $globalCount = 0;
+
+ $ignoredGlobals = [
+ 'GLOBALS' => true,
+ '_SERVER' => true,
+ '_REQUEST' => true,
+ '_POST' => true,
+ '_GET' => true,
+ '_FILES' => true,
+ '_ENV' => true,
+ '_COOKIE' => true,
+ '_SESSION' => true
+ ];
+
+ foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){
+ if(isset($ignoredGlobals[$varName])){
+ continue;
+ }
+
+ $globalCount++;
+ $globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+ }
+
+ file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
+ $logger->info("Wrote $globalCount global variables");
+
+ foreach(get_defined_functions()["user"] as $function){
+ $reflect = new \ReflectionFunction($function);
+
+ $vars = [];
+ foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){
+ $vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+ }
+ if(count($vars) > 0){
+ $functionStaticVars[$function] = $vars;
+ $functionStaticVarsCount += count($vars);
+ }
+ }
+ file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
+ $logger->info("Wrote $functionStaticVarsCount function static variables");
+
+ $data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+
+ do{
+ $continue = false;
+ foreach(Utils::stringifyKeys($objects) as $hash => $object){
+ if(!is_object($object)){
+ continue;
+ }
+ $continue = true;
+
+ $className = get_class($object);
+ if(!isset($instanceCounts[$className])){
+ $instanceCounts[$className] = 1;
+ }else{
+ $instanceCounts[$className]++;
+ }
+
+ $objects[$hash] = true;
+ $info = [
+ "information" => "$hash@$className",
+ ];
+ if($object instanceof \Closure){
+ $info["definition"] = Utils::getNiceClosureName($object);
+ $info["referencedVars"] = [];
+ $reflect = new \ReflectionFunction($object);
+ if(($closureThis = $reflect->getClosureThis()) !== null){
+ $info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+ }
+
+ foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){
+ $info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+ }
+ }else{
+ $reflection = new \ReflectionObject($object);
+
+ $info["properties"] = [];
+
+ for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){
+ foreach($reflection->getProperties() as $property){
+ if($property->isStatic()){
+ continue;
+ }
+
+ $name = $property->getName();
+ if($reflection !== $original){
+ if($property->isPrivate()){
+ $name = $reflection->getName() . ":" . $name;
+ }else{
+ continue;
+ }
+ }
+ if(!$property->isInitialized($object)){
+ continue;
+ }
+
+ $info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
+ }
+ }
+ }
+
+ fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n");
+ }
+
+ }while($continue);
+
+ $logger->info("Wrote " . count($objects) . " objects");
+
+ fclose($obData);
+
+ file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
+ file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
+
+ arsort($instanceCounts, SORT_NUMERIC);
+ file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
+
+ $logger->info("Finished!");
+
+ ini_set('memory_limit', $hardLimit);
+ if($gcEnabled){
+ gc_enable();
+ }
+ }
+
+ /**
+ * @param object[]|true[] $objects reference parameter
+ * @param int[] $refCounts reference parameter
+ *
+ * @phpstan-param array $objects
+ * @phpstan-param array $refCounts
+ * @phpstan-param-out array $objects
+ * @phpstan-param-out array $refCounts
+ */
+ private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{
+ if($maxNesting <= 0){
+ return "(error) NESTING LIMIT REACHED";
+ }
+
+ --$maxNesting;
+
+ if(is_object($from)){
+ if(!isset($objects[$hash = spl_object_hash($from)])){
+ $objects[$hash] = $from;
+ $refCounts[$hash] = 0;
+ }
+
+ ++$refCounts[$hash];
+
+ $data = "(object) $hash";
+ }elseif(is_array($from)){
+ if($recursion >= 5){
+ return "(error) ARRAY RECURSION LIMIT REACHED";
+ }
+ $data = [];
+ $numeric = 0;
+ foreach(Utils::promoteKeys($from) as $key => $value){
+ $data[$numeric] = [
+ "k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
+ "v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
+ ];
+ $numeric++;
+ }
+ }elseif(is_string($from)){
+ $data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
+ }elseif(is_resource($from)){
+ $data = "(resource) " . print_r($from, true);
+ }elseif(is_float($from)){
+ $data = "(float) $from";
+ }else{
+ $data = $from;
+ }
+
+ return $data;
+ }
+}
diff --git a/src/MemoryManager.php b/src/MemoryManager.php
index 4308167d3..2b7f5f1d3 100644
--- a/src/MemoryManager.php
+++ b/src/MemoryManager.php
@@ -29,52 +29,24 @@ use pocketmine\scheduler\DumpWorkerMemoryTask;
use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\timings\Timings;
use pocketmine\utils\Process;
-use pocketmine\utils\Utils;
use pocketmine\YmlServerProperties as Yml;
-use Symfony\Component\Filesystem\Path;
-use function arsort;
-use function count;
-use function fclose;
-use function file_exists;
-use function file_put_contents;
-use function fopen;
-use function fwrite;
use function gc_collect_cycles;
-use function gc_disable;
-use function gc_enable;
use function gc_mem_caches;
-use function get_class;
-use function get_declared_classes;
-use function get_defined_functions;
-use function ini_get;
use function ini_set;
use function intdiv;
-use function is_array;
-use function is_float;
-use function is_object;
-use function is_resource;
-use function is_string;
-use function json_encode;
use function mb_strtoupper;
use function min;
-use function mkdir;
use function preg_match;
-use function print_r;
use function round;
-use function spl_object_hash;
use function sprintf;
-use function strlen;
-use function substr;
-use const JSON_PRETTY_PRINT;
-use const JSON_THROW_ON_ERROR;
-use const JSON_UNESCAPED_SLASHES;
-use const SORT_NUMERIC;
class MemoryManager{
private const DEFAULT_CHECK_RATE = Server::TARGET_TICKS_PER_SECOND;
private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2;
private const DEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND;
+ private GarbageCollectorManager $cycleGcManager;
+
private int $memoryLimit;
private int $globalMemoryLimit;
private int $checkRate;
@@ -88,14 +60,8 @@ class MemoryManager{
private int $garbageCollectionPeriod;
private int $garbageCollectionTicker = 0;
- private bool $garbageCollectionTrigger;
- private bool $garbageCollectionAsync;
private int $lowMemChunkRadiusOverride;
- private bool $lowMemChunkGC;
-
- private bool $lowMemDisableChunkCache;
- private bool $lowMemClearWorldCache;
private bool $dumpWorkers = true;
@@ -105,6 +71,7 @@ class MemoryManager{
private Server $server
){
$this->logger = new \PrefixedLogger($server->getLogger(), "Memory Manager");
+ $this->cycleGcManager = new GarbageCollectorManager($this->logger, Timings::$memoryManager);
$this->init($server->getConfigGroup());
}
@@ -142,17 +109,10 @@ class MemoryManager{
$this->continuousTriggerRate = $config->getPropertyInt(Yml::MEMORY_CONTINUOUS_TRIGGER_RATE, self::DEFAULT_CONTINUOUS_TRIGGER_RATE);
$this->garbageCollectionPeriod = $config->getPropertyInt(Yml::MEMORY_GARBAGE_COLLECTION_PERIOD, self::DEFAULT_TICKS_PER_GC);
- $this->garbageCollectionTrigger = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER, true);
- $this->garbageCollectionAsync = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER, true);
$this->lowMemChunkRadiusOverride = $config->getPropertyInt(Yml::MEMORY_MAX_CHUNKS_CHUNK_RADIUS, 4);
- $this->lowMemChunkGC = $config->getPropertyBool(Yml::MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT, true);
-
- $this->lowMemDisableChunkCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE, true);
- $this->lowMemClearWorldCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER, true);
$this->dumpWorkers = $config->getPropertyBool(Yml::MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER, true);
- gc_enable();
}
public function isLowMemory() : bool{
@@ -163,8 +123,11 @@ class MemoryManager{
return $this->globalMemoryLimit;
}
+ /**
+ * @deprecated
+ */
public function canUseChunkCache() : bool{
- return !$this->lowMemory || !$this->lowMemDisableChunkCache;
+ return !$this->lowMemory;
}
/**
@@ -180,26 +143,19 @@ class MemoryManager{
public function trigger(int $memory, int $limit, bool $global = false, int $triggerCount = 0) : void{
$this->logger->debug(sprintf("%sLow memory triggered, limit %gMB, using %gMB",
$global ? "Global " : "", round(($limit / 1024) / 1024, 2), round(($memory / 1024) / 1024, 2)));
- if($this->lowMemClearWorldCache){
- foreach($this->server->getWorldManager()->getWorlds() as $world){
- $world->clearCache(true);
- }
- ChunkCache::pruneCaches();
+ foreach($this->server->getWorldManager()->getWorlds() as $world){
+ $world->clearCache(true);
}
+ ChunkCache::pruneCaches();
- if($this->lowMemChunkGC){
- foreach($this->server->getWorldManager()->getWorlds() as $world){
- $world->doChunkGarbageCollection();
- }
+ foreach($this->server->getWorldManager()->getWorlds() as $world){
+ $world->doChunkGarbageCollection();
}
$ev = new LowMemoryEvent($memory, $limit, $global, $triggerCount);
$ev->call();
- $cycles = 0;
- if($this->garbageCollectionTrigger){
- $cycles = $this->triggerGarbageCollector();
- }
+ $cycles = $this->triggerGarbageCollector();
$this->logger->debug(sprintf("Freed %gMB, $cycles cycles", round(($ev->getMemoryFreed() / 1024) / 1024, 2)));
}
@@ -239,6 +195,8 @@ class MemoryManager{
if($this->garbageCollectionPeriod > 0 && ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
$this->garbageCollectionTicker = 0;
$this->triggerGarbageCollector();
+ }else{
+ $this->cycleGcManager->maybeCollectCycles();
}
Timings::$memoryManager->stopTiming();
@@ -247,14 +205,12 @@ class MemoryManager{
public function triggerGarbageCollector() : int{
Timings::$garbageCollector->startTiming();
- if($this->garbageCollectionAsync){
- $pool = $this->server->getAsyncPool();
- if(($w = $pool->shutdownUnusedWorkers()) > 0){
- $this->logger->debug("Shut down $w idle async pool workers");
- }
- foreach($pool->getRunningWorkers() as $i){
- $pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
- }
+ $pool = $this->server->getAsyncPool();
+ if(($w = $pool->shutdownUnusedWorkers()) > 0){
+ $this->logger->debug("Shut down $w idle async pool workers");
+ }
+ foreach($pool->getRunningWorkers() as $i){
+ $pool->submitTaskToWorker(new GarbageCollectionTask(), $i);
}
$cycles = gc_collect_cycles();
@@ -271,7 +227,7 @@ class MemoryManager{
public function dumpServerMemory(string $outputFolder, int $maxNesting, int $maxStringSize) : void{
$logger = new \PrefixedLogger($this->server->getLogger(), "Memory Dump");
$logger->notice("After the memory dump is done, the server might crash");
- self::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $logger);
+ MemoryDump::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $logger);
if($this->dumpWorkers){
$pool = $this->server->getAsyncPool();
@@ -283,239 +239,10 @@ class MemoryManager{
/**
* Static memory dumper accessible from any thread.
+ * @deprecated
+ * @see MemoryDump
*/
public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
- $hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist");
- ini_set('memory_limit', '-1');
- gc_disable();
-
- if(!file_exists($outputFolder)){
- mkdir($outputFolder, 0777, true);
- }
-
- $obData = Utils::assumeNotFalse(fopen(Path::join($outputFolder, "objects.js"), "wb+"));
-
- $objects = [];
-
- $refCounts = [];
-
- $instanceCounts = [];
-
- $staticProperties = [];
- $staticCount = 0;
-
- $functionStaticVars = [];
- $functionStaticVarsCount = 0;
-
- foreach(get_declared_classes() as $className){
- $reflection = new \ReflectionClass($className);
- $staticProperties[$className] = [];
- foreach($reflection->getProperties() as $property){
- if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){
- continue;
- }
-
- if(!$property->isInitialized()){
- continue;
- }
-
- $staticCount++;
- $staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
- }
-
- if(count($staticProperties[$className]) === 0){
- unset($staticProperties[$className]);
- }
-
- foreach($reflection->getMethods() as $method){
- if($method->getDeclaringClass()->getName() !== $reflection->getName()){
- continue;
- }
- $methodStatics = [];
- foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){
- $methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
- }
- if(count($methodStatics) > 0){
- $functionStaticVars[$className . "::" . $method->getName()] = $methodStatics;
- $functionStaticVarsCount += count($functionStaticVars);
- }
- }
- }
-
- file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
- $logger->info("Wrote $staticCount static properties");
-
- $globalVariables = [];
- $globalCount = 0;
-
- $ignoredGlobals = [
- 'GLOBALS' => true,
- '_SERVER' => true,
- '_REQUEST' => true,
- '_POST' => true,
- '_GET' => true,
- '_FILES' => true,
- '_ENV' => true,
- '_COOKIE' => true,
- '_SESSION' => true
- ];
-
- foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){
- if(isset($ignoredGlobals[$varName])){
- continue;
- }
-
- $globalCount++;
- $globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
- }
-
- file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
- $logger->info("Wrote $globalCount global variables");
-
- foreach(get_defined_functions()["user"] as $function){
- $reflect = new \ReflectionFunction($function);
-
- $vars = [];
- foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){
- $vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
- }
- if(count($vars) > 0){
- $functionStaticVars[$function] = $vars;
- $functionStaticVarsCount += count($vars);
- }
- }
- file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
- $logger->info("Wrote $functionStaticVarsCount function static variables");
-
- $data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
-
- do{
- $continue = false;
- foreach(Utils::stringifyKeys($objects) as $hash => $object){
- if(!is_object($object)){
- continue;
- }
- $continue = true;
-
- $className = get_class($object);
- if(!isset($instanceCounts[$className])){
- $instanceCounts[$className] = 1;
- }else{
- $instanceCounts[$className]++;
- }
-
- $objects[$hash] = true;
- $info = [
- "information" => "$hash@$className",
- ];
- if($object instanceof \Closure){
- $info["definition"] = Utils::getNiceClosureName($object);
- $info["referencedVars"] = [];
- $reflect = new \ReflectionFunction($object);
- if(($closureThis = $reflect->getClosureThis()) !== null){
- $info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
- }
-
- foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){
- $info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
- }
- }else{
- $reflection = new \ReflectionObject($object);
-
- $info["properties"] = [];
-
- for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){
- foreach($reflection->getProperties() as $property){
- if($property->isStatic()){
- continue;
- }
-
- $name = $property->getName();
- if($reflection !== $original){
- if($property->isPrivate()){
- $name = $reflection->getName() . ":" . $name;
- }else{
- continue;
- }
- }
- if(!$property->isInitialized($object)){
- continue;
- }
-
- $info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize);
- }
- }
- }
-
- fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n");
- }
-
- }while($continue);
-
- $logger->info("Wrote " . count($objects) . " objects");
-
- fclose($obData);
-
- file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
- file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
-
- arsort($instanceCounts, SORT_NUMERIC);
- file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
-
- $logger->info("Finished!");
-
- ini_set('memory_limit', $hardLimit);
- gc_enable();
- }
-
- /**
- * @param object[] $objects reference parameter
- * @param int[] $refCounts reference parameter
- *
- * @phpstan-param array $objects
- * @phpstan-param array $refCounts
- * @phpstan-param-out array $objects
- * @phpstan-param-out array $refCounts
- */
- private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{
- if($maxNesting <= 0){
- return "(error) NESTING LIMIT REACHED";
- }
-
- --$maxNesting;
-
- if(is_object($from)){
- if(!isset($objects[$hash = spl_object_hash($from)])){
- $objects[$hash] = $from;
- $refCounts[$hash] = 0;
- }
-
- ++$refCounts[$hash];
-
- $data = "(object) $hash";
- }elseif(is_array($from)){
- if($recursion >= 5){
- return "(error) ARRAY RECURSION LIMIT REACHED";
- }
- $data = [];
- $numeric = 0;
- foreach(Utils::promoteKeys($from) as $key => $value){
- $data[$numeric] = [
- "k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
- "v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
- ];
- $numeric++;
- }
- }elseif(is_string($from)){
- $data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize);
- }elseif(is_resource($from)){
- $data = "(resource) " . print_r($from, true);
- }elseif(is_float($from)){
- $data = "(float) $from";
- }else{
- $data = $from;
- }
-
- return $data;
+ MemoryDump::dumpMemory($startingObject, $outputFolder, $maxNesting, $maxStringSize, $logger);
}
}
diff --git a/src/PocketMine.php b/src/PocketMine.php
index b2e1cd046..c34b8e221 100644
--- a/src/PocketMine.php
+++ b/src/PocketMine.php
@@ -98,6 +98,7 @@ namespace pocketmine {
"crypto" => "php-crypto",
"ctype" => "ctype",
"date" => "Date",
+ "encoding" => "pmmp/ext-encoding",
"gmp" => "GMP",
"hash" => "Hash",
"igbinary" => "igbinary",
@@ -155,6 +156,12 @@ namespace pocketmine {
}
}
+ if(($encoding_version = phpversion("encoding")) !== false){
+ if(version_compare($encoding_version, "1.0.0") < 0 || version_compare($encoding_version, "2.0.0") >= 0){
+ $messages[] = "pmmp/ext-encoding ^1.0.0 is required, while you have $encoding_version.";
+ }
+ }
+
if(extension_loaded("pocketmine")){
$messages[] = "The native PocketMine extension is no longer supported.";
}
@@ -264,7 +271,7 @@ JIT_WARNING
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
if($composerGitHash !== null){
//we can't verify dependency versions if we were installed without using git
- $currentGitHash = explode("-", VersionInfo::GIT_HASH())[0];
+ $currentGitHash = explode("-", VersionInfo::GIT_HASH(), 2)[0];
if($currentGitHash !== $composerGitHash){
critical_error("Composer dependencies and/or autoloader are out of sync.");
critical_error("- Current revision is $currentGitHash");
@@ -282,6 +289,11 @@ JIT_WARNING
exit(0);
}
+ if(defined('pocketmine\ORIGINAL_PHAR_PATH')){
+ //if we're inside a phar cache, \pocketmine\PATH will not include the original phar
+ Filesystem::addCleanedPath(ORIGINAL_PHAR_PATH, Filesystem::CLEAN_PATH_SRC_PREFIX);
+ }
+
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
$dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd;
$pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
diff --git a/src/Server.php b/src/Server.php
index 9003a7f9c..af9cbeda7 100644
--- a/src/Server.php
+++ b/src/Server.php
@@ -36,6 +36,7 @@ use pocketmine\crafting\CraftingManager;
use pocketmine\crafting\CraftingManagerFromDataHelper;
use pocketmine\crash\CrashDump;
use pocketmine\crash\CrashDumpRenderer;
+use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\entity\EntityDataHelper;
use pocketmine\entity\Location;
use pocketmine\event\HandlerListManager;
@@ -49,6 +50,7 @@ use pocketmine\lang\Language;
use pocketmine\lang\LanguageNotFoundException;
use pocketmine\lang\Translatable;
use pocketmine\nbt\tag\CompoundTag;
+use pocketmine\network\mcpe\auth\AuthKeyProvider;
use pocketmine\network\mcpe\compression\CompressBatchPromise;
use pocketmine\network\mcpe\compression\CompressBatchTask;
use pocketmine\network\mcpe\compression\Compressor;
@@ -138,6 +140,7 @@ use function file_put_contents;
use function filemtime;
use function fopen;
use function get_class;
+use function gettype;
use function ini_set;
use function is_array;
use function is_dir;
@@ -268,6 +271,7 @@ class Server{
private int $maxPlayers;
private bool $onlineMode = true;
+ private AuthKeyProvider $authKeyProvider;
private Network $network;
private bool $networkCompressionAsync = true;
@@ -697,7 +701,7 @@ class Server{
public function removeOp(string $name) : void{
$lowercaseName = strtolower($name);
- foreach($this->operators->getAll() as $operatorName => $_){
+ foreach(Utils::promoteKeys($this->operators->getAll()) as $operatorName => $_){
$operatorName = (string) $operatorName;
if($lowercaseName === strtolower($operatorName)){
$this->operators->remove($operatorName);
@@ -918,6 +922,7 @@ class Server{
TimingsHandler::getCollectCallbacks()->add(function() : array{
$promises = [];
foreach($this->asyncPool->getRunningWorkers() as $workerId){
+ /** @phpstan-var PromiseResolver> $resolver */
$resolver = new PromiseResolver();
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
@@ -979,6 +984,8 @@ class Server{
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
}
+ $this->authKeyProvider = new AuthKeyProvider(new \PrefixedLogger($this->logger, "Minecraft Auth Key Provider"), $this->asyncPool);
+
if($this->configGroup->getConfigBool(ServerProperties::HARDCORE, false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
$this->configGroup->setConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_HARD);
}
@@ -1003,7 +1010,7 @@ class Server{
$this->commandMap = new SimpleCommandMap($this);
- $this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes"));
+ $this->craftingManager = CraftingManagerFromDataHelper::make(BedrockDataFiles::RECIPES);
$this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger);
@@ -1013,7 +1020,11 @@ class Server{
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
}
try{
- $pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile)));
+ $array = yaml_parse(Filesystem::fileGetContents($graylistFile));
+ if(!is_array($array)){
+ throw new \InvalidArgumentException("Expected array for root, but have " . gettype($array));
+ }
+ $pluginGraylist = PluginGraylist::fromArray($array);
}catch(\InvalidArgumentException $e){
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
$this->forceShutdownExit();
@@ -1174,7 +1185,7 @@ class Server{
if($this->worldManager->getDefaultWorld() === null){
$default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
- if(trim($default) == ""){
+ if(trim($default) === ""){
$this->logger->warning("level-name cannot be null, using default");
$default = "world";
$this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
@@ -1611,7 +1622,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)){
@@ -1793,6 +1804,13 @@ class Server{
return $this->forceLanguage;
}
+ /**
+ * @internal
+ */
+ public function getAuthKeyProvider() : AuthKeyProvider{
+ return $this->authKeyProvider;
+ }
+
public function getNetwork() : Network{
return $this->network;
}
diff --git a/src/VersionInfo.php b/src/VersionInfo.php
index efb38d71b..944d6d84f 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.21.3";
+ public const BASE_VERSION = "5.33.3";
public const IS_DEVELOPMENT_BUILD = true;
public const BUILD_CHANNEL = "stable";
diff --git a/src/YmlServerProperties.php b/src/YmlServerProperties.php
index 9bd203eef..282b0b3cd 100644
--- a/src/YmlServerProperties.php
+++ b/src/YmlServerProperties.php
@@ -75,20 +75,14 @@ final class YmlServerProperties{
public const MEMORY_CONTINUOUS_TRIGGER = 'memory.continuous-trigger';
public const MEMORY_CONTINUOUS_TRIGGER_RATE = 'memory.continuous-trigger-rate';
public const MEMORY_GARBAGE_COLLECTION = 'memory.garbage-collection';
- public const MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER = 'memory.garbage-collection.collect-async-worker';
- public const MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER = 'memory.garbage-collection.low-memory-trigger';
public const MEMORY_GARBAGE_COLLECTION_PERIOD = 'memory.garbage-collection.period';
public const MEMORY_GLOBAL_LIMIT = 'memory.global-limit';
public const MEMORY_MAIN_HARD_LIMIT = 'memory.main-hard-limit';
public const MEMORY_MAIN_LIMIT = 'memory.main-limit';
public const MEMORY_MAX_CHUNKS = 'memory.max-chunks';
public const MEMORY_MAX_CHUNKS_CHUNK_RADIUS = 'memory.max-chunks.chunk-radius';
- public const MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT = 'memory.max-chunks.trigger-chunk-collect';
public const MEMORY_MEMORY_DUMP = 'memory.memory-dump';
public const MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER = 'memory.memory-dump.dump-async-worker';
- public const MEMORY_WORLD_CACHES = 'memory.world-caches';
- public const MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE = 'memory.world-caches.disable-chunk-cache';
- public const MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER = 'memory.world-caches.low-memory-trigger';
public const NETWORK = 'network';
public const NETWORK_ASYNC_COMPRESSION = 'network.async-compression';
public const NETWORK_ASYNC_COMPRESSION_THRESHOLD = 'network.async-compression-threshold';
diff --git a/src/block/ActivatorRail.php b/src/block/ActivatorRail.php
index dcd0ef93b..da15eb1e8 100644
--- a/src/block/ActivatorRail.php
+++ b/src/block/ActivatorRail.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\RailPoweredByRedstoneTrait;
-class ActivatorRail extends StraightOnlyRail{
+class ActivatorRail extends StraightOnlyRail implements PoweredByRedstone{
use RailPoweredByRedstoneTrait;
//TODO
diff --git a/src/block/AmethystCluster.php b/src/block/AmethystCluster.php
index 639490456..8a750e974 100644
--- a/src/block/AmethystCluster.php
+++ b/src/block/AmethystCluster.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AmethystTrait;
+use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\block\utils\SupportType;
@@ -38,7 +39,7 @@ use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\world\BlockTransaction;
-final class AmethystCluster extends Transparent{
+final class AmethystCluster extends Transparent implements AnyFacing{
use AmethystTrait;
use AnyFacingTrait;
diff --git a/src/block/Anvil.php b/src/block/Anvil.php
index 0a1a47070..fcb4d045c 100644
--- a/src/block/Anvil.php
+++ b/src/block/Anvil.php
@@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\inventory\AnvilInventory;
use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -41,7 +42,7 @@ use pocketmine\world\sound\AnvilFallSound;
use pocketmine\world\sound\Sound;
use function round;
-class Anvil extends Transparent implements Fallable{
+class Anvil extends Transparent implements Fallable, HorizontalFacing{
use FallableTrait;
use HorizontalFacingTrait;
@@ -70,9 +71,6 @@ class Anvil extends Transparent implements Fallable{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->squash(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)];
}
diff --git a/src/block/Bamboo.php b/src/block/Bamboo.php
index 9f605bca6..fd64e10ef 100644
--- a/src/block/Bamboo.php
+++ b/src/block/Bamboo.php
@@ -87,9 +87,6 @@ class Bamboo extends Transparent{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
//this places the BB at the northwest corner, not the center
$inset = 1 - (($this->thick ? 3 : 2) / 16);
diff --git a/src/block/Barrel.php b/src/block/Barrel.php
index 0f0499ab9..7b2ea356e 100644
--- a/src/block/Barrel.php
+++ b/src/block/Barrel.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\Barrel as TileBarrel;
+use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@@ -33,7 +34,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function abs;
-class Barrel extends Opaque{
+class Barrel extends Opaque implements AnyFacing{
use AnyFacingTrait;
protected bool $open = false;
diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php
index 6b9e493d1..e8bf187ee 100644
--- a/src/block/BaseBanner.php
+++ b/src/block/BaseBanner.php
@@ -25,19 +25,19 @@ 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\AxisAlignedBB;
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;
/**
@@ -50,6 +50,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());
}
@@ -57,6 +61,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);
@@ -97,9 +108,6 @@ abstract class BaseBanner extends Transparent{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [];
}
diff --git a/src/block/BaseBigDripleaf.php b/src/block/BaseBigDripleaf.php
index f0ff59cf0..94e2c12a2 100644
--- a/src/block/BaseBigDripleaf.php
+++ b/src/block/BaseBigDripleaf.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\StructureGrowEvent;
@@ -33,7 +34,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-abstract class BaseBigDripleaf extends Transparent{
+abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{
use HorizontalFacingTrait;
abstract protected function isHead() : bool;
diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php
index b9c595a97..c1cc9bb25 100644
--- a/src/block/BaseCoral.php
+++ b/src/block/BaseCoral.php
@@ -24,12 +24,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\CoralMaterial;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use function mt_rand;
-abstract class BaseCoral extends Transparent{
+abstract class BaseCoral extends Transparent implements CoralMaterial{
use CoralTypeTrait;
public function onNearbyBlockChange() : void{
diff --git a/src/block/BaseOminousBanner.php b/src/block/BaseOminousBanner.php
new file mode 100644
index 000000000..192e6fac2
--- /dev/null
+++ b/src/block/BaseOminousBanner.php
@@ -0,0 +1,90 @@
+position->getWorld()->getTile($this->position);
+ assert($tile instanceof TileBanner);
+ $tile->setBaseColor(DyeColor::WHITE);
+ $tile->setPatterns([]);
+ $tile->setType(TileBanner::TYPE_OMINOUS);
+ }
+
+ public function isSolid() : bool{
+ return false;
+ }
+
+ public function getMaxStackSize() : int{
+ return 16;
+ }
+
+ public function getFuelTime() : int{
+ return 300;
+ }
+
+ protected function recalculateCollisionBoxes() : array{
+ return [];
+ }
+
+ public function getSupportType(int $facing) : SupportType{
+ return SupportType::NONE;
+ }
+
+ private function canBeSupportedBy(Block $block) : bool{
+ return $block->isSolid();
+ }
+
+ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
+ if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
+ return false;
+ }
+
+ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+ }
+
+ abstract protected function getSupportingFace() : int;
+
+ public function onNearbyBlockChange() : void{
+ if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
+ $this->position->getWorld()->useBreakOn($this->position);
+ }
+ }
+
+ public function asItem() : Item{
+ return VanillaItems::OMINOUS_BANNER();
+ }
+}
diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php
index 5a905f8b8..8c324918b 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,21 +35,25 @@ use pocketmine\event\block\SignChangeEvent;
use pocketmine\item\Dye;
use pocketmine\item\Item;
use pocketmine\item\ItemTypeIds;
-use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\utils\TextFormat;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\DyeUseSound;
use pocketmine\world\sound\InkSacUseSound;
+use function abs;
use function array_map;
use function assert;
+use function atan2;
+use function fmod;
+use function rad2deg;
use function strlen;
-abstract class BaseSign extends Transparent{
+abstract class BaseSign extends Transparent implements WoodMaterial{
use WoodTypeTrait;
- protected SignText $text;
+ protected SignText $text; //TODO: rename this (BC break)
+ protected SignText $backText;
private bool $waxed = false;
protected ?int $editorEntityRuntimeId = null;
@@ -63,6 +68,7 @@ abstract class BaseSign extends Transparent{
$this->woodType = $woodType;
parent::__construct($idInfo, $name, $typeInfo);
$this->text = new SignText();
+ $this->backText = new SignText();
$this->asItemCallback = $asItemCallback;
}
@@ -71,6 +77,7 @@ abstract class BaseSign extends Transparent{
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileSign){
$this->text = $tile->getText();
+ $this->backText = $tile->getBackText();
$this->waxed = $tile->isWaxed();
$this->editorEntityRuntimeId = $tile->getEditorEntityRuntimeId();
}
@@ -83,6 +90,7 @@ abstract class BaseSign extends Transparent{
$tile = $this->position->getWorld()->getTile($this->position);
assert($tile instanceof TileSign);
$tile->setText($this->text);
+ $tile->setBackText($this->backText);
$tile->setWaxed($this->waxed);
$tile->setEditorEntityRuntimeId($this->editorEntityRuntimeId);
}
@@ -95,9 +103,6 @@ abstract class BaseSign extends Transparent{
return 16;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [];
}
@@ -130,11 +135,11 @@ abstract class BaseSign extends Transparent{
}
}
- private function doSignChange(SignText $newText, Player $player, Item $item) : bool{
- $ev = new SignChangeEvent($this, $player, $newText);
+ private function doSignChange(SignText $newText, Player $player, Item $item, bool $frontFace) : bool{
+ $ev = new SignChangeEvent($this, $player, $newText, $frontFace);
$ev->call();
if(!$ev->isCancelled()){
- $this->text = $ev->getNewText();
+ $this->setFaceText($frontFace, $ev->getNewText());
$this->position->getWorld()->setBlock($this->position, $this);
$item->pop();
return true;
@@ -143,8 +148,9 @@ abstract class BaseSign extends Transparent{
return false;
}
- private function changeSignGlowingState(bool $glowing, Player $player, Item $item) : bool{
- if($this->text->isGlowing() !== $glowing && $this->doSignChange(new SignText($this->text->getLines(), $this->text->getBaseColor(), $glowing), $player, $item)){
+ private function changeSignGlowingState(bool $glowing, Player $player, Item $item, bool $frontFace) : bool{
+ $text = $this->getFaceText($frontFace);
+ if($text->isGlowing() !== $glowing && $this->doSignChange(new SignText($text->getLines(), $text->getBaseColor(), $glowing), $player, $item, $frontFace)){
$this->position->getWorld()->addSound($this->position, new InkSacUseSound());
return true;
}
@@ -171,6 +177,8 @@ abstract class BaseSign extends Transparent{
return true;
}
+ $frontFace = $this->interactsFront($this->getHitboxCenter(), $player->getPosition(), $this->getFacingDegrees());
+
$dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
@@ -179,40 +187,82 @@ abstract class BaseSign extends Transparent{
};
if($dyeColor !== null){
$color = $dyeColor === DyeColor::BLACK ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
+ $text = $this->getFaceText($frontFace);
if(
- $color->toARGB() !== $this->text->getBaseColor()->toARGB() &&
- $this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)
+ $color->toARGB() !== $text->getBaseColor()->toARGB() &&
+ $this->doSignChange(new SignText($text->getLines(), $color, $text->isGlowing()), $player, $item, $frontFace)
){
$this->position->getWorld()->addSound($this->position, new DyeUseSound());
return true;
}
}elseif(match($item->getTypeId()){
- ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item),
- ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item),
+ ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item, $frontFace),
+ ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item, $frontFace),
ItemTypeIds::HONEYCOMB => $this->wax($player, $item),
default => false
}){
return true;
}
- $player->openSignEditor($this->position);
+ $player->openSignEditor($this->position, $frontFace);
return true;
}
+ private function interactsFront(Vector3 $hitboxCenter, Vector3 $playerPosition, float $signFacingDegrees) : bool{
+ $playerCenterDiffX = $playerPosition->x - $hitboxCenter->x;
+ $playerCenterDiffZ = $playerPosition->z - $hitboxCenter->z;
+
+ $f1 = rad2deg(atan2($playerCenterDiffZ, $playerCenterDiffX)) - 90.0;
+
+ $rotationDiff = $signFacingDegrees - $f1;
+ $rotation = fmod($rotationDiff + 180.0, 360.0) - 180.0; // Normalize to [-180, 180]
+ return abs($rotation) <= 90.0;
+ }
+
+ /**
+ * Returns the center of the sign's hitbox. Used to decide which face of the sign to open when a player interacts.
+ */
+ protected function getHitboxCenter() : Vector3{
+ return $this->position->add(0.5, 0.5, 0.5);
+ }
+
+ /**
+ * TODO: make this abstract (BC break)
+ */
+ protected function getFacingDegrees() : float{
+ return 0;
+ }
+
/**
* Returns an object containing information about the sign text.
+ * @deprecated
+ * @see self::getFaceText()
*/
public function getText() : SignText{
return $this->text;
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @see self::setFaceText()
+ * @return $this
+ */
public function setText(SignText $text) : self{
$this->text = $text;
return $this;
}
+ public function getFaceText(bool $frontFace) : SignText{
+ return $frontFace ? $this->text : $this->backText;
+ }
+
+ /** @return $this */
+ public function setFaceText(bool $frontFace, SignText $text) : self{
+ $frontFace ? $this->text = $text : $this->backText = $text;
+ return $this;
+ }
+
/**
* Returns whether the sign has been waxed using a honeycomb. If true, the sign cannot be edited by a player.
*/
@@ -237,13 +287,21 @@ abstract class BaseSign extends Transparent{
return $this;
}
+ /**
+ * @deprecated
+ * @see self::updateFaceText()
+ */
+ public function updateText(Player $author, SignText $text) : bool{
+ return $this->updateFaceText($author, true, $text);
+ }
+
/**
* Called by the player controller (network session) to update the sign text, firing events as appropriate.
*
* @return bool if the sign update was successful.
* @throws \UnexpectedValueException if the text payload is too large
*/
- public function updateText(Player $author, SignText $text) : bool{
+ public function updateFaceText(Player $author, bool $frontFace, SignText $text) : bool{
$size = 0;
foreach($text->getLines() as $line){
$size += strlen($line);
@@ -251,15 +309,16 @@ abstract class BaseSign extends Transparent{
if($size > 1000){
throw new \UnexpectedValueException($author->getName() . " tried to write $size bytes of text onto a sign (bigger than max 1000)");
}
+ $oldText = $this->getFaceText($frontFace);
$ev = new SignChangeEvent($this, $author, new SignText(array_map(function(string $line) : string{
return TextFormat::clean($line, false);
- }, $text->getLines()), $this->text->getBaseColor(), $this->text->isGlowing()));
+ }, $text->getLines()), $oldText->getBaseColor(), $oldText->isGlowing()), $frontFace);
if($this->waxed || $this->editorEntityRuntimeId !== $author->getId()){
$ev->cancel();
}
$ev->call();
if(!$ev->isCancelled()){
- $this->setText($ev->getNewText());
+ $this->setFaceText($frontFace, $ev->getNewText());
$this->setEditorEntityRuntimeId(null);
$this->position->getWorld()->setBlock($this->position, $this);
return true;
diff --git a/src/block/Bed.php b/src/block/Bed.php
index 8efbdfe01..21bd3a172 100644
--- a/src/block/Bed.php
+++ b/src/block/Bed.php
@@ -24,8 +24,10 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\Bed as TileBed;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -41,7 +43,7 @@ use pocketmine\utils\TextFormat;
use pocketmine\world\BlockTransaction;
use pocketmine\world\World;
-class Bed extends Transparent{
+class Bed extends Transparent implements Colored, HorizontalFacing{
use ColoredTrait;
use HorizontalFacingTrait;
@@ -76,9 +78,6 @@ class Bed extends Transparent{
}
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)];
}
diff --git a/src/block/Bell.php b/src/block/Bell.php
index 53a6fc7fb..258abc628 100644
--- a/src/block/Bell.php
+++ b/src/block/Bell.php
@@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Bell as TileBell;
use pocketmine\block\utils\BellAttachmentType;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -38,7 +39,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\BellRingSound;
-final class Bell extends Transparent{
+final class Bell extends Transparent implements HorizontalFacing{
use HorizontalFacingTrait;
private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR;
diff --git a/src/block/Block.php b/src/block/Block.php
index 89fe39265..fd644eae4 100644
--- a/src/block/Block.php
+++ b/src/block/Block.php
@@ -26,6 +26,8 @@ declare(strict_types=1);
*/
namespace pocketmine\block;
+use pmmp\encoding\BE;
+use pmmp\encoding\LE;
use pocketmine\block\tile\Spawnable;
use pocketmine\block\tile\Tile;
use pocketmine\block\utils\SupportType;
@@ -49,7 +51,6 @@ use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
-use pocketmine\utils\Binary;
use pocketmine\world\BlockTransaction;
use pocketmine\world\format\Chunk;
use pocketmine\world\Position;
@@ -75,7 +76,10 @@ class Block{
protected BlockTypeInfo $typeInfo;
protected Position $position;
- /** @var AxisAlignedBB[]|null */
+ /**
+ * @var AxisAlignedBB[]|null
+ * @phpstan-var list|null
+ */
protected ?array $collisionBoxes = null;
private int $requiredBlockItemStateDataBits;
@@ -95,9 +99,10 @@ class Block{
* of operations required to compute the state ID (micro optimization).
*/
private static function computeStateIdXorMask(int $typeId) : int{
+ //TODO: the mixed byte order here is probably a mistake, but it doesn't break anything for now
return
$typeId << self::INTERNAL_STATE_DATA_BITS |
- (Binary::readLong(hash('xxh3', Binary::writeLLong($typeId), binary: true)) & self::INTERNAL_STATE_DATA_MASK);
+ (BE::unpackSignedLong(hash('xxh3', LE::packSignedLong($typeId), binary: true)) & self::INTERNAL_STATE_DATA_MASK);
}
/**
@@ -907,6 +912,7 @@ class Block{
* - anti-cheat checks in plugins
*
* @return AxisAlignedBB[]
+ * @phpstan-return list
*/
final public function getCollisionBoxes() : array{
if($this->collisionBoxes === null){
@@ -931,6 +937,7 @@ class Block{
/**
* @return AxisAlignedBB[]
+ * @phpstan-return list
*/
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()];
diff --git a/src/block/BlockBreakInfo.php b/src/block/BlockBreakInfo.php
index b49cb6f13..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);
}
@@ -95,7 +95,7 @@ class BlockBreakInfo{
* Returns whether this block can be instantly broken.
*/
public function breaksInstantly() : bool{
- return $this->hardness == 0.0;
+ return $this->hardness === 0.0;
}
/**
@@ -154,7 +154,7 @@ class BlockBreakInfo{
$efficiency = $item->getMiningEfficiency(($this->toolType & $item->getBlockToolType()) !== 0);
if($efficiency <= 0){
- throw new \InvalidArgumentException(get_class($item) . " has invalid mining efficiency: expected >= 0, got $efficiency");
+ throw new \InvalidArgumentException(get_class($item) . " must have a positive mining efficiency, but got $efficiency");
}
$base /= $efficiency;
diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php
index 3914a4b74..e4d49746f 100644
--- a/src/block/BlockTypeIds.php
+++ b/src/block/BlockTypeIds.php
@@ -765,8 +765,65 @@ final class BlockTypeIds{
public const COPPER_TRAPDOOR = 10735;
public const CHISELED_COPPER = 10736;
public const COPPER_GRATE = 10737;
+ public const PALE_OAK_BUTTON = 10738;
+ public const PALE_OAK_DOOR = 10739;
+ public const PALE_OAK_FENCE = 10740;
+ public const PALE_OAK_FENCE_GATE = 10741;
+ public const PALE_OAK_LEAVES = 10742;
+ public const PALE_OAK_LOG = 10743;
+ public const PALE_OAK_PLANKS = 10744;
+ public const PALE_OAK_PRESSURE_PLATE = 10745;
+ public const PALE_OAK_SIGN = 10746;
+ public const PALE_OAK_SLAB = 10747;
+ public const PALE_OAK_STAIRS = 10748;
+ public const PALE_OAK_TRAPDOOR = 10749;
+ public const PALE_OAK_WALL_SIGN = 10750;
+ public const PALE_OAK_WOOD = 10751;
+ public const RESIN = 10752;
+ public const RESIN_BRICK_SLAB = 10753;
+ public const RESIN_BRICK_STAIRS = 10754;
+ public const RESIN_BRICK_WALL = 10755;
+ 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 = 10738;
+ 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/Button.php b/src/block/Button.php
index 73bd1d556..4d1f1bbc5 100644
--- a/src/block/Button.php
+++ b/src/block/Button.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@@ -33,7 +34,7 @@ use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\RedstonePowerOffSound;
use pocketmine\world\sound\RedstonePowerOnSound;
-abstract class Button extends Flowable{
+abstract class Button extends Flowable implements AnyFacing{
use AnyFacingTrait;
protected bool $pressed = false;
diff --git a/src/block/Cactus.php b/src/block/Cactus.php
index 6f2b04c8a..51c98786b 100644
--- a/src/block/Cactus.php
+++ b/src/block/Cactus.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
@@ -33,7 +34,7 @@ use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
-class Cactus extends Transparent{
+class Cactus extends Transparent implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
@@ -43,9 +44,6 @@ class Cactus extends Transparent{
return true;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
$shrinkSize = 1 / 16;
return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)];
diff --git a/src/block/Cake.php b/src/block/Cake.php
index 073fc62ac..e8c6dc93e 100644
--- a/src/block/Cake.php
+++ b/src/block/Cake.php
@@ -40,9 +40,6 @@ class Cake extends BaseCake{
$w->boundedIntAuto(0, self::MAX_BITES, $this->bites);
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()
diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php
index 380d080c5..1462776c2 100644
--- a/src/block/CakeWithCandle.php
+++ b/src/block/CakeWithCandle.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CandleTrait;
+use pocketmine\block\utils\Lightable;
use pocketmine\entity\Living;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
@@ -31,14 +32,11 @@ 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;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()
diff --git a/src/block/CakeWithDyedCandle.php b/src/block/CakeWithDyedCandle.php
index 0dff358e1..04ab0c6eb 100644
--- a/src/block/CakeWithDyedCandle.php
+++ b/src/block/CakeWithDyedCandle.php
@@ -23,10 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor;
-class CakeWithDyedCandle extends CakeWithCandle{
+class CakeWithDyedCandle extends CakeWithCandle implements Colored{
use ColoredTrait;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
diff --git a/src/block/Campfire.php b/src/block/Campfire.php
index 9f4c42a9c..edfc87ecb 100644
--- a/src/block/Campfire.php
+++ b/src/block/Campfire.php
@@ -25,7 +25,9 @@ namespace pocketmine\block;
use pocketmine\block\inventory\CampfireInventory;
use pocketmine\block\tile\Campfire as TileCampfire;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
+use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\LightableTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\crafting\FurnaceRecipe;
@@ -59,7 +61,7 @@ use function count;
use function min;
use function mt_rand;
-class Campfire extends Transparent{
+class Campfire extends Transparent implements Lightable, HorizontalFacing{
use HorizontalFacingTrait{
HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState;
}
diff --git a/src/block/Candle.php b/src/block/Candle.php
index 7f22641e1..977f13a09 100644
--- a/src/block/Candle.php
+++ b/src/block/Candle.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CandleTrait;
+use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@@ -35,7 +36,7 @@ use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\world\BlockTransaction;
-class Candle extends Transparent{
+class Candle extends Transparent implements Lightable{
use CandleTrait {
describeBlockOnlyState as encodeLitState;
getLightLevel as getBaseLightLevel;
diff --git a/src/block/Carpet.php b/src/block/Carpet.php
index 1ee7240c5..fd8b42a09 100644
--- a/src/block/Carpet.php
+++ b/src/block/Carpet.php
@@ -23,12 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
-class Carpet extends Flowable{
+class Carpet extends Flowable implements Colored{
use ColoredTrait;
use StaticSupportTrait;
@@ -36,9 +37,6 @@ class Carpet extends Flowable{
return true;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
}
diff --git a/src/block/CarvedPumpkin.php b/src/block/CarvedPumpkin.php
index 98f3c2307..cfdb8d49d 100644
--- a/src/block/CarvedPumpkin.php
+++ b/src/block/CarvedPumpkin.php
@@ -24,7 +24,8 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
-class CarvedPumpkin extends Opaque{
+class CarvedPumpkin extends Opaque implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
}
diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php
index daa973507..84d7d3169 100644
--- a/src/block/CaveVines.php
+++ b/src/block/CaveVines.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
@@ -39,7 +40,7 @@ use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\GlowBerriesPickSound;
use function mt_rand;
-class CaveVines extends Flowable{
+class CaveVines extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
diff --git a/src/block/CeilingCenterHangingSign.php b/src/block/CeilingCenterHangingSign.php
new file mode 100644
index 000000000..df6b4e229
--- /dev/null
+++ b/src/block/CeilingCenterHangingSign.php
@@ -0,0 +1,65 @@
+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);
+ }
+
+ protected function getFacingDegrees() : float{
+ return $this->rotation * 22.5;
+ }
+}
diff --git a/src/block/CeilingEdgesHangingSign.php b/src/block/CeilingEdgesHangingSign.php
new file mode 100644
index 000000000..503915fa0
--- /dev/null
+++ b/src/block/CeilingEdgesHangingSign.php
@@ -0,0 +1,79 @@
+facing = Facing::opposite($player->getHorizontalFacing());
+ }
+ if(!$this->canBeSupportedAt($blockReplace)){
+ return false;
+ }
+
+ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+ }
+
+ public function onNearbyBlockChange() : void{
+ if(!$this->canBeSupportedAt($this)){
+ $this->position->getWorld()->useBreakOn($this->position);
+ }
+ }
+
+ private function canBeSupportedAt(Block $block) : bool{
+ $supportBlock = $block->getSide(Facing::UP);
+ return
+ $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL ||
+ (($supportBlock instanceof WallHangingSign || $supportBlock instanceof CeilingEdgesHangingSign) && Facing::axis($supportBlock->getFacing()) === Facing::axis($this->facing));
+ }
+
+ protected function getFacingDegrees() : float{
+ return match($this->facing){
+ Facing::SOUTH => 0,
+ Facing::WEST => 90,
+ Facing::NORTH => 180,
+ Facing::EAST => 270,
+ default => throw new AssumptionFailedError("Invalid facing direction: " . $this->facing),
+ };
+ }
+}
diff --git a/src/block/Chain.php b/src/block/Chain.php
index e9cc2c9be..5ec3b1e2a 100644
--- a/src/block/Chain.php
+++ b/src/block/Chain.php
@@ -23,13 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\PillarRotation;
use pocketmine\block\utils\PillarRotationTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
-final class Chain extends Transparent{
+final class Chain extends Transparent implements PillarRotation{
use PillarRotationTrait;
public function getSupportType(int $facing) : SupportType{
diff --git a/src/block/ChemistryTable.php b/src/block/ChemistryTable.php
index 058e40288..9b5e78f92 100644
--- a/src/block/ChemistryTable.php
+++ b/src/block/ChemistryTable.php
@@ -24,11 +24,12 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
-final class ChemistryTable extends Opaque{
+final class ChemistryTable extends Opaque implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/Chest.php b/src/block/Chest.php
index dca21576a..c0dc09d9e 100644
--- a/src/block/Chest.php
+++ b/src/block/Chest.php
@@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Chest as TileChest;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\ChestPairEvent;
use pocketmine\item\Item;
@@ -33,12 +34,9 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
-class Chest extends Transparent{
+class Chest extends Transparent implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
- /**
- * @return AxisAlignedBB[]
- */
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)];
diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php
index 73c4861bf..cbe22a94b 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;
@@ -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.
*/
diff --git a/src/block/ChorusFlower.php b/src/block/ChorusFlower.php
index cc3c606d9..ef48d5a89 100644
--- a/src/block/ChorusFlower.php
+++ b/src/block/ChorusFlower.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\entity\projectile\Projectile;
@@ -40,7 +41,7 @@ use function array_rand;
use function min;
use function mt_rand;
-final class ChorusFlower extends Flowable{
+final class ChorusFlower extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
diff --git a/src/block/ChorusPlant.php b/src/block/ChorusPlant.php
index 9013f6825..88cf1787c 100644
--- a/src/block/ChorusPlant.php
+++ b/src/block/ChorusPlant.php
@@ -34,11 +34,16 @@ use function mt_rand;
final class ChorusPlant extends Flowable{
use StaticSupportTrait;
+ /**
+ * @var true[]
+ * @phpstan-var array
+ */
+ protected array $connections = [];
+
protected function recalculateCollisionBoxes() : array{
$bb = AxisAlignedBB::one();
- foreach($this->getAllSides() as $facing => $block){
- $id = $block->getTypeId();
- if($id !== BlockTypeIds::END_STONE && $id !== BlockTypeIds::CHORUS_FLOWER && !$block->hasSameTypeId($this)){
+ foreach(Facing::ALL as $facing){
+ if(!isset($this->connections[$facing])){
$bb->trim($facing, 2 / 16);
}
}
@@ -46,6 +51,26 @@ final class ChorusPlant extends Flowable{
return [$bb];
}
+ public function readStateFromWorld() : Block{
+ parent::readStateFromWorld();
+
+ $this->collisionBoxes = null;
+
+ foreach(Facing::ALL as $facing){
+ $block = $this->getSide($facing);
+ if(match($block->getTypeId()){
+ BlockTypeIds::END_STONE, BlockTypeIds::CHORUS_FLOWER, $this->getTypeId() => true,
+ default => false
+ }){
+ $this->connections[$facing] = true;
+ }else{
+ unset($this->connections[$facing]);
+ }
+ }
+
+ return $this;
+ }
+
private function canBeSupportedBy(Block $block) : bool{
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
}
diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php
index 6d8ce1adc..ae09ccb0a 100644
--- a/src/block/CocoaBlock.php
+++ b/src/block/CocoaBlock.php
@@ -23,8 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\WoodType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -39,7 +41,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
-class CocoaBlock extends Flowable{
+class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{
use HorizontalFacingTrait;
use AgeableTrait;
@@ -50,9 +52,6 @@ class CocoaBlock extends Flowable{
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()
diff --git a/src/block/Concrete.php b/src/block/Concrete.php
index fae6f8e2f..6cad11193 100644
--- a/src/block/Concrete.php
+++ b/src/block/Concrete.php
@@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
-class Concrete extends Opaque{
+class Concrete extends Opaque implements Colored{
use ColoredTrait;
}
diff --git a/src/block/ConcretePowder.php b/src/block/ConcretePowder.php
index 59f14bc72..89b53053b 100644
--- a/src/block/ConcretePowder.php
+++ b/src/block/ConcretePowder.php
@@ -24,12 +24,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait;
use pocketmine\math\Facing;
-class ConcretePowder extends Opaque implements Fallable{
+class ConcretePowder extends Opaque implements Fallable, Colored{
use ColoredTrait;
use FallableTrait {
onNearbyBlockChange as protected startFalling;
diff --git a/src/block/CopperBulb.php b/src/block/CopperBulb.php
index 97fc209fe..d0bdedf3c 100644
--- a/src/block/CopperBulb.php
+++ b/src/block/CopperBulb.php
@@ -26,11 +26,13 @@ namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CopperTrait;
+use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\LightableTrait;
+use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
-class CopperBulb extends Opaque implements CopperMaterial{
+class CopperBulb extends Opaque implements CopperMaterial, Lightable, PoweredByRedstone{
use CopperTrait;
use PoweredByRedstoneTrait;
use LightableTrait{
diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php
index 3e7ca8224..c21209998 100644
--- a/src/block/CoralBlock.php
+++ b/src/block/CoralBlock.php
@@ -24,11 +24,12 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\CoralMaterial;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\item\Item;
use function mt_rand;
-final class CoralBlock extends Opaque{
+final class CoralBlock extends Opaque implements CoralMaterial{
use CoralTypeTrait;
public function onNearbyBlockChange() : void{
diff --git a/src/block/Crops.php b/src/block/Crops.php
index e90ac6236..b0488d150 100644
--- a/src/block/Crops.php
+++ b/src/block/Crops.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CropGrowthHelper;
@@ -34,7 +35,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function mt_rand;
-abstract class Crops extends Flowable{
+abstract class Crops extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php
index 4141a2b7e..ed56d2f44 100644
--- a/src/block/DaylightSensor.php
+++ b/src/block/DaylightSensor.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\AnalogRedstoneSignalEmitter;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -36,7 +37,7 @@ use function max;
use function round;
use const M_PI;
-class DaylightSensor extends Transparent{
+class DaylightSensor extends Transparent implements AnalogRedstoneSignalEmitter{
use AnalogRedstoneSignalEmitterTrait;
protected bool $inverted = false;
@@ -62,9 +63,6 @@ class DaylightSensor extends Transparent{
return 300;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 10 / 16)];
}
diff --git a/src/block/Door.php b/src/block/Door.php
index 82ddaab51..2ff53645c 100644
--- a/src/block/Door.php
+++ b/src/block/Door.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -34,7 +35,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\DoorSound;
-class Door extends Transparent{
+class Door extends Transparent implements HorizontalFacing{
use HorizontalFacingTrait;
protected bool $top = false;
@@ -95,9 +96,6 @@ class Door extends Transparent{
return false;
}
- /**
- * @return AxisAlignedBB[]
- */
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)];
diff --git a/src/block/DoublePitcherCrop.php b/src/block/DoublePitcherCrop.php
index 1233ed05d..89d9e98b5 100644
--- a/src/block/DoublePitcherCrop.php
+++ b/src/block/DoublePitcherCrop.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\CropGrowthHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -37,7 +38,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-final class DoublePitcherCrop extends DoublePlant{
+final class DoublePitcherCrop extends DoublePlant implements Ageable{
use AgeableTrait {
describeBlockOnlyState as describeAge;
}
diff --git a/src/block/DyedCandle.php b/src/block/DyedCandle.php
index a495e8d00..57a8b5923 100644
--- a/src/block/DyedCandle.php
+++ b/src/block/DyedCandle.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
-class DyedCandle extends Candle{
+class DyedCandle extends Candle implements Colored{
use ColoredTrait;
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
diff --git a/src/block/DyedShulkerBox.php b/src/block/DyedShulkerBox.php
index 5eae9237e..8be2718a2 100644
--- a/src/block/DyedShulkerBox.php
+++ b/src/block/DyedShulkerBox.php
@@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
-final class DyedShulkerBox extends ShulkerBox{
+final class DyedShulkerBox extends ShulkerBox implements Colored{
use ColoredTrait;
}
diff --git a/src/block/EnchantingTable.php b/src/block/EnchantingTable.php
index 6a6c936b2..53573d064 100644
--- a/src/block/EnchantingTable.php
+++ b/src/block/EnchantingTable.php
@@ -33,9 +33,6 @@ use pocketmine\player\Player;
class EnchantingTable extends Transparent{
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 0.25)];
}
diff --git a/src/block/EndPortalFrame.php b/src/block/EndPortalFrame.php
index 612cf3723..6c4865fdd 100644
--- a/src/block/EndPortalFrame.php
+++ b/src/block/EndPortalFrame.php
@@ -24,11 +24,12 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
-class EndPortalFrame extends Opaque{
+class EndPortalFrame extends Opaque implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
protected bool $eye = false;
@@ -50,9 +51,6 @@ class EndPortalFrame extends Opaque{
return 1;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 3 / 16)];
}
diff --git a/src/block/EndRod.php b/src/block/EndRod.php
index f0b28c26d..e3d439f6a 100644
--- a/src/block/EndRod.php
+++ b/src/block/EndRod.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\item\Item;
use pocketmine\math\Axis;
@@ -32,7 +33,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class EndRod extends Flowable{
+class EndRod extends Flowable implements AnyFacing{
use AnyFacingTrait;
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
@@ -52,9 +53,6 @@ class EndRod extends Flowable{
return 14;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
$myAxis = Facing::axis($this->facing);
diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php
index 9004f7c79..5dec4f3af 100644
--- a/src/block/EnderChest.php
+++ b/src/block/EnderChest.php
@@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\inventory\EnderChestInventory;
use pocketmine\block\tile\EnderChest as TileEnderChest;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
@@ -33,16 +34,13 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
-class EnderChest extends Transparent{
+class EnderChest extends Transparent implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
public function getLightLevel() : int{
return 7;
}
- /**
- * @return AxisAlignedBB[]
- */
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)];
diff --git a/src/block/Farmland.php b/src/block/Farmland.php
index b7a2500a8..83bc34561 100644
--- a/src/block/Farmland.php
+++ b/src/block/Farmland.php
@@ -94,9 +94,6 @@ class Farmland extends Transparent{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)];
}
diff --git a/src/block/Fence.php b/src/block/Fence.php
index 30caaa4cf..52256d9f0 100644
--- a/src/block/Fence.php
+++ b/src/block/Fence.php
@@ -54,13 +54,9 @@ class Fence extends Transparent{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
$inset = 0.5 - $this->getThickness() / 2;
- /** @var AxisAlignedBB[] $bbs */
$bbs = [];
$connectWest = isset($this->connections[Facing::WEST]);
diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php
index 735456449..d8cdfe4e2 100644
--- a/src/block/FenceGate.php
+++ b/src/block/FenceGate.php
@@ -23,8 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@@ -35,7 +37,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\DoorSound;
-class FenceGate extends Transparent{
+class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{
use WoodTypeTrait;
use HorizontalFacingTrait;
@@ -64,9 +66,6 @@ class FenceGate extends Transparent{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return $this->open ? [] : [AxisAlignedBB::one()->extend(Facing::UP, 0.5)->squash(Facing::axis($this->facing), 6 / 16)];
}
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..ff57b5973 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,9 +31,13 @@ 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 getOminousVersion() : Block{
+ return VanillaBlocks::OMINOUS_BANNER()->setRotation($this->rotation);
+ }
+
protected function getSupportingFace() : int{
return Facing::DOWN;
}
diff --git a/src/block/FloorSign.php b/src/block/FloorSign.php
index 5615d15d8..08bc480d8 100644
--- a/src/block/FloorSign.php
+++ b/src/block/FloorSign.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\SignLikeRotation;
use pocketmine\block\utils\SignLikeRotationTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
@@ -30,7 +31,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-final class FloorSign extends BaseSign{
+final class FloorSign extends BaseSign implements SignLikeRotation{
use SignLikeRotationTrait;
protected function getSupportingFace() : int{
@@ -47,4 +48,8 @@ final class FloorSign extends BaseSign{
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
+
+ protected function getFacingDegrees() : float{
+ return $this->rotation * 22.5;
+ }
}
diff --git a/src/block/Flowable.php b/src/block/Flowable.php
index 0328bcd74..355c9caea 100644
--- a/src/block/Flowable.php
+++ b/src/block/Flowable.php
@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
-use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
/**
@@ -46,9 +45,6 @@ abstract class Flowable extends Transparent{
parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [];
}
diff --git a/src/block/FlowerPot.php b/src/block/FlowerPot.php
index fb3e78d82..79fb73b12 100644
--- a/src/block/FlowerPot.php
+++ b/src/block/FlowerPot.php
@@ -83,9 +83,6 @@ class FlowerPot extends Flowable{
return $block->hasTypeTag(BlockTypeTags::POTTABLE_PLANTS);
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
}
diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php
index 3e8592306..046d75811 100644
--- a/src/block/FrostedIce.php
+++ b/src/block/FrostedIce.php
@@ -23,11 +23,12 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use function mt_rand;
-class FrostedIce extends Ice{
+class FrostedIce extends Ice implements Ageable{
use AgeableTrait;
public const MAX_AGE = 3;
diff --git a/src/block/Furnace.php b/src/block/Furnace.php
index 7a64e3cd3..43ab463a6 100644
--- a/src/block/Furnace.php
+++ b/src/block/Furnace.php
@@ -25,6 +25,8 @@ namespace pocketmine\block;
use pocketmine\block\tile\Furnace as TileFurnace;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
+use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\LightableTrait;
use pocketmine\crafting\FurnaceType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -33,7 +35,7 @@ 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;
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 d30e25395..4588d647d 100644
--- a/src/block/GlowLichen.php
+++ b/src/block/GlowLichen.php
@@ -24,11 +24,11 @@ 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;
use pocketmine\item\Item;
-use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
@@ -36,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{
@@ -47,9 +47,6 @@ class GlowLichen extends Transparent{
return false;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [];
}
diff --git a/src/block/GrassPath.php b/src/block/GrassPath.php
index 5b11bd374..ea56e4b95 100644
--- a/src/block/GrassPath.php
+++ b/src/block/GrassPath.php
@@ -29,9 +29,6 @@ use pocketmine\math\Facing;
class GrassPath extends Transparent{
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)];
}
diff --git a/src/block/HayBale.php b/src/block/HayBale.php
index 6fdd2cb63..dacfe92fa 100644
--- a/src/block/HayBale.php
+++ b/src/block/HayBale.php
@@ -23,10 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\PillarRotation;
use pocketmine\block\utils\PillarRotationTrait;
use pocketmine\entity\Entity;
-class HayBale extends Opaque{
+class HayBale extends Opaque implements PillarRotation{
use PillarRotationTrait;
public function getFlameEncouragement() : int{
diff --git a/src/block/Hopper.php b/src/block/Hopper.php
index 0d823674b..4956b668f 100644
--- a/src/block/Hopper.php
+++ b/src/block/Hopper.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\Hopper as TileHopper;
+use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -34,7 +35,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class Hopper extends Transparent{
+class Hopper extends Transparent implements PoweredByRedstone{
use PoweredByRedstoneTrait;
private int $facing = Facing::DOWN;
diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php
index c03806a3b..0fda77758 100644
--- a/src/block/ItemFrame.php
+++ b/src/block/ItemFrame.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\ItemFrame as TileItemFrame;
+use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -39,7 +40,7 @@ use pocketmine\world\sound\ItemFrameRotateItemSound;
use function is_infinite;
use function is_nan;
-class ItemFrame extends Flowable{
+class ItemFrame extends Flowable implements AnyFacing{
use AnyFacingTrait;
public const ROTATIONS = 8;
diff --git a/src/block/Ladder.php b/src/block/Ladder.php
index 58f133f6e..6edaebbf2 100644
--- a/src/block/Ladder.php
+++ b/src/block/Ladder.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
@@ -35,7 +36,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class Ladder extends Transparent{
+class Ladder extends Transparent implements HorizontalFacing{
use HorizontalFacingTrait;
public function hasEntityCollision() : bool{
@@ -58,9 +59,6 @@ class Ladder extends Transparent{
return true;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim($this->facing, 13 / 16)];
}
diff --git a/src/block/Lantern.php b/src/block/Lantern.php
index e9cbcc3fe..302e69fd7 100644
--- a/src/block/Lantern.php
+++ b/src/block/Lantern.php
@@ -59,9 +59,6 @@ class Lantern extends Transparent{
return $this->lightLevel;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [
AxisAlignedBB::one()
diff --git a/src/block/Leaves.php b/src/block/Leaves.php
index 7fe9eae74..847536557 100644
--- a/src/block/Leaves.php
+++ b/src/block/Leaves.php
@@ -157,6 +157,7 @@ class Leaves extends Transparent{
LeavesType::MANGROVE, //TODO: mangrove propagule
LeavesType::AZALEA, LeavesType::FLOWERING_AZALEA => null, //TODO: azalea
LeavesType::CHERRY => null, //TODO: cherry
+ LeavesType::PALE_OAK => null, //TODO: pale oak
})?->asItem();
if($sapling !== null){
$drops[] = $sapling;
diff --git a/src/block/Lectern.php b/src/block/Lectern.php
index 03880b3c5..9ba01c1c5 100644
--- a/src/block/Lectern.php
+++ b/src/block/Lectern.php
@@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Lectern as TileLectern;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@@ -36,7 +37,7 @@ use pocketmine\player\Player;
use pocketmine\world\sound\LecternPlaceBookSound;
use function count;
-class Lectern extends Transparent{
+class Lectern extends Transparent implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
protected int $viewedPage = 0;
diff --git a/src/block/LightningRod.php b/src/block/LightningRod.php
index a0dd50542..9c1971229 100644
--- a/src/block/LightningRod.php
+++ b/src/block/LightningRod.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\item\Item;
use pocketmine\math\Axis;
@@ -32,7 +33,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-final class LightningRod extends Transparent{
+final class LightningRod extends Transparent implements AnyFacing{
use AnyFacingTrait;
protected function recalculateCollisionBoxes() : array{
diff --git a/src/block/Liquid.php b/src/block/Liquid.php
index a37019d65..813d76904 100644
--- a/src/block/Liquid.php
+++ b/src/block/Liquid.php
@@ -30,7 +30,6 @@ use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Item;
-use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\utils\Utils;
@@ -89,9 +88,6 @@ abstract class Liquid extends Transparent{
return false;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [];
}
diff --git a/src/block/Loom.php b/src/block/Loom.php
index d3dd4f3c7..a2b9fc235 100644
--- a/src/block/Loom.php
+++ b/src/block/Loom.php
@@ -25,11 +25,12 @@ namespace pocketmine\block;
use pocketmine\block\inventory\LoomInventory;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
-final class Loom extends Opaque{
+final class Loom extends Opaque implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/MobHead.php b/src/block/MobHead.php
index f4e945841..41e816c55 100644
--- a/src/block/MobHead.php
+++ b/src/block/MobHead.php
@@ -104,9 +104,6 @@ class MobHead extends Flowable{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
$collisionBox = AxisAlignedBB::one()
->contract(0.25, 0, 0.25)
diff --git a/src/block/NetherPortal.php b/src/block/NetherPortal.php
index 6a45fb7a0..1b199c603 100644
--- a/src/block/NetherPortal.php
+++ b/src/block/NetherPortal.php
@@ -28,7 +28,6 @@ use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\item\Item;
use pocketmine\math\Axis;
-use pocketmine\math\AxisAlignedBB;
class NetherPortal extends Transparent{
@@ -62,9 +61,6 @@ class NetherPortal extends Transparent{
return false;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [];
}
diff --git a/src/block/NetherVines.php b/src/block/NetherVines.php
index e8729c00f..67a0b6f94 100644
--- a/src/block/NetherVines.php
+++ b/src/block/NetherVines.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\block\utils\StaticSupportTrait;
@@ -41,7 +42,7 @@ use function mt_rand;
/**
* This class is used for Weeping & Twisting vines, because they have same behaviour
*/
-class NetherVines extends Flowable{
+class NetherVines extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
diff --git a/src/block/NetherWartPlant.php b/src/block/NetherWartPlant.php
index 34e6fd57e..df0609754 100644
--- a/src/block/NetherWartPlant.php
+++ b/src/block/NetherWartPlant.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\FortuneDropHelper;
@@ -31,7 +32,7 @@ use pocketmine\item\Item;
use pocketmine\math\Facing;
use function mt_rand;
-class NetherWartPlant extends Flowable{
+class NetherWartPlant extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
diff --git a/src/block/OminousFloorBanner.php b/src/block/OminousFloorBanner.php
new file mode 100644
index 000000000..240aeccfc
--- /dev/null
+++ b/src/block/OminousFloorBanner.php
@@ -0,0 +1,53 @@
+rotation = self::getRotationFromYaw($player->getLocation()->getYaw());
+ }
+ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+ }
+}
diff --git a/src/block/OminousWallBanner.php b/src/block/OminousWallBanner.php
new file mode 100644
index 000000000..1eb5ba753
--- /dev/null
+++ b/src/block/OminousWallBanner.php
@@ -0,0 +1,49 @@
+facing);
+ }
+
+ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
+ if(Facing::axis($face) === Axis::Y){
+ return false;
+ }
+ $this->facing = $face;
+ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+ }
+}
diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php
index 17bc4c50a..b8b6e248c 100644
--- a/src/block/PinkPetals.php
+++ b/src/block/PinkPetals.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -34,7 +35,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class PinkPetals extends Flowable{
+class PinkPetals extends Flowable implements HorizontalFacing{
use HorizontalFacingTrait;
use StaticSupportTrait {
canBePlacedAt as supportedWhenPlacedAt;
diff --git a/src/block/PitcherCrop.php b/src/block/PitcherCrop.php
index d41aed284..1c771bb8a 100644
--- a/src/block/PitcherCrop.php
+++ b/src/block/PitcherCrop.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CropGrowthHelper;
@@ -38,7 +39,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-final class PitcherCrop extends Flowable{
+final class PitcherCrop extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
diff --git a/src/block/Planks.php b/src/block/Planks.php
index 1074f8adf..ad7956361 100644
--- a/src/block/Planks.php
+++ b/src/block/Planks.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
-class Planks extends Opaque{
+class Planks extends Opaque implements WoodMaterial{
use WoodTypeTrait;
public function getFuelTime() : int{
diff --git a/src/block/PoweredRail.php b/src/block/PoweredRail.php
index 25d6dc9e3..321b19a46 100644
--- a/src/block/PoweredRail.php
+++ b/src/block/PoweredRail.php
@@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\RailPoweredByRedstoneTrait;
-class PoweredRail extends StraightOnlyRail{
+class PoweredRail extends StraightOnlyRail implements PoweredByRedstone{
use RailPoweredByRedstoneTrait;
}
diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php
index ee63a77a9..88050b85a 100644
--- a/src/block/RedstoneComparator.php
+++ b/src/block/RedstoneComparator.php
@@ -24,8 +24,11 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\Comparator;
+use pocketmine\block\utils\AnalogRedstoneSignalEmitter;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
+use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
@@ -38,7 +41,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function assert;
-class RedstoneComparator extends Flowable{
+class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter, PoweredByRedstone, HorizontalFacing{
use HorizontalFacingTrait;
use AnalogRedstoneSignalEmitterTrait;
use PoweredByRedstoneTrait;
@@ -79,9 +82,6 @@ class RedstoneComparator extends Flowable{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)];
}
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..3477a3519 100644
--- a/src/block/RedstoneOre.php
+++ b/src/block/RedstoneOre.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\FortuneDropHelper;
+use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\LightableTrait;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
@@ -31,7 +32,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function mt_rand;
-class RedstoneOre extends Opaque{
+class RedstoneOre extends Opaque implements Lightable{
use LightableTrait;
public function getLightLevel() : int{
diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php
index 7e6e73da8..4059ff1cc 100644
--- a/src/block/RedstoneRepeater.php
+++ b/src/block/RedstoneRepeater.php
@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
+use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
@@ -35,7 +37,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class RedstoneRepeater extends Flowable{
+class RedstoneRepeater extends Flowable implements PoweredByRedstone, HorizontalFacing{
use HorizontalFacingTrait;
use PoweredByRedstoneTrait;
use StaticSupportTrait;
@@ -62,9 +64,6 @@ class RedstoneRepeater extends Flowable{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)];
}
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
new file mode 100644
index 000000000..a56a386d4
--- /dev/null
+++ b/src/block/ResinClump.php
@@ -0,0 +1,55 @@
+faces : [];
+ }
+
+ protected function recalculateCollisionBoxes() : array{
+ return [];
+ }
+}
diff --git a/src/block/RespawnAnchor.php b/src/block/RespawnAnchor.php
new file mode 100644
index 000000000..e19ea8f6d
--- /dev/null
+++ b/src/block/RespawnAnchor.php
@@ -0,0 +1,123 @@
+boundedIntAuto(self::MIN_CHARGES, self::MAX_CHARGES, $this->charges);
+ }
+
+ public function getCharges() : int{
+ return $this->charges;
+ }
+
+ /** @return $this */
+ public function setCharges(int $charges) : self{
+ if($charges < self::MIN_CHARGES || $charges > self::MAX_CHARGES){
+ throw new \InvalidArgumentException("Charges must be between " . self::MIN_CHARGES . " and " . self::MAX_CHARGES . ", given: $charges");
+ }
+ $this->charges = $charges;
+ return $this;
+ }
+
+ public function getLightLevel() : int{
+ return $this->charges > 0 ? ($this->charges * 4) - 1 : 0;
+ }
+
+ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
+ if($item->getTypeId() === ItemTypeIds::fromBlockTypeId(BlockTypeIds::GLOWSTONE) && $this->charges < self::MAX_CHARGES){
+ $this->position->getWorld()->setBlock($this->position, $this->setCharges($this->charges + 1));
+ $this->position->getWorld()->addSound($this->position, new RespawnAnchorChargeSound());
+ return true;
+ }
+
+ if($this->charges > self::MIN_CHARGES){
+ if($player === null){
+ return false;
+ }
+
+ $ev = new PlayerRespawnAnchorUseEvent($player, $this, PlayerRespawnAnchorUseEvent::ACTION_EXPLODE);
+ $ev->call();
+ if($ev->isCancelled()){
+ return false;
+ }
+
+ switch($ev->getAction()){
+ case PlayerRespawnAnchorUseEvent::ACTION_EXPLODE:
+ $this->explode($player);
+ return 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/RuntimeBlockStateRegistry.php b/src/block/RuntimeBlockStateRegistry.php
index a8ece7722..a458b3368 100644
--- a/src/block/RuntimeBlockStateRegistry.php
+++ b/src/block/RuntimeBlockStateRegistry.php
@@ -28,6 +28,7 @@ use pocketmine\block\BlockIdentifier as BID;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use pocketmine\world\light\LightUpdate;
+use function count;
use function min;
/**
@@ -40,6 +41,11 @@ use function min;
class RuntimeBlockStateRegistry{
use SingletonTrait;
+ public const COLLISION_CUSTOM = 0;
+ public const COLLISION_CUBE = 1;
+ public const COLLISION_NONE = 2;
+ public const COLLISION_MAY_OVERFLOW = 3;
+
/**
* @var Block[]
* @phpstan-var array
@@ -74,6 +80,13 @@ class RuntimeBlockStateRegistry{
*/
public array $blastResistance = [];
+ /**
+ * Map of state ID -> useful AABB info to avoid unnecessary block allocations
+ * @var int[]
+ * @phpstan-var array
+ */
+ public array $collisionInfo = [];
+
public function __construct(){
foreach(VanillaBlocks::getAll() as $block){
$this->register($block);
@@ -100,6 +113,70 @@ class RuntimeBlockStateRegistry{
}
}
+ /**
+ * Checks if the given class method overrides a method in Block.
+ * Used to determine if a block might need to disable fast path optimizations.
+ *
+ * @phpstan-param anyClosure $closure
+ */
+ private static function overridesBlockMethod(\Closure $closure) : bool{
+ $declarer = (new \ReflectionFunction($closure))->getClosureScopeClass();
+ return $declarer !== null && $declarer->getName() !== Block::class;
+ }
+
+ /**
+ * A big ugly hack to set up fast paths for handling collisions on blocks with common shapes.
+ * The information returned here is stored in RuntimeBlockStateRegistry->collisionInfo, and is used during entity
+ * collision box calculations to avoid complex logic and unnecessary block object allocations.
+ * This hack allows significant performance improvements.
+ *
+ * TODO: We'll want to redesign block collision box handling and block shapes in the future, but that's a job for a
+ * major version. For now, this hack nets major performance wins.
+ */
+ private static function calculateCollisionInfo(Block $block) : int{
+ if(
+ self::overridesBlockMethod($block->getModelPositionOffset(...)) ||
+ self::overridesBlockMethod($block->readStateFromWorld(...))
+ ){
+ //getModelPositionOffset() might cause AABBs to shift outside the cell
+ //readStateFromWorld() might cause overflow in ways we can't predict just by looking at known states
+ //TODO: excluding overriders of readStateFromWorld() also excludes blocks with tiles that don't do anything
+ //weird with their AABBs, but for now this is the best we can do.
+ return self::COLLISION_MAY_OVERFLOW;
+ }
+
+ //TODO: this could blow up if any recalculateCollisionBoxes() uses the world
+ //it shouldn't, but that doesn't mean that custom blocks won't...
+ $boxes = $block->getCollisionBoxes();
+ if(count($boxes) === 0){
+ return self::COLLISION_NONE;
+ }
+
+ if(
+ count($boxes) === 1 &&
+ $boxes[0]->minX === 0.0 &&
+ $boxes[0]->minY === 0.0 &&
+ $boxes[0]->minZ === 0.0 &&
+ $boxes[0]->maxX === 1.0 &&
+ $boxes[0]->maxY === 1.0 &&
+ $boxes[0]->maxZ === 1.0
+ ){
+ return self::COLLISION_CUBE;
+ }
+
+ foreach($boxes as $box){
+ if(
+ $box->minX < 0 || $box->maxX > 1 ||
+ $box->minY < 0 || $box->maxY > 1 ||
+ $box->minZ < 0 || $box->maxZ > 1
+ ){
+ return self::COLLISION_MAY_OVERFLOW;
+ }
+ }
+
+ return self::COLLISION_CUSTOM;
+ }
+
private function fillStaticArrays(int $index, Block $block) : void{
$fullId = $block->getStateId();
if($index !== $fullId){
@@ -112,6 +189,8 @@ class RuntimeBlockStateRegistry{
if($block->blocksDirectSkyLight()){
$this->blocksDirectSkyLight[$index] = true;
}
+
+ $this->collisionInfo[$index] = self::calculateCollisionInfo($block);
}
}
@@ -130,6 +209,10 @@ class RuntimeBlockStateRegistry{
return $block;
}
+ public function hasStateId(int $stateId) : bool{
+ return isset($this->fullList[$stateId]);
+ }
+
/**
* @return Block[]
* @phpstan-return array
diff --git a/src/block/SeaPickle.php b/src/block/SeaPickle.php
index 627af9bac..34f5c3e9e 100644
--- a/src/block/SeaPickle.php
+++ b/src/block/SeaPickle.php
@@ -26,7 +26,6 @@ namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
-use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
@@ -70,9 +69,6 @@ class SeaPickle extends Transparent{
return $this->underwater ? ($this->count + 1) * 3 : 0;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [];
}
diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php
index d557401ee..52a3b83cd 100644
--- a/src/block/ShulkerBox.php
+++ b/src/block/ShulkerBox.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
+use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -32,7 +33,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class ShulkerBox extends Opaque{
+class ShulkerBox extends Opaque implements AnyFacing{
use AnyFacingTrait;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
diff --git a/src/block/SimplePillar.php b/src/block/SimplePillar.php
index 98c89f89c..6a7af96ed 100644
--- a/src/block/SimplePillar.php
+++ b/src/block/SimplePillar.php
@@ -23,12 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\PillarRotation;
use pocketmine\block\utils\PillarRotationTrait;
/**
* @internal This class provides a general base for pillar-like blocks. It **should not** be used for contract binding
* in APIs, because not all pillar-like blocks extend this class.
*/
-class SimplePillar extends Opaque{
+class SimplePillar extends Opaque implements PillarRotation{
use PillarRotationTrait;
}
diff --git a/src/block/Slab.php b/src/block/Slab.php
index 6000bec39..2bbb7528c 100644
--- a/src/block/Slab.php
+++ b/src/block/Slab.php
@@ -93,9 +93,6 @@ class Slab extends Transparent{
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
if($this->slabType === SlabType::DOUBLE){
return [AxisAlignedBB::one()];
diff --git a/src/block/SmallDripleaf.php b/src/block/SmallDripleaf.php
index d192e43db..846be5c93 100644
--- a/src/block/SmallDripleaf.php
+++ b/src/block/SmallDripleaf.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -36,7 +37,7 @@ use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
use function mt_rand;
-class SmallDripleaf extends Transparent{
+class SmallDripleaf extends Transparent implements HorizontalFacing{
use HorizontalFacingTrait;
protected bool $top = false;
diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php
index cca8424a9..8549f0b31 100644
--- a/src/block/SnowLayer.php
+++ b/src/block/SnowLayer.php
@@ -65,9 +65,6 @@ class SnowLayer extends Flowable implements Fallable{
return $this->layers < self::MAX_LAYERS;
}
- /**
- * @return AxisAlignedBB[]
- */
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)];
diff --git a/src/block/SoulSand.php b/src/block/SoulSand.php
index 2c6453b6c..e1285d095 100644
--- a/src/block/SoulSand.php
+++ b/src/block/SoulSand.php
@@ -28,9 +28,6 @@ use pocketmine\math\Facing;
class SoulSand extends Opaque{
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(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..56101de13 100644
--- a/src/block/Stair.php
+++ b/src/block/Stair.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\StairShape;
use pocketmine\block\utils\SupportType;
@@ -35,7 +36,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class Stair extends Transparent{
+class Stair extends Transparent implements HorizontalFacing{
use HorizontalFacingTrait;
protected bool $upsideDown = false;
diff --git a/src/block/Stem.php b/src/block/Stem.php
index 2ac95aa3f..2b6f2150c 100644
--- a/src/block/Stem.php
+++ b/src/block/Stem.php
@@ -25,11 +25,12 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CropGrowthHelper;
+use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
+use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use function array_rand;
-use function mt_rand;
abstract class Stem extends Crops{
protected int $facing = Facing::UP;
@@ -90,8 +91,10 @@ abstract class Stem extends Crops{
}
public function getDropsForCompatibleTool(Item $item) : array{
+ //TODO: bit annoying we have to pass an Item instance here
+ //this should not be affected by Fortune, but still follows a binomial distribution
return [
- $this->asItem()->setCount(mt_rand(0, 2))
+ $this->asItem()->setCount(FortuneDropHelper::binomial(VanillaItems::AIR(), 0, chance: ($this->age + 1) / 15))
];
}
}
diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php
index 30c19d25d..0fd259326 100644
--- a/src/block/Stonecutter.php
+++ b/src/block/Stonecutter.php
@@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\inventory\StonecutterInventory;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
@@ -32,7 +33,7 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
-class Stonecutter extends Transparent{
+class Stonecutter extends Transparent implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php
index 2da2dc9b9..0874413c5 100644
--- a/src/block/Sugarcane.php
+++ b/src/block/Sugarcane.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
@@ -34,7 +35,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
-class Sugarcane extends Flowable{
+class Sugarcane extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait {
onNearbyBlockChange as onSupportBlockChange;
diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php
index eabdde118..881b7359f 100644
--- a/src/block/SweetBerryBush.php
+++ b/src/block/SweetBerryBush.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\FortuneDropHelper;
@@ -39,7 +40,7 @@ use pocketmine\player\Player;
use pocketmine\world\sound\SweetBerriesPickSound;
use function mt_rand;
-class SweetBerryBush extends Flowable{
+class SweetBerryBush extends Flowable implements Ageable{
use AgeableTrait;
use StaticSupportTrait;
diff --git a/src/block/Thin.php b/src/block/Thin.php
index dde2d7d84..82010697a 100644
--- a/src/block/Thin.php
+++ b/src/block/Thin.php
@@ -56,7 +56,6 @@ class Thin extends Transparent{
protected function recalculateCollisionBoxes() : array{
$inset = 7 / 16;
- /** @var AxisAlignedBB[] $bbs */
$bbs = [];
if(isset($this->connections[Facing::WEST]) || isset($this->connections[Facing::EAST])){
diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php
index 20b6af2ab..5e8a7dc3f 100644
--- a/src/block/Trapdoor.php
+++ b/src/block/Trapdoor.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -34,7 +35,7 @@ use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\DoorSound;
-class Trapdoor extends Transparent{
+class Trapdoor extends Transparent implements HorizontalFacing{
use HorizontalFacingTrait;
protected bool $open = false;
@@ -62,9 +63,6 @@ class Trapdoor extends Transparent{
return $this;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)];
}
diff --git a/src/block/TripwireHook.php b/src/block/TripwireHook.php
index 325819825..4e40cf62e 100644
--- a/src/block/TripwireHook.php
+++ b/src/block/TripwireHook.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@@ -32,7 +33,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-class TripwireHook extends Flowable{
+class TripwireHook extends Flowable implements HorizontalFacing{
use HorizontalFacingTrait;
protected bool $connected = false;
diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php
index 54cf90a0c..36074c606 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;
@@ -54,6 +55,7 @@ use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner;
use pocketmine\block\tile\NormalFurnace as TileNormalFurnace;
use pocketmine\block\tile\Note as TileNote;
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
+use pocketmine\block\tile\Sign as TileSign;
use pocketmine\block\tile\Smoker as TileSmoker;
use pocketmine\block\tile\Tile;
use pocketmine\block\utils\AmethystTrait;
@@ -79,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()
@@ -91,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()
@@ -121,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()
@@ -133,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()
@@ -169,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()
@@ -180,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()
@@ -190,6 +201,7 @@ use function strtolower;
* @method static Opaque CHISELED_POLISHED_BLACKSTONE()
* @method static SimplePillar CHISELED_QUARTZ()
* @method static Opaque CHISELED_RED_SANDSTONE()
+ * @method static Opaque CHISELED_RESIN_BRICKS()
* @method static Opaque CHISELED_SANDSTONE()
* @method static Opaque CHISELED_STONE_BRICKS()
* @method static Opaque CHISELED_TUFF()
@@ -229,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()
@@ -241,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()
@@ -252,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()
@@ -264,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()
@@ -486,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()
@@ -498,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()
@@ -520,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()
@@ -532,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()
@@ -570,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()
@@ -582,13 +608,33 @@ 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()
+ * @method static Leaves PALE_OAK_LEAVES()
+ * @method static Wood PALE_OAK_LOG()
+ * @method static Planks PALE_OAK_PLANKS()
+ * @method static WoodenPressurePlate PALE_OAK_PRESSURE_PLATE()
+ * @method static FloorSign PALE_OAK_SIGN()
+ * @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()
* @method static PinkPetals PINK_PETALS()
* @method static Flower PINK_TULIP()
@@ -672,6 +718,13 @@ use function strtolower;
* @method static Flower RED_TULIP()
* @method static Opaque REINFORCED_DEEPSLATE()
* @method static Reserved6 RESERVED6()
+ * @method static Opaque RESIN()
+ * @method static Opaque RESIN_BRICKS()
+ * @method static Slab RESIN_BRICK_SLAB()
+ * @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()
@@ -710,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()
@@ -722,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()
@@ -765,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()
@@ -777,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()
@@ -837,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;
@@ -845,24 +904,26 @@ 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, ToolTier::WOOD))), TileBell::class);
+ self::register("bell", fn(BID $id) => new Bell($id, "Bell", new Info(BreakInfo::pickaxe(5.0))), TileBell::class);
self::register("blue_ice", fn(BID $id) => new BlueIce($id, "Blue Ice", new Info(BreakInfo::pickaxe(2.8))));
self::register("bone_block", fn(BID $id) => new BoneBlock($id, "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD))));
self::register("bookshelf", fn(BID $id) => new Bookshelf($id, "Bookshelf", new Info(BreakInfo::axe(1.5))));
self::register("chiseled_bookshelf", fn(BID $id) => new ChiseledBookshelf($id, "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5))), TileChiseledBookshelf::class);
- self::register("brewing_stand", fn(BID $id) => new BrewingStand($id, "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))), TileBrewingStand::class);
+ self::register("brewing_stand", fn(BID $id) => new BrewingStand($id, "Brewing Stand", new Info(BreakInfo::pickaxe(0.5))), TileBrewingStand::class);
$bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
self::register("brick_stairs", fn(BID $id) => new Stair($id, "Brick Stairs", $bricksBreakInfo));
@@ -891,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));
@@ -908,19 +969,19 @@ 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));
- self::register("ender_chest", fn(BID $id) => new EnderChest($id, "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD, 3000.0))), TileEnderChest::class);
+ self::register("ender_chest", fn(BID $id) => new EnderChest($id, "Ender Chest", new Info(BreakInfo::pickaxe(22.5, blastResistance: 3000.0))), TileEnderChest::class);
self::register("farmland", fn(BID $id) => new Farmland($id, "Farmland", new Info(BreakInfo::shovel(0.6), [Tags::DIRT])));
self::register("fire", fn(BID $id) => new Fire($id, "Fire Block", new Info(BreakInfo::instant(), [Tags::FIRE])));
@@ -940,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);
@@ -948,52 +1009,50 @@ 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));
self::register("iron_bars", fn(BID $id) => new Thin($id, "Iron Bars", $ironBreakInfo));
- $ironDoorBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 25.0));
- self::register("iron_door", fn(BID $id) => new Door($id, "Iron Door", $ironDoorBreakInfo));
- self::register("iron_trapdoor", fn(BID $id) => new Trapdoor($id, "Iron Trapdoor", $ironDoorBreakInfo));
+
+ self::register("iron_door", fn(BID $id) => new Door($id, "Iron Door", new Info(BreakInfo::pickaxe(5.0))));
+ self::register("iron_trapdoor", fn(BID $id) => new Trapdoor($id, "Iron Trapdoor", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))));
$itemFrameInfo = new Info(new BreakInfo(0.25));
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, ToolTier::WOOD));
+ $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))));
@@ -1043,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])));
@@ -1105,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));
@@ -1122,9 +1182,8 @@ final class VanillaBlocks{
self::register("mossy_stone_brick_stairs", fn(BID $id) => new Stair($id, "Mossy Stone Brick Stairs", $stoneBreakInfo));
self::register("stone_button", fn(BID $id) => new StoneButton($id, "Stone Button", new Info(BreakInfo::pickaxe(0.5))));
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, ToolTier::WOOD))));
+ 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));
@@ -1135,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())));
@@ -1178,7 +1240,7 @@ final class VanillaBlocks{
self::register("water", fn(BID $id) => new Water($id, "Water", new Info(BreakInfo::indestructible(500.0))));
self::register("lily_pad", fn(BID $id) => new WaterLily($id, "Lily Pad", new Info(BreakInfo::instant())));
- $weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD));
+ $weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5));
self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlateHeavy(
$id,
"Weighted Pressure Plate Heavy",
@@ -1215,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))));
@@ -1250,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();
@@ -1298,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 [];
}
@@ -1311,6 +1378,7 @@ final class VanillaBlocks{
self::registerBlocksR17();
self::registerBlocksR18();
self::registerMudBlocks();
+ self::registerResinBlocks();
self::registerTuffBlocks();
self::registerCraftingTables();
@@ -1323,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));
@@ -1358,9 +1427,27 @@ final class VanillaBlocks{
WoodType::CRIMSON => VanillaItems::CRIMSON_SIGN(...),
WoodType::WARPED => VanillaItems::WARPED_SIGN(...),
WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...),
+ WoodType::PALE_OAK => VanillaItems::PALE_OAK_SIGN(...),
};
- self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem));
- self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem));
+ 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);
}
}
@@ -1513,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)));
@@ -1557,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; }
});
@@ -1578,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, ToolTier::WOOD)), 20));
+ 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" : "");
@@ -1598,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; }
@@ -1617,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{
@@ -1635,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()];
@@ -1647,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));
@@ -1678,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));
@@ -1689,9 +1777,8 @@ final class VanillaBlocks{
self::register("cut_copper_stairs", fn(BID $id) => new CopperStairs($id, "Cut Copper Stairs", $copperBreakInfo));
self::register("copper_bulb", fn(BID $id) => new CopperBulb($id, "Copper Bulb", $copperBreakInfo));
- $copperDoorBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0));
- self::register("copper_door", fn(BID $id) => new CopperDoor($id, "Copper Door", $copperDoorBreakInfo));
- self::register("copper_trapdoor", fn(BID $id) => new CopperTrapdoor($id, "Copper Trapdoor", $copperDoorBreakInfo));
+ self::register("copper_door", fn(BID $id) => new CopperDoor($id, "Copper Door", new Info(BreakInfo::pickaxe(3.0, blastResistance: 30.0))));
+ self::register("copper_trapdoor", fn(BID $id) => new CopperTrapdoor($id, "Copper Trapdoor", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0))));
$candleBreakInfo = new Info(new BreakInfo(0.1));
self::register("candle", fn(BID $id) => new Candle($id, "Candle", $candleBreakInfo));
@@ -1707,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{
@@ -1719,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));
@@ -1727,6 +1814,18 @@ final class VanillaBlocks{
self::register("mud_brick_wall", fn(BID $id) => new Wall($id, "Mud Brick Wall", $mudBricksBreakInfo));
}
+ private static function registerResinBlocks() : void{
+ 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, 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));
+ self::register("resin_bricks", fn(BID $id) => new Opaque($id, "Resin Bricks", $resinBricksInfo));
+ self::register("chiseled_resin_bricks", fn(BID $id) => new Opaque($id, "Chiseled Resin Bricks", $resinBricksInfo));
+ }
+
private static function registerTuffBlocks() : void{
$tuffBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0));
diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php
index 5182fe9f8..b631e0c81 100644
--- a/src/block/WallBanner.php
+++ b/src/block/WallBanner.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\item\Item;
use pocketmine\math\Axis;
@@ -31,9 +32,13 @@ 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 getOminousVersion() : Block{
+ return VanillaBlocks::OMINOUS_WALL_BANNER()->setFacing($this->facing);
+ }
+
protected function getSupportingFace() : int{
return Facing::opposite($this->facing);
}
diff --git a/src/block/WallCoralFan.php b/src/block/WallCoralFan.php
index f9dece1cd..67745a537 100644
--- a/src/block/WallCoralFan.php
+++ b/src/block/WallCoralFan.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@@ -33,7 +34,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
-final class WallCoralFan extends BaseCoral{
+final class WallCoralFan extends BaseCoral implements HorizontalFacing{
use HorizontalFacingTrait;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php
new file mode 100644
index 000000000..6d4cfb95e
--- /dev/null
+++ b/src/block/WallHangingSign.php
@@ -0,0 +1,92 @@
+facing, 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()->trim(Facing::DOWN, 7 / 8)->squash(Facing::axis($this->facing), 3 / 4)];
+ }
+
+ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
+ if($player === null){
+ return false;
+ }
+ $attachFace = Facing::axis($face) === Axis::Y ? Facing::rotateY($player->getHorizontalFacing(), clockwise: true) : $face;
+
+ if($this->canBeSupportedAt($blockReplace->getSide($attachFace), $attachFace)){
+ $direction = $attachFace;
+ }elseif($this->canBeSupportedAt($blockReplace->getSide($opposite = Facing::opposite($attachFace)), $opposite)){
+ $direction = $opposite;
+ }else{
+ return false;
+ }
+
+ $this->facing = Facing::rotateY(Facing::opposite($direction), clockwise: true);
+ //the front should always face the player if possible
+ if($this->facing === $player->getHorizontalFacing()){
+ $this->facing = Facing::opposite($this->facing);
+ }
+
+ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+ }
+
+ private function canBeSupportedAt(Block $block, int $face) : bool{
+ return
+ ($block instanceof WallHangingSign && Facing::axis(Facing::rotateY($block->getFacing(), clockwise: true)) === Facing::axis($face)) ||
+ $block->getSupportType(Facing::opposite($face)) === SupportType::FULL;
+ }
+
+ protected function getFacingDegrees() : float{
+ return match($this->facing){
+ Facing::SOUTH => 0,
+ Facing::WEST => 90,
+ Facing::NORTH => 180,
+ Facing::EAST => 270,
+ default => throw new AssumptionFailedError("Invalid facing direction: " . $this->facing),
+ };
+ }
+}
diff --git a/src/block/WallSign.php b/src/block/WallSign.php
index 07826eae7..40e1ba458 100644
--- a/src/block/WallSign.php
+++ b/src/block/WallSign.php
@@ -23,15 +23,17 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
+use pocketmine\utils\AssumptionFailedError;
use pocketmine\world\BlockTransaction;
-final class WallSign extends BaseSign{
+final class WallSign extends BaseSign implements HorizontalFacing{
use HorizontalFacingTrait;
protected function getSupportingFace() : int{
@@ -45,4 +47,25 @@ final class WallSign extends BaseSign{
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
+
+ protected function getHitboxCenter() : Vector3{
+ [$xOffset, $zOffset] = match($this->facing){
+ Facing::NORTH => [0, 15 / 16],
+ Facing::SOUTH => [0, 1 / 16],
+ Facing::WEST => [15 / 16, 0],
+ Facing::EAST => [1 / 16, 0],
+ default => throw new AssumptionFailedError("Invalid facing direction: " . $this->facing),
+ };
+ return $this->position->add($xOffset, 0.5, $zOffset);
+ }
+
+ protected function getFacingDegrees() : float{
+ return match($this->facing){
+ Facing::SOUTH => 0,
+ Facing::WEST => 90,
+ Facing::NORTH => 180,
+ Facing::EAST => 270,
+ default => throw new AssumptionFailedError("Invalid facing direction: " . $this->facing),
+ };
+ }
}
diff --git a/src/block/Water.php b/src/block/Water.php
index b711ab5a1..44759783a 100644
--- a/src/block/Water.php
+++ b/src/block/Water.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
+use pocketmine\event\entity\EntityExtinguishEvent;
use pocketmine\world\sound\BucketEmptyWaterSound;
use pocketmine\world\sound\BucketFillWaterSound;
use pocketmine\world\sound\Sound;
@@ -53,7 +54,7 @@ class Water extends Liquid{
public function onEntityInside(Entity $entity) : bool{
$entity->resetFallDistance();
if($entity->isOnFire()){
- $entity->extinguish();
+ $entity->extinguish(EntityExtinguishEvent::CAUSE_WATER);
}
return true;
}
diff --git a/src/block/WaterCauldron.php b/src/block/WaterCauldron.php
index e470aa6cb..8129f2960 100644
--- a/src/block/WaterCauldron.php
+++ b/src/block/WaterCauldron.php
@@ -27,6 +27,7 @@ use pocketmine\block\tile\Cauldron as TileCauldron;
use pocketmine\block\utils\DyeColor;
use pocketmine\color\Color;
use pocketmine\entity\Entity;
+use pocketmine\event\entity\EntityExtinguishEvent;
use pocketmine\item\Armor;
use pocketmine\item\Banner;
use pocketmine\item\Dye;
@@ -183,7 +184,7 @@ final class WaterCauldron extends FillableCauldron{
public function onEntityInside(Entity $entity) : bool{
if($entity->isOnFire()){
- $entity->extinguish();
+ $entity->extinguish(EntityExtinguishEvent::CAUSE_WATER_CAULDRON);
//TODO: particles
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::ENTITY_EXTINGUISH_USE_AMOUNT));
diff --git a/src/block/WaterLily.php b/src/block/WaterLily.php
index 5dfb0d74a..b04b1baed 100644
--- a/src/block/WaterLily.php
+++ b/src/block/WaterLily.php
@@ -33,9 +33,6 @@ class WaterLily extends Flowable{
canBePlacedAt as supportedWhenPlacedAt;
}
- /**
- * @return AxisAlignedBB[]
- */
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)];
}
diff --git a/src/block/WeightedPressurePlate.php b/src/block/WeightedPressurePlate.php
index 726b31f6b..065f8b2c8 100644
--- a/src/block/WeightedPressurePlate.php
+++ b/src/block/WeightedPressurePlate.php
@@ -23,13 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\AnalogRedstoneSignalEmitter;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use function ceil;
use function count;
use function max;
use function min;
-class WeightedPressurePlate extends PressurePlate{
+class WeightedPressurePlate extends PressurePlate implements AnalogRedstoneSignalEmitter{
use AnalogRedstoneSignalEmitterTrait;
private readonly float $signalStrengthFactor;
diff --git a/src/block/Wood.php b/src/block/Wood.php
index 127533b98..7aa667bc8 100644
--- a/src/block/Wood.php
+++ b/src/block/Wood.php
@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\PillarRotation;
use pocketmine\block\utils\PillarRotationTrait;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Axe;
@@ -32,7 +34,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\ItemUseOnBlockSound;
-class Wood extends Opaque{
+class Wood extends Opaque implements PillarRotation, WoodMaterial{
use PillarRotationTrait;
use WoodTypeTrait;
diff --git a/src/block/WoodenButton.php b/src/block/WoodenButton.php
index 7ba8a7af0..4eed3a50e 100644
--- a/src/block/WoodenButton.php
+++ b/src/block/WoodenButton.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
-class WoodenButton extends Button{
+class WoodenButton extends Button implements WoodMaterial{
use WoodTypeTrait;
protected function getActivationTime() : int{
diff --git a/src/block/WoodenDoor.php b/src/block/WoodenDoor.php
index 96f349e49..b7918a820 100644
--- a/src/block/WoodenDoor.php
+++ b/src/block/WoodenDoor.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
-class WoodenDoor extends Door{
+class WoodenDoor extends Door implements WoodMaterial{
use WoodTypeTrait;
public function getFuelTime() : int{
diff --git a/src/block/WoodenFence.php b/src/block/WoodenFence.php
index c12043a86..73c8da44d 100644
--- a/src/block/WoodenFence.php
+++ b/src/block/WoodenFence.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
-class WoodenFence extends Fence{
+class WoodenFence extends Fence implements WoodMaterial{
use WoodTypeTrait;
public function getFuelTime() : int{
diff --git a/src/block/WoodenPressurePlate.php b/src/block/WoodenPressurePlate.php
index a629c2f1c..6d638788b 100644
--- a/src/block/WoodenPressurePlate.php
+++ b/src/block/WoodenPressurePlate.php
@@ -23,10 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodType;
use pocketmine\block\utils\WoodTypeTrait;
-class WoodenPressurePlate extends SimplePressurePlate{
+class WoodenPressurePlate extends SimplePressurePlate implements WoodMaterial{
use WoodTypeTrait;
public function __construct(
diff --git a/src/block/WoodenSlab.php b/src/block/WoodenSlab.php
index b388a36ea..3ed606c64 100644
--- a/src/block/WoodenSlab.php
+++ b/src/block/WoodenSlab.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
-class WoodenSlab extends Slab{
+class WoodenSlab extends Slab implements WoodMaterial{
use WoodTypeTrait;
public function getFuelTime() : int{
diff --git a/src/block/WoodenStairs.php b/src/block/WoodenStairs.php
index 0d9ba62ce..8da3fceb6 100644
--- a/src/block/WoodenStairs.php
+++ b/src/block/WoodenStairs.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
-class WoodenStairs extends Stair{
+class WoodenStairs extends Stair implements WoodMaterial{
use WoodTypeTrait;
public function getFuelTime() : int{
diff --git a/src/block/WoodenTrapdoor.php b/src/block/WoodenTrapdoor.php
index c0a6623a1..2b796df52 100644
--- a/src/block/WoodenTrapdoor.php
+++ b/src/block/WoodenTrapdoor.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\WoodMaterial;
use pocketmine\block\utils\WoodTypeTrait;
-class WoodenTrapdoor extends Trapdoor{
+class WoodenTrapdoor extends Trapdoor implements WoodMaterial{
use WoodTypeTrait;
public function getFuelTime() : int{
diff --git a/src/block/Wool.php b/src/block/Wool.php
index 0b008ac04..d47c27d27 100644
--- a/src/block/Wool.php
+++ b/src/block/Wool.php
@@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
+use pocketmine\block\utils\Colored;
use pocketmine\block\utils\ColoredTrait;
-class Wool extends Opaque{
+class Wool extends Opaque implements Colored{
use ColoredTrait;
public function getFlameEncouragement() : int{
diff --git a/src/block/tile/Banner.php b/src/block/tile/Banner.php
index 08a560707..ac19d9835 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(
@@ -63,9 +69,8 @@ class Banner extends Spawnable{
$patternTypeIdMap = BannerPatternTypeIdMap::getInstance();
- $patterns = $nbt->getListTag(self::TAG_PATTERNS);
+ $patterns = $nbt->getListTag(self::TAG_PATTERNS, CompoundTag::class);
if($patterns !== null){
- /** @var CompoundTag $pattern */
foreach($patterns as $pattern){
$patternColor = $colorIdMap->fromInvertedId($pattern->getInt(self::TAG_PATTERN_COLOR)) ?? DyeColor::BLACK; //TODO: missing pattern colour should be an error
$patternType = $patternTypeIdMap->fromId($pattern->getString(self::TAG_PATTERN_NAME));
@@ -75,6 +80,8 @@ class Banner extends Spawnable{
$this->patterns[] = new BannerPatternLayer($patternType, $patternColor);
}
}
+
+ $this->type = $nbt->getInt(self::TAG_TYPE, self::TYPE_NORMAL);
}
protected function writeSaveData(CompoundTag $nbt) : void{
@@ -89,6 +96,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 +111,7 @@ class Banner extends Spawnable{
);
}
$nbt->setTag(self::TAG_PATTERNS, $patterns);
+ $nbt->setInt(self::TAG_TYPE, $this->type);
}
/**
@@ -136,6 +145,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/ChiseledBookshelf.php b/src/block/tile/ChiseledBookshelf.php
index 06175e27f..90bf8f29b 100644
--- a/src/block/tile/ChiseledBookshelf.php
+++ b/src/block/tile/ChiseledBookshelf.php
@@ -34,6 +34,7 @@ use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
+use pocketmine\nbt\UnexpectedTagTypeException;
use pocketmine\world\World;
use function count;
@@ -86,13 +87,18 @@ class ChiseledBookshelf extends Tile implements Container{
}
protected function loadItems(CompoundTag $tag) : void{
- if(($inventoryTag = $tag->getTag(Container::TAG_ITEMS)) instanceof ListTag && $inventoryTag->getTagType() === NBT::TAG_Compound){
+ try{
+ $inventoryTag = $tag->getListTag(Container::TAG_ITEMS, CompoundTag::class);
+ }catch(UnexpectedTagTypeException){
+ //preserve the old behaviour of not throwing on wrong types
+ $inventoryTag = null;
+ }
+ if($inventoryTag !== null){
$inventory = $this->getRealInventory();
$listeners = $inventory->getListeners()->toArray();
$inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
$newContents = [];
- /** @var CompoundTag $itemNBT */
foreach($inventoryTag as $slot => $itemNBT){
try{
$count = $itemNBT->getByte(SavedItemStackData::TAG_COUNT);
diff --git a/src/block/tile/ContainerTrait.php b/src/block/tile/ContainerTrait.php
index fdd050a41..6b9158d7a 100644
--- a/src/block/tile/ContainerTrait.php
+++ b/src/block/tile/ContainerTrait.php
@@ -31,6 +31,7 @@ use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
+use pocketmine\nbt\UnexpectedTagTypeException;
use pocketmine\world\Position;
/**
@@ -43,13 +44,18 @@ trait ContainerTrait{
abstract public function getRealInventory() : Inventory;
protected function loadItems(CompoundTag $tag) : void{
- if(($inventoryTag = $tag->getTag(Container::TAG_ITEMS)) instanceof ListTag && $inventoryTag->getTagType() === NBT::TAG_Compound){
+ try{
+ $inventoryTag = $tag->getListTag(Container::TAG_ITEMS, CompoundTag::class);
+ }catch(UnexpectedTagTypeException){
+ //preserve the old behaviour of not throwing on wrong types
+ $inventoryTag = null;
+ }
+ if($inventoryTag !== null){
$inventory = $this->getRealInventory();
$listeners = $inventory->getListeners()->toArray();
$inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
$newContents = [];
- /** @var CompoundTag $itemNBT */
foreach($inventoryTag as $itemNBT){
try{
$newContents[$itemNBT->getByte(SavedItemStackData::TAG_SLOT)] = Item::nbtDeserialize($itemNBT);
diff --git a/src/block/tile/HangingSign.php b/src/block/tile/HangingSign.php
new file mode 100644
index 000000000..a5be9ba5c
--- /dev/null
+++ b/src/block/tile/HangingSign.php
@@ -0,0 +1,31 @@
+text = new SignText();
+ $this->backText = new SignText();
parent::__construct($world, $pos);
}
- private function readTextTag(CompoundTag $nbt, bool $lightingBugResolved) : void{
+ private function readTextTag(CompoundTag $nbt, bool $lightingBugResolved) : SignText{
$baseColor = new Color(0, 0, 0);
$glowingText = false;
if(($baseColorTag = $nbt->getTag(self::TAG_TEXT_COLOR)) instanceof IntTag){
@@ -88,19 +92,27 @@ class Sign extends Spawnable{
//see https://bugs.mojang.com/browse/MCPE-117835
$glowingText = $glowingTextTag->getValue() !== 0;
}
- $this->text = SignText::fromBlob(mb_scrub($nbt->getString(self::TAG_TEXT_BLOB), 'UTF-8'), $baseColor, $glowingText);
+ return SignText::fromBlob(mb_scrub($nbt->getString(self::TAG_TEXT_BLOB), 'UTF-8'), $baseColor, $glowingText);
+ }
+
+ private function writeTextTag(SignText $text) : CompoundTag{
+ return CompoundTag::create()
+ ->setString(self::TAG_TEXT_BLOB, rtrim(implode("\n", $text->getLines()), "\n"))
+ ->setInt(self::TAG_TEXT_COLOR, Binary::signInt($text->getBaseColor()->toARGB()))
+ ->setByte(self::TAG_GLOWING_TEXT, $text->isGlowing() ? 1 : 0)
+ ->setByte(self::TAG_PERSIST_FORMATTING, 1);
}
public function readSaveData(CompoundTag $nbt) : void{
$frontTextTag = $nbt->getTag(self::TAG_FRONT_TEXT);
if($frontTextTag instanceof CompoundTag){
- $this->readTextTag($frontTextTag, true);
+ $this->text = $this->readTextTag($frontTextTag, true);
}elseif($nbt->getTag(self::TAG_TEXT_BLOB) instanceof StringTag){ //MCPE 1.2 save format
$lightingBugResolved = false;
if(($lightingBugResolvedTag = $nbt->getTag(self::TAG_LEGACY_BUG_RESOLVE)) instanceof ByteTag){
$lightingBugResolved = $lightingBugResolvedTag->getValue() !== 0;
}
- $this->readTextTag($nbt, $lightingBugResolved);
+ $this->text = $this->readTextTag($nbt, $lightingBugResolved);
}else{
$text = [];
for($i = 0; $i < SignText::LINE_COUNT; ++$i){
@@ -111,22 +123,14 @@ class Sign extends Spawnable{
}
$this->text = new SignText($text);
}
+ $backTextTag = $nbt->getTag(self::TAG_BACK_TEXT);
+ $this->backText = $backTextTag instanceof CompoundTag ? $this->readTextTag($backTextTag, true) : new SignText();
$this->waxed = $nbt->getByte(self::TAG_WAXED, 0) !== 0;
}
protected function writeSaveData(CompoundTag $nbt) : void{
- $nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create()
- ->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()))
- ->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB()))
- ->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0)
- ->setByte(self::TAG_PERSIST_FORMATTING, 1)
- );
- $nbt->setTag(self::TAG_BACK_TEXT, CompoundTag::create()
- ->setString(self::TAG_TEXT_BLOB, "")
- ->setInt(self::TAG_TEXT_COLOR, Binary::signInt(0xff_00_00_00))
- ->setByte(self::TAG_GLOWING_TEXT, 0)
- ->setByte(self::TAG_PERSIST_FORMATTING, 1)
- );
+ $nbt->setTag(self::TAG_FRONT_TEXT, $this->writeTextTag($this->text));
+ $nbt->setTag(self::TAG_BACK_TEXT, $this->writeTextTag($this->backText));
$nbt->setByte(self::TAG_WAXED, $this->waxed ? 1 : 0);
}
@@ -139,6 +143,10 @@ class Sign extends Spawnable{
$this->text = $text;
}
+ public function getBackText() : SignText{ return $this->backText; }
+
+ public function setBackText(SignText $backText) : void{ $this->backText = $backText; }
+
public function isWaxed() : bool{ return $this->waxed; }
public function setWaxed(bool $waxed) : void{ $this->waxed = $waxed; }
@@ -160,19 +168,8 @@ 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()))
- ->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
- );
- //TODO: this is not yet used by the server, but needed to rollback any client-side changes to the back text
- $nbt->setTag(self::TAG_BACK_TEXT, CompoundTag::create()
- ->setString(self::TAG_TEXT_BLOB, "")
- ->setInt(self::TAG_TEXT_COLOR, Binary::signInt(0xff_00_00_00))
- ->setByte(self::TAG_GLOWING_TEXT, 0)
- ->setByte(self::TAG_PERSIST_FORMATTING, 1)
- );
+ $nbt->setTag(self::TAG_FRONT_TEXT, $this->writeTextTag($this->text));
+ $nbt->setTag(self::TAG_BACK_TEXT, $this->writeTextTag($this->backText));
$nbt->setByte(self::TAG_WAXED, $this->waxed ? 1 : 0);
$nbt->setLong(self::TAG_LOCKED_FOR_EDITING_BY, $this->editorEntityRuntimeId ?? -1);
}
diff --git a/src/block/tile/Spawnable.php b/src/block/tile/Spawnable.php
index 67bc72fd9..0c41713f2 100644
--- a/src/block/tile/Spawnable.php
+++ b/src/block/tile/Spawnable.php
@@ -32,7 +32,7 @@ use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use function get_class;
abstract class Spawnable extends Tile{
- /** @phpstan-var CacheableNbt<\pocketmine\nbt\tag\CompoundTag>|null */
+ /** @phpstan-var CacheableNbt|null */
private ?CacheableNbt $spawnCompoundCache = null;
/**
@@ -73,7 +73,7 @@ abstract class Spawnable extends Tile{
* Returns encoded NBT (varint, little-endian) used to spawn this tile to clients. Uses cache where possible,
* populates cache if it is null.
*
- * @phpstan-return CacheableNbt<\pocketmine\nbt\tag\CompoundTag>
+ * @phpstan-return CacheableNbt
*/
final public function getSerializedSpawnCompound() : CacheableNbt{
if($this->spawnCompoundCache === null){
diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php
index 515dd8c63..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
@@ -114,6 +115,13 @@ final class TileFactory{
$this->saveNames[$className] = reset($saveNames);
}
+ /**
+ * @phpstan-param class-string $class
+ */
+ public function isRegistered(string $class) : bool{
+ return isset($this->saveNames[$class]);
+ }
+
/**
* @internal
* @throws SavedDataLoadingException
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 @@
+lit ? 3 : 0;
}
- /** @see Block::onInteract() */
+ /**
+ * @param Item[] &$returnedItems
+ * @see Block::onInteract()
+ */
public function onInteract(Item $item, int $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){
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;
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 @@
+ "Mangrove",
self::AZALEA => "Azalea",
self::FLOWERING_AZALEA => "Flowering Azalea",
- self::CHERRY => "Cherry"
+ self::CHERRY => "Cherry",
+ self::PALE_OAK => "Pale Oak",
};
}
}
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 @@
+ ["C418 - cat", LevelSoundEvent::RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()],
self::DISK_BLOCKS => ["C418 - blocks", LevelSoundEvent::RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()],
self::DISK_CHIRP => ["C418 - chirp", LevelSoundEvent::RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()],
+ self::DISK_CREATOR => ["Lena Raine - Creator", LevelSoundEvent::RECORD_CREATOR, KnownTranslationFactory::item_record_creator_desc()],
+ self::DISK_CREATOR_MUSIC_BOX => ["Lena Raine - Creator (Music Box)", LevelSoundEvent::RECORD_CREATOR_MUSIC_BOX, KnownTranslationFactory::item_record_creator_music_box_desc()],
self::DISK_FAR => ["C418 - far", LevelSoundEvent::RECORD_FAR, KnownTranslationFactory::item_record_far_desc()],
self::DISK_MALL => ["C418 - mall", LevelSoundEvent::RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()],
self::DISK_MELLOHI => ["C418 - mellohi", LevelSoundEvent::RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()],
self::DISK_OTHERSIDE => ["Lena Raine - otherside", LevelSoundEvent::RECORD_OTHERSIDE, KnownTranslationFactory::item_record_otherside_desc()],
self::DISK_PIGSTEP => ["Lena Raine - Pigstep", LevelSoundEvent::RECORD_PIGSTEP, KnownTranslationFactory::item_record_pigstep_desc()],
+ self::DISK_PRECIPICE => ["Aaron Cherof - Precipice", LevelSoundEvent::RECORD_PRECIPICE, KnownTranslationFactory::item_record_precipice_desc()],
+ self::DISK_RELIC => ["Aaron Cherof - Relic", LevelSoundEvent::RECORD_RELIC, KnownTranslationFactory::item_record_relic_desc()],
self::DISK_STAL => ["C418 - stal", LevelSoundEvent::RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()],
self::DISK_STRAD => ["C418 - strad", LevelSoundEvent::RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()],
self::DISK_WARD => ["C418 - ward", LevelSoundEvent::RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()],
diff --git a/src/block/utils/SignLikeRotation.php b/src/block/utils/SignLikeRotation.php
new file mode 100644
index 000000000..031c4abaf
--- /dev/null
+++ b/src/block/utils/SignLikeRotation.php
@@ -0,0 +1,39 @@
+ "Crimson",
self::WARPED => "Warped",
self::CHERRY => "Cherry",
+ self::PALE_OAK => "Pale Oak",
};
}
diff --git a/src/command/Command.php b/src/command/Command.php
index 4c2c6815b..54822d80e 100644
--- a/src/command/Command.php
+++ b/src/command/Command.php
@@ -33,9 +33,11 @@ use pocketmine\permission\PermissionManager;
use pocketmine\Server;
use pocketmine\utils\BroadcastLoggerForwarder;
use pocketmine\utils\TextFormat;
+use function array_values;
use function explode;
use function implode;
use function str_replace;
+use const PHP_INT_MAX;
abstract class Command{
@@ -80,6 +82,7 @@ abstract class Command{
/**
* @param string[] $args
+ * @phpstan-param list $args
*
* @return mixed
* @throws CommandException
@@ -111,7 +114,7 @@ abstract class Command{
}
public function setPermission(?string $permission) : void{
- $this->setPermissions($permission === null ? [] : explode(";", $permission));
+ $this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
}
public function testPermission(CommandSender $target, ?string $permission = null) : bool{
@@ -212,6 +215,7 @@ abstract class Command{
* @phpstan-param list $aliases
*/
public function setAliases(array $aliases) : void{
+ $aliases = array_values($aliases); //because plugins can and will pass crap
$this->aliases = $aliases;
if(!$this->isRegistered()){
$this->activeAliases = $aliases;
diff --git a/src/command/FormattedCommandAlias.php b/src/command/FormattedCommandAlias.php
index 5086672f6..b47363397 100644
--- a/src/command/FormattedCommandAlias.php
+++ b/src/command/FormattedCommandAlias.php
@@ -121,6 +121,7 @@ class FormattedCommandAlias extends Command{
/**
* @param string[] $args
+ * @phpstan-param list $args
*/
private function buildCommand(string $formatString, array $args) : ?string{
$index = 0;
diff --git a/src/command/SimpleCommandMap.php b/src/command/SimpleCommandMap.php
index e0d8e6565..9f5441746 100644
--- a/src/command/SimpleCommandMap.php
+++ b/src/command/SimpleCommandMap.php
@@ -73,6 +73,7 @@ use pocketmine\timings\Timings;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use function array_shift;
+use function array_values;
use function count;
use function implode;
use function str_contains;
@@ -163,7 +164,7 @@ class SimpleCommandMap implements CommandMap{
unset($aliases[$index]);
}
}
- $command->setAliases($aliases);
+ $command->setAliases(array_values($aliases));
if(!$registered){
$command->setLabel($fallbackPrefix . ":" . $label);
diff --git a/src/command/defaults/HelpCommand.php b/src/command/defaults/HelpCommand.php
index 487c915f2..054585455 100644
--- a/src/command/defaults/HelpCommand.php
+++ b/src/command/defaults/HelpCommand.php
@@ -39,6 +39,7 @@ use function ksort;
use function min;
use function sort;
use function strtolower;
+use const PHP_INT_MAX;
use const SORT_FLAG_CASE;
use const SORT_NATURAL;
@@ -108,7 +109,7 @@ class HelpCommand extends VanillaCommand{
$usage = $cmd->getUsage();
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
- $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString)))
+ $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
->prefix(TextFormat::GOLD));
$aliases = $cmd->getAliases();
diff --git a/src/command/defaults/ParticleCommand.php b/src/command/defaults/ParticleCommand.php
index f20d47ccc..4867e3eb5 100644
--- a/src/command/defaults/ParticleCommand.php
+++ b/src/command/defaults/ParticleCommand.php
@@ -219,7 +219,11 @@ class ParticleCommand extends VanillaCommand{
break;
case "blockdust":
if($data !== null){
- $d = explode("_", $data);
+ //to preserve the old unlimited explode behaviour, allow this to split into at most 5 parts
+ //this allows the 4th argument to be processed normally if given without forcing it to also consume
+ //any unexpected parts
+ //we probably ought to error in this case, but this will do for now
+ $d = explode("_", $data, limit: 5);
if(count($d) >= 3){
return new DustParticle(new Color(
((int) $d[0]) & 0xff,
diff --git a/src/command/defaults/TimingsCommand.php b/src/command/defaults/TimingsCommand.php
index ec26f3efe..ffbc08491 100644
--- a/src/command/defaults/TimingsCommand.php
+++ b/src/command/defaults/TimingsCommand.php
@@ -46,6 +46,8 @@ use function fwrite;
use function http_build_query;
use function implode;
use function is_array;
+use function is_int;
+use function is_string;
use function json_decode;
use function mkdir;
use function strtolower;
@@ -146,7 +148,8 @@ class TimingsCommand extends VanillaCommand{
private function uploadReport(array $lines, CommandSender $sender) : void{
$data = [
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
- "data" => implode("\n", $lines)
+ "data" => implode("\n", $lines),
+ "private" => "true"
];
$host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
@@ -178,9 +181,14 @@ class TimingsCommand extends VanillaCommand{
return;
}
$response = json_decode($result->getBody(), true);
- if(is_array($response) && isset($response["id"])){
- Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
- "https://" . $host . "/?id=" . $response["id"]));
+ if(is_array($response) && isset($response["id"]) && (is_int($response["id"]) || is_string($response["id"]))){
+ $url = "https://" . $host . "/?id=" . $response["id"];
+ if(isset($response["access_token"]) && is_string($response["access_token"])){
+ $url .= "&access_token=" . $response["access_token"];
+ }else{
+ $sender->getServer()->getLogger()->warning("Your chosen timings host does not support private reports. Anyone will be able to see your report if they guess the ID.");
+ }
+ Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead($url));
}else{
$sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody());
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError());
diff --git a/src/command/utils/CommandStringHelper.php b/src/command/utils/CommandStringHelper.php
index eacc5d3d8..76d70a9bb 100644
--- a/src/command/utils/CommandStringHelper.php
+++ b/src/command/utils/CommandStringHelper.php
@@ -51,9 +51,8 @@ final class CommandStringHelper{
foreach($matches[0] as $k => $_){
for($i = 1; $i <= 2; ++$i){
if($matches[$i][$k] !== ""){
- /** @var string $match */ //phpstan can't understand preg_match and friends by itself :(
$match = $matches[$i][$k];
- $args[(int) $k] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
+ $args[] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
break;
}
}
diff --git a/src/console/ConsoleCommandSender.php b/src/console/ConsoleCommandSender.php
index aa7ea6e69..a0c1c5200 100644
--- a/src/console/ConsoleCommandSender.php
+++ b/src/console/ConsoleCommandSender.php
@@ -62,7 +62,7 @@ class ConsoleCommandSender implements CommandSender{
$message = $this->getLanguage()->translate($message);
}
- foreach(explode("\n", trim($message)) as $line){
+ foreach(explode("\n", trim($message), limit: PHP_INT_MAX) as $line){
Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::addBase(TextFormat::WHITE, $line));
}
}
diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php
index ff2be6926..673095c6e 100644
--- a/src/crafting/CraftingManager.php
+++ b/src/crafting/CraftingManager.php
@@ -23,14 +23,19 @@ declare(strict_types=1);
namespace pocketmine\crafting;
+use pmmp\encoding\ByteBufferWriter;
+use pmmp\encoding\VarInt;
use pocketmine\item\Item;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\TreeRoot;
-use pocketmine\utils\BinaryStream;
use pocketmine\utils\DestructorCallbackTrait;
use pocketmine\utils\ObjectSet;
+use function array_shift;
+use function count;
+use function implode;
+use function ksort;
use function spl_object_id;
-use function usort;
+use const SORT_STRING;
class CraftingManager{
use DestructorCallbackTrait;
@@ -100,6 +105,7 @@ class CraftingManager{
/**
* 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.
@@ -108,47 +114,32 @@ class CraftingManager{
return $retval;
}
- /**
- * @param Item[] $items
- * @phpstan-param list- $items
- *
- * @return Item[]
- * @phpstan-return list
-
- */
- private static function pack(array $items) : array{
- $result = [];
+ private static function hashOutput(Item $output) : string{
+ $write = new ByteBufferWriter();
+ VarInt::writeSignedInt($write, $output->getStateId());
+ //TODO: the NBT serializer allocates its own ByteBufferWriter, we should change the API in the future to
+ //allow passing our own to avoid this extra allocation
+ $write->writeByteArray((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag())));
- foreach($items as $item){
- foreach($result as $otherItem){
- if($item->canStackWith($otherItem)){
- $otherItem->setCount($otherItem->getCount() + $item->getCount());
- continue 2;
- }
- }
-
- //No matching item found
- $result[] = clone $item;
- }
-
- return $result;
+ return $write->getData();
}
/**
* @param Item[] $outputs
- * @phpstan-param list
- $outputs
*/
private static function hashOutputs(array $outputs) : string{
- $outputs = self::pack($outputs);
- usort($outputs, [self::class, "sort"]);
- $result = new BinaryStream();
+ if(count($outputs) === 1){
+ return self::hashOutput(array_shift($outputs));
+ }
+ $unique = [];
foreach($outputs as $o){
//count is not written because the outputs might be from multiple repetitions of a single recipe
//this reduces the accuracy of the hash, but it won't matter in most cases.
- $result->putVarInt($o->getStateId());
- $result->put((new LittleEndianNbtSerializer())->write(new TreeRoot($o->getNamedTag())));
+ $hash = self::hashOutput($o);
+ $unique[$hash] = $hash;
}
-
- return $result->getBuffer();
+ ksort($unique, SORT_STRING);
+ return implode("", $unique);
}
/**
diff --git a/src/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php
index 3df6bfb62..7c9cdd58b 100644
--- a/src/crafting/CraftingManagerFromDataHelper.php
+++ b/src/crafting/CraftingManagerFromDataHelper.php
@@ -162,7 +162,7 @@ final class CraftingManagerFromDataHelper{
}
$mapper = new \JsonMapper();
- $mapper->bStrictObjectTypeChecking = true;
+ $mapper->bStrictObjectTypeChecking = false; //to allow hydrating ItemStackData - since this is only used for offline data it's safe
$mapper->bExceptionOnUndefinedProperty = true;
$mapper->bExceptionOnMissingData = true;
diff --git a/src/crafting/ShapedRecipe.php b/src/crafting/ShapedRecipe.php
index 4c40eb0f5..2af3f5134 100644
--- a/src/crafting/ShapedRecipe.php
+++ b/src/crafting/ShapedRecipe.php
@@ -97,6 +97,7 @@ class ShapedRecipe implements CraftingRecipe{
$this->shape = $shape;
+ Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{});
foreach(Utils::stringifyKeys($ingredients) as $char => $i){
if(!str_contains(implode($this->shape), $char)){
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
@@ -105,6 +106,7 @@ class ShapedRecipe implements CraftingRecipe{
$this->ingredientList[$char] = clone $i;
}
+ Utils::validateArrayValueType($results, function(Item $_) : void{});
$this->results = Utils::cloneObjectArray($results);
}
diff --git a/src/crafting/ShapelessRecipe.php b/src/crafting/ShapelessRecipe.php
index 7a4a22fda..b139439ef 100644
--- a/src/crafting/ShapelessRecipe.php
+++ b/src/crafting/ShapelessRecipe.php
@@ -53,7 +53,9 @@ class ShapelessRecipe implements CraftingRecipe{
if(count($ingredients) > 9){
throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients");
}
+ Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{});
$this->ingredients = $ingredients;
+ Utils::validateArrayValueType($results, function(Item $_) : void{});
$this->results = Utils::cloneObjectArray($results);
}
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/data/bedrock/BedrockDataFiles.php b/src/data/bedrock/BedrockDataFiles.php
index 5c476ca1c..53bd9b11e 100644
--- a/src/data/bedrock/BedrockDataFiles.php
+++ b/src/data/bedrock/BedrockDataFiles.php
@@ -31,21 +31,23 @@ 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';
public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json';
public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt';
public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json';
- public const CREATIVEITEMS_JSON = BEDROCK_DATA_PATH . '/creativeitems.json';
+ public const CREATIVE = BEDROCK_DATA_PATH . '/creative';
public const ENTITY_ID_MAP_JSON = BEDROCK_DATA_PATH . '/entity_id_map.json';
public const ENTITY_IDENTIFIERS_NBT = BEDROCK_DATA_PATH . '/entity_identifiers.nbt';
+ public const ENUMS = BEDROCK_DATA_PATH . '/enums';
+ public const ENUMS_PY = BEDROCK_DATA_PATH . '/enums.py';
public const ITEM_TAGS_JSON = BEDROCK_DATA_PATH . '/item_tags.json';
public const LEVEL_SOUND_ID_MAP_JSON = BEDROCK_DATA_PATH . '/level_sound_id_map.json';
- public const PARTICLE_ID_MAP_JSON = BEDROCK_DATA_PATH . '/particle_id_map.json';
+ public const PROTOCOL_INFO_JSON = BEDROCK_DATA_PATH . '/protocol_info.json';
public const R12_TO_CURRENT_BLOCK_MAP_BIN = BEDROCK_DATA_PATH . '/r12_to_current_block_map.bin';
public const R16_TO_CURRENT_ITEM_MAP_JSON = BEDROCK_DATA_PATH . '/r16_to_current_item_map.json';
+ public const RECIPES = BEDROCK_DATA_PATH . '/recipes';
public const REQUIRED_ITEM_LIST_JSON = BEDROCK_DATA_PATH . '/required_item_list.json';
}
diff --git a/src/data/bedrock/BiomeIds.php b/src/data/bedrock/BiomeIds.php
index 1169a51ea..f3c38d3ed 100644
--- a/src/data/bedrock/BiomeIds.php
+++ b/src/data/bedrock/BiomeIds.php
@@ -122,4 +122,5 @@ final class BiomeIds{
public const DEEP_DARK = 190;
public const MANGROVE_SWAMP = 191;
public const CHERRY_GROVE = 192;
+ public const PALE_GARDEN = 193;
}
diff --git a/src/data/bedrock/FireworkRocketTypeIdMap.php b/src/data/bedrock/FireworkRocketTypeIdMap.php
new file mode 100644
index 000000000..4358c2073
--- /dev/null
+++ b/src/data/bedrock/FireworkRocketTypeIdMap.php
@@ -0,0 +1,45 @@
+ */
+ use IntSaveIdMapTrait;
+
+ private function __construct(){
+ foreach(FireworkRocketType::cases() as $case){
+ $this->register(match($case){
+ FireworkRocketType::SMALL_BALL => FireworkRocketTypeIds::SMALL_BALL,
+ FireworkRocketType::LARGE_BALL => FireworkRocketTypeIds::LARGE_BALL,
+ FireworkRocketType::STAR => FireworkRocketTypeIds::STAR,
+ FireworkRocketType::CREEPER => FireworkRocketTypeIds::CREEPER,
+ FireworkRocketType::BURST => FireworkRocketTypeIds::BURST,
+ }, $case);
+ }
+ }
+}
diff --git a/src/data/bedrock/FireworkRocketTypeIds.php b/src/data/bedrock/FireworkRocketTypeIds.php
new file mode 100644
index 000000000..7ddf0e6b9
--- /dev/null
+++ b/src/data/bedrock/FireworkRocketTypeIds.php
@@ -0,0 +1,32 @@
+ */
use IntSaveIdMapTrait;
public function __construct(){
+ $newMapping = ValueMappings::getInstance()->mushroomBlockType;
foreach(MushroomBlockType::cases() as $case){
- $this->register(match($case){
- MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES,
- MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER,
- MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE,
- MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER,
- MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE,
- MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY,
- MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE,
- MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER,
- MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE,
- MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER,
- MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP,
- }, $case);
+ $this->register($newMapping->valueToRaw($case), $case);
}
}
}
diff --git a/src/data/bedrock/NoteInstrumentIdMap.php b/src/data/bedrock/NoteInstrumentIdMap.php
index c847ecd98..7f0c0f310 100644
--- a/src/data/bedrock/NoteInstrumentIdMap.php
+++ b/src/data/bedrock/NoteInstrumentIdMap.php
@@ -39,10 +39,10 @@ final class NoteInstrumentIdMap{
NoteInstrument::SNARE => 2,
NoteInstrument::CLICKS_AND_STICKS => 3,
NoteInstrument::DOUBLE_BASS => 4,
- NoteInstrument::BELL => 5,
- NoteInstrument::FLUTE => 6,
- NoteInstrument::CHIME => 7,
- NoteInstrument::GUITAR => 8,
+ NoteInstrument::FLUTE => 5,
+ NoteInstrument::BELL => 6,
+ NoteInstrument::GUITAR => 7,
+ NoteInstrument::CHIME => 8,
NoteInstrument::XYLOPHONE => 9,
NoteInstrument::IRON_XYLOPHONE => 10,
NoteInstrument::COW_BELL => 11,
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,49 +52,53 @@ 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->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
return $this->cache[$stateId] ??= $this->serializeBlock(RuntimeBlockStateRegistry::getInstance()->fromStateId($stateId));
}
+ public function isRegistered(Block $block) : bool{
+ return isset($this->serializers[$block->getTypeId()]);
+ }
+
/**
* @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;
+ //writer accepted for convenience only
+ $this->serializers[$block->getTypeId()] = $serializer instanceof Writer ? $serializer->getBlockStateData() : $serializer;
}
+ /**
+ * @deprecated
+ */
public function mapSimple(Block $block, string $id) : void{
- $this->map($block, fn() => Writer::create($id));
+ $this->map($block, BlockStateData::current($id, []));
}
+ /**
+ * @deprecated
+ */
public function mapSlab(Slab $block, string $singleId, string $doubleId) : void{
$this->map($block, fn(Slab $block) => Helper::encodeSlab($block, $singleId, $doubleId));
}
+ /**
+ * @deprecated
+ */
public function mapStairs(Stair $block, string $id) : void{
$this->map($block, fn(Stair $block) => Helper::encodeStairs($block, Writer::create($id)));
}
+ /**
+ * @deprecated
+ */
public function mapLog(Wood $block, string $unstrippedId, string $strippedId) : void{
$this->map($block, fn(Wood $block) => Helper::encodeLog($block, $unstrippedId, $strippedId));
}
@@ -265,1588 +117,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::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)));
-
- //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 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_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::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::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
- )
- )
- );
- });
- $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->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(WeightedPressurePlateHeavy $block) : Writer{
- return Writer::create(Ids::HEAVY_WEIGHTED_PRESSURE_PLATE)
- ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength());
- });
- $this->map(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), function(WeightedPressurePlateLight $block) : Writer{
- return Writer::create(Ids::LIGHT_WEIGHTED_PRESSURE_PLATE)
- ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength());
- });
- $this->map(Blocks::WHEAT(), fn(Wheat $block) => Helper::encodeCrops($block, new Writer(Ids::WHEAT)));
+ 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..02491bae6
--- /dev/null
+++ b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php
@@ -0,0 +1,237 @@
+> $components
+ *
+ * @return string[][]
+ * @phpstan-return list
>
+ */
+ private static function compileFlattenedIdPartMatrix(array $components) : array{
+ $result = [];
+ foreach($components as $component){
+ $column = is_string($component) ? [$component] : $component->getPossibleValues();
+
+ if(count($result) === 0){
+ $result = array_map(fn($value) => [$value], $column);
+ }else{
+ $stepResult = [];
+ foreach($result as $parts){
+ foreach($column as $value){
+ $stepPart = $parts;
+ $stepPart[] = $value;
+ $stepResult[] = $stepPart;
+ }
+ }
+
+ $result = $stepResult;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param string[]|StringProperty[] $idComponents
+ *
+ * @phpstan-template TBlock of Block
+ *
+ * @phpstan-param TBlock $block
+ * @phpstan-param list> $idComponents
+ */
+ private static function serializeFlattenedId(Block $block, array $idComponents) : string{
+ $id = "";
+ foreach($idComponents as $infix){
+ $id .= is_string($infix) ? $infix : $infix->serializePlain($block);
+ }
+ return $id;
+ }
+
+ /**
+ * @param string[]|StringProperty[] $idComponents
+ * @param string[] $idPropertyValues
+ *
+ * @phpstan-template TBlock of Block
+ *
+ * @phpstan-param TBlock $baseBlock
+ * @phpstan-param list> $idComponents
+ * @phpstan-param list $idPropertyValues
+ *
+ * @phpstan-return TBlock
+ */
+ private static function deserializeFlattenedId(Block $baseBlock, array $idComponents, array $idPropertyValues) : Block{
+ $preparedBlock = clone $baseBlock;
+ foreach($idComponents as $k => $component){
+ if($component instanceof StringProperty){
+ $fakeValue = $idPropertyValues[$k];
+ $component->deserializePlain($preparedBlock, $fakeValue);
+ }
+ }
+
+ return $preparedBlock;
+ }
+
+ public function mapSimple(Block $block, string $id) : void{
+ $this->deserializer->mapSimple($id, fn() => clone $block);
+ $this->serializer->mapSimple($block, $id);
+ }
+
+ /**
+ * @phpstan-template TBlock of Block
+ * @phpstan-param FlattenedIdModel $model
+ */
+ public function mapFlattenedId(FlattenedIdModel $model) : void{
+ $block = $model->getBlock();
+
+ $idComponents = $model->getIdComponents();
+ if(count($idComponents) === 0){
+ throw new \InvalidArgumentException("No ID components provided");
+ }
+ $properties = $model->getProperties();
+
+ //This is a really cursed hack that lets us essentially write flattened properties as blockstate properties, and
+ //then pull them out to compile an ID :D
+ //This works surprisingly well and is much more elegant than I would've expected
+
+ if(count($properties) > 0){
+ $this->serializer->map($block, function(Block $block) use ($idComponents, $properties) : Writer{
+ $id = self::serializeFlattenedId($block, $idComponents);
+
+ $writer = new Writer($id);
+ foreach($properties as $property){
+ $property->serialize($block, $writer);
+ }
+
+ return $writer;
+ });
+ }else{
+ $this->serializer->map($block, function(Block $block) use ($idComponents) : BlockStateData{
+ //fast path for blocks with no state properties
+ $id = self::serializeFlattenedId($block, $idComponents);
+ return BlockStateData::current($id, []);
+ });
+ }
+
+ $idPermutations = self::compileFlattenedIdPartMatrix($idComponents);
+ foreach($idPermutations as $idParts){
+ //deconstruct the ID into a partial state
+ //we can do this at registration time since there will be multiple deserializers
+ $preparedBlock = self::deserializeFlattenedId($block, $idComponents, $idParts);
+ $id = implode("", $idParts);
+
+ if(count($properties) > 0){
+ $this->deserializer->map($id, function(Reader $reader) use ($preparedBlock, $properties) : Block{
+ $block = clone $preparedBlock;
+
+ foreach($properties as $property){
+ $property->deserialize($block, $reader);
+ }
+ return $block;
+ });
+ }else{
+ //fast path for blocks with no state properties
+ $this->deserializer->map($id, fn() => clone $preparedBlock);
+ }
+ }
+ }
+
+ /**
+ * @phpstan-template TBlock of Block&Colored
+ * @phpstan-param TBlock $block
+ */
+ public function mapColored(Block $block, string $idPrefix, string $idSuffix) : void{
+ $this->mapFlattenedId(FlattenedIdModel::create($block)
+ ->idComponents([
+ $idPrefix,
+ CommonProperties::getInstance()->dyeColorIdInfix,
+ $idSuffix
+ ])
+ );
+ }
+
+ public function mapSlab(Slab $block, string $type) : void{
+ $commonProperties = CommonProperties::getInstance();
+ $this->mapFlattenedId(FlattenedIdModel::create($block)
+ ->idComponents(["minecraft:", $type, "_", $commonProperties->slabIdInfix, "slab"])
+ ->properties([$commonProperties->slabPositionProperty])
+ );
+ }
+
+ public function mapStairs(Stair $block, string $id) : void{
+ $this->mapModel(Model::create($block, $id)->properties(CommonProperties::getInstance()->stairProperties));
+ }
+
+ /**
+ * @phpstan-template TBlock of Block
+ * @phpstan-param Model $model
+ */
+ public function mapModel(Model $model) : void{
+ $id = $model->getId();
+ $block = $model->getBlock();
+ $propertyDescriptors = $model->getProperties();
+
+ $this->deserializer->map($id, static function(Reader $in) use ($block, $propertyDescriptors) : Block{
+ $newBlock = clone $block;
+ foreach($propertyDescriptors as $descriptor){
+ $descriptor->deserialize($newBlock, $in);
+ }
+ return $newBlock;
+ });
+ $this->serializer->map($block, static function(Block $block) use ($id, $propertyDescriptors) : Writer{
+ $writer = new Writer($id);
+ foreach($propertyDescriptors as $descriptor){
+ $descriptor->serialize($block, $writer);
+ }
+ return $writer;
+ });
+ }
+}
diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php
index fda0455aa..1d48ec76f 100644
--- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php
+++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php
@@ -56,11 +56,13 @@ use pocketmine\data\bedrock\block\BlockLegacyMetadata;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateNames as StateNames;
+use pocketmine\data\bedrock\block\convert\property\ValueMappings;
use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
-use pocketmine\math\Axis;
use pocketmine\math\Facing;
-use pocketmine\utils\AssumptionFailedError;
+/**
+ * @deprecated
+ */
final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
@@ -70,7 +72,14 @@ final class BlockStateDeserializerHelper{
->setPressed($in->readBool(BlockStateNames::BUTTON_PRESSED_BIT));
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @phpstan-template TCandle of Candle
+ * @phpstan-param TCandle $block
+ * @phpstan-return TCandle
+ *
+ * @throws BlockStateDeserializeException
+ */
public static function decodeCandle(Candle $block, BlockStateReader $in) : Candle{
return $block
->setCount($in->readBoundedInt(StateNames::CANDLES, 0, 3) + 1)
@@ -97,6 +106,7 @@ final class BlockStateDeserializerHelper{
}
/**
+ * @deprecated
* @phpstan-template TBlock of CopperMaterial
*
* @phpstan-param TBlock $block
@@ -109,6 +119,7 @@ final class BlockStateDeserializerHelper{
}
/**
+ * @deprecated
* @phpstan-template TBlock of CopperMaterial
*
* @phpstan-param TBlock $block
@@ -126,12 +137,20 @@ final class BlockStateDeserializerHelper{
->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15));
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @phpstan-template TDoor of Door
+ * @phpstan-param TDoor $block
+ * @phpstan-return TDoor
+ *
+ * @throws BlockStateDeserializeException
+ */
public static function decodeDoor(Door $block, BlockStateReader $in) : Door{
//TODO: check if these need any special treatment to get the appropriate data to both halves of the door
return $block
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT))
- ->setFacing(Facing::rotateY($in->readLegacyHorizontalFacing(), false))
+ //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));
}
@@ -142,25 +161,30 @@ final class BlockStateDeserializerHelper{
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT));
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{
return $block
- ->setFacing($in->readLegacyHorizontalFacing())
+ ->setFacing($in->readCardinalHorizontalFacing())
->setInWall($in->readBool(BlockStateNames::IN_WALL_BIT))
->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{
return $block
- ->setAxis(match($in->readBoundedInt(BlockStateNames::CORAL_FAN_DIRECTION, 0, 1)){
- 0 => Axis::X,
- 1 => Axis::Z,
- default => throw new AssumptionFailedError("readBoundedInt() should have prevented this"),
- });
+ ->setAxis($in->mapIntFromInt(BlockStateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis));
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public static function decodeFloorSign(FloorSign $block, BlockStateReader $in) : FloorSign{
return $block
->setRotation($in->readBoundedInt(BlockStateNames::GROUND_SIGN_DIRECTION, 0, 15));
@@ -173,7 +197,10 @@ final class BlockStateDeserializerHelper{
->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT));
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @throws BlockStateDeserializeException
+ * @deprecated
+ */
public static function decodeLeaves(Leaves $block, BlockStateReader $in) : Leaves{
return $block
->setNoDecay($in->readBool(StateNames::PERSISTENT_BIT))
@@ -223,7 +250,10 @@ final class BlockStateDeserializerHelper{
->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @throws BlockStateDeserializeException
+ * @deprecated
+ */
public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{
return $block
->setReady($in->readBool(BlockStateNames::AGE_BIT));
@@ -236,18 +266,37 @@ final class BlockStateDeserializerHelper{
return $block->setPressed($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15) !== 0);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @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());
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @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);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @phpstan-template TStair of Stair
+ * @phpstan-param TStair $block
+ * @phpstan-return TStair
+ *
+ * @throws BlockStateDeserializeException
+ */
public static function decodeStairs(Stair $block, BlockStateReader $in) : Stair{
return $block
->setUpsideDown($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT))
@@ -264,7 +313,14 @@ final class BlockStateDeserializerHelper{
->setFacing($facing === Facing::DOWN ? Facing::UP : $facing);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @phpstan-template TTrapdoor of Trapdoor
+ * @phpstan-param TTrapdoor $block
+ * @phpstan-return TTrapdoor
+ *
+ * @throws BlockStateDeserializeException
+ */
public static function decodeTrapdoor(Trapdoor $block, BlockStateReader $in) : Trapdoor{
return $block
->setFacing($in->read5MinusHorizontalFacing())
@@ -283,12 +339,19 @@ final class BlockStateDeserializerHelper{
return $block;
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{
return $block
->setFacing($in->readHorizontalFacing());
}
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public static function decodeWeightedPressurePlate(WeightedPressurePlate $block, BlockStateReader $in) : WeightedPressurePlate{
return $block
->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15));
diff --git a/src/data/bedrock/block/convert/BlockStateReader.php b/src/data/bedrock/block/convert/BlockStateReader.php
index e3a02885f..4d09d2f85 100644
--- a/src/data/bedrock/block/convert/BlockStateReader.php
+++ b/src/data/bedrock/block/convert/BlockStateReader.php
@@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues;
+use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap;
+use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap;
+use pocketmine\data\bedrock\block\convert\property\ValueMappings;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\nbt\tag\ByteTag;
@@ -112,45 +115,45 @@ final class BlockStateReader{
}
/**
- * @param int[] $mapping
- * @phpstan-param array $mapping
- * @phpstan-return int
+ * @deprecated
+ * @phpstan-param IntFromRawStateMap $map
* @throws BlockStateDeserializeException
*/
- private function parseFacingValue(int $value, array $mapping) : int{
- $result = $mapping[$value] ?? null;
- if($result === null){
- throw new BlockStateDeserializeException("Unmapped facing value " . $value);
- }
- return $result;
- }
+ public function mapIntFromString(string $name, IntFromRawStateMap $map) : int{
+ $raw = $this->readString($name);
- /** @throws BlockStateDeserializeException */
- public function readFacingDirection() : int{
- return $this->parseFacingValue($this->readInt(BlockStateNames::FACING_DIRECTION), [
- 0 => Facing::DOWN,
- 1 => Facing::UP,
- 2 => Facing::NORTH,
- 3 => Facing::SOUTH,
- 4 => Facing::WEST,
- 5 => Facing::EAST
- ]);
- }
-
- /** @throws BlockStateDeserializeException */
- public function readBlockFace() : int{
- return match($raw = $this->readString(BlockStateNames::MC_BLOCK_FACE)){
- StringValues::MC_BLOCK_FACE_DOWN => Facing::DOWN,
- StringValues::MC_BLOCK_FACE_UP => Facing::UP,
- StringValues::MC_BLOCK_FACE_NORTH => Facing::NORTH,
- StringValues::MC_BLOCK_FACE_SOUTH => Facing::SOUTH,
- StringValues::MC_BLOCK_FACE_WEST => Facing::WEST,
- StringValues::MC_BLOCK_FACE_EAST => Facing::EAST,
- default => throw $this->badValueException(BlockStateNames::MC_BLOCK_FACE, $raw)
- };
+ return $map->rawToValue($raw) ?? throw $this->badValueException($name, $raw);
}
/**
+ * @deprecated
+ * @phpstan-param IntFromRawStateMap $map
+ * @throws BlockStateDeserializeException
+ */
+ public function mapIntFromInt(string $name, IntFromRawStateMap $map) : int{
+ $raw = $this->readInt($name);
+
+ return $map->rawToValue($raw) ?? throw $this->badValueException($name, (string) $raw);
+ }
+
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
+ public function readFacingDirection() : int{
+ return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing);
+ }
+
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
+ public function readBlockFace() : int{
+ return $this->mapIntFromString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace);
+ }
+
+ /**
+ * @deprecated
* @return int[]
* @phpstan-return array
*/
@@ -173,82 +176,69 @@ final class BlockStateReader{
return $result;
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readEndRodFacingDirection() : int{
$result = $this->readFacingDirection();
return Facing::axis($result) !== Axis::Y ? Facing::opposite($result) : $result;
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readHorizontalFacing() : int{
- return $this->parseFacingValue($this->readInt(BlockStateNames::FACING_DIRECTION), [
- 0 => Facing::NORTH, //should be illegal, but 1.13 allows it
- 1 => Facing::NORTH, //also should be illegal
- 2 => Facing::NORTH,
- 3 => Facing::SOUTH,
- 4 => Facing::WEST,
- 5 => Facing::EAST
- ]);
- }
-
- /** @throws BlockStateDeserializeException */
- public function readWeirdoHorizontalFacing() : int{
- return $this->parseFacingValue($this->readInt(BlockStateNames::WEIRDO_DIRECTION), [
- 0 => Facing::EAST,
- 1 => Facing::WEST,
- 2 => Facing::SOUTH,
- 3 => Facing::NORTH
- ]);
- }
-
- /** @throws BlockStateDeserializeException */
- public function readLegacyHorizontalFacing() : int{
- return $this->parseFacingValue($this->readInt(BlockStateNames::DIRECTION), [
- 0 => Facing::SOUTH,
- 1 => Facing::WEST,
- 2 => Facing::NORTH,
- 3 => Facing::EAST
- ]);
+ return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic);
}
/**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
+ public function readWeirdoHorizontalFacing() : int{
+ return $this->mapIntFromInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus);
+ }
+
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
+ public function readLegacyHorizontalFacing() : int{
+ return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE);
+ }
+
+ /**
+ * @deprecated
* This is for trapdoors, because Mojang botched the conversion in 1.13
* @throws BlockStateDeserializeException
*/
public function read5MinusHorizontalFacing() : int{
- return $this->parseFacingValue($this->readInt(BlockStateNames::DIRECTION), [
- 0 => Facing::EAST,
- 1 => Facing::WEST,
- 2 => Facing::SOUTH,
- 3 => Facing::NORTH
- ]);
+ return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus);
}
/**
+ * @deprecated
* Used by pumpkins as of 1.20.0.23 beta
* @throws BlockStateDeserializeException
*/
public function readCardinalHorizontalFacing() : int{
- return match($raw = $this->readString(BlockStateNames::MC_CARDINAL_DIRECTION)){
- StringValues::MC_CARDINAL_DIRECTION_NORTH => Facing::NORTH,
- StringValues::MC_CARDINAL_DIRECTION_SOUTH => Facing::SOUTH,
- StringValues::MC_CARDINAL_DIRECTION_WEST => Facing::WEST,
- StringValues::MC_CARDINAL_DIRECTION_EAST => Facing::EAST,
- default => throw $this->badValueException(BlockStateNames::MC_CARDINAL_DIRECTION, $raw)
- };
+ return $this->mapIntFromString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readCoralFacing() : int{
- return $this->parseFacingValue($this->readInt(BlockStateNames::CORAL_DIRECTION), [
- 0 => Facing::WEST,
- 1 => Facing::EAST,
- 2 => Facing::NORTH,
- 3 => Facing::SOUTH
- ]);
+ return $this->mapIntFromInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readFacingWithoutDown() : int{
$result = $this->readFacingDirection();
if($result === Facing::DOWN){ //shouldn't be legal, but 1.13 allows it
@@ -257,6 +247,10 @@ final class BlockStateReader{
return $result;
}
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readFacingWithoutUp() : int{
$result = $this->readFacingDirection();
if($result === Facing::UP){
@@ -266,23 +260,17 @@ final class BlockStateReader{
}
/**
- * @phpstan-return Axis::*
+ * @deprecated
* @throws BlockStateDeserializeException
*/
public function readPillarAxis() : int{
- $rawValue = $this->readString(BlockStateNames::PILLAR_AXIS);
- $value = [
- StringValues::PILLAR_AXIS_X => Axis::X,
- StringValues::PILLAR_AXIS_Y => Axis::Y,
- StringValues::PILLAR_AXIS_Z => Axis::Z
- ][$rawValue] ?? null;
- if($value === null){
- throw $this->badValueException(BlockStateNames::PILLAR_AXIS, $rawValue, "Invalid axis value");
- }
- return $value;
+ return $this->mapIntFromString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readSlabPosition() : SlabType{
return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){
StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM,
@@ -292,34 +280,25 @@ final class BlockStateReader{
}
/**
- * @phpstan-return Facing::UP|Facing::NORTH|Facing::SOUTH|Facing::WEST|Facing::EAST
+ * @deprecated
* @throws BlockStateDeserializeException
*/
public function readTorchFacing() : int{
- //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036)
- return match($rawValue = $this->readString(BlockStateNames::TORCH_FACING_DIRECTION)){
- StringValues::TORCH_FACING_DIRECTION_EAST => Facing::WEST,
- StringValues::TORCH_FACING_DIRECTION_NORTH => Facing::SOUTH,
- StringValues::TORCH_FACING_DIRECTION_SOUTH => Facing::NORTH,
- StringValues::TORCH_FACING_DIRECTION_TOP => Facing::UP,
- StringValues::TORCH_FACING_DIRECTION_UNKNOWN => Facing::UP, //should be illegal, but 1.13 allows it
- StringValues::TORCH_FACING_DIRECTION_WEST => Facing::EAST,
- default => throw $this->badValueException(BlockStateNames::TORCH_FACING_DIRECTION, $rawValue, "Invalid torch facing"),
- };
+ return $this->mapIntFromString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readBellAttachmentType() : BellAttachmentType{
- return match($type = $this->readString(BlockStateNames::ATTACHMENT)){
- StringValues::ATTACHMENT_HANGING => BellAttachmentType::CEILING,
- StringValues::ATTACHMENT_STANDING => BellAttachmentType::FLOOR,
- StringValues::ATTACHMENT_SIDE => BellAttachmentType::ONE_WALL,
- StringValues::ATTACHMENT_MULTIPLE => BellAttachmentType::TWO_WALLS,
- default => throw $this->badValueException(BlockStateNames::ATTACHMENT, $type),
- };
+ return $this->readUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType);
}
- /** @throws BlockStateDeserializeException */
+ /**
+ * @deprecated
+ * @throws BlockStateDeserializeException
+ */
public function readWallConnectionType(string $name) : ?WallConnectionType{
return match($type = $this->readString($name)){
//TODO: this looks a bit confusing due to use of EAST, but the values are the same for all connections
@@ -332,6 +311,23 @@ final class BlockStateReader{
};
}
+ /**
+ * @deprecated
+ * @phpstan-template TEnum of \UnitEnum
+ * @phpstan-param EnumFromRawStateMap $map
+ * @phpstan-return TEnum
+ * @throws BlockStateDeserializeException
+ */
+ public function readUnitEnum(string $name, EnumFromRawStateMap $map) : \UnitEnum{
+ $value = $this->readString($name);
+
+ $mapped = $map->rawToValue($value);
+ if($mapped === null){
+ throw $this->badValueException($name, $value);
+ }
+ return $mapped;
+ }
+
/**
* Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled
*/
diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php
index 3e2215746..da3dbb387 100644
--- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php
+++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php
@@ -55,6 +55,9 @@ use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer;
use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Facing;
+/**
+ * @deprecated
+ */
final class BlockStateSerializerHelper{
public static function encodeButton(Button $block, Writer $out) : Writer{
return $out
@@ -77,6 +80,9 @@ final class BlockStateSerializerHelper{
return $out->writeInt(BlockStateNames::GROWTH, $block->getAge());
}
+ /**
+ * @deprecated
+ */
public static function encodeTorch(Torch $block, Writer $out) : Writer{
return $out
->writeTorchFacing($block->getFacing());
@@ -97,10 +103,14 @@ final class BlockStateSerializerHelper{
};
}
+ /**
+ * @deprecated
+ */
public static function encodeDoor(Door $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
- ->writeLegacyHorizontalFacing(Facing::rotateY($block->getFacing(), true))
+ //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());
}
@@ -110,13 +120,19 @@ final class BlockStateSerializerHelper{
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop());
}
+ /**
+ * @deprecated
+ */
public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{
return $out
- ->writeLegacyHorizontalFacing($block->getFacing())
+ ->writeCardinalHorizontalFacing($block->getFacing())
->writeBool(BlockStateNames::IN_WALL_BIT, $block->isInWall())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}
+ /**
+ * @deprecated
+ */
public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{
return $out
->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation());
@@ -134,6 +150,9 @@ final class BlockStateSerializerHelper{
->writeFacingDirection($block->getFacing());
}
+ /**
+ * @deprecated
+ */
public static function encodeLeaves(Leaves $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay())
@@ -158,11 +177,17 @@ final class BlockStateSerializerHelper{
->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType()));
}
+ /**
+ * @deprecated
+ */
public static function encodeQuartz(int $axis, Writer $out) : Writer{
return $out
->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway
}
+ /**
+ * @deprecated
+ */
public static function encodeSapling(Sapling $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::AGE_BIT, $block->isReady());
@@ -192,6 +217,9 @@ final class BlockStateSerializerHelper{
self::encodeSingleSlab($block, $singleId);
}
+ /**
+ * @deprecated
+ */
public static function encodeStairs(Stair $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown())
@@ -207,6 +235,9 @@ final class BlockStateSerializerHelper{
->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing);
}
+ /**
+ * @deprecated
+ */
public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{
return $out
->write5MinusHorizontalFacing($block->getFacing())
@@ -223,6 +254,9 @@ final class BlockStateSerializerHelper{
->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST));
}
+ /**
+ * @deprecated
+ */
public static function encodeWallSign(WallSign $block, Writer $out) : Writer{
return $out
->writeHorizontalFacing($block->getFacing());
diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php
index 5c0a427cc..ca5c12412 100644
--- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php
+++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php
@@ -23,45 +23,18 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\convert;
-use pocketmine\block\AmethystCluster;
-use pocketmine\block\Anvil;
-use pocketmine\block\Bamboo;
use pocketmine\block\Block;
-use pocketmine\block\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\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 function array_key_exists;
use function count;
-use function min;
final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
@@ -77,44 +50,52 @@ 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->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
- return $this->simpleCache[$stateData->getName()] ??= $this->deserializeBlock($stateData)->getStateId();
+ return $this->simpleCache[$stateData->getName()] ??= $this->deserializeToStateId($stateData);
}
//we can't cache blocks that have properties - go ahead and deserialize the slow way
- return $this->deserializeBlock($stateData)->getStateId();
+ return $this->deserializeToStateId($stateData);
+ }
+
+ private function deserializeToStateId(BlockStateData $stateData) : int{
+ $stateId = $this->deserializeBlock($stateData)->getStateId();
+ //plugin devs seem to keep missing this and causing core crashes, so we need to verify this at the earliest
+ //available opportunity
+ if(!RuntimeBlockStateRegistry::getInstance()->hasStateId($stateId)){
+ throw new \LogicException("State ID $stateId returned by deserializer for " . $stateData->getName() . " is not registered in RuntimeBlockStateRegistry");
+ }
+ return $stateId;
}
/** @phpstan-param \Closure(Reader) : Block $c */
public function map(string $id, \Closure $c) : void{
- if(array_key_exists($id, $this->deserializeFuncs)){
- throw new \InvalidArgumentException("Deserializer is already assigned for \"$id\"");
- }
$this->deserializeFuncs[$id] = $c;
+ $this->simpleCache = [];
}
- /** @phpstan-param \Closure() : Block $getBlock */
+ /**
+ * Returns the existing data deserializer for the given ID, or null if none exists.
+ * This may be useful if you need to override a deserializer, but still want to be able to fall back to the original.
+ *
+ * @phpstan-return ?\Closure(Reader) : Block
+ */
+ public function getDeserializerForId(string $id) : ?\Closure{
+ return $this->deserializeFuncs[$id] ?? null;
+ }
+
+ /**
+ * @deprecated
+ * @phpstan-param \Closure() : Block $getBlock
+ */
public function mapSimple(string $id, \Closure $getBlock) : void{
$this->map($id, $getBlock);
}
/**
+ * @deprecated
* @phpstan-param \Closure(Reader) : Slab $getBlock
*/
public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock) : void{
@@ -123,1669 +104,22 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
}
/**
+ * @deprecated
* @phpstan-param \Closure() : Stair $getBlock
*/
public function mapStairs(string $id, \Closure $getBlock) : void{
$this->map($id, fn(Reader $in) : Stair => Helper::decodeStairs($getBlock(), $in));
}
- /** @phpstan-param \Closure() : Wood $getBlock */
+ /**
+ * @deprecated
+ * @phpstan-param \Closure() : Wood $getBlock
+ */
public function mapLog(string $unstrippedId, string $strippedId, \Closure $getBlock) : void{
$this->map($unstrippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), false, $in));
$this->map($strippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), true, $in));
}
- 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::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::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()));
- }
- }
-
- 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_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::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->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE));
- $this->map(Ids::COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
- $this->map(Ids::COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
- $this->map(Ids::COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
- $this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
- $this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
- $this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
- $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::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
- $this->map(Ids::EXPOSED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
- $this->map(Ids::EXPOSED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
- $this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
- $this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
- $this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
- $this->map(Ids::EXPOSED_COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
- $this->map(Ids::EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
- $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::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
- $this->map(Ids::OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
- $this->map(Ids::OXIDIZED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
- $this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
- $this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
- $this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
- $this->map(Ids::OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
- $this->map(Ids::OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
- $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::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::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE));
- $this->map(Ids::WAXED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE));
- $this->map(Ids::WAXED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
- $this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
- $this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
- $this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
- $this->map(Ids::WAXED_COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::WAXED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
- $this->map(Ids::WAXED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
- $this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
- $this->map(Ids::WAXED_EXPOSED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
- $this->map(Ids::WAXED_EXPOSED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
- $this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
- $this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
- $this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
- $this->map(Ids::WAXED_EXPOSED_COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::WAXED_EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
- $this->map(Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
- $this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
- $this->map(Ids::WAXED_OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
- $this->map(Ids::WAXED_OXIDIZED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
- $this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
- $this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
- $this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
- $this->map(Ids::WAXED_OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::WAXED_OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
- $this->map(Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
- $this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
- $this->map(Ids::WAXED_WEATHERED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
- $this->map(Ids::WAXED_WEATHERED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
- $this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
- $this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
- $this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
- $this->map(Ids::WAXED_WEATHERED_COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::WAXED_WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
- $this->map(Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
- $this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
- $this->map(Ids::WEATHERED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
- $this->map(Ids::WEATHERED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
- $this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
- $this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
- $this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
- $this->map(Ids::WEATHERED_COPPER_BULB, function(Reader $in) : Block{
- return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
- ->setLit($in->readBool(StateNames::LIT))
- ->setPowered($in->readBool(StateNames::POWERED_BIT));
- });
- $this->map(Ids::WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
- $this->map(Ids::WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
- $this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{
- return Blocks::WEEPING_VINES()
- ->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25));
- });
- $this->map(Ids::WHEAT, fn(Reader $in) => Helper::decodeCrops(Blocks::WHEAT(), $in));
- }
-
/** @throws BlockStateDeserializeException */
public function deserializeBlock(BlockStateData $blockStateData) : Block{
$id = $blockStateData->getName();
diff --git a/src/data/bedrock/block/convert/BlockStateWriter.php b/src/data/bedrock/block/convert/BlockStateWriter.php
index 63af92d10..0119bd02e 100644
--- a/src/data/bedrock/block/convert/BlockStateWriter.php
+++ b/src/data/bedrock/block/convert/BlockStateWriter.php
@@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateSerializeException;
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues;
+use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap;
+use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap;
+use pocketmine\data\bedrock\block\convert\property\ValueMappings;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\nbt\tag\ByteTag;
@@ -73,35 +76,47 @@ final class BlockStateWriter{
return $this;
}
- /** @return $this */
- public function writeFacingDirection(int $value) : self{
- $this->writeInt(BlockStateNames::FACING_DIRECTION, match($value){
- Facing::DOWN => 0,
- Facing::UP => 1,
- Facing::NORTH => 2,
- Facing::SOUTH => 3,
- Facing::WEST => 4,
- Facing::EAST => 5,
- default => throw new BlockStateSerializeException("Invalid Facing $value")
- });
- return $this;
- }
-
- /** @return $this */
- public function writeBlockFace(int $value) : self{
- $this->writeString(BlockStateNames::MC_BLOCK_FACE, match($value){
- Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN,
- Facing::UP => StringValues::MC_BLOCK_FACE_UP,
- Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH,
- Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH,
- Facing::WEST => StringValues::MC_BLOCK_FACE_WEST,
- Facing::EAST => StringValues::MC_BLOCK_FACE_EAST,
- default => throw new BlockStateSerializeException("Invalid Facing $value")
- });
+ /**
+ * @deprecated
+ * @phpstan-param IntFromRawStateMap $map
+ * @return $this
+ */
+ public function mapIntToString(string $name, IntFromRawStateMap $map, int $value) : self{
+ $raw = $map->valueToRaw($value);
+ $this->writeString($name, $raw);
return $this;
}
/**
+ * @deprecated
+ * @phpstan-param IntFromRawStateMap $map
+ * @return $this
+ */
+ public function mapIntToInt(string $name, IntFromRawStateMap $map, int $value) : self{
+ $raw = $map->valueToRaw($value);
+ $this->writeInt($name, $raw);
+ return $this;
+ }
+
+ /**
+ * @deprecated
+ * @return $this
+ */
+ public function writeFacingDirection(int $value) : self{
+ return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing, $value);
+ }
+
+ /**
+ * @deprecated
+ * @return $this
+ */
+ public function writeBlockFace(int $value) : self{
+ $this->mapIntToString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace, $value);
+ return $this;
+ }
+
+ /**
+ * @deprecated
* @param int[] $faces
* @phpstan-param array $faces
* @return $this
@@ -123,86 +138,69 @@ final class BlockStateWriter{
return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result);
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeEndRodFacingDirection(int $value) : self{
//end rods are stupid in bedrock and have everything except up/down the wrong way round
return $this->writeFacingDirection(Facing::axis($value) !== Axis::Y ? Facing::opposite($value) : $value);
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeHorizontalFacing(int $value) : self{
- if($value === Facing::UP || $value === Facing::DOWN){
- throw new BlockStateSerializeException("Y-axis facing is not allowed");
- }
-
- return $this->writeFacingDirection($value);
- }
-
- /** @return $this */
- public function writeWeirdoHorizontalFacing(int $value) : self{
- $this->writeInt(BlockStateNames::WEIRDO_DIRECTION, match($value){
- Facing::EAST => 0,
- Facing::WEST => 1,
- Facing::SOUTH => 2,
- Facing::NORTH => 3,
- default => throw new BlockStateSerializeException("Invalid horizontal facing $value")
- });
- return $this;
- }
-
- /** @return $this */
- public function writeLegacyHorizontalFacing(int $value) : self{
- $this->writeInt(BlockStateNames::DIRECTION, match($value){
- Facing::SOUTH => 0,
- Facing::WEST => 1,
- Facing::NORTH => 2,
- Facing::EAST => 3,
- default => throw new BlockStateSerializeException("Invalid horizontal facing $value")
- });
- return $this;
+ return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic, $value);
}
/**
+ * @deprecated
+ * @return $this
+ */
+ public function writeWeirdoHorizontalFacing(int $value) : self{
+ return $this->mapIntToInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value);
+ }
+
+ /**
+ * @deprecated
+ * @return $this
+ */
+ public function writeLegacyHorizontalFacing(int $value) : self{
+ return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE, $value);
+ }
+
+ /**
+ * @deprecated
* This is for trapdoors, because Mojang botched the conversion in 1.13
* @return $this
*/
public function write5MinusHorizontalFacing(int $value) : self{
- return $this->writeInt(BlockStateNames::DIRECTION, match($value){
- Facing::EAST => 0,
- Facing::WEST => 1,
- Facing::SOUTH => 2,
- Facing::NORTH => 3,
- default => throw new BlockStateSerializeException("Invalid horizontal facing $value")
- });
+ return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value);
}
/**
+ * @deprecated
* Used by pumpkins as of 1.20.0.23 beta
* @return $this
*/
public function writeCardinalHorizontalFacing(int $value) : self{
- return $this->writeString(BlockStateNames::MC_CARDINAL_DIRECTION, match($value){
- Facing::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH,
- Facing::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST,
- Facing::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH,
- Facing::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST,
- default => throw new BlockStateSerializeException("Invalid horizontal facing $value")
- });
+ return $this->mapIntToString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection, $value);
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeCoralFacing(int $value) : self{
- $this->writeInt(BlockStateNames::CORAL_DIRECTION, match($value){
- Facing::WEST => 0,
- Facing::EAST => 1,
- Facing::NORTH => 2,
- Facing::SOUTH => 3,
- default => throw new BlockStateSerializeException("Invalid horizontal facing $value")
- });
- return $this;
+ return $this->mapIntToInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, $value);
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeFacingWithoutDown(int $value) : self{
if($value === Facing::DOWN){
throw new BlockStateSerializeException("Invalid facing DOWN");
@@ -211,7 +209,10 @@ final class BlockStateWriter{
return $this;
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeFacingWithoutUp(int $value) : self{
if($value === Facing::UP){
throw new BlockStateSerializeException("Invalid facing UP");
@@ -220,18 +221,19 @@ final class BlockStateWriter{
return $this;
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writePillarAxis(int $axis) : self{
- $this->writeString(BlockStateNames::PILLAR_AXIS, match($axis){
- Axis::X => StringValues::PILLAR_AXIS_X,
- Axis::Y => StringValues::PILLAR_AXIS_Y,
- Axis::Z => StringValues::PILLAR_AXIS_Z,
- default => throw new BlockStateSerializeException("Invalid axis $axis")
- });
+ $this->mapIntToString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis, $axis);
return $this;
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeSlabPosition(SlabType $slabType) : self{
$this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){
SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP,
@@ -241,32 +243,27 @@ final class BlockStateWriter{
return $this;
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeTorchFacing(int $facing) : self{
- //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036)
- $this->writeString(BlockStateNames::TORCH_FACING_DIRECTION, match($facing){
- Facing::UP => StringValues::TORCH_FACING_DIRECTION_TOP,
- Facing::SOUTH => StringValues::TORCH_FACING_DIRECTION_NORTH,
- Facing::NORTH => StringValues::TORCH_FACING_DIRECTION_SOUTH,
- Facing::EAST => StringValues::TORCH_FACING_DIRECTION_WEST,
- Facing::WEST => StringValues::TORCH_FACING_DIRECTION_EAST,
- default => throw new BlockStateSerializeException("Invalid Torch facing $facing")
- });
+ $this->mapIntToString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing, $facing);
return $this;
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{
- $this->writeString(BlockStateNames::ATTACHMENT, match($attachmentType){
- BellAttachmentType::FLOOR => StringValues::ATTACHMENT_STANDING,
- BellAttachmentType::CEILING => StringValues::ATTACHMENT_HANGING,
- BellAttachmentType::ONE_WALL => StringValues::ATTACHMENT_SIDE,
- BellAttachmentType::TWO_WALLS => StringValues::ATTACHMENT_MULTIPLE,
- });
- return $this;
+ return $this->writeUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, $attachmentType);
}
- /** @return $this */
+ /**
+ * @deprecated
+ * @return $this
+ */
public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{
$this->writeString($name, match($wallConnectionType){
null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE,
@@ -276,6 +273,21 @@ final class BlockStateWriter{
return $this;
}
+ /**
+ * @deprecated
+ * @phpstan-template TEnum of \UnitEnum
+ * @phpstan-param EnumFromRawStateMap $map
+ * @phpstan-param TEnum $case
+ *
+ * @return $this
+ */
+ public function writeUnitEnum(string $name, EnumFromRawStateMap $map, \UnitEnum $case) : self{
+ $value = $map->valueToRaw($case);
+ $this->writeString($name, $value);
+
+ return $this;
+ }
+
public function getBlockStateData() : BlockStateData{
return BlockStateData::current($this->id, $this->states);
}
diff --git a/src/data/bedrock/block/convert/FlattenedIdModel.php b/src/data/bedrock/block/convert/FlattenedIdModel.php
new file mode 100644
index 000000000..50bf5cdd9
--- /dev/null
+++ b/src/data/bedrock/block/convert/FlattenedIdModel.php
@@ -0,0 +1,107 @@
+>
+ */
+ private array $idComponents = [];
+
+ /**
+ * @var Property[]
+ * @phpstan-var list>
+ */
+ private array $properties = [];
+
+ /**
+ * @phpstan-param TBlock $block
+ */
+ private function __construct(
+ private Block $block
+ ){}
+
+ /**
+ * @phpstan-template TBlock_ of Block
+ * @phpstan-param TBlock_ $block
+ * @return self
+ */
+ public static function create(Block $block) : self{
+ /** @phpstan-var self $result */
+ $result = new self($block);
+ return $result;
+ }
+
+ /** @phpstan-return TBlock */
+ public function getBlock() : Block{ return $this->block; }
+
+ /**
+ * @return string[]|StringProperty[]
+ * @phpstan-return list>
+ */
+ public function getIdComponents() : array{ return $this->idComponents; }
+
+ /**
+ * @return Property[]
+ * @phpstan-return list>
+ */
+ public function getProperties() : array{ return $this->properties; }
+
+ /**
+ * @param string[]|StringProperty[] $components
+ * @phpstan-param non-empty-list> $components
+ * @return $this
+ * @phpstan-this-out self
+ */
+ public function idComponents(array $components) : self{
+ $this->idComponents = $components;
+ return $this;
+ }
+
+ /**
+ * @param Property[] $properties
+ * @phpstan-param non-empty-list> $properties
+ * @return $this
+ * @phpstan-this-out self
+ */
+ public function properties(array $properties) : self{
+ $this->properties = $properties;
+ return $this;
+ }
+}
diff --git a/src/data/bedrock/block/convert/Model.php b/src/data/bedrock/block/convert/Model.php
new file mode 100644
index 000000000..3474a8932
--- /dev/null
+++ b/src/data/bedrock/block/convert/Model.php
@@ -0,0 +1,82 @@
+>
+ */
+ private array $properties = [];
+
+ /**
+ * @phpstan-param TBlock $block
+ */
+ private function __construct(
+ private Block $block,
+ private string $id
+ ){}
+
+ /** @phpstan-return TBlock */
+ public function getBlock() : Block{ return $this->block; }
+
+ public function getId() : string{ return $this->id; }
+
+ /**
+ * @return Property[]
+ * @phpstan-return list>
+ */
+ public function getProperties() : array{ return $this->properties; }
+
+ /**
+ * @phpstan-template TBlock_ of Block
+ * @phpstan-param TBlock_ $block
+ * @phpstan-return self
+ */
+ public static function create(Block $block, string $id) : self{
+ return new self($block, $id);
+ }
+
+ /**
+ * @param Property[] $properties
+ * @phpstan-param list> $properties
+ * @phpstan-return $this
+ */
+ public function properties(array $properties) : self{
+ $this->properties = $properties;
+ return $this;
+ }
+}
diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php
new file mode 100644
index 000000000..f339ce3ef
--- /dev/null
+++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php
@@ -0,0 +1,1646 @@
+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,
+ IntFromRawStateMap::int([
+ Facing::NORTH => BlockLegacyMetadata::VINE_FLAG_NORTH,
+ Facing::SOUTH => BlockLegacyMetadata::VINE_FLAG_SOUTH,
+ Facing::WEST => BlockLegacyMetadata::VINE_FLAG_WEST,
+ Facing::EAST => BlockLegacyMetadata::VINE_FLAG_EAST,
+ ]),
+ fn(Vine $b) => $b->getFaces(),
+ fn(Vine $b, array $v) => $b->setFaces($v)
+ )
+ ]));
+
+ $reg->mapModel(Model::create(Blocks::SWEET_BERRY_BUSH(), Ids::SWEET_BERRY_BUSH)->properties([
+ //TODO: berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7
+ new IntProperty(StateNames::GROWTH, 0, 7, fn(SweetBerryBush $b) => $b->getAge(), fn(SweetBerryBush $b, int $v) => $b->setAge(min($v, SweetBerryBush::STAGE_MATURE)))
+ ]));
+ $reg->mapModel(Model::create(Blocks::TORCHFLOWER_CROP(), Ids::TORCHFLOWER_CROP)->properties([
+ //TODO: this property can have values 0-7, but only 0-1 are valid
+ new IntProperty(StateNames::GROWTH, 0, 7, fn(TorchflowerCrop $b) => $b->isReady() ? 1 : 0, fn(TorchflowerCrop $b, int $v) => $b->setReady($v !== 0))
+ ]));
+ }
+
+ private static function registerCoralMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL())->idComponents([...$commonProperties->coralIdPrefixes, "_coral"]));
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL_BLOCK())->idComponents([...$commonProperties->coralIdPrefixes, "_coral_block"]));
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL_FAN())
+ ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_fan"])
+ ->properties([
+ new ValueFromIntProperty(StateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis, fn(FloorCoralFan $b) => $b->getAxis(), fn(FloorCoralFan $b, int $v) => $b->setAxis($v))
+ ])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::WALL_CORAL_FAN())
+ ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_wall_fan"])
+ ->properties([
+ new ValueFromIntProperty(StateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, fn(HorizontalFacing $b) => $b->getFacing(), fn(HorizontalFacing $b, int $v) => $b->setFacing($v)),
+ ])
+ );
+ }
+
+ private static function registerCopperMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_BULB())
+ ->idComponents([...$commonProperties->copperIdPrefixes, "copper_bulb"])
+ ->properties([
+ $commonProperties->lit,
+ new BoolProperty(StateNames::POWERED_BIT, fn(PoweredByRedstone $b) => $b->isPowered(), fn(PoweredByRedstone $b, bool $v) => $b->setPowered($v)),
+ ])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER())
+ ->idComponents([
+ ...$commonProperties->copperIdPrefixes,
+ "copper",
+ //HACK: the non-waxed, non-oxidised variant has a _block suffix, but none of the others do
+ new BoolFromStringProperty("bruhhhh", "", "_block", fn(Copper $b) => !$b->isWaxed() && $b->getOxidation() === CopperOxidation::NONE, fn() => null)
+ ])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CHISELED_COPPER())->idComponents([...$commonProperties->copperIdPrefixes, "chiseled_copper"]));
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_GRATE())->idComponents([...$commonProperties->copperIdPrefixes, "copper_grate"]));
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER())->idComponents([...$commonProperties->copperIdPrefixes, "cut_copper"]));
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER_STAIRS())
+ ->idComponents([...$commonProperties->copperIdPrefixes, "cut_copper_stairs"])
+ ->properties($commonProperties->stairProperties)
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_TRAPDOOR())
+ ->idComponents([...$commonProperties->copperIdPrefixes, "copper_trapdoor"])
+ ->properties($commonProperties->trapdoorProperties)
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_DOOR())
+ ->idComponents([...$commonProperties->copperIdPrefixes, "copper_door"])
+ ->properties($commonProperties->doorProperties)
+ );
+
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER_SLAB())
+ ->idComponents([
+ ...$commonProperties->copperIdPrefixes,
+ $commonProperties->slabIdInfix,
+ "cut_copper_slab"
+ ])
+ ->properties([$commonProperties->slabPositionProperty])
+ );
+ }
+
+ private static function registerFlattenedEnumMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ //A
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::ANVIL())
+ ->idComponents([
+ new ValueFromStringProperty("id", IntFromRawStateMap::string([
+ 0 => Ids::ANVIL,
+ 1 => Ids::CHIPPED_ANVIL,
+ 2 => Ids::DAMAGED_ANVIL,
+ ]), fn(Anvil $b) => $b->getDamage(), fn(Anvil $b, int $v) => $b->setDamage($v))
+ ])
+ ->properties([$commonProperties->horizontalFacingCardinal])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::AMETHYST_CLUSTER())
+ ->idComponents([
+ new ValueFromStringProperty("id", IntFromRawStateMap::string([
+ AmethystCluster::STAGE_SMALL_BUD => Ids::SMALL_AMETHYST_BUD,
+ AmethystCluster::STAGE_MEDIUM_BUD => Ids::MEDIUM_AMETHYST_BUD,
+ AmethystCluster::STAGE_LARGE_BUD => Ids::LARGE_AMETHYST_BUD,
+ AmethystCluster::STAGE_CLUSTER => Ids::AMETHYST_CLUSTER
+ ]), fn(AmethystCluster $b) => $b->getStage(), fn(AmethystCluster $b, int $v) => $b->setStage($v))
+ ])
+ ->properties([$commonProperties->blockFace])
+ );
+
+ //C
+ //This one is a special offender :<
+ //I have no idea why this only has 3 IDs - there are 4 in Java and 4 visually distinct states in Bedrock
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CAVE_VINES())
+ ->idComponents([
+ "minecraft:cave_vines",
+ new ValueFromStringProperty(
+ "variant",
+ EnumFromRawStateMap::string(FlattenedCaveVinesVariant::class, fn(FlattenedCaveVinesVariant $case) => $case->value),
+ fn(CaveVines $b) => $b->hasBerries() ?
+ ($b->isHead() ?
+ FlattenedCaveVinesVariant::HEAD_WITH_BERRIES :
+ FlattenedCaveVinesVariant::BODY_WITH_BERRIES) :
+ FlattenedCaveVinesVariant::NO_BERRIES,
+ fn(CaveVines $b, FlattenedCaveVinesVariant $v) => match($v){
+ FlattenedCaveVinesVariant::HEAD_WITH_BERRIES => $b->setBerries(true)->setHead(true),
+ FlattenedCaveVinesVariant::BODY_WITH_BERRIES => $b->setBerries(true)->setHead(false),
+ FlattenedCaveVinesVariant::NO_BERRIES => $b->setBerries(false)->setHead(false), //assume this isn't a head, since we don't have enough information
+ }
+ )
+ ])
+ ->properties([
+ new IntProperty(StateNames::GROWING_PLANT_AGE, 0, 25, fn(CaveVines $b) => $b->getAge(), fn(CaveVines $b, int $v) => $b->setAge($v)),
+ ])
+ );
+
+ //D
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DIRT())
+ ->idComponents([
+ new ValueFromStringProperty("id", EnumFromRawStateMap::string(DirtType::class, fn(DirtType $case) => match ($case) {
+ DirtType::NORMAL => Ids::DIRT,
+ DirtType::COARSE => Ids::COARSE_DIRT,
+ DirtType::ROOTED => Ids::DIRT_WITH_ROOTS,
+ }), fn(Dirt $b) => $b->getDirtType(), fn(Dirt $b, DirtType $v) => $b->setDirtType($v))
+ ])
+ );
+
+ //F
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::FROGLIGHT())
+ ->idComponents([
+ new ValueFromStringProperty("id", ValueMappings::getInstance()->froglightType, fn(Froglight $b) => $b->getFroglightType(), fn(Froglight $b, FroglightType $v) => $b->setFroglightType($v)),
+ ])
+ ->properties([$commonProperties->pillarAxis])
+ );
+
+ //L
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::LIGHT())
+ ->idComponents([
+ "minecraft:light_block_",
+ //this is a bit shit but it's easier than adapting IntProperty to support flattening :D
+ new ValueFromStringProperty(
+ "light_level",
+ IntFromRawStateMap::string(array_map(strval(...), range(0, 15))),
+ fn(Light $b) => $b->getLightLevel(),
+ fn(Light $b, int $v) => $b->setLightLevel($v)
+ )
+ ])
+ );
+
+ //M
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::MOB_HEAD())
+ ->idComponents([
+ new ValueFromStringProperty("id", ValueMappings::getInstance()->mobHeadType, fn(MobHead $b) => $b->getMobHeadType(), fn(MobHead $b, MobHeadType $v) => $b->setMobHeadType($v)),
+ ])
+ ->properties([
+ new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptDown, fn(MobHead $b) => $b->getFacing(), fn(MobHead $b, int $v) => $b->setFacing($v))
+ ])
+ );
+
+ foreach([
+ [Blocks::LAVA(), "lava"],
+ [Blocks::WATER(), "water"]
+ ] as [$block, $idSuffix]){
+ $reg->mapFlattenedId(FlattenedIdModel::create($block)
+ ->idComponents([...$commonProperties->liquidIdPrefixes, $idSuffix])
+ ->properties([$commonProperties->liquidData])
+ );
+ }
+ }
+
+ private static function registerFlattenedBoolMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ foreach([
+ [Blocks::BLAST_FURNACE(), "blast_furnace"],
+ [Blocks::FURNACE(), "furnace"],
+ [Blocks::SMOKER(), "smoker"]
+ ] as [$block, $idSuffix]){
+ $reg->mapFlattenedId(FlattenedIdModel::create($block)
+ ->idComponents([...$commonProperties->furnaceIdPrefixes, $idSuffix])
+ ->properties([$commonProperties->horizontalFacingCardinal])
+ );
+ }
+
+ foreach([
+ [Blocks::REDSTONE_LAMP(), "redstone_lamp"],
+ [Blocks::REDSTONE_ORE(), "redstone_ore"],
+ [Blocks::DEEPSLATE_REDSTONE_ORE(), "deepslate_redstone_ore"]
+ ] as [$block, $idSuffix]){
+ $reg->mapFlattenedId(FlattenedIdModel::create($block)->idComponents(["minecraft:", $commonProperties->litIdInfix, $idSuffix]));
+ }
+
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DAYLIGHT_SENSOR())
+ ->idComponents([
+ "minecraft:daylight_detector",
+ new BoolFromStringProperty("inverted", "", "_inverted", fn(DaylightSensor $b) => $b->isInverted(), fn(DaylightSensor $b, bool $v) => $b->setInverted($v))
+ ])
+ ->properties([$commonProperties->analogRedstoneSignal])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_REPEATER())
+ ->idComponents([
+ "minecraft:",
+ new BoolFromStringProperty("powered", "un", "", fn(RedstoneRepeater $b) => $b->isPowered(), fn(RedstoneRepeater $b, bool $v) => $b->setPowered($v)),
+ "powered_repeater"
+ ])
+ ->properties([
+ $commonProperties->horizontalFacingCardinal,
+ new IntProperty(StateNames::REPEATER_DELAY, 0, 3, fn(RedstoneRepeater $b) => $b->getDelay(), fn(RedstoneRepeater $b, int $v) => $b->setDelay($v), offset: 1),
+ ])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_COMPARATOR())
+ ->idComponents([
+ "minecraft:",
+ //this property also appears in the state, so we ignore it in the ID
+ //this is baked here purely to keep minecraft happy
+ new BoolFromStringProperty("dummy_powered", "un", "", fn(RedstoneComparator $b) => $b->isPowered(), fn() => null),
+ "powered_comparator"
+ ])
+ ->properties([
+ $commonProperties->horizontalFacingCardinal,
+ new BoolProperty(StateNames::OUTPUT_LIT_BIT, fn(RedstoneComparator $b) => $b->isPowered(), fn(RedstoneComparator $b, bool $v) => $b->setPowered($v)),
+ new BoolProperty(StateNames::OUTPUT_SUBTRACT_BIT, fn(RedstoneComparator $b) => $b->isSubtractMode(), fn(RedstoneComparator $b, bool $v) => $b->setSubtractMode($v)),
+ ])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_TORCH())
+ ->idComponents([
+ "minecraft:",
+ new BoolFromStringProperty("lit", "unlit_", "", fn(RedstoneTorch $b) => $b->isLit(), fn(RedstoneTorch $b, bool $v) => $b->setLit($v)),
+ "redstone_torch"
+ ])
+ ->properties([$commonProperties->torchFacing])
+ );
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::SPONGE())->idComponents([
+ "minecraft:",
+ new BoolFromStringProperty("wet", "", "wet_", fn(Sponge $b) => $b->isWet(), fn(Sponge $b, bool $v) => $b->setWet($v)),
+ "sponge"
+ ]));
+ $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::TNT())
+ ->idComponents([
+ "minecraft:",
+ new BoolFromStringProperty("underwater", "", "underwater_", fn(TNT $b) => $b->worksUnderwater(), fn(TNT $b, bool $v) => $b->setWorksUnderwater($v)),
+ "tnt"
+ ])
+ ->properties([
+ new BoolProperty(StateNames::EXPLODE_BIT, fn(TNT $b) => $b->isUnstable(), fn(TNT $b, bool $v) => $b->setUnstable($v)),
+ ])
+ );
+ }
+
+ private static function registerStoneLikeSlabMappings(BlockSerializerDeserializerRegistrar $reg) : void{
+ $reg->mapSlab(Blocks::ANDESITE_SLAB(), "andesite");
+ $reg->mapSlab(Blocks::BLACKSTONE_SLAB(), "blackstone");
+ $reg->mapSlab(Blocks::BRICK_SLAB(), "brick");
+ $reg->mapSlab(Blocks::COBBLED_DEEPSLATE_SLAB(), "cobbled_deepslate");
+ $reg->mapSlab(Blocks::COBBLESTONE_SLAB(), "cobblestone");
+ $reg->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), "cut_red_sandstone");
+ $reg->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), "cut_sandstone");
+ $reg->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), "dark_prismarine");
+ $reg->mapSlab(Blocks::DEEPSLATE_BRICK_SLAB(), "deepslate_brick");
+ $reg->mapSlab(Blocks::DEEPSLATE_TILE_SLAB(), "deepslate_tile");
+ $reg->mapSlab(Blocks::DIORITE_SLAB(), "diorite");
+ $reg->mapSlab(Blocks::END_STONE_BRICK_SLAB(), "end_stone_brick");
+ $reg->mapSlab(Blocks::FAKE_WOODEN_SLAB(), "petrified_oak");
+ $reg->mapSlab(Blocks::GRANITE_SLAB(), "granite");
+ $reg->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), "mossy_cobblestone");
+ $reg->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), "mossy_stone_brick");
+ $reg->mapSlab(Blocks::MUD_BRICK_SLAB(), "mud_brick");
+ $reg->mapSlab(Blocks::NETHER_BRICK_SLAB(), "nether_brick");
+ $reg->mapSlab(Blocks::POLISHED_ANDESITE_SLAB(), "polished_andesite");
+ $reg->mapSlab(Blocks::POLISHED_BLACKSTONE_BRICK_SLAB(), "polished_blackstone_brick");
+ $reg->mapSlab(Blocks::POLISHED_BLACKSTONE_SLAB(), "polished_blackstone");
+ $reg->mapSlab(Blocks::POLISHED_DEEPSLATE_SLAB(), "polished_deepslate");
+ $reg->mapSlab(Blocks::POLISHED_DIORITE_SLAB(), "polished_diorite");
+ $reg->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), "polished_granite");
+ $reg->mapSlab(Blocks::POLISHED_TUFF_SLAB(), "polished_tuff");
+ $reg->mapSlab(Blocks::PRISMARINE_BRICKS_SLAB(), "prismarine_brick");
+ $reg->mapSlab(Blocks::PRISMARINE_SLAB(), "prismarine");
+ $reg->mapSlab(Blocks::PURPUR_SLAB(), "purpur");
+ $reg->mapSlab(Blocks::QUARTZ_SLAB(), "quartz");
+ $reg->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), "red_nether_brick");
+ $reg->mapSlab(Blocks::RED_SANDSTONE_SLAB(), "red_sandstone");
+ $reg->mapSlab(Blocks::RESIN_BRICK_SLAB(), "resin_brick");
+ $reg->mapSlab(Blocks::SANDSTONE_SLAB(), "sandstone");
+ $reg->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), "smooth_quartz");
+ $reg->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), "smooth_red_sandstone");
+ $reg->mapSlab(Blocks::SMOOTH_SANDSTONE_SLAB(), "smooth_sandstone");
+ $reg->mapSlab(Blocks::SMOOTH_STONE_SLAB(), "smooth_stone");
+ $reg->mapSlab(Blocks::STONE_BRICK_SLAB(), "stone_brick");
+ $reg->mapSlab(Blocks::STONE_SLAB(), "normal_stone");
+ $reg->mapSlab(Blocks::TUFF_BRICK_SLAB(), "tuff_brick");
+ $reg->mapSlab(Blocks::TUFF_SLAB(), "tuff");
+ }
+
+ private static function registerStoneLikeStairMappings(BlockSerializerDeserializerRegistrar $reg) : void{
+ $reg->mapStairs(Blocks::ANDESITE_STAIRS(), Ids::ANDESITE_STAIRS);
+ $reg->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS);
+ $reg->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS);
+ $reg->mapStairs(Blocks::COBBLED_DEEPSLATE_STAIRS(), Ids::COBBLED_DEEPSLATE_STAIRS);
+ $reg->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS);
+ $reg->mapStairs(Blocks::DARK_PRISMARINE_STAIRS(), Ids::DARK_PRISMARINE_STAIRS);
+ $reg->mapStairs(Blocks::DEEPSLATE_BRICK_STAIRS(), Ids::DEEPSLATE_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::DEEPSLATE_TILE_STAIRS(), Ids::DEEPSLATE_TILE_STAIRS);
+ $reg->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS);
+ $reg->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS);
+ $reg->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS);
+ $reg->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::MUD_BRICK_STAIRS(), Ids::MUD_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS);
+ $reg->mapStairs(Blocks::POLISHED_BLACKSTONE_BRICK_STAIRS(), Ids::POLISHED_BLACKSTONE_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::POLISHED_BLACKSTONE_STAIRS(), Ids::POLISHED_BLACKSTONE_STAIRS);
+ $reg->mapStairs(Blocks::POLISHED_DEEPSLATE_STAIRS(), Ids::POLISHED_DEEPSLATE_STAIRS);
+ $reg->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS);
+ $reg->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS);
+ $reg->mapStairs(Blocks::POLISHED_TUFF_STAIRS(), Ids::POLISHED_TUFF_STAIRS);
+ $reg->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS);
+ $reg->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS);
+ $reg->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS);
+ $reg->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS);
+ $reg->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS);
+ $reg->mapStairs(Blocks::RESIN_BRICK_STAIRS(), Ids::RESIN_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS);
+ $reg->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS);
+ $reg->mapStairs(Blocks::SMOOTH_RED_SANDSTONE_STAIRS(), Ids::SMOOTH_RED_SANDSTONE_STAIRS);
+ $reg->mapStairs(Blocks::SMOOTH_SANDSTONE_STAIRS(), Ids::SMOOTH_SANDSTONE_STAIRS);
+ $reg->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::STONE_STAIRS(), Ids::NORMAL_STONE_STAIRS);
+ $reg->mapStairs(Blocks::TUFF_BRICK_STAIRS(), Ids::TUFF_BRICK_STAIRS);
+ $reg->mapStairs(Blocks::TUFF_STAIRS(), Ids::TUFF_STAIRS);
+ }
+
+ private static function registerStoneLikeWallMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ foreach([
+ Ids::ANDESITE_WALL => Blocks::ANDESITE_WALL(),
+ Ids::BLACKSTONE_WALL => Blocks::BLACKSTONE_WALL(),
+ Ids::BRICK_WALL => Blocks::BRICK_WALL(),
+ Ids::COBBLED_DEEPSLATE_WALL => Blocks::COBBLED_DEEPSLATE_WALL(),
+ Ids::COBBLESTONE_WALL => Blocks::COBBLESTONE_WALL(),
+ Ids::DEEPSLATE_BRICK_WALL => Blocks::DEEPSLATE_BRICK_WALL(),
+ Ids::DEEPSLATE_TILE_WALL => Blocks::DEEPSLATE_TILE_WALL(),
+ Ids::DIORITE_WALL => Blocks::DIORITE_WALL(),
+ Ids::END_STONE_BRICK_WALL => Blocks::END_STONE_BRICK_WALL(),
+ Ids::GRANITE_WALL => Blocks::GRANITE_WALL(),
+ Ids::MOSSY_COBBLESTONE_WALL => Blocks::MOSSY_COBBLESTONE_WALL(),
+ Ids::MOSSY_STONE_BRICK_WALL => Blocks::MOSSY_STONE_BRICK_WALL(),
+ Ids::MUD_BRICK_WALL => Blocks::MUD_BRICK_WALL(),
+ Ids::NETHER_BRICK_WALL => Blocks::NETHER_BRICK_WALL(),
+ Ids::POLISHED_BLACKSTONE_BRICK_WALL => Blocks::POLISHED_BLACKSTONE_BRICK_WALL(),
+ Ids::POLISHED_BLACKSTONE_WALL => Blocks::POLISHED_BLACKSTONE_WALL(),
+ Ids::POLISHED_DEEPSLATE_WALL => Blocks::POLISHED_DEEPSLATE_WALL(),
+ Ids::POLISHED_TUFF_WALL => Blocks::POLISHED_TUFF_WALL(),
+ Ids::PRISMARINE_WALL => Blocks::PRISMARINE_WALL(),
+ Ids::RED_NETHER_BRICK_WALL => Blocks::RED_NETHER_BRICK_WALL(),
+ Ids::RED_SANDSTONE_WALL => Blocks::RED_SANDSTONE_WALL(),
+ Ids::RESIN_BRICK_WALL => Blocks::RESIN_BRICK_WALL(),
+ Ids::SANDSTONE_WALL => Blocks::SANDSTONE_WALL(),
+ Ids::STONE_BRICK_WALL => Blocks::STONE_BRICK_WALL(),
+ Ids::TUFF_BRICK_WALL => Blocks::TUFF_BRICK_WALL(),
+ Ids::TUFF_WALL => Blocks::TUFF_WALL()
+ ] as $id => $block){
+ $reg->mapModel(Model::create($block, $id)->properties($commonProperties->wallProperties));
+ }
+ }
+
+ private static function registerWoodMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ //buttons
+ foreach([
+ [Blocks::ACACIA_BUTTON(), Ids::ACACIA_BUTTON],
+ [Blocks::BIRCH_BUTTON(), Ids::BIRCH_BUTTON],
+ [Blocks::CHERRY_BUTTON(), Ids::CHERRY_BUTTON],
+ [Blocks::CRIMSON_BUTTON(), Ids::CRIMSON_BUTTON],
+ [Blocks::DARK_OAK_BUTTON(), Ids::DARK_OAK_BUTTON],
+ [Blocks::JUNGLE_BUTTON(), Ids::JUNGLE_BUTTON],
+ [Blocks::MANGROVE_BUTTON(), Ids::MANGROVE_BUTTON],
+ [Blocks::OAK_BUTTON(), Ids::WOODEN_BUTTON],
+ [Blocks::PALE_OAK_BUTTON(), Ids::PALE_OAK_BUTTON],
+ [Blocks::SPRUCE_BUTTON(), Ids::SPRUCE_BUTTON],
+ [Blocks::WARPED_BUTTON(), Ids::WARPED_BUTTON]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties($commonProperties->buttonProperties));
+ }
+
+ //doors
+ foreach([
+ [Blocks::ACACIA_DOOR(), Ids::ACACIA_DOOR],
+ [Blocks::BIRCH_DOOR(), Ids::BIRCH_DOOR],
+ [Blocks::CHERRY_DOOR(), Ids::CHERRY_DOOR],
+ [Blocks::CRIMSON_DOOR(), Ids::CRIMSON_DOOR],
+ [Blocks::DARK_OAK_DOOR(), Ids::DARK_OAK_DOOR],
+ [Blocks::JUNGLE_DOOR(), Ids::JUNGLE_DOOR],
+ [Blocks::MANGROVE_DOOR(), Ids::MANGROVE_DOOR],
+ [Blocks::OAK_DOOR(), Ids::WOODEN_DOOR],
+ [Blocks::PALE_OAK_DOOR(), Ids::PALE_OAK_DOOR],
+ [Blocks::SPRUCE_DOOR(), Ids::SPRUCE_DOOR],
+ [Blocks::WARPED_DOOR(), Ids::WARPED_DOOR]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties($commonProperties->doorProperties));
+ }
+
+ //fences
+ foreach([
+ [Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE],
+ [Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE],
+ [Blocks::CHERRY_FENCE(), Ids::CHERRY_FENCE],
+ [Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE],
+ [Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE],
+ [Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE],
+ [Blocks::OAK_FENCE(), Ids::OAK_FENCE],
+ [Blocks::PALE_OAK_FENCE(), Ids::PALE_OAK_FENCE],
+ [Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE],
+ [Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE],
+ [Blocks::WARPED_FENCE(), Ids::WARPED_FENCE]
+ ] as [$block, $id]){
+ $reg->mapSimple($block, $id);
+ }
+
+ foreach([
+ [Blocks::ACACIA_FENCE_GATE(), Ids::ACACIA_FENCE_GATE],
+ [Blocks::BIRCH_FENCE_GATE(), Ids::BIRCH_FENCE_GATE],
+ [Blocks::CHERRY_FENCE_GATE(), Ids::CHERRY_FENCE_GATE],
+ [Blocks::DARK_OAK_FENCE_GATE(), Ids::DARK_OAK_FENCE_GATE],
+ [Blocks::JUNGLE_FENCE_GATE(), Ids::JUNGLE_FENCE_GATE],
+ [Blocks::MANGROVE_FENCE_GATE(), Ids::MANGROVE_FENCE_GATE],
+ [Blocks::OAK_FENCE_GATE(), Ids::FENCE_GATE],
+ [Blocks::PALE_OAK_FENCE_GATE(), Ids::PALE_OAK_FENCE_GATE],
+ [Blocks::SPRUCE_FENCE_GATE(), Ids::SPRUCE_FENCE_GATE],
+ [Blocks::CRIMSON_FENCE_GATE(), Ids::CRIMSON_FENCE_GATE],
+ [Blocks::WARPED_FENCE_GATE(), Ids::WARPED_FENCE_GATE]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties($commonProperties->fenceGateProperties));
+ }
+
+ foreach([
+ [Blocks::ACACIA_SIGN(), Ids::ACACIA_STANDING_SIGN],
+ [Blocks::BIRCH_SIGN(), Ids::BIRCH_STANDING_SIGN],
+ [Blocks::CHERRY_SIGN(), Ids::CHERRY_STANDING_SIGN],
+ [Blocks::DARK_OAK_SIGN(), Ids::DARKOAK_STANDING_SIGN],
+ [Blocks::JUNGLE_SIGN(), Ids::JUNGLE_STANDING_SIGN],
+ [Blocks::MANGROVE_SIGN(), Ids::MANGROVE_STANDING_SIGN],
+ [Blocks::OAK_SIGN(), Ids::STANDING_SIGN],
+ [Blocks::PALE_OAK_SIGN(), Ids::PALE_OAK_STANDING_SIGN],
+ [Blocks::SPRUCE_SIGN(), Ids::SPRUCE_STANDING_SIGN],
+ [Blocks::CRIMSON_SIGN(), Ids::CRIMSON_STANDING_SIGN],
+ [Blocks::WARPED_SIGN(), Ids::WARPED_STANDING_SIGN]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->floorSignLikeRotation]));
+ }
+
+ //logs
+ foreach([
+ [Blocks::ACACIA_LOG(), "acacia_log"],
+ [Blocks::BIRCH_LOG(), "birch_log"],
+ [Blocks::CHERRY_LOG(), "cherry_log"],
+ [Blocks::DARK_OAK_LOG(), "dark_oak_log"],
+ [Blocks::JUNGLE_LOG(), "jungle_log"],
+ [Blocks::MANGROVE_LOG(), "mangrove_log"],
+ [Blocks::OAK_LOG(), "oak_log"],
+ [Blocks::PALE_OAK_LOG(), "pale_oak_log"],
+ [Blocks::SPRUCE_LOG(), "spruce_log"],
+ [Blocks::CRIMSON_STEM(), "crimson_stem"],
+ [Blocks::WARPED_STEM(), "warped_stem"],
+
+ //all-sided logs
+ [Blocks::ACACIA_WOOD(), "acacia_wood"],
+ [Blocks::BIRCH_WOOD(), "birch_wood"],
+ [Blocks::CHERRY_WOOD(), "cherry_wood"],
+ [Blocks::DARK_OAK_WOOD(), "dark_oak_wood"],
+ [Blocks::JUNGLE_WOOD(), "jungle_wood"],
+ [Blocks::MANGROVE_WOOD(), "mangrove_wood"],
+ [Blocks::OAK_WOOD(), "oak_wood"],
+ [Blocks::PALE_OAK_WOOD(), "pale_oak_wood"],
+ [Blocks::SPRUCE_WOOD(), "spruce_wood"],
+ [Blocks::CRIMSON_HYPHAE(), "crimson_hyphae"],
+ [Blocks::WARPED_HYPHAE(), "warped_hyphae"]
+ ] as [$block, $idSuffix]){
+ $reg->mapFlattenedId(FlattenedIdModel::create($block)
+ ->idComponents([...$commonProperties->woodIdPrefixes, $idSuffix])
+ ->properties([$commonProperties->pillarAxis])
+ );
+ }
+
+ //planks
+ foreach([
+ [Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS],
+ [Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS],
+ [Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS],
+ [Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS],
+ [Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS],
+ [Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS],
+ [Blocks::OAK_PLANKS(), Ids::OAK_PLANKS],
+ [Blocks::PALE_OAK_PLANKS(), Ids::PALE_OAK_PLANKS],
+ [Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS],
+ [Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS],
+ [Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS]
+ ] as [$block, $id]){
+ $reg->mapSimple($block, $id);
+ }
+
+ //pressure plates
+ foreach([
+ [Blocks::ACACIA_PRESSURE_PLATE(), Ids::ACACIA_PRESSURE_PLATE],
+ [Blocks::BIRCH_PRESSURE_PLATE(), Ids::BIRCH_PRESSURE_PLATE],
+ [Blocks::CHERRY_PRESSURE_PLATE(), Ids::CHERRY_PRESSURE_PLATE],
+ [Blocks::DARK_OAK_PRESSURE_PLATE(), Ids::DARK_OAK_PRESSURE_PLATE],
+ [Blocks::JUNGLE_PRESSURE_PLATE(), Ids::JUNGLE_PRESSURE_PLATE],
+ [Blocks::MANGROVE_PRESSURE_PLATE(), Ids::MANGROVE_PRESSURE_PLATE],
+ [Blocks::OAK_PRESSURE_PLATE(), Ids::WOODEN_PRESSURE_PLATE],
+ [Blocks::PALE_OAK_PRESSURE_PLATE(), Ids::PALE_OAK_PRESSURE_PLATE],
+ [Blocks::SPRUCE_PRESSURE_PLATE(), Ids::SPRUCE_PRESSURE_PLATE],
+ [Blocks::CRIMSON_PRESSURE_PLATE(), Ids::CRIMSON_PRESSURE_PLATE],
+ [Blocks::WARPED_PRESSURE_PLATE(), Ids::WARPED_PRESSURE_PLATE]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties($commonProperties->simplePressurePlateProperties));
+ }
+
+ //slabs
+ foreach([
+ [Blocks::ACACIA_SLAB(), "acacia"],
+ [Blocks::BIRCH_SLAB(), "birch"],
+ [Blocks::CHERRY_SLAB(), "cherry"],
+ [Blocks::DARK_OAK_SLAB(), "dark_oak"],
+ [Blocks::JUNGLE_SLAB(), "jungle"],
+ [Blocks::MANGROVE_SLAB(), "mangrove"],
+ [Blocks::OAK_SLAB(), "oak"],
+ [Blocks::PALE_OAK_SLAB(), "pale_oak"],
+ [Blocks::SPRUCE_SLAB(), "spruce"],
+ [Blocks::CRIMSON_SLAB(), "crimson"],
+ [Blocks::WARPED_SLAB(), "warped"]
+ ] as [$block, $type]){
+ $reg->mapSlab($block, $type);
+ }
+
+ //stairs
+ foreach([
+ [Blocks::ACACIA_STAIRS(), Ids::ACACIA_STAIRS],
+ [Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS],
+ [Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS],
+ [Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS],
+ [Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS],
+ [Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS],
+ [Blocks::OAK_STAIRS(), Ids::OAK_STAIRS],
+ [Blocks::PALE_OAK_STAIRS(), Ids::PALE_OAK_STAIRS],
+ [Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS],
+ [Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS],
+ [Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS]
+ ] as [$block, $id]){
+ $reg->mapStairs($block, $id);
+ }
+
+ //trapdoors
+ foreach([
+ [Blocks::ACACIA_TRAPDOOR(), Ids::ACACIA_TRAPDOOR],
+ [Blocks::BIRCH_TRAPDOOR(), Ids::BIRCH_TRAPDOOR],
+ [Blocks::CHERRY_TRAPDOOR(), Ids::CHERRY_TRAPDOOR],
+ [Blocks::DARK_OAK_TRAPDOOR(), Ids::DARK_OAK_TRAPDOOR],
+ [Blocks::JUNGLE_TRAPDOOR(), Ids::JUNGLE_TRAPDOOR],
+ [Blocks::MANGROVE_TRAPDOOR(), Ids::MANGROVE_TRAPDOOR],
+ [Blocks::OAK_TRAPDOOR(), Ids::TRAPDOOR],
+ [Blocks::PALE_OAK_TRAPDOOR(), Ids::PALE_OAK_TRAPDOOR],
+ [Blocks::SPRUCE_TRAPDOOR(), Ids::SPRUCE_TRAPDOOR],
+ [Blocks::CRIMSON_TRAPDOOR(), Ids::CRIMSON_TRAPDOOR],
+ [Blocks::WARPED_TRAPDOOR(), Ids::WARPED_TRAPDOOR]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties($commonProperties->trapdoorProperties));
+ }
+
+ //wall signs
+ foreach([
+ [Blocks::ACACIA_WALL_SIGN(), Ids::ACACIA_WALL_SIGN],
+ [Blocks::BIRCH_WALL_SIGN(), Ids::BIRCH_WALL_SIGN],
+ [Blocks::CHERRY_WALL_SIGN(), Ids::CHERRY_WALL_SIGN],
+ [Blocks::DARK_OAK_WALL_SIGN(), Ids::DARKOAK_WALL_SIGN],
+ [Blocks::JUNGLE_WALL_SIGN(), Ids::JUNGLE_WALL_SIGN],
+ [Blocks::MANGROVE_WALL_SIGN(), Ids::MANGROVE_WALL_SIGN],
+ [Blocks::OAK_WALL_SIGN(), Ids::WALL_SIGN],
+ [Blocks::PALE_OAK_WALL_SIGN(), Ids::PALE_OAK_WALL_SIGN],
+ [Blocks::SPRUCE_WALL_SIGN(), Ids::SPRUCE_WALL_SIGN],
+ [Blocks::CRIMSON_WALL_SIGN(), Ids::CRIMSON_WALL_SIGN],
+ [Blocks::WARPED_WALL_SIGN(), Ids::WARPED_WALL_SIGN]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->horizontalFacingClassic]));
+ }
+ }
+
+ private static function registerTorchMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ foreach([
+ [Blocks::BLUE_TORCH(), Ids::COLORED_TORCH_BLUE],
+ [Blocks::GREEN_TORCH(), Ids::COLORED_TORCH_GREEN],
+ [Blocks::PURPLE_TORCH(), Ids::COLORED_TORCH_PURPLE],
+ [Blocks::RED_TORCH(), Ids::COLORED_TORCH_RED],
+ [Blocks::SOUL_TORCH(), Ids::SOUL_TORCH],
+ [Blocks::TORCH(), Ids::TORCH],
+ [Blocks::UNDERWATER_TORCH(), Ids::UNDERWATER_TORCH]
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->torchFacing]));
+ }
+ }
+
+ private static function registerChemistryMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ foreach([
+ [Blocks::COMPOUND_CREATOR(), Ids::COMPOUND_CREATOR],
+ [Blocks::ELEMENT_CONSTRUCTOR(), Ids::ELEMENT_CONSTRUCTOR],
+ [Blocks::LAB_TABLE(), Ids::LAB_TABLE],
+ [Blocks::MATERIAL_REDUCER(), Ids::MATERIAL_REDUCER],
+ ] as [$block, $id]){
+ $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->horizontalFacingSWNEInverted]));
+ }
+ }
+
+ private static function register1to1CustomMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
+ //TODO: some of these have repeated accessor refs, we might be able to deduplicate them
+ //A
+ $reg->mapModel(Model::create(Blocks::ACTIVATOR_RAIL(), Ids::ACTIVATOR_RAIL)->properties([
+ new BoolProperty(StateNames::RAIL_DATA_BIT, fn(ActivatorRail $b) => $b->isPowered(), fn(ActivatorRail $b, bool $v) => $b->setPowered($v)),
+ new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(ActivatorRail $b) => $b->getShape(), fn(ActivatorRail $b, int $v) => $b->setShape($v))
+ ]));
+
+ //B
+ $reg->mapModel(Model::create(Blocks::BAMBOO(), Ids::BAMBOO)->properties([
+ new ValueFromStringProperty(StateNames::BAMBOO_LEAF_SIZE, ValueMappings::getInstance()->bambooLeafSize, fn(Bamboo $b) => $b->getLeafSize(), fn(Bamboo $b, int $v) => $b->setLeafSize($v)),
+ new BoolProperty(StateNames::AGE_BIT, fn(Bamboo $b) => $b->isReady(), fn(Bamboo $b, bool $v) => $b->setReady($v)),
+ new BoolFromStringProperty(StateNames::BAMBOO_STALK_THICKNESS, StringValues::BAMBOO_STALK_THICKNESS_THIN, StringValues::BAMBOO_STALK_THICKNESS_THICK, fn(Bamboo $b) => $b->isThick(), fn(Bamboo $b, bool $v) => $b->setThick($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::BAMBOO_SAPLING(), Ids::BAMBOO_SAPLING)->properties([
+ new BoolProperty(StateNames::AGE_BIT, fn(BambooSapling $b) => $b->isReady(), fn(BambooSapling $b, bool $v) => $b->setReady($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::BANNER(), Ids::STANDING_BANNER)->properties([$commonProperties->floorSignLikeRotation]));
+ $reg->mapModel(Model::create(Blocks::BARREL(), Ids::BARREL)->properties([
+ $commonProperties->anyFacingClassic,
+ new BoolProperty(StateNames::OPEN_BIT, fn(Barrel $b) => $b->isOpen(), fn(Barrel $b, bool $v) => $b->setOpen($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::BASALT(), Ids::BASALT)->properties([$commonProperties->pillarAxis]));
+ $reg->mapModel(Model::create(Blocks::BED(), Ids::BED)->properties([
+ new BoolProperty(StateNames::HEAD_PIECE_BIT, fn(Bed $b) => $b->isHeadPart(), fn(Bed $b, bool $v) => $b->setHead($v)),
+ new BoolProperty(StateNames::OCCUPIED_BIT, fn(Bed $b) => $b->isOccupied(), fn(Bed $b, bool $v) => $b->setOccupied($v)),
+ $commonProperties->horizontalFacingSWNE
+ ]));
+ $reg->mapModel(Model::create(Blocks::BEDROCK(), Ids::BEDROCK)->properties([
+ new BoolProperty(StateNames::INFINIBURN_BIT, fn(Bedrock $b) => $b->burnsForever(), fn(Bedrock $b, bool $v) => $b->setBurnsForever($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::BELL(), Ids::BELL)->properties([
+ BoolProperty::unused(StateNames::TOGGLE_BIT, false),
+ new ValueFromStringProperty(StateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, fn(Bell $b) => $b->getAttachmentType(), fn(Bell $b, BellAttachmentType $v) => $b->setAttachmentType($v)),
+ $commonProperties->horizontalFacingSWNE
+ ]));
+ $reg->mapModel(Model::create(Blocks::BONE_BLOCK(), Ids::BONE_BLOCK)->properties([
+ IntProperty::unused(StateNames::DEPRECATED, 0),
+ $commonProperties->pillarAxis
+ ]));
+
+ $reg->mapModel(Model::create(Blocks::BREWING_STAND(), Ids::BREWING_STAND)->properties(array_map(fn(BrewingStandSlot $slot) => new BoolProperty(match ($slot) {
+ BrewingStandSlot::EAST => StateNames::BREWING_STAND_SLOT_A_BIT,
+ BrewingStandSlot::SOUTHWEST => StateNames::BREWING_STAND_SLOT_B_BIT,
+ BrewingStandSlot::NORTHWEST => StateNames::BREWING_STAND_SLOT_C_BIT
+ }, fn(BrewingStand $b) => $b->hasSlot($slot), fn(BrewingStand $b, bool $v) => $b->setSlot($slot, $v)), BrewingStandSlot::cases())));
+
+ //C
+ $reg->mapModel(Model::create(Blocks::CACTUS(), Ids::CACTUS)->properties([
+ new IntProperty(StateNames::AGE, 0, 15, fn(Cactus $b) => $b->getAge(), fn(Cactus $b, int $v) => $b->setAge($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::CAKE(), Ids::CAKE)->properties([
+ new IntProperty(StateNames::BITE_COUNTER, 0, 6, fn(Cake $b) => $b->getBites(), fn(Cake $b, int $v) => $b->setBites($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::CAMPFIRE(), Ids::CAMPFIRE)->properties($commonProperties->campfireProperties));
+ $reg->mapModel(Model::create(Blocks::CARVED_PUMPKIN(), Ids::CARVED_PUMPKIN)->properties([
+ $commonProperties->horizontalFacingCardinal
+ ]));
+ $reg->mapModel(Model::create(Blocks::CHAIN(), Ids::CHAIN)->properties([$commonProperties->pillarAxis]));
+ $reg->mapModel(Model::create(Blocks::CHISELED_BOOKSHELF(), Ids::CHISELED_BOOKSHELF)->properties([
+ $commonProperties->horizontalFacingSWNE,
+ new 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)),
+ new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail
+ ]));
+
+ //E
+ $reg->mapModel(Model::create(Blocks::ENDER_CHEST(), Ids::ENDER_CHEST)->properties([$commonProperties->horizontalFacingCardinal]));
+ $reg->mapModel(Model::create(Blocks::END_PORTAL_FRAME(), Ids::END_PORTAL_FRAME)->properties([
+ new BoolProperty(StateNames::END_PORTAL_EYE_BIT, fn(EndPortalFrame $b) => $b->hasEye(), fn(EndPortalFrame $b, bool $v) => $b->setEye($v)),
+ $commonProperties->horizontalFacingCardinal
+ ]));
+ $reg->mapModel(Model::create(Blocks::END_ROD(), Ids::END_ROD)->properties([
+ new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingEndRod, fn(EndRod $b) => $b->getFacing(), fn(EndRod $b, int $v) => $b->setFacing($v)),
+ ]));
+
+ //F
+ $reg->mapModel(Model::create(Blocks::FARMLAND(), Ids::FARMLAND)->properties([
+ new IntProperty(StateNames::MOISTURIZED_AMOUNT, 0, 7, fn(Farmland $b) => $b->getWetness(), fn(Farmland $b, int $v) => $b->setWetness($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::FIRE(), Ids::FIRE)->properties([
+ new IntProperty(StateNames::AGE, 0, 15, fn(Fire $b) => $b->getAge(), fn(Fire $b, int $v) => $b->setAge($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::FLOWER_POT(), Ids::FLOWER_POT)->properties([
+ BoolProperty::unused(StateNames::UPDATE_BIT, false)
+ ]));
+ $reg->mapModel(Model::create(Blocks::FROSTED_ICE(), Ids::FROSTED_ICE)->properties([
+ new IntProperty(StateNames::AGE, 0, 3, fn(FrostedIce $b) => $b->getAge(), fn(FrostedIce $b, int $v) => $b->setAge($v))
+ ]));
+
+ //G
+ $reg->mapModel(Model::create(Blocks::GLOWING_ITEM_FRAME(), Ids::GLOW_FRAME)->properties($commonProperties->itemFrameProperties));
+
+ //H
+ $reg->mapModel(Model::create(Blocks::HAY_BALE(), Ids::HAY_BLOCK)->properties([
+ IntProperty::unused(StateNames::DEPRECATED, 0),
+ $commonProperties->pillarAxis
+ ]));
+ $reg->mapModel(Model::create(Blocks::HOPPER(), Ids::HOPPER)->properties([
+ //kinda weird this doesn't use powered_bit?
+ new BoolProperty(StateNames::TOGGLE_BIT, fn(PoweredByRedstone $b) => $b->isPowered(), fn(PoweredByRedstone $b, bool $v) => $b->setPowered($v)),
+ new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptUp, fn(Hopper $b) => $b->getFacing(), fn(Hopper $b, int $v) => $b->setFacing($v)),
+ ]));
+
+ //I
+ $reg->mapModel(Model::create(Blocks::IRON_DOOR(), Ids::IRON_DOOR)->properties($commonProperties->doorProperties));
+ $reg->mapModel(Model::create(Blocks::IRON_TRAPDOOR(), Ids::IRON_TRAPDOOR)->properties($commonProperties->trapdoorProperties));
+ $reg->mapModel(Model::create(Blocks::ITEM_FRAME(), Ids::FRAME)->properties($commonProperties->itemFrameProperties));
+
+ //L
+ $reg->mapModel(Model::create(Blocks::LADDER(), Ids::LADDER)->properties([$commonProperties->horizontalFacingClassic]));
+ $reg->mapModel(Model::create(Blocks::LANTERN(), Ids::LANTERN)->properties([
+ new BoolProperty(StateNames::HANGING, fn(Lantern $b) => $b->isHanging(), fn(Lantern $b, bool $v) => $b->setHanging($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::LECTERN(), Ids::LECTERN)->properties([
+ new BoolProperty(StateNames::POWERED_BIT, fn(Lectern $b) => $b->isProducingSignal(), fn(Lectern $b, bool $v) => $b->setProducingSignal($v)),
+ $commonProperties->horizontalFacingCardinal,
+ ]));
+ $reg->mapModel(Model::create(Blocks::LEVER(), Ids::LEVER)->properties([
+ new ValueFromStringProperty(StateNames::LEVER_DIRECTION, ValueMappings::getInstance()->leverFacing, fn(Lever $b) => $b->getFacing(), fn(Lever $b, LeverFacing $v) => $b->setFacing($v)),
+ new BoolProperty(StateNames::OPEN_BIT, fn(Lever $b) => $b->isActivated(), fn(Lever $b, bool $v) => $b->setActivated($v)),
+ ]));
+ $reg->mapModel(Model::create(Blocks::LIGHTNING_ROD(), Ids::LIGHTNING_ROD)->properties([$commonProperties->anyFacingClassic]));
+ $reg->mapModel(Model::create(Blocks::LIT_PUMPKIN(), Ids::LIT_PUMPKIN)->properties([$commonProperties->horizontalFacingCardinal]));
+ $reg->mapModel(Model::create(Blocks::LOOM(), Ids::LOOM)->properties([$commonProperties->horizontalFacingSWNE]));
+
+ //M
+ $reg->mapModel(Model::create(Blocks::MUDDY_MANGROVE_ROOTS(), Ids::MUDDY_MANGROVE_ROOTS)->properties([$commonProperties->pillarAxis]));
+ $reg->mapModel(Model::create(Blocks::NETHER_WART(), Ids::NETHER_WART)->properties([
+ new IntProperty(StateNames::AGE, 0, 3, fn(NetherWartPlant $b) => $b->getAge(), fn(NetherWartPlant $b, int $v) => $b->setAge($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::NETHER_PORTAL(), Ids::PORTAL)->properties([
+ new ValueFromStringProperty(StateNames::PORTAL_AXIS, ValueMappings::getInstance()->portalAxis, fn(NetherPortal $b) => $b->getAxis(), fn(NetherPortal $b, int $v) => $b->setAxis($v))
+ ]));
+
+ //P
+ $reg->mapModel(Model::create(Blocks::PINK_PETALS(), Ids::PINK_PETALS)->properties([
+ //Pink petals only uses 0-3, but GROWTH state can go up to 7
+ new IntProperty(StateNames::GROWTH, 0, 7, fn(PinkPetals $b) => $b->getCount(), fn(PinkPetals $b, int $v) => $b->setCount(min($v, PinkPetals::MAX_COUNT)), offset: 1),
+ $commonProperties->horizontalFacingCardinal
+ ]));
+ $reg->mapModel(Model::create(Blocks::POWERED_RAIL(), Ids::GOLDEN_RAIL)->properties([
+ new BoolProperty(StateNames::RAIL_DATA_BIT, fn(PoweredRail $b) => $b->isPowered(), fn(PoweredRail $b, bool $v) => $b->setPowered($v)), //TODO: shared with ActivatorRail
+ new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail
+ ]));
+ $reg->mapModel(Model::create(Blocks::PITCHER_PLANT(), Ids::PITCHER_PLANT)->properties([
+ new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePlant $b) => $b->isTop(), fn(DoublePlant $b, bool $v) => $b->setTop($v)), //TODO: don't we have helpers for this?
+ ]));
+ $reg->mapModel(Model::create(Blocks::POLISHED_BASALT(), Ids::POLISHED_BASALT)->properties([$commonProperties->pillarAxis]));
+ $reg->mapModel(Model::create(Blocks::POLISHED_BLACKSTONE_BUTTON(), Ids::POLISHED_BLACKSTONE_BUTTON)->properties($commonProperties->buttonProperties));
+ $reg->mapModel(Model::create(Blocks::POLISHED_BLACKSTONE_PRESSURE_PLATE(), Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE)->properties($commonProperties->simplePressurePlateProperties));
+ $reg->mapModel(Model::create(Blocks::PUMPKIN(), Ids::PUMPKIN)->properties([
+ //not used, has no visible effect
+ $commonProperties->dummyCardinalDirection
+ ]));
+ $reg->mapModel(Model::create(Blocks::PURPUR(), Ids::PURPUR_BLOCK)->properties([
+ $commonProperties->dummyPillarAxis
+ ]));
+ $reg->mapModel(Model::create(Blocks::PURPUR_PILLAR(), Ids::PURPUR_PILLAR)->properties([$commonProperties->pillarAxis]));
+
+ //Q
+ $reg->mapModel(Model::create(Blocks::QUARTZ(), Ids::QUARTZ_BLOCK)->properties([
+ $commonProperties->dummyPillarAxis
+ ]));
+ $reg->mapModel(Model::create(Blocks::QUARTZ_PILLAR(), Ids::QUARTZ_PILLAR)->properties([$commonProperties->pillarAxis]));
+
+ //R
+ $reg->mapModel(Model::create(Blocks::RAIL(), Ids::RAIL)->properties([
+ new IntProperty(StateNames::RAIL_DIRECTION, 0, 9, fn(Rail $b) => $b->getShape(), fn(Rail $b, int $v) => $b->setShape($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::REDSTONE_WIRE(), Ids::REDSTONE_WIRE)->properties([$commonProperties->analogRedstoneSignal]));
+ $reg->mapModel(Model::create(Blocks::RESPAWN_ANCHOR(), Ids::RESPAWN_ANCHOR)->properties([
+ new IntProperty(StateNames::RESPAWN_ANCHOR_CHARGE, 0, 4, fn(RespawnAnchor $b) => $b->getCharges(), fn(RespawnAnchor $b, int $v) => $b->setCharges($v))
+ ]));
+
+ //S
+ $reg->mapModel(Model::create(Blocks::SEA_PICKLE(), Ids::SEA_PICKLE)->properties([
+ new IntProperty(StateNames::CLUSTER_COUNT, 0, 3, fn(SeaPickle $b) => $b->getCount(), fn(SeaPickle $b, int $v) => $b->setCount($v), offset: 1),
+ new BoolProperty(StateNames::DEAD_BIT, fn(SeaPickle $b) => $b->isUnderwater(), fn(SeaPickle $b, bool $v) => $b->setUnderwater($v), inverted: true)
+ ]));
+ $reg->mapModel(Model::create(Blocks::SMALL_DRIPLEAF(), Ids::SMALL_DRIPLEAF_BLOCK)->properties([
+ new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(SmallDripleaf $b) => $b->isTop(), fn(SmallDripleaf $b, bool $v) => $b->setTop($v)),
+ $commonProperties->horizontalFacingCardinal
+ ]));
+ $reg->mapModel(Model::create(Blocks::SMOOTH_QUARTZ(), Ids::SMOOTH_QUARTZ)->properties([
+ $commonProperties->dummyPillarAxis
+ ]));
+ $reg->mapModel(Model::create(Blocks::SNOW_LAYER(), Ids::SNOW_LAYER)->properties([
+ new DummyProperty(StateNames::COVERED_BIT, false),
+ new IntProperty(StateNames::HEIGHT, 0, 7, fn(SnowLayer $b) => $b->getLayers(), fn(SnowLayer $b, int $v) => $b->setLayers($v), offset: 1)
+ ]));
+ $reg->mapModel(Model::create(Blocks::SOUL_CAMPFIRE(), Ids::SOUL_CAMPFIRE)->properties($commonProperties->campfireProperties));
+ $reg->mapModel(Model::create(Blocks::SOUL_FIRE(), Ids::SOUL_FIRE)->properties([
+ new DummyProperty(StateNames::AGE, 0) //this is useless for soul fire, since it doesn't have the logic associated
+ ]));
+ $reg->mapModel(Model::create(Blocks::SOUL_LANTERN(), Ids::SOUL_LANTERN)->properties([
+ new BoolProperty(StateNames::HANGING, fn(Lantern $b) => $b->isHanging(), fn(Lantern $b, bool $v) => $b->setHanging($v)) //TODO: repeated
+ ]));
+ $reg->mapModel(Model::create(Blocks::STONE_BUTTON(), Ids::STONE_BUTTON)->properties($commonProperties->buttonProperties));
+ $reg->mapModel(Model::create(Blocks::STONE_PRESSURE_PLATE(), Ids::STONE_PRESSURE_PLATE)->properties($commonProperties->simplePressurePlateProperties));
+ $reg->mapModel(Model::create(Blocks::STONECUTTER(), Ids::STONECUTTER_BLOCK)->properties([
+ $commonProperties->horizontalFacingCardinal
+ ]));
+ $reg->mapModel(Model::create(Blocks::SUGARCANE(), Ids::REEDS)->properties([
+ new IntProperty(StateNames::AGE, 0, 15, fn(Sugarcane $b) => $b->getAge(), fn(Sugarcane $b, int $v) => $b->setAge($v))
+ ]));
+
+ //T
+ $reg->mapModel(Model::create(Blocks::TRAPPED_CHEST(), Ids::TRAPPED_CHEST)->properties([
+ $commonProperties->horizontalFacingCardinal
+ ]));
+ $reg->mapModel(Model::create(Blocks::TRIPWIRE(), Ids::TRIP_WIRE)->properties([
+ new BoolProperty(StateNames::ATTACHED_BIT, fn(Tripwire $b) => $b->isConnected(), fn(Tripwire $b, bool $v) => $b->setConnected($v)),
+ new BoolProperty(StateNames::DISARMED_BIT, fn(Tripwire $b) => $b->isDisarmed(), fn(Tripwire $b, bool $v) => $b->setDisarmed($v)),
+ new BoolProperty(StateNames::SUSPENDED_BIT, fn(Tripwire $b) => $b->isSuspended(), fn(Tripwire $b, bool $v) => $b->setSuspended($v)),
+ new BoolProperty(StateNames::POWERED_BIT, fn(Tripwire $b) => $b->isTriggered(), fn(Tripwire $b, bool $v) => $b->setTriggered($v)),
+ ]));
+ $reg->mapModel(Model::create(Blocks::TRIPWIRE_HOOK(), Ids::TRIPWIRE_HOOK)->properties([
+ new BoolProperty(StateNames::ATTACHED_BIT, fn(TripwireHook $b) => $b->isConnected(), fn(TripwireHook $b, bool $v) => $b->setConnected($v)),
+ new BoolProperty(StateNames::POWERED_BIT, fn(TripwireHook $b) => $b->isPowered(), fn(TripwireHook $b, bool $v) => $b->setPowered($v)),
+ $commonProperties->horizontalFacingSWNE
+ ]));
+
+ $reg->mapModel(Model::create(Blocks::TWISTING_VINES(), Ids::TWISTING_VINES)->properties([
+ new IntProperty(StateNames::TWISTING_VINES_AGE, 0, 25, fn(NetherVines $b) => $b->getAge(), fn(NetherVines $b, int $v) => $b->setAge($v))
+ ]));
+
+ //W
+ $reg->mapModel(Model::create(Blocks::WALL_BANNER(), Ids::WALL_BANNER)->properties([$commonProperties->horizontalFacingClassic]));
+ $reg->mapModel(Model::create(Blocks::WEEPING_VINES(), Ids::WEEPING_VINES)->properties([
+ new IntProperty(StateNames::WEEPING_VINES_AGE, 0, 25, fn(NetherVines $b) => $b->getAge(), fn(NetherVines $b, int $v) => $b->setAge($v))
+ ]));
+ $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_HEAVY(), Ids::HEAVY_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal]));
+ $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), Ids::LIGHT_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal]));
+ }
+
+ /**
+ * @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