Compare commits

..

75 Commits

Author SHA1 Message Date
95bbca7c33 Remove more unnecessary changes 2025-09-09 22:06:56 +01:00
1bdd1c380d Fix confusing variable names 2025-09-09 22:05:19 +01:00
2d7d2223c0 Undo unnecessary change 2025-09-09 22:05:02 +01:00
fecdbe6bd2 stable protocol package 2025-09-09 21:54:29 +01:00
f913f79da1 Issuer and audience are now required to be set 2025-09-09 21:48:15 +01:00
b867e21be2 Restore self-signed login capability, and refactor a bunch of code 2025-09-09 21:42:20 +01:00
87ffb9ac89 stfu 2025-09-08 21:23:14 +01:00
82dceaaf38 Reworked a bunch of auth code 2025-09-08 21:20:55 +01:00
094196cbf8 Fix PHPStan 2025-09-07 00:23:54 +02:00
c5d017208d Rename variables from manager to provider 2025-09-06 23:46:11 +02:00
b3170913f7 Initial attempt to support new authentication 2025-09-06 23:44:25 +02:00
dc04992ba9 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into stable 2025-09-05 19:25:31 +01:00
bddab47ee8 Bump build/php from ce1b095 to b839e52 (#6795) 2025-09-05 10:32:17 +00:00
3411103e11 Remove dead PHPStan ignores 2025-09-04 23:29:55 +01:00
1868536916 Add PHP 8.4 to test matrix 2025-09-04 21:58:12 +01:00
9a0a8a55b1 Bump shivammathur/setup-php in the github-actions group (#6787) 2025-09-02 13:17:41 +00:00
09cc76ae2b 5.33.2 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/17351431906
2025-08-31 02:28:17 +00:00
a540de1e3c Prepare 5.33.1 patch release (#6784) 2025-08-31 03:27:21 +01:00
9eee1a9a6e Banner: don't bail on missing type tags
we didn't set these prior to 5.33.0, so these won't be present on older worlds.
2025-08-31 03:22:58 +01:00
f673159471 5.33.1 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/17346780638
2025-08-30 17:46:07 +00:00
831c5a0464 Merge pull request #6783 from pmmp/r5.33.0
Release 5.33.0
2025-08-30 18:45:11 +01:00
5c363965f0 Fix build date
we really need a better way to deal with this
2025-08-30 18:43:18 +01:00
95679b5a29 Update BedrockData and some transient deps 2025-08-30 18:36:42 +01:00
f1b1e1977e Harden validation for server auth block breaking 2025-08-29 20:37:29 +01:00
23d612f1af Suggested additions 2025-08-29 18:49:08 +01:00
8f7e16a9ad Prepare 5.33.0 release 2025-08-29 14:11:50 +01:00
beaedc3627 Tidy up in block properties aisle 2025-08-29 13:07:09 +01:00
48ba334218 CS again :< 2025-08-29 12:33:50 +01:00
0be15a7403 Rename MultiFacing -> MultiAnyFacing
to match the trait name
2025-08-29 12:33:04 +01:00
2404d63b1f Ageable: added getMaxAge()
we'll probably need this...
2025-08-29 12:24:24 +01:00
dd9030f1f5 tools/generate-bedrock-data-from-packets: generate less noise for items
if we have only a name (the majority case), we can just return the name directly instead of an object.
this massively reduces the amount of noise in the files as seen in pmmp/BedrockData@f814036229
2025-08-28 21:15:09 +01:00
de234d1f38 Improved placement logic 2025-08-26 00:10:50 +01:00
db54c481aa Fixed hanging signs placement criteria (#6775) 2025-08-25 23:27:17 +01:00
ac2c07c3fe Added a space after hanging sign wood type (#6776) 2025-08-25 17:00:41 +01:00
ec56d65bcc Fix BC break in BaseBanner 2025-08-25 02:17:45 +01:00
c548923116 ... 2025-08-25 02:16:38 +01:00
4a2c7dc684 Apparently hanging signs are self supporting 2025-08-25 02:15:24 +01:00
f04c458e54 Merge branch 'stable' into minor-next 2025-08-25 01:49:19 +01:00
5c0a109f18 Sign: Strip trailing newlines from text blobs
fixes sign editor always putting the cursor on the last line when right-clicking to edit
2025-08-25 01:48:29 +01:00
31f6f5d252 CS again 2025-08-24 20:13:15 +01:00
0e498720bd Regenerate phpstan-bugs baseline 2025-08-24 20:10:34 +01:00
00d6171463 Implement hanging signs 2025-08-24 20:07:59 +01:00
be90c6c399 World: trigger readStateFromWorld on tile blocks immediately on load
this ensures that the state IDs reflect the actual PM block type, which would probably
be important for a bunch of different async things.
2025-08-24 17:01:59 +01:00
17ecf11a1b Remove stupid thing PhpStorm keeps doing 2025-08-24 16:49:49 +01:00
93e33dad8e tidy CS 2025-08-24 16:42:05 +01:00
4cdf064344 VanillaBlockMappings: Use some model mappings
this way there are some minor symmetry benefits, and the only asymmetric parts are the code that selects which model to use.

it also has the added benefit of removing some duplicated code paths (e.g. now it's possible to get rid of readUnitEnum() and such).
2025-08-24 16:37:42 +01:00
5bf0cbec87 ... 2025-08-24 15:39:23 +01:00
ef53676a59 Fix unit tests 2025-08-24 15:38:07 +01:00
8f9478e82f Illager banners finally working
closes #2951
2025-08-24 15:31:10 +01:00
7c521b456e Unify block serializers (#6769)
This has several advantages:

    Easier to implement new blocks (one less file to modify)
    Easier to adjust serialization of existing blocks
    Guaranteed consistency between serializers and deserializers
    Potentially, exposes more metadata for programmatic analysis, instead of having everything baked inside opaque Closures

There are some exceptions which still use the old approach: big dripleaf, cauldrons, mushroom stems, and pitcher crops. These all have multiple PM block types for a single ID, with relatively complex logic to select which to use. These weren't worth the effort to unify due to their small number. I may revisit this in the future, but I already spent a lot of brainpower on it.
2025-08-24 14:12:18 +01:00
47140cb8d7 RedstoneLamp: implement Lightable, shimmed to powered 2025-08-22 18:27:32 +01:00
e824266457 ChiseledBookshelf: add setSlots() 2025-08-22 18:27:06 +01:00
2bb78f2a94 Fixed Furnace not implementing HorizontalFacing
looks like this was missed in #6639
I checked all other uses of HorizontalFacingTrait and FacesOppositePlacingPlayerTrait and this seems to be the only one.
2025-08-20 00:58:57 +01:00
547544b5b4 Merge branch 'stable' of github.com:pmmp/PocketMine-MP into minor-next 2025-08-17 15:25:57 +01:00
eea4f40138 BlockStateToObjectDeserializer: Remove duplicated CHISELED_COPPER registration
allowing overriding of serializers by the same method as first registration was a mistake...
2025-08-17 15:24:40 +01:00
237ac0f802 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/17001748601
2025-08-16 00:03:09 +00:00
431790a319 Additional specialisation for colored blocks
this reduces boilerplate even further
2025-08-15 22:24:27 +01:00
c0fad353a2 missed one
sadly glazed_terracotta had to be special
2025-08-15 22:09:54 +01:00
e89523ce66 First look at flattened ID specialisation for block serializers
in the future we should be able to unify these, similarly to simple mappings.
unifying blocks with states will, however, be considerably more work.

only color benefits from this so far
2025-08-15 22:02:12 +01:00
1e8612cfc8 BlockObjectToStateSerializer: Avoid unnecessary Writer and Closure (#6759)
---------

Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
2025-08-15 20:39:13 +01:00
cb7a562c8b Bump the github-actions group across 1 directory with 2 updates (#6767) 2025-08-15 00:17:24 +00:00
edb8dcbe90 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/16979483636
2025-08-15 00:03:08 +00:00
f633416f05 5.32.2 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/16962847004
2025-08-14 10:38:22 +00:00
442049d564 Prepare 5.32.1 release (#6766) 2025-08-14 11:37:24 +01:00
cce55e8939 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/16923820104
2025-08-13 00:03:15 +00:00
e375437439 ResourcePacksPacketHandler: harden checks for client responses 2025-08-12 20:11:35 +01:00
c417ecd30d NetworkSession: Abort packet processing if handling triggered a disconnection
this shows up when requesting invalid data during resource pack handling, for example
2025-08-12 18:38:24 +01:00
1f87c67e37 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/16867858213
2025-08-11 00:03:28 +00:00
11612ed0e2 Fixed content log warning about recipe with missing ID 2025-08-11 00:49:37 +01:00
959fd7e5e6 Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/16791576698
2025-08-07 00:03:34 +00:00
0b9e680753 Fix GitHub release trigger actions borked
https://github.com/actions/runner/issues/2788
2025-08-06 17:15:21 +01:00
275fdc4280 5.32.1 is next
Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/16781699267
2025-08-06 15:42:50 +00:00
173b685b02 Bedrock 1.21.100 (#6760)
---------

Co-authored-by: Dylan T. <dktapps@pmmp.io>
2025-08-06 16:41:44 +01:00
89d18f929f RakLibServer: fixed deadlock on thread crash
synchronized block -> getCrashInfo -> join -> synchronized on the same
context on the child thread -> deadlock

instead we check for isTerminated and then get the crash info outside
of the synchronized block.
2025-08-06 15:49:52 +01:00
5139800e13 BlockStateUpgrader: All but removed dependency on BlockStateData 2025-08-03 15:47:12 +01:00
287 changed files with 9562 additions and 5263 deletions

View File

@ -4,6 +4,11 @@ on:
release:
types:
- published
workflow_dispatch:
inputs:
release:
description: 'Tag name to build'
required: true
jobs:
build:
@ -28,16 +33,28 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Clone pmmp/PocketMine-Docker repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pmmp/PocketMine-Docker
fetch-depth: 1
- name: Get tag names
- name: Get tag name
id: tag-name
run: |
VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
echo TAG_NAME=$VERSION >> $GITHUB_OUTPUT
if [[ "${{ github.event_name }}" == "release" ]]; then
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
else
echo "Unsupported event type: ${{ github.event_name }}"
exit 1
fi
- name: Parse version
id: version
run: |
VERSION="${{ steps.tag-name.outputs.TAG_NAME }}"
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
@ -71,8 +88,8 @@ jobs:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }}
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}
@ -84,8 +101,8 @@ jobs:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }}
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}

View File

@ -18,7 +18,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP
uses: pmmp/setup-php-action@3.2.0
@ -41,7 +41,7 @@ jobs:
run: composer install --prefer-dist --no-interaction
- name: Clone extension stubs
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pmmp/phpstorm-stubs
path: extension-stubs

View File

@ -4,18 +4,23 @@ on:
release:
types:
- published
workflow_dispatch:
inputs:
release:
description: 'Release to make notification for'
required: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.34.1
uses: shivammathur/setup-php@2.35.4
with:
php-version: 8.3
php-version: 8.2
- name: Restore Composer package cache
uses: actions/cache@v4
@ -30,9 +35,17 @@ jobs:
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
- name: Get actual tag name
- name: Get tag name
id: tag-name
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
else
echo "Unsupported event type: ${{ github.event_name }}"
exit 1
fi
- name: Run webhook post script
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }} ${{ secrets.DISCORD_NEWS_PING_ROLE_ID }}

View File

@ -30,7 +30,7 @@ jobs:
valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Check IS_DEVELOPMENT_BUILD flag
id: validate
@ -46,12 +46,12 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP
uses: shivammathur/setup-php@2.34.1
uses: shivammathur/setup-php@2.35.4
with:
php-version: 8.3
php-version: 8.2
- name: Restore Composer package cache
uses: actions/cache@v4

View File

@ -18,7 +18,7 @@ on:
- "*"
env:
PHP_VERSION: "8.3"
PHP_VERSION: "8.2"
jobs:
skip:
@ -82,12 +82,12 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.34.1
uses: shivammathur/setup-php@2.35.4
with:
php-version: ${{ env.PHP_VERSION }}

View File

@ -27,7 +27,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP
uses: pmmp/setup-php-action@3.2.0
@ -59,7 +59,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP
uses: pmmp/setup-php-action@3.2.0
@ -91,7 +91,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: true
@ -125,7 +125,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP
uses: pmmp/setup-php-action@3.2.0

View File

@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ["8.3"]
php: ["8.1", "8.2", "8.3", "8.4"]
uses: ./.github/workflows/main-php-matrix.yml
with:
@ -25,10 +25,10 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.34.1
uses: shivammathur/setup-php@2.35.4
with:
php-version: 8.3
tools: php-cs-fixer:3.75
@ -45,7 +45,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@2.0.0

View File

@ -4,6 +4,11 @@ on:
release:
types:
- published
workflow_dispatch:
inputs:
release:
description: 'Release to publish info for'
required: true
jobs:
build:
@ -14,14 +19,22 @@ jobs:
- name: Install jq
run: sudo apt update && sudo apt install jq -y
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
repository: ${{ github.repository_owner }}/update.pmmp.io
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
- name: Get actual tag name
- name: Get tag name
id: tag-name
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
else
echo "Unsupported event type: ${{ github.event_name }}"
exit 1
fi
- name: Download new release information
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "tests/plugins/DevTools"]
path = tests/plugins/DevTools
url = https://github.com/pmmp/DevTools.git
[submodule "build/php"]
path = build/php
url = https://github.com/pmmp/php-build-scripts.git

View File

@ -5,7 +5,7 @@ $finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/build')
->in(__DIR__ . '/tests')
->in(__DIR__ . '/tools')
->notPath('plugins/DevTools')
//JsonMapper will break if the FQNs in the doc comments for these are shortened :(
->notPath('crafting/json')
->notPath('inventory/json')

25
changelogs/5.32.md Normal file
View File

@ -0,0 +1,25 @@
# 5.32.0
Released 6th August 2025.
This is a support release for Minecraft: Bedrock Edition 1.21.100.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.21.100.
- Removed support for earlier versions.
## Fixes
- Fixed deadlock on RakLib thread crash (e.g. due to port binding failure).
# 5.32.1
Released 14th August 2025.
## Fixes
- Hardened checks when processing resource pack sending during player logins.
- Fixed content log warning about crafting recipe with missing ID.
- Fixed packets in a batch still being processed after one of them caused the session to be terminated.

135
changelogs/5.33.md Normal file
View File

@ -0,0 +1,135 @@
# 5.33.0
Released 30th August 2025.
This is a minor feature release containing internals improvements, API improvements and new gameplay features.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## Performance
- Worlds now remember when a chunk isn't generated. This reduces world I/O during world generation.
- `BlockObjectToStateSerializer` now creates fewer objects in certain cases.
## Gameplay
- The following blocks have been added and/or are now properly supported:
- Hanging signs
- Illager banners
## Tools
- `generate-bedrock-data-from-packets.php` now represents items as strings directly when only an ID is present. This significantly improves readability in `BedrockData` and reduces file sizes.
## API
### `pocketmine\block`
- Added (and implemented) interfaces for many common block properties, to allow `instanceof` to be used:
- `Ageable`: for blocks with age, such as crops
- `AnyFacing`: for blocks which can face up, down, and horizontal directions (not the same as `HorizontalFacing`!)
- `Colored`: for blocks with 16 `DyeColor` variants
- `CoralMaterial`: for coral blocks, provides access to coral type and dead/alive
- `HorizontalFacing`: for blocks which can **only** face horizontal directions (not the same as `AnyFacing`!)
- `Lightable`: for light-source blocks which can be turned on and off, e.g. redstone lamp
- `MultiAnyFacing`: for blocks which can appear in multiple faces of the same block (including up, down, and horizontal faces), e.g. glow lichen
- `PillarRotation`: for blocks which can be oriented on an axis, e.g. logs
- `PoweredByRedstone`: for blocks which receive power from a redstone component, e.g. redstone lamp
- `SignLikeRotation`: for blocks which can be rotated 16 ways, e.g. signs, banners
- `WoodMaterial`: for blocks made from wood
- These interfaces have been implemented on many blocks. For the sake of brevity, they are not listed here, but you can expect to see them wherever the corresponding traits were used.
- The following classes have been added:
- `BaseOminousBanner`
- `CeilingCenterHangingSign` - both chains connected to the same point on the block above, can face 16 directions
- `CeilingEdgesHangingSign` - each chain connected to separate edges of the block above, can face 4 directions
- `OminousFloorBanner` - floor version of illager banner, can face 16 directions
- `OminousWallBanner` - wall version of illager banner, can face 4 directions
- `WallHangingSign` - hangs from a horizontal beam, can face 4 directions
- The following API methods have been added:
- `public ChiseledBookshelf->setSlots(list<ChiseledBookshelfSlot> $slots) : $this`
- `public static VanillaBlocks` methods:
- `ACACIA_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `ACACIA_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `ACACIA_WALL_HANGING_SIGN() : WallHangingSign`
- `BIRCH_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `BIRCH_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `BIRCH_WALL_HANGING_SIGN() : WallHangingSign`
- `CHERRY_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `CHERRY_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `CHERRY_WALL_HANGING_SIGN() : WallHangingSign`
- `CRIMSON_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `CRIMSON_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `CRIMSON_WALL_HANGING_SIGN() : WallHangingSign`
- `DARK_OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `DARK_OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `DARK_OAK_WALL_HANGING_SIGN() : WallHangingSign`
- `JUNGLE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `JUNGLE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `JUNGLE_WALL_HANGING_SIGN() : WallHangingSign`
- `MANGROVE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `MANGROVE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `MANGROVE_WALL_HANGING_SIGN() : WallHangingSign`
- `OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `OAK_WALL_HANGING_SIGN() : WallHangingSign`
- `OMINOUS_FLOOR_BANNER() : OminousFloorBanner`
- `OMINOUS_WALL_BANNER() : OminousWallBanner`
- `PALE_OAK_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `PALE_OAK_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `PALE_OAK_WALL_HANGING_SIGN() : WallHangingSign`
- `SPRUCE_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `SPRUCE_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `SPRUCE_WALL_HANGING_SIGN() : WallHangingSign`
- `WARPED_CEILING_CENTER_HANGING_SIGN() : CeilingCenterHangingSign`
- `WARPED_CEILING_EDGES_HANGING_SIGN() : CeilingEdgesHangingSign`
- `WARPED_WALL_HANGING_SIGN() : WallHangingSign`
- `public AgeableTrait->getMaxAge() : int` (included by all growable plant-like blocks, e.g. crops)
### `pocketmine\data\bedrock\block\convert`
- A new system for symmetric block serializers and deserializers has been introduced.
- This allows registering both a serializer and a deserializer with the same code, meaning way less code
- It also eliminates information duplication and potential inconsistencies, improving maintainability.
- A proper way to deal with flattened IDs (e.g. color blocks) has been introduced which _doesn't_ require hardcoding a giant mess of IDs
- This symmetric system covers 99% of blocks which have a 1:1 association between PM and vanilla blocks, or 1:N where IDs are flattened
- However, there are still some special cases which require registering separate serializers and deserializers (usually in cases where the PM implementation deviates from Mojang where Mojang's implementation sucks, such as hanging signs or big dripleaf).
- No backwards compatibility breaks are expected as a result of this change. However, it's recommended to migrate old code to this new system for maintainability.
- The following new classes have been added:
- `BlockSerializerDeserializerRegistrar` - handles unified registration of block serializers and deserializers, based on a provided block model
- `FlattenedIdModel` - represents a block with some properties baked into its Minecraft ID, e.g. coral or color blocks
- `Model` - represents a regular block with all properties in its `states` NBT
- `property\BoolFromStringProperty<TBlock>` - property mapping a bool value from a string NBT state
- `property\BoolProperty<TBlock>`
- `property\CommonProperties` - singleton containing commonly-used block property definitions and groups, e.g. facing, stair properties
- `property\EnumFromRawStateMap<TEnum, TRaw>` - maps a raw NBT value to a PHP `enum` and vice versa
- `property\IntFromRawStateMap<TRaw>` - maps a raw NBT value to PM integer constants and vice versa
- `property\IntProperty<TBlock>` - an integer range property with a min, max, and optional offset
- `property\Property<TBlock>` - interface implemented by all property definitions accepted by a `Model` or `FlattenedIdModel`
- `property\StateMap<TValue, TRaw>` - interface implemented by classes accepted by mapping properties, e.g. `BoolFromStringProperty`
- `property\StringProperty<TBlock>` - interface implemented by properties whose raw outputs are strings - these can be used as ID components in `FlattenedIdModel`
- `property\ValueFromIntProperty<TBlock, TValue>` - property mapping a generic PM value from an int NBT state
- `property\ValueFromStringProperty<TBlock, TValue>` - same as above, but for a string NBT state
- `property\ValueSetFromIntProperty<TBlock, TOption>` - a property mapping an `int[]` or `enum[]` from a set of flags in NBT states
- `property\ValueMappings` - singleton containing commonly-needed `StateMap`s
- The following classes have been deprecated:
- `BlockStateDeserializerHelper`
- `BlockStateSerializerHelper`
- The following methods have been deprecated:
- All methods for decoding mapped property types in `BlockStateReader`, e.g. `readFacingDirection()`
- All methods for encoding mapped property types in `BlockStateWriter`, e.g. `writeFacingDirection()`
- All specific blocktype mapping functions in `BlockStateToObjectDeserializer`, e.g. `mapStairs()`
- All specific blocktype mapping functions in `BlockObjectToStateSerializer`, e.g. `mapStairs()`
### `pocketmine\item`
- The following hooks have been added:
- `public Item->getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction` - allows more complex logic for itemblocks to place blocks, without duplicating their placement conditions (used for hanging signs)
### `pocketmine\world`
- `World->setChunk()` now verifies that blockstate IDs in the provided chunk are all registered in `RuntimeBlockStateRegistry`. This should provide earlier detection for custom block registration errors by plugins.
## Internals
- `BlockStateUpgrader` is now almost entirely independent from `BlockStateData`. It's anticipated that the upgrader library will be separable from the core in the future.
- `Block->readStateFromWorld()` is now triggered on chunk load for any position containing a tile. This should allow more effective updating of blocks with properties in their tiles.
# 5.33.1
Released 31st August 2025.
## Fixes
- Fixed banners placed in prior versions getting their tiles deleted (due to missing `Type` tags).

View File

@ -5,7 +5,7 @@
"homepage": "https://pmmp.io",
"license": "LGPL-3.0",
"require": {
"php": "^8.3",
"php": "^8.1",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-crypto": "^0.3.1",
@ -34,9 +34,9 @@
"adhocore/json-comment": "~1.2.0",
"netresearch/jsonmapper": "~v5.0.0",
"pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60",
"pocketmine/bedrock-data": "~5.2.0+bedrock-1.21.93",
"pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "~39.1.0+bedrock-1.21.93",
"pocketmine/bedrock-data": "~6.0.0+bedrock-1.21.100",
"pocketmine/bedrock-item-upgrade-schema": "~1.15.0+bedrock-1.21.100",
"pocketmine/bedrock-protocol": "~41.0.0+bedrock-1.21.100",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
@ -49,13 +49,13 @@
"pocketmine/raklib-ipc": "~1.0.0",
"pocketmine/snooze": "^0.5.0",
"ramsey/uuid": "~4.9.0",
"symfony/filesystem": "~7.3.0"
"symfony/filesystem": "~6.4.0"
},
"require-dev": {
"phpstan/phpstan": "2.1.17",
"phpstan/phpstan-phpunit": "^2.0.0",
"phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^12.2.1"
"phpunit/phpunit": "^10.5.24"
},
"replace": {
"symfony/polyfill-ctype": "*",
@ -77,11 +77,12 @@
},
"config": {
"platform": {
"php": "8.3.0"
"php": "8.1.0"
},
"sort-packages": true
},
"scripts": {
"make-devtools": "@php -dphar.readonly=0 tests/plugins/DevTools/src/ConsoleScript.php --make ./ --relative tests/plugins/DevTools --out plugins/DevTools.phar",
"make-server": [
"@composer install --no-dev --classmap-authoritative --ignore-platform-reqs",
"@php -dphar.readonly=0 build/server-phar.php"

809
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,17 @@ includes:
- tests/phpstan/configs/impossible-generics.neon
- tests/phpstan/configs/php-bugs.neon
- tests/phpstan/configs/phpstan-bugs.neon
- tests/phpstan/configs/property-hook-sadness.neon
- tests/phpstan/configs/reflection-class-sadness.neon
- tests/phpstan/configs/spl-fixed-array-sucks.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
rules:
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
- pocketmine\phpstan\rules\DisallowDynamicNewRule
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
- pocketmine\phpstan\rules\ExplodeLimitRule
- pocketmine\phpstan\rules\UnsafeForeachRule

View File

@ -123,6 +123,13 @@ class MemoryManager{
return $this->globalMemoryLimit;
}
/**
* @deprecated
*/
public function canUseChunkCache() : bool{
return !$this->lowMemory;
}
/**
* Returns the allowed chunk radius based on the current memory usage.
*/
@ -229,4 +236,13 @@ class MemoryManager{
}
}
}
/**
* Static memory dumper accessible from any thread.
* @deprecated
* @see MemoryDump
*/
public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
MemoryDump::dumpMemory($startingObject, $outputFolder, $maxNesting, $maxStringSize, $logger);
}
}

View File

@ -55,7 +55,7 @@ namespace pocketmine {
require_once __DIR__ . '/VersionInfo.php';
const MIN_PHP_VERSION = "8.3.0";
const MIN_PHP_VERSION = "8.1.0";
/**
* @param string $message

View File

@ -50,6 +50,7 @@ use pocketmine\lang\Language;
use pocketmine\lang\LanguageNotFoundException;
use pocketmine\lang\Translatable;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\auth\AuthKeyProvider;
use pocketmine\network\mcpe\compression\CompressBatchPromise;
use pocketmine\network\mcpe\compression\CompressBatchTask;
use pocketmine\network\mcpe\compression\Compressor;
@ -80,7 +81,6 @@ use pocketmine\player\PlayerDataLoadException;
use pocketmine\player\PlayerDataProvider;
use pocketmine\player\PlayerDataSaveException;
use pocketmine\player\PlayerInfo;
use pocketmine\plugin\FolderPluginLoader;
use pocketmine\plugin\PharPluginLoader;
use pocketmine\plugin\PluginEnableOrder;
use pocketmine\plugin\PluginGraylist;
@ -271,6 +271,7 @@ class Server{
private int $maxPlayers;
private bool $onlineMode = true;
private AuthKeyProvider $authKeyProvider;
private Network $network;
private bool $networkCompressionAsync = true;
@ -347,10 +348,6 @@ class Server{
return $this->maxPlayers;
}
public function setMaxPlayers(int $maxPlayers) : void{
$this->maxPlayers = $maxPlayers;
}
/**
* Returns whether the server requires that players be authenticated to Xbox Live. If true, connecting players who
* are not logged into Xbox Live will be disconnected.
@ -987,6 +984,8 @@ class Server{
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
}
$this->authKeyProvider = new AuthKeyProvider(new \PrefixedLogger($this->logger, "Minecraft Auth Key Provider"), $this->asyncPool);
if($this->configGroup->getConfigBool(ServerProperties::HARDCORE, false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
$this->configGroup->setConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_HARD);
}
@ -1034,7 +1033,6 @@ class Server{
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->dataPath, "plugin_data"), $pluginGraylist);
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
$this->pluginManager->registerInterface(new ScriptPluginLoader());
$this->pluginManager->registerInterface(new FolderPluginLoader($this->autoloader));
$providerManager = new WorldProviderManager();
if(
@ -1806,6 +1804,10 @@ class Server{
return $this->forceLanguage;
}
public function getAuthKeyProvider() : AuthKeyProvider{
return $this->authKeyProvider;
}
public function getNetwork() : Network{
return $this->network;
}

View File

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

View File

@ -50,6 +50,10 @@ abstract class BaseBanner extends Transparent implements Colored{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileBanner){
if($tile->getType() === TileBanner::TYPE_OMINOUS){
//illager banner is implemented as a separate block, as it doesn't support base color or custom patterns
return $this->getOminousVersion();
}
$this->color = $tile->getBaseColor();
$this->setPatterns($tile->getPatterns());
}
@ -57,6 +61,13 @@ abstract class BaseBanner extends Transparent implements Colored{
return $this;
}
/**
* TODO: make this abstract in PM6 (BC break)
*/
protected function getOminousVersion() : Block{
return VanillaBlocks::AIR();
}
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);

View File

@ -0,0 +1,90 @@
<?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\tile\Banner as TileBanner;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function assert;
abstract class BaseOminousBanner extends Transparent{
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
assert($tile instanceof TileBanner);
$tile->setBaseColor(DyeColor::WHITE);
$tile->setPatterns([]);
$tile->setType(TileBanner::TYPE_OMINOUS);
}
public function isSolid() : bool{
return false;
}
public function getMaxStackSize() : int{
return 16;
}
public function getFuelTime() : int{
return 300;
}
protected function recalculateCollisionBoxes() : array{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE;
}
private function canBeSupportedBy(Block $block) : bool{
return $block->isSolid();
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
abstract protected function getSupportingFace() : int;
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function asItem() : Item{
return VanillaItems::OMINOUS_BANNER();
}
}

View File

@ -787,8 +787,43 @@ final class BlockTypeIds{
public const RESIN_CLUMP = 10757;
public const CHISELED_RESIN_BRICKS = 10758;
public const RESPAWN_ANCHOR = 10759;
public const OMINOUS_BANNER = 10760;
public const OMINOUS_WALL_BANNER = 10761;
public const ACACIA_CEILING_CENTER_HANGING_SIGN = 10762;
public const ACACIA_CEILING_EDGES_HANGING_SIGN = 10763;
public const ACACIA_WALL_HANGING_SIGN = 10764;
public const BIRCH_CEILING_CENTER_HANGING_SIGN = 10765;
public const BIRCH_CEILING_EDGES_HANGING_SIGN = 10766;
public const BIRCH_WALL_HANGING_SIGN = 10767;
public const CHERRY_CEILING_CENTER_HANGING_SIGN = 10768;
public const CHERRY_CEILING_EDGES_HANGING_SIGN = 10769;
public const CHERRY_WALL_HANGING_SIGN = 10770;
public const CRIMSON_CEILING_CENTER_HANGING_SIGN = 10771;
public const CRIMSON_CEILING_EDGES_HANGING_SIGN = 10772;
public const CRIMSON_WALL_HANGING_SIGN = 10773;
public const DARK_OAK_CEILING_CENTER_HANGING_SIGN = 10774;
public const DARK_OAK_CEILING_EDGES_HANGING_SIGN = 10775;
public const DARK_OAK_WALL_HANGING_SIGN = 10776;
public const JUNGLE_CEILING_CENTER_HANGING_SIGN = 10777;
public const JUNGLE_CEILING_EDGES_HANGING_SIGN = 10778;
public const JUNGLE_WALL_HANGING_SIGN = 10779;
public const MANGROVE_CEILING_CENTER_HANGING_SIGN = 10780;
public const MANGROVE_CEILING_EDGES_HANGING_SIGN = 10781;
public const MANGROVE_WALL_HANGING_SIGN = 10782;
public const OAK_CEILING_CENTER_HANGING_SIGN = 10783;
public const OAK_CEILING_EDGES_HANGING_SIGN = 10784;
public const OAK_WALL_HANGING_SIGN = 10785;
public const PALE_OAK_CEILING_CENTER_HANGING_SIGN = 10786;
public const PALE_OAK_CEILING_EDGES_HANGING_SIGN = 10787;
public const PALE_OAK_WALL_HANGING_SIGN = 10788;
public const SPRUCE_CEILING_CENTER_HANGING_SIGN = 10789;
public const SPRUCE_CEILING_EDGES_HANGING_SIGN = 10790;
public const SPRUCE_WALL_HANGING_SIGN = 10791;
public const WARPED_CEILING_CENTER_HANGING_SIGN = 10792;
public const WARPED_CEILING_EDGES_HANGING_SIGN = 10793;
public const WARPED_WALL_HANGING_SIGN = 10794;
public const FIRST_UNUSED_BLOCK_ID = 10760;
public const FIRST_UNUSED_BLOCK_ID = 10795;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

View File

@ -31,4 +31,5 @@ final class BlockTypeTags{
public const SAND = self::PREFIX . "sand";
public const POTTABLE_PLANTS = self::PREFIX . "pottable";
public const FIRE = self::PREFIX . "fire";
public const HANGING_SIGN = self::PREFIX . "hanging_sign";
}

View File

@ -0,0 +1,61 @@
<?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\SignLikeRotation;
use pocketmine\block\utils\SignLikeRotationTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class CeilingCenterHangingSign extends BaseSign implements SignLikeRotation{
use SignLikeRotationTrait;
use StaticSupportTrait;
protected function getSupportingFace() : int{
return Facing::UP;
}
//TODO: duplicated code :(
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face !== Facing::DOWN){
return false;
}
if($player !== null){
$this->rotation = self::getRotationFromYaw($player->getLocation()->getYaw());
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::UP);
return
$supportBlock->getSupportType(Facing::DOWN)->hasCenterSupport() ||
$supportBlock->hasTypeTag(BlockTypeTags::HANGING_SIGN);
}
}

View File

@ -0,0 +1,68 @@
<?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\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class CeilingEdgesHangingSign extends BaseSign implements HorizontalFacing{
use HorizontalFacingTrait;
protected function getSupportingFace() : int{
return Facing::UP;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face !== Facing::DOWN){
return false;
}
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::UP);
return
$supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL ||
(($supportBlock instanceof WallHangingSign || $supportBlock instanceof CeilingEdgesHangingSign) && Facing::axis($supportBlock->getFacing()) === Facing::axis($this->facing));
}
}

View File

@ -114,6 +114,18 @@ class ChiseledBookshelf extends Opaque implements HorizontalFacing{
return $this->slots;
}
/**
* @param ChiseledBookshelfSlot[] $slots
* @return $this
*/
public function setSlots(array $slots) : self{
$this->slots = [];
foreach($slots as $slot){
$this->setSlot($slot, true);
}
return $this;
}
/**
* Returns the last slot interacted by a player or null if no slot has been interacted with yet.
*/

View File

@ -34,6 +34,10 @@ use pocketmine\world\BlockTransaction;
final class FloorBanner extends BaseBanner implements SignLikeRotation{
use SignLikeRotationTrait;
protected function getOminousVersion() : Block{
return VanillaBlocks::OMINOUS_BANNER()->setRotation($this->rotation);
}
protected function getSupportingFace() : int{
return Facing::DOWN;
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\tile\Furnace as TileFurnace;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\LightableTrait;
use pocketmine\crafting\FurnaceType;
@ -34,7 +35,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function mt_rand;
class Furnace extends Opaque implements Lightable{
class Furnace extends Opaque implements Lightable, HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
use LightableTrait;

View File

@ -24,8 +24,8 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\MultiAnyFacing;
use pocketmine\block\utils\MultiAnySupportTrait;
use pocketmine\block\utils\MultiFacing;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
@ -36,7 +36,7 @@ use pocketmine\world\World;
use function count;
use function shuffle;
class GlowLichen extends Transparent implements MultiFacing{
class GlowLichen extends Transparent implements MultiAnyFacing{
use MultiAnySupportTrait;
public function getLightLevel() : int{

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\SignLikeRotation;
use pocketmine\block\utils\SignLikeRotationTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class OminousFloorBanner extends BaseOminousBanner implements SignLikeRotation{
use SignLikeRotationTrait;
//TODO: duplicated code :(
protected function getSupportingFace() : int{
return Facing::DOWN;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face !== Facing::UP){
return false;
}
if($player !== null){
$this->rotation = self::getRotationFromYaw($player->getLocation()->getYaw());
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}

View File

@ -0,0 +1,49 @@
<?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\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class OminousWallBanner extends BaseOminousBanner implements HorizontalFacing{
use HorizontalFacingTrait;
protected function getSupportingFace() : int{
return Facing::opposite($this->facing);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(Facing::axis($face) === Axis::Y){
return false;
}
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}

View File

@ -23,11 +23,12 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
class RedstoneLamp extends Opaque implements PoweredByRedstone{
class RedstoneLamp extends Opaque implements PoweredByRedstone, Lightable{
use PoweredByRedstoneTrait;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@ -37,4 +38,14 @@ class RedstoneLamp extends Opaque implements PoweredByRedstone{
public function getLightLevel() : int{
return $this->powered ? 15 : 0;
}
public function isLit() : bool{
return $this->powered;
}
/** @return $this */
public function setLit(bool $lit = true) : self{
$this->powered = $lit;
return $this;
}
}

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\MultiAnyFacing;
use pocketmine\block\utils\MultiAnySupportTrait;
use pocketmine\block\utils\MultiFacing;
use pocketmine\block\utils\SupportType;
final class ResinClump extends Transparent implements MultiFacing{
final class ResinClump extends Transparent implements MultiAnyFacing{
use MultiAnySupportTrait;
public function isSolid() : bool{

View File

@ -59,10 +59,17 @@ class SweetBerryBush extends Flowable implements Ageable{
return 0;
}
/**
* @deprecated
*/
protected function canBeSupportedBy(Block $block) : bool{
return $block->getTypeId() !== BlockTypeIds::FARMLAND && //bedrock-specific thing (bug?)
($block->hasTypeTag(BlockTypeTags::DIRT) || $block->hasTypeTag(BlockTypeTags::MUD));
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return $supportBlock->getTypeId() !== BlockTypeIds::FARMLAND && //bedrock-specific thing (bug?)
($supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD));
return $this->canBeSupportedBy($supportBlock);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -45,6 +45,7 @@ use pocketmine\block\tile\EnchantTable as TileEnchantingTable;
use pocketmine\block\tile\EnderChest as TileEnderChest;
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
use pocketmine\block\tile\GlowingItemFrame as TileGlowingItemFrame;
use pocketmine\block\tile\HangingSign as TileHangingSign;
use pocketmine\block\tile\Hopper as TileHopper;
use pocketmine\block\tile\ItemFrame as TileItemFrame;
use pocketmine\block\tile\Jukebox as TileJukebox;
@ -80,6 +81,8 @@ use function strtolower;
* @generate-registry-docblock
*
* @method static WoodenButton ACACIA_BUTTON()
* @method static CeilingCenterHangingSign ACACIA_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign ACACIA_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor ACACIA_DOOR()
* @method static WoodenFence ACACIA_FENCE()
* @method static FenceGate ACACIA_FENCE_GATE()
@ -92,6 +95,7 @@ use function strtolower;
* @method static WoodenSlab ACACIA_SLAB()
* @method static WoodenStairs ACACIA_STAIRS()
* @method static WoodenTrapdoor ACACIA_TRAPDOOR()
* @method static WallHangingSign ACACIA_WALL_HANGING_SIGN()
* @method static WallSign ACACIA_WALL_SIGN()
* @method static Wood ACACIA_WOOD()
* @method static ActivatorRail ACTIVATOR_RAIL()
@ -122,6 +126,8 @@ use function strtolower;
* @method static BigDripleafHead BIG_DRIPLEAF_HEAD()
* @method static BigDripleafStem BIG_DRIPLEAF_STEM()
* @method static WoodenButton BIRCH_BUTTON()
* @method static CeilingCenterHangingSign BIRCH_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign BIRCH_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor BIRCH_DOOR()
* @method static WoodenFence BIRCH_FENCE()
* @method static FenceGate BIRCH_FENCE_GATE()
@ -134,6 +140,7 @@ use function strtolower;
* @method static WoodenSlab BIRCH_SLAB()
* @method static WoodenStairs BIRCH_STAIRS()
* @method static WoodenTrapdoor BIRCH_TRAPDOOR()
* @method static WallHangingSign BIRCH_WALL_HANGING_SIGN()
* @method static WallSign BIRCH_WALL_SIGN()
* @method static Wood BIRCH_WOOD()
* @method static Opaque BLACKSTONE()
@ -170,6 +177,8 @@ use function strtolower;
* @method static Chain CHAIN()
* @method static ChemicalHeat CHEMICAL_HEAT()
* @method static WoodenButton CHERRY_BUTTON()
* @method static CeilingCenterHangingSign CHERRY_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign CHERRY_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor CHERRY_DOOR()
* @method static WoodenFence CHERRY_FENCE()
* @method static FenceGate CHERRY_FENCE_GATE()
@ -181,6 +190,7 @@ use function strtolower;
* @method static WoodenSlab CHERRY_SLAB()
* @method static WoodenStairs CHERRY_STAIRS()
* @method static WoodenTrapdoor CHERRY_TRAPDOOR()
* @method static WallHangingSign CHERRY_WALL_HANGING_SIGN()
* @method static WallSign CHERRY_WALL_SIGN()
* @method static Wood CHERRY_WOOD()
* @method static Chest CHEST()
@ -231,6 +241,8 @@ use function strtolower;
* @method static Opaque CRACKED_STONE_BRICKS()
* @method static CraftingTable CRAFTING_TABLE()
* @method static WoodenButton CRIMSON_BUTTON()
* @method static CeilingCenterHangingSign CRIMSON_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign CRIMSON_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor CRIMSON_DOOR()
* @method static WoodenFence CRIMSON_FENCE()
* @method static FenceGate CRIMSON_FENCE_GATE()
@ -243,6 +255,7 @@ use function strtolower;
* @method static WoodenStairs CRIMSON_STAIRS()
* @method static Wood CRIMSON_STEM()
* @method static WoodenTrapdoor CRIMSON_TRAPDOOR()
* @method static WallHangingSign CRIMSON_WALL_HANGING_SIGN()
* @method static WallSign CRIMSON_WALL_SIGN()
* @method static Opaque CRYING_OBSIDIAN()
* @method static Copper CUT_COPPER()
@ -254,6 +267,8 @@ use function strtolower;
* @method static Slab CUT_SANDSTONE_SLAB()
* @method static Flower DANDELION()
* @method static WoodenButton DARK_OAK_BUTTON()
* @method static CeilingCenterHangingSign DARK_OAK_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign DARK_OAK_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor DARK_OAK_DOOR()
* @method static WoodenFence DARK_OAK_FENCE()
* @method static FenceGate DARK_OAK_FENCE_GATE()
@ -266,6 +281,7 @@ use function strtolower;
* @method static WoodenSlab DARK_OAK_SLAB()
* @method static WoodenStairs DARK_OAK_STAIRS()
* @method static WoodenTrapdoor DARK_OAK_TRAPDOOR()
* @method static WallHangingSign DARK_OAK_WALL_HANGING_SIGN()
* @method static WallSign DARK_OAK_WALL_SIGN()
* @method static Wood DARK_OAK_WOOD()
* @method static Opaque DARK_PRISMARINE()
@ -488,6 +504,8 @@ use function strtolower;
* @method static ItemFrame ITEM_FRAME()
* @method static Jukebox JUKEBOX()
* @method static WoodenButton JUNGLE_BUTTON()
* @method static CeilingCenterHangingSign JUNGLE_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign JUNGLE_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor JUNGLE_DOOR()
* @method static WoodenFence JUNGLE_FENCE()
* @method static FenceGate JUNGLE_FENCE_GATE()
@ -500,6 +518,7 @@ use function strtolower;
* @method static WoodenSlab JUNGLE_SLAB()
* @method static WoodenStairs JUNGLE_STAIRS()
* @method static WoodenTrapdoor JUNGLE_TRAPDOOR()
* @method static WallHangingSign JUNGLE_WALL_HANGING_SIGN()
* @method static WallSign JUNGLE_WALL_SIGN()
* @method static Wood JUNGLE_WOOD()
* @method static ChemistryTable LAB_TABLE()
@ -522,6 +541,8 @@ use function strtolower;
* @method static Loom LOOM()
* @method static Magma MAGMA()
* @method static WoodenButton MANGROVE_BUTTON()
* @method static CeilingCenterHangingSign MANGROVE_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign MANGROVE_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor MANGROVE_DOOR()
* @method static WoodenFence MANGROVE_FENCE()
* @method static FenceGate MANGROVE_FENCE_GATE()
@ -534,6 +555,7 @@ use function strtolower;
* @method static WoodenSlab MANGROVE_SLAB()
* @method static WoodenStairs MANGROVE_STAIRS()
* @method static WoodenTrapdoor MANGROVE_TRAPDOOR()
* @method static WallHangingSign MANGROVE_WALL_HANGING_SIGN()
* @method static WallSign MANGROVE_WALL_SIGN()
* @method static Wood MANGROVE_WOOD()
* @method static ChemistryTable MATERIAL_REDUCER()
@ -572,6 +594,8 @@ use function strtolower;
* @method static Opaque NETHER_WART_BLOCK()
* @method static Note NOTE_BLOCK()
* @method static WoodenButton OAK_BUTTON()
* @method static CeilingCenterHangingSign OAK_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign OAK_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor OAK_DOOR()
* @method static WoodenFence OAK_FENCE()
* @method static FenceGate OAK_FENCE_GATE()
@ -584,14 +608,19 @@ use function strtolower;
* @method static WoodenSlab OAK_SLAB()
* @method static WoodenStairs OAK_STAIRS()
* @method static WoodenTrapdoor OAK_TRAPDOOR()
* @method static WallHangingSign OAK_WALL_HANGING_SIGN()
* @method static WallSign OAK_WALL_SIGN()
* @method static Wood OAK_WOOD()
* @method static Opaque OBSIDIAN()
* @method static OminousFloorBanner OMINOUS_BANNER()
* @method static OminousWallBanner OMINOUS_WALL_BANNER()
* @method static Flower ORANGE_TULIP()
* @method static Flower OXEYE_DAISY()
* @method static PackedIce PACKED_ICE()
* @method static Opaque PACKED_MUD()
* @method static WoodenButton PALE_OAK_BUTTON()
* @method static CeilingCenterHangingSign PALE_OAK_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign PALE_OAK_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor PALE_OAK_DOOR()
* @method static WoodenFence PALE_OAK_FENCE()
* @method static FenceGate PALE_OAK_FENCE_GATE()
@ -603,6 +632,7 @@ use function strtolower;
* @method static WoodenSlab PALE_OAK_SLAB()
* @method static WoodenStairs PALE_OAK_STAIRS()
* @method static WoodenTrapdoor PALE_OAK_TRAPDOOR()
* @method static WallHangingSign PALE_OAK_WALL_HANGING_SIGN()
* @method static WallSign PALE_OAK_WALL_SIGN()
* @method static Wood PALE_OAK_WOOD()
* @method static DoublePlant PEONY()
@ -733,6 +763,8 @@ use function strtolower;
* @method static Sponge SPONGE()
* @method static SporeBlossom SPORE_BLOSSOM()
* @method static WoodenButton SPRUCE_BUTTON()
* @method static CeilingCenterHangingSign SPRUCE_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign SPRUCE_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor SPRUCE_DOOR()
* @method static WoodenFence SPRUCE_FENCE()
* @method static FenceGate SPRUCE_FENCE_GATE()
@ -745,6 +777,7 @@ use function strtolower;
* @method static WoodenSlab SPRUCE_SLAB()
* @method static WoodenStairs SPRUCE_STAIRS()
* @method static WoodenTrapdoor SPRUCE_TRAPDOOR()
* @method static WallHangingSign SPRUCE_WALL_HANGING_SIGN()
* @method static WallSign SPRUCE_WALL_SIGN()
* @method static Wood SPRUCE_WOOD()
* @method static StainedHardenedClay STAINED_CLAY()
@ -788,6 +821,8 @@ use function strtolower;
* @method static WallBanner WALL_BANNER()
* @method static WallCoralFan WALL_CORAL_FAN()
* @method static WoodenButton WARPED_BUTTON()
* @method static CeilingCenterHangingSign WARPED_CEILING_CENTER_HANGING_SIGN()
* @method static CeilingEdgesHangingSign WARPED_CEILING_EDGES_HANGING_SIGN()
* @method static WoodenDoor WARPED_DOOR()
* @method static WoodenFence WARPED_FENCE()
* @method static FenceGate WARPED_FENCE_GATE()
@ -800,13 +835,14 @@ use function strtolower;
* @method static WoodenStairs WARPED_STAIRS()
* @method static Wood WARPED_STEM()
* @method static WoodenTrapdoor WARPED_TRAPDOOR()
* @method static WallHangingSign WARPED_WALL_HANGING_SIGN()
* @method static WallSign WARPED_WALL_SIGN()
* @method static Opaque WARPED_WART_BLOCK()
* @method static Water WATER()
* @method static WaterCauldron WATER_CAULDRON()
* @method static NetherVines WEEPING_VINES()
* @method static WeightedPressurePlate WEIGHTED_PRESSURE_PLATE_HEAVY()
* @method static WeightedPressurePlate WEIGHTED_PRESSURE_PLATE_LIGHT()
* @method static WeightedPressurePlateHeavy WEIGHTED_PRESSURE_PLATE_HEAVY()
* @method static WeightedPressurePlateLight WEIGHTED_PRESSURE_PLATE_LIGHT()
* @method static Wheat WHEAT()
* @method static Flower WHITE_TULIP()
* @method static WitherRose WITHER_ROSE()
@ -873,6 +909,8 @@ final class VanillaBlocks{
$bannerBreakInfo = new Info(BreakInfo::axe(1.0));
self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class);
self::register("wall_banner", fn(BID $id) => new WallBanner($id, "Wall Banner", $bannerBreakInfo), TileBanner::class);
self::register("ominous_banner", fn(BID $id) => new OminousFloorBanner($id, "Ominous Banner", $bannerBreakInfo), TileBanner::class);
self::register("ominous_wall_banner", fn(BID $id) => new OminousWallBanner($id, "Ominous Wall Banner", $bannerBreakInfo), TileBanner::class);
self::register("barrel", fn(BID $id) => new Barrel($id, "Barrel", new Info(BreakInfo::axe(2.5))), TileBarrel::class);
self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible())));
self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class);
@ -1203,14 +1241,14 @@ final class VanillaBlocks{
self::register("lily_pad", fn(BID $id) => new WaterLily($id, "Lily Pad", new Info(BreakInfo::instant())));
$weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5));
self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlate(
self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlateHeavy(
$id,
"Weighted Pressure Plate Heavy",
$weightedPressurePlateBreakInfo,
deactivationDelayTicks: 10,
signalStrengthFactor: 0.1
));
self::register("weighted_pressure_plate_light", fn(BID $id) => new WeightedPressurePlate(
self::register("weighted_pressure_plate_light", fn(BID $id) => new WeightedPressurePlateLight(
$id,
"Weighted Pressure Plate Light",
$weightedPressurePlateBreakInfo,
@ -1353,6 +1391,7 @@ final class VanillaBlocks{
private static function registerWoodenBlocks() : void{
$planksBreakInfo = new Info(BreakInfo::axe(2.0, null, 15.0));
$signBreakInfo = new Info(BreakInfo::axe(1.0));
$hangingSignBreakInfo = new Info(BreakInfo::axe(1.0), [Tags::HANGING_SIGN]);
$logBreakInfo = new Info(BreakInfo::axe(2.0));
$woodenDoorBreakInfo = new Info(BreakInfo::axe(3.0, null, 15.0));
$woodenButtonBreakInfo = new Info(BreakInfo::axe(0.5));
@ -1392,6 +1431,23 @@ final class VanillaBlocks{
};
self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
$hangingSignAsItem = match($woodType){
WoodType::OAK => VanillaItems::OAK_HANGING_SIGN(...),
WoodType::SPRUCE => VanillaItems::SPRUCE_HANGING_SIGN(...),
WoodType::BIRCH => VanillaItems::BIRCH_HANGING_SIGN(...),
WoodType::JUNGLE => VanillaItems::JUNGLE_HANGING_SIGN(...),
WoodType::ACACIA => VanillaItems::ACACIA_HANGING_SIGN(...),
WoodType::DARK_OAK => VanillaItems::DARK_OAK_HANGING_SIGN(...),
WoodType::MANGROVE => VanillaItems::MANGROVE_HANGING_SIGN(...),
WoodType::CRIMSON => VanillaItems::CRIMSON_HANGING_SIGN(...),
WoodType::WARPED => VanillaItems::WARPED_HANGING_SIGN(...),
WoodType::CHERRY => VanillaItems::CHERRY_HANGING_SIGN(...),
WoodType::PALE_OAK => VanillaItems::PALE_OAK_HANGING_SIGN(...),
};
self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . " Center Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class);
self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . " Edges Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class);
self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class);
}
}

View File

@ -35,6 +35,10 @@ use pocketmine\world\BlockTransaction;
final class WallBanner extends BaseBanner implements HorizontalFacing{
use HorizontalFacingTrait;
protected function getOminousVersion() : Block{
return VanillaBlocks::OMINOUS_WALL_BANNER()->setFacing($this->facing);
}
protected function getSupportingFace() : int{
return Facing::opposite($this->facing);
}

View File

@ -0,0 +1,81 @@
<?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\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class WallHangingSign extends BaseSign implements HorizontalFacing{
use HorizontalFacingTrait;
protected function getSupportingFace() : int{
return Facing::rotateY($this->facing, clockwise: true);
}
public function onNearbyBlockChange() : void{
//NOOP - disable default self-destruct behaviour
}
protected function recalculateCollisionBoxes() : array{
//only the cross bar is collidable
return [AxisAlignedBB::one()->trim(Facing::DOWN, 7 / 8)->squash(Facing::axis($this->facing), 3 / 4)];
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($player === null){
return false;
}
$attachFace = Facing::axis($face) === Axis::Y ? Facing::rotateY($player->getHorizontalFacing(), clockwise: true) : $face;
if($this->canBeSupportedAt($blockReplace->getSide($attachFace), $attachFace)){
$direction = $attachFace;
}elseif($this->canBeSupportedAt($blockReplace->getSide($opposite = Facing::opposite($attachFace)), $opposite)){
$direction = $opposite;
}else{
return false;
}
$this->facing = Facing::rotateY(Facing::opposite($direction), clockwise: true);
//the front should always face the player if possible
if($this->facing === $player->getHorizontalFacing()){
$this->facing = Facing::opposite($this->facing);
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
private function canBeSupportedAt(Block $block, int $face) : bool{
return
($block instanceof WallHangingSign && Facing::axis(Facing::rotateY($block->getFacing(), clockwise: true)) === Facing::axis($face)) ||
$block->getSupportType(Facing::opposite($face)) === SupportType::FULL;
}
}

View File

@ -0,0 +1,31 @@
<?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;
/**
* @deprecated
*/
class WeightedPressurePlateHeavy extends WeightedPressurePlate{
}

View File

@ -0,0 +1,31 @@
<?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;
/**
* @deprecated
*/
class WeightedPressurePlateLight extends WeightedPressurePlate{
}

View File

@ -41,6 +41,10 @@ class Banner extends Spawnable{
public const TAG_PATTERNS = "Patterns";
public const TAG_PATTERN_COLOR = "Color";
public const TAG_PATTERN_NAME = "Pattern";
public const TAG_TYPE = "Type";
public const TYPE_NORMAL = 0;
public const TYPE_OMINOUS = 1;
private DyeColor $baseColor = DyeColor::BLACK;
@ -50,6 +54,8 @@ class Banner extends Spawnable{
*/
private array $patterns = [];
private int $type = self::TYPE_NORMAL;
public function readSaveData(CompoundTag $nbt) : void{
$colorIdMap = DyeColorIdMap::getInstance();
if(
@ -75,6 +81,8 @@ class Banner extends Spawnable{
$this->patterns[] = new BannerPatternLayer($patternType, $patternColor);
}
}
$this->type = $nbt->getInt(self::TAG_TYPE, self::TYPE_NORMAL);
}
protected function writeSaveData(CompoundTag $nbt) : void{
@ -89,6 +97,7 @@ class Banner extends Spawnable{
);
}
$nbt->setTag(self::TAG_PATTERNS, $patterns);
$nbt->setInt(self::TAG_TYPE, $this->type);
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
@ -103,6 +112,7 @@ class Banner extends Spawnable{
);
}
$nbt->setTag(self::TAG_PATTERNS, $patterns);
$nbt->setInt(self::TAG_TYPE, $this->type);
}
/**
@ -136,6 +146,10 @@ class Banner extends Spawnable{
$this->patterns = $patterns;
}
public function getType() : int{ return $this->type; }
public function setType(int $type) : void{ $this->type = $type; }
public function getDefaultName() : string{
return "Banner";
}

View File

@ -0,0 +1,31 @@
<?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;
/**
* @deprecated
*/
final class HangingSign extends Sign{
}

View File

@ -37,6 +37,7 @@ use function array_slice;
use function explode;
use function implode;
use function mb_scrub;
use function rtrim;
use function sprintf;
/**
@ -117,7 +118,7 @@ class Sign extends Spawnable{
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create()
->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()))
->setString(self::TAG_TEXT_BLOB, rtrim(implode("\n", $this->text->getLines()), "\n"))
->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB()))
->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0)
->setByte(self::TAG_PERSIST_FORMATTING, 1)
@ -162,7 +163,7 @@ class Sign extends Spawnable{
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create()
->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()))
->setString(self::TAG_TEXT_BLOB, rtrim(implode("\n", $this->text->getLines()), "\n"))
->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB()))
->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0)
->setByte(self::TAG_PERSIST_FORMATTING, 1) //TODO: not sure what this is used for

View File

@ -35,6 +35,20 @@ abstract class Spawnable extends Tile{
/** @phpstan-var CacheableNbt<CompoundTag>|null */
private ?CacheableNbt $spawnCompoundCache = null;
/**
* @deprecated
*/
public function isDirty() : bool{
return $this->spawnCompoundCache === null;
}
/**
* @deprecated
*/
public function setDirty(bool $dirty = true) : void{
$this->clearSpawnCompoundCache();
}
public function clearSpawnCompoundCache() : void{
$this->spawnCompoundCache = null;
}

View File

@ -79,6 +79,7 @@ final class TileFactory{
$this->register(SporeBlossom::class, ["SporeBlossom", "minecraft:spore_blossom"]);
$this->register(MobHead::class, ["Skull", "minecraft:skull"]);
$this->register(GlowingItemFrame::class, ["GlowItemFrame"]);
$this->register(HangingSign::class, ["HangingSign", "minecraft:hanging_sign"]);
//TODO: ChalkboardBlock
//TODO: ChemistryTable

View File

@ -27,7 +27,10 @@ interface Ageable{
public function getAge() : int;
public function getMaxAge() : int;
/**
* Must be in range 0 - getMaxAge()
* @return $this
*/
public function setAge(int $age) : self;

View File

@ -38,6 +38,8 @@ trait AgeableTrait{
public function getAge() : int{ return $this->age; }
public function getMaxAge() : int{ return self::MAX_AGE; }
/**
* @return $this
*/

View File

@ -23,7 +23,54 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static BannerPatternType BORDER()
* @method static BannerPatternType BRICKS()
* @method static BannerPatternType CIRCLE()
* @method static BannerPatternType CREEPER()
* @method static BannerPatternType CROSS()
* @method static BannerPatternType CURLY_BORDER()
* @method static BannerPatternType DIAGONAL_LEFT()
* @method static BannerPatternType DIAGONAL_RIGHT()
* @method static BannerPatternType DIAGONAL_UP_LEFT()
* @method static BannerPatternType DIAGONAL_UP_RIGHT()
* @method static BannerPatternType FLOWER()
* @method static BannerPatternType GRADIENT()
* @method static BannerPatternType GRADIENT_UP()
* @method static BannerPatternType HALF_HORIZONTAL()
* @method static BannerPatternType HALF_HORIZONTAL_BOTTOM()
* @method static BannerPatternType HALF_VERTICAL()
* @method static BannerPatternType HALF_VERTICAL_RIGHT()
* @method static BannerPatternType MOJANG()
* @method static BannerPatternType RHOMBUS()
* @method static BannerPatternType SKULL()
* @method static BannerPatternType SMALL_STRIPES()
* @method static BannerPatternType SQUARE_BOTTOM_LEFT()
* @method static BannerPatternType SQUARE_BOTTOM_RIGHT()
* @method static BannerPatternType SQUARE_TOP_LEFT()
* @method static BannerPatternType SQUARE_TOP_RIGHT()
* @method static BannerPatternType STRAIGHT_CROSS()
* @method static BannerPatternType STRIPE_BOTTOM()
* @method static BannerPatternType STRIPE_CENTER()
* @method static BannerPatternType STRIPE_DOWNLEFT()
* @method static BannerPatternType STRIPE_DOWNRIGHT()
* @method static BannerPatternType STRIPE_LEFT()
* @method static BannerPatternType STRIPE_MIDDLE()
* @method static BannerPatternType STRIPE_RIGHT()
* @method static BannerPatternType STRIPE_TOP()
* @method static BannerPatternType TRIANGLES_BOTTOM()
* @method static BannerPatternType TRIANGLES_TOP()
* @method static BannerPatternType TRIANGLE_BOTTOM()
* @method static BannerPatternType TRIANGLE_TOP()
*/
enum BannerPatternType{
use LegacyEnumShimTrait;
case BORDER;
case BRICKS;
case CIRCLE;

View File

@ -23,7 +23,20 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static BellAttachmentType CEILING()
* @method static BellAttachmentType FLOOR()
* @method static BellAttachmentType ONE_WALL()
* @method static BellAttachmentType TWO_WALLS()
*/
enum BellAttachmentType{
use LegacyEnumShimTrait;
case CEILING;
case FLOOR;
case ONE_WALL;

View File

@ -24,8 +24,19 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\inventory\BrewingStandInventory;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static BrewingStandSlot EAST()
* @method static BrewingStandSlot NORTHWEST()
* @method static BrewingStandSlot SOUTHWEST()
*/
enum BrewingStandSlot{
use LegacyEnumShimTrait;
case EAST;
case NORTHWEST;
case SOUTHWEST;

View File

@ -23,7 +23,20 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static CopperOxidation EXPOSED()
* @method static CopperOxidation NONE()
* @method static CopperOxidation OXIDIZED()
* @method static CopperOxidation WEATHERED()
*/
enum CopperOxidation : int{
use LegacyEnumShimTrait;
case NONE = 0;
case EXPOSED = 1;
case WEATHERED = 2;

View File

@ -23,7 +23,21 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static CoralType BRAIN()
* @method static CoralType BUBBLE()
* @method static CoralType FIRE()
* @method static CoralType HORN()
* @method static CoralType TUBE()
*/
enum CoralType{
use LegacyEnumShimTrait;
case TUBE;
case BRAIN;
case BUBBLE;

View File

@ -23,7 +23,19 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static DirtType COARSE()
* @method static DirtType NORMAL()
* @method static DirtType ROOTED()
*/
enum DirtType{
use LegacyEnumShimTrait;
case NORMAL;
case COARSE;
case ROOTED;

View File

@ -23,7 +23,20 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static DripleafState FULL_TILT()
* @method static DripleafState PARTIAL_TILT()
* @method static DripleafState STABLE()
* @method static DripleafState UNSTABLE()
*/
enum DripleafState{
use LegacyEnumShimTrait;
case STABLE;
case UNSTABLE;
case PARTIAL_TILT;

View File

@ -24,12 +24,35 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\color\Color;
use pocketmine\utils\LegacyEnumShimTrait;
use function spl_object_id;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static DyeColor BLACK()
* @method static DyeColor BLUE()
* @method static DyeColor BROWN()
* @method static DyeColor CYAN()
* @method static DyeColor GRAY()
* @method static DyeColor GREEN()
* @method static DyeColor LIGHT_BLUE()
* @method static DyeColor LIGHT_GRAY()
* @method static DyeColor LIME()
* @method static DyeColor MAGENTA()
* @method static DyeColor ORANGE()
* @method static DyeColor PINK()
* @method static DyeColor PURPLE()
* @method static DyeColor RED()
* @method static DyeColor WHITE()
* @method static DyeColor YELLOW()
*
* @phpstan-type TMetadata array{0: string, 1: Color}
*/
enum DyeColor{
use LegacyEnumShimTrait;
case WHITE;
case ORANGE;
case MAGENTA;

View File

@ -23,7 +23,19 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static FroglightType OCHRE()
* @method static FroglightType PEARLESCENT()
* @method static FroglightType VERDANT()
*/
enum FroglightType{
use LegacyEnumShimTrait;
case OCHRE;
case PEARLESCENT;
case VERDANT;

View File

@ -23,7 +23,26 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static LeavesType ACACIA()
* @method static LeavesType AZALEA()
* @method static LeavesType BIRCH()
* @method static LeavesType CHERRY()
* @method static LeavesType DARK_OAK()
* @method static LeavesType FLOWERING_AZALEA()
* @method static LeavesType JUNGLE()
* @method static LeavesType MANGROVE()
* @method static LeavesType OAK()
* @method static LeavesType SPRUCE()
*/
enum LeavesType{
use LegacyEnumShimTrait;
case OAK;
case SPRUCE;
case BIRCH;

View File

@ -24,8 +24,24 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\math\Facing;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static LeverFacing DOWN_AXIS_X()
* @method static LeverFacing DOWN_AXIS_Z()
* @method static LeverFacing EAST()
* @method static LeverFacing NORTH()
* @method static LeverFacing SOUTH()
* @method static LeverFacing UP_AXIS_X()
* @method static LeverFacing UP_AXIS_Z()
* @method static LeverFacing WEST()
*/
enum LeverFacing{
use LegacyEnumShimTrait;
case UP_AXIS_X;
case UP_AXIS_Z;
case DOWN_AXIS_X;

View File

@ -23,7 +23,23 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static MobHeadType CREEPER()
* @method static MobHeadType DRAGON()
* @method static MobHeadType PIGLIN()
* @method static MobHeadType PLAYER()
* @method static MobHeadType SKELETON()
* @method static MobHeadType WITHER_SKELETON()
* @method static MobHeadType ZOMBIE()
*/
enum MobHeadType{
use LegacyEnumShimTrait;
case SKELETON;
case WITHER_SKELETON;
case ZOMBIE;

View File

@ -25,7 +25,7 @@ namespace pocketmine\block\utils;
use pocketmine\math\Facing;
interface MultiFacing{
interface MultiAnyFacing{
/**
* @return int[]

View File

@ -23,7 +23,27 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static MushroomBlockType ALL_CAP()
* @method static MushroomBlockType CAP_EAST()
* @method static MushroomBlockType CAP_MIDDLE()
* @method static MushroomBlockType CAP_NORTH()
* @method static MushroomBlockType CAP_NORTHEAST()
* @method static MushroomBlockType CAP_NORTHWEST()
* @method static MushroomBlockType CAP_SOUTH()
* @method static MushroomBlockType CAP_SOUTHEAST()
* @method static MushroomBlockType CAP_SOUTHWEST()
* @method static MushroomBlockType CAP_WEST()
* @method static MushroomBlockType PORES()
*/
enum MushroomBlockType{
use LegacyEnumShimTrait;
case PORES;
case CAP_NORTHWEST;
case CAP_NORTH;

View File

@ -26,12 +26,34 @@ namespace pocketmine\block\utils;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Translatable;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
use pocketmine\utils\LegacyEnumShimTrait;
use function spl_object_id;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static RecordType DISK_11()
* @method static RecordType DISK_13()
* @method static RecordType DISK_5()
* @method static RecordType DISK_BLOCKS()
* @method static RecordType DISK_CAT()
* @method static RecordType DISK_CHIRP()
* @method static RecordType DISK_FAR()
* @method static RecordType DISK_MALL()
* @method static RecordType DISK_MELLOHI()
* @method static RecordType DISK_OTHERSIDE()
* @method static RecordType DISK_PIGSTEP()
* @method static RecordType DISK_STAL()
* @method static RecordType DISK_STRAD()
* @method static RecordType DISK_WAIT()
* @method static RecordType DISK_WARD()
*
* @phpstan-type TMetadata array{0: string, 1: LevelSoundEvent::*, 2: Translatable}
*/
enum RecordType{
use LegacyEnumShimTrait;
case DISK_13;
case DISK_5;
case DISK_CAT;

View File

@ -23,9 +23,23 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
use pocketmine\world\generator\object\TreeType;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static SaplingType ACACIA()
* @method static SaplingType BIRCH()
* @method static SaplingType DARK_OAK()
* @method static SaplingType JUNGLE()
* @method static SaplingType OAK()
* @method static SaplingType SPRUCE()
*/
enum SaplingType{
use LegacyEnumShimTrait;
case OAK;
case SPRUCE;
case BIRCH;

View File

@ -23,7 +23,19 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static SlabType BOTTOM()
* @method static SlabType DOUBLE()
* @method static SlabType TOP()
*/
enum SlabType{
use LegacyEnumShimTrait;
case BOTTOM;
case TOP;
case DOUBLE;

View File

@ -23,7 +23,21 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static StairShape INNER_LEFT()
* @method static StairShape INNER_RIGHT()
* @method static StairShape OUTER_LEFT()
* @method static StairShape OUTER_RIGHT()
* @method static StairShape STRAIGHT()
*/
enum StairShape{
use LegacyEnumShimTrait;
case STRAIGHT;
case INNER_LEFT;
case INNER_RIGHT;

View File

@ -23,7 +23,20 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static SupportType CENTER()
* @method static SupportType EDGE()
* @method static SupportType FULL()
* @method static SupportType NONE()
*/
enum SupportType{
use LegacyEnumShimTrait;
case FULL;
case CENTER;
case EDGE;

View File

@ -23,7 +23,18 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static WallConnectionType SHORT()
* @method static WallConnectionType TALL()
*/
enum WallConnectionType{
use LegacyEnumShimTrait;
case SHORT;
case TALL;
}

View File

@ -23,7 +23,26 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\utils\LegacyEnumShimTrait;
/**
* TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
* These are retained for backwards compatibility only.
*
* @method static WoodType ACACIA()
* @method static WoodType BIRCH()
* @method static WoodType CHERRY()
* @method static WoodType CRIMSON()
* @method static WoodType DARK_OAK()
* @method static WoodType JUNGLE()
* @method static WoodType MANGROVE()
* @method static WoodType OAK()
* @method static WoodType SPRUCE()
* @method static WoodType WARPED()
*/
enum WoodType{
use LegacyEnumShimTrait;
case OAK;
case SPRUCE;
case BIRCH;

View File

@ -38,17 +38,19 @@ final class ClosureCommand extends Command{
* @phpstan-param Execute $execute
*/
public function __construct(
string $name,
\Closure $execute,
array $permissions,
Translatable|string $description = "",
Translatable|string|null $usageMessage = null,
array $aliases = []
){
Utils::validateCallableSignature(
fn(CommandSender $sender, Command $command, string $commandLabel, array $args) : mixed => 1,
$execute,
);
$this->execute = $execute;
parent::__construct($description, $usageMessage);
parent::__construct($name, $description, $usageMessage, $aliases);
$this->setPermissions($permissions);
}

View File

@ -33,20 +33,52 @@ use pocketmine\permission\PermissionManager;
use pocketmine\Server;
use pocketmine\utils\BroadcastLoggerForwarder;
use pocketmine\utils\TextFormat;
use function array_values;
use function explode;
use function implode;
use function str_replace;
use const PHP_INT_MAX;
abstract class Command{
private string $name;
private string $nextLabel;
private string $label;
/**
* @var string[]
* @phpstan-var list<string>
*/
private array $aliases = [];
/**
* @var string[]
* @phpstan-var list<string>
*/
private array $activeAliases = [];
private ?CommandMap $commandMap = null;
protected Translatable|string $description = "";
protected Translatable|string $usageMessage;
/** @var string[] */
private array $permission = [];
private Translatable|string|null $permissionMessage = null;
private ?string $permissionMessage = null;
public function __construct(
private Translatable|string $description = "",
private Translatable|string|null $usageMessage = null
){}
/**
* @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;
$this->setLabel($name);
$this->setDescription($description);
$this->usageMessage = $usageMessage ?? ("/" . $name);
$this->setAliases($aliases);
}
/**
* @param string[] $args
@ -57,6 +89,10 @@ abstract class Command{
*/
abstract public function execute(CommandSender $sender, string $commandLabel, array $args);
public function getName() : string{
return $this->name;
}
/**
* @return string[]
*/
@ -81,21 +117,15 @@ abstract class Command{
$this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
}
/**
* @param string $context usually the command name, but may include extra args if useful (e.g. for subcommands)
* @param CommandSender $target the target to check the permission for
* @param string|null $permission the permission to check, if null, will check if the target has any of the command's permissions
*/
public function testPermission(string $context, CommandSender $target, ?string $permission = null) : bool{
public function testPermission(CommandSender $target, ?string $permission = null) : bool{
if($this->testPermissionSilent($target, $permission)){
return true;
}
$message = $this->permissionMessage ?? KnownTranslationFactory::pocketmine_command_error_permission($context);
if($message instanceof Translatable){
$target->sendMessage($message->prefix(TextFormat::RED));
}elseif($message !== ""){
$target->sendMessage(str_replace("<permission>", $permission ?? implode(";", $this->permission), $message));
if($this->permissionMessage === null){
$target->sendMessage(KnownTranslationFactory::pocketmine_command_error_permission($this->name)->prefix(TextFormat::RED));
}elseif($this->permissionMessage !== ""){
$target->sendMessage(str_replace("<permission>", $permission ?? implode(";", $this->permission), $this->permissionMessage));
}
return false;
@ -112,7 +142,63 @@ abstract class Command{
return false;
}
public function getPermissionMessage() : Translatable|string|null{
public function getLabel() : string{
return $this->label;
}
public function setLabel(string $name) : bool{
$this->nextLabel = $name;
if(!$this->isRegistered()){
$this->label = $name;
return true;
}
return false;
}
/**
* Registers the command into a Command map
*/
public function register(CommandMap $commandMap) : bool{
if($this->allowChangesFrom($commandMap)){
$this->commandMap = $commandMap;
return true;
}
return false;
}
public function unregister(CommandMap $commandMap) : bool{
if($this->allowChangesFrom($commandMap)){
$this->commandMap = null;
$this->activeAliases = $this->aliases;
$this->label = $this->nextLabel;
return true;
}
return false;
}
private function allowChangesFrom(CommandMap $commandMap) : bool{
return $this->commandMap === null || $this->commandMap === $commandMap;
}
public function isRegistered() : bool{
return $this->commandMap !== null;
}
/**
* @return string[]
* @phpstan-return list<string>
*/
public function getAliases() : array{
return $this->activeAliases;
}
public function getPermissionMessage() : ?string{
return $this->permissionMessage;
}
@ -120,19 +206,31 @@ abstract class Command{
return $this->description;
}
public function getUsage() : Translatable|string|null{
public function getUsage() : Translatable|string{
return $this->usageMessage;
}
/**
* @param string[] $aliases
* @phpstan-param list<string> $aliases
*/
public function setAliases(array $aliases) : void{
$aliases = array_values($aliases); //because plugins can and will pass crap
$this->aliases = $aliases;
if(!$this->isRegistered()){
$this->activeAliases = $aliases;
}
}
public function setDescription(Translatable|string $description) : void{
$this->description = $description;
}
public function setPermissionMessage(Translatable|string $permissionMessage) : void{
public function setPermissionMessage(string $permissionMessage) : void{
$this->permissionMessage = $permissionMessage;
}
public function setUsage(Translatable|string|null $usage) : void{
public function setUsage(Translatable|string $usage) : void{
$this->usageMessage = $usage;
}
@ -153,4 +251,8 @@ abstract class Command{
}
}
}
public function __toString() : string{
return $this->name;
}
}

View File

@ -24,12 +24,13 @@ declare(strict_types=1);
namespace pocketmine\command;
interface CommandMap{
/**
* @param string[] $otherAliases
*
* @phpstan-param list<string> $otherAliases
* @param Command[] $commands
*/
public function register(string $fallbackPrefix, Command $command, string $preferredAlias, array $otherAliases = []) : CommandMapEntry;
public function registerAll(string $fallbackPrefix, array $commands) : void;
public function register(string $fallbackPrefix, Command $command, ?string $label = null) : bool;
public function dispatch(CommandSender $sender, string $cmdLine) : bool;

View File

@ -50,9 +50,10 @@ class FormattedCommandAlias extends Command{
* @param string[] $formatStrings
*/
public function __construct(
string $alias,
private array $formatStrings
){
parent::__construct(KnownTranslationFactory::pocketmine_command_userDefined_description());
parent::__construct($alias, KnownTranslationFactory::pocketmine_command_userDefined_description());
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -94,14 +95,12 @@ class FormattedCommandAlias extends Command{
throw new AssumptionFailedError("This should have been checked before construction");
}
if(($target = $commandMap->getEntry($commandLabel)) !== null){
//TODO: using labels for command dispatch is problematic - what if the label changes?
//maybe this should use command class instead?
$timings = Timings::getCommandDispatchTimings($target->getPreferredAlias());
if(($target = $commandMap->getCommand($commandLabel)) !== null){
$timings = Timings::getCommandDispatchTimings($target->getLabel());
$timings->startTiming();
try{
$target->command->execute($sender, $commandLabel, $commandArgs);
$target->execute($sender, $commandLabel, $commandArgs);
}catch(InvalidCommandSyntaxException $e){
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
}finally{

View File

@ -24,23 +24,17 @@ declare(strict_types=1);
namespace pocketmine\command;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\Translatable;
use pocketmine\plugin\Plugin;
use pocketmine\plugin\PluginOwned;
final class PluginCommand extends Command implements PluginOwned{
public function __construct(
private string $name,
string $name,
private Plugin $owner,
private CommandExecutor $executor,
Translatable|string $description = "",
Translatable|string|null $usageMessage = null
private CommandExecutor $executor
){
parent::__construct($description, $usageMessage);
}
public function getName() : string{
return $this->name;
parent::__construct($name);
$this->usageMessage = "";
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@ -49,11 +43,13 @@ final class PluginCommand extends Command implements PluginOwned{
return false;
}
if(!$this->executor->onCommand($sender, $this, $commandLabel, $args)){
$success = $this->executor->onCommand($sender, $this, $commandLabel, $args);
if(!$success && $this->usageMessage !== ""){
throw new InvalidCommandSyntaxException();
}
return true;
return $success;
}
public function getOwningPlugin() : Plugin{

View File

@ -61,6 +61,7 @@ use pocketmine\command\defaults\TimeCommand;
use pocketmine\command\defaults\TimingsCommand;
use pocketmine\command\defaults\TitleCommand;
use pocketmine\command\defaults\TransferServerCommand;
use pocketmine\command\defaults\VanillaCommand;
use pocketmine\command\defaults\VersionCommand;
use pocketmine\command\defaults\WhitelistCommand;
use pocketmine\command\defaults\XpCommand;
@ -69,15 +70,12 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use function array_filter;
use function array_shift;
use function array_values;
use function count;
use function implode;
use function spl_object_id;
use function str_contains;
use function strcasecmp;
use function strtolower;
@ -89,138 +87,122 @@ class SimpleCommandMap implements CommandMap{
* @var Command[]
* @phpstan-var array<string, Command>
*/
protected array $aliasToCommandMap = [];
/**
* @var CommandMapEntry[]
* @phpstan-var array<int, CommandMapEntry>
*/
private array $uniqueCommands = [];
protected array $knownCommands = [];
public function __construct(private Server $server){
$this->setDefaultCommands();
}
private function setDefaultCommands() : void{
$pmPrefix = "pocketmine";
$this->register($pmPrefix, new BanCommand(), "ban");
$this->register($pmPrefix, new BanIpCommand(), "ban-ip");
$this->register($pmPrefix, new BanListCommand(), "banlist");
$this->register($pmPrefix, new ClearCommand(), "clear");
$this->register($pmPrefix, new DefaultGamemodeCommand(), "defaultgamemode");
$this->register($pmPrefix, new DeopCommand(), "deop");
$this->register($pmPrefix, new DifficultyCommand(), "difficulty");
$this->register($pmPrefix, new DumpMemoryCommand(), "dumpmemory");
$this->register($pmPrefix, new EffectCommand(), "effect");
$this->register($pmPrefix, new EnchantCommand(), "enchant");
$this->register($pmPrefix, new GamemodeCommand(), "gamemode");
$this->register($pmPrefix, new GarbageCollectorCommand(), "gc");
$this->register($pmPrefix, new GiveCommand(), "give");
$this->register($pmPrefix, new HelpCommand(), "help", ["?"]);
$this->register($pmPrefix, new KickCommand(), "kick");
$this->register($pmPrefix, new KillCommand(), "kill", ["suicide"]);
$this->register($pmPrefix, new ListCommand(), "list");
$this->register($pmPrefix, new MeCommand(), "me");
$this->register($pmPrefix, new OpCommand(), "op");
$this->register($pmPrefix, new PardonCommand(), "pardon", ["unban"]);
$this->register($pmPrefix, new PardonIpCommand(), "pardon-ip", ["unban-ip"]);
$this->register($pmPrefix, new ParticleCommand(), "particle");
$this->register($pmPrefix, new PluginsCommand(), "plugins", ["pl"]);
$this->register($pmPrefix, new SaveCommand(), "save-all");
$this->register($pmPrefix, new SaveOffCommand(), "save-off");
$this->register($pmPrefix, new SaveOnCommand(), "save-on");
$this->register($pmPrefix, new SayCommand(), "say");
$this->register($pmPrefix, new SeedCommand(), "seed");
$this->register($pmPrefix, new SetWorldSpawnCommand(), "setworldspawn");
$this->register($pmPrefix, new SpawnpointCommand(), "spawnpoint");
$this->register($pmPrefix, new StatusCommand(), "status");
$this->register($pmPrefix, new StopCommand(), "stop");
$this->register($pmPrefix, new TeleportCommand(), "tp", ["teleport"]);
$this->register($pmPrefix, new TellCommand(), "tell", ["w", "msg"]);
$this->register($pmPrefix, new TimeCommand(), "time");
$this->register($pmPrefix, new TimingsCommand(), "timings");
$this->register($pmPrefix, new TitleCommand(), "title");
$this->register($pmPrefix, new TransferServerCommand(), "transferserver");
$this->register($pmPrefix, new VersionCommand(), "version", ["ver", "about"]);
$this->register($pmPrefix, new WhitelistCommand(), "whitelist");
$this->register($pmPrefix, new XpCommand(), "xp");
$this->registerAll("pocketmine", [
new BanCommand(),
new BanIpCommand(),
new BanListCommand(),
new ClearCommand(),
new DefaultGamemodeCommand(),
new DeopCommand(),
new DifficultyCommand(),
new DumpMemoryCommand(),
new EffectCommand(),
new EnchantCommand(),
new GamemodeCommand(),
new GarbageCollectorCommand(),
new GiveCommand(),
new HelpCommand(),
new KickCommand(),
new KillCommand(),
new ListCommand(),
new MeCommand(),
new OpCommand(),
new PardonCommand(),
new PardonIpCommand(),
new ParticleCommand(),
new PluginsCommand(),
new SaveCommand(),
new SaveOffCommand(),
new SaveOnCommand(),
new SayCommand(),
new SeedCommand(),
new SetWorldSpawnCommand(),
new SpawnpointCommand(),
new StatusCommand(),
new StopCommand(),
new TeleportCommand(),
new TellCommand(),
new TimeCommand(),
new TimingsCommand(),
new TitleCommand(),
new TransferServerCommand(),
new VersionCommand(),
new WhitelistCommand(),
new XpCommand(),
]);
}
public function register(string $fallbackPrefix, Command $command, string $preferredAlias, array $otherAliases = []) : CommandMapEntry{
public function registerAll(string $fallbackPrefix, array $commands) : void{
foreach($commands as $command){
$this->register($fallbackPrefix, $command);
}
}
public function register(string $fallbackPrefix, Command $command, ?string $label = null) : bool{
if(count($command->getPermissions()) === 0){
throw new \InvalidArgumentException("Commands must have a permission set");
}
if(isset($this->uniqueCommands[spl_object_id($command)])){
throw new \InvalidArgumentException("This Command object has already been registered");
}
$preferredAlias = trim($preferredAlias);
if($label === null){
$label = $command->getLabel();
}
$label = trim($label);
$fallbackPrefix = strtolower(trim($fallbackPrefix));
$registeredAliases = [];
//primary labels take precedence over any existing registrations
$this->mapAlias($preferredAlias, $command, $registeredAliases);
$this->mapAlias($fallbackPrefix . ":" . $preferredAlias, $command, $registeredAliases);
$registered = $this->registerAlias($command, false, $fallbackPrefix, $label);
foreach($otherAliases as $alias){
$this->mapAlias($fallbackPrefix . ":" . $alias, $command, $registeredAliases);
if(!isset($this->aliasToCommandMap[$alias])){
$this->mapAlias($alias, $command, $registeredAliases);
$aliases = $command->getAliases();
foreach($aliases as $index => $alias){
if(!$this->registerAlias($command, true, $fallbackPrefix, $alias)){
unset($aliases[$index]);
}
}
$command->setAliases(array_values($aliases));
$entry = new CommandMapEntry($command, $registeredAliases);
$this->uniqueCommands[spl_object_id($command)] = $entry;
return $entry;
}
/**
* @param string[] &$registeredAliases
* @phpstan-param list<string> &$registeredAliases
* @phpstan-param-out non-empty-list<string> $registeredAliases
*/
private function mapAlias(string $alias, Command $command, array &$registeredAliases) : void{
$this->unregisterAlias($alias);
$this->aliasToCommandMap[$alias] = $command;
$registeredAliases[] = $alias;
}
public function registerAlias(string $existingAlias, string $newAlias) : void{
$existingCommand = $this->aliasToCommandMap[$existingAlias] ?? null;
if($existingCommand === null){
throw new \InvalidArgumentException("No command is currently using the alias \"$existingAlias\", cannot create an alias to it");
if(!$registered){
$command->setLabel($fallbackPrefix . ":" . $label);
}
$registration = $this->uniqueCommands[spl_object_id($existingCommand)];
$newAliases = $registration->aliases;
$this->mapAlias($newAlias, $existingCommand, $newAliases);
$this->uniqueCommands[spl_object_id($existingCommand)] = new CommandMapEntry($existingCommand, $newAliases);
}
public function unregisterAlias(string $alias) : void{
$oldCommand = $this->aliasToCommandMap[$alias] ?? null;
if($oldCommand !== null){
unset($this->aliasToCommandMap[$alias]);
$oldCommandKey = spl_object_id($oldCommand);
$oldCommandEntry = $this->uniqueCommands[$oldCommandKey];
$filteredAliases = array_values(array_filter($oldCommandEntry->aliases, fn(string $oldAlias) => $oldAlias !== $alias));
if(count($filteredAliases) > 0){
$this->uniqueCommands[$oldCommandKey] = new CommandMapEntry($oldCommand, $filteredAliases);
}else{
unset($this->uniqueCommands[$oldCommandKey]);
}
}
$command->register($this);
return $registered;
}
public function unregister(Command $command) : bool{
$entry = $this->uniqueCommands[spl_object_id($command)] ?? null;
if($entry !== null){
unset($this->uniqueCommands[spl_object_id($command)]);
foreach($entry->aliases as $alias){
unset($this->aliasToCommandMap[$alias]);
foreach(Utils::promoteKeys($this->knownCommands) as $lbl => $cmd){
if($cmd === $command){
unset($this->knownCommands[$lbl]);
}
}
$command->unregister($this);
return true;
}
private function registerAlias(Command $command, bool $isAlias, string $fallbackPrefix, string $label) : bool{
$this->knownCommands[$fallbackPrefix . ":" . $label] = $command;
if(($command instanceof VanillaCommand || $isAlias) && isset($this->knownCommands[$label])){
return false;
}
if(isset($this->knownCommands[$label]) && $this->knownCommands[$label]->getLabel() === $label){
return false;
}
if(!$isAlias){
$command->setLabel($label);
}
$this->knownCommands[$label] = $command;
return true;
}
@ -228,15 +210,13 @@ class SimpleCommandMap implements CommandMap{
$args = CommandStringHelper::parseQuoteAware($commandLine);
$sentCommandLabel = array_shift($args);
if($sentCommandLabel !== null && ($target = $this->getEntry($sentCommandLabel)) !== null){
//TODO: using labels for command dispatch is problematic - what if the label changes?
//maybe this should use command class instead?
$timings = Timings::getCommandDispatchTimings($target->getPreferredAlias());
if($sentCommandLabel !== null && ($target = $this->getCommand($sentCommandLabel)) !== null){
$timings = Timings::getCommandDispatchTimings($target->getLabel());
$timings->startTiming();
try{
if($target->command->testPermission($sentCommandLabel, $sender)){
$target->command->execute($sender, $sentCommandLabel, $args);
if($target->testPermission($sender)){
$target->execute($sender, $sentCommandLabel, $args);
}
}catch(InvalidCommandSyntaxException $e){
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
@ -251,36 +231,23 @@ class SimpleCommandMap implements CommandMap{
}
public function clearCommands() : void{
$this->aliasToCommandMap = [];
$this->uniqueCommands = [];
foreach($this->knownCommands as $command){
$command->unregister($this);
}
$this->knownCommands = [];
$this->setDefaultCommands();
}
public function getCommand(string $name) : ?Command{
return $this->aliasToCommandMap[$name] ?? null;
return $this->knownCommands[$name] ?? null;
}
/**
* @return Command[]
* @phpstan-return array<string, Command>
*/
public function getAliasToCommandMap() : array{
return $this->aliasToCommandMap;
}
/**
* @return CommandMapEntry[]
* @phpstan-return array<int, CommandMapEntry>
*/
public function getUniqueCommands() : array{
return $this->uniqueCommands;
}
public function getEntry(string $name) : ?CommandMapEntry{
$command = $this->getCommand($name);
return $command !== null ?
$this->uniqueCommands[spl_object_id($command)] ?? throw new AssumptionFailedError("This should never be unset") :
null;
public function getCommands() : array{
return $this->knownCommands;
}
public function registerServerAliases() : void{
@ -322,13 +289,12 @@ class SimpleCommandMap implements CommandMap{
//These registered commands have absolute priority
$lowerAlias = strtolower($alias);
$this->unregisterAlias($lowerAlias);
if(count($targets) > 0){
$aliasInstance = new FormattedCommandAlias($targets);
$registeredAliases = [];
$this->mapAlias($lowerAlias, $aliasInstance, $registeredAliases);
$this->uniqueCommands[spl_object_id($aliasInstance)] = new CommandMapEntry($aliasInstance, $registeredAliases);
$this->knownCommands[$lowerAlias] = new FormattedCommandAlias($lowerAlias, $targets);
}else{
unset($this->knownCommands[$lowerAlias]);
}
}
}
}

View File

@ -37,6 +37,7 @@ class BanCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"ban",
KnownTranslationFactory::pocketmine_command_ban_player_description(),
KnownTranslationFactory::commands_ban_usage()
);

View File

@ -38,6 +38,7 @@ class BanIpCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"ban-ip",
KnownTranslationFactory::pocketmine_command_ban_ip_description(),
KnownTranslationFactory::commands_banip_usage()
);

View File

@ -39,6 +39,7 @@ class BanListCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"banlist",
KnownTranslationFactory::pocketmine_command_banlist_description(),
KnownTranslationFactory::commands_banlist_usage()
);

View File

@ -41,6 +41,7 @@ class ClearCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"clear",
KnownTranslationFactory::pocketmine_command_clear_description(),
KnownTranslationFactory::pocketmine_command_clear_usage()
);
@ -52,7 +53,7 @@ class ClearCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
if($target === null){
return true;
}

View File

@ -35,6 +35,7 @@ class DefaultGamemodeCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"defaultgamemode",
KnownTranslationFactory::pocketmine_command_defaultgamemode_description(),
KnownTranslationFactory::commands_defaultgamemode_usage()
);

View File

@ -37,6 +37,7 @@ class DeopCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"deop",
KnownTranslationFactory::pocketmine_command_deop_description(),
KnownTranslationFactory::commands_deop_usage()
);

View File

@ -36,6 +36,7 @@ class DifficultyCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"difficulty",
KnownTranslationFactory::pocketmine_command_difficulty_description(),
KnownTranslationFactory::commands_difficulty_usage()
);

View File

@ -33,6 +33,7 @@ class DumpMemoryCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"dumpmemory",
KnownTranslationFactory::pocketmine_command_dumpmemory_description(),
"/dumpmemory [path]"
);

View File

@ -38,6 +38,7 @@ class EffectCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"effect",
KnownTranslationFactory::pocketmine_command_effect_description(),
KnownTranslationFactory::commands_effect_usage()
);
@ -52,7 +53,7 @@ class EffectCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
if($player === null){
return true;
}

View File

@ -36,6 +36,7 @@ class EnchantCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"enchant",
KnownTranslationFactory::pocketmine_command_enchant_description(),
KnownTranslationFactory::commands_enchant_usage()
);
@ -50,7 +51,7 @@ class EnchantCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
if($player === null){
return true;
}

View File

@ -35,6 +35,7 @@ class GamemodeCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"gamemode",
KnownTranslationFactory::pocketmine_command_gamemode_description(),
KnownTranslationFactory::commands_gamemode_usage()
);
@ -55,7 +56,7 @@ class GamemodeCommand extends VanillaCommand{
return true;
}
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
$target = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
if($target === null){
return true;
}

View File

@ -36,6 +36,7 @@ class GarbageCollectorCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"gc",
KnownTranslationFactory::pocketmine_command_gc_description()
);
$this->setPermission(DefaultPermissionNames::COMMAND_GC);

View File

@ -43,6 +43,7 @@ class GiveCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"give",
KnownTranslationFactory::pocketmine_command_give_description(),
KnownTranslationFactory::pocketmine_command_give_usage()
);
@ -57,7 +58,7 @@ class GiveCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
if($player === null){
return true;
}

View File

@ -47,8 +47,10 @@ class HelpCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"help",
KnownTranslationFactory::pocketmine_command_help_description(),
KnownTranslationFactory::commands_help_usage()
KnownTranslationFactory::commands_help_usage(),
["?"]
);
$this->setPermission(DefaultPermissionNames::COMMAND_HELP);
}
@ -70,13 +72,11 @@ class HelpCommand extends VanillaCommand{
$pageHeight = $sender->getScreenLineHeight();
//TODO: maybe inject this in the constructor instead of assuming the server's command map?
$commandMap = $sender->getServer()->getCommandMap();
if($commandName === ""){
$commands = [];
foreach($commandMap->getUniqueCommands() as $commandEntry){
if($commandEntry->command->testPermissionSilent($sender)){
$commands[$commandEntry->getPreferredAlias()] = $commandEntry;
foreach($sender->getServer()->getCommandMap()->getCommands() as $command){
if($command->testPermissionSilent($sender)){
$commands[$command->getLabel()] = $command;
}
}
ksort($commands, SORT_NATURAL | SORT_FLAG_CASE);
@ -88,31 +88,31 @@ class HelpCommand extends VanillaCommand{
$sender->sendMessage(KnownTranslationFactory::commands_help_header((string) $pageNumber, (string) count($commands)));
$lang = $sender->getLanguage();
if(isset($commands[$pageNumber - 1])){
foreach($commands[$pageNumber - 1] as $commandEntry){
$description = $commandEntry->command->getDescription();
foreach($commands[$pageNumber - 1] as $command){
$description = $command->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
$sender->sendMessage(TextFormat::DARK_GREEN . "/" . $commandEntry->getPreferredAlias() . ": " . TextFormat::RESET . $descriptionString);
$sender->sendMessage(TextFormat::DARK_GREEN . "/" . $command->getLabel() . ": " . TextFormat::RESET . $descriptionString);
}
}
return true;
}else{
if(($commandEntry = $commandMap->getEntry(strtolower($commandName))) !== null){
if($commandEntry->command->testPermissionSilent($sender)){
if(($cmd = $sender->getServer()->getCommandMap()->getCommand(strtolower($commandName))) instanceof Command){
if($cmd->testPermissionSilent($sender)){
$lang = $sender->getLanguage();
$description = $commandEntry->command->getDescription();
$description = $cmd->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($commandEntry->getPreferredAlias())
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($cmd->getLabel())
->format(TextFormat::YELLOW . "--------- " . TextFormat::RESET, TextFormat::YELLOW . " ---------"));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::RESET . $descriptionString)
->prefix(TextFormat::GOLD));
$usage = $commandEntry->getUsage();
$usage = $cmd->getUsage();
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
->prefix(TextFormat::GOLD));
$aliases = $commandEntry->aliases;
$aliases = $cmd->getAliases();
sort($aliases, SORT_NATURAL);
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_aliases(TextFormat::RESET . implode(", ", $aliases))
->prefix(TextFormat::GOLD));

View File

@ -39,6 +39,7 @@ class KickCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"kick",
KnownTranslationFactory::pocketmine_command_kick_description(),
KnownTranslationFactory::commands_kick_usage()
);

View File

@ -35,8 +35,10 @@ class KillCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"kill",
KnownTranslationFactory::pocketmine_command_kill_description(),
KnownTranslationFactory::pocketmine_command_kill_usage()
KnownTranslationFactory::pocketmine_command_kill_usage(),
["suicide"]
);
$this->setPermissions([DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER]);
}
@ -46,7 +48,7 @@ class KillCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
$player = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
if($player === null){
return true;
}

View File

@ -38,6 +38,7 @@ class ListCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"list",
KnownTranslationFactory::pocketmine_command_list_description()
);
$this->setPermission(DefaultPermissionNames::COMMAND_LIST);

View File

@ -36,6 +36,7 @@ class MeCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"me",
KnownTranslationFactory::pocketmine_command_me_description(),
KnownTranslationFactory::commands_me_usage()
);

View File

@ -37,6 +37,7 @@ class OpCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"op",
KnownTranslationFactory::pocketmine_command_op_description(),
KnownTranslationFactory::commands_op_usage()
);

View File

@ -34,8 +34,10 @@ class PardonCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"pardon",
KnownTranslationFactory::pocketmine_command_unban_player_description(),
KnownTranslationFactory::commands_unban_usage()
KnownTranslationFactory::commands_unban_usage(),
["unban"]
);
$this->setPermission(DefaultPermissionNames::COMMAND_UNBAN_PLAYER);
}

View File

@ -35,8 +35,10 @@ class PardonIpCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"pardon-ip",
KnownTranslationFactory::pocketmine_command_unban_ip_description(),
KnownTranslationFactory::commands_unbanip_usage()
KnownTranslationFactory::commands_unbanip_usage(),
["unban-ip"]
);
$this->setPermission(DefaultPermissionNames::COMMAND_UNBAN_IP);
}

View File

@ -76,6 +76,7 @@ class ParticleCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"particle",
KnownTranslationFactory::pocketmine_command_particle_description(),
KnownTranslationFactory::pocketmine_command_particle_usage()
);

View File

@ -38,8 +38,10 @@ class PluginsCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"plugins",
KnownTranslationFactory::pocketmine_command_plugins_description(),
null
null,
["pl"]
);
$this->setPermission(DefaultPermissionNames::COMMAND_PLUGINS);
}

View File

@ -34,6 +34,7 @@ class SaveCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"save-all",
KnownTranslationFactory::pocketmine_command_save_description()
);
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_PERFORM);

View File

@ -32,6 +32,7 @@ class SaveOffCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"save-off",
KnownTranslationFactory::pocketmine_command_saveoff_description()
);
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_DISABLE);

View File

@ -32,6 +32,7 @@ class SaveOnCommand extends VanillaCommand{
public function __construct(){
parent::__construct(
"save-on",
KnownTranslationFactory::pocketmine_command_saveon_description()
);
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_ENABLE);

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