Compare commits

...

107 Commits

Author SHA1 Message Date
ba6828c6bd Release 5.22.0 (Bedrock 1.21.50 support) (#6559)
Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
2024-12-04 13:36:52 +00:00
c56d4d3e3c dependabot: update github actions deps together, monthly 2024-12-03 14:56:22 +00:00
06028aac97 issues: don't recommend forums to get help 2024-12-03 02:07:58 +00:00
49da50659f Bump docker/build-push-action from 6.9.0 to 6.10.0 (#6553) 2024-12-02 16:36:12 +00:00
fcef015f32 L link 2024-12-02 00:40:55 +00:00
44771c892d Use PlayerAuthInputPacket::SNEAKING flag to test for sneaking (#6544)
This binds internal sneaking to whether or not the player is currently pressing the shift key, which fixes #5792 and fixes #5903.

However, it does introduce visual issues with sneaking, as explained in #6548. This needs to be worked on separately. For now, it's better we trade 2 functional bugs for 1 visual bug.
2024-12-01 13:14:30 +00:00
9cab72ed59 5.21.3 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12090411351
2024-11-29 20:43:17 +00:00
2bd9f4108b 5.21.2 (#6545) 2024-11-29 20:42:14 +00:00
cd2a1fdc1d Bump build/php from 8a396c6 to 5016e0a (#6541) 2024-11-29 11:03:27 +00:00
d666f17ec6 Bump build/php from a51259d to 8a396c6 (#6540) 2024-11-28 15:46:30 +00:00
0543bf301e draft-release: updated php_download_url 2024-11-27 19:19:51 +00:00
daacc8eddb Updated setup-php-action to 3.2.0 2024-11-27 17:56:00 +00:00
8cdc7d7ee1 auto-approve: drop pull_request_review trigger
this doesn't work for PRs from forks, since fork PRs don't have access to repo secrets.

we'll need some more advanced mechanism to avoid redundant reviews, but that's a job for another time.
2024-11-25 20:43:59 +00:00
a9787f0d99 Fix PHPStan error 2024-11-25 14:32:17 +00:00
5325ecee37 Deal with a whole lot of PHPStan suppressed key casting errors
closes #6534
2024-11-25 14:30:58 +00:00
aef4fa7174 Remove unused variable 2024-11-24 23:50:30 +00:00
53aa380ca3 Candle: fix extinguish logic
closes #5983
2024-11-24 23:49:32 +00:00
7460e12b6a pr-remove-waiting-label: suppress failure on 404 errors
this usually means the label wasn't on the PR in the first place
2024-11-23 21:48:30 +00:00
5b72f202bf actions: automatically remove waiting label from PRs on synchronize
there are probably other conditions where we'd want to remove this, but this will do for now.
2024-11-23 20:13:46 +00:00
0070426e97 auto-approve: only re-review if previous review was dismissed
this avoids unnecessary spam when someone clicks "Update branch" on the PR.
2024-11-23 19:07:24 +00:00
7c2ed7d884 Fix insta break check (#6528) 2024-11-21 02:22:10 +00:00
bf7a53b00f Update api-change-request.md
[ci skip]
2024-11-19 17:47:46 +00:00
e710b3750f Adjust pretty name of closures on PHP 8.4 (#6351)
related to https://github.com/php/php-src/pull/13550

see analog symfony change: https://github.com/symfony/symfony/pull/54614
2024-11-19 17:05:21 +00:00
faf1e26bac Fix: implicitly nullable parameter declarations deprecated (#6522) 2024-11-18 23:54:22 +00:00
b2aa6396c3 auto-approve: don't request approvals for draft PRs 2024-11-16 23:15:07 +00:00
48a908ee8c maybe making this specific to gameplay wasn't a good idea 2024-11-16 01:36:51 +00:00
4331f69b9c Update team-pr-auto-approve.yml 2024-11-15 20:44:51 +00:00
8a693f2a4c team-pr-auto-approve: Use RestrictedActions auto approver 2024-11-15 20:08:54 +00:00
b5469dede2 Flowable blocks now can't be placed inside liquid (#5392) 2024-11-15 00:10:43 +00:00
09bf203267 Update RuntimeDataDescriber.php 2024-11-12 22:57:14 +00:00
f3cc4a28e1 Easy wins for PHPStan 2.0 support 2024-11-12 22:12:54 +00:00
fe70150db2 Update composer dependencies 2024-11-12 21:47:52 +00:00
054538e6b7 Update PULL_REQUEST_TEMPLATE.md 2024-11-12 17:54:31 +00:00
1f86949836 Create CODEOWNERS 2024-11-12 14:37:02 +00:00
053a71c59d Bump build/php from 084822a to a51259d (#6495) 2024-11-12 14:28:32 +00:00
07d5046b83 5.21.2 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/11796630402
2024-11-12 11:48:59 +00:00
4a702b97fd Prepare 5.21.1 release (#6493) 2024-11-12 11:48:01 +00:00
d2c3b8dacb Fix GC cycle count increases on player disconnect (#6487) 2024-11-11 16:10:19 +01:00
2ff6470792 Fixed server crash when applying item cooldown (#6491)
This commit fixes server crash when applying a cooldown to any item which count is equals to 1.

closes #6490 
closes #6488
2024-11-10 19:15:30 +00:00
231eec911f Enchanted Golden Apple: Regeneration 5 => 2 matching Java (#6445) 2024-11-09 19:43:30 +00:00
8c04d47b1b Make weakness effect only applicable for melee damage (#6489) 2024-11-09 19:18:30 +00:00
b3b8aaddff Bump docker/build-push-action from 6.8.0 to 6.9.0 (#6465) 2024-11-04 16:29:01 +00:00
2173aab967 Bump thollander/actions-comment-pull-request from 2 to 3 (#6482) 2024-11-04 16:05:38 +00:00
e598364f06 5.21.1 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/11652565588
2024-11-03 15:30:16 +00:00
734ca1cc6b Merge pull request #6480 from pmmp/minor-next
5.21.0
2024-11-03 15:29:06 +00:00
96b12bddc1 Prepare 5.21.0 release 2024-11-03 15:24:43 +00:00
84464cde4f Update BedrockBlockUpgradeSchema 2024-11-03 14:44:50 +00:00
72fc138631 Regenerate PHPStan baselines 2024-11-03 14:43:34 +00:00
c63d0ef1b6 Fix dodgy ignored PHPStan error 2024-11-03 14:43:34 +00:00
82c416624d Merge pull request #6416 from pmmp/blockstate-schema-generator-improvements
Blockstate schema generator improvements
2024-11-03 14:10:38 +00:00
9e19391f20 Merge branch 'minor-next' into blockstate-schema-generator-improvements 2024-11-03 14:06:57 +00:00
c0b74b0341 Update BlockStateUpgrader.php 2024-11-03 14:05:46 +00:00
3c96e72f7d Merge remote-tracking branch 'origin/stable' into minor-next 2024-11-03 14:01:47 +00:00
0376e37966 5.20.2 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/11614028030
2024-10-31 14:38:30 +00:00
94dff74494 Prepare 5.20.1 release (#6479) 2024-10-31 14:35:42 +00:00
0065fe649f New release workflow triggered by the merge of a PR changing
IS_DEVELOPMENT_BUILD to false

This is more streamlined than the previous approach, and works better
for a world where 1 person isn't doing all the work.

Now, the flow is simpler:
- Do changes (e.g. protocol update), changelog & set IS_DEVELOPMENT_BUILD to false all in a single PR, which can be squash-merged if desired
- Once the PR is merged, a draft release will be prepared
- RestrictedActions will automatically set IS_DEVELOPMENT_BUILD back to
  true and bump the version
- Tag will be created when the release is published

Previously, multiple PRs might be needed, and the PR containing the
release changelog couldn't be squash-merged. Manual intervention was
also required to create a tag and prepare a release.

This PR also includes new CI checks to check for basic errors like
forgotten changelog files to ensure changelog links work correctly.

Note: Only PRs from PMMP Team members with **write** access to the
repository can trigger release generation. Random people cannot trigger
release generation by sending PRs.
2024-10-31 13:57:38 +00:00
8ef5e737de Temporary resolve loading old skulls from storage (#6476) 2024-10-27 01:29:34 +01:00
e7d8d99ca6 5.20.1 is next 2024-10-26 15:42:43 +01:00
414e8acf8c Release 5.20.0 2024-10-26 15:42:43 +01:00
d372af351a Fix changelog typo 2024-10-26 15:40:22 +01:00
4814db4fe7 Assemble 1.21.40 (#6471) 2024-10-25 14:21:51 +01:00
d01203d7c4 Reduce code duplication 2024-10-24 18:50:39 +01:00
847f931660 Merge branch 'minor-next' into blockstate-schema-generator-improvements 2024-10-24 17:46:57 +01:00
22718c4971 Add support for specialized flattenedProperties in schema format 2024-10-24 16:12:28 +01:00
acbfb0a3e9 Support for updating a batch of schemas using BlockPaletteArchive 2024-10-24 13:30:55 +01:00
7f9e79c83e Automatically test new schemas to ensure they produce the results predicted by the input file 2024-10-24 13:30:24 +01:00
7e343617b9 Rename ICopper to CopperMaterial (#6470) 2024-10-23 11:34:42 +01:00
d945cbf517 Merge remote-tracking branch 'origin/minor-next' into blockstate-schema-generator-improvements 2024-10-17 21:00:11 +01:00
c4e72c880e Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/11392123557
2024-10-17 19:59:13 +00:00
3ef7001d8e Merge branch 'stable' into blockstate-schema-generator-improvements 2024-10-17 20:55:34 +01:00
f1b1a7022d and a sanity check just in case 2024-10-17 20:55:12 +01:00
59d14de1d8 generate-blockstate-upgrade-schema: fallback to exact state match when encountering ambiguous filters
this popped up due to new changes in 1.20.40. Really we need to improve the way the filters are calculated, but this workaround solves the issue for now.
2024-10-17 20:51:17 +01:00
c8f567b093 Fix missing arg count check 2024-10-17 20:24:57 +01:00
5cc1068cd4 Bump docker/build-push-action from 6.7.0 to 6.8.0 (#6462) 2024-10-03 21:29:22 +00:00
f6e6f15c63 Implemented a proper way to handle items cooldown (#6405) 2024-09-25 13:28:17 -05:00
4e6b34f573 Implement new 1.21 copper blocks (#6366)
Added the following new blocks:
- All types of Copper Bulb
- All types of Copper Door
- All types of Copper Trapdoor
- All types of Chiseled Copper
- All types of Copper Grate
2024-09-24 21:25:10 -05:00
a4a07a8e5a Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/10986731877
2024-09-23 02:36:00 +00:00
0a7cbdd56d Release 5.19.0 (1.21.30 support) 2024-09-21 11:50:04 -05:00
a5babb2c9f 5.19.1 is next 2024-09-20 19:00:23 -05:00
f6e2a1ecce Validate transaction slots (#6304) 2024-09-09 09:48:38 +02:00
8cb2e577a1 Implement missing last interacted slot property in chiseled bookshelf (#6440) 2024-09-02 18:02:06 -05:00
5241118f0b Merge branch 'minor-next' into blockstate-schema-generator-improvements 2024-08-19 19:05:08 +01:00
93a270d251 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/10458521667
2024-08-19 18:03:24 +00:00
c44e6c59fc Merge pull request #6430 from pmmp/sync-minor-next
Merge branch 'stable' into minor-next
2024-08-18 17:49:25 +01:00
92f380bb9c Merge branch 'stable' into sync-minor-next 2024-08-17 11:17:48 -05:00
994fa5f792 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/10373700926
2024-08-13 16:37:45 +00:00
13f5cc9f87 ChiseledBookshelf: fixed function visibility (#6421) 2024-08-13 16:11:22 +01:00
1ce774ae73 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/10371827817
2024-08-13 14:36:00 +00:00
e017780cc3 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into minor-next 2024-08-13 13:37:56 +00:00
ee17ac5246 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into minor-next 2024-08-09 13:32:07 +01:00
33dc995cc7 blockstate-upgrade-schema-utils: added a command to update old schemas to a newer format
this is useful when the generator was updated with new features & optimisations, to reduce the size and/or improve readability of existing schemas.
2024-08-09 13:12:46 +01:00
26761c2b87 Blockstate schema tool now supports testing schemas as well as generating them
this is useful when making changes to the generator, since regenerated schemas can now be tested for validity. This helps to find bugs in the generator.
2024-08-09 12:38:03 +01:00
c4a2b6494d Implement Aqua Affinity enchantment (#6408) 2024-08-06 09:12:47 -05:00
2aa64dc15e Simplify phpstan-doc type hint for better readability 2024-08-05 17:13:23 -05:00
d0d7a995fb Add a TODO in BlockStateUpgrader
this issue can be worked around by adding a dummy schema, but it's a bit clunky.
2024-08-05 22:38:32 +01:00
be2437ac6e Support for flattening TAG_Byte and TAG_Int properties
this allows optimisation in upcoming versions.
2024-08-05 22:38:02 +01:00
bdb5845cec Allow name flattening rules where multiple old values map to the same new ID
this allows more compaction in certain cases, such as tallgrass recently.
instead of blacklisting any mapping which reuses the same flattened infix, we select the flatten property which produces the smallest number of distinct rules, which produces the most compact schema possible.
this change also permits potentially flattening other types of properties such as for corals (live/dead and type), although only one may be selected at a time.
2024-08-05 22:34:40 +01:00
787afb6b00 Implement all new 1.21 tuff blocks (#6391) 2024-07-23 11:34:46 -05:00
d6c48fd3a2 Implement new 1.21 paintings (#6393) 2024-07-12 13:24:43 +01:00
ef9791eaed Merge pull request #6397 from pmmp/stable
Merge branch 'stable' into minor-next
2024-07-10 18:56:52 +01:00
accaa0acce Implement ICopper interface for blocks with common properties (#6390) 2024-07-10 11:15:06 -05:00
2ffc38c835 Implement campfire & soul campfire (#4696) 2024-07-07 15:01:34 -05:00
f6c0b228ec Add flow and guster banner patterns 2024-07-04 16:47:18 -05:00
b342c497d1 Added 1.21 banner patterns. 2024-06-23 13:27:52 +03:00
428e38f913 Merge branch 'stable' into minor-next 2024-06-21 10:42:32 -05:00
5ae13209c0 Merge branch 'stable' into merge-stable 2024-06-20 19:21:33 -05:00
Max
371eccd007 Make access modifier consistent with parent abstract class (#6341) 2024-05-07 12:02:50 +01:00
169 changed files with 3390 additions and 591 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @pmmp/server-developers

View File

@ -7,13 +7,13 @@ assignees: ''
---
<!--- tell us what you want -->
## Description
<!--- Describe the problem you want to solve -->
## Problem description
<!--- explain why you want this and why it's a good idea -->
## Justification
<!--- Describe what changes you want to make to solve this problem -->
## Proposed solution
<!--- (optional) describe alternative methods you've explored to achieve your goal -->
## Alternative methods
## Alternative solutions that don't require API changes

View File

@ -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

View File

@ -1,43 +1,32 @@
## Introduction
<!-- Summarize your PR here. Keep it short and simple. -->
<!-- Explain existing problems or why this pull request is necessary -->
### Relevant issues
<!-- List relevant issues here -->
### Related issues & PRs
<!--
* Fixes #1
* Fixes #2
* Related to #2
-->
## Changes
### API changes
<!-- Any additions to the API that should be documented in release notes? -->
<!-- If not, you can delete this section -->
### Behavioural changes
<!-- Any change in how the server behaves, or its performance? -->
<!-- If not, you can delete this section -->
## Backwards compatibility
<!-- Any possible backwards incompatible changes? How are they solved, or how can they be solved? -->
<!-- If not, you can delete this section -->
## Follow-up
<!-- Suggest any actions to be done before/after merging this pull request -->
<!--
Requires translations:
| Name | Value in eng.ini |
| :--: | :---: |
| `foo.bar` | `Foo bar` |
-->
<!-- For example, future changes that this PR lays the groundwork for -->
## Tests
<!--
PRs which have not been tested MUST be marked as draft.
If this PR affects gameplay or user experience in some way, it must be manually tested.
Include any screenshots or videos of manual testing here.
Any test plugin code should also be pasted here if it can't be adapted to a PHPUnit test.
-->
I tested this PR by doing the following (tick all that apply):
- [ ] Writing PHPUnit tests (commit these in the `tests/phpunit` folder)
- [ ] Playtesting using a Minecraft client (provide screenshots or a video)
- [ ] Writing a test plugin (provide the code and sample output)
- [ ] Other (provide details)

View File

@ -37,4 +37,7 @@ updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
interval: monthly
groups:
github-actions:
patterns: ["*"]

View File

@ -53,7 +53,7 @@ jobs:
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.10.0
with:
push: true
context: ./pocketmine-mp
@ -66,7 +66,7 @@ jobs:
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.10.0
with:
push: true
context: ./pocketmine-mp
@ -79,7 +79,7 @@ jobs:
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.10.0
with:
push: true
context: ./pocketmine-mp
@ -92,7 +92,7 @@ jobs:
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.10.0
with:
push: true
context: ./pocketmine-mp

View File

@ -0,0 +1,65 @@
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 }}"}'

View File

@ -0,0 +1,13 @@
#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

View File

@ -0,0 +1,111 @@
name: Release PR checks
on:
#do checks on every PR update
pull_request:
branches:
- stable
- minor-next
- major-next
- "legacy/*"
paths:
- "src/VersionInfo.php"
#allow this workflow to be invoked on PR merge, prior to creating the release
workflow_call:
outputs:
valid:
description: Whether this commit is valid for release
value: ${{ jobs.check-intent.outputs.valid && jobs.check-validity.result == 'success' }}
permissions:
contents: read #for user access check
jobs:
check-intent:
name: Check release trigger
runs-on: ubuntu-20.04
outputs:
valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }}
steps:
- uses: actions/checkout@v4
- name: Check IS_DEVELOPMENT_BUILD flag
id: validate
run: |
echo DEV_BUILD=$(sed -n "s/^\s*public const IS_DEVELOPMENT_BUILD = \(true\|false\);$/\1/p" src/VersionInfo.php) >> $GITHUB_OUTPUT
check-validity:
name: Validate release info
needs: [check-intent]
#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
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@2.31.1
with:
php-version: 8.2
- name: Restore Composer package cache
uses: actions/cache@v4
with:
path: |
~/.cache/composer/files
~/.cache/composer/vcs
key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}"
restore-keys: |
composer-v2-cache-
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
- name: Check author permissions
id: check-permission
uses: actions-cool/check-user-permission@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
require: write
username: ${{ github.event.pull_request.user.login }}
#technically this would be fine for dependabot but generally bots don't count as team members
check-bot: true
- name: Abort if user permissions are insufficient
#user doesn't have permission or is a bot
if: steps.check-permission.outputs.require-result != 'true' || steps.check-permission.outputs.check-result != 'false'
run: |
echo "::error::This user is not authorized to trigger releases"
exit 1
- name: Check changelog file is present
id: file-presence
run: |
CHANGELOG_FILE="changelogs/$(php build/dump-version-info.php changelog_file_name)"
if [ ! -f "${{ github.workspace }}/$CHANGELOG_FILE" ]; then
echo "::error::$CHANGELOG_FILE does not exist"
exit 1
fi
echo FILE="$CHANGELOG_FILE" >> $GITHUB_OUTPUT
- name: Check header is present in changelog file
run: |
FILE="${{ steps.file-presence.outputs.FILE }}"
VERSION="$(php build/dump-version-info.php base_version)"
if ! grep -Fqx "# $VERSION" "${{ github.workspace }}/$FILE"; then
echo "::error::Header for $VERSION not found in $FILE"
exit 1
fi
- name: Check version is valid for the selected channel
run: |
CHANNEL="$(php build/dump-version-info.php channel)"
if [ "$(php build/dump-version-info.php suffix_valid)" != "true" ]; then
echo "::error::Version $(php build/dump-version-info.php base_version) is not allowed on the $CHANNEL channel"
exit 1
fi

View File

@ -1,19 +1,29 @@
name: Draft release
on:
push:
tags: "*"
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 }}
jobs:
draft:
name: Create GitHub draft release
if: "startsWith(github.event.head_commit.message, 'Release ')"
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
php-version: [8.2]
outputs:
draft-url: ${{ steps.create-draft.outputs.html_url }}
version: ${{ steps.get-pm-version.outputs.PM_VERSION }}
steps:
- uses: actions/checkout@v4
with:
@ -53,17 +63,17 @@ jobs:
- name: Get PocketMine-MP release version
id: get-pm-version
run: |
echo PM_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;') >> $GITHUB_OUTPUT
echo MCPE_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;') >> $GITHUB_OUTPUT
echo PM_VERSION_SHORT=$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);') >> $GITHUB_OUTPUT
echo PM_VERSION_MD=$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);') >> $GITHUB_OUTPUT
echo CHANGELOG_SUFFIX=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "" : "-" . \pocketmine\VersionInfo::BUILD_CHANNEL;') >> $GITHUB_OUTPUT
echo PRERELEASE=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "false" : "true";') >> $GITHUB_OUTPUT
echo PM_VERSION=$(php build/dump-version-info.php base_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
- 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/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-${{ matrix.php-version }}-latest" >> $GITHUB_OUTPUT
- name: Generate build info
run: |
@ -91,6 +101,7 @@ jobs:
- name: Create draft release
uses: ncipollo/release-action@v1.14.0
id: create-draft
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst
commit: ${{ github.sha }}
@ -99,9 +110,10 @@ jobs:
name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }}
tag: ${{ steps.get-pm-version.outputs.PM_VERSION }}
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.PM_VERSION_SHORT }}${{ steps.get-pm-version.outputs.CHANGELOG_SUFFIX }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details.
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 }}).

View File

@ -30,7 +30,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
uses: pmmp/setup-php-action@3.2.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"
@ -62,7 +62,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
uses: pmmp/setup-php-action@3.2.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"
@ -96,7 +96,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
uses: pmmp/setup-php-action@3.2.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"
@ -128,7 +128,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP
uses: pmmp/setup-php-action@3.1.0
uses: pmmp/setup-php-action@3.2.0
with:
php-version: ${{ inputs.php }}
install-path: "./bin"

View File

@ -0,0 +1,33 @@
name: Remove waiting label from PRs
on:
pull_request_target:
types: synchronize
jobs:
delabel:
name: Remove label
runs-on: ubuntu-latest
steps:
- name: Remove label
uses: actions/github-script@v7
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;
}
}

View File

@ -9,34 +9,30 @@ on:
pull_request_target:
types:
- opened
- synchronize
- reopened
- ready_for_review
permissions:
pull-requests: write
- synchronize
jobs:
approve:
name: Auto approve
dispatch:
name: Request approval
runs-on: ubuntu-latest
if: '! github.event.pull_request.draft'
steps:
- name: Check if PR author has write access
id: check-permission
uses: actions-cool/check-user-permission@v2
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
require: write
username: ${{ github.event.pull_request.user.login }}
#technically this would be fine for dependabot but generally bots don't count as team members
check-bot: true
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
owner: ${{ github.repository_owner }}
repositories: RestrictedActions
#TODO: Some way to avoid unnecessary repeated reviews would be nice here
- name: Approve PR if authorized
if: steps.check-permission.outputs.require-result == 'true' && steps.check-permission.outputs.check-result == 'false'
uses: juliangruber/approve-pull-request-action@v2
- name: Dispatch restricted action
uses: peter-evans/repository-dispatch@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ github.event.pull_request.number }}
token: ${{ steps.generate-token.outputs.token }}
repository: ${{ github.repository_owner }}/RestrictedActions
event-type: auto_approve_collaborator_pr
client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}", "reviewer_id": "0" }'

View File

@ -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.

View File

@ -0,0 +1,87 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\VersionInfo;
require dirname(__DIR__) . '/vendor/autoload.php';
/*
* Dumps version info in a machine-readable format for use in GitHub Actions workflows
*/
/**
* @var string[]|\Closure[] $options
* @phpstan-var array<string, string|\Closure() : string> $options
*/
$options = [
"base_version" => VersionInfo::BASE_VERSION,
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0],
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
"changelog_file_name" => function() : string{
$version = VersionInfo::VERSION();
$result = $version->getMajor() . "." . $version->getMinor();
$suffix = $version->getSuffix();
if($suffix !== ""){
if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){
fwrite(STDERR, "error: invalid current version suffix \"$suffix\"; aborting" . PHP_EOL);
exit(1);
}
$baseSuffix = $matches[1];
$result .= "-" . strtolower($baseSuffix);
}
return $result . ".md";
},
"changelog_md_header" => fn() : string => str_replace(".", "", VersionInfo::BASE_VERSION),
"prerelease" => fn() : bool => VersionInfo::VERSION()->getSuffix() !== "",
"channel" => VersionInfo::BUILD_CHANNEL,
"suffix_valid" => function() : bool{
//TODO: maybe this should be put into its own script?
$suffix = VersionInfo::VERSION()->getSuffix();
if(VersionInfo::BUILD_CHANNEL === "stable"){
//stable builds may not have suffixes
return $suffix === "";
}
if(VersionInfo::BUILD_CHANNEL === "alpha" || VersionInfo::BUILD_CHANNEL === "beta"){
$upperChannel = strtoupper(VersionInfo::BUILD_CHANNEL);
$upperSuffix = strtoupper($suffix);
return str_starts_with($upperSuffix, $upperChannel) && is_numeric(substr($upperSuffix, strlen($upperChannel)));
}
return true;
}
];
if(count($argv) !== 2 || !isset($options[$argv[1]])){
fwrite(STDERR, "Please provide an option (one of: " . implode(", ", array_keys($options)) . PHP_EOL);
exit(1);
}
$result = $options[$argv[1]];
if($result instanceof Closure){
$result = $result();
}
if(is_bool($result)){
echo $result ? "true" : "false";
}else{
echo $result;
}

View File

@ -122,7 +122,7 @@ if(!is_array($ids)){
throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object");
}
$cleanedIds = [];
foreach($ids as $name => $id){
foreach(Utils::promoteKeys($ids) as $name => $id){
if(!is_string($name) || !is_int($id)){
throw new \RuntimeException("Invalid biome ID map, expected string => int map");
}

View File

@ -42,7 +42,7 @@ $constants = [];
* @phpstan-param-out array<string, string> $constants
*/
function collectProperties(string $prefix, array $properties, array &$constants) : void{
foreach($properties as $propertyName => $property){
foreach(Utils::promoteKeys($properties) as $propertyName => $property){
$fullPropertyName = ($prefix !== "" ? $prefix . "." : "") . $propertyName;
$constName = str_replace([".", "-"], "_", strtoupper($fullPropertyName));

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\build\update_registry_annotations;
use pocketmine\utils\Utils;
use function basename;
use function class_exists;
use function count;
@ -48,6 +49,7 @@ if(count($argv) !== 2){
/**
* @param object[] $members
* @phpstan-param array<string, object> $members
*/
function generateMethodAnnotations(string $namespaceName, array $members) : string{
$selfName = basename(__FILE__);
@ -60,7 +62,7 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
static $lineTmpl = " * @method static %2\$s %s()";
$memberLines = [];
foreach($members as $name => $member){
foreach(Utils::stringifyKeys($members) as $name => $member){
$reflect = new \ReflectionClass($member);
while($reflect !== false && $reflect->isAnonymous()){
$reflect = $reflect->getParentClass();

View File

@ -86,7 +86,8 @@ function systemWrapper(string $command, string $errorMessage) : void{
function main() : void{
$filteredOpts = [];
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help"])) as $optName => $optValue){
$postCommitOnly = false;
foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help", "post"])) as $optName => $optValue){
if($optName === "help"){
fwrite(STDOUT, "Options:\n");
@ -96,6 +97,10 @@ function main() : void{
}
exit(0);
}
if($optName === "post"){
$postCommitOnly = true;
continue;
}
if(!is_string($optValue)){
fwrite(STDERR, "--$optName expects exactly 1 value\n");
exit(1);
@ -141,20 +146,25 @@ function main() : void{
$channel ??= "stable";
}
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);
}
$versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php';
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");
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");

View File

@ -1,5 +1,5 @@
# 5.19.0
Released 21tst September 2024.
Released 21st September 2024.
**For Minecraft: Bedrock Edition 1.21.30**

25
changelogs/5.20.md Normal file
View File

@ -0,0 +1,25 @@
# 5.20.0
Released 26th October 2024.
**For Minecraft: Bedrock Edition 1.21.40**
This is a support release for Minecraft: Bedrock Edition 1.21.40.
**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.40.
- Removed support for earlier versions.
## Fixes
- Fixed a bug in `tools/generate-blockstate-upgrade-schema.php` that caused it to fail on 1.21.40 with the new mushroom block changes.
# 5.20.1
Released 31st October 2024.
## Fixes
- Workaround old mob heads in world saves not being upgraded correctly and causing crashes.

128
changelogs/5.21.md Normal file
View File

@ -0,0 +1,128 @@
# 5.21.0
Released 3rd November 2024.
This is a minor feature release, including gameplay features and minor internals 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.
## Gameplay
- Added the following new blocks:
- Campfire
- Chiseled Copper
- Chiseled Tuff
- Chiseled Tuff Bricks
- Copper Bulb
- Copper Door
- Copper Grate
- Copper Trapdoor
- Polished Tuff, Slabs, Stairs and Walls
- Soul Campfire
- Tuff Bricks, Slabs, Stairs and Walls
- Tuff Slab, Stairs and Walls
- Added the following new types of painting:
- backyard
- baroque
- bouquet
- cavebird
- changing
- cotan
- endboss
- fern
- finding
- humble
- lowmist
- meditative
- orb
- owlemons
- passage
- pond
- prairie_ride
- sunflowers
- tides
- unpacked
- Armor slots are now properly restricted (on the server side) to only contain the appropriate type of armor or headwear.
- Implemented Aqua Affinity enchantment. Since the server doesn't currently enforce any movement restrictions in water, this enchantment works based on client-side behaviour only.
## API
### `pocketmine\block`
- The following new API methods have been added:
- `public ChiseledBookshelf->getLastInteractedSlot() : ?ChiseledBookshelfSlot`
- `public ChiseledBookshelf->setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : $this`
- The following new classes have been added:
- `utils\CopperMaterial` - interface implemented by all copper-like blocks with oxidation and waxed properties
- `CopperBulb`
- `CopperDoor`
- `CopperGrate`
- `CopperTrapdoor`
- `SoulCampfire`
- `Campfire`
- The following enums have new cases:
- `utils\BannerPatternType` has new cases `FLOW` and `GUSTER`
### `pocketmine\crafting`
- The following enums have new cases:
- `FurnaceType` has new cases `CAMPFIRE` and `SOUL_CAMPFIRE`
### `pocketmine\event`
- The following new classes have been added:
- `block\CampfireCookEvent` - called when a campfire finishes cooking an item
### `pocketmine\inventory`
- Added support for slot validators, which permit restricting the types of items a player can put into an inventory slot.
- The following new classes have been added:
- `transaction\action\SlotValidator` - interface
- `transaction\action\CallbackSlotValidator` - class allowing a closure to be used for slot content validation
- `SlotValidatedInventory` - implemented by inventories which support the use of slot validators
### `pocketmine\item`
- The following new API methods have been added:
- `public Item->getCooldownTag() : ?string` - returns the cooldown group this item belongs to, used for ensuring that, for example, different types of goat horns all respect a general horn cooldown
- The following new classes have been added:
- `ItemCooldownTags` - list of cooldown group tags used by PocketMine-MP
### `pocketmine\world\sound`
- The following new classes have been added
- `CampfireSound` - sound made by campfires while lit
## Tools
- `tools/blockstate-upgrade-schema-utils.php` (formerly `generate-blockstate-upgrade-schema.php`) has several improvements:
- Support for generating `flattenedProperties` rules as per [BedrockBlockUpgradeSchema 5.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/5.0.0)
- Improved criteria for flattened property selection to minimize the amount of rules required
- Several subcommands are now available:
- `generate` - generates a schema from provided data
- `update` - regenerates an existing schema in a newer format
- `update-all` - regenerates a folder of existing schemas in a newer format (useful for updating `BedrockBlockUpgradeSchema` en masse)
- `test` - verifies that a schema produces the results expected by provided data
## Internals
- Fixed incorrect visibility of `createEntity` in spawn eggs.
- Added support for newer `BedrockBlockUpgradeSchema` in `BlockStateUpgrader`.
# 5.21.1
Released 12th November 2024.
## Fixes
- Fixed server crash when applying a cooldown to an item with 1 count.
- Fixed garbage collector cycle count increase on player disconnect.
- Fixed weakness effect being applied to all attack types, causing damage splash potions to become weaker.
- Fixed Enchanted Golden Apple regeneration effect amplifier to match vanilla.
# 5.21.2
Released 29th November 2024.
## Fixes
- Fixed blocks destroyable by water being able to be placed inside water.
- Fixed deprecation warnings about nullable typehints on PHP 8.4.
- Fixed `Utils::getNiceClosureName()` not working correctly on PHP 8.4.
- Fixed incorrect break animations when breaking the block behind an instantly-breakable block like a torch.
- Fixed candle extinguish logic.
- Fixed various documentation issues around array key types.
- Introduced a new PHPStan rule along with `Utils::promoteKeys()` to improve PHPStan error reporting around unspecified array key types. Previously, some errors were missed due to PHPStan's BenevolentUnionType.
## DevOps
- `pmmp/server-developers` team is now automatically requested for review on any new PR.
- `Status: Waiting on Author` label is now automatically removed from PRs when they are updated.

16
changelogs/5.22.md Normal file
View File

@ -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.

View File

@ -33,10 +33,10 @@
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
"pocketmine/bedrock-block-upgrade-schema": "~4.4.0+bedrock-1.21.30",
"pocketmine/bedrock-data": "~2.13.0+bedrock-1.21.30",
"pocketmine/bedrock-item-upgrade-schema": "~1.12.0+bedrock-1.21.30",
"pocketmine/bedrock-protocol": "~34.0.0+bedrock-1.21.30",
"pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40",
"pocketmine/bedrock-data": "~2.15.0+bedrock-1.21.50",
"pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.50",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",

212
composer.lock generated
View File

@ -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": "e16d3ebe48e32bbf96348981249c0ac1",
"content-hash": "26d10b9381ab4e19684ca0b7a5a11a42",
"packages": [
{
"name": "adhocore/json-comment",
@ -127,16 +127,16 @@
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "4.4.0",
"version": "5.0.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "89e5f6e19c29e0d0d24835639f72a5ef157c2761"
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/89e5f6e19c29e0d0d24835639f72a5ef157c2761",
"reference": "89e5f6e19c29e0d0d24835639f72a5ef157c2761",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
"shasum": ""
},
"type": "library",
@ -147,22 +147,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/4.4.0"
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0"
},
"time": "2024-09-17T16:06:36+00:00"
"time": "2024-11-03T14:13:50+00:00"
},
{
"name": "pocketmine/bedrock-data",
"version": "2.13.0+bedrock-1.21.30",
"version": "2.15.0+bedrock-1.21.50",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "23d9356b866654cbd2a62b31373118bedb4a2562"
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/23d9356b866654cbd2a62b31373118bedb4a2562",
"reference": "23d9356b866654cbd2a62b31373118bedb4a2562",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad",
"shasum": ""
},
"type": "library",
@ -173,22 +173,22 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.30"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.50"
},
"time": "2024-09-17T16:03:14+00:00"
"time": "2024-12-04T12:59:12+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
"version": "1.12.0",
"version": "1.14.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
"reference": "85a0014c7dfd4a25c22a9efb0b447afb7dc6c409"
"reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/85a0014c7dfd4a25c22a9efb0b447afb7dc6c409",
"reference": "85a0014c7dfd4a25c22a9efb0b447afb7dc6c409",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/9fc7c9bbb558a017395c1cb7dd819c033ee971bb",
"reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb",
"shasum": ""
},
"type": "library",
@ -199,22 +199,22 @@
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.12.0"
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.14.0"
},
"time": "2024-09-11T19:48:31+00:00"
"time": "2024-12-04T12:22:49+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "34.0.0+bedrock-1.21.30",
"version": "35.0.0+bedrock-1.21.50",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "440c8078c66cc2a8f2abf58468df7df7246ee33b"
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/440c8078c66cc2a8f2abf58468df7df7246ee33b",
"reference": "440c8078c66cc2a8f2abf58468df7df7246ee33b",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435",
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435",
"shasum": ""
},
"require": {
@ -245,9 +245,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/34.0.0+bedrock-1.21.30"
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.50"
},
"time": "2024-09-18T20:58:42+00:00"
"time": "2024-12-04T13:02:00+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -926,16 +926,16 @@
},
{
"name": "symfony/filesystem",
"version": "v6.4.9",
"version": "v6.4.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "b51ef8059159330b74a4d52f68e671033c0fe463"
"reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463",
"reference": "b51ef8059159330b74a4d52f68e671033c0fe463",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3",
"reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3",
"shasum": ""
},
"require": {
@ -972,7 +972,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.4.9"
"source": "https://github.com/symfony/filesystem/tree/v6.4.13"
},
"funding": [
{
@ -988,24 +988,24 @@
"type": "tidelift"
}
],
"time": "2024-06-28T09:49:33+00:00"
"time": "2024-10-25T15:07:50+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.30.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
"php": ">=7.1"
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
@ -1051,7 +1051,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
@ -1067,24 +1067,24 @@
"type": "tidelift"
}
],
"time": "2024-05-31T15:07:36+00:00"
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.30.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
"shasum": ""
},
"require": {
"php": ">=7.1"
"php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
@ -1131,7 +1131,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
},
"funding": [
{
@ -1147,22 +1147,22 @@
"type": "tidelift"
}
],
"time": "2024-06-19T12:30:46+00:00"
"time": "2024-09-09T11:45:10+00:00"
}
],
"packages-dev": [
{
"name": "myclabs/deep-copy",
"version": "1.12.0",
"version": "1.12.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
"shasum": ""
},
"require": {
@ -1201,7 +1201,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
},
"funding": [
{
@ -1209,20 +1209,20 @@
"type": "tidelift"
}
],
"time": "2024-06-12T14:39:25+00:00"
"time": "2024-11-08T17:47:46+00:00"
},
{
"name": "nikic/php-parser",
"version": "v5.0.2",
"version": "v5.3.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b",
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b",
"shasum": ""
},
"require": {
@ -1233,7 +1233,7 @@
},
"require-dev": {
"ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
"phpunit/phpunit": "^9.0"
},
"bin": [
"bin/php-parse"
@ -1265,9 +1265,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1"
},
"time": "2024-03-05T20:51:40+00:00"
"time": "2024-10-08T18:51:32+00:00"
},
{
"name": "phar-io/manifest",
@ -1548,32 +1548,32 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.15",
"version": "10.1.16",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae"
"reference": "7e308268858ed6baedc8704a304727d20bc07c77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae",
"reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77",
"reference": "7e308268858ed6baedc8704a304727d20bc07c77",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.18 || ^5.0",
"nikic/php-parser": "^4.19.1 || ^5.1.0",
"php": ">=8.1",
"phpunit/php-file-iterator": "^4.0",
"phpunit/php-text-template": "^3.0",
"sebastian/code-unit-reverse-lookup": "^3.0",
"sebastian/complexity": "^3.0",
"sebastian/environment": "^6.0",
"sebastian/lines-of-code": "^2.0",
"sebastian/version": "^4.0",
"theseer/tokenizer": "^1.2.0"
"phpunit/php-file-iterator": "^4.1.0",
"phpunit/php-text-template": "^3.0.1",
"sebastian/code-unit-reverse-lookup": "^3.0.0",
"sebastian/complexity": "^3.2.0",
"sebastian/environment": "^6.1.0",
"sebastian/lines-of-code": "^2.0.2",
"sebastian/version": "^4.0.1",
"theseer/tokenizer": "^1.2.3"
},
"require-dev": {
"phpunit/phpunit": "^10.1"
@ -1585,7 +1585,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "10.1-dev"
"dev-main": "10.1.x-dev"
}
},
"autoload": {
@ -1614,7 +1614,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16"
},
"funding": [
{
@ -1622,7 +1622,7 @@
"type": "github"
}
],
"time": "2024-06-29T08:25:15+00:00"
"time": "2024-08-22T04:31:57+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1869,16 +1869,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.5.24",
"version": "10.5.38",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015"
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f124e3e3e561006047b532fd0431bf5bb6b9015",
"reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132",
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132",
"shasum": ""
},
"require": {
@ -1888,26 +1888,26 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.10.1",
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"myclabs/deep-copy": "^1.12.0",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.1",
"phpunit/php-code-coverage": "^10.1.5",
"phpunit/php-file-iterator": "^4.0",
"phpunit/php-invoker": "^4.0",
"phpunit/php-text-template": "^3.0",
"phpunit/php-timer": "^6.0",
"sebastian/cli-parser": "^2.0",
"sebastian/code-unit": "^2.0",
"sebastian/comparator": "^5.0",
"sebastian/diff": "^5.0",
"sebastian/environment": "^6.0",
"sebastian/exporter": "^5.1",
"sebastian/global-state": "^6.0.1",
"sebastian/object-enumerator": "^5.0",
"sebastian/recursion-context": "^5.0",
"sebastian/type": "^4.0",
"sebastian/version": "^4.0"
"phpunit/php-code-coverage": "^10.1.16",
"phpunit/php-file-iterator": "^4.1.0",
"phpunit/php-invoker": "^4.0.0",
"phpunit/php-text-template": "^3.0.1",
"phpunit/php-timer": "^6.0.0",
"sebastian/cli-parser": "^2.0.1",
"sebastian/code-unit": "^2.0.0",
"sebastian/comparator": "^5.0.3",
"sebastian/diff": "^5.1.1",
"sebastian/environment": "^6.1.0",
"sebastian/exporter": "^5.1.2",
"sebastian/global-state": "^6.0.2",
"sebastian/object-enumerator": "^5.0.0",
"sebastian/recursion-context": "^5.0.0",
"sebastian/type": "^4.0.0",
"sebastian/version": "^4.0.1"
},
"suggest": {
"ext-soap": "To be able to generate mocks based on WSDL files"
@ -1950,7 +1950,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.24"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38"
},
"funding": [
{
@ -1966,7 +1966,7 @@
"type": "tidelift"
}
],
"time": "2024-06-20T13:09:54+00:00"
"time": "2024-10-28T13:06:21+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2138,16 +2138,16 @@
},
{
"name": "sebastian/comparator",
"version": "5.0.1",
"version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "2db5010a484d53ebf536087a70b4a5423c102372"
"reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372",
"reference": "2db5010a484d53ebf536087a70b4a5423c102372",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
"reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
"shasum": ""
},
"require": {
@ -2158,7 +2158,7 @@
"sebastian/exporter": "^5.0"
},
"require-dev": {
"phpunit/phpunit": "^10.3"
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
@ -2203,7 +2203,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
"source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1"
"source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3"
},
"funding": [
{
@ -2211,7 +2211,7 @@
"type": "github"
}
],
"time": "2023-08-14T13:18:12+00:00"
"time": "2024-10-18T14:56:07+00:00"
},
{
"name": "sebastian/complexity",

View File

@ -40,6 +40,7 @@ parameters:
- build/php
dynamicConstantNames:
- pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD
- pocketmine\VersionInfo::BUILD_CHANNEL
- pocketmine\DEBUG
- pocketmine\IS_DEVELOPMENT_BUILD
stubFiles:
@ -47,8 +48,6 @@ parameters:
- tests/phpstan/stubs/leveldb.stub
- tests/phpstan/stubs/pmmpthread.stub
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
staticReflectionClassNamePatterns:
- "#^COM$#"
typeAliases:
#variadics don't work for this - mixed probably shouldn't work either, but for now it does
#what we actually need is something that accepts an infinite number of parameters, but in the absence of that,

View File

@ -332,7 +332,7 @@ class MemoryManager{
continue;
}
$methodStatics = [];
foreach($method->getStaticVariables() as $name => $variable){
foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){
$methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
if(count($methodStatics) > 0){
@ -360,7 +360,7 @@ class MemoryManager{
'_SESSION' => true
];
foreach($GLOBALS as $varName => $value){
foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){
if(isset($ignoredGlobals[$varName])){
continue;
}
@ -376,7 +376,7 @@ class MemoryManager{
$reflect = new \ReflectionFunction($function);
$vars = [];
foreach($reflect->getStaticVariables() as $varName => $variable){
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){
$vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
if(count($vars) > 0){
@ -416,7 +416,7 @@ class MemoryManager{
$info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
foreach($reflect->getStaticVariables() as $name => $variable){
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){
$info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
}
}else{
@ -499,7 +499,7 @@ class MemoryManager{
}
$data = [];
$numeric = 0;
foreach($from as $key => $value){
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),

View File

@ -736,12 +736,15 @@ class Server{
/**
* @return string[][]
* @phpstan-return array<string, list<string>>
*/
public function getCommandAliases() : array{
$section = $this->configGroup->getProperty(Yml::ALIASES);
$result = [];
if(is_array($section)){
foreach($section as $key => $value){
foreach(Utils::promoteKeys($section) as $key => $value){
//TODO: more validation needed here
//key might not be a string, value might not be list<string>
$commands = [];
if(is_array($value)){
$commands = $value;
@ -749,7 +752,7 @@ class Server{
$commands[] = (string) $value;
}
$result[$key] = $commands;
$result[(string) $key] = $commands;
}
}
@ -1094,7 +1097,11 @@ class Server{
$anyWorldFailedToLoad = false;
foreach((array) $this->configGroup->getProperty(Yml::WORLDS, []) as $name => $options){
foreach(Utils::promoteKeys((array) $this->configGroup->getProperty(Yml::WORLDS, [])) as $name => $options){
if(!is_string($name)){
//TODO: this probably should be an error
continue;
}
if($options === null){
$options = [];
}elseif(!is_array($options)){

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.19.0";
public const BASE_VERSION = "5.22.0";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";

View File

@ -362,6 +362,8 @@ class Block{
*
* A replacement block may be returned. This is useful if the block type changed due to reading of world data (e.g.
* data from a block entity).
*
* @phpstan-impure
*/
public function readStateFromWorld() : Block{
return $this;

View File

@ -745,8 +745,28 @@ final class BlockTypeIds{
public const PITCHER_PLANT = 10715;
public const PITCHER_CROP = 10716;
public const DOUBLE_PITCHER_CROP = 10717;
public const CAMPFIRE = 10718;
public const SOUL_CAMPFIRE = 10719;
public const TUFF_SLAB = 10720;
public const TUFF_STAIRS = 10721;
public const TUFF_WALL = 10722;
public const CHISELED_TUFF = 10723;
public const TUFF_BRICKS = 10724;
public const TUFF_BRICK_SLAB = 10725;
public const TUFF_BRICK_STAIRS = 10726;
public const TUFF_BRICK_WALL = 10727;
public const CHISELED_TUFF_BRICKS = 10728;
public const POLISHED_TUFF = 10729;
public const POLISHED_TUFF_SLAB = 10730;
public const POLISHED_TUFF_STAIRS = 10731;
public const POLISHED_TUFF_WALL = 10732;
public const COPPER_BULB = 10733;
public const COPPER_DOOR = 10734;
public const COPPER_TRAPDOOR = 10735;
public const CHISELED_COPPER = 10736;
public const COPPER_GRATE = 10737;
public const FIRST_UNUSED_BLOCK_ID = 10718;
public const FIRST_UNUSED_BLOCK_ID = 10738;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

View File

@ -52,6 +52,9 @@ class CakeWithCandle extends BaseCake{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($this->lit && $face !== Facing::UP){
return true;
}
if($this->onInteractCandle($item, $face, $clickVector, $player, $returnedItems)){
return true;
}

277
src/block/Campfire.php Normal file
View File

@ -0,0 +1,277 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\CampfireInventory;
use pocketmine\block\tile\Campfire as TileCampfire;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\LightableTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\crafting\FurnaceRecipe;
use pocketmine\crafting\FurnaceType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use pocketmine\entity\projectile\Projectile;
use pocketmine\entity\projectile\SplashPotion;
use pocketmine\event\block\CampfireCookEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\item\Durable;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
use pocketmine\item\ItemTypeIds;
use pocketmine\item\PotionType;
use pocketmine\item\Shovel;
use pocketmine\item\VanillaItems;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\BlazeShootSound;
use pocketmine\world\sound\FireExtinguishSound;
use pocketmine\world\sound\FlintSteelSound;
use pocketmine\world\sound\ItemFrameAddItemSound;
use function count;
use function min;
use function mt_rand;
class Campfire extends Transparent{
use HorizontalFacingTrait{
HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState;
}
use LightableTrait{
LightableTrait::describeBlockOnlyState as encodeLitState;
}
private const UPDATE_INTERVAL_TICKS = 10;
protected CampfireInventory $inventory;
/**
* @var int[] slot => ticks
* @phpstan-var array<int, int>
*/
protected array $cookingTimes = [];
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$this->encodeFacingState($w);
$this->encodeLitState($w);
}
public function readStateFromWorld() : Block{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileCampfire){
$this->inventory = $tile->getInventory();
$this->cookingTimes = $tile->getCookingTimes();
}else{
$this->inventory = new CampfireInventory($this->position);
}
return $this;
}
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileCampfire){
$tile->setCookingTimes($this->cookingTimes);
}
}
public function hasEntityCollision() : bool{
return true;
}
public function getLightLevel() : int{
return $this->lit ? 15 : 0;
}
public function isAffectedBySilkTouch() : bool{
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [
VanillaItems::CHARCOAL()->setCount(2)
];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE;
}
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)];
}
public function getInventory() : CampfireInventory{
return $this->inventory;
}
protected function getFurnaceType() : FurnaceType{
return FurnaceType::CAMPFIRE;
}
protected function getEntityCollisionDamage() : int{
return 1;
}
/**
* Sets the number of ticks during the item in the given slot has been cooked.
*/
public function setCookingTime(int $slot, int $time) : void{
if($slot < 0 || $slot > 3){
throw new \InvalidArgumentException("Slot must be in range 0-3");
}
if($time < 0 || $time > $this->getFurnaceType()->getCookDurationTicks()){
throw new \InvalidArgumentException("CookingTime must be in range 0-" . $this->getFurnaceType()->getCookDurationTicks());
}
$this->cookingTimes[$slot] = $time;
}
/**
* Returns the number of ticks during the item in the given slot has been cooked.
*/
public function getCookingTime(int $slot) : int{
return $this->cookingTimes[$slot] ?? 0;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->getSide(Facing::DOWN) instanceof Campfire){
return false;
}
if($player !== null){
$this->facing = $player->getHorizontalFacing();
}
$this->lit = true;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if(!$this->lit){
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){
$item->pop();
$this->ignite();
$this->position->getWorld()->addSound($this->position, new BlazeShootSound());
return true;
}elseif($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
if($item instanceof Durable){
$item->applyDamage(1);
}
$this->ignite();
return true;
}
}elseif($item instanceof Shovel){
$item->applyDamage(1);
$this->extinguish();
return true;
}
if($this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($this->getFurnaceType())->match($item) !== null){
$ingredient = clone $item;
$ingredient->setCount(1);
if(count($this->inventory->addItem($ingredient)) === 0){
$item->pop();
$this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
return true;
}
}
return false;
}
public function onNearbyBlockChange() : void{
if($this->lit && $this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){
$this->extinguish();
//TODO: Waterlogging
}
}
public function onEntityInside(Entity $entity) : bool{
if(!$this->lit){
if($entity->isOnFire()){
$this->ignite();
return false;
}
}elseif($entity instanceof Living){
$entity->attack(new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, $this->getEntityCollisionDamage()));
}
return true;
}
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
if($this->lit && $projectile instanceof SplashPotion && $projectile->getPotionType() === PotionType::WATER){
$this->extinguish();
}
}
public function onScheduledUpdate() : void{
if($this->lit){
$items = $this->inventory->getContents();
$furnaceType = $this->getFurnaceType();
$maxCookDuration = $furnaceType->getCookDurationTicks();
foreach($items as $slot => $item){
$this->setCookingTime($slot, min($maxCookDuration, $this->getCookingTime($slot) + self::UPDATE_INTERVAL_TICKS));
if($this->getCookingTime($slot) >= $maxCookDuration){
$result =
($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($item)) instanceof FurnaceRecipe ?
$recipe->getResult() :
VanillaItems::AIR();
$ev = new CampfireCookEvent($this, $slot, $item, $result);
$ev->call();
if ($ev->isCancelled()){
continue;
}
$this->inventory->setItem($slot, VanillaItems::AIR());
$this->setCookingTime($slot, 0);
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $ev->getResult());
}
}
if(count($items) > 0){
$this->position->getWorld()->setBlock($this->position, $this);
}
if(mt_rand(1, 6) === 1){
$this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound());
}
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
}
}
private function extinguish() : void{
$this->position->getWorld()->addSound($this->position, new FireExtinguishSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(false));
}
private function ignite() : void{
$this->position->getWorld()->addSound($this->position, new FlintSteelSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(true));
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
}
}

View File

@ -48,11 +48,32 @@ class ChiseledBookshelf extends Opaque{
*/
private array $slots = [];
private ?ChiseledBookshelfSlot $lastInteractedSlot = null;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
}
public function readStateFromWorld() : Block{
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileChiseledBookshelf){
$this->lastInteractedSlot = $tile->getLastInteractedSlot();
}else{
$this->lastInteractedSlot = null;
}
return $this;
}
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileChiseledBookshelf){
$tile->setLastInteractedSlot($this->lastInteractedSlot);
}
}
/**
* Returns whether the given slot is displayed as occupied.
* This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
@ -92,6 +113,23 @@ class ChiseledBookshelf extends Opaque{
return $this->slots;
}
/**
* Returns the last slot interacted by a player or null if no slot has been interacted with yet.
*/
public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{
return $this->lastInteractedSlot;
}
/**
* Sets the last slot interacted by a player.
*
* @return $this
*/
public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : self{
$this->lastInteractedSlot = $lastInteractedSlot;
return $this;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($face !== $this->facing){
return false;
@ -112,10 +150,12 @@ class ChiseledBookshelf extends Opaque{
$returnedItems[] = $inventory->getItem($slot->value);
$inventory->clear($slot->value);
$this->setSlot($slot, false);
$this->lastInteractedSlot = $slot;
}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
//TODO: type tags like blocks would be better for this
$inventory->setItem($slot->value, $item->pop());
$this->setSlot($slot, true);
$this->lastInteractedSlot = $slot;
}else{
return true;
}

View File

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperTrait;
class Copper extends Opaque{
class Copper extends Opaque implements CopperMaterial{
use CopperTrait;
}

69
src/block/CopperBulb.php Normal file
View File

@ -0,0 +1,69 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CopperTrait;
use pocketmine\block\utils\LightableTrait;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
class CopperBulb extends Opaque implements CopperMaterial{
use CopperTrait;
use PoweredByRedstoneTrait;
use LightableTrait{
describeBlockOnlyState as encodeLitState;
}
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$this->encodeLitState($w);
$w->bool($this->powered);
}
/** @return $this */
public function togglePowered(bool $powered) : self{
if($powered === $this->powered){
return $this;
}
if ($powered) {
$this->setLit(!$this->lit);
}
$this->setPowered($powered);
return $this;
}
public function getLightLevel() : int{
if ($this->lit) {
return match($this->oxidation){
CopperOxidation::NONE => 15,
CopperOxidation::EXPOSED => 12,
CopperOxidation::WEATHERED => 8,
CopperOxidation::OXIDIZED => 4,
};
}
return 0;
}
}

53
src/block/CopperDoor.php Normal file
View File

@ -0,0 +1,53 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
class CopperDoor extends Door implements CopperMaterial{
use CopperTrait{
onInteract as onInteractCopper;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) {
//copy copper properties to other half
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
$world = $this->position->getWorld();
if ($other instanceof CopperDoor) {
$other->setOxidation($this->oxidation);
$other->setWaxed($this->waxed);
$world->setBlock($other->position, $other);
}
return true;
}
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
}
}

33
src/block/CopperGrate.php Normal file
View File

@ -0,0 +1,33 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperTrait;
class CopperGrate extends Transparent implements CopperMaterial{
use CopperTrait;
//TODO: waterlogging!
}

View File

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperTrait;
class CopperSlab extends Slab{
class CopperSlab extends Slab implements CopperMaterial{
use CopperTrait;
}

View File

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperTrait;
class CopperStairs extends Stair{
class CopperStairs extends Stair implements CopperMaterial{
use CopperTrait;
}

View File

@ -0,0 +1,44 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperTrait;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
class CopperTrapdoor extends Trapdoor implements CopperMaterial{
use CopperTrait{
onInteract as onInteractCopper;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) {
return true;
}
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
/**
* "Flowable" blocks are destroyed if water flows into the same space as the block. These blocks usually don't have any
@ -40,6 +41,11 @@ abstract class Flowable extends Transparent{
return false;
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return (!$this->canBeFlowedInto() || !$blockReplace instanceof Liquid) &&
parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
/**
* @return AxisAlignedBB[]
*/

View File

@ -132,6 +132,7 @@ class RuntimeBlockStateRegistry{
/**
* @return Block[]
* @phpstan-return array<int, Block>
*/
public function getAllKnownStates() : array{
return $this->fullList;

View File

@ -0,0 +1,48 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\crafting\FurnaceType;
use pocketmine\item\Item;
class SoulCampfire extends Campfire{
public function getLightLevel() : int{
return $this->lit ? 10 : 0;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [
VanillaBlocks::SOUL_SOIL()->asItem()
];
}
protected function getEntityCollisionDamage() : int{
return 2;
}
protected function getFurnaceType() : FurnaceType{
return FurnaceType::SOUL_CAMPFIRE;
}
}

View File

@ -36,6 +36,7 @@ use pocketmine\block\tile\Bed as TileBed;
use pocketmine\block\tile\Bell as TileBell;
use pocketmine\block\tile\BlastFurnace as TileBlastFurnace;
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
use pocketmine\block\tile\Campfire as TileCampfire;
use pocketmine\block\tile\Cauldron as TileCauldron;
use pocketmine\block\tile\Chest as TileChest;
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
@ -154,6 +155,7 @@ use function strtolower;
* @method static CakeWithCandle CAKE_WITH_CANDLE()
* @method static CakeWithDyedCandle CAKE_WITH_DYED_CANDLE()
* @method static Opaque CALCITE()
* @method static Campfire CAMPFIRE()
* @method static Candle CANDLE()
* @method static Carpet CARPET()
* @method static Carrot CARROTS()
@ -179,6 +181,7 @@ use function strtolower;
* @method static Wood CHERRY_WOOD()
* @method static Chest CHEST()
* @method static ChiseledBookshelf CHISELED_BOOKSHELF()
* @method static Copper CHISELED_COPPER()
* @method static Opaque CHISELED_DEEPSLATE()
* @method static Opaque CHISELED_NETHER_BRICKS()
* @method static Opaque CHISELED_POLISHED_BLACKSTONE()
@ -186,6 +189,8 @@ use function strtolower;
* @method static Opaque CHISELED_RED_SANDSTONE()
* @method static Opaque CHISELED_SANDSTONE()
* @method static Opaque CHISELED_STONE_BRICKS()
* @method static Opaque CHISELED_TUFF()
* @method static Opaque CHISELED_TUFF_BRICKS()
* @method static ChorusFlower CHORUS_FLOWER()
* @method static ChorusPlant CHORUS_PLANT()
* @method static Clay CLAY()
@ -205,7 +210,11 @@ use function strtolower;
* @method static Concrete CONCRETE()
* @method static ConcretePowder CONCRETE_POWDER()
* @method static Copper COPPER()
* @method static CopperBulb COPPER_BULB()
* @method static CopperDoor COPPER_DOOR()
* @method static CopperGrate COPPER_GRATE()
* @method static CopperOre COPPER_ORE()
* @method static CopperTrapdoor COPPER_TRAPDOOR()
* @method static Coral CORAL()
* @method static CoralBlock CORAL_BLOCK()
* @method static FloorCoralFan CORAL_FAN()
@ -607,6 +616,10 @@ use function strtolower;
* @method static Opaque POLISHED_GRANITE()
* @method static Slab POLISHED_GRANITE_SLAB()
* @method static Stair POLISHED_GRANITE_STAIRS()
* @method static Opaque POLISHED_TUFF()
* @method static Slab POLISHED_TUFF_SLAB()
* @method static Stair POLISHED_TUFF_STAIRS()
* @method static Wall POLISHED_TUFF_WALL()
* @method static Flower POPPY()
* @method static Potato POTATOES()
* @method static PotionCauldron POTION_CAULDRON()
@ -685,6 +698,7 @@ use function strtolower;
* @method static Slab SMOOTH_STONE_SLAB()
* @method static Snow SNOW()
* @method static SnowLayer SNOW_LAYER()
* @method static SoulCampfire SOUL_CAMPFIRE()
* @method static SoulFire SOUL_FIRE()
* @method static Lantern SOUL_LANTERN()
* @method static SoulSand SOUL_SAND()
@ -735,6 +749,13 @@ use function strtolower;
* @method static Tripwire TRIPWIRE()
* @method static TripwireHook TRIPWIRE_HOOK()
* @method static Opaque TUFF()
* @method static Opaque TUFF_BRICKS()
* @method static Slab TUFF_BRICK_SLAB()
* @method static Stair TUFF_BRICK_STAIRS()
* @method static Wall TUFF_BRICK_WALL()
* @method static Slab TUFF_SLAB()
* @method static Stair TUFF_STAIRS()
* @method static Wall TUFF_WALL()
* @method static NetherVines TWISTING_VINES()
* @method static UnderwaterTorch UNDERWATER_TORCH()
* @method static Vine VINES()
@ -826,6 +847,11 @@ final class VanillaBlocks{
self::register("brown_mushroom", new BrownMushroom(new BID(Ids::BROWN_MUSHROOM), "Brown Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS])));
self::register("cactus", new Cactus(new BID(Ids::CACTUS), "Cactus", new Info(new BreakInfo(0.4), [Tags::POTTABLE_PLANTS])));
self::register("cake", new Cake(new BID(Ids::CAKE), "Cake", new Info(new BreakInfo(0.5))));
$campfireBreakInfo = new Info(BreakInfo::axe(2.0));
self::register("campfire", new Campfire(new BID(Ids::CAMPFIRE, TileCampfire::class), "Campfire", $campfireBreakInfo));
self::register("soul_campfire", new SoulCampfire(new BID(Ids::SOUL_CAMPFIRE, TileCampfire::class), "Soul Campfire", $campfireBreakInfo));
self::register("carrots", new Carrot(new BID(Ids::CARROTS), "Carrot Block", new Info(BreakInfo::instant())));
$chestBreakInfo = new Info(BreakInfo::axe(2.5));
@ -1261,6 +1287,7 @@ final class VanillaBlocks{
self::registerBlocksR17();
self::registerBlocksR18();
self::registerMudBlocks();
self::registerTuffBlocks();
self::registerCraftingTables();
self::registerChorusBlocks();
@ -1568,7 +1595,6 @@ final class VanillaBlocks{
self::register("amethyst_cluster", new AmethystCluster(new BID(Ids::AMETHYST_CLUSTER), "Amethyst Cluster", $amethystInfo));
self::register("calcite", new Opaque(new BID(Ids::CALCITE), "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD))));
self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0))));
self::register("raw_copper", new Opaque(new BID(Ids::RAW_COPPER), "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0))));
self::register("raw_gold", new Opaque(new BID(Ids::RAW_GOLD), "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0))));
@ -1621,9 +1647,16 @@ final class VanillaBlocks{
self::register("lightning_rod", new LightningRod(new BID(Ids::LIGHTNING_ROD), "Lightning Rod", $copperBreakInfo));
self::register("copper", new Copper(new BID(Ids::COPPER), "Copper Block", $copperBreakInfo));
self::register("chiseled_copper", new Copper(new BID(Ids::CHISELED_COPPER), "Chiseled Copper", $copperBreakInfo));
self::register("copper_grate", new CopperGrate(new BID(Ids::COPPER_GRATE), "Copper Grate", $copperBreakInfo));
self::register("cut_copper", new Copper(new BID(Ids::CUT_COPPER), "Cut Copper Block", $copperBreakInfo));
self::register("cut_copper_slab", new CopperSlab(new BID(Ids::CUT_COPPER_SLAB), "Cut Copper Slab", $copperBreakInfo));
self::register("cut_copper_stairs", new CopperStairs(new BID(Ids::CUT_COPPER_STAIRS), "Cut Copper Stairs", $copperBreakInfo));
self::register("copper_bulb", new CopperBulb(new BID(Ids::COPPER_BULB), "Copper Bulb", $copperBreakInfo));
$copperDoorBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0));
self::register("copper_door", new CopperDoor(new BID(Ids::COPPER_DOOR), "Copper Door", $copperDoorBreakInfo));
self::register("copper_trapdoor", new CopperTrapdoor(new BID(Ids::COPPER_TRAPDOOR), "Copper Trapdoor", $copperDoorBreakInfo));
$candleBreakInfo = new Info(new BreakInfo(0.1));
self::register("candle", new Candle(new BID(Ids::CANDLE), "Candle", $candleBreakInfo));
@ -1659,6 +1692,27 @@ final class VanillaBlocks{
self::register("mud_brick_wall", new Wall(new BID(Ids::MUD_BRICK_WALL), "Mud Brick Wall", $mudBricksBreakInfo));
}
private static function registerTuffBlocks() : void{
$tuffBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0));
self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", $tuffBreakInfo));
self::register("tuff_slab", new Slab(new BID(Ids::TUFF_SLAB), "Tuff", $tuffBreakInfo));
self::register("tuff_stairs", new Stair(new BID(Ids::TUFF_STAIRS), "Tuff Stairs", $tuffBreakInfo));
self::register("tuff_wall", new Wall(new BID(Ids::TUFF_WALL), "Tuff Wall", $tuffBreakInfo));
self::register("chiseled_tuff", new Opaque(new BID(Ids::CHISELED_TUFF), "Chiseled Tuff", $tuffBreakInfo));
self::register("tuff_bricks", new Opaque(new BID(Ids::TUFF_BRICKS), "Tuff Bricks", $tuffBreakInfo));
self::register("tuff_brick_slab", new Slab(new BID(Ids::TUFF_BRICK_SLAB), "Tuff Brick", $tuffBreakInfo));
self::register("tuff_brick_stairs", new Stair(new BID(Ids::TUFF_BRICK_STAIRS), "Tuff Brick Stairs", $tuffBreakInfo));
self::register("tuff_brick_wall", new Wall(new BID(Ids::TUFF_BRICK_WALL), "Tuff Brick Wall", $tuffBreakInfo));
self::register("chiseled_tuff_bricks", new Opaque(new BID(Ids::CHISELED_TUFF_BRICKS), "Chiseled Tuff Bricks", $tuffBreakInfo));
self::register("polished_tuff", new Opaque(new BID(Ids::POLISHED_TUFF), "Polished Tuff", $tuffBreakInfo));
self::register("polished_tuff_slab", new Slab(new BID(Ids::POLISHED_TUFF_SLAB), "Polished Tuff", $tuffBreakInfo));
self::register("polished_tuff_stairs", new Stair(new BID(Ids::POLISHED_TUFF_STAIRS), "Polished Tuff Stairs", $tuffBreakInfo));
self::register("polished_tuff_wall", new Wall(new BID(Ids::POLISHED_TUFF_WALL), "Polished Tuff Wall", $tuffBreakInfo));
}
private static function registerCauldronBlocks() : void{
$cauldronBreakInfo = new Info(BreakInfo::pickaxe(2, ToolTier::WOOD));

View File

@ -0,0 +1,40 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\inventory;
use pocketmine\inventory\SimpleInventory;
use pocketmine\world\Position;
class CampfireInventory extends SimpleInventory implements BlockInventory{
use BlockInventoryTrait;
public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(4);
}
public function getMaxStackSize() : int{
return 1;
}
}

View File

@ -39,7 +39,10 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor
public const SLOT_INPUT = 0;
public const SLOT_LAPIS = 1;
/** @var EnchantingOption[] $options */
/**
* @var EnchantingOption[] $options
* @phpstan-var list<EnchantingOption>
*/
private array $options = [];
public function __construct(Position $holder){

143
src/block/tile/Campfire.php Normal file
View File

@ -0,0 +1,143 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\Campfire as BlockCampfire;
use pocketmine\block\inventory\CampfireInventory;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\world\World;
class Campfire extends Spawnable implements Container{
use ContainerTrait;
private const TAG_FIRST_INPUT_ITEM = "Item1"; //TAG_Compound
private const TAG_SECOND_INPUT_ITEM = "Item2"; //TAG_Compound
private const TAG_THIRD_INPUT_ITEM = "Item3"; //TAG_Compound
private const TAG_FOURTH_INPUT_ITEM = "Item4"; //TAG_Compound
private const TAG_FIRST_COOKING_TIME = "ItemTime1"; //TAG_Int
private const TAG_SECOND_COOKING_TIME = "ItemTime2"; //TAG_Int
private const TAG_THIRD_COOKING_TIME = "ItemTime3"; //TAG_Int
private const TAG_FOURTH_COOKING_TIME = "ItemTime4"; //TAG_Int
protected CampfireInventory $inventory;
/** @var array<int, int> */
private array $cookingTimes = [];
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new CampfireInventory($this->position);
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
static function(Inventory $unused) use ($world, $pos) : void{
$block = $world->getBlock($pos);
if($block instanceof BlockCampfire){
$world->setBlock($pos, $block);
}
})
);
}
public function getInventory() : CampfireInventory{
return $this->inventory;
}
public function getRealInventory() : CampfireInventory{
return $this->inventory;
}
/**
* @return int[]
* @phpstan-return array<int, int>
*/
public function getCookingTimes() : array{
return $this->cookingTimes;
}
/**
* @param int[] $cookingTimes
* @phpstan-param array<int, int> $cookingTimes
*/
public function setCookingTimes(array $cookingTimes) : void{
$this->cookingTimes = $cookingTimes;
}
public function readSaveData(CompoundTag $nbt) : void{
$items = [];
$listeners = $this->inventory->getListeners()->toArray();
$this->inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
foreach([
[0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME],
[1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME],
[2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME],
[3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME],
] as [$slot, $itemTag, $cookingTimeTag]){
if(($tag = $nbt->getTag($itemTag)) instanceof CompoundTag){
$items[$slot] = Item::nbtDeserialize($tag);
}
if(($tag = $nbt->getTag($cookingTimeTag)) instanceof IntTag){
$this->cookingTimes[$slot] = $tag->getValue();
}
}
$this->inventory->setContents($items);
$this->inventory->getListeners()->add(...$listeners);
}
protected function writeSaveData(CompoundTag $nbt) : void{
foreach([
[0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME],
[1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME],
[2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME],
[3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME],
] as [$slot, $itemTag, $cookingTimeTag]){
$item = $this->inventory->getItem($slot);
if(!$item->isNull()){
$nbt->setTag($itemTag, $item->nbtSerialize());
if(isset($this->cookingTimes[$slot])){
$nbt->setInt($cookingTimeTag, $this->cookingTimes[$slot]);
}
}
}
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
foreach([
0 => self::TAG_FIRST_INPUT_ITEM,
1 => self::TAG_SECOND_INPUT_ITEM,
2 => self::TAG_THIRD_INPUT_ITEM,
3 => self::TAG_FOURTH_INPUT_ITEM
] as $slot => $tag){
$item = $this->inventory->getItem($slot);
if(!$item->isNull()){
$nbt->setTag($tag, TypeConverter::getInstance()->getItemTranslator()->toNetworkNbt($item));
}
}
}
}

View File

@ -40,8 +40,12 @@ use function count;
class ChiseledBookshelf extends Tile implements Container{
use ContainerTrait;
private const TAG_LAST_INTERACTED_SLOT = "LastInteractedSlot"; //TAG_Int
private SimpleInventory $inventory;
private ?ChiseledBookshelfSlot $lastInteractedSlot = null;
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases()));
@ -55,12 +59,30 @@ class ChiseledBookshelf extends Tile implements Container{
return $this->inventory;
}
public function readSaveData(CompoundTag $nbt) : void{
$this->loadItems($nbt);
public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{
return $this->lastInteractedSlot;
}
public function writeSaveData(CompoundTag $nbt) : void{
public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : void{
$this->lastInteractedSlot = $lastInteractedSlot;
}
public function readSaveData(CompoundTag $nbt) : void{
$this->loadItems($nbt);
$lastInteractedSlot = $nbt->getInt(self::TAG_LAST_INTERACTED_SLOT, 0);
if($lastInteractedSlot !== 0){
$this->lastInteractedSlot = ChiseledBookshelfSlot::tryFrom($lastInteractedSlot - 1);
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$this->saveItems($nbt);
$nbt->setInt(self::TAG_LAST_INTERACTED_SLOT, $this->lastInteractedSlot !== null ?
$this->lastInteractedSlot->value + 1 :
0
);
}
protected function loadItems(CompoundTag $tag) : void{

View File

@ -57,6 +57,7 @@ final class TileFactory{
$this->register(Bell::class, ["Bell", "minecraft:bell"]);
$this->register(BlastFurnace::class, ["BlastFurnace", "minecraft:blast_furnace"]);
$this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]);
$this->register(Campfire::class, ["Campfire", "minecraft:campfire"]);
$this->register(Cauldron::class, ["Cauldron", "minecraft:cauldron"]);
$this->register(Chest::class, ["Chest", "minecraft:chest"]);
$this->register(ChiseledBookshelf::class, ["ChiseledBookshelf", "minecraft:chiseled_bookshelf"]);
@ -79,7 +80,6 @@ final class TileFactory{
$this->register(MobHead::class, ["Skull", "minecraft:skull"]);
$this->register(GlowingItemFrame::class, ["GlowItemFrame"]);
//TODO: Campfire
//TODO: ChalkboardBlock
//TODO: ChemistryTable
//TODO: CommandBlock

View File

@ -81,10 +81,12 @@ enum BannerPatternType{
case DIAGONAL_RIGHT;
case DIAGONAL_UP_LEFT;
case DIAGONAL_UP_RIGHT;
case FLOW;
case FLOWER;
case GLOBE;
case GRADIENT;
case GRADIENT_UP;
case GUSTER;
case HALF_HORIZONTAL;
case HALF_HORIZONTAL_BOTTOM;
case HALF_VERTICAL;

View File

@ -0,0 +1,38 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\utils;
/**
* Represents copper blocks that have oxidized and waxed variations.
*/
interface CopperMaterial{
public function getOxidation() : CopperOxidation;
public function setOxidation(CopperOxidation $oxidation) : CopperMaterial;
public function isWaxed() : bool;
public function setWaxed(bool $waxed) : CopperMaterial;
}

View File

@ -36,13 +36,17 @@ use function str_contains;
class SignText{
public const LINE_COUNT = 4;
/** @var string[] */
/**
* @var string[]
* @phpstan-var array{0: string, 1: string, 2: string, 3: string}
*/
private array $lines;
private Color $baseColor;
private bool $glowing;
/**
* @param string[]|null $lines index-sensitive; keys 0-3 will be used, regardless of array order
* @phpstan-param array{0?: string, 1?: string, 2?: string, 3?: string}|null $lines
*
* @throws \InvalidArgumentException if the array size is greater than 4
* @throws \InvalidArgumentException if invalid keys (out of bounds or string) are found in the array
@ -82,6 +86,7 @@ class SignText{
* Returns an array of lines currently on the sign.
*
* @return string[]
* @phpstan-return array{0: string, 1: string, 2: string, 3: string}
*/
public function getLines() : array{
return $this->lines;

View File

@ -44,10 +44,16 @@ abstract class Command{
private string $nextLabel;
private string $label;
/** @var string[] */
/**
* @var string[]
* @phpstan-var list<string>
*/
private array $aliases = [];
/** @var string[] */
/**
* @var string[]
* @phpstan-var list<string>
*/
private array $activeAliases = [];
private ?CommandMap $commandMap = null;
@ -62,6 +68,7 @@ abstract class Command{
/**
* @param string[] $aliases
* @phpstan-param list<string> $aliases
*/
public function __construct(string $name, Translatable|string $description = "", Translatable|string|null $usageMessage = null, array $aliases = []){
$this->name = $name;
@ -182,6 +189,7 @@ abstract class Command{
/**
* @return string[]
* @phpstan-return list<string>
*/
public function getAliases() : array{
return $this->activeAliases;
@ -201,6 +209,7 @@ abstract class Command{
/**
* @param string[] $aliases
* @phpstan-param list<string> $aliases
*/
public function setAliases(array $aliases) : void{
$this->aliases = $aliases;

View File

@ -70,6 +70,7 @@ use pocketmine\lang\KnownTranslationFactory;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use function array_shift;
use function count;
use function implode;
@ -80,7 +81,10 @@ use function trim;
class SimpleCommandMap implements CommandMap{
/** @var Command[] */
/**
* @var Command[]
* @phpstan-var array<string, Command>
*/
protected array $knownCommands = [];
public function __construct(private Server $server){
@ -169,7 +173,7 @@ class SimpleCommandMap implements CommandMap{
}
public function unregister(Command $command) : bool{
foreach($this->knownCommands as $lbl => $cmd){
foreach(Utils::promoteKeys($this->knownCommands) as $lbl => $cmd){
if($cmd === $command){
unset($this->knownCommands[$lbl]);
}
@ -237,6 +241,7 @@ class SimpleCommandMap implements CommandMap{
/**
* @return Command[]
* @phpstan-return array<string, Command>
*/
public function getCommands() : array{
return $this->knownCommands;
@ -245,7 +250,7 @@ class SimpleCommandMap implements CommandMap{
public function registerServerAliases() : void{
$values = $this->server->getCommandAliases();
foreach($values as $alias => $commandStrings){
foreach(Utils::stringifyKeys($values) as $alias => $commandStrings){
if(str_contains($alias, ":")){
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias)));
continue;

View File

@ -110,14 +110,15 @@ class CraftingManager{
/**
* @param Item[] $items
* @phpstan-param list<Item> $items
*
* @return Item[]
* @phpstan-return list<Item>
*/
private static function pack(array $items) : array{
/** @var Item[] $result */
$result = [];
foreach($items as $i => $item){
foreach($items as $item){
foreach($result as $otherItem){
if($item->canStackWith($otherItem)){
$otherItem->setCount($otherItem->getCount() + $item->getCount());
@ -134,6 +135,7 @@ class CraftingManager{
/**
* @param Item[] $outputs
* @phpstan-param list<Item> $outputs
*/
private static function hashOutputs(array $outputs) : string{
$outputs = self::pack($outputs);

View File

@ -193,7 +193,7 @@ final class CraftingManagerFromDataHelper{
*/
private static function loadJsonObjectListIntoModel(\JsonMapper $mapper, string $modelClass, array $data) : array{
$result = [];
foreach($data as $i => $item){
foreach(Utils::promoteKeys($data) as $i => $item){
if(!is_object($item)){
throw new SavedDataLoadingException("Invalid entry at index $i: expected object, got " . get_debug_type($item));
}
@ -275,7 +275,8 @@ final class CraftingManagerFromDataHelper{
"furnace" => FurnaceType::FURNACE,
"blast_furnace" => FurnaceType::BLAST_FURNACE,
"smoker" => FurnaceType::SMOKER,
//TODO: campfire
"campfire" => FurnaceType::CAMPFIRE,
"soul_campfire" => FurnaceType::SOUL_CAMPFIRE,
default => null
};
if($furnaceType === null){

View File

@ -30,6 +30,7 @@ interface CraftingRecipe{
* Returns a list of items needed to craft this recipe. This MUST NOT include Air items or items with a zero count.
*
* @return RecipeIngredient[]
* @phpstan-return list<RecipeIngredient>
*/
public function getIngredientList() : array;
@ -37,6 +38,7 @@ interface CraftingRecipe{
* Returns a list of results this recipe will produce when the inputs in the given crafting grid are consumed.
*
* @return Item[]
* @phpstan-return list<Item>
*/
public function getResultsFor(CraftingGrid $grid) : array;

View File

@ -25,6 +25,7 @@ namespace pocketmine\crafting;
use pocketmine\utils\LegacyEnumShimTrait;
use pocketmine\world\sound\BlastFurnaceSound;
use pocketmine\world\sound\CampfireSound;
use pocketmine\world\sound\FurnaceSound;
use pocketmine\world\sound\SmokerSound;
use pocketmine\world\sound\Sound;
@ -35,8 +36,10 @@ use function spl_object_id;
* These are retained for backwards compatibility only.
*
* @method static FurnaceType BLAST_FURNACE()
* @method static FurnaceType CAMPFIRE()
* @method static FurnaceType FURNACE()
* @method static FurnaceType SMOKER()
* @method static FurnaceType SOUL_CAMPFIRE()
*
* @phpstan-type TMetadata array{0: int, 1: Sound}
*/
@ -46,6 +49,8 @@ enum FurnaceType{
case FURNACE;
case BLAST_FURNACE;
case SMOKER;
case CAMPFIRE;
case SOUL_CAMPFIRE;
/**
* @phpstan-return TMetadata
@ -58,6 +63,7 @@ enum FurnaceType{
self::FURNACE => [200, new FurnaceSound()],
self::BLAST_FURNACE => [100, new BlastFurnaceSound()],
self::SMOKER => [100, new SmokerSound()],
self::CAMPFIRE, self::SOUL_CAMPFIRE => [600, new CampfireSound()]
};
}

View File

@ -32,11 +32,20 @@ use function str_contains;
use function strlen;
class ShapedRecipe implements CraftingRecipe{
/** @var string[] */
/**
* @var string[]
* @phpstan-var list<string>
*/
private array $shape = [];
/** @var RecipeIngredient[] char => RecipeIngredient map */
/**
* @var RecipeIngredient[] char => RecipeIngredient map
* @phpstan-var array<string, RecipeIngredient>
*/
private array $ingredientList = [];
/** @var Item[] */
/**
* @var Item[]
* @phpstan-var list<Item>
*/
private array $results = [];
private int $height;
@ -56,6 +65,10 @@ class ShapedRecipe implements CraftingRecipe{
* @param Item[] $results List of items that this recipe produces when crafted.
*
* Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns.
*
* @phpstan-param list<string> $shape
* @phpstan-param array<string, RecipeIngredient> $ingredients
* @phpstan-param list<Item> $results
*/
public function __construct(array $shape, array $ingredients, array $results){
$this->height = count($shape);
@ -84,7 +97,7 @@ class ShapedRecipe implements CraftingRecipe{
$this->shape = $shape;
foreach($ingredients as $char => $i){
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 +118,7 @@ class ShapedRecipe implements CraftingRecipe{
/**
* @return Item[]
* @phpstan-return list<Item>
*/
public function getResults() : array{
return Utils::cloneObjectArray($this->results);
@ -112,6 +126,7 @@ class ShapedRecipe implements CraftingRecipe{
/**
* @return Item[]
* @phpstan-return list<Item>
*/
public function getResultsFor(CraftingGrid $grid) : array{
return $this->getResults();
@ -119,6 +134,7 @@ class ShapedRecipe implements CraftingRecipe{
/**
* @return (RecipeIngredient|null)[][]
* @phpstan-return list<list<RecipeIngredient|null>>
*/
public function getIngredientMap() : array{
$ingredients = [];
@ -132,9 +148,6 @@ class ShapedRecipe implements CraftingRecipe{
return $ingredients;
}
/**
* @return RecipeIngredient[]
*/
public function getIngredientList() : array{
$ingredients = [];
@ -157,6 +170,7 @@ class ShapedRecipe implements CraftingRecipe{
/**
* Returns an array of strings containing characters representing the recipe's shape.
* @return string[]
* @phpstan-return list<string>
*/
public function getShape() : array{
return $this->shape;

View File

@ -28,15 +28,24 @@ use pocketmine\utils\Utils;
use function count;
class ShapelessRecipe implements CraftingRecipe{
/** @var RecipeIngredient[] */
/**
* @var RecipeIngredient[]
* @phpstan-var list<RecipeIngredient>
*/
private array $ingredients = [];
/** @var Item[] */
/**
* @var Item[]
* @phpstan-var list<Item>
*/
private array $results;
private ShapelessRecipeType $type;
/**
* @param RecipeIngredient[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
* @param Item[] $results List of result items created by this recipe.
*
* @phpstan-param list<RecipeIngredient> $ingredients
* @phpstan-param list<Item> $results
*/
public function __construct(array $ingredients, array $results, ShapelessRecipeType $type){
$this->type = $type;
@ -50,6 +59,7 @@ class ShapelessRecipe implements CraftingRecipe{
/**
* @return Item[]
* @phpstan-return list<Item>
*/
public function getResults() : array{
return Utils::cloneObjectArray($this->results);
@ -63,9 +73,6 @@ class ShapelessRecipe implements CraftingRecipe{
return $this->type;
}
/**
* @return RecipeIngredient[]
*/
public function getIngredientList() : array{
return $this->ingredients;
}

View File

@ -43,7 +43,10 @@ final class CrashDumpData implements \JsonSerializable{
public string $plugin = "";
/** @var string[] */
/**
* @var string[]
* @phpstan-var array<int, string>
*/
public array $code = [];
/** @var string[] */
@ -55,7 +58,10 @@ final class CrashDumpData implements \JsonSerializable{
*/
public array $plugins = [];
/** @var string[] */
/**
* @var string[]
* @phpstan-var list<string>
*/
public array $parameters = [];
public string $serverDotProperties = "";

View File

@ -56,9 +56,11 @@ final class BannerPatternTypeIdMap{
BannerPatternType::DIAGONAL_UP_LEFT => "ld",
BannerPatternType::DIAGONAL_UP_RIGHT => "rud",
BannerPatternType::FLOWER => "flo",
BannerPatternType::FLOW => "flw",
BannerPatternType::GLOBE => "glb",
BannerPatternType::GRADIENT => "gra",
BannerPatternType::GRADIENT_UP => "gru",
BannerPatternType::GUSTER => "gus",
BannerPatternType::HALF_HORIZONTAL => "hh",
BannerPatternType::HALF_HORIZONTAL_BOTTOM => "hhb",
BannerPatternType::HALF_VERTICAL => "vh",

View File

@ -35,6 +35,7 @@ final class BedrockDataFiles{
public const BIOME_DEFINITIONS_FULL_NBT = BEDROCK_DATA_PATH . '/biome_definitions_full.nbt';
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';
@ -44,6 +45,7 @@ final class BedrockDataFiles{
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 REQUIRED_ITEM_LIST_JSON = BEDROCK_DATA_PATH . '/required_item_list.json';

View File

@ -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;
}

View File

@ -43,6 +43,7 @@ final class EnchantmentIdMap{
$this->register(EnchantmentIds::PROJECTILE_PROTECTION, VanillaEnchantments::PROJECTILE_PROTECTION());
$this->register(EnchantmentIds::THORNS, VanillaEnchantments::THORNS());
$this->register(EnchantmentIds::RESPIRATION, VanillaEnchantments::RESPIRATION());
$this->register(EnchantmentIds::AQUA_AFFINITY, VanillaEnchantments::AQUA_AFFINITY());
$this->register(EnchantmentIds::SHARPNESS, VanillaEnchantments::SHARPNESS());
//TODO: smite, bane of arthropods (these don't make sense now because their applicable mobs don't exist yet)

View File

@ -48,7 +48,7 @@ final class ItemTagToIdMap{
throw new AssumptionFailedError("Invalid item tag map, expected array");
}
$cleanMap = [];
foreach($map as $tagName => $ids){
foreach(Utils::promoteKeys($map) as $tagName => $ids){
if(!is_string($tagName)){
throw new AssumptionFailedError("Invalid item tag name $tagName, expected string as key");
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use function is_array;
use function is_int;
use function is_string;
@ -43,7 +44,7 @@ abstract class LegacyToStringIdMap{
if(!is_array($stringToLegacyId)){
throw new AssumptionFailedError("Invalid format of ID map");
}
foreach($stringToLegacyId as $stringId => $legacyId){
foreach(Utils::promoteKeys($stringToLegacyId) as $stringId => $legacyId){
if(!is_string($stringId) || !is_int($legacyId)){
throw new AssumptionFailedError("ID map should have string keys and int values");
}

View File

@ -45,8 +45,8 @@ final class BlockStateData{
public const CURRENT_VERSION =
(1 << 24) | //major
(21 << 16) | //minor
(30 << 8) | //patch
(7); //revision
(40 << 8) | //patch
(1); //revision
public const TAG_NAME = "name";
public const TAG_STATES = "states";

View File

@ -93,12 +93,17 @@ final class BlockStateNames{
public const MC_VERTICAL_HALF = "minecraft:vertical_half";
public const MOISTURIZED_AMOUNT = "moisturized_amount";
public const MULTI_FACE_DIRECTION_BITS = "multi_face_direction_bits";
public const NATURAL = "natural";
public const OCCUPIED_BIT = "occupied_bit";
public const OMINOUS = "ominous";
public const OPEN_BIT = "open_bit";
public const ORIENTATION = "orientation";
public const OUTPUT_LIT_BIT = "output_lit_bit";
public const OUTPUT_SUBTRACT_BIT = "output_subtract_bit";
public const PALE_MOSS_CARPET_SIDE_EAST = "pale_moss_carpet_side_east";
public const PALE_MOSS_CARPET_SIDE_NORTH = "pale_moss_carpet_side_north";
public const PALE_MOSS_CARPET_SIDE_SOUTH = "pale_moss_carpet_side_south";
public const PALE_MOSS_CARPET_SIDE_WEST = "pale_moss_carpet_side_west";
public const PERSISTENT_BIT = "persistent_bit";
public const PILLAR_AXIS = "pillar_axis";
public const PORTAL_AXIS = "portal_axis";
@ -114,9 +119,9 @@ final class BlockStateNames{
public const SEA_GRASS_TYPE = "sea_grass_type";
public const STABILITY = "stability";
public const STABILITY_CHECK = "stability_check";
public const STRIPPED_BIT = "stripped_bit";
public const STRUCTURE_BLOCK_TYPE = "structure_block_type";
public const SUSPENDED_BIT = "suspended_bit";
public const TIP = "tip";
public const TOGGLE_BIT = "toggle_bit";
public const TORCH_FACING_DIRECTION = "torch_facing_direction";
public const TRIAL_SPAWNER_STATE = "trial_spawner_state";

View File

@ -106,6 +106,22 @@ final class BlockStateStringValues{
public const ORIENTATION_UP_WEST = "up_west";
public const ORIENTATION_WEST_UP = "west_up";
public const PALE_MOSS_CARPET_SIDE_EAST_NONE = "none";
public const PALE_MOSS_CARPET_SIDE_EAST_SHORT = "short";
public const PALE_MOSS_CARPET_SIDE_EAST_TALL = "tall";
public const PALE_MOSS_CARPET_SIDE_NORTH_NONE = "none";
public const PALE_MOSS_CARPET_SIDE_NORTH_SHORT = "short";
public const PALE_MOSS_CARPET_SIDE_NORTH_TALL = "tall";
public const PALE_MOSS_CARPET_SIDE_SOUTH_NONE = "none";
public const PALE_MOSS_CARPET_SIDE_SOUTH_SHORT = "short";
public const PALE_MOSS_CARPET_SIDE_SOUTH_TALL = "tall";
public const PALE_MOSS_CARPET_SIDE_WEST_NONE = "none";
public const PALE_MOSS_CARPET_SIDE_WEST_SHORT = "short";
public const PALE_MOSS_CARPET_SIDE_WEST_TALL = "tall";
public const PILLAR_AXIS_X = "x";
public const PILLAR_AXIS_Y = "y";
public const PILLAR_AXIS_Z = "z";

View File

@ -192,6 +192,7 @@ final class BlockTypeNames{
public const CAVE_VINES_HEAD_WITH_BERRIES = "minecraft:cave_vines_head_with_berries";
public const CHAIN = "minecraft:chain";
public const CHAIN_COMMAND_BLOCK = "minecraft:chain_command_block";
public const CHALKBOARD = "minecraft:chalkboard";
public const CHEMICAL_HEAT = "minecraft:chemical_heat";
public const CHERRY_BUTTON = "minecraft:cherry_button";
public const CHERRY_DOOR = "minecraft:cherry_door";
@ -219,6 +220,7 @@ final class BlockTypeNames{
public const CHISELED_POLISHED_BLACKSTONE = "minecraft:chiseled_polished_blackstone";
public const CHISELED_QUARTZ_BLOCK = "minecraft:chiseled_quartz_block";
public const CHISELED_RED_SANDSTONE = "minecraft:chiseled_red_sandstone";
public const CHISELED_RESIN_BRICKS = "minecraft:chiseled_resin_bricks";
public const CHISELED_SANDSTONE = "minecraft:chiseled_sandstone";
public const CHISELED_STONE_BRICKS = "minecraft:chiseled_stone_bricks";
public const CHISELED_TUFF = "minecraft:chiseled_tuff";
@ -227,6 +229,7 @@ final class BlockTypeNames{
public const CHORUS_PLANT = "minecraft:chorus_plant";
public const CLAY = "minecraft:clay";
public const CLIENT_REQUEST_PLACEHOLDER_BLOCK = "minecraft:client_request_placeholder_block";
public const CLOSED_EYEBLOSSOM = "minecraft:closed_eyeblossom";
public const COAL_BLOCK = "minecraft:coal_block";
public const COAL_ORE = "minecraft:coal_ore";
public const COARSE_DIRT = "minecraft:coarse_dirt";
@ -262,6 +265,8 @@ final class BlockTypeNames{
public const CRACKED_STONE_BRICKS = "minecraft:cracked_stone_bricks";
public const CRAFTER = "minecraft:crafter";
public const CRAFTING_TABLE = "minecraft:crafting_table";
public const CREAKING_HEART = "minecraft:creaking_heart";
public const CREEPER_HEAD = "minecraft:creeper_head";
public const CRIMSON_BUTTON = "minecraft:crimson_button";
public const CRIMSON_DOOR = "minecraft:crimson_door";
public const CRIMSON_DOUBLE_SLAB = "minecraft:crimson_double_slab";
@ -384,6 +389,7 @@ final class BlockTypeNames{
public const DISPENSER = "minecraft:dispenser";
public const DOUBLE_CUT_COPPER_SLAB = "minecraft:double_cut_copper_slab";
public const DRAGON_EGG = "minecraft:dragon_egg";
public const DRAGON_HEAD = "minecraft:dragon_head";
public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block";
public const DRIPSTONE_BLOCK = "minecraft:dripstone_block";
public const DROPPER = "minecraft:dropper";
@ -797,6 +803,7 @@ final class BlockTypeNames{
public const MUD_BRICK_WALL = "minecraft:mud_brick_wall";
public const MUD_BRICKS = "minecraft:mud_bricks";
public const MUDDY_MANGROVE_ROOTS = "minecraft:muddy_mangrove_roots";
public const MUSHROOM_STEM = "minecraft:mushroom_stem";
public const MYCELIUM = "minecraft:mycelium";
public const NETHER_BRICK = "minecraft:nether_brick";
public const NETHER_BRICK_DOUBLE_SLAB = "minecraft:nether_brick_double_slab";
@ -828,6 +835,7 @@ final class BlockTypeNames{
public const OBSERVER = "minecraft:observer";
public const OBSIDIAN = "minecraft:obsidian";
public const OCHRE_FROGLIGHT = "minecraft:ochre_froglight";
public const OPEN_EYEBLOSSOM = "minecraft:open_eyeblossom";
public const ORANGE_CANDLE = "minecraft:orange_candle";
public const ORANGE_CANDLE_CAKE = "minecraft:orange_candle_cake";
public const ORANGE_CARPET = "minecraft:orange_carpet";
@ -853,10 +861,31 @@ final class BlockTypeNames{
public const OXIDIZED_DOUBLE_CUT_COPPER_SLAB = "minecraft:oxidized_double_cut_copper_slab";
public const PACKED_ICE = "minecraft:packed_ice";
public const PACKED_MUD = "minecraft:packed_mud";
public const PALE_HANGING_MOSS = "minecraft:pale_hanging_moss";
public const PALE_MOSS_BLOCK = "minecraft:pale_moss_block";
public const PALE_MOSS_CARPET = "minecraft:pale_moss_carpet";
public const PALE_OAK_BUTTON = "minecraft:pale_oak_button";
public const PALE_OAK_DOOR = "minecraft:pale_oak_door";
public const PALE_OAK_DOUBLE_SLAB = "minecraft:pale_oak_double_slab";
public const PALE_OAK_FENCE = "minecraft:pale_oak_fence";
public const PALE_OAK_FENCE_GATE = "minecraft:pale_oak_fence_gate";
public const PALE_OAK_HANGING_SIGN = "minecraft:pale_oak_hanging_sign";
public const PALE_OAK_LEAVES = "minecraft:pale_oak_leaves";
public const PALE_OAK_LOG = "minecraft:pale_oak_log";
public const PALE_OAK_PLANKS = "minecraft:pale_oak_planks";
public const PALE_OAK_PRESSURE_PLATE = "minecraft:pale_oak_pressure_plate";
public const PALE_OAK_SAPLING = "minecraft:pale_oak_sapling";
public const PALE_OAK_SLAB = "minecraft:pale_oak_slab";
public const PALE_OAK_STAIRS = "minecraft:pale_oak_stairs";
public const PALE_OAK_STANDING_SIGN = "minecraft:pale_oak_standing_sign";
public const PALE_OAK_TRAPDOOR = "minecraft:pale_oak_trapdoor";
public const PALE_OAK_WALL_SIGN = "minecraft:pale_oak_wall_sign";
public const PALE_OAK_WOOD = "minecraft:pale_oak_wood";
public const PEARLESCENT_FROGLIGHT = "minecraft:pearlescent_froglight";
public const PEONY = "minecraft:peony";
public const PETRIFIED_OAK_DOUBLE_SLAB = "minecraft:petrified_oak_double_slab";
public const PETRIFIED_OAK_SLAB = "minecraft:petrified_oak_slab";
public const PIGLIN_HEAD = "minecraft:piglin_head";
public const PINK_CANDLE = "minecraft:pink_candle";
public const PINK_CANDLE_CAKE = "minecraft:pink_candle_cake";
public const PINK_CARPET = "minecraft:pink_carpet";
@ -874,6 +903,7 @@ final class BlockTypeNames{
public const PISTON_ARM_COLLISION = "minecraft:piston_arm_collision";
public const PITCHER_CROP = "minecraft:pitcher_crop";
public const PITCHER_PLANT = "minecraft:pitcher_plant";
public const PLAYER_HEAD = "minecraft:player_head";
public const PODZOL = "minecraft:podzol";
public const POINTED_DRIPSTONE = "minecraft:pointed_dripstone";
public const POLISHED_ANDESITE = "minecraft:polished_andesite";
@ -989,6 +1019,13 @@ final class BlockTypeNames{
public const REINFORCED_DEEPSLATE = "minecraft:reinforced_deepslate";
public const REPEATING_COMMAND_BLOCK = "minecraft:repeating_command_block";
public const RESERVED6 = "minecraft:reserved6";
public const RESIN_BLOCK = "minecraft:resin_block";
public const RESIN_BRICK_DOUBLE_SLAB = "minecraft:resin_brick_double_slab";
public const RESIN_BRICK_SLAB = "minecraft:resin_brick_slab";
public const RESIN_BRICK_STAIRS = "minecraft:resin_brick_stairs";
public const RESIN_BRICK_WALL = "minecraft:resin_brick_wall";
public const RESIN_BRICKS = "minecraft:resin_bricks";
public const RESIN_CLUMP = "minecraft:resin_clump";
public const RESPAWN_ANCHOR = "minecraft:respawn_anchor";
public const ROSE_BUSH = "minecraft:rose_bush";
public const SAND = "minecraft:sand";
@ -1009,7 +1046,7 @@ final class BlockTypeNames{
public const SHORT_GRASS = "minecraft:short_grass";
public const SHROOMLIGHT = "minecraft:shroomlight";
public const SILVER_GLAZED_TERRACOTTA = "minecraft:silver_glazed_terracotta";
public const SKULL = "minecraft:skull";
public const SKELETON_SKULL = "minecraft:skeleton_skull";
public const SLIME = "minecraft:slime";
public const SMALL_AMETHYST_BUD = "minecraft:small_amethyst_bud";
public const SMALL_DRIPLEAF_BLOCK = "minecraft:small_dripleaf_block";
@ -1091,6 +1128,8 @@ final class BlockTypeNames{
public const STRIPPED_MANGROVE_WOOD = "minecraft:stripped_mangrove_wood";
public const STRIPPED_OAK_LOG = "minecraft:stripped_oak_log";
public const STRIPPED_OAK_WOOD = "minecraft:stripped_oak_wood";
public const STRIPPED_PALE_OAK_LOG = "minecraft:stripped_pale_oak_log";
public const STRIPPED_PALE_OAK_WOOD = "minecraft:stripped_pale_oak_wood";
public const STRIPPED_SPRUCE_LOG = "minecraft:stripped_spruce_log";
public const STRIPPED_SPRUCE_WOOD = "minecraft:stripped_spruce_wood";
public const STRIPPED_WARPED_HYPHAE = "minecraft:stripped_warped_hyphae";
@ -1229,6 +1268,7 @@ final class BlockTypeNames{
public const WHITE_TULIP = "minecraft:white_tulip";
public const WHITE_WOOL = "minecraft:white_wool";
public const WITHER_ROSE = "minecraft:wither_rose";
public const WITHER_SKELETON_SKULL = "minecraft:wither_skeleton_skull";
public const WOODEN_BUTTON = "minecraft:wooden_button";
public const WOODEN_DOOR = "minecraft:wooden_door";
public const WOODEN_PRESSURE_PLATE = "minecraft:wooden_pressure_plate";
@ -1243,4 +1283,5 @@ final class BlockTypeNames{
public const YELLOW_STAINED_GLASS_PANE = "minecraft:yellow_stained_glass_pane";
public const YELLOW_TERRACOTTA = "minecraft:yellow_terracotta";
public const YELLOW_WOOL = "minecraft:yellow_wool";
public const ZOMBIE_HEAD = "minecraft:zombie_head";
}

View File

@ -43,6 +43,7 @@ use pocketmine\block\Cactus;
use pocketmine\block\Cake;
use pocketmine\block\CakeWithCandle;
use pocketmine\block\CakeWithDyedCandle;
use pocketmine\block\Campfire;
use pocketmine\block\Candle;
use pocketmine\block\Carpet;
use pocketmine\block\Carrot;
@ -57,8 +58,12 @@ use pocketmine\block\CocoaBlock;
use pocketmine\block\Concrete;
use pocketmine\block\ConcretePowder;
use pocketmine\block\Copper;
use pocketmine\block\CopperBulb;
use pocketmine\block\CopperDoor;
use pocketmine\block\CopperGrate;
use pocketmine\block\CopperSlab;
use pocketmine\block\CopperStairs;
use pocketmine\block\CopperTrapdoor;
use pocketmine\block\Coral;
use pocketmine\block\CoralBlock;
use pocketmine\block\DaylightSensor;
@ -124,6 +129,7 @@ use pocketmine\block\SimplePressurePlate;
use pocketmine\block\Slab;
use pocketmine\block\SmallDripleaf;
use pocketmine\block\SnowLayer;
use pocketmine\block\SoulCampfire;
use pocketmine\block\Sponge;
use pocketmine\block\StainedGlass;
use pocketmine\block\StainedGlassPane;
@ -151,6 +157,7 @@ use pocketmine\block\utils\DripleafState;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\FroglightType;
use pocketmine\block\utils\LeverFacing;
use pocketmine\block\utils\MobHeadType;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\block\Vine;
use pocketmine\block\Wall;
@ -205,6 +212,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->registerFlatWoodBlockSerializers();
$this->registerLeavesSerializers();
$this->registerSaplingSerializers();
$this->registerMobHeadSerializers();
$this->registerSimpleSerializers();
$this->registerSerializers();
}
@ -624,17 +632,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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->map(Blocks::CHERRY_WOOD(), function(Wood $block) : Writer{
//we can't use the standard method for this because cherry_wood has a useless property attached to it
if(!$block->isStripped()){
return Writer::create(Ids::CHERRY_WOOD)
->writePillarAxis($block->getAxis())
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
}else{
return Writer::create(Ids::STRIPPED_CHERRY_WOOD)
->writePillarAxis($block->getAxis());
}
});
$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)));
@ -690,17 +688,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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->map(Blocks::MANGROVE_WOOD(), function(Wood $block) : Writer{
//we can't use the standard method for this because mangrove_wood has a useless property attached to it
if(!$block->isStripped()){
return Writer::create(Ids::MANGROVE_WOOD)
->writePillarAxis($block->getAxis())
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
}else{
return Writer::create(Ids::STRIPPED_MANGROVE_WOOD)
->writePillarAxis($block->getAxis());
}
});
$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)));
@ -775,6 +763,18 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
}
}
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);
@ -797,6 +797,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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);
@ -1020,6 +1022,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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);
@ -1055,6 +1058,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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);
@ -1078,7 +1082,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered())
->writeInt(StateNames::RAIL_DIRECTION, $block->getShape());
});
$this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK)
$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()){
@ -1193,6 +1197,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
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)
@ -1244,6 +1253,40 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
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() ?
@ -1311,6 +1354,67 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
)
);
});
$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())
@ -1483,10 +1587,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
});
$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->map(Blocks::MOB_HEAD(), function(MobHead $block) : Writer{
return Writer::create(Ids::SKULL)
->writeFacingWithoutDown($block->getFacing());
});
$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)));
@ -1498,7 +1598,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::MUD_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::MUD_BRICK_WALL)));
$this->map(Blocks::MUDDY_MANGROVE_ROOTS(), fn(SimplePillar $block) => Writer::create(Ids::MUDDY_MANGROVE_ROOTS)
->writePillarAxis($block->getAxis()));
$this->map(Blocks::MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK)
$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);
@ -1556,6 +1656,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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)
@ -1645,6 +1748,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->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
@ -1704,6 +1812,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->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());

View File

@ -26,9 +26,6 @@ namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\Block;
use pocketmine\block\Button;
use pocketmine\block\Candle;
use pocketmine\block\Copper;
use pocketmine\block\CopperSlab;
use pocketmine\block\CopperStairs;
use pocketmine\block\Crops;
use pocketmine\block\DaylightSensor;
use pocketmine\block\Door;
@ -48,9 +45,9 @@ use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\Stem;
use pocketmine\block\Trapdoor;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\SlabType;
use pocketmine\block\VanillaBlocks;
use pocketmine\block\Wall;
use pocketmine\block\WallSign;
use pocketmine\block\WeightedPressurePlate;
@ -100,24 +97,24 @@ final class BlockStateDeserializerHelper{
}
/**
* @phpstan-template TBlock of Copper|CopperSlab|CopperStairs
* @phpstan-template TBlock of CopperMaterial
*
* @phpstan-param TBlock $block
* @phpstan-return TBlock
*/
public static function decodeCopper(Copper|CopperSlab|CopperStairs $block, CopperOxidation $oxidation) : Copper|CopperSlab|CopperStairs{
public static function decodeCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{
$block->setOxidation($oxidation);
$block->setWaxed(false);
return $block;
}
/**
* @phpstan-template TBlock of Copper|CopperSlab|CopperStairs
* @phpstan-template TBlock of CopperMaterial
*
* @phpstan-param TBlock $block
* @phpstan-return TBlock
*/
public static function decodeWaxedCopper(Copper|CopperSlab|CopperStairs $block, CopperOxidation $oxidation) : Copper|CopperSlab|CopperStairs{
public static function decodeWaxedCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{
$block->setOxidation($oxidation);
$block->setWaxed(true);
return $block;
@ -210,8 +207,8 @@ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeMushroomBlock(RedMushroomBlock $block, BlockStateReader $in) : Block{
switch($type = $in->readBoundedInt(BlockStateNames::HUGE_MUSHROOM_BITS, 0, 15)){
case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM: return VanillaBlocks::ALL_SIDED_MUSHROOM_STEM();
case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: return VanillaBlocks::MUSHROOM_STEM();
case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM:
case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: throw new BlockStateDeserializeException("This state does not exist");
default:
//invalid types get left as default
$type = MushroomBlockTypeIdMap::getInstance()->fromId($type);

View File

@ -45,6 +45,7 @@ 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;
@ -85,6 +86,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->registerLeavesDeserializers();
$this->registerSaplingDeserializers();
$this->registerLightDeserializers();
$this->registerMobHeadDeserializers();
$this->registerSimpleDeserializers();
$this->registerDeserializers();
}
@ -531,10 +533,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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, function(Reader $in){
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
return Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in);
});
$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));
@ -591,10 +590,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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, function(Reader $in){
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
return Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in);
});
$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
@ -690,6 +686,20 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
}
}
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());
@ -712,6 +722,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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());
@ -930,6 +942,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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());
@ -967,6 +980,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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());
@ -1099,6 +1113,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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()
@ -1108,6 +1127,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
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()
@ -1147,6 +1171,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return $block;
});
$this->map(Ids::CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE));
$this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{
return Blocks::CHISELED_QUARTZ()
->setAxis($in->readPillarAxis());
@ -1178,6 +1203,14 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->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));
@ -1236,9 +1269,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->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));
@ -1392,9 +1434,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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{
@ -1441,6 +1492,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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)){
@ -1521,10 +1575,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1)
->setUnderwater(!$in->readBool(StateNames::DEAD_BIT));
});
$this->map(Ids::SKULL, function(Reader $in) : Block{
return Blocks::MOB_HEAD()
->setFacing($in->readFacingWithoutDown());
});
$this->map(Ids::SMOKER, function(Reader $in) : Block{
return Blocks::SMOKER()
->setFacing($in->readCardinalHorizontalFacing())
@ -1555,6 +1605,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$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();
@ -1618,6 +1673,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->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));
@ -1654,25 +1715,70 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
$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));

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenInfo as FlattenInfo;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap as ValueRemap;
use pocketmine\nbt\tag\Tag;
use function count;
@ -58,6 +59,12 @@ final class BlockStateUpgradeSchema{
*/
public array $remappedPropertyValues = [];
/**
* @var FlattenInfo[]
* @phpstan-var array<string, FlattenInfo>
*/
public array $flattenedProperties = [];
/**
* @var BlockStateUpgradeSchemaBlockRemap[][]
* @phpstan-var array<string, list<BlockStateUpgradeSchemaBlockRemap>>
@ -93,6 +100,7 @@ final class BlockStateUpgradeSchema{
$this->removedProperties,
$this->renamedProperties,
$this->remappedPropertyValues,
$this->flattenedProperties,
$this->remappedStates,
] as $list){
if(count($list) !== 0){

View File

@ -40,7 +40,7 @@ final class BlockStateUpgradeSchemaBlockRemap{
*/
public function __construct(
public array $oldState,
public string|BlockStateUpgradeSchemaFlattenedName $newName,
public string|BlockStateUpgradeSchemaFlattenInfo $newName,
public array $newState,
public array $copiedState
){}
@ -48,8 +48,8 @@ final class BlockStateUpgradeSchemaBlockRemap{
public function equals(self $that) : bool{
$sameName = $this->newName === $that->newName ||
(
$this->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
$that->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
$this->newName instanceof BlockStateUpgradeSchemaFlattenInfo &&
$that->newName instanceof BlockStateUpgradeSchemaFlattenInfo &&
$this->newName->equals($that->newName)
);
if(!$sameName){

View File

@ -23,20 +23,25 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use function ksort;
use const SORT_STRING;
final class BlockStateUpgradeSchemaFlattenedName{
final class BlockStateUpgradeSchemaFlattenInfo{
/**
* @param string[] $flattenedValueRemaps
* @phpstan-param array<string, string> $flattenedValueRemaps
* @phpstan-param ?class-string<ByteTag|IntTag|StringTag> $flattenedPropertyType
*/
public function __construct(
public string $prefix,
public string $flattenedProperty,
public string $suffix,
public array $flattenedValueRemaps
public array $flattenedValueRemaps,
public ?string $flattenedPropertyType = null
){
ksort($this->flattenedValueRemaps, SORT_STRING);
}
@ -45,6 +50,7 @@ final class BlockStateUpgradeSchemaFlattenedName{
return $this->prefix === $that->prefix &&
$this->flattenedProperty === $that->flattenedProperty &&
$this->suffix === $that->suffix &&
$this->flattenedValueRemaps === $that->flattenedValueRemaps;
$this->flattenedValueRemaps === $that->flattenedValueRemaps &&
$this->flattenedPropertyType === $that->flattenedPropertyType;
}
}

View File

@ -25,7 +25,7 @@ namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModel;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenedName;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenInfo;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelTag;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap;
use pocketmine\nbt\tag\ByteTag;
@ -155,20 +155,24 @@ final class BlockStateUpgradeSchemaUtils{
}
}
foreach(Utils::stringifyKeys($model->flattenedProperties ?? []) as $blockName => $flattenRule){
$result->flattenedProperties[$blockName] = self::jsonModelToFlattenRule($flattenRule);
}
foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){
foreach($remaps as $remap){
if(isset($remap->newName) === isset($remap->newFlattenedName)){
if(isset($remap->newName)){
$remapName = $remap->newName;
}elseif(isset($remap->newFlattenedName)){
$flattenRule = $remap->newFlattenedName;
$remapName = self::jsonModelToFlattenRule($flattenRule);
}else{
throw new \UnexpectedValueException("Expected exactly one of 'newName' or 'newFlattenedName' properties to be set");
}
$result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap(
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []),
$remap->newName ?? new BlockStateUpgradeSchemaFlattenedName(
$remap->newFlattenedName->prefix,
$remap->newFlattenedName->flattenedProperty,
$remap->newFlattenedName->suffix,
$remap->newFlattenedName->flattenedValueRemaps ?? [],
),
$remapName,
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
$remap->copiedState ?? []
);
@ -254,6 +258,36 @@ final class BlockStateUpgradeSchemaUtils{
$model->remappedPropertyValues = $modelDedupMapping;
}
private static function flattenRuleToJsonModel(BlockStateUpgradeSchemaFlattenInfo $flattenRule) : BlockStateUpgradeSchemaModelFlattenInfo{
return new BlockStateUpgradeSchemaModelFlattenInfo(
$flattenRule->prefix,
$flattenRule->flattenedProperty,
$flattenRule->suffix,
$flattenRule->flattenedValueRemaps,
match($flattenRule->flattenedPropertyType){
StringTag::class => null, //omit for TAG_String, as this is the common case
ByteTag::class => "byte",
IntTag::class => "int",
default => throw new \LogicException("Unexpected tag type " . $flattenRule->flattenedPropertyType . " in flattened property type")
}
);
}
private static function jsonModelToFlattenRule(BlockStateUpgradeSchemaModelFlattenInfo $flattenRule) : BlockStateUpgradeSchemaFlattenInfo{
return new BlockStateUpgradeSchemaFlattenInfo(
$flattenRule->prefix,
$flattenRule->flattenedProperty,
$flattenRule->suffix,
$flattenRule->flattenedValueRemaps ?? [],
match ($flattenRule->flattenedPropertyType) {
"string", null => StringTag::class,
"int" => IntTag::class,
"byte" => ByteTag::class,
default => throw new \UnexpectedValueException("Unexpected flattened property type $flattenRule->flattenedPropertyType, expected 'string', 'int' or 'byte'")
}
);
}
public static function toJsonModel(BlockStateUpgradeSchema $schema) : BlockStateUpgradeSchemaModel{
$result = new BlockStateUpgradeSchemaModel();
$result->maxVersionMajor = $schema->maxVersionMajor;
@ -292,19 +326,19 @@ final class BlockStateUpgradeSchemaUtils{
self::buildRemappedValuesIndex($schema, $result);
foreach(Utils::stringifyKeys($schema->flattenedProperties) as $blockName => $flattenRule){
$result->flattenedProperties[$blockName] = self::flattenRuleToJsonModel($flattenRule);
}
if(isset($result->flattenedProperties)){
ksort($result->flattenedProperties);
}
foreach(Utils::stringifyKeys($schema->remappedStates) as $oldBlockName => $remaps){
$keyedRemaps = [];
foreach($remaps as $remap){
$modelRemap = new BlockStateUpgradeSchemaModelBlockRemap(
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
is_string($remap->newName) ?
$remap->newName :
new BlockStateUpgradeSchemaModelFlattenedName(
$remap->newName->prefix,
$remap->newName->flattenedProperty,
$remap->newName->suffix,
$remap->newName->flattenedValueRemaps
),
is_string($remap->newName) ? $remap->newName : self::flattenRuleToJsonModel($remap->newName),
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
$remap->copiedState
);

View File

@ -24,10 +24,14 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
use function count;
use function get_class;
use function is_string;
use function ksort;
use function max;
@ -79,6 +83,8 @@ final class BlockStateUpgrader{
* version doesn't tell us which of the schemas have already been applied.
* If there's only one schema for a version (the norm), we can safely assume it's already been applied if
* the version is the same, and skip over it.
* TODO: this causes issues when testing isolated schemas since there will only be one schema for a version.
* The second check should be disabled for that case.
*/
if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){
continue;
@ -104,10 +110,21 @@ final class BlockStateUpgrader{
}
$oldName = $blockStateData->getName();
$newName = $schema->renamedIds[$oldName] ?? null;
$states = $blockStateData->getStates();
if(isset($schema->renamedIds[$oldName]) && isset($schema->flattenedProperties[$oldName])){
//TODO: this probably ought to be validated when the schema is constructed
throw new AssumptionFailedError("Both renamedIds and flattenedProperties are set for the same block ID \"$oldName\" - don't know what to do");
}
if(isset($schema->renamedIds[$oldName])){
$newName = $schema->renamedIds[$oldName] ?? null;
}elseif(isset($schema->flattenedProperties[$oldName])){
[$newName, $states] = $this->applyPropertyFlattened($schema->flattenedProperties[$oldName], $oldName, $states);
}else{
$newName = null;
}
$stateChanges = 0;
$states = $blockStateData->getStates();
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
@ -140,15 +157,8 @@ final class BlockStateUpgrader{
if(is_string($remap->newName)){
$newName = $remap->newName;
}else{
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
if($flattenedValue instanceof StringTag){
$embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue();
$newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix);
unset($oldState[$remap->newName->flattenedProperty]);
}else{
//flattened property is not a TAG_String, so this transformation is not applicable
continue;
}
//discard flatten modifications to state - the remap newState and copiedState will take care of it
[$newName, ] = $this->applyPropertyFlattened($remap->newName, $oldName, $oldState);
}
$newState = $remap->newState;
@ -266,4 +276,32 @@ final class BlockStateUpgrader{
return $states;
}
/**
* @param Tag[] $states
* @phpstan-param array<string, Tag> $states
*
* @return (string|Tag[])[]
* @phpstan-return array{0: string, 1: array<string, Tag>}
*/
private function applyPropertyFlattened(BlockStateUpgradeSchemaFlattenInfo $flattenInfo, string $oldName, array $states) : array{
$flattenedValue = $states[$flattenInfo->flattenedProperty] ?? null;
$expectedType = $flattenInfo->flattenedPropertyType;
if(!$flattenedValue instanceof $expectedType){
//flattened property is not of the expected type, so this transformation is not applicable
return [$oldName, $states];
}
$embedKey = match(get_class($flattenedValue)){
StringTag::class => $flattenedValue->getValue(),
ByteTag::class => (string) $flattenedValue->getValue(),
IntTag::class => (string) $flattenedValue->getValue(),
//flattenedPropertyType is always one of these three types, but PHPStan doesn't know that
default => throw new AssumptionFailedError("flattenedPropertyType should be one of these three types, but have " . get_class($flattenedValue)),
};
$embedValue = $flattenInfo->flattenedValueRemaps[$embedKey] ?? $embedKey;
$newName = sprintf("%s%s%s", $flattenInfo->prefix, $embedValue, $flattenInfo->suffix);
unset($states[$flattenInfo->flattenedProperty]);
return [$newName, $states];
}
}

View File

@ -75,6 +75,12 @@ final class BlockStateUpgradeSchemaModel implements \JsonSerializable{
*/
public array $remappedPropertyValuesIndex;
/**
* @var BlockStateUpgradeSchemaModelFlattenInfo[]
* @phpstan-var array<string, BlockStateUpgradeSchemaModelFlattenInfo>
*/
public array $flattenedProperties;
/**
* @var BlockStateUpgradeSchemaModelBlockRemap[][]
* @phpstan-var array<string, list<BlockStateUpgradeSchemaModelBlockRemap>>

View File

@ -43,7 +43,7 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
* Either this or newName must be present
* Due to technical limitations of jsonmapper, we can't use a union type here
*/
public BlockStateUpgradeSchemaModelFlattenedName $newFlattenedName;
public BlockStateUpgradeSchemaModelFlattenInfo $newFlattenedName;
/**
* @var BlockStateUpgradeSchemaModelTag[]|null
@ -67,9 +67,9 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
* @phpstan-param list<string> $copiedState
*/
public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenedName $newNameRule, array $newState, array $copiedState){
public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenInfo $newNameRule, array $newState, array $copiedState){
$this->oldState = count($oldState) === 0 ? null : $oldState;
if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenedName){
if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenInfo){
$this->newFlattenedName = $newNameRule;
}else{
$this->newName = $newNameRule;

View File

@ -25,12 +25,13 @@ namespace pocketmine\data\bedrock\block\upgrade\model;
use function count;
final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializable{
final class BlockStateUpgradeSchemaModelFlattenInfo implements \JsonSerializable{
/** @required */
public string $prefix;
/** @required */
public string $flattenedProperty;
public ?string $flattenedPropertyType = null;
/** @required */
public string $suffix;
/**
@ -43,11 +44,12 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab
* @param string[] $flattenedValueRemaps
* @phpstan-param array<string, string> $flattenedValueRemaps
*/
public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps){
public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps, ?string $flattenedPropertyType = null){
$this->prefix = $prefix;
$this->flattenedProperty = $flattenedProperty;
$this->suffix = $suffix;
$this->flattenedValueRemaps = $flattenedValueRemaps;
$this->flattenedPropertyType = $flattenedPropertyType;
}
/**
@ -58,6 +60,9 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab
if(count($this->flattenedValueRemaps) === 0){
unset($result["flattenedValueRemaps"]);
}
if($this->flattenedPropertyType === null){
unset($result["flattenedPropertyType"]);
}
return $result;
}
}

View File

@ -25,7 +25,8 @@ namespace pocketmine\data\bedrock\item;
use pocketmine\block\Bed;
use pocketmine\block\Block;
use pocketmine\block\MobHead;
use pocketmine\block\CopperDoor;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\data\bedrock\CompoundTypeIds;
@ -33,7 +34,6 @@ use pocketmine\data\bedrock\DyeColorIdMap;
use pocketmine\data\bedrock\item\ItemTypeNames as Ids;
use pocketmine\data\bedrock\item\SavedItemData as Data;
use pocketmine\data\bedrock\MedicineTypeIdMap;
use pocketmine\data\bedrock\MobHeadTypeIdMap;
use pocketmine\data\bedrock\PotionTypeIdMap;
use pocketmine\data\bedrock\SuspiciousStewTypeIdMap;
use pocketmine\item\Banner;
@ -56,6 +56,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->register1to1BlockWithMetaMappings();
$this->register1to1ItemWithMetaMappings();
$this->register1ToNItemMappings();
$this->registerMiscBlockMappings();
$this->registerMiscItemMappings();
}
@ -133,6 +134,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Block(Ids::BIRCH_DOOR, Blocks::BIRCH_DOOR());
$this->map1to1Block(Ids::BREWING_STAND, Blocks::BREWING_STAND());
$this->map1to1Block(Ids::CAKE, Blocks::CAKE());
$this->map1to1Block(Ids::CAMPFIRE, Blocks::CAMPFIRE());
$this->map1to1Block(Ids::CAULDRON, Blocks::CAULDRON());
$this->map1to1Block(Ids::CHAIN, Blocks::CHAIN());
$this->map1to1Block(Ids::CHERRY_DOOR, Blocks::CHERRY_DOOR());
@ -148,6 +150,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR());
$this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART());
$this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER());
$this->map1to1Block(Ids::SOUL_CAMPFIRE, Blocks::SOUL_CAMPFIRE());
$this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR());
$this->map1to1Block(Ids::SUGAR_CANE, Blocks::SUGARCANE());
$this->map1to1Block(Ids::WARPED_DOOR, Blocks::WARPED_DOOR());
@ -464,14 +467,6 @@ final class ItemSerializerDeserializerRegistrar{
},
fn(Bed $block) => DyeColorIdMap::getInstance()->toId($block->getColor())
);
$this->map1to1BlockWithMeta(
Ids::SKULL,
Blocks::MOB_HEAD(),
function(MobHead $block, int $meta) : void{
$block->setMobHeadType(MobHeadTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown mob head type ID $meta"));
},
fn(MobHead $block) => MobHeadTypeIdMap::getInstance()->toId($block->getMobHeadType())
);
}
/**
@ -536,4 +531,29 @@ final class ItemSerializerDeserializerRegistrar{
}
$this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor())));
}
/**
* Registers serializers and deserializers for PocketMine-MP blockitems that don't fit any other pattern.
* Ideally we want to get rid of this completely, if possible.
*
* Most of these are single PocketMine-MP blocks which map to multiple IDs depending on their properties, which is
* complex to implement in a generic way.
*/
private function registerMiscBlockMappings() : void{
$copperDoorStateIdMap = [];
foreach ([
[Ids::COPPER_DOOR, CopperOxidation::NONE, false],
[Ids::EXPOSED_COPPER_DOOR, CopperOxidation::EXPOSED, false],
[Ids::WEATHERED_COPPER_DOOR, CopperOxidation::WEATHERED, false],
[Ids::OXIDIZED_COPPER_DOOR, CopperOxidation::OXIDIZED, false],
[Ids::WAXED_COPPER_DOOR, CopperOxidation::NONE, true],
[Ids::WAXED_EXPOSED_COPPER_DOOR, CopperOxidation::EXPOSED, true],
[Ids::WAXED_WEATHERED_COPPER_DOOR, CopperOxidation::WEATHERED, true],
[Ids::WAXED_OXIDIZED_COPPER_DOOR, CopperOxidation::OXIDIZED, true]
] as [$id, $oxidation, $waxed]) {
$copperDoorStateIdMap[$oxidation->value][$waxed ? 1 : 0] = $id;
$this->deserializer?->mapBlock($id, fn() => Blocks::COPPER_DOOR()->setOxidation($oxidation)->setWaxed($waxed));
}
$this->serializer?->mapBlock(Blocks::COPPER_DOOR(), fn(CopperDoor $block) => new Data($copperDoorStateIdMap[$block->getOxidation()->value][$block->isWaxed() ? 1 : 0]));
}
}

View File

@ -66,13 +66,16 @@ final class ItemTypeNames{
public const BIRCH_DOOR = "minecraft:birch_door";
public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign";
public const BIRCH_SIGN = "minecraft:birch_sign";
public const BLACK_BUNDLE = "minecraft:black_bundle";
public const BLACK_DYE = "minecraft:black_dye";
public const BLADE_POTTERY_SHERD = "minecraft:blade_pottery_sherd";
public const BLAZE_POWDER = "minecraft:blaze_powder";
public const BLAZE_ROD = "minecraft:blaze_rod";
public const BLAZE_SPAWN_EGG = "minecraft:blaze_spawn_egg";
public const BLEACH = "minecraft:bleach";
public const BLUE_BUNDLE = "minecraft:blue_bundle";
public const BLUE_DYE = "minecraft:blue_dye";
public const BOARD = "minecraft:board";
public const BOAT = "minecraft:boat";
public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg";
public const BOLT_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:bolt_armor_trim_smithing_template";
@ -88,6 +91,7 @@ final class ItemTypeNames{
public const BREWER_POTTERY_SHERD = "minecraft:brewer_pottery_sherd";
public const BREWING_STAND = "minecraft:brewing_stand";
public const BRICK = "minecraft:brick";
public const BROWN_BUNDLE = "minecraft:brown_bundle";
public const BROWN_DYE = "minecraft:brown_dye";
public const BRUSH = "minecraft:brush";
public const BUCKET = "minecraft:bucket";
@ -151,12 +155,14 @@ final class ItemTypeNames{
public const CORAL_FAN = "minecraft:coral_fan";
public const CORAL_FAN_DEAD = "minecraft:coral_fan_dead";
public const COW_SPAWN_EGG = "minecraft:cow_spawn_egg";
public const CREAKING_SPAWN_EGG = "minecraft:creaking_spawn_egg";
public const CREEPER_BANNER_PATTERN = "minecraft:creeper_banner_pattern";
public const CREEPER_SPAWN_EGG = "minecraft:creeper_spawn_egg";
public const CRIMSON_DOOR = "minecraft:crimson_door";
public const CRIMSON_HANGING_SIGN = "minecraft:crimson_hanging_sign";
public const CRIMSON_SIGN = "minecraft:crimson_sign";
public const CROSSBOW = "minecraft:crossbow";
public const CYAN_BUNDLE = "minecraft:cyan_bundle";
public const CYAN_DYE = "minecraft:cyan_dye";
public const DANGER_POTTERY_SHERD = "minecraft:danger_pottery_sherd";
public const DARK_OAK_BOAT = "minecraft:dark_oak_boat";
@ -255,7 +261,9 @@ final class ItemTypeNames{
public const GOLDEN_PICKAXE = "minecraft:golden_pickaxe";
public const GOLDEN_SHOVEL = "minecraft:golden_shovel";
public const GOLDEN_SWORD = "minecraft:golden_sword";
public const GRAY_BUNDLE = "minecraft:gray_bundle";
public const GRAY_DYE = "minecraft:gray_dye";
public const GREEN_BUNDLE = "minecraft:green_bundle";
public const GREEN_DYE = "minecraft:green_dye";
public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg";
public const GUNPOWDER = "minecraft:gunpowder";
@ -309,8 +317,11 @@ final class ItemTypeNames{
public const LEAVES = "minecraft:leaves";
public const LEAVES2 = "minecraft:leaves2";
public const LIGHT_BLOCK = "minecraft:light_block";
public const LIGHT_BLUE_BUNDLE = "minecraft:light_blue_bundle";
public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye";
public const LIGHT_GRAY_BUNDLE = "minecraft:light_gray_bundle";
public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye";
public const LIME_BUNDLE = "minecraft:lime_bundle";
public const LIME_DYE = "minecraft:lime_dye";
public const LINGERING_POTION = "minecraft:lingering_potion";
public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg";
@ -318,6 +329,7 @@ final class ItemTypeNames{
public const LOG = "minecraft:log";
public const LOG2 = "minecraft:log2";
public const MACE = "minecraft:mace";
public const MAGENTA_BUNDLE = "minecraft:magenta_bundle";
public const MAGENTA_DYE = "minecraft:magenta_dye";
public const MAGMA_CREAM = "minecraft:magma_cream";
public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg";
@ -384,9 +396,15 @@ final class ItemTypeNames{
public const OCELOT_SPAWN_EGG = "minecraft:ocelot_spawn_egg";
public const OMINOUS_BOTTLE = "minecraft:ominous_bottle";
public const OMINOUS_TRIAL_KEY = "minecraft:ominous_trial_key";
public const ORANGE_BUNDLE = "minecraft:orange_bundle";
public const ORANGE_DYE = "minecraft:orange_dye";
public const OXIDIZED_COPPER_DOOR = "minecraft:oxidized_copper_door";
public const PAINTING = "minecraft:painting";
public const PALE_OAK_BOAT = "minecraft:pale_oak_boat";
public const PALE_OAK_CHEST_BOAT = "minecraft:pale_oak_chest_boat";
public const PALE_OAK_DOOR = "minecraft:pale_oak_door";
public const PALE_OAK_HANGING_SIGN = "minecraft:pale_oak_hanging_sign";
public const PALE_OAK_SIGN = "minecraft:pale_oak_sign";
public const PANDA_SPAWN_EGG = "minecraft:panda_spawn_egg";
public const PAPER = "minecraft:paper";
public const PARROT_SPAWN_EGG = "minecraft:parrot_spawn_egg";
@ -397,6 +415,7 @@ final class ItemTypeNames{
public const PIGLIN_BRUTE_SPAWN_EGG = "minecraft:piglin_brute_spawn_egg";
public const PIGLIN_SPAWN_EGG = "minecraft:piglin_spawn_egg";
public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg";
public const PINK_BUNDLE = "minecraft:pink_bundle";
public const PINK_DYE = "minecraft:pink_dye";
public const PITCHER_POD = "minecraft:pitcher_pod";
public const PLANKS = "minecraft:planks";
@ -416,6 +435,7 @@ final class ItemTypeNames{
public const PUFFERFISH_SPAWN_EGG = "minecraft:pufferfish_spawn_egg";
public const PUMPKIN_PIE = "minecraft:pumpkin_pie";
public const PUMPKIN_SEEDS = "minecraft:pumpkin_seeds";
public const PURPLE_BUNDLE = "minecraft:purple_bundle";
public const PURPLE_DYE = "minecraft:purple_dye";
public const QUARTZ = "minecraft:quartz";
public const RABBIT = "minecraft:rabbit";
@ -430,10 +450,12 @@ final class ItemTypeNames{
public const RAW_GOLD = "minecraft:raw_gold";
public const RAW_IRON = "minecraft:raw_iron";
public const RECOVERY_COMPASS = "minecraft:recovery_compass";
public const RED_BUNDLE = "minecraft:red_bundle";
public const RED_DYE = "minecraft:red_dye";
public const RED_FLOWER = "minecraft:red_flower";
public const REDSTONE = "minecraft:redstone";
public const REPEATER = "minecraft:repeater";
public const RESIN_BRICK = "minecraft:resin_brick";
public const RIB_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:rib_armor_trim_smithing_template";
public const ROTTEN_FLESH = "minecraft:rotten_flesh";
public const SADDLE = "minecraft:saddle";
@ -537,6 +559,7 @@ final class ItemTypeNames{
public const WEATHERED_COPPER_DOOR = "minecraft:weathered_copper_door";
public const WHEAT = "minecraft:wheat";
public const WHEAT_SEEDS = "minecraft:wheat_seeds";
public const WHITE_BUNDLE = "minecraft:white_bundle";
public const WHITE_DYE = "minecraft:white_dye";
public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wild_armor_trim_smithing_template";
public const WIND_CHARGE = "minecraft:wind_charge";
@ -556,6 +579,7 @@ final class ItemTypeNames{
public const WOOL = "minecraft:wool";
public const WRITABLE_BOOK = "minecraft:writable_book";
public const WRITTEN_BOOK = "minecraft:written_book";
public const YELLOW_BUNDLE = "minecraft:yellow_bundle";
public const YELLOW_DYE = "minecraft:yellow_dye";
public const ZOGLIN_SPAWN_EGG = "minecraft:zoglin_spawn_egg";
public const ZOMBIE_HORSE_SPAWN_EGG = "minecraft:zombie_horse_spawn_egg";

View File

@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock\item\upgrade;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader;
use pocketmine\data\bedrock\item\BlockItemIdMap;
use pocketmine\data\bedrock\item\SavedItemData;
use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException;
@ -35,6 +36,7 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\convert\BlockStateDictionary;
use pocketmine\utils\Binary;
use function assert;
@ -46,6 +48,8 @@ final class ItemDataUpgrader{
private LegacyItemIdToStringIdMap $legacyIntToStringIdMap,
private R12ItemIdToBlockIdMap $r12ItemIdToBlockIdMap,
private BlockDataUpgrader $blockDataUpgrader,
private BlockItemIdMap $blockItemIdMap,
private BlockStateDictionary $blockStateDictionary
){}
/**
@ -148,6 +152,17 @@ final class ItemDataUpgrader{
[$newNameId, $newMeta] = $this->idMetaUpgrader->upgrade($rawNameId, $meta);
//TODO: Dirty hack to load old skulls from disk: Put this into item upgrade schema's before Mojang makes something with a non 0 default state
if($blockStateData === null && ($blockId = $this->blockItemIdMap->lookupBlockId($newNameId)) !== null){
$networkRuntimeId = $this->blockStateDictionary->lookupStateIdFromIdMeta($blockId, 0);
if($networkRuntimeId === null){
throw new SavedDataLoadingException("Failed to find blockstate for blockitem $newNameId");
}
$blockStateData = $this->blockStateDictionary->generateDataFromStateId($networkRuntimeId);
}
//TODO: this won't account for spawn eggs from before 1.16.100 - perhaps we're lucky and they just left the meta in there anyway?
//TODO: read version from VersionInfo::TAG_WORLD_DATA_VERSION - we may need it to fix up old items

View File

@ -56,7 +56,7 @@ final class R12ItemIdToBlockIdMap{
}
$builtMap = [];
foreach($map as $itemId => $blockId){
foreach(Utils::promoteKeys($map) as $itemId => $blockId){
if(!is_string($itemId)){
throw new AssumptionFailedError("Invalid blockitem ID mapping table, expected string as key");
}

View File

@ -89,6 +89,11 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{
public function straightOnlyRailShape(int &$railShape) : void;
/**
* @phpstan-template T of \UnitEnum
* @phpstan-param T &$case
* @phpstan-param-out T $case
*/
public function enum(\UnitEnum &$case) : void;
/**

View File

@ -102,7 +102,10 @@ abstract class Entity{
return self::$entityCount++;
}
/** @var Player[] */
/**
* @var Player[]
* @phpstan-var array<int, Player>
*/
protected array $hasSpawned = [];
protected int $id;

View File

@ -37,9 +37,11 @@ class PaintingMotive{
new PaintingMotive(1, 1, "Aztec2"),
new PaintingMotive(1, 1, "Bomb"),
new PaintingMotive(1, 1, "Kebab"),
new PaintingMotive(1, 1, "meditative"),
new PaintingMotive(1, 1, "Plant"),
new PaintingMotive(1, 1, "Wasteland"),
new PaintingMotive(1, 2, "Graham"),
new PaintingMotive(1, 2, "prairie_ride"),
new PaintingMotive(1, 2, "Wanderer"),
new PaintingMotive(2, 1, "Courbet"),
new PaintingMotive(2, 1, "Creebet"),
@ -47,8 +49,10 @@ class PaintingMotive{
new PaintingMotive(2, 1, "Sea"),
new PaintingMotive(2, 1, "Sunset"),
new PaintingMotive(2, 2, "Bust"),
new PaintingMotive(2, 2, "baroque"),
new PaintingMotive(2, 2, "Earth"),
new PaintingMotive(2, 2, "Fire"),
new PaintingMotive(2, 2, "humble"),
new PaintingMotive(2, 2, "Match"),
new PaintingMotive(2, 2, "SkullAndRoses"),
new PaintingMotive(2, 2, "Stage"),
@ -56,12 +60,28 @@ class PaintingMotive{
new PaintingMotive(2, 2, "Water"),
new PaintingMotive(2, 2, "Wind"),
new PaintingMotive(2, 2, "Wither"),
new PaintingMotive(3, 3, "bouquet"),
new PaintingMotive(3, 3, "cavebird"),
new PaintingMotive(3, 3, "cotan"),
new PaintingMotive(3, 3, "endboss"),
new PaintingMotive(3, 3, "fern"),
new PaintingMotive(3, 3, "owlemons"),
new PaintingMotive(3, 3, "sunflowers"),
new PaintingMotive(3, 3, "tides"),
new PaintingMotive(3, 4, "backyard"),
new PaintingMotive(3, 4, "pond"),
new PaintingMotive(4, 2, "changing"),
new PaintingMotive(4, 2, "Fighters"),
new PaintingMotive(4, 2, "finding"),
new PaintingMotive(4, 2, "lowmist"),
new PaintingMotive(4, 2, "passage"),
new PaintingMotive(4, 3, "DonkeyKong"),
new PaintingMotive(4, 3, "Skeleton"),
new PaintingMotive(4, 4, "BurningSkull"),
new PaintingMotive(4, 4, "orb"),
new PaintingMotive(4, 4, "Pigscene"),
new PaintingMotive(4, 4, "Pointer")
new PaintingMotive(4, 4, "Pointer"),
new PaintingMotive(4, 4, "unpacked")
] as $motive){
self::registerMotive($motive);
}

View File

@ -30,7 +30,10 @@ use function spl_object_id;
use const SORT_NUMERIC;
class HandlerList{
/** @var RegisteredListener[][] */
/**
* @var RegisteredListener[][]
* @phpstan-var array<int, array<int, RegisteredListener>>
*/
private array $handlerSlots = [];
/** @var RegisteredListenerCache[] */

View File

@ -0,0 +1,63 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event\block;
use pocketmine\block\Campfire;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
use pocketmine\item\Item;
class CampfireCookEvent extends BlockEvent implements Cancellable{
use CancellableTrait;
public function __construct(
private Campfire $campfire,
private int $slot,
private Item $input,
private Item $result
){
parent::__construct($campfire);
$this->input = clone $input;
}
public function getCampfire() : Campfire{
return $this->campfire;
}
public function getSlot() : int{
return $this->slot;
}
public function getInput() : Item{
return $this->input;
}
public function getResult() : Item{
return $this->result;
}
public function setResult(Item $result) : void{
$this->result = $result;
}
}

View File

@ -57,7 +57,7 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
$this->setModifier($this->getBaseDamage() * 0.3 * $strength->getEffectLevel(), self::MODIFIER_STRENGTH);
}
if(($weakness = $effects->get(VanillaEffects::WEAKNESS())) !== null){
if(($weakness = $effects->get(VanillaEffects::WEAKNESS())) !== null && $this->getCause() === EntityDamageEvent::CAUSE_ENTITY_ATTACK){
$this->setModifier(-($this->getBaseDamage() * 0.2 * $weakness->getEffectLevel()), self::MODIFIER_WEAKNESS);
}
}

View File

@ -23,8 +23,13 @@ declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\block\BlockTypeIds;
use pocketmine\entity\Living;
use pocketmine\inventory\transaction\action\validator\CallbackSlotValidator;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\Armor;
use pocketmine\item\Item;
use pocketmine\item\ItemBlock;
class ArmorInventory extends SimpleInventory{
public const SLOT_HEAD = 0;
@ -36,6 +41,8 @@ class ArmorInventory extends SimpleInventory{
protected Living $holder
){
parent::__construct(4);
$this->validators->add(new CallbackSlotValidator(self::validate(...)));
}
public function getHolder() : Living{
@ -73,4 +80,20 @@ class ArmorInventory extends SimpleInventory{
public function setBoots(Item $boots) : void{
$this->setItem(self::SLOT_FEET, $boots);
}
private static function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{
if($item instanceof Armor){
if($item->getArmorSlot() !== $slot){
return new TransactionValidationException("Armor item is in wrong slot");
}
}else{
if(!($slot === ArmorInventory::SLOT_HEAD && $item instanceof ItemBlock && (
$item->getBlock()->getTypeId() === BlockTypeIds::CARVED_PUMPKIN ||
$item->getBlock()->getTypeId() === BlockTypeIds::MOB_HEAD
))){
return new TransactionValidationException("Item is not accepted in an armor slot");
}
}
return null;
}
}

View File

@ -36,19 +36,27 @@ use function spl_object_id;
/**
* This class provides everything needed to implement an inventory, minus the underlying storage system.
*
* @phpstan-import-type SlotValidators from SlotValidatedInventory
*/
abstract class BaseInventory implements Inventory{
abstract class BaseInventory implements Inventory, SlotValidatedInventory{
protected int $maxStackSize = Inventory::MAX_STACK;
/** @var Player[] */
/**
* @var Player[]
* @phpstan-var array<int, Player>
*/
protected array $viewers = [];
/**
* @var InventoryListener[]|ObjectSet
* @phpstan-var ObjectSet<InventoryListener>
*/
protected ObjectSet $listeners;
/** @phpstan-var SlotValidators */
protected ObjectSet $validators;
public function __construct(){
$this->listeners = new ObjectSet();
$this->validators = new ObjectSet();
}
public function getMaxStackSize() : int{
@ -281,8 +289,6 @@ abstract class BaseInventory implements Inventory{
}
public function removeItem(Item ...$slots) : array{
/** @var Item[] $searchItems */
/** @var Item[] $slots */
$searchItems = [];
foreach($slots as $slot){
if(!$slot->isNull()){
@ -398,4 +404,8 @@ abstract class BaseInventory implements Inventory{
public function getListeners() : ObjectSet{
return $this->listeners;
}
public function getSlotValidators() : ObjectSet{
return $this->validators;
}
}

View File

@ -36,7 +36,10 @@ final class CreativeInventory{
use SingletonTrait;
use DestructorCallbackTrait;
/** @var Item[] */
/**
* @var Item[]
* @phpstan-var array<int, Item>
*/
private array $creative = [];
/** @phpstan-var ObjectSet<\Closure() : void> */
@ -69,6 +72,7 @@ final class CreativeInventory{
/**
* @return Item[]
* @phpstan-return array<int, Item>
*/
public function getAll() : array{
return Utils::cloneObjectArray($this->creative);

View File

@ -0,0 +1,46 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\inventory\transaction\action\validator\SlotValidator;
use pocketmine\utils\ObjectSet;
/**
* A "slot validated inventory" has validators which may restrict items
* from being placed in particular slots of the inventory when transactions are executed.
*
* @phpstan-type SlotValidators ObjectSet<SlotValidator>
*/
interface SlotValidatedInventory{
/**
* Returns a set of validators that will be used to determine whether an item can be placed in a particular slot.
* All validators need to return null for the transaction to be allowed.
* If one of the validators returns an exception, the transaction will be cancelled.
*
* There is no guarantee that the validators will be called in any particular order.
*
* @phpstan-return SlotValidators
*/
public function getSlotValidators() : ObjectSet;
}

View File

@ -57,9 +57,15 @@ class CraftingTransaction extends InventoryTransaction{
protected ?CraftingRecipe $recipe = null;
protected ?int $repetitions = null;
/** @var Item[] */
/**
* @var Item[]
* @phpstan-var list<Item>
*/
protected array $inputs = [];
/** @var Item[] */
/**
* @var Item[]
* @phpstan-var list<Item>
*/
protected array $outputs = [];
private CraftingManager $craftingManager;
@ -74,6 +80,9 @@ class CraftingTransaction extends InventoryTransaction{
/**
* @param Item[] $providedItems
* @return Item[]
*
* @phpstan-param list<Item> $providedItems
* @phpstan-return list<Item>
*/
private static function packItems(array $providedItems) : array{
$packedProvidedItems = [];
@ -94,6 +103,9 @@ class CraftingTransaction extends InventoryTransaction{
/**
* @param Item[] $providedItems
* @param RecipeIngredient[] $recipeIngredients
*
* @phpstan-param list<Item> $providedItems
* @phpstan-param list<RecipeIngredient> $recipeIngredients
*/
public static function matchIngredients(array $providedItems, array $recipeIngredients, int $expectedIterations) : void{
if(count($recipeIngredients) === 0){
@ -172,6 +184,9 @@ class CraftingTransaction extends InventoryTransaction{
* @param Item[] $txItems
* @param Item[] $recipeItems
*
* @phpstan-param list<Item> $txItems
* @phpstan-param list<Item> $recipeItems
*
* @throws TransactionValidationException
*/
protected function matchOutputs(array $txItems, array $recipeItems) : int{

View File

@ -29,6 +29,7 @@ use pocketmine\inventory\transaction\action\InventoryAction;
use pocketmine\inventory\transaction\action\SlotChangeAction;
use pocketmine\item\Item;
use pocketmine\player\Player;
use pocketmine\utils\Utils;
use function array_keys;
use function array_values;
use function assert;
@ -57,10 +58,16 @@ use function spl_object_id;
class InventoryTransaction{
protected bool $hasExecuted = false;
/** @var Inventory[] */
/**
* @var Inventory[]
* @phpstan-var array<int, Inventory>
*/
protected array $inventories = [];
/** @var InventoryAction[] */
/**
* @var InventoryAction[]
* @phpstan-var array<int, InventoryAction>
*/
protected array $actions = [];
/**
@ -81,6 +88,7 @@ class InventoryTransaction{
/**
* @return Inventory[]
* @phpstan-return array<int, Inventory>
*/
public function getInventories() : array{
return $this->inventories;
@ -93,6 +101,7 @@ class InventoryTransaction{
* significance and should not be relied on.
*
* @return InventoryAction[]
* @phpstan-return array<int, InventoryAction>
*/
public function getActions() : array{
return $this->actions;
@ -133,8 +142,8 @@ class InventoryTransaction{
/**
* @param Item[] $needItems
* @param Item[] $haveItems
* @phpstan-param-out Item[] $needItems
* @phpstan-param-out Item[] $haveItems
* @phpstan-param-out list<Item> $needItems
* @phpstan-param-out list<Item> $haveItems
*
* @throws TransactionValidationException
*/
@ -188,11 +197,8 @@ class InventoryTransaction{
* wrong order), so this method also tries to chain them into order.
*/
protected function squashDuplicateSlotChanges() : void{
/** @var SlotChangeAction[][] $slotChanges */
$slotChanges = [];
/** @var Inventory[] $inventories */
$inventories = [];
/** @var int[] $slots */
$slots = [];
foreach($this->actions as $key => $action){
@ -203,7 +209,7 @@ class InventoryTransaction{
}
}
foreach($slotChanges as $hash => $list){
foreach(Utils::stringifyKeys($slotChanges) as $hash => $list){
if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot
continue;
}
@ -233,6 +239,7 @@ class InventoryTransaction{
/**
* @param SlotChangeAction[] $possibleActions
* @phpstan-param list<SlotChangeAction> $possibleActions
*/
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
assert(count($possibleActions) > 0);

Some files were not shown because too many files have changed in this diff Show More