mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-10 11:38:08 +00:00
Compare commits
227 Commits
5.0.0-BETA
...
5.3.2
Author | SHA1 | Date | |
---|---|---|---|
69abd5eb53 | |||
f6ee7ddc9e | |||
cff4a8d2bc | |||
20b7e8d702 | |||
c6110be051 | |||
c053742f5d | |||
0051b34797 | |||
30db658d70 | |||
0c1bfb058a | |||
45d1ce9bb8 | |||
f7c08dedee | |||
250d18e41b | |||
86bd6777a3 | |||
935df62006 | |||
489a7ba365 | |||
2709dd359c | |||
4e646d19a4 | |||
2a11762e61 | |||
d2f4ba74c6 | |||
d4716ef457 | |||
d630b3af7b | |||
c2bb51cb37 | |||
7e0b5cf73d | |||
e903da8998 | |||
b7210755a7 | |||
f2193d1ba7 | |||
4daacb2ab7 | |||
f7977c9668 | |||
93d3f439bf | |||
200e5f940c | |||
9365ffa7fa | |||
cfd9950b02 | |||
8ebcdb452d | |||
ef85fbffe1 | |||
aacc00a911 | |||
0c250a2ef0 | |||
8f217ca6e0 | |||
f0d5647aa2 | |||
e6de9a70a2 | |||
a34514c6a1 | |||
6a80b210d4 | |||
02ffb04b92 | |||
6cbb03bf9b | |||
3abd592b1f | |||
644b417d2c | |||
588a754f1c | |||
70dd9c7371 | |||
f8e6f036af | |||
a2a7006878 | |||
b78c18ad2d | |||
41281db6a5 | |||
2278275505 | |||
53a6d8451b | |||
bbabccfc89 | |||
1698eac6dc | |||
83378d9403 | |||
321972b87b | |||
24b74a96eb | |||
e61796b146 | |||
c86c9b3ead | |||
249ef9c534 | |||
17842703a1 | |||
f4dab17a1b | |||
eed423505e | |||
c165670e0a | |||
882d50b14e | |||
0b0b72f596 | |||
2654fb294b | |||
2b40c1a5be | |||
74d219dcb6 | |||
470a3e1a3a | |||
ad67fb7291 | |||
3eed0a4620 | |||
b8788c55c5 | |||
c06763c59b | |||
881451c40c | |||
36f52f1ade | |||
0240d35c05 | |||
8dedbb7471 | |||
6f82942c64 | |||
9d0d60afd1 | |||
391732f00c | |||
ad3f854701 | |||
64e09525f3 | |||
774f92435a | |||
be8cca1d55 | |||
eb9f804781 | |||
bccda4fe44 | |||
8f48f8a596 | |||
288ebfa08a | |||
a3046eb6fa | |||
ff0199cdf8 | |||
39a6a9ee70 | |||
0939301938 | |||
202be92c06 | |||
df4a8d4788 | |||
1d25e15ec8 | |||
1b35c352cc | |||
1533dc4e56 | |||
3800c0480f | |||
0f8e61eda4 | |||
0eb5f9b684 | |||
0f9283fda1 | |||
ab8386ed5a | |||
64c1776910 | |||
e85605af7f | |||
92bd88c77c | |||
7cd317bf39 | |||
35fc9abacf | |||
dfd70615ad | |||
ee903cad1f | |||
9a04481bec | |||
833f9401f9 | |||
a46dfaf677 | |||
4a3b175468 | |||
73ee94b62c | |||
ab83210aa0 | |||
3f2d51c58a | |||
13dee2a615 | |||
5d514a274f | |||
2220dc557e | |||
b5fc31a781 | |||
9a67e3d660 | |||
132330e16f | |||
179eec9754 | |||
441f1f534f | |||
e747478afd | |||
92c45dd7e1 | |||
2538880408 | |||
4af981d726 | |||
ab3ebc1aa8 | |||
57cbc25080 | |||
b9bdfe580b | |||
6d7f44d8fe | |||
f9818efff4 | |||
7fef8f0ab6 | |||
2f43ccea6f | |||
af1f7e098b | |||
1706fb43eb | |||
8d7f8ff3f5 | |||
c715efb18e | |||
40be564689 | |||
4e031e7b3e | |||
4340c26029 | |||
9c6d4093ae | |||
007ef833d4 | |||
6678360c00 | |||
102fd4d63a | |||
2c0715ac6e | |||
f75bb061ce | |||
73e7201acb | |||
b16cc69974 | |||
5de4215169 | |||
e128a4e1e7 | |||
c95c600100 | |||
3db45b6a68 | |||
3e87ad281f | |||
b72da777eb | |||
de49910e84 | |||
d2fe537159 | |||
bb31df051d | |||
1101f35c17 | |||
07225df936 | |||
3948dc4f75 | |||
4f4dca7fc0 | |||
0ed5e94a72 | |||
3a4e958e84 | |||
20942b37eb | |||
d343db8750 | |||
f2df702c67 | |||
481270e6aa | |||
e7bdaa8579 | |||
76749cbaa7 | |||
a897bdfaa0 | |||
09668a37d6 | |||
ea92a23d0d | |||
691e67018d | |||
fe2140a716 | |||
8744032ab6 | |||
5a9cdef40c | |||
a49842278a | |||
d57954dff0 | |||
ce5e663a73 | |||
c10be0f346 | |||
f5a1a0c9cb | |||
7f1550ef04 | |||
9baf59702b | |||
473c062b40 | |||
b8ba2d03ba | |||
06b0fa4d67 | |||
fddab29e87 | |||
bdb0ed0701 | |||
edafe9d21f | |||
cc77f18ff0 | |||
24e897f813 | |||
ffe3556be1 | |||
57330a7186 | |||
2ebf8757c1 | |||
1c69116717 | |||
9d9c628acd | |||
cbda24d77e | |||
ed64eac76f | |||
a7aebed0a0 | |||
c66a3a8b3e | |||
4aba9d9725 | |||
d2c34615f5 | |||
815b4e2bab | |||
69273f3ff7 | |||
9ddac21de0 | |||
fdb724c646 | |||
3109a179db | |||
5afeeb8f89 | |||
1cb7846f7c | |||
097feba4d5 | |||
9509d7e04d | |||
3116fb8187 | |||
e0630fbb25 | |||
8245cfab0e | |||
8454076235 | |||
4a3843a881 | |||
bd6af68f91 | |||
b27d3a5fce | |||
c91aa24daa | |||
b8a1b32461 | |||
9c82507168 | |||
db95bf8b9b | |||
edcf0f8405 |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
.github/readme/pocketmine-dark-rgb.gif
vendored
Normal file
BIN
.github/readme/pocketmine-dark-rgb.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 KiB |
BIN
.github/readme/pocketmine-rgb.gif
vendored
Normal file
BIN
.github/readme/pocketmine-rgb.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 KiB |
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -53,7 +53,7 @@ jobs:
|
||||
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
uses: docker/build-push-action@v4.1.1
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
uses: docker/build-push-action@v4.1.1
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
uses: docker/build-push-action@v4.1.1
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
uses: docker/build-push-action@v4.1.1
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
2
.github/workflows/discord-release-notify.yml
vendored
2
.github/workflows/discord-release-notify.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.24.0
|
||||
uses: shivammathur/setup-php@2.25.2
|
||||
with:
|
||||
php-version: 8.1
|
||||
|
||||
|
2
.github/workflows/draft-release.yml
vendored
2
.github/workflows/draft-release.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.24.0
|
||||
uses: shivammathur/setup-php@2.25.2
|
||||
with:
|
||||
php-version: 8.1
|
||||
|
||||
|
61
.github/workflows/main.yml
vendored
61
.github/workflows/main.yml
vendored
@ -6,47 +6,26 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-php:
|
||||
name: Prepare PHP
|
||||
runs-on: ${{ matrix.image }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.1.19, 8.2.6]
|
||||
|
||||
steps:
|
||||
- name: Build and prepare PHP cache
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
phpstan:
|
||||
name: PHPStan analysis
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.1.19, 8.2.6]
|
||||
php: ["8.1", "8.2"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@ -58,34 +37,30 @@ jobs:
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --prefer-dist --no-interaction
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
|
||||
|
||||
phpunit:
|
||||
name: PHPUnit tests
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.1.19, 8.2.6]
|
||||
php: ["8.1", "8.2"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@ -97,20 +72,19 @@ jobs:
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --prefer-dist --no-interaction
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPUnit tests
|
||||
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
|
||||
|
||||
integration:
|
||||
name: Integration tests
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.1.19, 8.2.6]
|
||||
php: ["8.1", "8.2"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -118,15 +92,12 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@ -138,34 +109,30 @@ jobs:
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --no-dev --prefer-dist --no-interaction
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Run integration tests
|
||||
run: ./tests/travis.sh -t4
|
||||
|
||||
codegen:
|
||||
name: Generated Code consistency checks
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [8.1.19, 8.2.6]
|
||||
php: ["8.1", "8.2"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@c7fb29d83535320922068087c7285bdedbbfa3c2
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@ -177,7 +144,7 @@ jobs:
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --no-dev --prefer-dist --no-interaction
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Regenerate registry annotations
|
||||
run: php build/generate-registry-annotations.php src
|
||||
@ -206,10 +173,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.24.0
|
||||
uses: shivammathur/setup-php@2.25.2
|
||||
with:
|
||||
php-version: 8.1
|
||||
tools: php-cs-fixer:3.16
|
||||
tools: php-cs-fixer:3.17
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
50
.github/workflows/update-php-versions.php
vendored
50
.github/workflows/update-php-versions.php
vendored
@ -1,50 +0,0 @@
|
||||
<?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);
|
||||
|
||||
const VERSIONS = [
|
||||
"8.1",
|
||||
"8.2"
|
||||
];
|
||||
|
||||
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
||||
$newWorkflowFile = $workflowFile;
|
||||
foreach(VERSIONS as $v){
|
||||
$releaseInfo = file_get_contents("https://secure.php.net/releases?json&version=$v");
|
||||
if($releaseInfo === false){
|
||||
throw new \RuntimeException("Failed to contact php.net API");
|
||||
}
|
||||
$data = json_decode($releaseInfo, true);
|
||||
if(!is_array($data) || !isset($data["version"]) || !is_string($data["version"]) || preg_match('/^\d+\.\d+\.\d+(-[A-Za-z\d]+)?$/', $data["version"]) === 0){
|
||||
throw new \RuntimeException("Invalid data returned by API");
|
||||
}
|
||||
$updated = preg_replace("/$v\.\d+/", $data["version"], $newWorkflowFile);
|
||||
if($updated !== $newWorkflowFile){
|
||||
echo "Updated $v revision to " . $data["version"] . "\n";
|
||||
}
|
||||
$newWorkflowFile = $updated;
|
||||
}
|
||||
|
||||
if($workflowFile !== $newWorkflowFile){
|
||||
echo "Writing modified workflow file\n";
|
||||
file_put_contents(__DIR__ . '/main.yml', $newWorkflowFile);
|
||||
}
|
1
.github/workflows/update-updater-api.yml
vendored
1
.github/workflows/update-updater-api.yml
vendored
@ -8,6 +8,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
concurrency: update-updater-api # only one job can run at a time, to avoid git conflicts when updating the repository
|
||||
|
||||
steps:
|
||||
- name: Install jq
|
||||
|
7
.idea/codeStyles/Project.xml
generated
7
.idea/codeStyles/Project.xml
generated
@ -30,6 +30,13 @@
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Markdown">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="CLASS_BRACE_STYLE" value="1" />
|
||||
<option name="METHOD_BRACE_STYLE" value="1" />
|
||||
|
@ -28,10 +28,8 @@ Take a look at the table below if you can't find the class or function you're lo
|
||||
|:----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [pmmp/BedrockProtocol](https://github.com/pmmp/BedrockProtocol) | `pocketmine\network\mcpe\protocol` |
|
||||
| [pmmp/BinaryUtils](https://github.com/pmmp/BinaryUtils) | `pocketmine\utils\BinaryDataException`</br>`pocketmine\utils\BinaryStream`</br>`pocketmine\utils\Binary` |
|
||||
| [pmmp/ClassLoader](https://github.com/pmmp/`ClassLoader`) | `BaseClassLoader`</br>`ClassLoader`</br>`DynamicClassLoader` |
|
||||
| [pmmp/Color](https://github.com/pmmp/Color) | `pocketmine\color` |
|
||||
| [pmmp/ErrorHandler](https://github.com/pmmp/ErrorHandler) | `pocketmine\errorhandler` |
|
||||
| [pmmp/LogPthreads](https://github.com/pmmp/LogPthreads) | `ThreadedLoggerAttachment`</br>`ThreadedLogger`</br>`AttachableThreadedLogger` |
|
||||
| [pmmp/Log](https://github.com/pmmp/Log) | `AttachableLogger`</br>`BufferedLogger`</br>`GlobalLogger`</br>`LogLevel`</br>`Logger`</br>`PrefixedLogger`</br>`SimpleLogger` |
|
||||
| [pmmp/Math](https://github.com/pmmp/Math) | `pocketmine\math` |
|
||||
| [pmmp/NBT](https://github.com/pmmp/NBT) | `pocketmine\nbt` |
|
||||
|
@ -4,8 +4,8 @@
|
||||
<img src="https://github.com/pmmp/PocketMine-MP/blob/stable/.github/readme/pocketmine.png" alt="The PocketMine-MP logo" title="PocketMine" loading="eager" />
|
||||
<![endif]-->
|
||||
<picture>
|
||||
<source srcset="https://github.com/pmmp/PocketMine-MP/raw/stable/.github/readme/pocketmine-dark.png" media="(prefers-color-scheme: dark)">
|
||||
<img src="https://github.com/pmmp/PocketMine-MP/raw/stable/.github/readme/pocketmine.png" loading="eager" />
|
||||
<source srcset="https://raw.githubusercontent.com/pmmp/PocketMine-MP/stable/.github/readme/pocketmine-dark-rgb.gif" media="(prefers-color-scheme: dark)">
|
||||
<img src="https://raw.githubusercontent.com/pmmp/PocketMine-MP/stable/.github/readme/pocketmine-rgb.gif" loading="eager" />
|
||||
</picture>
|
||||
</a><br>
|
||||
<b>A highly customisable, open source server software for Minecraft: Bedrock Edition written in PHP</b>
|
||||
@ -26,7 +26,7 @@
|
||||
- [Docker image](https://github.com/pmmp/PocketMine-MP/pkgs/container/pocketmine-mp)
|
||||
- [Plugin repository](https://poggit.pmmp.io/plugins)
|
||||
|
||||
## Discussion/Help
|
||||
## Community & Support
|
||||
- [Forums](https://forums.pmmp.io/)
|
||||
- [Discord](https://discord.gg/bmSAZBG)
|
||||
- [StackOverflow](https://stackoverflow.com/tags/pocketmine)
|
||||
|
@ -44,6 +44,7 @@ use function fwrite;
|
||||
use function is_string;
|
||||
use function ksort;
|
||||
use function mb_strtoupper;
|
||||
use function preg_replace;
|
||||
use function sort;
|
||||
use function strrpos;
|
||||
use function strtoupper;
|
||||
@ -138,7 +139,7 @@ function generateBlockStateNames(BlockPaletteReport $data) : void{
|
||||
|
||||
fwrite($output, generateClassHeader(BlockStateNames::class));
|
||||
foreach(Utils::stringifyKeys($data->seenStateValues) as $state => $values){
|
||||
$constName = mb_strtoupper($state, 'US-ASCII');
|
||||
$constName = mb_strtoupper(preg_replace("/^minecraft:/", "mc_", $state) ?? throw new AssumptionFailedError("This regex is not invalid"), 'US-ASCII');
|
||||
fwrite($output, "\tpublic const $constName = \"$state\";\n");
|
||||
}
|
||||
|
||||
@ -158,7 +159,7 @@ function generateBlockStringValues(BlockPaletteReport $data) : void{
|
||||
continue;
|
||||
}
|
||||
$anyWritten = true;
|
||||
$constName = mb_strtoupper($stateName . "_" . $value, 'US-ASCII');
|
||||
$constName = mb_strtoupper(preg_replace("/^minecraft:/", "mc_", $stateName) . "_" . $value, 'US-ASCII');
|
||||
fwrite($output, "\tpublic const $constName = \"$value\";\n");
|
||||
}
|
||||
if($anyWritten){
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\generate_item_serializer_ids;
|
||||
|
||||
use pocketmine\data\bedrock\item\BlockItemIdMap;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\network\mcpe\convert\ItemTypeDictionaryFromDataHelper;
|
||||
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
|
||||
@ -45,10 +46,10 @@ function constifyMcId(string $id) : string{
|
||||
return strtoupper(explode(":", $id, 2)[1]);
|
||||
}
|
||||
|
||||
function generateItemIds(ItemTypeDictionary $dictionary) : void{
|
||||
function generateItemIds(ItemTypeDictionary $dictionary, BlockItemIdMap $blockItemIdMap) : void{
|
||||
$ids = [];
|
||||
foreach($dictionary->getEntries() as $entry){
|
||||
if($entry->getNumericId() < 256){ //blockitems are serialized via BlockStateSerializer
|
||||
if($entry->getStringId() === "minecraft:air" || $blockItemIdMap->lookupBlockId($entry->getStringId()) !== null){ //blockitems are serialized via BlockStateSerializer
|
||||
continue;
|
||||
}
|
||||
$ids[$entry->getStringId()] = $entry->getStringId();
|
||||
@ -92,6 +93,7 @@ if($raw === false){
|
||||
}
|
||||
|
||||
$dictionary = ItemTypeDictionaryFromDataHelper::loadFromString($raw);
|
||||
generateItemIds($dictionary);
|
||||
$blockItemIdMap = BlockItemIdMap::getInstance();
|
||||
generateItemIds($dictionary, $blockItemIdMap);
|
||||
|
||||
echo "Done. Don't forget to run CS fixup after generating code.\n";
|
||||
|
@ -30,8 +30,8 @@ use pocketmine\block\utils\DirtType;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FroglightType;
|
||||
use pocketmine\block\utils\LeverFacing;
|
||||
use pocketmine\block\utils\MobHeadType;
|
||||
use pocketmine\block\utils\MushroomBlockType;
|
||||
use pocketmine\block\utils\SkullType;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\item\MedicineType;
|
||||
use pocketmine\item\PotionType;
|
||||
@ -150,7 +150,7 @@ $enumsUsed = [
|
||||
LeverFacing::getAll(),
|
||||
MedicineType::getAll(),
|
||||
MushroomBlockType::getAll(),
|
||||
SkullType::getAll(),
|
||||
MobHeadType::getAll(),
|
||||
SlabType::getAll(),
|
||||
SuspiciousStewType::getAll(),
|
||||
PotionType::getAll()
|
||||
|
Submodule build/php updated: f860ade30a...e0c918d137
@ -71,3 +71,9 @@ Released 6th May 2023.
|
||||
## Fixes
|
||||
- Fixed players being forced into flight mode in every game mode.
|
||||
- Moral of the story: do not assume anything in Mojang internals does what its name suggests...
|
||||
|
||||
# 4.20.5
|
||||
Released 30th May 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash due to a bug in upstream dependency [`netresearch/jsonmapper`](https://github.com/cweiske/JsonMapper).
|
||||
|
@ -69,3 +69,9 @@ Released 17th May 2023.
|
||||
- Registered but ineligible ticking chunks are no longer rechecked every tick.
|
||||
- This was causing wasted cycles during async worker backlog.
|
||||
- The internal system must call `markTickingChunkForRecheck()` whenever a ticking chunk's eligibility for ticking has potentially changed, rather than just when it has changed from a yes to a no.
|
||||
|
||||
# 4.21.1
|
||||
Released 30th May 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash due to a bug in upstream dependency [`netresearch/jsonmapper`](https://github.com/cweiske/JsonMapper).
|
||||
|
58
changelogs/4.22.md
Normal file
58
changelogs/4.22.md
Normal file
@ -0,0 +1,58 @@
|
||||
# 4.22.0
|
||||
Released 7th June 2023.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.0**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.0.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
## Interim releases
|
||||
If you're upgrading from 4.20.x directly to 4.22.x, please also read the following changelogs, as the interim releases contain important changes:
|
||||
- [4.21.0](https://github.com/pmmp/PocketMine-MP/blob/4.22.0/changelogs/4.21.md#4210) - PHP 8.1 minimum version, minor performance improvements
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.20.0.
|
||||
- Removed support for older versions.
|
||||
|
||||
## Fixes
|
||||
- Removed deprecated `ReflectionProperty::setAccessible()` calls.
|
||||
- Fixed jukebox music not stopping when destroyed by an explosion.
|
||||
|
||||
# 4.22.1
|
||||
Released 9th June 2023.
|
||||
|
||||
## Fixes
|
||||
- Replaced workaround for an old teleporting client bug:
|
||||
- This workaround broke due to an additional client bug introduced by 1.20, causing players to become frozen to observers when teleported.
|
||||
- The original client bug has still not been fixed, meaning a new workaround was needed, but no perfect solution could be found.
|
||||
- The new workaround involves broadcasting teleport movements as regular movements, which causes unwanted interpolation between the old and new positions, but otherwise works correctly. This solution is not ideal, but it is the best we can do for now.
|
||||
- See issues [#4394](https://github.com/pmmp/PocketMine-MP/issues/4394) and [#5810](https://github.com/pmmp/PocketMine-MP/issues/5810) for more details.
|
||||
|
||||
# 4.22.2
|
||||
Released 1st July 2023.
|
||||
|
||||
## Changes
|
||||
- Added obsoletion warnings to the server log at the end of the startup sequence.
|
||||
|
||||
## Fixes
|
||||
- Fixed players being disconnected en masse with "Not authenticated" messages.
|
||||
- This occurred due to a check intended to disable the old authentication key after July 1st.
|
||||
- We expected that the new key would have been deployed by Mojang by now, but it seems like that has not yet happened.
|
||||
- Due to the lack of a hard date for the key changeover, we guessed that July 1st would be a safe bet, but this appears to have backfired.
|
||||
- This version will accept both old and new keys indefinitely.
|
||||
- A security release will be published to remove the old key after the transition occurs.
|
||||
|
||||
# 4.22.3
|
||||
Released 11th July 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed mishandling of NBT leading to a server crash when editing signs.
|
||||
- Fixed an edge case crash that could occur in `AsyncTask->__destruct()` when thread-local storage referenced other `AsyncTask` objects.
|
||||
|
||||
## Internals
|
||||
- Added a concurrency lock to prevent the `update-updater-api` GitHub Action from running for multiple releases at the same time (which would have caused one of them to fail due to git conflicts).
|
32
changelogs/4.23.md
Normal file
32
changelogs/4.23.md
Normal file
@ -0,0 +1,32 @@
|
||||
# 4.23.0
|
||||
Released 12th July 2023.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.10**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.10.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` 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.20.10.
|
||||
- Removed support for older versions.
|
||||
|
||||
## Fixes
|
||||
- Fixed Docker image build failure due to outdated `build/php` submodule.
|
||||
|
||||
# 4.23.1
|
||||
Released 14th July 2023.
|
||||
|
||||
## Fixes
|
||||
- Hardened validation of JWT signing keys in `LoginPacket`.
|
||||
- Fixed server crash due to a bug in upstream dependency [`netresearch/jsonmapper`](https://github.com/cweiske/JsonMapper).
|
||||
|
||||
# 4.23.2
|
||||
Released 18th July 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed login errors due to a new `sandboxId` field appearing in the Xbox Live authentication data in `LoginPacket`. All clients, regardless of version, are affected by this change.
|
@ -194,3 +194,22 @@ Released 17th May 2023.
|
||||
- `RuntimeBlockMapping` has been renamed to `BlockTranslator`.
|
||||
- Singletons in the `pocketmine\network\mcpe\convert` package have been centralized under `TypeConverter`. In the future, this will be injected where needed, allowing different converters to be used for different sessions (useful for multiversion).
|
||||
- Overriding of serializers and deserializers of items and blocks is now consistently disallowed. Due to the lack of a good reason to allow overriding built-in blocks and items, this is no longer allowed.
|
||||
|
||||
# 5.0.0-BETA4
|
||||
Released 23rd May 2023.
|
||||
|
||||
## General
|
||||
- [`ext-pmmpthread` version 6.0.0](https://github.com/pmmp/ext-pmmpthread/releases/6.0.0) (renamed from `ext-pthreads`) is now required. This version has API changes compared to pthreads v5. Please read the linked changelog for details.
|
||||
- [`pocketmine/snooze` version 0.5.0](https://github.com/pmmp/Snooze/releases/0.5.0) is now required.
|
||||
- `pocketmine/classloader` and `pocketmine/log-pthreads` packages have been removed. The relevant classes from these packages are now included in-house in the `pocketmine/thread` namespace.
|
||||
- `BaseClassLoader` is replaced with `pocketmine\thread\ThreadSafeClassLoader`
|
||||
- `ThreadedLogger` is replaced by `pocketmine\thread\ThreadSafeLogger`
|
||||
- `AttachableThreadedLogger` is replaced by `pocketmine\thread\AttachableThreadSafeLogger`
|
||||
- `ThreadedLoggerAttachment` is replaced by `pocketmine\thread\ThreadSafeLoggerAttachment`
|
||||
|
||||
## Fixes
|
||||
- Fixed crash on unknown blocks due to late initialization of properties in `UnknownBlock`.
|
||||
|
||||
## API changes
|
||||
### `pocketmine\scheduler`
|
||||
- `AsyncTask->setResult()` now works with thread-safe objects. This was previously not possible due to limitations in the `pthreads` extension.
|
||||
|
791
changelogs/5.0.md
Normal file
791
changelogs/5.0.md
Normal file
@ -0,0 +1,791 @@
|
||||
# 5.0.0
|
||||
Released 1st June 2023.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.19.80**
|
||||
|
||||
5.0.0 is a major feature update to PocketMine-MP, including support for Bedrock worlds from 1.13 onwards, a large array of new blocks and items, and various changes to the plugin API.
|
||||
|
||||
It is **not** compatible with plugins written for 4.x.y, and plugins may require code changes to work with it.
|
||||
|
||||
**As this changelog is quite large, its accuracy and completeness cannot be guaranteed.**
|
||||
Make sure you're looking at the [latest revision](https://github.com/pmmp/PocketMine-MP/blob/stable/changelogs/5.0.md), as it may be amended after release.
|
||||
|
||||
## Core
|
||||
- Worlds are now saved according to the Bedrock 1.19.80 format.
|
||||
- Worlds generated by Bedrock from 1.13.0 and up are now supported (previously, only worlds up to 1.12 were supported).
|
||||
- `/particle` now accepts strings for particle data instead of integers.
|
||||
- `/particle` no longer accepts integers for block or item IDs.
|
||||
- The usage of `blockcrack`, `iconcrack` and `blockdust` particle types in `/particle` now follows the same pattern as
|
||||
other particle types, with the data for each being passed in the `data` parameter instead of being baked into the
|
||||
particle name.
|
||||
- Commands are now enabled by default in worlds exported from PocketMine-MP to Bedrock.
|
||||
|
||||
## Build system
|
||||
- `build/generate-block-serializer-consts.php` has been added to generate `BlockTypeNames`, `BlockStateNames` and `BlockStateStringValues` in the `pocketmine\data\bedrock\block` package.
|
||||
- `build/generate-item-type-names.php` has been added to generate `ItemTypeNames` in the `pocketmine\data\bedrock\item` package.
|
||||
- `build/generate-runtime-enum-serializers.php` has been added to generate `RuntimeEnumSerializer`, `RuntimeEnumSerializerTrait`, and `RuntimeEnumSizeCalculatorTrait` in `pocketmine\data\runtime` package.
|
||||
|
||||
## Localization
|
||||
- Localized disconnect messages are now used in the following cases:
|
||||
- Server full
|
||||
- Player not on the server whitelist
|
||||
- Player on the server ban list
|
||||
- Invalid skin
|
||||
- Invalid username
|
||||
- Kicked using `/kick`
|
||||
- Banned using `/ban`
|
||||
- Failure to find a safe spawn position
|
||||
- Session open, session close and session player name discovery messages are now localized.
|
||||
- All permissions now have localized descriptions. These are not currently used by PocketMine-MP, but may be useful for plugins.
|
||||
|
||||
## Performance
|
||||
- Improved performance of dropping block inventory contents when the block is destroyed.
|
||||
- Improved light propagation performance by 10-15%.
|
||||
- [`ext-pmmpthread` version 6.0.0](https://github.com/pmmp/ext-pmmpthread/releases/tag/6.0.0) is now used, featuring significant performance improvements for thread-safe objects and various threading use-cases.
|
||||
|
||||
## Tools
|
||||
- The following tool scripts have been added:
|
||||
- `generate-block-palette-spec.php` - generates a JSON file with a readable overview of blocks, their state
|
||||
properties, and their possible values
|
||||
- `generate-blockstate-upgrade-schema.php` - generates JSON blockstate upgrade schemas like those found
|
||||
in [BedrockBlockUpgradeSchema](https://github.com/pmmp/BedrockBlockUpgradeSchema)
|
||||
- `generate-item-upgrade-schema.php` - generates JSON item ID/meta upgrade schemas like those found
|
||||
in [BedrockItemUpgradeSchema](https://github.com/pmmp/BedrockItemUpgradeSchema)
|
||||
- `generate-bedrock-data-from-packets.php` - generates various files for [BedrockData](https://github.com/pmmp/BedrockData)
|
||||
- This script accepts a txt file containing base64-encoded packet dumps.
|
||||
- This script has been in use for several years, but has only now been open-sourced.
|
||||
- It's used to generate data such as crafting recipes, creative inventory data, and various other blobs of data needed to support the current version of Minecraft: Bedrock Edition.
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Added the following new blocks:
|
||||
- Amethyst Block
|
||||
- Ancient Debris
|
||||
- Azalea Leaves
|
||||
- Basalt
|
||||
- Blackstone blocks, slabs, stairs, and walls
|
||||
- Cakes with Candle & Dyed Candle
|
||||
- Calcite
|
||||
- Candle & Dyed Candle
|
||||
- Cartography Table (not currently usable due to maps not being implemented)
|
||||
- Cauldron
|
||||
- Cave Vines
|
||||
- Chain
|
||||
- Chiseled Deepslate
|
||||
- Chiseled Nether Bricks
|
||||
- Chiseled Polished Blackstone
|
||||
- Chorus Flower
|
||||
- Chorus Plant
|
||||
- Cobbled Deepslate blocks, slabs, stairs, and walls
|
||||
- Copper Ore
|
||||
- Copper block (random oxidation not yet implemented)
|
||||
- Crached Deepslate Tiles
|
||||
- Cracked Deepslate Bricks
|
||||
- Cracked Nether Bricks
|
||||
- Cracked Polished Blackstone Bricks
|
||||
- Crimson buttons, doors, fences, fence gates, hyphae, planks, pressure plates, signs, slabs, stairs, stems, and trapdoors
|
||||
- Crying Obsidian
|
||||
- Cut Copper block, stairs and slabs (random oxidation not yet implemented)
|
||||
- Deepslate
|
||||
- Deepslate Bricks blocks, slabs, stairs, and walls
|
||||
- Deepslate Ores (coal, copper, diamond, emerald, gold, iron, lapis lazuli, redstone)
|
||||
- Deepslate Tiles blocks, slabs, stairs, and walls
|
||||
- Flowering Azalea Leaves
|
||||
- Froglight (pearlescent, verdant, ochre)
|
||||
- Gilded Blackstone
|
||||
- Glow Item Frame
|
||||
- Hanging Roots
|
||||
- Honeycomb Block
|
||||
- Light Block
|
||||
- Lightning Rod
|
||||
- Mangrove Leaves
|
||||
- Mangrove Roots
|
||||
- Mangrove buttons, doors, fences, fence gates, logs, planks, pressure plates, signs, slabs, stairs, trapdoors, and wood
|
||||
- Mud Bricks blocks, slabs, stairs, and walls
|
||||
- Muddy Mangrove Roots
|
||||
- Nether Gold Ore
|
||||
- Netherite Block
|
||||
- Polished Basalt
|
||||
- Polished Blackstone Bricks blocks, slabs, stairs, and walls
|
||||
- Polished Blackstone blocks, buttons, pressure plates, slabs, stairs, and walls
|
||||
- Polished Deepslate blocks, slabs, stairs, and walls
|
||||
- Quartz Bricks
|
||||
- Reinforced Deepslate
|
||||
- Rooted Dirt
|
||||
- Sculk
|
||||
- Shroomlight
|
||||
- Smithing Table
|
||||
- Smooth Basalt
|
||||
- Soul Fire
|
||||
- Soul Lantern
|
||||
- Soul Soil
|
||||
- Soul Torch
|
||||
- Spore Blossom
|
||||
- Tinted Glass
|
||||
- Tuff
|
||||
- Twisting Vines
|
||||
- Warped Wart Block
|
||||
- Warped buttons, doors, fences, fence gates, hyphae, planks, pressure plates, signs, slabs, stairs, stems, and trapdoors
|
||||
- Weeping Vines
|
||||
- Wither Rose
|
||||
- Added support for basalt generators
|
||||
- Added support for dyeing sign text and making it glow.
|
||||
- All-sided logs ("wood", for want of a better name) can now be placed in X, Y, and Z orientations.
|
||||
- Coral and coral fans now behave correctly when placed out of water (they no longer immediately die).
|
||||
- Fixed dead bush being able to be placed on some invalid blocks (e.g. stone).
|
||||
- Fixed lava setting entities on fire for an incorrect duration (Java vs Bedrock inconsistency).
|
||||
- Fixed sugarcane not being able to be placed on some blocks.
|
||||
- Iron Ore and Gold Ore now drop Raw Iron and Raw Gold respectively, instead of the ore blocks.
|
||||
- Item frames can now be placed on the top and bottom of blocks.
|
||||
- Stripping logs by right-clicking them with an axe is now supported.
|
||||
- TNT can now be ignited by fire charges.
|
||||
- Vines can now only be placed on the side of full-cube blocks.
|
||||
- Walls now connect when placed, following the pre-1.16 logic. (1.16 logic is planned to be implemented, but currently low priority.)
|
||||
- Anvils are now damaged when they hit the ground after falling.
|
||||
- Added missing sounds for anvils hitting the ground after falling.
|
||||
- Anvils now damage entities when they fall on top of them.
|
||||
|
||||
### Items
|
||||
- Added the following new items:
|
||||
- Amethyst Shard
|
||||
- Antidote (from Education Edition)
|
||||
- Copper Ingot
|
||||
- Disc Fragment (5)
|
||||
- Echo Shard
|
||||
- Elixir (from Education Edition)
|
||||
- Eye Drops (from Education Edition)
|
||||
- Fire Charge
|
||||
- Glow Berries
|
||||
- Glow Ink Sac
|
||||
- Honey Bottle
|
||||
- Honeycomb
|
||||
- Mangrove Boat (incomplete)
|
||||
- Music Disc (5)
|
||||
- Music Disc (Otherside)
|
||||
- Music Disc (Pigstep)
|
||||
- Netherite Axe
|
||||
- Netherite Boots
|
||||
- Netherite Chestplate
|
||||
- Netherite Helmet
|
||||
- Netherite Ingot
|
||||
- Netherite Leggings
|
||||
- Netherite Pickaxe
|
||||
- Netherite Scrap
|
||||
- Netherite Shovel
|
||||
- Netherite Sword
|
||||
- Phantom Membrane
|
||||
- Raw Copper
|
||||
- Raw Gold
|
||||
- Raw Iron
|
||||
- Spyglass
|
||||
- Suspicious Stew
|
||||
- Tonic (from Education Edition)
|
||||
- Glass bottles can now be filled with water by clicking on a water source block.
|
||||
- Implemented Swift Sneak enchantment.
|
||||
- Armour durability is now only reduced when the wearer receives a type of damage that the armour can protect against.
|
||||
- Bells now ring when hit by a projectile.
|
||||
|
||||
### Worlds
|
||||
- World height of -64 to 319 is now supported.
|
||||
- Added support for 3D biomes. This isn't used by PocketMine-MP yet, but is necessary to be able to fully load 1.18 worlds.
|
||||
|
||||
## API
|
||||
### General
|
||||
- Union and mixed native parameter, return and property types are now used where appropriate.
|
||||
- Protected and public properties now use native property types wherever possible.
|
||||
- Parameter and return typehints have been applied in many places where it wasn't previously possible.
|
||||
|
||||
### Dependencies
|
||||
- [`ext-pmmpthread` version 6.0.0](https://github.com/pmmp/ext-pmmpthread/releases/6.0.0) (renamed from `ext-pthreads`) is now required. This version features major API changes and improvements. Please read the [upgrading guide](https://github.com/pmmp/ext-pmmpthread/blob/fork/docs/UPGRADING_4.x_to_6.0.md) for details.
|
||||
- [`pocketmine/snooze` version 0.5.0](https://github.com/pmmp/Snooze/releases/0.5.0) is now required.
|
||||
- [`pocketmine/raklib` version 0.15.0](https://github.com/pmmp/RakLib/releases/0.15.0) is now required.
|
||||
- [`pocketmine/raklib-ipc` version 0.2.0](https://github.com/pmmp/RakLibIpc/releases/0.2.0) is now required.
|
||||
- `pocketmine/classloader` and `pocketmine/log-pthreads` packages have been removed. The relevant classes from these packages are now included in-house in the `pocketmine/thread` namespace.
|
||||
- `BaseClassLoader` is replaced with `pocketmine\thread\ThreadSafeClassLoader`
|
||||
- `ThreadedLogger` is replaced by `pocketmine\thread\ThreadSafeLogger`
|
||||
- `AttachableThreadedLogger` is replaced by `pocketmine\thread\AttachableThreadSafeLogger`
|
||||
- `ThreadedLoggerAttachment` is replaced by `pocketmine\thread\ThreadSafeLoggerAttachment`
|
||||
- `webmozart/path-util` has been removed (replaced by [`symfony/filesystem`](https://github.com/symfony/filesystem)).
|
||||
|
||||
### `pocketmine\block`
|
||||
#### Highlights
|
||||
- Blocks no longer use internal Minecraft IDs and metadata to identify themselves. All APIs associated with legacy IDs
|
||||
and meta have been removed.
|
||||
- A new set of runtime IDs generated from `VanillaBlocks` is used to identify block types. These IDs are defined
|
||||
in `BlockTypeIds`.
|
||||
- These new IDs are used for runtime representation of blocks on chunks, and for type comparison purposes.
|
||||
- Block type IDs are used at **runtime only**. **Do not store them in configs or databases**, as they are subject to
|
||||
change without warning.
|
||||
- Block type IDs are **specific to PocketMine-MP** and have no relation to the IDs used by Minecraft.
|
||||
- Block type IDs cannot be negative
|
||||
- Block type IDs must not be reused, even if overriding an already defined block
|
||||
- Block state properties (e.g. facing, colour, etc.) are now represented by PM-specific state data instead of legacy
|
||||
metadata. The state data consists of:
|
||||
- Block-item state properties - retained by items when the block is broken (colour, wet/dry, coral type, etc.) - handled by `Block->describeBlockItemState()`
|
||||
- Block-only state data - discarded when the block is broken (facing direction, lit/unlit, powered/unpowered, etc.) - handled by `Block->describeBlockOnlyState()`
|
||||
- Chunks now store dynamic state ID derived from the runtime type ID and runtime (PocketMine-MP defined) state data.
|
||||
- Introduced "type tags" concept, which allows marking certain blocks as having certain behaviours.
|
||||
- The idea for this system was borrowed from the Minecraft Java tags system.
|
||||
- It's still in very early concept stage, but is currently used for deciding which types of blocks plants can be placed on without needing to enumerate every single ID in every class, eliminating a bunch of boilerplate code and improving consistency.
|
||||
- All `Block` descendents now accept `BlockTypeInfo` in the constructor, instead of `BlockBreakInfo`. This allows for future additions without needing to change dozens of overridden constructors.
|
||||
- `&$returnedItems` reference parameter is now used in some places such as `Block->onInteract()` to enable actions to return items to players without caring about whether they are in creative or anything else.
|
||||
- This eliminates boilerplate code of deciding whether to set the player's held item or not, as well as automatically dropping any overflow items that don't fit into the inventory.
|
||||
- This is currently used when filling/emptying cauldrons using buckets or glass bottles.
|
||||
- Dependency between `RuntimeBlockStateRegistry` (previously `BlockFactory`) and `VanillaBlocks` has been inverted.
|
||||
- Now, blocks types are defined in `VanillaBlocks`
|
||||
- `RuntimeBlockStateRegistry` automatically registers states for blocks defined in `VanillaBlocks`.
|
||||
- Manual registration in `RuntimeBlockStateRegistry` is still required for custom blocks (see section below about registering new blocks).
|
||||
- `RuntimeBlockStateRegistry` now has only one purpose - to map internal blockstate IDs to `Block` objects when reading blocks from chunks. It should not be used by plugins unless registering new blocks.
|
||||
- To get a block at runtime, e.g. stone, use `VanillaBlocks::STONE()`
|
||||
- To load a block from **old** config or database data:
|
||||
1. Use `GlobalBlockStateHandlers::getUpgrader()->upgradeIntIdMeta()` to convert it to modern data
|
||||
2. Pass the data to `GlobalBlockStateHandlers::getDeserializer()` to get a blockstate ID
|
||||
3. Pass the blockstate ID to `RuntimeBlockStateRegistry::fromStateId()` to get a `Block` instance
|
||||
- Prefer using `StringToItemParser` wherever possible for configs and databases (see `lookupAliases()` and `lookupBlockAliases()`).
|
||||
|
||||
#### Registering new blocks
|
||||
##### In a plugin
|
||||
To add a vanilla block in a plugin which isn't yet supported by PocketMine-MP, do the following:
|
||||
|
||||
1. Get a new type ID using `BlockTypeIds::newId()` - _you'll want to keep this in a property somewhere if you want to
|
||||
compare using `getTypeId()` later_
|
||||
2. Set up the block type somewhere - _this can be anywhere you like, e.g. a plugin main class property, but using
|
||||
a `RegistryTrait` class is recommended - you'll need this later to create new instances of the block_
|
||||
3. Register the block in `RuntimeBlockStateRegistry` - _this informs the server of all the block's possible states so
|
||||
that it can be read from chunks at runtime_
|
||||
4. Register a deserializer for the block's Minecraft ID in `BlockStateToObjectDeserializer` - _needed for the block to
|
||||
be recognized when loaded from disk_
|
||||
5. Register a serializer for the block in `BlockObjectToStateSerializer` - _needed for the block to be saved to disk,
|
||||
and to be sent over the network_
|
||||
6. Optionally, register a string alias for the block in `StringToItemParser` - _so that it can be given via `/give`_
|
||||
|
||||
To see a demo of how to do this in a plugin, see [this example plugin](https://github.com/pmmp/RegisterBlocksDemoPM5).
|
||||
|
||||
Registering custom blocks follows a similar process, but requires additional steps to modify `BlockStateDictionary`
|
||||
which won't be covered here.
|
||||
Since this is not currently officially supported by PocketMine-MP, this won't be covered here.
|
||||
|
||||
This is admittedly rather more of a hassle than in PM4, but this system offers significantly more flexibility than the
|
||||
old system.
|
||||
|
||||
##### As a PocketMine-MP core contribution
|
||||
To register a new vanilla block into the core, the process is slightly different:
|
||||
|
||||
1. Instead of using `BlockTypeIds::newId()`, add a new constant for the block to `BlockTypeIds`
|
||||
2. Register the new block in `VanillaBlocks` - `RuntimeBlockStateRegistry` will automagically take notice of all blocks
|
||||
defined in `VanillaBlocks`
|
||||
3. Follow steps 4 onwards above
|
||||
|
||||
#### Change list
|
||||
- The following classes have been removed:
|
||||
- `BlockIdentifierFlattened`
|
||||
- `BlockLegacyIdHelper`
|
||||
- `BlockLegacyIds`
|
||||
- `BlockLegacyMetadata`
|
||||
- `utils\BlockDataSerializer`
|
||||
- `utils\ColorInMetadataTrait` - `utils\ColoredTrait` now implements colour type data serialization uniformly
|
||||
- `utils\InvalidBlockStateException` - this has been superseded by `pocketmine\data\runtime\InvalidSerializedRuntimeDataException`
|
||||
- `utils\NormalHorizontalFacingInMetadataTrait` - `utils\HorizontalFacingTrait` now implements facing type data serialization uniformly
|
||||
- `utils\PillarRotationInMetadataTrait` - `utils\PillarRotationTrait` now implements rotation type data serialization uniformly
|
||||
- The following classes have been renamed:
|
||||
- `BlockFactory` -> `RuntimeBlockStateRegistry` - this class is now exclusively used for mapping state IDs to block instances for runtime chunk block reading
|
||||
- `Skull` -> `MobHead`
|
||||
- `utils\SkullType` -> `utils\MobHeadType`
|
||||
- `utils\TreeType` -> `pocketmine\world\generator\object\TreeType`
|
||||
- The following classes have been added:
|
||||
- `BaseCake`
|
||||
- `BaseFire`
|
||||
- `BlockTypeIds` - list of type IDs, one for each entry in `VanillaBlocks`
|
||||
- `BlockTypeInfo`
|
||||
- `BlockTypeTags`
|
||||
- `CakeWithCandle`
|
||||
- `CakeWithDyedCandle`
|
||||
- `Candle`
|
||||
- `CartographyTable`
|
||||
- `Chain`
|
||||
- `CopperOre`
|
||||
- `CopperSlab`
|
||||
- `CopperStairs`
|
||||
- `Copper`
|
||||
- `DyedCandle`
|
||||
- `GildedBlackstone`
|
||||
- `GoldOre`
|
||||
- `HangingRoots`
|
||||
- `IronOre`
|
||||
- `Light`
|
||||
- `LightningRod`
|
||||
- `NetherGoldOre`
|
||||
- `Sculk`
|
||||
- `SmithingTable`
|
||||
- `SoulFire`
|
||||
- `WitherRose`
|
||||
- `utils\CandleTrait`
|
||||
- `utils\CopperOxidation`
|
||||
- `utils\CopperTrait`
|
||||
- `utils\SaplingType` - enum of all sapling types
|
||||
- `utils\WallConnectionType` - enum of all possible wall connection types
|
||||
- `utils\WoodTypeTrait`
|
||||
- `utils\WoodType` - enum of all possible wood types, used for wood material blocks like planks and logs
|
||||
- The following API methods have been removed:
|
||||
- `Block->getId()` - for type comparisons, use `Block->getTypeId()` instead
|
||||
- `Block->getMeta()` - for state comparisons, use `Block->getStateId()` instead
|
||||
- `Block->getStateBitmask()`
|
||||
- `Block->readStateFromData()`
|
||||
- `Block->writeStateToItemMeta()`
|
||||
- `Block->writeStateToMeta()`
|
||||
- `BlockFactory->get()` - see notes above about `RuntimeBlockStateRegistry`
|
||||
- `BlockIdentifier->getAllBlockIds()`
|
||||
- `BlockIdentifier->getBlockId()`
|
||||
- `BlockIdentifier->getItemId()`
|
||||
- `BlockIdentifier->getVariant()`
|
||||
- `Door->isPowered()`
|
||||
- `Door->setPowered()`
|
||||
- `MobHead->isNoDrops()` (previously `Skull->isNoDrops()`)
|
||||
- `MobHead->setNoDrops()` (previously `Skull->setNoDrops()`)
|
||||
- `VanillaBlocks::*_GLAZED_TERRACOTTA()` - use `VanillaBlocks::GLAZED_TERRACOTTA()->setColor(DyeColor::WHATEVER())` instead
|
||||
- `utils\FallableTrait->getId()` is no longer required
|
||||
- `utils\FallableTrait->getMeta()` is no longer required
|
||||
- `utils\MobHeadType->getMagicNumber()` (previously `utils\SkullType->getMagicNumber()`)
|
||||
- `utils\MobHeadType::fromMagicNumber()` (previously `utils\SkullType::fromMagicNumber()`)
|
||||
- The following constants have been removed:
|
||||
- `Block::INTERNAL_METADATA_BITS`
|
||||
- `Block::INTERNAL_METADATA_MASK`
|
||||
- The following API methods have been renamed:
|
||||
- `Block->getFullId()` -> `Block->getStateId()`
|
||||
- `Block->isSameType()` -> `Block->hasSameTypeId()`
|
||||
- `MobHead->getSkullType()` -> `MobHead->getMobHeadType()` (previously `Skull->getSkullType()`)
|
||||
- `MobHead->setSkullType()` -> `MobHead->setMobHeadType()` (previously `Skull->setSkullType()`)
|
||||
- The following API methods have signature changes:
|
||||
- `Block->onBreak()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Block->onInteract()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Block->readStateFromWorld()` now returns `Block` - this allows blocks to replace themselves with a different block entirely based on world conditions.
|
||||
- `BlockIdentifier->__construct()` now accepts `int $blockTypeId`, and no longer accepts `int $blockId, int $variant, ?int $itemId`
|
||||
- `ItemFrame->getFacing()` may now return `Facing::UP` and `Facing::DOWN`
|
||||
- `ItemFrame->setFacing()` now accepts `Facing::UP` and `Facing::DOWN`
|
||||
- `Leaves->__construct()` now accepts `LeavesType $leavesType` instead of `TreeType $treeType`
|
||||
- `RuntimeBlockStateRegistry->register()` no longer accepts an `$override` parameter.
|
||||
- `Sapling::__construct()` now accepts `SaplingType $saplingType` instead of `TreeType $treeType`
|
||||
- `utils\SignText::__construct()` now accepts two new optional parameters: `?Color $baseColor` and `bool $glowing`
|
||||
- `utils\SignText::fromBlob()` now accepts two new optional parameters: `?Color $baseColor` and `bool $glowing`
|
||||
- The following API methods have been added:
|
||||
- `protected Block->describeBlockOnlyState(RuntimeDataDescriber $w) : void` - describes state properties which are discarded when the block is broken or block-picked, such as facing, powered, etc.
|
||||
- `public Block->describeBlockItemState(RuntimeDataDescriber $w) : void` - describes state properties which are kept by the item when the block is broken or block-picked, such as dye color
|
||||
- `public Block->generateStatePermutations() : \Generator<int, Block, void, void>` - yields all possible states this block type can be in (used for `RuntimeBlockStateRegistry`)
|
||||
- `public Block->getTypeTags() : array<string>`
|
||||
- `public Block->hasTypeTag(string $tag) : bool`
|
||||
- `public Block->isFireProofAsItem() : bool`
|
||||
- `public Block->onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void`
|
||||
- `public BlockIdentifier->getBlockTypeId() : int` - returns the block's type ID according to `BlockTypeIds`
|
||||
- `public Furnace->getFurnaceType() : utils\FurnaceType`
|
||||
- `public GlazedTerracotta->getColor() : utils\DyeColor` (from `ColoredTrait`) - this was previously unsupported due to legacy limitations
|
||||
- `public GlazedTerracotta->setColor(utils\DyeColor $color) : $this` (from `ColoredTrait`) - this was previously unsupported due to legacy limitations
|
||||
- `public Leaves->getLeavesType() : utils\LeavesType` - returns the type of leaves
|
||||
- `public Wall->getConnection(int $face) : utils\WallConnectionType`
|
||||
- `public Wall->getConnections() : array<int, utils\WallConnectionType>` - returns the wall's connections and their types (see `utils\WallConnectionType`)
|
||||
- `public Wall->isPost() : bool`
|
||||
- `public Wall->setConnection(int $face, ?utils\WallConnectionType $type) : $this`
|
||||
- `public Wall->setConnections()` - sets the wall's connections and their types (see `utils\WallConnectionType`)
|
||||
- `public Wall->setPost(bool $post) : $this`
|
||||
- `public Wood->isStripped() : bool`
|
||||
- `public Wood->setStripped(bool $stripped) : $this`
|
||||
- `public static BlockBreakInfo::axe(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public static BlockBreakInfo::pickaxe(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public static BlockBreakInfo::shovel(float $hardness, ?ToolTier $toolTier = null, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public static BlockBreakInfo::tier(float $hardness, int $toolType, ToolTier $toolTier, ?float $blastResistance = null) : BlockBreakInfo`
|
||||
- `public tile\Spawnable->getRenderUpdateBugWorkaroundStateProperties(Block $block) : array<string, Tag>` - allows spawnable tiles to spoof block state properties to work around client-side rendering bugs without actually changing the block server-side
|
||||
- `public utils\SignText->getBaseColor() : \pocketmine\color\Color`
|
||||
- `public utils\SignText->isGlowing() : bool`
|
||||
- The following classes now use new traits, adding API methods and/or properties:
|
||||
- `FenceGate` uses `utils\WoodTypeTrait`
|
||||
- `GlazedTerracotta` uses `utils\ColoredTrait`
|
||||
- `Planks` uses `utils\WoodTypeTrait`
|
||||
- `Wood` uses `utils\WoodTypeTrait`
|
||||
- `WoodenButton` uses `utils\WoodTypeTrait`
|
||||
- `WoodenDoor` uses `utils\WoodTypeTrait`
|
||||
- `WoodenFence` uses `utils\WoodTypeTrait`
|
||||
- `WoodenPressurePlate` uses `utils\WoodTypeTrait`
|
||||
- `WoodenSlab` uses `utils\WoodTypeTrait`
|
||||
- `WoodenStairs` uses `utils\WoodTypeTrait`
|
||||
- `WoodenTrapdoor` uses `utils\WoodTypeTrait`
|
||||
- The following API interface requirements have been added (BC breaking):
|
||||
- `public utils\Fallable->getFallDamagePerBlock() : float` (default implementation provided by `utils\FallableTrait`)
|
||||
- `public utils\Fallable->getLandSound() : ?Sound` (default implementation provided by `utils\FallableTrait`)
|
||||
- `public utils\Fallable->getMaxFallDamage() : float` (default implementation provided by `utils\FallableTrait`)
|
||||
- `public utils\Fallable->onHitGround(FallingBlock $blockEntity) : bool` (default implementation provided by `utils\FallableTrait`)
|
||||
|
||||
### `pocketmine\command`
|
||||
- Command permissions are now always checked by the server when running a command.
|
||||
- This only affects commands implemented by extending `Command`. Plugins using `PluginBase->onCommand()` are not affected by this change, since they already had permissions checked by the server anyway.
|
||||
- Previously, direct inheritors of `Command` were responsible for checking permissions, which required developers to duplicate the same code in every command, and opened lots of potential for security vulnerabilities.
|
||||
- If you want to do something on permission denied (e.g. sending a special message, or audit logging), you can do so by overriding `Command->testPermission()`, instead of baking the code directly into `Command->execute()`.
|
||||
- If you don't want to use permissions at all, just create a permission with a default of `true` (or belonging to `pocketmine.group.user`) and assign that.
|
||||
- `SimpleCommandMap` now requires all commands to have a permission set when registered.
|
||||
- If you actually want to allow everyone to use your command (not advised), you can add a new permission to the `pocketmine.group.user` group, or use `default: true` for `plugin.yml` permissions.
|
||||
- The following API methods have changed behaviour:
|
||||
- `Command->testPermissionSilent()` now returns `false` if there are no permissions associated with the command. This is to prevent commands with no permissions being usable by everyone, which has previously been a source of security issues.
|
||||
- The following API methods have been added:
|
||||
- `public Command->getPermissions() : list<string>` - returns a list of permissions which grant usage access to this command. A user with one or more of these permissions will be able to invoke the command's `execute()` method
|
||||
- `public Command->setPermissions(list<string> $permissions) : void` - sets the permissions which grant usage access to this command. This should be used instead of `setPermission()` with `;` separators (which is now deprecated)
|
||||
|
||||
### `pocketmine\crafting`
|
||||
- JSON models have been updated to reflect updated crafting data format.
|
||||
- The following enum classes have new members:
|
||||
- `ShapelessRecipeType` has new members `CARTOGRAPHY` and `SMITHING`
|
||||
- The following classes have been added:
|
||||
- `ExactRecipeIngredient` - matches an exact item
|
||||
- `MetaWildcardRecipeIngredient` - matches an item with the given legacy Minecraft ID, but any metadata value
|
||||
- `RecipeIngredient` interface
|
||||
- `TagWildcardRecipeIngredient` - matches an item based on its Minecraft tags, e.g. `minecraft:wooden_tier`
|
||||
- The following API methods have signature changes:
|
||||
- `FurnaceRecipe->__construct()` now accepts `RecipeIngredient` instead of `Item`
|
||||
- `FurnaceRecipe->getInput()` now returns `RecipeIngredient` instead of `Item`
|
||||
- `PotionContainerChangeRecipe->__construct()` now accepts `string, RecipeIngredient, string` (using Minecraft string IDs instead of legacy integers).
|
||||
- `PotionContainerChangeRecipe->getIngredient()` now returns `RecipeIngredient` instead of `Item`.
|
||||
- `PotionContainerChangeRecipe->getInputItemId()` now returns `string` (using Minecraft string IDs instead of legacy integers).
|
||||
- `PotionContainerChangeRecipe->getOutputItemId()` now returns `string` (using Minecraft string IDs instead of legacy integers).
|
||||
- `PotionTypeRecipe->__construct()` now accepts `RecipeIngredient` instead of `Item`
|
||||
- `PotionTypeRecipe->getIngredient()` now returns `RecipeIngredient` instead of `Item`
|
||||
- `PotionTypeRecipe->getInput()` now returns `RecipeIngredient` instead of `Item`
|
||||
- `ShapedRecipe->__construct()` now accepts `RecipeIngredient` instead of `Item`
|
||||
- `ShapedRecipe->getIngredient()` now returns `?RecipeIngredient` instead of `?Item`
|
||||
- `ShapedRecipe->getIngredientList()` now returns `RecipeIngredient[]` instead of `Item[]`
|
||||
- `ShapedRecipe->getIngredientMap()` now returns `RecipeIngredient[][]` instead of `Item[][]`
|
||||
- `ShapelessRecipe->__construct()` `$type` parameter is now mandatory.
|
||||
- `ShapelessRecipe->__construct()` now accepts `RecipeIngredient` instead of `Item`
|
||||
- `ShapelessRecipe->getIngredientList()` now returns `RecipeIngredient[]` instead of `Item[]`
|
||||
|
||||
### `pocketmine\data`
|
||||
- New packages `bedrock\block` and `bedrock\item` have been added. These packages contain all the necessary code for loading and saving Bedrock blocks and items from disk.
|
||||
- New package `runtime` has been added. This package contains code for serializing runtime data for blocks and items.
|
||||
- `LegacyToStringBidirectionalIdMap` has been reduced to `LegacyToStringIdMap`.
|
||||
- Since we never map from string ID to legacy ID, bidirectional mapping is no longer necessary.
|
||||
- This affects the following subclasses:
|
||||
- `LegacyBiomeIdToStringIdMap`
|
||||
- `LegacyBlockIdToStringIdMap`
|
||||
- `LegacyEntityIdToStringIdMap`
|
||||
- `LegacyItemIdToStringIdMap`
|
||||
- The following internal API methods have been added:
|
||||
- `public LegacyToStringIdMap->add(string $string, int $legacy) : void` - adds a mapping from a custom legacy ID to custom string ID, needed for upgrading old saved data
|
||||
|
||||
### `pocketmine\entity`
|
||||
- `Entity` now declares new abstract methods which must be implemented by subclasses:
|
||||
- `public Entity->getInitialDragMultiplier() : float`
|
||||
- `public Entity->getInitialGravity() : float`
|
||||
- The following new API methods have been added:
|
||||
- `public Living->getDisplayName() : string`
|
||||
- The following API methods have changed signatures:
|
||||
- `EntityFactory->register()` no longer accepts a `$legacyMcpeSaveId` parameter (now handled by internal conversions instead).
|
||||
- The following API methods have been renamed:
|
||||
- `Entity->isImmobile()` -> `Entity->hasNoClientPredictions()`
|
||||
- `Entity->setImmobile()` -> `Entity->setNoClientPredictions()`
|
||||
- The following internal fields have been renamed:
|
||||
- `Entity->immobile` -> `Entity->noClientPredictions`
|
||||
- The following classes have been removed:
|
||||
- `EntityLegacyIds`
|
||||
|
||||
### `pocketmine\event`
|
||||
- The following classes have inheritance changes:
|
||||
- `block\BlockPlaceEvent` no longer extends `BlockEvent`, and therefore no longer has `getBlock()`. Use `getTransaction()` instead (may contain multiple blocks).
|
||||
- `BlockFormEvent` now includes information about the block which caused the event.
|
||||
|
||||
- The following new classes have been added:
|
||||
- `world\WorldDisplayNameChangeEvent` - called when a world's display name is changed
|
||||
- The following classes have been renamed:
|
||||
- `entity\ExplosionPrimeEvent` -> `entity\EntityPreExplodeEvent`
|
||||
- The following API methods have been added:
|
||||
- `public block\BlockFormEvent->getCausingBlock() : Block`
|
||||
- `public block\BlockGrowEvent->getPlayer() : ?Player` - returns the player that triggered the block growth, or `null` if it was not triggered by a player
|
||||
- `public block\BlockPlaceEvent->getTransaction() : BlockTransaction` - returns the transaction containing a list of changed block positions and the blockstates they will be changed to
|
||||
- `public server\DataPacketSendEvent->setPackets(list<ClientboundPacket> $packets) : void`
|
||||
- The following API methods have changed signatures:
|
||||
- `block\BlockPlaceEvent->__construct()` now accepts `BlockTransaction $transaction` instead of `Block $blockPlace, Block $blockReplace`
|
||||
- `entity\EntityPreExplodeEvent->__construct()` has the `$force` parameter renamed to `$radius`
|
||||
- `entity\EntityPreExplodeEvent->getForce() : float` -> `entity\EntityPreExplodeEvent->getRadius() : float`
|
||||
- `entity\EntityPreExplodeEvent->setForce(float $force) : void` -> `entity\EntityPreExplodeEvent->setRadius(float $radius) : void`
|
||||
- The following API methods have been removed:
|
||||
- `block\BlockPlaceEvent->getBlockReplaced()` - this information is now provided in the `BlockTransaction` object returned by `BlockPlaceEvent->getTransaction()`
|
||||
- The following new API constants have been added:
|
||||
- `entity\EntityDamageEvent::CAUSE_FALLING_BLOCK`
|
||||
- `entity\EntityDamageEvent::MODIFIER_ARMOR_HELMET`
|
||||
|
||||
### `pocketmine\event\player`
|
||||
- `PlayerPreLoginEvent`, `PlayerDuplicateLoginEvent` and `PlayerKickEvent` now supports setting separate log reasons (disconnect reason) and disconnect screen messages.
|
||||
- The following classes have been removed:
|
||||
- `PlayerCommandPreprocessEvent`
|
||||
- The following API methods have changed signatures:
|
||||
- `PlayerDuplicateLoginEvent->getDisconnectMessage()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerDuplicateLoginEvent->setDisconnectMessage()` now accepts `Translatable|string` instead of `string`
|
||||
- `PlayerKickEvent->getReason()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerKickEvent->setReason()` now accepts `Translatable|string` instead of `string`
|
||||
- `PlayerLoginEvent->getKickMessage()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerLoginEvent->setKickMessage()` now accepts `Translatable|string` instead of `string`
|
||||
- `PlayerPreLoginEvent->getFinalKickMessage()` now returns `Translatable|string` instead of `string`
|
||||
- `PlayerPreLoginEvent->getKickMessage()` now returns `Translatable|string|null` instead of `string|null`
|
||||
- `PlayerPreLoginEvent->setKickFlag()` (previously `setKickReason()`) now accepts `Translatable|string $disconnectReason, Translatable|string|null $disconnectScreenMessage = null` instead of `Translatable|string $message`
|
||||
- `PlayerPreLoginEvent->setKickReason()` now accepts `Translatable|string` for the `$message` parameter instead of `string`
|
||||
- `PlayerQuitEvent->getQuitReason()` now returns `Translatable|string` instead of `string`
|
||||
- The following API methods have been removed:
|
||||
- `PlayerChatEvent->getFormat()` (use `PlayerChatEvent->getChatFormatter()` instead)
|
||||
- `PlayerChatEvent->setFormat()` (use `PlayerChatEvent->setChatFormatter()` instead)
|
||||
- `PlayerDuplicateLoginEvent->getDisconnectMessage()` - replaced by `getDisconnectReason()` and `getDisconnectScreenMessage()`
|
||||
- `PlayerDuplicateLoginEvent->setDisconnectMessage()` - replaced by `setDisconnectReason()` and `setDisconnectScreenMessage()`
|
||||
- `PlayerKickEvent->getReason()` - replaced by `getDisconnectReason()` and `getDisconnectScreenMessage()`
|
||||
- `PlayerKickEvent->setReason()` - replaced by `setDisconnectReason()` and `setDisconnectScreenMessage()`
|
||||
- The following new API methods have been added:
|
||||
- `public PlayerChatEvent->getChatFormatter() : \pocketmine\player\chat\ChatFormatter` - returns the chat formatter to be used for this event
|
||||
- `public PlayerChatEvent->setChatFormatter(\pocketmine\player\chat\ChatFormatter $formatter) : void` - sets the chat formatter to be used for this event
|
||||
- `public PlayerDeathEvent->getDeathScreenMessage() : Translatable|string` - returns the message to be displayed on the death screen
|
||||
- `public PlayerDeathEvent->setDeathScreenMessage(Translatable|string $deathScreenMessage) : void` - sets the message to be displayed on the death screen
|
||||
- `public PlayerDuplicateLoginEvent->getDisconnectReason() : Translatable|string` - returns the reason for the disconnection displayed in the console and server log
|
||||
- `public PlayerDuplicateLoginEvent->getDisconnectScreenMessage() : Translatable|string|null` - returns the message to be displayed on the disconnect screen (the message in `getDisconnectReason()` is used if null is returned)
|
||||
- `public PlayerDuplicateLoginEvent->setDisconnectReason(Translatable|string $disconnectReason) : void` - sets the reason for the disconnection displayed in the console and server log
|
||||
- `public PlayerDuplicateLoginEvent->setDisconnectScreenMessage(Translatable|string|null $disconnectScreenMessage) : void` - sets the message to be displayed on the disconnect screen (the message in `setDisconnectReason()` is used if null is passed)
|
||||
- `public PlayerKickEvent->getDisconnectReason() : Translatable|string` - returns the reason for the disconnection displayed in the console and server log
|
||||
- `public PlayerKickEvent->getDisconnectScreenMessage() : Translatable|string|null` - returns the message to be displayed on the disconnect screen (the message in `getDisconnectReason()` is used if null is returned)
|
||||
- `public PlayerKickEvent->setDisconnectReason(Translatable|string $disconnectReason) : void` - sets the reason for the disconnection displayed in the console and server log
|
||||
- `public PlayerKickEvent->setDisconnectScreenMessage(Translatable|string|null $disconnectScreenMessage) : void` - sets the message to be displayed on the disconnect screen (the message in `setDisconnectReason()` is used if null is passed)
|
||||
- `public PlayerPreLoginEvent->getDisconnectScreenMessage(int $flag) : Translatable|string|null` - returns the message to be displayed on the disconnect screen for the specified kick flag, if set
|
||||
- `public PlayerPreLoginEvent->getFinalDisconnectScreenMessage() : Translatable|string|null` - returns the message to be displayed on the disconnect screen, taking into account the kick flags set
|
||||
- The following classes have inheritance changes:
|
||||
- `PlayerPreLoginEvent` no longer implements `Cancellable`. This caused unexpected behaviour for most plugin devs due to default-ignoring cancelled events, forcing people to usually have to use `@handleCancelled` to handle the event when they wanted to use it.
|
||||
- The following API constants have been renamed:
|
||||
- `PlayerPreLoginEvent::KICK_REASON_BANNED` -> `PlayerPreLoginEvent::KICK_FLAG_BANNED`
|
||||
- `PlayerPreLoginEvent::KICK_REASON_PLUGIN` -> `PlayerPreLoginEvent::KICK_FLAG_PLUGIN`
|
||||
- `PlayerPreLoginEvent::KICK_REASON_PRIORITY` -> `PlayerPreLoginEvent::KICK_FLAG_PRIORITY`
|
||||
- `PlayerPreLoginEvent::KICK_REASON_SERVER_FULL` -> `PlayerPreLoginEvent::KICK_FLAG_SERVER_FULL`
|
||||
- `PlayerPreLoginEvent::KICK_REASON_SERVER_WHITELISTED` -> `PlayerPreLoginEvent::KICK_FLAG_SERVER_WHITELISTED`
|
||||
- The following API methods have been renamed:
|
||||
- `PlayerPreLoginEvent->clearAllKickReasons()` -> `PlayerPreLoginEvent->clearAllKickFlags()`
|
||||
- `PlayerPreLoginEvent->clearKickReason()` -> `PlayerPreLoginEvent->clearKickFlag()`
|
||||
- `PlayerPreLoginEvent->getFinalKickMessage()` -> `PlayerPreLoginEvent->getFinalDisconnectReason()` (now used for logs only, if a disconnect screen message is set for the highest priority flag)
|
||||
- `PlayerPreLoginEvent->getKickMessage()` -> `PlayerPreLoginEvent->getDisconnectReason()` (now used for logs only, if a disconnect screen message is set for the flag)
|
||||
- `PlayerPreLoginEvent->getKickReasons()` -> `PlayerPreLoginEvent->getKickFlags()`
|
||||
- `PlayerPreLoginEvent->isKickReasonSet()` -> `PlayerPreLoginEvent->isKickFlagSet()`
|
||||
- `PlayerPreLoginEvent->setKickReason()` -> `PlayerPreLoginEvent->setKickFlag()`
|
||||
|
||||
### `pocketmine\item`
|
||||
#### Highlights
|
||||
- `ItemFactory` has been removed. Vanilla item registration is now done via `VanillaItems`.
|
||||
- To get an item at runtime, e.g. iron ingot, use `VanillaItems::IRON_INGOT()`
|
||||
- To get a block as an item, e.g. stone, use `VanillaBlocks::STONE()->asItem()`
|
||||
- To load an item from legacy ID and meta:
|
||||
1. Use `GlobalItemDataHandlers::getUpgrader()->upgradeItemTypeDataInt()` to convert the legacy ID and meta to `SavedItemStackData`
|
||||
2. Pass the itemstack data to `GlobalItemDataHandlers::getDeserializer()` to get an `Item` instance
|
||||
- Items no longer use internal Minecraft string IDs and metadata to identify themselves. All APIs associated with legacy
|
||||
IDs and/or meta have been removed.
|
||||
- A new set of runtime item IDs generated from `VanillaItems` is now used to identify item types. These IDs are defined
|
||||
in `ItemTypeIds`.
|
||||
- These new IDs are primarily intended for type comparison purposes.
|
||||
- Item type IDs are used at **runtime only**. They should **NOT** be stored in configs or databases, as they are not
|
||||
guaranteed to remain the same between versions.
|
||||
- In some cases, items may have additional "type data" which provides extra type information about an item. This
|
||||
replaces item metadata in some cases.
|
||||
- Type data may be used to store dynamic type information such as dye colour, potion type, etc.
|
||||
- Items must have the same type ID **and** type data in order to be stackable.
|
||||
- Blocks, when represented as items:
|
||||
- retain their block type data, but not state data (for example, different colours of concrete don't stack, but things
|
||||
like facing don't affect stackability)
|
||||
- use the negative of their block type ID (e.g. a block with type ID `1` will have an item type ID of `-1`).
|
||||
- Durable items (e.g. tools, armour) now use NBT `Damage` tag to store durability (like Minecraft 1.13+), instead of
|
||||
legacy meta values.
|
||||
- `&$returnedItems` reference parameter is now used in some places (e.g. `onInteractBlock()`) to enable actions to return items to players without caring about whether they are in creative or anything else.
|
||||
- This eliminates boilerplate code of deciding whether to set the player's held item or not, as well as automatically dropping any overflow items that don't fit into the inventory.
|
||||
- This is used for things like filling/emptying buckets and bottles, and equipping armor.
|
||||
- Blocks which previously had separate items, such as mob heads and beds, no longer do. Their item form can be acquired using `Block->asItem()` in the same way as every other block. This is facilitated by the new serializer system.
|
||||
|
||||
#### Implementing new items
|
||||
##### In a plugin
|
||||
This follows a similar process to registering blocks.
|
||||
|
||||
1. Get a new type ID using `ItemTypeIds::newId()` - _you'll want to keep this in a property somewhere if you want to
|
||||
compare using `getTypeId()` later_
|
||||
2. Set up the item type somewhere - _this can be anywhere you like, e.g. a plugin main class property, but using
|
||||
a `RegistryTrait` class is recommended - you'll need this later to create new instances of the item_
|
||||
3. Register a deserializer in `ItemDeserializer` - _needed for the item to be recognized when loaded from disk_
|
||||
4. Register a serializer in `ItemSerializer` - _needed for the item to be saved to disk, and to be sent over the
|
||||
network_
|
||||
5. Optionally, register a string alias for the item in `StringToItemParser` - _so that it can be given via `/give`_
|
||||
|
||||
To see a demo of how to do this in a plugin, see [this example plugin](https://github.com/pmmp/RegisterBlocksDemoPM5).
|
||||
|
||||
Again, it's acknowledged this is rather more cumbersome than it should be, but this is an ongoing process.
|
||||
|
||||
##### As a PocketMine-MP core contribution
|
||||
|
||||
To register a new vanilla item into the core, the process is slightly different:
|
||||
|
||||
1. Instead of using `ItemTypeIds::newId()`, add a new constant for the block to `ItemTypeIds`
|
||||
2. Register the new item in `VanillaItems`
|
||||
3. Follow steps 3 onwards from the plugin instructions above
|
||||
|
||||
#### Change list
|
||||
- `Item` is no longer `json_encode()`-able.
|
||||
- The original purpose of this was to allow items to be serialized to JSON for crafting data generated from `CraftingDataPacket`. Due to changes in the generation methodology, bypassing `Item`s entirely, this is no longer necessary.
|
||||
- In addition, `jsonSerialize()` required the item to know about the method by which it will be serialized (since there is no way to inject context), creating a cyclic dependency between the `Item` implementation and its serialization method.
|
||||
- It's relatively easy to write a replacement method to encode items to JSON as you desire.
|
||||
- `Item::legacyJsonDeserialize()` (previously `Item::jsonDeserialize()`) is retained to allow loading legacy data, although it may be removed in the future.
|
||||
- The following classes have been removed:
|
||||
- `Bed`
|
||||
- `ItemFactory`
|
||||
- `ItemIds`
|
||||
- `Skull`
|
||||
- The following classes have been added:
|
||||
- `BoatType` - enum of all boat types
|
||||
- `CoralFan`
|
||||
- `HoneyBottle`
|
||||
- `MedicineType`
|
||||
- `Medicine`
|
||||
- `Spyglass`
|
||||
- `SuspiciousStewType`
|
||||
- `SuspiciousStew`
|
||||
- The following API methods have been added:
|
||||
- `protected Item->describeState(RuntimeDataDescriber $w) : void`
|
||||
- `public Armor->clearCustomColor() : $this` - clears the custom color of an armor item
|
||||
- `public ArmorTypeInfo->getToughness() : int`
|
||||
- `public ArmorTypeInfo->isFireProof() : bool`
|
||||
- `public Boat->getType() : BoatType`
|
||||
- `public Dye->setColor(\pocketmine\block\utils\DyeColor $color) : $this`
|
||||
- `public Item->getStateId() : int` - returns a runtime numeric state ID for comparisons including information such as coral type, dye color, etc. - DO NOT save this to disk or databases
|
||||
- `public Item->getTypeId() : int` - returns a runtime numeric type ID for comparisons - DO NOT save this to disk or databases
|
||||
- `public Item->isFireProof() : bool`
|
||||
- `public ItemIdentifer->getTypeId() : int`
|
||||
- `public Potion->setType(PotionType $type) : $this`
|
||||
- `public SplashPotion->setType(PotionType $type) : $this`
|
||||
- `public StringToItemParser->lookupAliases(Item $item) : list<string>` - returns a list of all registered aliases for the given item
|
||||
- `public StringToItemParser->lookupBlockAliases(Block $block) : list<string>` - returns a list of all registered aliases for the given block
|
||||
- `public static ItemIdentifier::fromBlock(Block $block) : self`
|
||||
- The following API methods have been removed:
|
||||
- `Boat->getWoodType()`
|
||||
- `Item->getId()` - for type comparisons, use `Item->getTypeId()` instead
|
||||
- `Item->getMeta()` - use the item's specific API methods to compare information such as colour, potion type etc.
|
||||
- `Item->hasAnyDamageValue()` - for meta wildcard recipe ingredients, use `pocketmine\crafting\MetaWildcardRecipeIngredient` instead
|
||||
- `ItemIdentifier->getId()`
|
||||
- `ItemIdentifier->getMeta()`
|
||||
- The following API methods have been renamed:
|
||||
- `Item::jsonDeserialize()` -> `Item::legacyJsonDeserialize()`
|
||||
- The following API methods have signature changes:
|
||||
- `ArmorTypeInfo->__construct()` now accepts optional parameters `int $toughness` and `bool $fireProof`
|
||||
- `BoatType::__construct()` now accepts `BoatType $boatType` instead of `TreeType $woodType`.
|
||||
- `Item->onAttackEntity()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onClickAir()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onDestroyBlock()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onInteractBlock()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `Item->onReleaseUsing()` now accepts `array<Item> &$returnedItems` reference parameter.
|
||||
- `ItemIdentifier->__construct()` no longer accepts a `$variant` parameter, and now expects an item type ID for the ID parameter
|
||||
- `LegacyStringToItemParser->addMapping()` now accepts a string for ID, instead of an integer
|
||||
- The following API methods have behaviour changes:
|
||||
- `Item->equals()`'s `$checkDamage` parameter is now ignored, as tool damage is now stored as an NBT tag. This parameter wasn't removed due to being followed by a second `bool` parameter, which would potentially end up in the wrong place and silently cause bugs in updated plugins.
|
||||
- `Item->equals()`'s `$checkTags` parameter will now cause tool and armor damage to be checked if true.
|
||||
- The following enums have new members:
|
||||
- `ToolTier` has new member `NETHERITE`
|
||||
|
||||
### `pocketmine\network`
|
||||
- The following API methods have changed signatures:
|
||||
- `NetworkSessionManager->close()` now accepts an additional `Translatable|string|null $disconnectScreenMessage` parameter.
|
||||
- The following API methods have changed signatures:
|
||||
- `query\QueryInfo->getPlayerList()` now returns `list<string>` instead of `list<Player>`
|
||||
- `query\QueryInfo->setPlayerList()` now accepts `list<string>` instead of `list<Player>`
|
||||
|
||||
### `pocketmine\player`
|
||||
- The following API methods have changed signatures:
|
||||
- `Player->disconnect()` now accepts `Translatable|string` for `$reason` instead of `string` (to allow localized disconnect messages)
|
||||
- `Player->disconnect()` now accepts an additional `Translatable|string|null $disconnectScreenMessage` parameter, which is the message to be displayed on the disconnect screen (the message in `$reason` is used if null is passed)
|
||||
- `Player->kick()` now accepts `Translatable|string` for `$reason` instead of `string` (to allow localized kick messages)
|
||||
- `Player->kick()` now accepts an additional `Translatable|string|null $disconnectScreenMessage` parameter, which is the message to be displayed on the disconnect screen (the message in `$reason` is used if null is passed)
|
||||
- `Player->sendJukeboxPopup()` now accepts `Translatable|string` instead of `string, string[]`
|
||||
- The following classes have been removed:
|
||||
- `PlayerChunkLoader` - deprecated in 4.19.0 (this was technically internal, but never marked as such)
|
||||
|
||||
### `pocketmine\player\chat`
|
||||
- The following new classes have been added:
|
||||
- `ChatFormatter` - interface implemented by chat formatters - this is far more powerful than the old API
|
||||
- `LegacyRawChatFormatter` - implements the same behaviour previously used by `PlayerChatEvent->setFormat()`
|
||||
- `StandardChatFormatter` - formats chat messages in the vanilla Minecraft style
|
||||
|
||||
### `pocketmine\scheduler`
|
||||
- `AsyncTask->setResult()` now works with thread-safe objects. This was previously not possible due to limitations in the `pthreads` extension.
|
||||
|
||||
### `pocketmine\world`
|
||||
- The following API methods have been added:
|
||||
- `public World->setDisplayName(string $name) : void`
|
||||
- The following API methods have changed signatures:
|
||||
- `Explosion->__construct()` has the `$size` parameter renamed to `$radius`
|
||||
- The following public properties have been renamed:
|
||||
- `Explosion->size` -> `Explosion->radius`
|
||||
|
||||
### `pocketmine\world\format`
|
||||
- Chunks are now considered dirty (modified) by default, unless loaded from a `WorldProvider` by `World`. Previously, chunks were considered unmodified by default, which allowed several pathways to bugs.
|
||||
- The following classes have been added:
|
||||
- `io\GlobalBlockStateHandlers` - gives access to block data serializer, deserializer, and upgraders
|
||||
- `io\GlobalItemDataHandlers` - gives access to item data serializer, deserializer, and upgraders
|
||||
- `io\LoadedChunkData` - represents a chunk loaded from disk, along with information such as whether the chunk was upgraded and what fixes it requires
|
||||
- The following new API methods have been added:
|
||||
- `public SubChunk->getBiomeArray() : PalettedBlockArray`
|
||||
- The following classes have been removed:
|
||||
- `BiomeArray` - `PalettedBlockArray` is now used for 3D biome data
|
||||
- The following API methods have changed signatures:
|
||||
- `Chunk->getBiomeId()` now accepts `int $x, int $y, int $z` instead of `int $x, int $z`
|
||||
- `Chunk->setBiomeId()` now accepts `int $x, int $y, int $z` instead of `int $x, int $z`
|
||||
- `Chunk->__construct()` no longer accepts `BiomeArray` as a parameter (contained in each subchunk instead)
|
||||
- `SubChunk->__construct()` now accepts `int $emptyBlockId, list<PalettedBlockArray> $blockLayers, PalettedBlockArray $biomes, ?LightArray $blockLight, ?LightArray $skyLight` instead of `int, list<PalettedBlockArray>, ?LightArray, ?LightArray`
|
||||
- `io\WorldProvider->loadChunk()` now returns `LoadedChunkData` instead of `ChunkData`
|
||||
- `io\WorldProvider->getAllChunks()` now yields `LoadedChunkData` instead of `ChunkData`
|
||||
- `io\ChunkData->__construct()` now accepts `array<int, SubChunk>, bool $populated` instead of `Chunk $chunk`
|
||||
- The following API methods have been renamed:
|
||||
- `Chunk->getFullBlock()` -> `Chunk->getBlockStateId()`
|
||||
- `Chunk->setFullBlock()` -> `Chunk->setBlockStateId()`
|
||||
- `SubChunk->getFullBlock()` -> `SubChunk->getBlockStateId()`
|
||||
- `SubChunk->setFullBlock()` -> `SubChunk->setBlockStateId()`
|
||||
- The following API interface requirements have been added:
|
||||
- `public io\data\WorldData->setDisplayName(string $value) : void`
|
||||
|
||||
### `pocketmine\world\generator\object`
|
||||
- The following API methods have been removed:
|
||||
- `TreeType::fromMagicNumber()`
|
||||
- `TreeType->getMagicNumber()`
|
||||
|
||||
### `pocketmine\world\sound`
|
||||
- The following classes have been added:
|
||||
- `CopperWaxApplySound`
|
||||
- `CopperWaxRemoveSound`
|
||||
- `DyeUseSound`
|
||||
- `InkSacUseSound`
|
||||
- The following enums have new members:
|
||||
- `NoteInstrument` has new members `BELL`, `FLUTE`, `CHIME`, `XYLOPHONE`, `IRON_XYLOPHONE`, `COW_BELL`, `DIDGERIDOO`, `BIT`, `BANJO`, `PLING`
|
||||
- The following API methods have been removed:
|
||||
- `NoteInstrument::fromMagicNumber()`
|
||||
- `NoteInstrument->getMagicNumber()`
|
||||
|
||||
## Internals
|
||||
- All external usages of `KnownTranslationKeys` are now removed. All localized messages are now sent using `Translatable` objects (usually from `KnownTranslationFactory`).
|
||||
- All usages of NBT keys now use class constants instead of hardcoded strings (except for an occasional overlooked one).
|
||||
- Built-in commands now declare their names inside the class constructor, rather than accepting them as parameters. This improves code consistency.
|
||||
- Commands now use an array for permissions internally, instead of a string separated by `;`.
|
||||
- Make use of `Item->canStackWith()` instead of `Item->equals()` wherever possible, to make the code more readable.
|
||||
- Moved command timings to `Timings`.
|
||||
- Overriding of serializers and deserializers of items and blocks is now consistently disallowed. Since overriding stuff is non-cooperative, it doesn't make any sense in plugins, which must coexist with other plugins. If you want to modify the functionality of built-in stuff, you have several alternative options:
|
||||
- Use existing API (e.g. events, API methods) - most uses of overrides in PM4 and earlier were abuses that could have been done with events
|
||||
- Submit feature proposals or pull requests for new API to be added (e.g. new events)
|
||||
- Register completely custom items, and reuse behaviour from the item you want to mimic
|
||||
- Fork PocketMine-MP and alter the code directly - this way your plugins aren't pretending to be cooperative with other plugins
|
||||
- `level.dat`, block, item, entity, tile and chunk data are now tagged with a version ID as per `VersionInfo::WORLD_DATA_VERSION`. This allows the server to apply fixes to older worlds if necessary.
|
||||
- Protocol creative inventory entries are now cached in `CreativeInventoryCache` to improve performance of initial join and game mode changes.
|
||||
- Singletons in the `pocketmine\network\mcpe\convert` package have been centralized under `TypeConverter`. In the future, this will be injected where needed, allowing different converters to be used for different sessions (useful for multiversion).
|
||||
- `BlockStateDictionary` memory usage is now reduced from 9 MB to 3.5 MB using various techniques, including string reuse and binary-encoded states.
|
||||
- `NetworkSession` disconnect APIs now accept `Translatable|string` instead of `string` to allow localized disconnect messages.
|
||||
- `NetworkSession` disconnect methods have been altered to allow specifying a different disconnect reason and disconnection screen message.
|
||||
- `RuntimeBlockMapping` has been renamed to `BlockTranslator`.
|
||||
|
||||
# 5.0.1
|
||||
Released 3rd June 2023.
|
||||
|
||||
## Changes
|
||||
- [`ext-pmmpthread` version 6.0.1](https://github.com/pmmp/ext-pmmpthread/releases/tag/6.0.1) is now required (for bug fixes).
|
||||
|
||||
## Fixes
|
||||
- Fixed server crash when breaking blocks placed in the same session (mishandled default block states).
|
||||
- Fixed spore blossoms not dropping when broken.
|
||||
- Fixed jukebox music not stopping when destroyed by an explosion.
|
||||
|
||||
## Documentation
|
||||
- Added documentation for `BlockSpreadEvent->__construct()` parameters.
|
52
changelogs/5.1.md
Normal file
52
changelogs/5.1.md
Normal file
@ -0,0 +1,52 @@
|
||||
# 5.1.0
|
||||
Released 7th June 2023.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.0**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.0.
|
||||
|
||||
**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` 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.20.0.
|
||||
- Removed support for older versions.
|
||||
|
||||
# 5.1.1
|
||||
Released 7th June 2023.
|
||||
|
||||
## Fixes
|
||||
- Fixed blockstates being saved with the wrong version ID for 1.20.0.
|
||||
|
||||
# 5.1.2
|
||||
Released 9th June 2023.
|
||||
|
||||
**This release includes changes from the following releases:**
|
||||
- [4.22.1](https://github.com/pmmp/PocketMine-MP/blob/4.22.1/changelogs/4.22.md#4221) - Teleportation client bug workarounds
|
||||
|
||||
This release contains no other changes.
|
||||
|
||||
# 5.1.3
|
||||
Released 1st July 2023.
|
||||
|
||||
**This release includes changes from the following releases:**
|
||||
- [4.22.2](https://github.com/pmmp/PocketMine-MP/blob/4.22.2/changelogs/4.22.md#4222) - Authentication time bomb fix
|
||||
|
||||
## General
|
||||
- Updated logos to new RGB-style logo. Thanks to @MrCakeSlayer and @HBIDamian for their efforts.
|
||||
- Improved error messages generated by the world system when some version tags are missing from `level.dat` in Bedrock worlds.
|
||||
- Outsourced Composer dependencies now only receive patch updates automatically (pinned using the `~` constraint).
|
||||
- Minor and major updates now require manually updating `composer.json`, to ensure that the plugin API is not broken by libraries getting randomly updated from one patch release to the next.
|
||||
|
||||
## Documentation
|
||||
- Updated doc comment for `Player->setGamemode()` to remove outdated information.
|
||||
- Added documentation for the `$clickVector` parameter of `Block->onInteract()` to specify that it is relative to the block's position.
|
||||
- Added missing `@required` tag for `BlockStateUpgradeSchemaModelBlockRemap->newState`.
|
||||
|
||||
## Fixes
|
||||
- Fixed blue candles not appearing in the creative inventory.
|
||||
- Fixed server crash when block-picking candle cakes.
|
||||
- `World->useItemOn()` now ensures that the `$clickVector` components are always in the range of 0-1. Previously, any invalid values were accepted, potentially leading to a crash.
|
79
changelogs/5.2.md
Normal file
79
changelogs/5.2.md
Normal file
@ -0,0 +1,79 @@
|
||||
# 5.2.0
|
||||
Released 4th July 2023.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.0**
|
||||
|
||||
This is a minor technical update, including changes to AsyncTask error handling and support for BedrockBlockUpgradeSchema version 3.0.0.
|
||||
|
||||
**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.
|
||||
|
||||
## Core
|
||||
- [BedrockBlockUpgradeSchema version 3.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/3.0.0) is now supported.
|
||||
- [`ext-pmmpthread` version 6.0.4](https://github.com/pmmp/ext-pmmpthread/releases/tag/6.0.4) is now required (bug fixes required to support technical changes in this release).
|
||||
|
||||
## Performance
|
||||
- Improved performance of `AsyncPool->submitTask()` and `AsyncPool->submitTaskToWorker()`.
|
||||
- Added new timings for `AsyncTask->onProgressUpdate()` and `AsyncTask->onCompletion()`.
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Added the following new blocks:
|
||||
- Cherry Button
|
||||
- Cherry Door
|
||||
- Cherry Fence
|
||||
- Cherry Fence Gate
|
||||
- Cherry Leaves
|
||||
- Cherry Log
|
||||
- Cherry Planks
|
||||
- Cherry Pressure Plate
|
||||
- Cherry Sign
|
||||
- Cherry Slab
|
||||
- Cherry Stairs
|
||||
- Cherry Trapdoor
|
||||
- Cherry Wood
|
||||
- Glow Lichen
|
||||
- Piglin Head
|
||||
|
||||
## Tools
|
||||
- `generate-block-upgrade-schema.php` now supports generating schemas a la BedrockBlockUpgradeSchema version 3.0.0, using `newFlattenedName` to reduce schema size.
|
||||
- Improved property remapping detection in `generate-block-upgrade-schema.php`. It now detects related properties with more confidence (even when multiple properties were change), and no longer considers unrelated properties as mapped (e.g. `mapped_type` and `deprecated` in 1.9->1.10).
|
||||
|
||||
## API
|
||||
### `pocketmine\data\bedrock\block`
|
||||
- The following new API methods have been added:
|
||||
- `public BlockStateData->toVanillaNbt() : CompoundTag` - returns the NBT for the blockstate without any PMMP extra metadata (`toNbt()` will normally include a `PMMPDataVersion` tag).
|
||||
|
||||
### `pocketmine\data\runtime`
|
||||
- The following new API methods have been added:
|
||||
- `public RuntimeDataDescriber->facingFlags(list<int> $faces) : void`
|
||||
|
||||
### `pocketmine\scheduler`
|
||||
- `AsyncTask->onRun()` no longer tolerates uncaught exceptions.
|
||||
- This means that any uncaught exceptions thrown from `AsyncTask->onRun()` will now crash the worker thread, and by extension, the server.
|
||||
- This change makes it easier to debug errors by detecting them earlier.
|
||||
- The following API methods have been deprecated:
|
||||
- `AsyncTask->onError()`
|
||||
|
||||
## Internals
|
||||
- `AsyncTask->progressUpdates` is now lazily initialized when a task publishes a progress update.
|
||||
- This was previously not possible due to technical limitations of the `ext-pmmpthread` extension.
|
||||
- This change improves performance of `AsyncPool->submitTask()` and `AsyncPool->submitTaskToWorker()`, as well as reducing the amount of work needed to check for progress updates on tick.
|
||||
- Errors in `AsyncWorker` now cascade and crash the whole server.
|
||||
- This makes it easier to debug errors by detecting them earlier.
|
||||
- This includes all types of unexpected errors, such as OOM, uncaught exceptions, etc.
|
||||
- This change is not expected to affect normal server operation, as worker threads are not expected to crash under normal circumstances.
|
||||
- `AsyncTask::$threadLocalStorage` now uses a plain `array` instead of `ArrayObject`. The `ArrayObject` was a workaround for `ext-pthreads` to prevent thread-locals getting copied to the worker thread, and is no longer necessary.
|
||||
- Regenerated `pocketmine\data\bedrock\item\ItemTypeNames` for Bedrock 1.20 (BC breaking, some item names have changed).
|
||||
- Fixed `build/generate-item-type-names.php` not including some newer blockitems, such as doors and hanging signs.
|
||||
|
||||
# 5.2.1
|
||||
Released 11th July 2023.
|
||||
|
||||
**This release includes changes from the following releases:**
|
||||
- [4.22.3](https://github.com/pmmp/PocketMine-MP/blob/4.22.3/changelogs/4.22.md#4223) - Fixes for some crash issues
|
||||
|
||||
This release contains no other changes.
|
49
changelogs/5.3.md
Normal file
49
changelogs/5.3.md
Normal file
@ -0,0 +1,49 @@
|
||||
# 5.3.0
|
||||
Released 12th July 2023.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.10**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.10.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
## Interim releases
|
||||
If you're upgrading directly from 5.1.x to 5.3.x, please also read the following changelogs, as the interim releases contain important changes:
|
||||
- [5.2.0](https://github.com/pmmp/PocketMine-MP/blob/5.2.0/changelogs/5.2.md#520)
|
||||
|
||||
## Included releases
|
||||
**This release includes changes from the following releases:**
|
||||
- [4.23.0](https://github.com/pmmp/PocketMine-MP/blob/4.23.0/changelogs/4.23.md#4230) - Support for Minecraft: Bedrock Edition 1.20.10
|
||||
|
||||
## Internals
|
||||
- `BlockTypeNames`, `BlockStateNames`, `BlockStateStringValues` and `ItemTypeNames` in the `pocketmine\data\bedrock` package have BC-breaking changes to accommodate Bedrock 1.20.10.
|
||||
|
||||
# 5.3.1
|
||||
Released 14th July 2023.
|
||||
|
||||
## Included releases
|
||||
**This release includes changes from the following releases:**
|
||||
- [4.23.1](https://github.com/pmmp/PocketMine-MP/blob/4.23.1/changelogs/4.23.md#4231) - Security fixes
|
||||
|
||||
## General
|
||||
- Updated `build/php` submodule to pmmp/PHP-Binaries@e0c918d1379465964acefd562d9e48f87cfc2c9e.
|
||||
|
||||
# 5.3.2
|
||||
Released 18th July 2023.
|
||||
|
||||
## Included releases
|
||||
**This release includes changes from the following releases:**
|
||||
- [4.23.2](https://github.com/pmmp/PocketMine-MP/blob/4.23.2/changelogs/4.23.md#4232) - Fix for `sandboxId`-related login errors
|
||||
|
||||
## Documentation
|
||||
- Fixed documentation error in `StringToTParser`.
|
||||
|
||||
## Fixes
|
||||
- Fixed turtle helmet not being able to be unequipped.
|
||||
|
||||
## Internals
|
||||
- Armor pieces are no longer set back into the armor inventory if no change was made. This reduces the number of slot updates sent to clients, as well as avoiding unnecessary updates for armor pieces which have Unbreaking enchantments.
|
@ -22,7 +22,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": "^5.1",
|
||||
"ext-pmmpthread": "^6.0.4",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
@ -31,34 +31,32 @@
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~2.1.0+bedrock-1.19.80",
|
||||
"pocketmine/bedrock-data": "~2.2.0+bedrock-1.19.80",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.2.0+bedrock-1.19.80",
|
||||
"pocketmine/bedrock-protocol": "~21.0.0+bedrock-1.19.80",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"fgrosse/phpasn1": "~2.5.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.1.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-data": "~2.4.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.4.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-protocol": "~23.0.0+bedrock-1.20.10",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.3.0",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/locale-data": "~2.19.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/log-pthreads": "^0.5.0",
|
||||
"pocketmine/math": "^0.4.0",
|
||||
"pocketmine/nbt": "^0.3.2",
|
||||
"pocketmine/raklib": "^0.15.0",
|
||||
"pocketmine/raklib-ipc": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.4.0",
|
||||
"ramsey/uuid": "^4.1",
|
||||
"symfony/filesystem": "^5.4"
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"symfony/filesystem": "~6.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.15",
|
||||
"phpstan/phpstan": "1.10.16",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
"phpunit/phpunit": "^10.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
978
composer.lock
generated
978
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,7 @@ parameters:
|
||||
- tests/phpstan/stubs/JsonMapper.stub
|
||||
- tests/phpstan/stubs/leveldb.stub
|
||||
- tests/phpstan/stubs/phpasn1.stub
|
||||
- tests/phpstan/stubs/pthreads.stub
|
||||
- tests/phpstan/stubs/pmmpthread.stub
|
||||
reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings
|
||||
staticReflectionClassNamePatterns:
|
||||
- "#^COM$#"
|
||||
|
@ -314,9 +314,6 @@ class MemoryManager{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
if(!$property->isInitialized()){
|
||||
continue;
|
||||
}
|
||||
@ -440,9 +437,6 @@ class MemoryManager{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!$property->isPublic()){
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
if(!$property->isInitialized($object)){
|
||||
continue;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine {
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\thread\ThreadManager;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Process;
|
||||
@ -103,7 +104,7 @@ namespace pocketmine {
|
||||
"openssl" => "OpenSSL",
|
||||
"pcre" => "PCRE",
|
||||
"phar" => "Phar",
|
||||
"pthreads" => "pthreads",
|
||||
"pmmpthread" => "pmmpthread",
|
||||
"reflection" => "Reflection",
|
||||
"sockets" => "Sockets",
|
||||
"spl" => "SPL",
|
||||
@ -118,12 +119,9 @@ namespace pocketmine {
|
||||
}
|
||||
}
|
||||
|
||||
if(($pthreads_version = phpversion("pthreads")) !== false){
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
}
|
||||
if(version_compare($pthreads_version, "5.1.0") < 0 || version_compare($pthreads_version, "6.0.0") >= 0){
|
||||
$messages[] = "pthreads ^5.0.0 is required, while you have $pthreads_version.";
|
||||
if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
|
||||
if(version_compare($pmmpthread_version, "6.0.4") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
|
||||
$messages[] = "pmmpthread ^6.0.4 is required, while you have $pmmpthread_version.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,7 +328,7 @@ JIT_WARNING
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
*/
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader = new ThreadSafeClassLoader();
|
||||
$autoloader->register(false);
|
||||
|
||||
new Server($autoloader, $logger, $dataPath, $pluginPath);
|
||||
@ -338,7 +336,7 @@ JIT_WARNING
|
||||
$logger->info("Stopping other threads");
|
||||
|
||||
$killer = new ServerKiller(8);
|
||||
$killer->start(PTHREADS_INHERIT_NONE);
|
||||
$killer->start();
|
||||
usleep(10000); //Fixes ServerKiller not being able to start on single-core machines
|
||||
|
||||
if(ThreadManager::getInstance()->stopAll() > 0){
|
||||
|
@ -92,6 +92,8 @@ use pocketmine\resourcepacks\ResourcePackManager;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\stats\SendUsageTask;
|
||||
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\updater\UpdateChecker;
|
||||
@ -413,11 +415,11 @@ class Server{
|
||||
return $this->configGroup->getConfigString("motd", self::DEFAULT_SERVER_NAME);
|
||||
}
|
||||
|
||||
public function getLoader() : \DynamicClassLoader{
|
||||
public function getLoader() : ThreadSafeClassLoader{
|
||||
return $this->autoloader;
|
||||
}
|
||||
|
||||
public function getLogger() : \AttachableThreadedLogger{
|
||||
public function getLogger() : AttachableThreadSafeLogger{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
@ -759,8 +761,8 @@ class Server{
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
private \DynamicClassLoader $autoloader,
|
||||
private \AttachableThreadedLogger $logger,
|
||||
private ThreadSafeClassLoader $autoloader,
|
||||
private AttachableThreadSafeLogger $logger,
|
||||
string $dataPath,
|
||||
string $pluginPath
|
||||
){
|
||||
|
@ -31,9 +31,24 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.0.0-BETA3";
|
||||
public const BASE_VERSION = "5.3.2";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
/**
|
||||
* PocketMine-MP-specific version ID for world data. Used to determine what fixes need to be applied to old world
|
||||
* data (e.g. stuff saved wrongly by past versions).
|
||||
* This version supplements the Minecraft vanilla world version.
|
||||
*
|
||||
* This should be bumped if any **non-Mojang** BC-breaking change or bug fix is made to world save data of any kind
|
||||
* (entities, tiles, blocks, biomes etc.). For example, if PM accidentally saved a block with its facing value
|
||||
* swapped, we would bump this, but not if Mojang did the same change.
|
||||
*/
|
||||
public const WORLD_DATA_VERSION = 1;
|
||||
/**
|
||||
* Name of the NBT tag used to store the world data version.
|
||||
*/
|
||||
public const TAG_WORLD_DATA_VERSION = "PMMPDataVersion"; //TAG_Long
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
|
@ -58,6 +58,11 @@ class Block{
|
||||
public const INTERNAL_STATE_DATA_BITS = 8;
|
||||
public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (BlockTypeIds::AIR & self::INTERNAL_STATE_DATA_MASK);
|
||||
|
||||
protected BlockIdentifier $idInfo;
|
||||
protected string $fallbackName;
|
||||
protected BlockTypeInfo $typeInfo;
|
||||
@ -68,7 +73,8 @@ class Block{
|
||||
|
||||
private int $requiredBlockItemStateDataBits;
|
||||
private int $requiredBlockOnlyStateDataBits;
|
||||
private int $defaultBlockOnlyStateData;
|
||||
|
||||
private Block $defaultState;
|
||||
|
||||
/**
|
||||
* @param string $name English name of the block type (TODO: implement translations)
|
||||
@ -87,7 +93,9 @@ class Block{
|
||||
$this->describeBlockOnlyState($calculator);
|
||||
$this->requiredBlockOnlyStateDataBits = $calculator->getBitsUsed();
|
||||
|
||||
$this->defaultBlockOnlyStateData = $this->encodeBlockOnlyState();
|
||||
$defaultState = clone $this;
|
||||
$this->defaultState = $defaultState;
|
||||
$defaultState->defaultState = $defaultState;
|
||||
}
|
||||
|
||||
public function __clone(){
|
||||
@ -140,7 +148,13 @@ class Block{
|
||||
* {@link RuntimeBlockStateRegistry::fromStateId()}.
|
||||
*/
|
||||
public function getStateId() : int{
|
||||
return ($this->getTypeId() << self::INTERNAL_STATE_DATA_BITS) | $this->encodeFullState();
|
||||
$typeId = $this->getTypeId();
|
||||
//TODO: this XOR mask improves hashtable distribution, but it's only effective if the number of unique block
|
||||
//type IDs is larger than the number of available state data bits. We should probably hash (e.g. using xxhash)
|
||||
//the type ID to create a better mask.
|
||||
//Alternatively, we could hash the whole state ID, but this is currently problematic, since we currently need
|
||||
//to be able to recover the state data from the state ID because of UnknownBlock.
|
||||
return ($typeId << self::INTERNAL_STATE_DATA_BITS) | ($this->encodeFullState() ^ ($typeId & self::INTERNAL_STATE_DATA_MASK));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,10 +196,11 @@ class Block{
|
||||
* Returns the block as an item.
|
||||
* Block-only state such as facing, powered/unpowered, open/closed, etc., is discarded.
|
||||
* Block-item state such as colour, wood type, etc. is preserved.
|
||||
* Complex state properties stored in the tile data (e.g. inventory) are discarded.
|
||||
*/
|
||||
public function asItem() : Item{
|
||||
$normalized = clone $this;
|
||||
$normalized->decodeBlockOnlyState($this->defaultBlockOnlyStateData);
|
||||
$normalized = clone $this->defaultState;
|
||||
$normalized->decodeBlockItemState($this->encodeBlockItemState());
|
||||
return new ItemBlock($normalized);
|
||||
}
|
||||
|
||||
@ -452,7 +467,8 @@ class Block{
|
||||
/**
|
||||
* Do actions when interacted by Item. Returns if it has done anything
|
||||
*
|
||||
* @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
|
||||
* @param Vector3 $clickVector Exact position where the click occurred, relative to the block's integer position
|
||||
* @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
|
||||
*/
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
return false;
|
||||
|
@ -24,11 +24,14 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
/**
|
||||
* Enum of all the block runtime IDs used by PocketMine-MP. These IDs are specific to PocketMine-MP and have no
|
||||
* relevance to any Minecraft vanilla things.
|
||||
* Every block in {@link VanillaBlocks} has a corresponding constant in this class. These constants can be used to
|
||||
* identify and compare block types efficiently using {@link Block::getTypeId()}.
|
||||
*
|
||||
* WARNING: DO NOT STORE THESE IDS. They can and will change without warning.
|
||||
* They should ONLY be used to IDENTIFY blocks at runtime.
|
||||
* Type ID is also used internally as part of block state ID, which is used to store blocks and their simple properties
|
||||
* in a memory-efficient way in chunks at runtime.
|
||||
*
|
||||
* WARNING: These are NOT a replacement for Minecraft legacy IDs. Do **NOT** hardcode their values, or store them in
|
||||
* configs or databases. They will change without warning.
|
||||
*/
|
||||
final class BlockTypeIds{
|
||||
|
||||
@ -714,8 +717,24 @@ final class BlockTypeIds{
|
||||
public const FLOWERING_AZALEA_LEAVES = 10687;
|
||||
public const REINFORCED_DEEPSLATE = 10688;
|
||||
public const CAVE_VINES = 10689;
|
||||
public const GLOW_LICHEN = 10690;
|
||||
public const CHERRY_BUTTON = 10691;
|
||||
public const CHERRY_DOOR = 10692;
|
||||
public const CHERRY_FENCE = 10693;
|
||||
public const CHERRY_FENCE_GATE = 10694;
|
||||
public const CHERRY_LEAVES = 10695;
|
||||
public const CHERRY_LOG = 10696;
|
||||
public const CHERRY_PLANKS = 10697;
|
||||
public const CHERRY_PRESSURE_PLATE = 10698;
|
||||
public const CHERRY_SAPLING = 10699;
|
||||
public const CHERRY_SIGN = 10700;
|
||||
public const CHERRY_SLAB = 10701;
|
||||
public const CHERRY_STAIRS = 10702;
|
||||
public const CHERRY_TRAPDOOR = 10703;
|
||||
public const CHERRY_WALL_SIGN = 10704;
|
||||
public const CHERRY_WOOD = 10705;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10690;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10706;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
|
@ -64,7 +64,7 @@ class CakeWithCandle extends BaseCake{
|
||||
}
|
||||
|
||||
public function getPickedItem(bool $addUserData = false) : Item{
|
||||
return VanillaBlocks::CAKE()->getPickedItem($addUserData);
|
||||
return VanillaBlocks::CAKE()->asItem();
|
||||
}
|
||||
|
||||
public function getResidue() : Block{
|
||||
|
284
src/block/GlowLichen.php
Normal file
284
src/block/GlowLichen.php
Normal file
@ -0,0 +1,284 @@
|
||||
<?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\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\World;
|
||||
use function array_key_first;
|
||||
use function count;
|
||||
use function shuffle;
|
||||
|
||||
class GlowLichen extends Transparent{
|
||||
|
||||
/** @var int[] */
|
||||
protected array $faces = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->facingFlags($this->faces);
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
public function getFaces() : array{ return $this->faces; }
|
||||
|
||||
public function hasFace(int $face) : bool{
|
||||
return isset($this->faces[$face]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
* @return $this
|
||||
*/
|
||||
public function setFaces(array $faces) : self{
|
||||
$uniqueFaces = [];
|
||||
foreach($faces as $face){
|
||||
Facing::validate($face);
|
||||
$uniqueFaces[$face] = $face;
|
||||
}
|
||||
$this->faces = $uniqueFaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setFace(int $face, bool $value) : self{
|
||||
Facing::validate($face);
|
||||
if($value){
|
||||
$this->faces[$face] = $face;
|
||||
}else{
|
||||
unset($this->faces[$face]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return 7;
|
||||
}
|
||||
|
||||
public function isSolid() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function canBeReplaced() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->faces = $blockReplace instanceof GlowLichen ? $blockReplace->faces : [];
|
||||
$availableFaces = $this->getAvailableFaces();
|
||||
|
||||
if(count($availableFaces) === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
$opposite = Facing::opposite($face);
|
||||
$placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces);
|
||||
$this->faces[$placedFace] = $placedFace;
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$changed = false;
|
||||
|
||||
foreach($this->faces as $face){
|
||||
if(!$this->getSide($face)->getSupportType(Facing::opposite($face))->equals(SupportType::FULL())){
|
||||
unset($this->faces[$face]);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$world = $this->position->getWorld();
|
||||
if(count($this->faces) === 0){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
$world->setBlock($this->position, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{
|
||||
if($replace instanceof self && $replace->hasSameTypeId($this)){
|
||||
if($replace->hasFace($spreadFace)){
|
||||
return null;
|
||||
}
|
||||
$result = $replace;
|
||||
}elseif($replace->getTypeId() === BlockTypeIds::AIR){
|
||||
$result = VanillaBlocks::GLOW_LICHEN();
|
||||
}else{
|
||||
//TODO: if this is a water block, generate a waterlogged block
|
||||
return null;
|
||||
}
|
||||
return $result->setFace($spreadFace, true);
|
||||
}
|
||||
|
||||
private function spread(World $world, Vector3 $replacePos, int $spreadFace) : bool{
|
||||
$supportBlock = $world->getBlock($replacePos->getSide($spreadFace));
|
||||
$supportFace = Facing::opposite($spreadFace);
|
||||
|
||||
if(!$supportBlock->getSupportType($supportFace)->equals(SupportType::FULL())){
|
||||
return false;
|
||||
}
|
||||
|
||||
$replacedBlock = $supportBlock->getSide($supportFace);
|
||||
$replacementBlock = $this->getSpreadBlock($replacedBlock, Facing::opposite($supportFace));
|
||||
if($replacementBlock === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
$ev = new BlockSpreadEvent($replacedBlock, $this, $replacementBlock);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$world->setBlock($replacedBlock->getPosition(), $ev->getNewState());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return \Generator<int, int, void, void>
|
||||
*/
|
||||
private static function getShuffledSpreadFaces(int $sourceFace) : \Generator{
|
||||
$skipAxis = Facing::axis($sourceFace);
|
||||
|
||||
$faces = Facing::ALL;
|
||||
shuffle($faces);
|
||||
foreach($faces as $spreadFace){
|
||||
if(Facing::axis($spreadFace) !== $skipAxis){
|
||||
yield $spreadFace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function spreadAroundSupport(int $sourceFace) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
$supportPos = $this->position->getSide($sourceFace);
|
||||
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
|
||||
$replacePos = $supportPos->getSide($spreadFace);
|
||||
if($this->spread($world, $replacePos, Facing::opposite($spreadFace))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function spreadAdjacentToSupport(int $sourceFace) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
|
||||
$replacePos = $this->position->getSide($spreadFace);
|
||||
if($this->spread($world, $replacePos, $sourceFace)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function spreadWithinSelf(int $sourceFace) : bool{
|
||||
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
|
||||
if(!$this->hasFace($spreadFace) && $this->spread($this->position->getWorld(), $this->position, $spreadFace)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer && count($this->faces) > 0){
|
||||
$shuffledFaces = $this->faces;
|
||||
shuffle($shuffledFaces);
|
||||
|
||||
$spreadMethods = [
|
||||
$this->spreadAroundSupport(...),
|
||||
$this->spreadAdjacentToSupport(...),
|
||||
$this->spreadWithinSelf(...),
|
||||
];
|
||||
shuffle($spreadMethods);
|
||||
|
||||
foreach($shuffledFaces as $sourceFace){
|
||||
foreach($spreadMethods as $spreadMethod){
|
||||
if($spreadMethod($sourceFace)){
|
||||
$item->pop();
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
if(($item->getBlockToolType() & BlockToolType::SHEARS) !== 0){
|
||||
return $this->getDropsForCompatibleTool($item);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
return 15;
|
||||
}
|
||||
|
||||
public function getFlammability() : int{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int> $faces
|
||||
*/
|
||||
private function getAvailableFaces() : array{
|
||||
$faces = [];
|
||||
foreach(Facing::ALL as $face){
|
||||
if(!$this->hasFace($face) && $this->getSide($face)->getSupportType(Facing::opposite($face))->equals(SupportType::FULL())){
|
||||
$faces[$face] = $face;
|
||||
}
|
||||
}
|
||||
return $faces;
|
||||
}
|
||||
}
|
@ -148,6 +148,7 @@ class Leaves extends Transparent{
|
||||
LeavesType::SPRUCE() => VanillaBlocks::SPRUCE_SAPLING(),
|
||||
LeavesType::MANGROVE(), //TODO: mangrove propagule
|
||||
LeavesType::AZALEA(), LeavesType::FLOWERING_AZALEA() => null, //TODO: azalea
|
||||
LeavesType::CHERRY() => null, //TODO: cherry
|
||||
default => throw new AssumptionFailedError("Unreachable")
|
||||
})?->asItem();
|
||||
if($sapling !== null){
|
||||
|
@ -23,8 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Skull as TileSkull;
|
||||
use pocketmine\block\utils\SkullType;
|
||||
use pocketmine\block\tile\MobHead as TileMobHead;
|
||||
use pocketmine\block\utils\MobHeadType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -35,22 +35,22 @@ use pocketmine\world\BlockTransaction;
|
||||
use function assert;
|
||||
use function floor;
|
||||
|
||||
class Skull extends Flowable{
|
||||
class MobHead extends Flowable{
|
||||
public const MIN_ROTATION = 0;
|
||||
public const MAX_ROTATION = 15;
|
||||
|
||||
protected SkullType $skullType;
|
||||
protected MobHeadType $mobHeadType;
|
||||
|
||||
protected int $facing = Facing::NORTH;
|
||||
protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->skullType = SkullType::SKELETON(); //TODO: this should be a parameter
|
||||
$this->mobHeadType = MobHeadType::SKELETON(); //TODO: this should be a parameter
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
$w->skullType($this->skullType);
|
||||
$w->mobHeadType($this->mobHeadType);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
@ -60,8 +60,8 @@ class Skull extends Flowable{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileSkull){
|
||||
$this->skullType = $tile->getSkullType();
|
||||
if($tile instanceof TileMobHead){
|
||||
$this->mobHeadType = $tile->getMobHeadType();
|
||||
$this->rotation = $tile->getRotation();
|
||||
}
|
||||
|
||||
@ -72,18 +72,18 @@ class Skull extends Flowable{
|
||||
parent::writeStateToWorld();
|
||||
//extra block properties storage hack
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
assert($tile instanceof TileSkull);
|
||||
assert($tile instanceof TileMobHead);
|
||||
$tile->setRotation($this->rotation);
|
||||
$tile->setSkullType($this->skullType);
|
||||
$tile->setMobHeadType($this->mobHeadType);
|
||||
}
|
||||
|
||||
public function getSkullType() : SkullType{
|
||||
return $this->skullType;
|
||||
public function getMobHeadType() : MobHeadType{
|
||||
return $this->mobHeadType;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setSkullType(SkullType $skullType) : self{
|
||||
$this->skullType = $skullType;
|
||||
public function setMobHeadType(MobHeadType $mobHeadType) : self{
|
||||
$this->mobHeadType = $mobHeadType;
|
||||
return $this;
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ class RuntimeBlockStateRegistry{
|
||||
$block = clone $this->fullList[$stateId];
|
||||
}else{
|
||||
$typeId = $stateId >> Block::INTERNAL_STATE_DATA_BITS;
|
||||
$stateData = $stateId & Block::INTERNAL_STATE_DATA_MASK;
|
||||
$stateData = ($stateId ^ $typeId) & Block::INTERNAL_STATE_DATA_MASK;
|
||||
$block = new UnknownBlock(new BID($typeId), new BlockTypeInfo(BreakInfo::instant()), $stateData);
|
||||
}
|
||||
|
||||
|
@ -49,8 +49,4 @@ final class SporeBlossom extends Flowable{
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ class UnknownBlock extends Transparent{
|
||||
private int $stateData;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, BlockTypeInfo $typeInfo, int $stateData){
|
||||
parent::__construct($idInfo, "Unknown", $typeInfo);
|
||||
$this->stateData = $stateData;
|
||||
parent::__construct($idInfo, "Unknown", $typeInfo);
|
||||
}
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
|
@ -48,11 +48,11 @@ use pocketmine\block\tile\Hopper as TileHopper;
|
||||
use pocketmine\block\tile\ItemFrame as TileItemFrame;
|
||||
use pocketmine\block\tile\Jukebox as TileJukebox;
|
||||
use pocketmine\block\tile\Lectern as TileLectern;
|
||||
use pocketmine\block\tile\MobHead as TileMobHead;
|
||||
use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner;
|
||||
use pocketmine\block\tile\NormalFurnace as TileNormalFurnace;
|
||||
use pocketmine\block\tile\Note as TileNote;
|
||||
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
|
||||
use pocketmine\block\tile\Skull as TileSkull;
|
||||
use pocketmine\block\tile\Smoker as TileSmoker;
|
||||
use pocketmine\block\utils\LeavesType;
|
||||
use pocketmine\block\utils\SaplingType;
|
||||
@ -159,6 +159,20 @@ use function mb_strtolower;
|
||||
* @method static CaveVines CAVE_VINES()
|
||||
* @method static Chain CHAIN()
|
||||
* @method static ChemicalHeat CHEMICAL_HEAT()
|
||||
* @method static WoodenButton CHERRY_BUTTON()
|
||||
* @method static WoodenDoor CHERRY_DOOR()
|
||||
* @method static WoodenFence CHERRY_FENCE()
|
||||
* @method static FenceGate CHERRY_FENCE_GATE()
|
||||
* @method static Leaves CHERRY_LEAVES()
|
||||
* @method static Wood CHERRY_LOG()
|
||||
* @method static Planks CHERRY_PLANKS()
|
||||
* @method static WoodenPressurePlate CHERRY_PRESSURE_PLATE()
|
||||
* @method static FloorSign CHERRY_SIGN()
|
||||
* @method static WoodenSlab CHERRY_SLAB()
|
||||
* @method static WoodenStairs CHERRY_STAIRS()
|
||||
* @method static WoodenTrapdoor CHERRY_TRAPDOOR()
|
||||
* @method static WallSign CHERRY_WALL_SIGN()
|
||||
* @method static Wood CHERRY_WOOD()
|
||||
* @method static Chest CHEST()
|
||||
* @method static Opaque CHISELED_DEEPSLATE()
|
||||
* @method static Opaque CHISELED_NETHER_BRICKS()
|
||||
@ -417,6 +431,7 @@ use function mb_strtolower;
|
||||
* @method static ItemFrame GLOWING_ITEM_FRAME()
|
||||
* @method static GlowingObsidian GLOWING_OBSIDIAN()
|
||||
* @method static Glowstone GLOWSTONE()
|
||||
* @method static GlowLichen GLOW_LICHEN()
|
||||
* @method static Opaque GOLD()
|
||||
* @method static GoldOre GOLD_ORE()
|
||||
* @method static Opaque GRANITE()
|
||||
@ -503,7 +518,7 @@ use function mb_strtolower;
|
||||
* @method static ChemistryTable MATERIAL_REDUCER()
|
||||
* @method static Melon MELON()
|
||||
* @method static MelonStem MELON_STEM()
|
||||
* @method static Skull MOB_HEAD()
|
||||
* @method static MobHead MOB_HEAD()
|
||||
* @method static MonsterSpawner MONSTER_SPAWNER()
|
||||
* @method static Opaque MOSSY_COBBLESTONE()
|
||||
* @method static Slab MOSSY_COBBLESTONE_SLAB()
|
||||
@ -864,6 +879,7 @@ final class VanillaBlocks{
|
||||
self::register("glass_pane", new GlassPane(new BID(Ids::GLASS_PANE), "Glass Pane", $glassBreakInfo));
|
||||
self::register("glowing_obsidian", new GlowingObsidian(new BID(Ids::GLOWING_OBSIDIAN), "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND(), 50.0))));
|
||||
self::register("glowstone", new Glowstone(new BID(Ids::GLOWSTONE), "Glowstone", new Info(BreakInfo::pickaxe(0.3))));
|
||||
self::register("glow_lichen", new GlowLichen(new BID(Ids::GLOW_LICHEN), "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2))));
|
||||
self::register("gold", new Opaque(new BID(Ids::GOLD), "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON(), 30.0))));
|
||||
|
||||
$grassBreakInfo = BreakInfo::shovel(0.6);
|
||||
@ -981,7 +997,7 @@ final class VanillaBlocks{
|
||||
|
||||
self::register("sea_lantern", new SeaLantern(new BID(Ids::SEA_LANTERN), "Sea Lantern", new Info(new BreakInfo(0.3))));
|
||||
self::register("sea_pickle", new SeaPickle(new BID(Ids::SEA_PICKLE), "Sea Pickle", new Info(BreakInfo::instant())));
|
||||
self::register("mob_head", new Skull(new BID(Ids::MOB_HEAD, TileSkull::class), "Mob Head", new Info(new BreakInfo(1.0))));
|
||||
self::register("mob_head", new MobHead(new BID(Ids::MOB_HEAD, TileMobHead::class), "Mob Head", new Info(new BreakInfo(1.0))));
|
||||
self::register("slime", new Slime(new BID(Ids::SLIME), "Slime Block", new Info(BreakInfo::instant())));
|
||||
self::register("snow", new Snow(new BID(Ids::SNOW), "Snow Block", new Info(BreakInfo::shovel(0.2, ToolTier::WOOD()))));
|
||||
self::register("snow_layer", new SnowLayer(new BID(Ids::SNOW_LAYER), "Snow Layer", new Info(BreakInfo::shovel(0.1, ToolTier::WOOD()))));
|
||||
|
@ -108,17 +108,18 @@ final class WaterCauldron extends FillableCauldron{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if(($newColor = match($item->getTypeId()){
|
||||
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE()->getRgbValue(),
|
||||
ItemTypeIds::INK_SAC => DyeColor::BLACK()->getRgbValue(),
|
||||
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN()->getRgbValue(),
|
||||
ItemTypeIds::BONE_MEAL => DyeColor::WHITE()->getRgbValue(),
|
||||
ItemTypeIds::DYE => $item instanceof Dye ? $item->getColor()->getRgbValue() : null,
|
||||
$world = $this->position->getWorld();
|
||||
if(($dyeColor = match($item->getTypeId()){
|
||||
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(),
|
||||
ItemTypeIds::INK_SAC => DyeColor::BLACK(),
|
||||
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(),
|
||||
ItemTypeIds::BONE_MEAL => DyeColor::WHITE(),
|
||||
ItemTypeIds::DYE => $item instanceof Dye ? $item->getColor() : null,
|
||||
default => null
|
||||
}) !== null && $newColor->toRGBA() !== $this->customWaterColor?->toRGBA()
|
||||
}) !== null && ($newColor = $dyeColor->getRgbValue())->toRGBA() !== $this->customWaterColor?->toRGBA()
|
||||
){
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setCustomWaterColor($this->customWaterColor === null ? $newColor : Color::mix($this->customWaterColor, $newColor)));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronAddDyeSound());
|
||||
$world->setBlock($this->position, $this->setCustomWaterColor($this->customWaterColor === null ? $newColor : Color::mix($this->customWaterColor, $newColor)));
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronAddDyeSound());
|
||||
|
||||
$item->pop();
|
||||
}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
|
||||
@ -137,13 +138,13 @@ final class WaterCauldron extends FillableCauldron{
|
||||
default => false
|
||||
} && $item->getCustomColor()?->toRGBA() !== $this->customWaterColor->toRGBA()){
|
||||
$item->setCustomColor($this->customWaterColor);
|
||||
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::DYE_ARMOR_USE_AMOUNT));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronDyeItemSound());
|
||||
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::DYE_ARMOR_USE_AMOUNT));
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronDyeItemSound());
|
||||
}
|
||||
}elseif($item->getCustomColor() !== null){
|
||||
$item->clearCustomColor();
|
||||
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_ARMOR_USE_AMOUNT));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
|
||||
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_ARMOR_USE_AMOUNT));
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
|
||||
}
|
||||
}elseif($item instanceof Banner){
|
||||
$patterns = $item->getPatterns();
|
||||
@ -151,8 +152,8 @@ final class WaterCauldron extends FillableCauldron{
|
||||
array_pop($patterns);
|
||||
$item->setPatterns($patterns);
|
||||
|
||||
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_BANNER_USE_AMOUNT));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
|
||||
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_BANNER_USE_AMOUNT));
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
|
||||
}
|
||||
}elseif(ItemTypeIds::toBlockTypeId($item->getTypeId()) === BlockTypeIds::DYED_SHULKER_BOX){
|
||||
if($this->customWaterColor === null){
|
||||
@ -162,8 +163,8 @@ final class WaterCauldron extends FillableCauldron{
|
||||
$item->pop();
|
||||
$returnedItems[] = $newItem;
|
||||
|
||||
$this->position->getWorld()->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_SHULKER_BOX_USE_AMOUNT));
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
|
||||
$world->setBlock($this->position, $this->withFillLevel($this->getFillLevel() - self::CLEAN_SHULKER_BOX_USE_AMOUNT));
|
||||
$world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronCleanItemSound());
|
||||
}
|
||||
}else{
|
||||
match($item->getTypeId()){
|
||||
|
@ -42,6 +42,8 @@ use pocketmine\utils\AssumptionFailedError;
|
||||
* as flammability, hardness, required tool tier, etc.
|
||||
* Therefore, to stay on the safe side of Mojang, wood-like blocks have static types. This does unfortunately generate
|
||||
* a lot of ugly code.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class WoodLikeBlockIdHelper{
|
||||
|
||||
@ -56,6 +58,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_PLANKS,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_PLANKS,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_PLANKS,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_PLANKS,
|
||||
default => throw new AssumptionFailedError("All tree types should be covered")
|
||||
});
|
||||
}
|
||||
@ -71,6 +74,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_FENCE,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_FENCE,
|
||||
default => throw new AssumptionFailedError("All tree types should be covered")
|
||||
});
|
||||
}
|
||||
@ -86,6 +90,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_SLAB,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_SLAB,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_SLAB,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_SLAB,
|
||||
default => throw new AssumptionFailedError("All tree types should be covered")
|
||||
});
|
||||
}
|
||||
@ -101,6 +106,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_LOG,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_STEM,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_STEM,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_LOG,
|
||||
default => throw new AssumptionFailedError("All tree types should be covered")
|
||||
});
|
||||
}
|
||||
@ -116,6 +122,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_WOOD,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_HYPHAE,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_HYPHAE,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_WOOD,
|
||||
default => throw new AssumptionFailedError("All tree types should be covered")
|
||||
});
|
||||
}
|
||||
@ -131,6 +138,7 @@ final class WoodLikeBlockIdHelper{
|
||||
LeavesType::MANGROVE()->id() => Ids::MANGROVE_LEAVES,
|
||||
LeavesType::AZALEA()->id() => Ids::AZALEA_LEAVES,
|
||||
LeavesType::FLOWERING_AZALEA()->id() => Ids::FLOWERING_AZALEA_LEAVES,
|
||||
LeavesType::CHERRY()->id() => Ids::CHERRY_LEAVES,
|
||||
default => throw new AssumptionFailedError("All leaves types should be covered")
|
||||
});
|
||||
}
|
||||
@ -207,7 +215,12 @@ final class WoodLikeBlockIdHelper{
|
||||
new BID(Ids::WARPED_WALL_SIGN, TileSign::class),
|
||||
fn() => VanillaItems::WARPED_SIGN()
|
||||
];
|
||||
|
||||
case WoodType::CHERRY()->id():
|
||||
return [
|
||||
new BID(Ids::CHERRY_SIGN, TileSign::class),
|
||||
new BID(Ids::CHERRY_WALL_SIGN, TileSign::class),
|
||||
fn() => VanillaItems::CHERRY_SIGN()
|
||||
];
|
||||
}
|
||||
throw new AssumptionFailedError("Switch should cover all wood types");
|
||||
}
|
||||
@ -223,6 +236,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_TRAPDOOR,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_TRAPDOOR,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_TRAPDOOR,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_TRAPDOOR,
|
||||
default => throw new AssumptionFailedError("All wood types should be covered")
|
||||
});
|
||||
}
|
||||
@ -238,6 +252,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_BUTTON,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_BUTTON,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_BUTTON,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_BUTTON,
|
||||
default => throw new AssumptionFailedError("All wood types should be covered")
|
||||
});
|
||||
}
|
||||
@ -253,6 +268,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_PRESSURE_PLATE,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_PRESSURE_PLATE,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_PRESSURE_PLATE,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_PRESSURE_PLATE,
|
||||
default => throw new AssumptionFailedError("All wood types should be covered")
|
||||
});
|
||||
}
|
||||
@ -268,6 +284,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_DOOR,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_DOOR,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_DOOR,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_DOOR,
|
||||
default => throw new AssumptionFailedError("All wood types should be covered")
|
||||
});
|
||||
}
|
||||
@ -283,6 +300,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE_GATE,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE_GATE,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_FENCE_GATE,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_FENCE_GATE,
|
||||
default => throw new AssumptionFailedError("All wood types should be covered")
|
||||
});
|
||||
}
|
||||
@ -298,6 +316,7 @@ final class WoodLikeBlockIdHelper{
|
||||
WoodType::MANGROVE()->id() => Ids::MANGROVE_STAIRS,
|
||||
WoodType::CRIMSON()->id() => Ids::CRIMSON_STAIRS,
|
||||
WoodType::WARPED()->id() => Ids::WARPED_STAIRS,
|
||||
WoodType::CHERRY()->id() => Ids::CHERRY_STAIRS,
|
||||
default => throw new AssumptionFailedError("All wood types should be covered")
|
||||
});
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\Record;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\world\sound\RecordStopSound;
|
||||
|
||||
class Jukebox extends Spawnable{
|
||||
private const TAG_RECORD = "RecordItem"; //Item CompoundTag
|
||||
@ -62,4 +63,8 @@ class Jukebox extends Spawnable{
|
||||
$nbt->setTag(self::TAG_RECORD, TypeConverter::getInstance()->getItemTranslator()->toNetworkNbt($this->record));
|
||||
}
|
||||
}
|
||||
|
||||
protected function onBlockDestroyedHook() : void{
|
||||
$this->position->getWorld()->addSound($this->position, new RecordStopSound());
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\tile;
|
||||
|
||||
use pocketmine\block\utils\SkullType;
|
||||
use pocketmine\block\utils\MobHeadType;
|
||||
use pocketmine\data\bedrock\MobHeadTypeIdMap;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -31,60 +33,60 @@ use pocketmine\world\World;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see \pocketmine\block\Skull
|
||||
* @see \pocketmine\block\MobHead
|
||||
*/
|
||||
class Skull extends Spawnable{
|
||||
class MobHead extends Spawnable{
|
||||
|
||||
private const TAG_SKULL_TYPE = "SkullType"; //TAG_Byte
|
||||
private const TAG_ROT = "Rot"; //TAG_Byte
|
||||
private const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte
|
||||
private const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int
|
||||
|
||||
private SkullType $skullType;
|
||||
private int $skullRotation = 0;
|
||||
private MobHeadType $mobHeadType;
|
||||
private int $rotation = 0;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
$this->skullType = SkullType::SKELETON();
|
||||
$this->mobHeadType = MobHeadType::SKELETON();
|
||||
parent::__construct($world, $pos);
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
if(($skullTypeTag = $nbt->getTag(self::TAG_SKULL_TYPE)) instanceof ByteTag){
|
||||
try{
|
||||
$this->skullType = SkullType::fromMagicNumber($skullTypeTag->getValue());
|
||||
}catch(\InvalidArgumentException $e){
|
||||
//bad data, drop it
|
||||
$mobHeadType = MobHeadTypeIdMap::getInstance()->fromId($skullTypeTag->getValue());
|
||||
if($mobHeadType === null){
|
||||
throw new SavedDataLoadingException("Invalid skull type tag value " . $skullTypeTag->getValue());
|
||||
}
|
||||
$this->mobHeadType = $mobHeadType;
|
||||
}
|
||||
$rotation = $nbt->getByte(self::TAG_ROT, 0);
|
||||
if($rotation >= 0 && $rotation <= 15){
|
||||
$this->skullRotation = $rotation;
|
||||
$this->rotation = $rotation;
|
||||
}
|
||||
}
|
||||
|
||||
protected function writeSaveData(CompoundTag $nbt) : void{
|
||||
$nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType->getMagicNumber());
|
||||
$nbt->setByte(self::TAG_ROT, $this->skullRotation);
|
||||
$nbt->setByte(self::TAG_SKULL_TYPE, MobHeadTypeIdMap::getInstance()->toId($this->mobHeadType));
|
||||
$nbt->setByte(self::TAG_ROT, $this->rotation);
|
||||
}
|
||||
|
||||
public function setSkullType(SkullType $type) : void{
|
||||
$this->skullType = $type;
|
||||
public function setMobHeadType(MobHeadType $type) : void{
|
||||
$this->mobHeadType = $type;
|
||||
}
|
||||
|
||||
public function getSkullType() : SkullType{
|
||||
return $this->skullType;
|
||||
public function getMobHeadType() : MobHeadType{
|
||||
return $this->mobHeadType;
|
||||
}
|
||||
|
||||
public function getRotation() : int{
|
||||
return $this->skullRotation;
|
||||
return $this->rotation;
|
||||
}
|
||||
|
||||
public function setRotation(int $rotation) : void{
|
||||
$this->skullRotation = $rotation;
|
||||
$this->rotation = $rotation;
|
||||
}
|
||||
|
||||
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType->getMagicNumber());
|
||||
$nbt->setByte(self::TAG_ROT, $this->skullRotation);
|
||||
$nbt->setByte(self::TAG_SKULL_TYPE, MobHeadTypeIdMap::getInstance()->toId($this->mobHeadType));
|
||||
$nbt->setByte(self::TAG_ROT, $this->rotation);
|
||||
}
|
||||
}
|
@ -34,6 +34,7 @@ use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\VersionInfo;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\World;
|
||||
use function get_class;
|
||||
@ -71,7 +72,8 @@ abstract class Tile{
|
||||
->setString(self::TAG_ID, TileFactory::getInstance()->getSaveId(get_class($this)))
|
||||
->setInt(self::TAG_X, $this->position->getFloorX())
|
||||
->setInt(self::TAG_Y, $this->position->getFloorY())
|
||||
->setInt(self::TAG_Z, $this->position->getFloorZ());
|
||||
->setInt(self::TAG_Z, $this->position->getFloorZ())
|
||||
->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
|
||||
$this->writeSaveData($nbt);
|
||||
|
||||
return $nbt;
|
||||
|
@ -75,7 +75,7 @@ final class TileFactory{
|
||||
$this->register(Sign::class, ["Sign", "minecraft:sign"]);
|
||||
$this->register(Smoker::class, ["Smoker", "minecraft:smoker"]);
|
||||
$this->register(SporeBlossom::class, ["SporeBlossom", "minecraft:spore_blossom"]);
|
||||
$this->register(Skull::class, ["Skull", "minecraft:skull"]);
|
||||
$this->register(MobHead::class, ["Skull", "minecraft:skull"]);
|
||||
$this->register(GlowingItemFrame::class, ["GlowItemFrame"]);
|
||||
|
||||
//TODO: Campfire
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\utils\EnumTrait;
|
||||
* @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()
|
||||
@ -57,7 +58,8 @@ final class LeavesType{
|
||||
new self("dark_oak", "Dark Oak"),
|
||||
new self("mangrove", "Mangrove"),
|
||||
new self("azalea", "Azalea"),
|
||||
new self("flowering_azalea", "Flowering Azalea")
|
||||
new self("flowering_azalea", "Flowering Azalea"),
|
||||
new self("cherry", "Cherry")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,54 +31,34 @@ use pocketmine\utils\EnumTrait;
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static SkullType CREEPER()
|
||||
* @method static SkullType DRAGON()
|
||||
* @method static SkullType PLAYER()
|
||||
* @method static SkullType SKELETON()
|
||||
* @method static SkullType WITHER_SKELETON()
|
||||
* @method static SkullType ZOMBIE()
|
||||
* @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()
|
||||
*/
|
||||
final class SkullType{
|
||||
final class MobHeadType{
|
||||
use EnumTrait {
|
||||
register as Enum_register;
|
||||
__construct as Enum___construct;
|
||||
}
|
||||
|
||||
/** @var SkullType[] */
|
||||
private static array $numericIdMap = [];
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
new SkullType("skeleton", "Skeleton Skull", 0),
|
||||
new SkullType("wither_skeleton", "Wither Skeleton Skull", 1),
|
||||
new SkullType("zombie", "Zombie Head", 2),
|
||||
new SkullType("player", "Player Head", 3),
|
||||
new SkullType("creeper", "Creeper Head", 4),
|
||||
new SkullType("dragon", "Dragon Head", 5)
|
||||
new MobHeadType("skeleton", "Skeleton Skull"),
|
||||
new MobHeadType("wither_skeleton", "Wither Skeleton Skull"),
|
||||
new MobHeadType("zombie", "Zombie Head"),
|
||||
new MobHeadType("player", "Player Head"),
|
||||
new MobHeadType("creeper", "Creeper Head"),
|
||||
new MobHeadType("dragon", "Dragon Head"),
|
||||
new MobHeadType("piglin", "Piglin Head")
|
||||
);
|
||||
}
|
||||
|
||||
protected static function register(SkullType $type) : void{
|
||||
self::Enum_register($type);
|
||||
self::$numericIdMap[$type->getMagicNumber()] = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function fromMagicNumber(int $magicNumber) : SkullType{
|
||||
if(!isset(self::$numericIdMap[$magicNumber])){
|
||||
throw new \InvalidArgumentException("Unknown skull type magic number $magicNumber");
|
||||
}
|
||||
return self::$numericIdMap[$magicNumber];
|
||||
}
|
||||
|
||||
private function __construct(
|
||||
string $enumName,
|
||||
private string $displayName,
|
||||
private int $magicNumber
|
||||
private string $displayName
|
||||
){
|
||||
$this->Enum___construct($enumName);
|
||||
}
|
||||
@ -86,8 +66,4 @@ final class SkullType{
|
||||
public function getDisplayName() : string{
|
||||
return $this->displayName;
|
||||
}
|
||||
|
||||
public function getMagicNumber() : int{
|
||||
return $this->magicNumber;
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ use pocketmine\utils\EnumTrait;
|
||||
*
|
||||
* @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()
|
||||
@ -56,7 +57,8 @@ final class WoodType{
|
||||
new self("dark_oak", "Dark Oak", true),
|
||||
new self("mangrove", "Mangrove", true),
|
||||
new self("crimson", "Crimson", false, "Stem", "Hyphae"),
|
||||
new self("warped", "Warped", false, "Stem", "Hyphae")
|
||||
new self("warped", "Warped", false, "Stem", "Hyphae"),
|
||||
new self("cherry", "Cherry", true),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\console;
|
||||
|
||||
use pmmp\thread\Thread as NativeThread;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\utils\Process;
|
||||
use function cli_set_process_title;
|
||||
use function count;
|
||||
@ -30,7 +32,6 @@ use function dirname;
|
||||
use function feof;
|
||||
use function fwrite;
|
||||
use function stream_socket_client;
|
||||
use const PTHREADS_INHERIT_NONE;
|
||||
|
||||
require dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
|
||||
@ -46,14 +47,14 @@ if($socket === false){
|
||||
throw new \RuntimeException("Failed to connect to server process ($errCode): $errMessage");
|
||||
}
|
||||
|
||||
/** @phpstan-var \ThreadedArray<int, string> $channel */
|
||||
$channel = new \ThreadedArray();
|
||||
$thread = new class($channel) extends \Thread{
|
||||
/** @phpstan-var ThreadSafeArray<int, string> $channel */
|
||||
$channel = new ThreadSafeArray();
|
||||
$thread = new class($channel) extends NativeThread{
|
||||
/**
|
||||
* @phpstan-param \ThreadedArray<int, string> $channel
|
||||
* @phpstan-param ThreadSafeArray<int, string> $channel
|
||||
*/
|
||||
public function __construct(
|
||||
private \ThreadedArray $channel,
|
||||
private ThreadSafeArray $channel,
|
||||
){}
|
||||
|
||||
public function run() : void{
|
||||
@ -73,7 +74,7 @@ $thread = new class($channel) extends \Thread{
|
||||
}
|
||||
};
|
||||
|
||||
$thread->start(PTHREADS_INHERIT_NONE);
|
||||
$thread->start(NativeThread::INHERIT_NONE);
|
||||
while(!feof($socket)){
|
||||
$line = $channel->synchronized(function() use ($channel) : ?string{
|
||||
if(count($channel) === 0){
|
||||
|
@ -248,7 +248,6 @@ class CrashDump{
|
||||
if(file_exists($filePath)){
|
||||
$reflection = new \ReflectionClass(PluginBase::class);
|
||||
$file = $reflection->getProperty("file");
|
||||
$file->setAccessible(true);
|
||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||
$filePath = Filesystem::cleanPath($file->getValue($plugin));
|
||||
if(str_starts_with($frameCleanPath, $filePath)){
|
||||
|
69
src/data/bedrock/MobHeadTypeIdMap.php
Normal file
69
src/data/bedrock/MobHeadTypeIdMap.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock;
|
||||
|
||||
use pocketmine\block\utils\MobHeadType;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
|
||||
final class MobHeadTypeIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var MobHeadType[]
|
||||
* @phpstan-var array<int, MobHeadType>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->register(0, MobHeadType::SKELETON());
|
||||
$this->register(1, MobHeadType::WITHER_SKELETON());
|
||||
$this->register(2, MobHeadType::ZOMBIE());
|
||||
$this->register(3, MobHeadType::PLAYER());
|
||||
$this->register(4, MobHeadType::CREEPER());
|
||||
$this->register(5, MobHeadType::DRAGON());
|
||||
$this->register(6, MobHeadType::PIGLIN());
|
||||
}
|
||||
|
||||
private function register(int $id, MobHeadType $type) : void{
|
||||
$this->idToEnum[$id] = $type;
|
||||
$this->enumToId[$type->id()] = $id;
|
||||
}
|
||||
|
||||
public function fromId(int $id) : ?MobHeadType{
|
||||
return $this->idToEnum[$id] ?? null;
|
||||
}
|
||||
|
||||
public function toId(MobHeadType $type) : int{
|
||||
if(!isset($this->enumToId[$type->id()])){
|
||||
throw new \InvalidArgumentException("Type does not have a mapped ID");
|
||||
}
|
||||
return $this->enumToId[$type->id()];
|
||||
}
|
||||
}
|
78
src/data/bedrock/NoteInstrumentIdMap.php
Normal file
78
src/data/bedrock/NoteInstrumentIdMap.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?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\data\bedrock;
|
||||
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\world\sound\NoteInstrument;
|
||||
|
||||
final class NoteInstrumentIdMap{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @var NoteInstrument[]
|
||||
* @phpstan-var array<int, NoteInstrument>
|
||||
*/
|
||||
private array $idToEnum = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
private array $enumToId = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->register(0, NoteInstrument::PIANO());
|
||||
$this->register(1, NoteInstrument::BASS_DRUM());
|
||||
$this->register(2, NoteInstrument::SNARE());
|
||||
$this->register(3, NoteInstrument::CLICKS_AND_STICKS());
|
||||
$this->register(4, NoteInstrument::DOUBLE_BASS());
|
||||
$this->register(5, NoteInstrument::BELL());
|
||||
$this->register(6, NoteInstrument::FLUTE());
|
||||
$this->register(7, NoteInstrument::CHIME());
|
||||
$this->register(8, NoteInstrument::GUITAR());
|
||||
$this->register(9, NoteInstrument::XYLOPHONE());
|
||||
$this->register(10, NoteInstrument::IRON_XYLOPHONE());
|
||||
$this->register(11, NoteInstrument::COW_BELL());
|
||||
$this->register(12, NoteInstrument::DIDGERIDOO());
|
||||
$this->register(13, NoteInstrument::BIT());
|
||||
$this->register(14, NoteInstrument::BANJO());
|
||||
$this->register(15, NoteInstrument::PLING());
|
||||
}
|
||||
|
||||
private function register(int $id, NoteInstrument $instrument) : void{
|
||||
$this->idToEnum[$id] = $instrument;
|
||||
$this->enumToId[$instrument->id()] = $id;
|
||||
}
|
||||
|
||||
public function fromId(int $id) : ?NoteInstrument{
|
||||
return $this->idToEnum[$id] ?? null;
|
||||
}
|
||||
|
||||
public function toId(NoteInstrument $instrument) : int{
|
||||
if(!isset($this->enumToId[$instrument->id()])){
|
||||
throw new \InvalidArgumentException("Type does not have a mapped ID");
|
||||
}
|
||||
return $this->enumToId[$instrument->id()];
|
||||
}
|
||||
}
|
@ -38,6 +38,13 @@ final class BlockLegacyMetadata{
|
||||
public const CORAL_VARIANT_FIRE = 3;
|
||||
public const CORAL_VARIANT_HORN = 4;
|
||||
|
||||
public const MULTI_FACE_DIRECTION_FLAG_DOWN = 0x01;
|
||||
public const MULTI_FACE_DIRECTION_FLAG_UP = 0x02;
|
||||
public const MULTI_FACE_DIRECTION_FLAG_SOUTH = 0x04;
|
||||
public const MULTI_FACE_DIRECTION_FLAG_WEST = 0x08;
|
||||
public const MULTI_FACE_DIRECTION_FLAG_NORTH = 0x10;
|
||||
public const MULTI_FACE_DIRECTION_FLAG_EAST = 0x20;
|
||||
|
||||
public const MUSHROOM_BLOCK_ALL_PORES = 0;
|
||||
public const MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER = 1;
|
||||
public const MUSHROOM_BLOCK_CAP_NORTH_SIDE = 2;
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\VersionInfo;
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function implode;
|
||||
@ -40,9 +41,9 @@ final class BlockStateData{
|
||||
*/
|
||||
public const CURRENT_VERSION =
|
||||
(1 << 24) | //major
|
||||
(19 << 16) | //minor
|
||||
(80 << 8) | //patch
|
||||
(11); //revision
|
||||
(20 << 16) | //minor
|
||||
(10 << 8) | //patch
|
||||
(32); //revision
|
||||
|
||||
public const TAG_NAME = "name";
|
||||
public const TAG_STATES = "states";
|
||||
@ -96,12 +97,13 @@ final class BlockStateData{
|
||||
$name = $nbt->getString(self::TAG_NAME);
|
||||
$states = $nbt->getCompoundTag(self::TAG_STATES) ?? throw new BlockStateDeserializeException("Missing tag \"" . self::TAG_STATES . "\"");
|
||||
$version = $nbt->getInt(self::TAG_VERSION, 0);
|
||||
//TODO: read version from VersionInfo::TAG_WORLD_DATA_VERSION - we may need it to fix up old blockstates
|
||||
}catch(NbtException $e){
|
||||
throw new BlockStateDeserializeException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
$allKeys = $nbt->getValue();
|
||||
unset($allKeys[self::TAG_NAME], $allKeys[self::TAG_STATES], $allKeys[self::TAG_VERSION]);
|
||||
unset($allKeys[self::TAG_NAME], $allKeys[self::TAG_STATES], $allKeys[self::TAG_VERSION], $allKeys[VersionInfo::TAG_WORLD_DATA_VERSION]);
|
||||
if(count($allKeys) !== 0){
|
||||
throw new BlockStateDeserializeException("Unexpected extra keys: " . implode(", ", array_keys($allKeys)));
|
||||
}
|
||||
@ -109,7 +111,10 @@ final class BlockStateData{
|
||||
return new self($name, $states->getValue(), $version);
|
||||
}
|
||||
|
||||
public function toNbt() : CompoundTag{
|
||||
/**
|
||||
* Encodes the blockstate as a TAG_Compound, exactly as it would be in vanilla Bedrock.
|
||||
*/
|
||||
public function toVanillaNbt() : CompoundTag{
|
||||
$statesTag = CompoundTag::create();
|
||||
foreach(Utils::stringifyKeys($this->states) as $key => $value){
|
||||
$statesTag->setTag($key, $value);
|
||||
@ -120,6 +125,15 @@ final class BlockStateData{
|
||||
->setTag(self::TAG_STATES, $statesTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the blockstate as a TAG_Compound, but with extra PM-specific metadata, used for fixing bugs in old saved
|
||||
* data. This should be used for anything saved to disk.
|
||||
*/
|
||||
public function toNbt() : CompoundTag{
|
||||
return $this->toVanillaNbt()
|
||||
->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
|
||||
}
|
||||
|
||||
public function equals(self $that) : bool{
|
||||
if($this->name !== $that->name || count($this->states) !== count($that->states)){
|
||||
return false;
|
||||
|
@ -98,6 +98,8 @@ final class BlockStateNames{
|
||||
public const LEVER_DIRECTION = "lever_direction";
|
||||
public const LIQUID_DEPTH = "liquid_depth";
|
||||
public const LIT = "lit";
|
||||
public const MC_CARDINAL_DIRECTION = "minecraft:cardinal_direction";
|
||||
public const MC_FACING_DIRECTION = "minecraft:facing_direction";
|
||||
public const MOISTURIZED_AMOUNT = "moisturized_amount";
|
||||
public const MONSTER_EGG_STONE_TYPE = "monster_egg_stone_type";
|
||||
public const MULTI_FACE_DIRECTION_BITS = "multi_face_direction_bits";
|
||||
@ -122,6 +124,7 @@ final class BlockStateNames{
|
||||
public const SAND_STONE_TYPE = "sand_stone_type";
|
||||
public const SAND_TYPE = "sand_type";
|
||||
public const SAPLING_TYPE = "sapling_type";
|
||||
public const SCULK_SENSOR_PHASE = "sculk_sensor_phase";
|
||||
public const SEA_GRASS_TYPE = "sea_grass_type";
|
||||
public const SPONGE_TYPE = "sponge_type";
|
||||
public const STABILITY = "stability";
|
||||
|
@ -131,6 +131,18 @@ final class BlockStateStringValues{
|
||||
public const LEVER_DIRECTION_UP_NORTH_SOUTH = "up_north_south";
|
||||
public const LEVER_DIRECTION_WEST = "west";
|
||||
|
||||
public const MC_CARDINAL_DIRECTION_EAST = "east";
|
||||
public const MC_CARDINAL_DIRECTION_NORTH = "north";
|
||||
public const MC_CARDINAL_DIRECTION_SOUTH = "south";
|
||||
public const MC_CARDINAL_DIRECTION_WEST = "west";
|
||||
|
||||
public const MC_FACING_DIRECTION_DOWN = "down";
|
||||
public const MC_FACING_DIRECTION_EAST = "east";
|
||||
public const MC_FACING_DIRECTION_NORTH = "north";
|
||||
public const MC_FACING_DIRECTION_SOUTH = "south";
|
||||
public const MC_FACING_DIRECTION_UP = "up";
|
||||
public const MC_FACING_DIRECTION_WEST = "west";
|
||||
|
||||
public const MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK = "chiseled_stone_brick";
|
||||
public const MONSTER_EGG_STONE_TYPE_COBBLESTONE = "cobblestone";
|
||||
public const MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK = "cracked_stone_brick";
|
||||
|
@ -97,7 +97,10 @@ final class BlockTypeNames{
|
||||
public const BIRCH_WALL_SIGN = "minecraft:birch_wall_sign";
|
||||
public const BLACK_CANDLE = "minecraft:black_candle";
|
||||
public const BLACK_CANDLE_CAKE = "minecraft:black_candle_cake";
|
||||
public const BLACK_CARPET = "minecraft:black_carpet";
|
||||
public const BLACK_CONCRETE = "minecraft:black_concrete";
|
||||
public const BLACK_GLAZED_TERRACOTTA = "minecraft:black_glazed_terracotta";
|
||||
public const BLACK_SHULKER_BOX = "minecraft:black_shulker_box";
|
||||
public const BLACK_WOOL = "minecraft:black_wool";
|
||||
public const BLACKSTONE = "minecraft:blackstone";
|
||||
public const BLACKSTONE_DOUBLE_SLAB = "minecraft:blackstone_double_slab";
|
||||
@ -107,22 +110,30 @@ final class BlockTypeNames{
|
||||
public const BLAST_FURNACE = "minecraft:blast_furnace";
|
||||
public const BLUE_CANDLE = "minecraft:blue_candle";
|
||||
public const BLUE_CANDLE_CAKE = "minecraft:blue_candle_cake";
|
||||
public const BLUE_CARPET = "minecraft:blue_carpet";
|
||||
public const BLUE_CONCRETE = "minecraft:blue_concrete";
|
||||
public const BLUE_GLAZED_TERRACOTTA = "minecraft:blue_glazed_terracotta";
|
||||
public const BLUE_ICE = "minecraft:blue_ice";
|
||||
public const BLUE_SHULKER_BOX = "minecraft:blue_shulker_box";
|
||||
public const BLUE_WOOL = "minecraft:blue_wool";
|
||||
public const BONE_BLOCK = "minecraft:bone_block";
|
||||
public const BOOKSHELF = "minecraft:bookshelf";
|
||||
public const BORDER_BLOCK = "minecraft:border_block";
|
||||
public const BRAIN_CORAL = "minecraft:brain_coral";
|
||||
public const BREWING_STAND = "minecraft:brewing_stand";
|
||||
public const BRICK_BLOCK = "minecraft:brick_block";
|
||||
public const BRICK_STAIRS = "minecraft:brick_stairs";
|
||||
public const BROWN_CANDLE = "minecraft:brown_candle";
|
||||
public const BROWN_CANDLE_CAKE = "minecraft:brown_candle_cake";
|
||||
public const BROWN_CARPET = "minecraft:brown_carpet";
|
||||
public const BROWN_CONCRETE = "minecraft:brown_concrete";
|
||||
public const BROWN_GLAZED_TERRACOTTA = "minecraft:brown_glazed_terracotta";
|
||||
public const BROWN_MUSHROOM = "minecraft:brown_mushroom";
|
||||
public const BROWN_MUSHROOM_BLOCK = "minecraft:brown_mushroom_block";
|
||||
public const BROWN_SHULKER_BOX = "minecraft:brown_shulker_box";
|
||||
public const BROWN_WOOL = "minecraft:brown_wool";
|
||||
public const BUBBLE_COLUMN = "minecraft:bubble_column";
|
||||
public const BUBBLE_CORAL = "minecraft:bubble_coral";
|
||||
public const BUDDING_AMETHYST = "minecraft:budding_amethyst";
|
||||
public const CACTUS = "minecraft:cactus";
|
||||
public const CAKE = "minecraft:cake";
|
||||
@ -132,7 +143,6 @@ final class BlockTypeNames{
|
||||
public const CAMPFIRE = "minecraft:campfire";
|
||||
public const CANDLE = "minecraft:candle";
|
||||
public const CANDLE_CAKE = "minecraft:candle_cake";
|
||||
public const CARPET = "minecraft:carpet";
|
||||
public const CARROTS = "minecraft:carrots";
|
||||
public const CARTOGRAPHY_TABLE = "minecraft:cartography_table";
|
||||
public const CARVED_PUMPKIN = "minecraft:carved_pumpkin";
|
||||
@ -184,12 +194,10 @@ final class BlockTypeNames{
|
||||
public const COLORED_TORCH_RG = "minecraft:colored_torch_rg";
|
||||
public const COMMAND_BLOCK = "minecraft:command_block";
|
||||
public const COMPOSTER = "minecraft:composter";
|
||||
public const CONCRETE = "minecraft:concrete";
|
||||
public const CONCRETE_POWDER = "minecraft:concrete_powder";
|
||||
public const CONDUIT = "minecraft:conduit";
|
||||
public const COPPER_BLOCK = "minecraft:copper_block";
|
||||
public const COPPER_ORE = "minecraft:copper_ore";
|
||||
public const CORAL = "minecraft:coral";
|
||||
public const CORAL_BLOCK = "minecraft:coral_block";
|
||||
public const CORAL_FAN = "minecraft:coral_fan";
|
||||
public const CORAL_FAN_DEAD = "minecraft:coral_fan_dead";
|
||||
@ -225,7 +233,10 @@ final class BlockTypeNames{
|
||||
public const CUT_COPPER_STAIRS = "minecraft:cut_copper_stairs";
|
||||
public const CYAN_CANDLE = "minecraft:cyan_candle";
|
||||
public const CYAN_CANDLE_CAKE = "minecraft:cyan_candle_cake";
|
||||
public const CYAN_CARPET = "minecraft:cyan_carpet";
|
||||
public const CYAN_CONCRETE = "minecraft:cyan_concrete";
|
||||
public const CYAN_GLAZED_TERRACOTTA = "minecraft:cyan_glazed_terracotta";
|
||||
public const CYAN_SHULKER_BOX = "minecraft:cyan_shulker_box";
|
||||
public const CYAN_WOOL = "minecraft:cyan_wool";
|
||||
public const DARK_OAK_BUTTON = "minecraft:dark_oak_button";
|
||||
public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
|
||||
@ -241,6 +252,11 @@ final class BlockTypeNames{
|
||||
public const DARKOAK_WALL_SIGN = "minecraft:darkoak_wall_sign";
|
||||
public const DAYLIGHT_DETECTOR = "minecraft:daylight_detector";
|
||||
public const DAYLIGHT_DETECTOR_INVERTED = "minecraft:daylight_detector_inverted";
|
||||
public const DEAD_BRAIN_CORAL = "minecraft:dead_brain_coral";
|
||||
public const DEAD_BUBBLE_CORAL = "minecraft:dead_bubble_coral";
|
||||
public const DEAD_FIRE_CORAL = "minecraft:dead_fire_coral";
|
||||
public const DEAD_HORN_CORAL = "minecraft:dead_horn_coral";
|
||||
public const DEAD_TUBE_CORAL = "minecraft:dead_tube_coral";
|
||||
public const DEADBUSH = "minecraft:deadbush";
|
||||
public const DECORATED_POT = "minecraft:decorated_pot";
|
||||
public const DEEPSLATE = "minecraft:deepslate";
|
||||
@ -419,6 +435,7 @@ final class BlockTypeNames{
|
||||
public const FARMLAND = "minecraft:farmland";
|
||||
public const FENCE_GATE = "minecraft:fence_gate";
|
||||
public const FIRE = "minecraft:fire";
|
||||
public const FIRE_CORAL = "minecraft:fire_coral";
|
||||
public const FLETCHING_TABLE = "minecraft:fletching_table";
|
||||
public const FLOWER_POT = "minecraft:flower_pot";
|
||||
public const FLOWERING_AZALEA = "minecraft:flowering_azalea";
|
||||
@ -444,11 +461,17 @@ final class BlockTypeNames{
|
||||
public const GRAVEL = "minecraft:gravel";
|
||||
public const GRAY_CANDLE = "minecraft:gray_candle";
|
||||
public const GRAY_CANDLE_CAKE = "minecraft:gray_candle_cake";
|
||||
public const GRAY_CARPET = "minecraft:gray_carpet";
|
||||
public const GRAY_CONCRETE = "minecraft:gray_concrete";
|
||||
public const GRAY_GLAZED_TERRACOTTA = "minecraft:gray_glazed_terracotta";
|
||||
public const GRAY_SHULKER_BOX = "minecraft:gray_shulker_box";
|
||||
public const GRAY_WOOL = "minecraft:gray_wool";
|
||||
public const GREEN_CANDLE = "minecraft:green_candle";
|
||||
public const GREEN_CANDLE_CAKE = "minecraft:green_candle_cake";
|
||||
public const GREEN_CARPET = "minecraft:green_carpet";
|
||||
public const GREEN_CONCRETE = "minecraft:green_concrete";
|
||||
public const GREEN_GLAZED_TERRACOTTA = "minecraft:green_glazed_terracotta";
|
||||
public const GREEN_SHULKER_BOX = "minecraft:green_shulker_box";
|
||||
public const GREEN_WOOL = "minecraft:green_wool";
|
||||
public const GRINDSTONE = "minecraft:grindstone";
|
||||
public const HANGING_ROOTS = "minecraft:hanging_roots";
|
||||
@ -462,6 +485,7 @@ final class BlockTypeNames{
|
||||
public const HONEY_BLOCK = "minecraft:honey_block";
|
||||
public const HONEYCOMB_BLOCK = "minecraft:honeycomb_block";
|
||||
public const HOPPER = "minecraft:hopper";
|
||||
public const HORN_CORAL = "minecraft:horn_coral";
|
||||
public const ICE = "minecraft:ice";
|
||||
public const INFESTED_DEEPSLATE = "minecraft:infested_deepslate";
|
||||
public const INFO_UPDATE = "minecraft:info_update";
|
||||
@ -492,7 +516,6 @@ final class BlockTypeNames{
|
||||
public const LAPIS_ORE = "minecraft:lapis_ore";
|
||||
public const LARGE_AMETHYST_BUD = "minecraft:large_amethyst_bud";
|
||||
public const LAVA = "minecraft:lava";
|
||||
public const LAVA_CAULDRON = "minecraft:lava_cauldron";
|
||||
public const LEAVES = "minecraft:leaves";
|
||||
public const LEAVES2 = "minecraft:leaves2";
|
||||
public const LECTERN = "minecraft:lectern";
|
||||
@ -500,16 +523,25 @@ final class BlockTypeNames{
|
||||
public const LIGHT_BLOCK = "minecraft:light_block";
|
||||
public const LIGHT_BLUE_CANDLE = "minecraft:light_blue_candle";
|
||||
public const LIGHT_BLUE_CANDLE_CAKE = "minecraft:light_blue_candle_cake";
|
||||
public const LIGHT_BLUE_CARPET = "minecraft:light_blue_carpet";
|
||||
public const LIGHT_BLUE_CONCRETE = "minecraft:light_blue_concrete";
|
||||
public const LIGHT_BLUE_GLAZED_TERRACOTTA = "minecraft:light_blue_glazed_terracotta";
|
||||
public const LIGHT_BLUE_SHULKER_BOX = "minecraft:light_blue_shulker_box";
|
||||
public const LIGHT_BLUE_WOOL = "minecraft:light_blue_wool";
|
||||
public const LIGHT_GRAY_CANDLE = "minecraft:light_gray_candle";
|
||||
public const LIGHT_GRAY_CANDLE_CAKE = "minecraft:light_gray_candle_cake";
|
||||
public const LIGHT_GRAY_CARPET = "minecraft:light_gray_carpet";
|
||||
public const LIGHT_GRAY_CONCRETE = "minecraft:light_gray_concrete";
|
||||
public const LIGHT_GRAY_SHULKER_BOX = "minecraft:light_gray_shulker_box";
|
||||
public const LIGHT_GRAY_WOOL = "minecraft:light_gray_wool";
|
||||
public const LIGHT_WEIGHTED_PRESSURE_PLATE = "minecraft:light_weighted_pressure_plate";
|
||||
public const LIGHTNING_ROD = "minecraft:lightning_rod";
|
||||
public const LIME_CANDLE = "minecraft:lime_candle";
|
||||
public const LIME_CANDLE_CAKE = "minecraft:lime_candle_cake";
|
||||
public const LIME_CARPET = "minecraft:lime_carpet";
|
||||
public const LIME_CONCRETE = "minecraft:lime_concrete";
|
||||
public const LIME_GLAZED_TERRACOTTA = "minecraft:lime_glazed_terracotta";
|
||||
public const LIME_SHULKER_BOX = "minecraft:lime_shulker_box";
|
||||
public const LIME_WOOL = "minecraft:lime_wool";
|
||||
public const LIT_BLAST_FURNACE = "minecraft:lit_blast_furnace";
|
||||
public const LIT_DEEPSLATE_REDSTONE_ORE = "minecraft:lit_deepslate_redstone_ore";
|
||||
@ -522,7 +554,10 @@ final class BlockTypeNames{
|
||||
public const LOOM = "minecraft:loom";
|
||||
public const MAGENTA_CANDLE = "minecraft:magenta_candle";
|
||||
public const MAGENTA_CANDLE_CAKE = "minecraft:magenta_candle_cake";
|
||||
public const MAGENTA_CARPET = "minecraft:magenta_carpet";
|
||||
public const MAGENTA_CONCRETE = "minecraft:magenta_concrete";
|
||||
public const MAGENTA_GLAZED_TERRACOTTA = "minecraft:magenta_glazed_terracotta";
|
||||
public const MAGENTA_SHULKER_BOX = "minecraft:magenta_shulker_box";
|
||||
public const MAGENTA_WOOL = "minecraft:magenta_wool";
|
||||
public const MAGMA = "minecraft:magma";
|
||||
public const MANGROVE_BUTTON = "minecraft:mangrove_button";
|
||||
@ -583,7 +618,10 @@ final class BlockTypeNames{
|
||||
public const OCHRE_FROGLIGHT = "minecraft:ochre_froglight";
|
||||
public const ORANGE_CANDLE = "minecraft:orange_candle";
|
||||
public const ORANGE_CANDLE_CAKE = "minecraft:orange_candle_cake";
|
||||
public const ORANGE_CARPET = "minecraft:orange_carpet";
|
||||
public const ORANGE_CONCRETE = "minecraft:orange_concrete";
|
||||
public const ORANGE_GLAZED_TERRACOTTA = "minecraft:orange_glazed_terracotta";
|
||||
public const ORANGE_SHULKER_BOX = "minecraft:orange_shulker_box";
|
||||
public const ORANGE_WOOL = "minecraft:orange_wool";
|
||||
public const OXIDIZED_COPPER = "minecraft:oxidized_copper";
|
||||
public const OXIDIZED_CUT_COPPER = "minecraft:oxidized_cut_copper";
|
||||
@ -595,11 +633,16 @@ final class BlockTypeNames{
|
||||
public const PEARLESCENT_FROGLIGHT = "minecraft:pearlescent_froglight";
|
||||
public const PINK_CANDLE = "minecraft:pink_candle";
|
||||
public const PINK_CANDLE_CAKE = "minecraft:pink_candle_cake";
|
||||
public const PINK_CARPET = "minecraft:pink_carpet";
|
||||
public const PINK_CONCRETE = "minecraft:pink_concrete";
|
||||
public const PINK_GLAZED_TERRACOTTA = "minecraft:pink_glazed_terracotta";
|
||||
public const PINK_PETALS = "minecraft:pink_petals";
|
||||
public const PINK_SHULKER_BOX = "minecraft:pink_shulker_box";
|
||||
public const PINK_WOOL = "minecraft:pink_wool";
|
||||
public const PISTON = "minecraft:piston";
|
||||
public const PISTON_ARM_COLLISION = "minecraft:piston_arm_collision";
|
||||
public const PITCHER_CROP = "minecraft:pitcher_crop";
|
||||
public const PITCHER_PLANT = "minecraft:pitcher_plant";
|
||||
public const PLANKS = "minecraft:planks";
|
||||
public const PODZOL = "minecraft:podzol";
|
||||
public const POINTED_DRIPSTONE = "minecraft:pointed_dripstone";
|
||||
@ -636,7 +679,10 @@ final class BlockTypeNames{
|
||||
public const PUMPKIN_STEM = "minecraft:pumpkin_stem";
|
||||
public const PURPLE_CANDLE = "minecraft:purple_candle";
|
||||
public const PURPLE_CANDLE_CAKE = "minecraft:purple_candle_cake";
|
||||
public const PURPLE_CARPET = "minecraft:purple_carpet";
|
||||
public const PURPLE_CONCRETE = "minecraft:purple_concrete";
|
||||
public const PURPLE_GLAZED_TERRACOTTA = "minecraft:purple_glazed_terracotta";
|
||||
public const PURPLE_SHULKER_BOX = "minecraft:purple_shulker_box";
|
||||
public const PURPLE_WOOL = "minecraft:purple_wool";
|
||||
public const PURPUR_BLOCK = "minecraft:purpur_block";
|
||||
public const PURPUR_STAIRS = "minecraft:purpur_stairs";
|
||||
@ -650,6 +696,8 @@ final class BlockTypeNames{
|
||||
public const RAW_IRON_BLOCK = "minecraft:raw_iron_block";
|
||||
public const RED_CANDLE = "minecraft:red_candle";
|
||||
public const RED_CANDLE_CAKE = "minecraft:red_candle_cake";
|
||||
public const RED_CARPET = "minecraft:red_carpet";
|
||||
public const RED_CONCRETE = "minecraft:red_concrete";
|
||||
public const RED_FLOWER = "minecraft:red_flower";
|
||||
public const RED_GLAZED_TERRACOTTA = "minecraft:red_glazed_terracotta";
|
||||
public const RED_MUSHROOM = "minecraft:red_mushroom";
|
||||
@ -658,6 +706,7 @@ final class BlockTypeNames{
|
||||
public const RED_NETHER_BRICK_STAIRS = "minecraft:red_nether_brick_stairs";
|
||||
public const RED_SANDSTONE = "minecraft:red_sandstone";
|
||||
public const RED_SANDSTONE_STAIRS = "minecraft:red_sandstone_stairs";
|
||||
public const RED_SHULKER_BOX = "minecraft:red_shulker_box";
|
||||
public const RED_WOOL = "minecraft:red_wool";
|
||||
public const REDSTONE_BLOCK = "minecraft:redstone_block";
|
||||
public const REDSTONE_LAMP = "minecraft:redstone_lamp";
|
||||
@ -683,7 +732,6 @@ final class BlockTypeNames{
|
||||
public const SEA_PICKLE = "minecraft:sea_pickle";
|
||||
public const SEAGRASS = "minecraft:seagrass";
|
||||
public const SHROOMLIGHT = "minecraft:shroomlight";
|
||||
public const SHULKER_BOX = "minecraft:shulker_box";
|
||||
public const SILVER_GLAZED_TERRACOTTA = "minecraft:silver_glazed_terracotta";
|
||||
public const SKULL = "minecraft:skull";
|
||||
public const SLIME = "minecraft:slime";
|
||||
@ -696,6 +744,7 @@ final class BlockTypeNames{
|
||||
public const SMOOTH_RED_SANDSTONE_STAIRS = "minecraft:smooth_red_sandstone_stairs";
|
||||
public const SMOOTH_SANDSTONE_STAIRS = "minecraft:smooth_sandstone_stairs";
|
||||
public const SMOOTH_STONE = "minecraft:smooth_stone";
|
||||
public const SNIFFER_EGG = "minecraft:sniffer_egg";
|
||||
public const SNOW = "minecraft:snow";
|
||||
public const SNOW_LAYER = "minecraft:snow_layer";
|
||||
public const SOUL_CAMPFIRE = "minecraft:soul_campfire";
|
||||
@ -767,6 +816,7 @@ final class BlockTypeNames{
|
||||
public const TRAPPED_CHEST = "minecraft:trapped_chest";
|
||||
public const TRIP_WIRE = "minecraft:trip_wire";
|
||||
public const TRIPWIRE_HOOK = "minecraft:tripwire_hook";
|
||||
public const TUBE_CORAL = "minecraft:tube_coral";
|
||||
public const TUFF = "minecraft:tuff";
|
||||
public const TURTLE_EGG = "minecraft:turtle_egg";
|
||||
public const TWISTING_VINES = "minecraft:twisting_vines";
|
||||
@ -831,7 +881,10 @@ final class BlockTypeNames{
|
||||
public const WHEAT = "minecraft:wheat";
|
||||
public const WHITE_CANDLE = "minecraft:white_candle";
|
||||
public const WHITE_CANDLE_CAKE = "minecraft:white_candle_cake";
|
||||
public const WHITE_CARPET = "minecraft:white_carpet";
|
||||
public const WHITE_CONCRETE = "minecraft:white_concrete";
|
||||
public const WHITE_GLAZED_TERRACOTTA = "minecraft:white_glazed_terracotta";
|
||||
public const WHITE_SHULKER_BOX = "minecraft:white_shulker_box";
|
||||
public const WHITE_WOOL = "minecraft:white_wool";
|
||||
public const WITHER_ROSE = "minecraft:wither_rose";
|
||||
public const WOOD = "minecraft:wood";
|
||||
@ -841,7 +894,10 @@ final class BlockTypeNames{
|
||||
public const WOODEN_SLAB = "minecraft:wooden_slab";
|
||||
public const YELLOW_CANDLE = "minecraft:yellow_candle";
|
||||
public const YELLOW_CANDLE_CAKE = "minecraft:yellow_candle_cake";
|
||||
public const YELLOW_CARPET = "minecraft:yellow_carpet";
|
||||
public const YELLOW_CONCRETE = "minecraft:yellow_concrete";
|
||||
public const YELLOW_FLOWER = "minecraft:yellow_flower";
|
||||
public const YELLOW_GLAZED_TERRACOTTA = "minecraft:yellow_glazed_terracotta";
|
||||
public const YELLOW_SHULKER_BOX = "minecraft:yellow_shulker_box";
|
||||
public const YELLOW_WOOL = "minecraft:yellow_wool";
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ use pocketmine\block\Froglight;
|
||||
use pocketmine\block\FrostedIce;
|
||||
use pocketmine\block\Furnace;
|
||||
use pocketmine\block\GlazedTerracotta;
|
||||
use pocketmine\block\GlowLichen;
|
||||
use pocketmine\block\HayBale;
|
||||
use pocketmine\block\Hopper;
|
||||
use pocketmine\block\ItemFrame;
|
||||
@ -93,6 +94,7 @@ use pocketmine\block\LightningRod;
|
||||
use pocketmine\block\LitPumpkin;
|
||||
use pocketmine\block\Loom;
|
||||
use pocketmine\block\MelonStem;
|
||||
use pocketmine\block\MobHead;
|
||||
use pocketmine\block\NetherPortal;
|
||||
use pocketmine\block\NetherVines;
|
||||
use pocketmine\block\NetherWartPlant;
|
||||
@ -112,7 +114,6 @@ use pocketmine\block\Sapling;
|
||||
use pocketmine\block\SeaPickle;
|
||||
use pocketmine\block\SimplePillar;
|
||||
use pocketmine\block\SimplePressurePlate;
|
||||
use pocketmine\block\Skull;
|
||||
use pocketmine\block\Slab;
|
||||
use pocketmine\block\SnowLayer;
|
||||
use pocketmine\block\Sponge;
|
||||
@ -169,7 +170,6 @@ use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function class_parents;
|
||||
use function get_class;
|
||||
|
||||
final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
@ -177,8 +177,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
* These callables actually accept Block, but for the sake of type completeness, it has to be never, since we can't
|
||||
* describe the bottom type of a type hierarchy only containing Block.
|
||||
*
|
||||
* @var \Closure[][]
|
||||
* @phpstan-var array<int, array<class-string, \Closure(never) : Writer>>
|
||||
* @var \Closure[]
|
||||
* @phpstan-var array<int, \Closure(never) : Writer>
|
||||
*/
|
||||
private array $serializers = [];
|
||||
|
||||
@ -191,8 +191,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
public function __construct(){
|
||||
$this->registerCandleSerializers();
|
||||
$this->registerFlatColorBlockSerializers();
|
||||
$this->registerFlatCoralSerializers();
|
||||
$this->registerCauldronSerializers();
|
||||
$this->registerWoodBlockSerializers();
|
||||
$this->registerFlatWoodBlockSerializers();
|
||||
$this->registerLegacyWoodBlockSerializers();
|
||||
$this->registerLeavesSerializers();
|
||||
$this->registerSimpleSerializers();
|
||||
$this->registerSerializers();
|
||||
}
|
||||
@ -212,7 +215,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
if(isset($this->serializers[$block->getTypeId()])){
|
||||
throw new \InvalidArgumentException("Block type ID " . $block->getTypeId() . " already has a serializer registered");
|
||||
}
|
||||
$this->serializers[$block->getTypeId()][get_class($block)] = $serializer;
|
||||
$this->serializers[$block->getTypeId()] = $serializer;
|
||||
}
|
||||
|
||||
public function mapSimple(Block $block, string $id) : void{
|
||||
@ -240,21 +243,16 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
public function serializeBlock(Block $blockState) : BlockStateData{
|
||||
$typeId = $blockState->getTypeId();
|
||||
|
||||
$locatedSerializer = $this->serializers[$typeId][get_class($blockState)] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
foreach(class_parents($blockState) as $parent){
|
||||
if(isset($this->serializers[$typeId][$parent])){
|
||||
$locatedSerializer = $this->serializers[$typeId][$parent];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$locatedSerializer = $this->serializers[$typeId] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
throw new BlockStateSerializeException("No serializer registered for " . get_class($blockState) . " with type ID $typeId");
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: there is no guarantee that this type actually matches that of $blockState - a plugin may have stolen
|
||||
* the type ID of the block (which never makes sense, even in a world where overriding block types is a thing).
|
||||
* In the future we'll need some way to guarantee that type IDs are never reused (perhaps spl_object_id()?)
|
||||
*
|
||||
* @var \Closure $serializer
|
||||
* @phpstan-var \Closure(TBlockType) : Writer $serializer
|
||||
*/
|
||||
@ -352,30 +350,289 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
DyeColor::YELLOW() => Ids::YELLOW_WOOL,
|
||||
default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
|
||||
}));
|
||||
|
||||
$this->map(Blocks::CARPET(), fn(Carpet $block) => Writer::create(match($color = $block->getColor()){
|
||||
DyeColor::BLACK() => Ids::BLACK_CARPET,
|
||||
DyeColor::BLUE() => Ids::BLUE_CARPET,
|
||||
DyeColor::BROWN() => Ids::BROWN_CARPET,
|
||||
DyeColor::CYAN() => Ids::CYAN_CARPET,
|
||||
DyeColor::GRAY() => Ids::GRAY_CARPET,
|
||||
DyeColor::GREEN() => Ids::GREEN_CARPET,
|
||||
DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_CARPET,
|
||||
DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_CARPET,
|
||||
DyeColor::LIME() => Ids::LIME_CARPET,
|
||||
DyeColor::MAGENTA() => Ids::MAGENTA_CARPET,
|
||||
DyeColor::ORANGE() => Ids::ORANGE_CARPET,
|
||||
DyeColor::PINK() => Ids::PINK_CARPET,
|
||||
DyeColor::PURPLE() => Ids::PURPLE_CARPET,
|
||||
DyeColor::RED() => Ids::RED_CARPET,
|
||||
DyeColor::WHITE() => Ids::WHITE_CARPET,
|
||||
DyeColor::YELLOW() => Ids::YELLOW_CARPET,
|
||||
default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
|
||||
}));
|
||||
|
||||
$this->map(Blocks::DYED_SHULKER_BOX(), fn(DyedShulkerBox $block) => Writer::create(match($color = $block->getColor()){
|
||||
DyeColor::BLACK() => Ids::BLACK_SHULKER_BOX,
|
||||
DyeColor::BLUE() => Ids::BLUE_SHULKER_BOX,
|
||||
DyeColor::BROWN() => Ids::BROWN_SHULKER_BOX,
|
||||
DyeColor::CYAN() => Ids::CYAN_SHULKER_BOX,
|
||||
DyeColor::GRAY() => Ids::GRAY_SHULKER_BOX,
|
||||
DyeColor::GREEN() => Ids::GREEN_SHULKER_BOX,
|
||||
DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_SHULKER_BOX,
|
||||
DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_SHULKER_BOX,
|
||||
DyeColor::LIME() => Ids::LIME_SHULKER_BOX,
|
||||
DyeColor::MAGENTA() => Ids::MAGENTA_SHULKER_BOX,
|
||||
DyeColor::ORANGE() => Ids::ORANGE_SHULKER_BOX,
|
||||
DyeColor::PINK() => Ids::PINK_SHULKER_BOX,
|
||||
DyeColor::PURPLE() => Ids::PURPLE_SHULKER_BOX,
|
||||
DyeColor::RED() => Ids::RED_SHULKER_BOX,
|
||||
DyeColor::WHITE() => Ids::WHITE_SHULKER_BOX,
|
||||
DyeColor::YELLOW() => Ids::YELLOW_SHULKER_BOX,
|
||||
default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
|
||||
}));
|
||||
|
||||
$this->map(Blocks::CONCRETE(), fn(Concrete $block) => Writer::create(match($color = $block->getColor()){
|
||||
DyeColor::BLACK() => Ids::BLACK_CONCRETE,
|
||||
DyeColor::BLUE() => Ids::BLUE_CONCRETE,
|
||||
DyeColor::BROWN() => Ids::BROWN_CONCRETE,
|
||||
DyeColor::CYAN() => Ids::CYAN_CONCRETE,
|
||||
DyeColor::GRAY() => Ids::GRAY_CONCRETE,
|
||||
DyeColor::GREEN() => Ids::GREEN_CONCRETE,
|
||||
DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_CONCRETE,
|
||||
DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_CONCRETE,
|
||||
DyeColor::LIME() => Ids::LIME_CONCRETE,
|
||||
DyeColor::MAGENTA() => Ids::MAGENTA_CONCRETE,
|
||||
DyeColor::ORANGE() => Ids::ORANGE_CONCRETE,
|
||||
DyeColor::PINK() => Ids::PINK_CONCRETE,
|
||||
DyeColor::PURPLE() => Ids::PURPLE_CONCRETE,
|
||||
DyeColor::RED() => Ids::RED_CONCRETE,
|
||||
DyeColor::WHITE() => Ids::WHITE_CONCRETE,
|
||||
DyeColor::YELLOW() => Ids::YELLOW_CONCRETE,
|
||||
default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
|
||||
}));
|
||||
}
|
||||
|
||||
private function registerFlatCoralSerializers() : void{
|
||||
$this->map(Blocks::CORAL(), fn(Coral $block) => Writer::create(
|
||||
match($coralType = $block->getCoralType()){
|
||||
CoralType::BRAIN() => $block->isDead() ? Ids::DEAD_BRAIN_CORAL : Ids::BRAIN_CORAL,
|
||||
CoralType::BUBBLE() => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL : Ids::BUBBLE_CORAL,
|
||||
CoralType::FIRE() => $block->isDead() ? Ids::DEAD_FIRE_CORAL : Ids::FIRE_CORAL,
|
||||
CoralType::HORN() => $block->isDead() ? Ids::DEAD_HORN_CORAL : Ids::HORN_CORAL,
|
||||
CoralType::TUBE() => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL,
|
||||
default => throw new AssumptionFailedError("Unhandled coral type " . $coralType->name())
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
private function registerCauldronSerializers() : void{
|
||||
$this->map(Blocks::CAULDRON(), fn() => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0, new Writer(Ids::CAULDRON)));
|
||||
$this->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel(), new Writer(Ids::LAVA_CAULDRON)));
|
||||
$this->map(Blocks::CAULDRON(), fn() => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0));
|
||||
$this->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel()));
|
||||
//potion cauldrons store their real information in the block actor data
|
||||
$this->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel(), new Writer(Ids::CAULDRON)));
|
||||
$this->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel(), new Writer(Ids::CAULDRON)));
|
||||
$this->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel()));
|
||||
$this->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel()));
|
||||
}
|
||||
|
||||
private function registerWoodBlockSerializers() : void{
|
||||
$this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE);
|
||||
$this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE);
|
||||
$this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE);
|
||||
$this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE);
|
||||
$this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE);
|
||||
$this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE);
|
||||
|
||||
private function registerFlatWoodBlockSerializers() : void{
|
||||
$this->map(Blocks::ACACIA_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::ACACIA_BUTTON)));
|
||||
$this->map(Blocks::ACACIA_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::ACACIA_DOOR)));
|
||||
$this->map(Blocks::ACACIA_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::ACACIA_FENCE_GATE)));
|
||||
$this->map(Blocks::ACACIA_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::ACACIA_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::ACACIA_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::ACACIA_STANDING_SIGN)));
|
||||
$this->map(Blocks::ACACIA_STAIRS(), fn(WoodenStairs $block) => Helper::encodeStairs($block, new Writer(Ids::ACACIA_STAIRS)));
|
||||
$this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR)));
|
||||
$this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG);
|
||||
$this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE);
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON)));
|
||||
$this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR)));
|
||||
$this->map(Blocks::BIRCH_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::BIRCH_FENCE_GATE)));
|
||||
$this->map(Blocks::BIRCH_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::BIRCH_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::BIRCH_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::BIRCH_STANDING_SIGN)));
|
||||
$this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR)));
|
||||
$this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG);
|
||||
$this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE);
|
||||
$this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS);
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::CHERRY_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CHERRY_BUTTON)));
|
||||
$this->map(Blocks::CHERRY_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CHERRY_DOOR)));
|
||||
$this->map(Blocks::CHERRY_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CHERRY_FENCE_GATE)));
|
||||
$this->map(Blocks::CHERRY_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG));
|
||||
$this->map(Blocks::CHERRY_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CHERRY_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::CHERRY_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CHERRY_STANDING_SIGN)));
|
||||
$this->map(Blocks::CHERRY_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CHERRY_TRAPDOOR)));
|
||||
$this->map(Blocks::CHERRY_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CHERRY_WALL_SIGN)));
|
||||
$this->mapSimple(Blocks::CHERRY_FENCE(), Ids::CHERRY_FENCE);
|
||||
$this->mapSimple(Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS);
|
||||
$this->mapSlab(Blocks::CHERRY_SLAB(), Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS);
|
||||
$this->map(Blocks::CHERRY_WOOD(), function(Wood $block) : Writer{
|
||||
//we can't use the standard method for this because cherry_wood has a useless property attached to it
|
||||
if(!$block->isStripped()){
|
||||
return Writer::create(Ids::CHERRY_WOOD)
|
||||
->writePillarAxis($block->getAxis())
|
||||
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
|
||||
}else{
|
||||
return Writer::create(Ids::STRIPPED_CHERRY_WOOD)
|
||||
->writePillarAxis($block->getAxis());
|
||||
}
|
||||
});
|
||||
|
||||
$this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON)));
|
||||
$this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR)));
|
||||
$this->map(Blocks::CRIMSON_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CRIMSON_FENCE_GATE)));
|
||||
$this->map(Blocks::CRIMSON_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE));
|
||||
$this->map(Blocks::CRIMSON_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CRIMSON_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::CRIMSON_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CRIMSON_STANDING_SIGN)));
|
||||
$this->map(Blocks::CRIMSON_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM));
|
||||
$this->map(Blocks::CRIMSON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CRIMSON_TRAPDOOR)));
|
||||
$this->map(Blocks::CRIMSON_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CRIMSON_WALL_SIGN)));
|
||||
$this->mapSimple(Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE);
|
||||
$this->mapSimple(Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS);
|
||||
$this->mapSlab(Blocks::CRIMSON_SLAB(), Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS);
|
||||
|
||||
$this->map(Blocks::DARK_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::DARK_OAK_BUTTON)));
|
||||
$this->map(Blocks::DARK_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::DARK_OAK_DOOR)));
|
||||
$this->map(Blocks::DARK_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::DARK_OAK_FENCE_GATE)));
|
||||
$this->map(Blocks::DARK_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::DARK_OAK_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::DARK_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::DARKOAK_STANDING_SIGN)));
|
||||
$this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR)));
|
||||
$this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG);
|
||||
$this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE);
|
||||
$this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS);
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON)));
|
||||
$this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR)));
|
||||
$this->map(Blocks::JUNGLE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::JUNGLE_FENCE_GATE)));
|
||||
$this->map(Blocks::JUNGLE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::JUNGLE_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::JUNGLE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::JUNGLE_STANDING_SIGN)));
|
||||
$this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR)));
|
||||
$this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN)));
|
||||
$this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG);
|
||||
$this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE);
|
||||
$this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS);
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON)));
|
||||
$this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR)));
|
||||
$this->map(Blocks::MANGROVE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::MANGROVE_FENCE_GATE)));
|
||||
$this->map(Blocks::MANGROVE_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG));
|
||||
$this->map(Blocks::MANGROVE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::MANGROVE_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::MANGROVE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::MANGROVE_STANDING_SIGN)));
|
||||
$this->map(Blocks::MANGROVE_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::MANGROVE_TRAPDOOR)));
|
||||
$this->map(Blocks::MANGROVE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::MANGROVE_WALL_SIGN)));
|
||||
$this->mapSimple(Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE);
|
||||
$this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS);
|
||||
$this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS);
|
||||
$this->map(Blocks::MANGROVE_WOOD(), function(Wood $block) : Writer{
|
||||
//we can't use the standard method for this because mangrove_wood has a useless property attached to it
|
||||
if(!$block->isStripped()){
|
||||
return Writer::create(Ids::MANGROVE_WOOD)
|
||||
->writePillarAxis($block->getAxis())
|
||||
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
|
||||
}else{
|
||||
return Writer::create(Ids::STRIPPED_MANGROVE_WOOD)
|
||||
->writePillarAxis($block->getAxis());
|
||||
}
|
||||
});
|
||||
|
||||
$this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON)));
|
||||
$this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR)));
|
||||
$this->map(Blocks::OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::FENCE_GATE)));
|
||||
$this->map(Blocks::OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WOODEN_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::STANDING_SIGN)));
|
||||
$this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR)));
|
||||
$this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN)));
|
||||
$this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG);
|
||||
$this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE);
|
||||
$this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS);
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE);
|
||||
$this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG);
|
||||
$this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON)));
|
||||
$this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR)));
|
||||
$this->map(Blocks::SPRUCE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::SPRUCE_FENCE_GATE)));
|
||||
$this->map(Blocks::SPRUCE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::SPRUCE_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::SPRUCE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::SPRUCE_STANDING_SIGN)));
|
||||
$this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS);
|
||||
$this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR)));
|
||||
$this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN)));
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Blocks::WARPED_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::WARPED_BUTTON)));
|
||||
$this->map(Blocks::WARPED_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::WARPED_DOOR)));
|
||||
$this->map(Blocks::WARPED_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::WARPED_FENCE_GATE)));
|
||||
$this->map(Blocks::WARPED_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE));
|
||||
$this->map(Blocks::WARPED_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WARPED_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::WARPED_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::WARPED_STANDING_SIGN)));
|
||||
$this->map(Blocks::WARPED_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM));
|
||||
$this->map(Blocks::WARPED_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::WARPED_TRAPDOOR)));
|
||||
$this->map(Blocks::WARPED_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WARPED_WALL_SIGN)));
|
||||
$this->mapSimple(Blocks::WARPED_FENCE(), Ids::WARPED_FENCE);
|
||||
$this->mapSimple(Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS);
|
||||
$this->mapSlab(Blocks::WARPED_SLAB(), Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS);
|
||||
}
|
||||
|
||||
private function registerLegacyWoodBlockSerializers() : void{
|
||||
foreach([
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_PLANKS(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_PLANKS(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_PLANKS(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_PLANKS(),
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_PLANKS(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_PLANKS(),
|
||||
] as $woodType => $block){
|
||||
$this->map($block, fn() => Writer::create(Ids::PLANKS)
|
||||
->writeString(StateNames::WOOD_TYPE, $woodType));
|
||||
}
|
||||
|
||||
foreach([
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_SLAB(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_SLAB(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_SLAB(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_SLAB(),
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_SLAB(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_SLAB(),
|
||||
] as $woodType => $block){
|
||||
$this->map($block, fn(Slab $block) => Helper::encodeWoodenSlab($block, $woodType));
|
||||
}
|
||||
|
||||
foreach([
|
||||
Blocks::ACACIA_WOOD(),
|
||||
Blocks::BIRCH_WOOD(),
|
||||
Blocks::DARK_OAK_WOOD(),
|
||||
Blocks::JUNGLE_WOOD(),
|
||||
Blocks::OAK_WOOD(),
|
||||
Blocks::SPRUCE_WOOD(),
|
||||
] as $block){
|
||||
$this->map($block, fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
}
|
||||
}
|
||||
|
||||
private function registerLeavesSerializers() : void{
|
||||
//flattened IDs
|
||||
$this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES)));
|
||||
$this->map(Blocks::CHERRY_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::CHERRY_LEAVES)));
|
||||
$this->map(Blocks::FLOWERING_AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES_FLOWERED)));
|
||||
$this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES)));
|
||||
|
||||
//legacy mess
|
||||
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_ACACIA));
|
||||
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_BIRCH));
|
||||
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_DARK_OAK));
|
||||
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_JUNGLE));
|
||||
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_OAK));
|
||||
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_SPRUCE));
|
||||
}
|
||||
|
||||
private function registerSimpleSerializers() : void{
|
||||
@ -408,8 +665,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS);
|
||||
$this->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS);
|
||||
$this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE);
|
||||
$this->mapSimple(Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE);
|
||||
$this->mapSimple(Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS);
|
||||
$this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN);
|
||||
$this->mapSimple(Blocks::DANDELION(), Ids::YELLOW_FLOWER);
|
||||
$this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH);
|
||||
@ -579,8 +834,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::LEGACY_STONECUTTER(), Ids::STONECUTTER);
|
||||
$this->mapSimple(Blocks::LILY_PAD(), Ids::WATERLILY);
|
||||
$this->mapSimple(Blocks::MAGMA(), Ids::MAGMA);
|
||||
$this->mapSimple(Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE);
|
||||
$this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS);
|
||||
$this->mapSimple(Blocks::MANGROVE_ROOTS(), Ids::MANGROVE_ROOTS);
|
||||
$this->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK);
|
||||
$this->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER);
|
||||
@ -627,27 +880,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM);
|
||||
$this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS);
|
||||
$this->mapSimple(Blocks::TUFF(), Ids::TUFF);
|
||||
$this->mapSimple(Blocks::WARPED_FENCE(), Ids::WARPED_FENCE);
|
||||
$this->mapSimple(Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS);
|
||||
$this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK);
|
||||
$this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE);
|
||||
}
|
||||
|
||||
private function registerSerializers() : void{
|
||||
$this->map(Blocks::ACACIA_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::ACACIA_BUTTON)));
|
||||
$this->map(Blocks::ACACIA_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::ACACIA_DOOR)));
|
||||
$this->map(Blocks::ACACIA_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::ACACIA_FENCE_GATE)));
|
||||
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_ACACIA));
|
||||
$this->map(Blocks::ACACIA_PLANKS(), fn() => Writer::create(Ids::PLANKS)
|
||||
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_ACACIA));
|
||||
$this->map(Blocks::ACACIA_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::ACACIA_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::ACACIA_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_ACACIA));
|
||||
$this->map(Blocks::ACACIA_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::ACACIA_STANDING_SIGN)));
|
||||
$this->map(Blocks::ACACIA_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_ACACIA));
|
||||
$this->map(Blocks::ACACIA_STAIRS(), fn(WoodenStairs $block) => Helper::encodeStairs($block, new Writer(Ids::ACACIA_STAIRS)));
|
||||
$this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR)));
|
||||
$this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN)));
|
||||
$this->map(Blocks::ACACIA_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
$this->map(Blocks::ACTIVATOR_RAIL(), function(ActivatorRail $block) : Writer{
|
||||
return Writer::create(Ids::ACTIVATOR_RAIL)
|
||||
->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered())
|
||||
@ -670,7 +908,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"),
|
||||
});
|
||||
});
|
||||
$this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES)));
|
||||
$this->map(Blocks::AZURE_BLUET(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_HOUSTONIA));
|
||||
$this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{
|
||||
return Writer::create(Ids::BAMBOO)
|
||||
@ -721,20 +958,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeLegacyHorizontalFacing($block->getFacing());
|
||||
|
||||
});
|
||||
$this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON)));
|
||||
$this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR)));
|
||||
$this->map(Blocks::BIRCH_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::BIRCH_FENCE_GATE)));
|
||||
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_BIRCH));
|
||||
$this->map(Blocks::BIRCH_PLANKS(), fn() => Writer::create(Ids::PLANKS)
|
||||
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_BIRCH));
|
||||
$this->map(Blocks::BIRCH_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::BIRCH_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::BIRCH_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_BIRCH));
|
||||
$this->map(Blocks::BIRCH_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::BIRCH_STANDING_SIGN)));
|
||||
$this->map(Blocks::BIRCH_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_BIRCH));
|
||||
$this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS);
|
||||
$this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR)));
|
||||
$this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN)));
|
||||
$this->map(Blocks::BIRCH_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
$this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS);
|
||||
$this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL)));
|
||||
@ -764,14 +988,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::CAKE)
|
||||
->writeInt(StateNames::BITE_COUNTER, $block->getBites());
|
||||
});
|
||||
$this->map(Blocks::CARPET(), function(Carpet $block) : Writer{
|
||||
return Writer::create(Ids::CARPET)
|
||||
->writeColor($block->getColor());
|
||||
});
|
||||
$this->map(Blocks::CARROTS(), fn(Carrot $block) => Helper::encodeCrops($block, new Writer(Ids::CARROTS)));
|
||||
$this->map(Blocks::CARVED_PUMPKIN(), function(CarvedPumpkin $block) : Writer{
|
||||
return Writer::create(Ids::CARVED_PUMPKIN)
|
||||
->writeLegacyHorizontalFacing($block->getFacing());
|
||||
->writeCardinalHorizontalFacing($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::CAVE_VINES(), function(CaveVines $block) : Writer{
|
||||
//I have no idea why this only has 3 IDs - there are 4 in Java and 4 visually distinct states in Bedrock
|
||||
@ -886,19 +1106,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing()));
|
||||
});
|
||||
$this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR, new Writer(Ids::CHEMISTRY_TABLE)));
|
||||
$this->map(Blocks::CONCRETE(), function(Concrete $block) : Writer{
|
||||
return Writer::create(Ids::CONCRETE)
|
||||
->writeColor($block->getColor());
|
||||
});
|
||||
$this->map(Blocks::CONCRETE_POWDER(), function(ConcretePowder $block) : Writer{
|
||||
return Writer::create(Ids::CONCRETE_POWDER)
|
||||
->writeColor($block->getColor());
|
||||
});
|
||||
$this->map(Blocks::CORAL(), function(Coral $block) : Writer{
|
||||
return Writer::create(Ids::CORAL)
|
||||
->writeBool(StateNames::DEAD_BIT, $block->isDead())
|
||||
->writeCoralType($block->getCoralType());
|
||||
});
|
||||
$this->map(Blocks::CORAL_BLOCK(), function(CoralBlock $block) : Writer{
|
||||
return Writer::create(Ids::CORAL_BLOCK)
|
||||
->writeBool(StateNames::DEAD_BIT, $block->isDead())
|
||||
@ -915,35 +1126,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
});
|
||||
$this->map(Blocks::CORNFLOWER(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_CORNFLOWER));
|
||||
$this->map(Blocks::CRACKED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CRACKED));
|
||||
$this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON)));
|
||||
$this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR)));
|
||||
$this->map(Blocks::CRIMSON_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CRIMSON_FENCE_GATE)));
|
||||
$this->map(Blocks::CRIMSON_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE));
|
||||
$this->map(Blocks::CRIMSON_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CRIMSON_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::CRIMSON_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CRIMSON_STANDING_SIGN)));
|
||||
$this->mapSlab(Blocks::CRIMSON_SLAB(), Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS);
|
||||
$this->map(Blocks::CRIMSON_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM));
|
||||
$this->map(Blocks::CRIMSON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CRIMSON_TRAPDOOR)));
|
||||
$this->map(Blocks::CRIMSON_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CRIMSON_WALL_SIGN)));
|
||||
$this->map(Blocks::CUT_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
|
||||
$this->map(Blocks::CUT_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE));
|
||||
$this->map(Blocks::CUT_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
|
||||
$this->map(Blocks::CUT_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE));
|
||||
$this->map(Blocks::DARK_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::DARK_OAK_BUTTON)));
|
||||
$this->map(Blocks::DARK_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::DARK_OAK_DOOR)));
|
||||
$this->map(Blocks::DARK_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::DARK_OAK_FENCE_GATE)));
|
||||
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_DARK_OAK));
|
||||
$this->map(Blocks::DARK_OAK_PLANKS(), fn() => Writer::create(Ids::PLANKS)
|
||||
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_DARK_OAK));
|
||||
$this->map(Blocks::DARK_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::DARK_OAK_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::DARK_OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_DARK_OAK));
|
||||
$this->map(Blocks::DARK_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::DARKOAK_STANDING_SIGN)));
|
||||
$this->map(Blocks::DARK_OAK_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_DARK_OAK));
|
||||
$this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS);
|
||||
$this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR)));
|
||||
$this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN)));
|
||||
$this->map(Blocks::DARK_OAK_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
$this->map(Blocks::DARK_PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE)
|
||||
->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DARK));
|
||||
$this->map(Blocks::DARK_PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK));
|
||||
@ -985,10 +1172,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
});
|
||||
});
|
||||
$this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_GRASS, Writer::create(Ids::DOUBLE_PLANT)));
|
||||
$this->map(Blocks::DYED_SHULKER_BOX(), function(DyedShulkerBox $block) : Writer{
|
||||
return Writer::create(Ids::SHULKER_BOX)
|
||||
->writeColor($block->getColor());
|
||||
});
|
||||
$this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR, new Writer(Ids::CHEMISTRY_TABLE)));
|
||||
$this->map(Blocks::ENDER_CHEST(), function(EnderChest $block) : Writer{
|
||||
return Writer::create(Ids::ENDER_CHEST)
|
||||
@ -1021,7 +1204,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::FLOWER_POT)
|
||||
->writeBool(StateNames::UPDATE_BIT, false); //to keep MCPE happy
|
||||
});
|
||||
$this->map(Blocks::FLOWERING_AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES_FLOWERED)));
|
||||
$this->map(Blocks::FROGLIGHT(), function(Froglight $block){
|
||||
return Writer::create(match($block->getFroglightType()){
|
||||
FroglightType::OCHRE() => Ids::OCHRE_FROGLIGHT,
|
||||
@ -1036,6 +1218,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeInt(StateNames::AGE, $block->getAge());
|
||||
});
|
||||
$this->map(Blocks::FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::FURNACE, Ids::LIT_FURNACE));
|
||||
$this->map(Blocks::GLOW_LICHEN(), function(GlowLichen $block) : Writer{
|
||||
return Writer::create(Ids::GLOW_LICHEN)
|
||||
->writeFacingFlags($block->getFaces());
|
||||
});
|
||||
$this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME));
|
||||
$this->map(Blocks::GRANITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_GRANITE));
|
||||
$this->map(Blocks::GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_GRANITE));
|
||||
@ -1067,20 +1253,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR)));
|
||||
$this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR)));
|
||||
$this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME));
|
||||
$this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON)));
|
||||
$this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR)));
|
||||
$this->map(Blocks::JUNGLE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::JUNGLE_FENCE_GATE)));
|
||||
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_JUNGLE));
|
||||
$this->map(Blocks::JUNGLE_PLANKS(), fn() => Writer::create(Ids::PLANKS)
|
||||
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_JUNGLE));
|
||||
$this->map(Blocks::JUNGLE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::JUNGLE_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::JUNGLE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_JUNGLE));
|
||||
$this->map(Blocks::JUNGLE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::JUNGLE_STANDING_SIGN)));
|
||||
$this->map(Blocks::JUNGLE_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_JUNGLE));
|
||||
$this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS);
|
||||
$this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR)));
|
||||
$this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN)));
|
||||
$this->map(Blocks::JUNGLE_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
$this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE, new Writer(Ids::CHEMISTRY_TABLE)));
|
||||
$this->map(Blocks::LADDER(), function(Ladder $block) : Writer{
|
||||
return Writer::create(Ids::LADDER)
|
||||
@ -1124,37 +1297,15 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::LILY_OF_THE_VALLEY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_LILY_OF_THE_VALLEY));
|
||||
$this->map(Blocks::LIT_PUMPKIN(), function(LitPumpkin $block) : Writer{
|
||||
return Writer::create(Ids::LIT_PUMPKIN)
|
||||
->writeLegacyHorizontalFacing($block->getFacing());
|
||||
->writeCardinalHorizontalFacing($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::LOOM(), function(Loom $block) : Writer{
|
||||
return Writer::create(Ids::LOOM)
|
||||
->writeLegacyHorizontalFacing($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON)));
|
||||
$this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR)));
|
||||
$this->map(Blocks::MANGROVE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::MANGROVE_FENCE_GATE)));
|
||||
$this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES)));
|
||||
$this->map(Blocks::MANGROVE_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG));
|
||||
$this->map(Blocks::MANGROVE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::MANGROVE_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::MANGROVE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::MANGROVE_STANDING_SIGN)));
|
||||
$this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS);
|
||||
$this->map(Blocks::MANGROVE_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::MANGROVE_TRAPDOOR)));
|
||||
$this->map(Blocks::MANGROVE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::MANGROVE_WALL_SIGN)));
|
||||
$this->map(Blocks::MANGROVE_WOOD(), function(Wood $block) : Writer{
|
||||
//we can't use the standard method for this because mangrove_wood has a useless property attached to it
|
||||
if(!$block->isStripped()){
|
||||
return Writer::create(Ids::MANGROVE_WOOD)
|
||||
->writePillarAxis($block->getAxis())
|
||||
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
|
||||
}else{
|
||||
return Writer::create(Ids::STRIPPED_MANGROVE_WOOD)
|
||||
->writePillarAxis($block->getAxis());
|
||||
}
|
||||
});
|
||||
$this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER, new Writer(Ids::CHEMISTRY_TABLE)));
|
||||
$this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM)));
|
||||
$this->map(Blocks::MOB_HEAD(), function(Skull $block) : Writer{
|
||||
$this->map(Blocks::MOB_HEAD(), function(MobHead $block) : Writer{
|
||||
return Writer::create(Ids::SKULL)
|
||||
->writeFacingWithoutDown($block->getFacing());
|
||||
});
|
||||
@ -1187,20 +1338,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::NETHER_WART)
|
||||
->writeInt(StateNames::AGE, $block->getAge());
|
||||
});
|
||||
$this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON)));
|
||||
$this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR)));
|
||||
$this->map(Blocks::OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::FENCE_GATE)));
|
||||
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_OAK));
|
||||
$this->map(Blocks::OAK_PLANKS(), fn() => Writer::create(Ids::PLANKS)
|
||||
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_OAK));
|
||||
$this->map(Blocks::OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WOODEN_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_OAK));
|
||||
$this->map(Blocks::OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::STANDING_SIGN)));
|
||||
$this->map(Blocks::OAK_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_OAK));
|
||||
$this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS);
|
||||
$this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR)));
|
||||
$this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN)));
|
||||
$this->map(Blocks::OAK_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
$this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE));
|
||||
$this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE));
|
||||
$this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT)));
|
||||
@ -1247,7 +1385,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_PRISMARINE));
|
||||
$this->map(Blocks::PUMPKIN(), function() : Writer{
|
||||
return Writer::create(Ids::PUMPKIN)
|
||||
->writeLegacyHorizontalFacing(Facing::SOUTH); //no longer used
|
||||
->writeCardinalHorizontalFacing(Facing::SOUTH); //no longer used
|
||||
});
|
||||
$this->map(Blocks::PUMPKIN_STEM(), fn(PumpkinStem $block) => Helper::encodeStem($block, new Writer(Ids::PUMPKIN_STEM)));
|
||||
$this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, true, Writer::create(Ids::COLORED_TORCH_BP)));
|
||||
@ -1348,20 +1486,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::SPONGE)
|
||||
->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY);
|
||||
});
|
||||
$this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON)));
|
||||
$this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR)));
|
||||
$this->map(Blocks::SPRUCE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::SPRUCE_FENCE_GATE)));
|
||||
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_SPRUCE));
|
||||
$this->map(Blocks::SPRUCE_PLANKS(), fn() => Writer::create(Ids::PLANKS)
|
||||
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_SPRUCE));
|
||||
$this->map(Blocks::SPRUCE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::SPRUCE_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::SPRUCE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_SPRUCE));
|
||||
$this->map(Blocks::SPRUCE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::SPRUCE_STANDING_SIGN)));
|
||||
$this->map(Blocks::SPRUCE_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_SPRUCE));
|
||||
$this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS);
|
||||
$this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR)));
|
||||
$this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN)));
|
||||
$this->map(Blocks::SPRUCE_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
|
||||
$this->map(Blocks::STAINED_CLAY(), function(StainedHardenedClay $block) : Writer{
|
||||
return Writer::create(Ids::STAINED_HARDENED_CLAY)
|
||||
->writeColor($block->getColor());
|
||||
@ -1458,17 +1583,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeBool(StateNames::DEAD_BIT, $block->isDead())
|
||||
->writeCoralFacing($block->getFacing());
|
||||
});
|
||||
$this->map(Blocks::WARPED_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::WARPED_BUTTON)));
|
||||
$this->map(Blocks::WARPED_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::WARPED_DOOR)));
|
||||
$this->map(Blocks::WARPED_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::WARPED_FENCE_GATE)));
|
||||
$this->map(Blocks::WARPED_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE));
|
||||
$this->map(Blocks::WARPED_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WARPED_PRESSURE_PLATE)));
|
||||
$this->map(Blocks::WARPED_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::WARPED_STANDING_SIGN)));
|
||||
$this->mapSlab(Blocks::WARPED_SLAB(), Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS);
|
||||
$this->map(Blocks::WARPED_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM));
|
||||
$this->map(Blocks::WARPED_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::WARPED_TRAPDOOR)));
|
||||
$this->map(Blocks::WARPED_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WARPED_WALL_SIGN)));
|
||||
$this->map(Blocks::WATER(), fn(Water $block) => Helper::encodeLiquid($block, Ids::WATER, Ids::FLOWING_WATER));
|
||||
$this->map(Blocks::WEEPING_VINES(), function(NetherVines $block) : Writer{
|
||||
return Writer::create(Ids::WEEPING_VINES)
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\utils\WallConnectionType;
|
||||
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
|
||||
use pocketmine\data\bedrock\block\BlockStateNames;
|
||||
@ -134,6 +135,29 @@ final class BlockStateReader{
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<int, int>
|
||||
*/
|
||||
public function readFacingFlags() : array{
|
||||
$result = [];
|
||||
$flags = $this->readBoundedInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, 0, 63);
|
||||
foreach([
|
||||
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN => Facing::DOWN,
|
||||
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP => Facing::UP,
|
||||
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH => Facing::NORTH,
|
||||
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH => Facing::SOUTH,
|
||||
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST => Facing::WEST,
|
||||
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST => Facing::EAST
|
||||
] as $flag => $facing){
|
||||
if(($flags & $flag) !== 0){
|
||||
$result[$facing] = $facing;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public function readEndRodFacingDirection() : int{
|
||||
$result = $this->readFacingDirection();
|
||||
@ -185,6 +209,20 @@ final class BlockStateReader{
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by pumpkins as of 1.20.0.23 beta
|
||||
* @throws BlockStateDeserializeException
|
||||
*/
|
||||
public function readCardinalHorizontalFacing() : int{
|
||||
return match($raw = $this->readString(BlockStateNames::MC_CARDINAL_DIRECTION)){
|
||||
StringValues::MC_CARDINAL_DIRECTION_NORTH => Facing::NORTH,
|
||||
StringValues::MC_CARDINAL_DIRECTION_SOUTH => Facing::SOUTH,
|
||||
StringValues::MC_CARDINAL_DIRECTION_WEST => Facing::WEST,
|
||||
StringValues::MC_CARDINAL_DIRECTION_EAST => Facing::EAST,
|
||||
default => throw $this->badValueException(BlockStateNames::MC_CARDINAL_DIRECTION, $raw)
|
||||
};
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public function readColor() : DyeColor{
|
||||
// * color (StringTag) = black, blue, brown, cyan, gray, green, light_blue, lime, magenta, orange, pink, purple, red, silver, white, yellow
|
||||
|
@ -93,8 +93,8 @@ final class BlockStateSerializerHelper{
|
||||
->writeTorchFacing($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeCauldron(string $liquid, int $fillLevel, BlockStateWriter $out) : BlockStateWriter{
|
||||
return $out
|
||||
public static function encodeCauldron(string $liquid, int $fillLevel) : BlockStateWriter{
|
||||
return Writer::create(Ids::CAULDRON)
|
||||
->writeString(BlockStateNames::CAULDRON_LIQUID, $liquid)
|
||||
->writeInt(BlockStateNames::FILL_LEVEL, $fillLevel);
|
||||
}
|
||||
|
@ -73,8 +73,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
public function __construct(){
|
||||
$this->registerCandleDeserializers();
|
||||
$this->registerFlatColorBlockDeserializers();
|
||||
$this->registerFlatCoralDeserializers();
|
||||
$this->registerCauldronDeserializers();
|
||||
$this->registerWoodBlockDeserializers();
|
||||
$this->registerFlatWoodBlockDeserializers();
|
||||
$this->registerLegacyWoodBlockDeserializers();
|
||||
$this->registerLeavesDeserializers();
|
||||
$this->registerSimpleDeserializers();
|
||||
$this->registerDeserializers();
|
||||
}
|
||||
@ -220,6 +223,90 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
] as $id => $color){
|
||||
$this->mapSimple($id, fn() => Blocks::WOOL()->setColor($color));
|
||||
}
|
||||
|
||||
foreach([
|
||||
Ids::BLACK_CARPET => DyeColor::BLACK(),
|
||||
Ids::BLUE_CARPET => DyeColor::BLUE(),
|
||||
Ids::BROWN_CARPET => DyeColor::BROWN(),
|
||||
Ids::CYAN_CARPET => DyeColor::CYAN(),
|
||||
Ids::GRAY_CARPET => DyeColor::GRAY(),
|
||||
Ids::GREEN_CARPET => DyeColor::GREEN(),
|
||||
Ids::LIGHT_BLUE_CARPET => DyeColor::LIGHT_BLUE(),
|
||||
Ids::LIGHT_GRAY_CARPET => DyeColor::LIGHT_GRAY(),
|
||||
Ids::LIME_CARPET => DyeColor::LIME(),
|
||||
Ids::MAGENTA_CARPET => DyeColor::MAGENTA(),
|
||||
Ids::ORANGE_CARPET => DyeColor::ORANGE(),
|
||||
Ids::PINK_CARPET => DyeColor::PINK(),
|
||||
Ids::PURPLE_CARPET => DyeColor::PURPLE(),
|
||||
Ids::RED_CARPET => DyeColor::RED(),
|
||||
Ids::WHITE_CARPET => DyeColor::WHITE(),
|
||||
Ids::YELLOW_CARPET => DyeColor::YELLOW(),
|
||||
] as $id => $color){
|
||||
$this->mapSimple($id, fn() => Blocks::CARPET()->setColor($color));
|
||||
}
|
||||
|
||||
foreach([
|
||||
Ids::BLACK_SHULKER_BOX => DyeColor::BLACK(),
|
||||
Ids::BLUE_SHULKER_BOX => DyeColor::BLUE(),
|
||||
Ids::BROWN_SHULKER_BOX => DyeColor::BROWN(),
|
||||
Ids::CYAN_SHULKER_BOX => DyeColor::CYAN(),
|
||||
Ids::GRAY_SHULKER_BOX => DyeColor::GRAY(),
|
||||
Ids::GREEN_SHULKER_BOX => DyeColor::GREEN(),
|
||||
Ids::LIGHT_BLUE_SHULKER_BOX => DyeColor::LIGHT_BLUE(),
|
||||
Ids::LIGHT_GRAY_SHULKER_BOX => DyeColor::LIGHT_GRAY(),
|
||||
Ids::LIME_SHULKER_BOX => DyeColor::LIME(),
|
||||
Ids::MAGENTA_SHULKER_BOX => DyeColor::MAGENTA(),
|
||||
Ids::ORANGE_SHULKER_BOX => DyeColor::ORANGE(),
|
||||
Ids::PINK_SHULKER_BOX => DyeColor::PINK(),
|
||||
Ids::PURPLE_SHULKER_BOX => DyeColor::PURPLE(),
|
||||
Ids::RED_SHULKER_BOX => DyeColor::RED(),
|
||||
Ids::WHITE_SHULKER_BOX => DyeColor::WHITE(),
|
||||
Ids::YELLOW_SHULKER_BOX => DyeColor::YELLOW(),
|
||||
] as $id => $color){
|
||||
$this->mapSimple($id, fn() => Blocks::DYED_SHULKER_BOX()->setColor($color));
|
||||
}
|
||||
|
||||
foreach([
|
||||
Ids::BLACK_CONCRETE => DyeColor::BLACK(),
|
||||
Ids::BLUE_CONCRETE => DyeColor::BLUE(),
|
||||
Ids::BROWN_CONCRETE => DyeColor::BROWN(),
|
||||
Ids::CYAN_CONCRETE => DyeColor::CYAN(),
|
||||
Ids::GRAY_CONCRETE => DyeColor::GRAY(),
|
||||
Ids::GREEN_CONCRETE => DyeColor::GREEN(),
|
||||
Ids::LIGHT_BLUE_CONCRETE => DyeColor::LIGHT_BLUE(),
|
||||
Ids::LIGHT_GRAY_CONCRETE => DyeColor::LIGHT_GRAY(),
|
||||
Ids::LIME_CONCRETE => DyeColor::LIME(),
|
||||
Ids::MAGENTA_CONCRETE => DyeColor::MAGENTA(),
|
||||
Ids::ORANGE_CONCRETE => DyeColor::ORANGE(),
|
||||
Ids::PINK_CONCRETE => DyeColor::PINK(),
|
||||
Ids::PURPLE_CONCRETE => DyeColor::PURPLE(),
|
||||
Ids::RED_CONCRETE => DyeColor::RED(),
|
||||
Ids::WHITE_CONCRETE => DyeColor::WHITE(),
|
||||
Ids::YELLOW_CONCRETE => DyeColor::YELLOW(),
|
||||
] as $id => $color){
|
||||
$this->mapSimple($id, fn() => Blocks::CONCRETE()->setColor($color));
|
||||
}
|
||||
}
|
||||
|
||||
private function registerFlatCoralDeserializers() : void{
|
||||
foreach([
|
||||
Ids::BRAIN_CORAL => CoralType::BRAIN(),
|
||||
Ids::BUBBLE_CORAL => CoralType::BUBBLE(),
|
||||
Ids::FIRE_CORAL => CoralType::FIRE(),
|
||||
Ids::HORN_CORAL => CoralType::HORN(),
|
||||
Ids::TUBE_CORAL => CoralType::TUBE(),
|
||||
] as $id => $coralType){
|
||||
$this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(false));
|
||||
}
|
||||
foreach([
|
||||
Ids::DEAD_BRAIN_CORAL => CoralType::BRAIN(),
|
||||
Ids::DEAD_BUBBLE_CORAL => CoralType::BUBBLE(),
|
||||
Ids::DEAD_FIRE_CORAL => CoralType::FIRE(),
|
||||
Ids::DEAD_HORN_CORAL => CoralType::HORN(),
|
||||
Ids::DEAD_TUBE_CORAL => CoralType::TUBE(),
|
||||
] as $id => $coralType){
|
||||
$this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(true));
|
||||
}
|
||||
}
|
||||
|
||||
private function registerCauldronDeserializers() : void{
|
||||
@ -238,23 +325,193 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
})->setFillLevel($level);
|
||||
};
|
||||
$this->map(Ids::CAULDRON, $deserializer);
|
||||
$this->map(Ids::LAVA_CAULDRON, $deserializer);
|
||||
}
|
||||
|
||||
private function registerWoodBlockDeserializers() : void{
|
||||
$this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE());
|
||||
$this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE());
|
||||
$this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE());
|
||||
$this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE());
|
||||
$this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE());
|
||||
$this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE());
|
||||
|
||||
private function registerFlatWoodBlockDeserializers() : void{
|
||||
$this->map(Ids::ACACIA_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::ACACIA_BUTTON(), $in));
|
||||
$this->map(Ids::ACACIA_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::ACACIA_DOOR(), $in));
|
||||
$this->map(Ids::ACACIA_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::ACACIA_FENCE_GATE(), $in));
|
||||
$this->map(Ids::ACACIA_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::ACACIA_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::ACACIA_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::ACACIA_SIGN(), $in));
|
||||
$this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in));
|
||||
$this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG());
|
||||
$this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE());
|
||||
$this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in));
|
||||
$this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in));
|
||||
$this->map(Ids::BIRCH_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::BIRCH_FENCE_GATE(), $in));
|
||||
$this->map(Ids::BIRCH_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::BIRCH_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::BIRCH_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::BIRCH_SIGN(), $in));
|
||||
$this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in));
|
||||
$this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG());
|
||||
$this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE());
|
||||
$this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::CHERRY_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CHERRY_BUTTON(), $in));
|
||||
$this->map(Ids::CHERRY_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CHERRY_DOOR(), $in));
|
||||
$this->map(Ids::CHERRY_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CHERRY_FENCE_GATE(), $in));
|
||||
$this->map(Ids::CHERRY_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CHERRY_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::CHERRY_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CHERRY_SIGN(), $in));
|
||||
$this->map(Ids::CHERRY_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CHERRY_TRAPDOOR(), $in));
|
||||
$this->map(Ids::CHERRY_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CHERRY_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG, fn() => Blocks::CHERRY_LOG());
|
||||
$this->mapSimple(Ids::CHERRY_FENCE, fn() => Blocks::CHERRY_FENCE());
|
||||
$this->mapSimple(Ids::CHERRY_PLANKS, fn() => Blocks::CHERRY_PLANKS());
|
||||
$this->mapSlab(Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB, fn() => Blocks::CHERRY_SLAB());
|
||||
$this->mapStairs(Ids::CHERRY_STAIRS, fn() => Blocks::CHERRY_STAIRS());
|
||||
$this->map(Ids::CHERRY_WOOD, function(Reader $in){
|
||||
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
|
||||
return Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in);
|
||||
});
|
||||
$this->map(Ids::STRIPPED_CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), true, $in));
|
||||
|
||||
$this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in));
|
||||
$this->map(Ids::CRIMSON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CRIMSON_DOOR(), $in));
|
||||
$this->map(Ids::CRIMSON_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CRIMSON_FENCE_GATE(), $in));
|
||||
$this->map(Ids::CRIMSON_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CRIMSON_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::CRIMSON_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CRIMSON_SIGN(), $in));
|
||||
$this->map(Ids::CRIMSON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CRIMSON_TRAPDOOR(), $in));
|
||||
$this->map(Ids::CRIMSON_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CRIMSON_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE, fn() => Blocks::CRIMSON_HYPHAE());
|
||||
$this->mapLog(Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM, fn() => Blocks::CRIMSON_STEM());
|
||||
$this->mapSimple(Ids::CRIMSON_FENCE, fn() => Blocks::CRIMSON_FENCE());
|
||||
$this->mapSimple(Ids::CRIMSON_PLANKS, fn() => Blocks::CRIMSON_PLANKS());
|
||||
$this->mapSlab(Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB, fn() => Blocks::CRIMSON_SLAB());
|
||||
$this->mapStairs(Ids::CRIMSON_STAIRS, fn() => Blocks::CRIMSON_STAIRS());
|
||||
|
||||
$this->map(Ids::DARKOAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::DARK_OAK_SIGN(), $in));
|
||||
$this->map(Ids::DARKOAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::DARK_OAK_WALL_SIGN(), $in));
|
||||
$this->map(Ids::DARK_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::DARK_OAK_BUTTON(), $in));
|
||||
$this->map(Ids::DARK_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::DARK_OAK_DOOR(), $in));
|
||||
$this->map(Ids::DARK_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::DARK_OAK_FENCE_GATE(), $in));
|
||||
$this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in));
|
||||
$this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG());
|
||||
$this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE());
|
||||
$this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in));
|
||||
$this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in));
|
||||
$this->map(Ids::JUNGLE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::JUNGLE_FENCE_GATE(), $in));
|
||||
$this->map(Ids::JUNGLE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::JUNGLE_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::JUNGLE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::JUNGLE_SIGN(), $in));
|
||||
$this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG());
|
||||
$this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE());
|
||||
$this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in));
|
||||
$this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in));
|
||||
$this->map(Ids::MANGROVE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::MANGROVE_FENCE_GATE(), $in));
|
||||
$this->map(Ids::MANGROVE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::MANGROVE_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::MANGROVE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::MANGROVE_SIGN(), $in));
|
||||
$this->map(Ids::MANGROVE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::MANGROVE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::MANGROVE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::MANGROVE_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG, fn() => Blocks::MANGROVE_LOG());
|
||||
$this->mapSimple(Ids::MANGROVE_FENCE, fn() => Blocks::MANGROVE_FENCE());
|
||||
$this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS());
|
||||
$this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB());
|
||||
$this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS());
|
||||
$this->map(Ids::MANGROVE_WOOD, function(Reader $in){
|
||||
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
|
||||
return Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in);
|
||||
});
|
||||
$this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in));
|
||||
|
||||
//oak - due to age, many of these don't specify "oak", making for confusing reading
|
||||
$this->map(Ids::WOODEN_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::OAK_BUTTON(), $in));
|
||||
$this->map(Ids::WOODEN_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::OAK_DOOR(), $in));
|
||||
$this->map(Ids::FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::OAK_FENCE_GATE(), $in));
|
||||
$this->map(Ids::WOODEN_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::OAK_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::OAK_SIGN(), $in));
|
||||
$this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in));
|
||||
$this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG());
|
||||
$this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE());
|
||||
$this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in));
|
||||
$this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in));
|
||||
$this->map(Ids::SPRUCE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::SPRUCE_FENCE_GATE(), $in));
|
||||
$this->map(Ids::SPRUCE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::SPRUCE_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::SPRUCE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::SPRUCE_SIGN(), $in));
|
||||
$this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG());
|
||||
$this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE());
|
||||
$this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS());
|
||||
//wood, planks and slabs still use the old way of storing wood type
|
||||
|
||||
$this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in));
|
||||
$this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in));
|
||||
$this->map(Ids::WARPED_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::WARPED_FENCE_GATE(), $in));
|
||||
$this->map(Ids::WARPED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::WARPED_PRESSURE_PLATE(), $in));
|
||||
$this->map(Ids::WARPED_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::WARPED_SIGN(), $in));
|
||||
$this->map(Ids::WARPED_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::WARPED_TRAPDOOR(), $in));
|
||||
$this->map(Ids::WARPED_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::WARPED_WALL_SIGN(), $in));
|
||||
$this->mapLog(Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE, fn() => Blocks::WARPED_HYPHAE());
|
||||
$this->mapLog(Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM, fn() => Blocks::WARPED_STEM());
|
||||
$this->mapSimple(Ids::WARPED_FENCE, fn() => Blocks::WARPED_FENCE());
|
||||
$this->mapSimple(Ids::WARPED_PLANKS, fn() => Blocks::WARPED_PLANKS());
|
||||
$this->mapSlab(Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB, fn() => Blocks::WARPED_SLAB());
|
||||
$this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS());
|
||||
}
|
||||
|
||||
private function registerLegacyWoodBlockDeserializers() : void{
|
||||
|
||||
$this->map(Ids::PLANKS, function(Reader $in) : Block{
|
||||
return match($woodName = $in->readString(StateNames::WOOD_TYPE)){
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_PLANKS(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_PLANKS(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_PLANKS(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_PLANKS(),
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_PLANKS(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_PLANKS(),
|
||||
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodName),
|
||||
};
|
||||
});
|
||||
$this->mapSlab(Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB, fn(Reader $in) => Helper::mapWoodenSlabType($in));
|
||||
|
||||
$this->map(Ids::WOOD, fn(Reader $in) : Block => Helper::decodeLog(match($woodType = $in->readString(StateNames::WOOD_TYPE)){
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_WOOD(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_WOOD(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_WOOD(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_WOOD(),
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_WOOD(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_WOOD(),
|
||||
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodType),
|
||||
}, $in->readBool(StateNames::STRIPPED_BIT), $in));
|
||||
}
|
||||
|
||||
private function registerLeavesDeserializers() : void{
|
||||
//flattened IDs
|
||||
$this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in));
|
||||
$this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in));
|
||||
$this->map(Ids::CHERRY_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::CHERRY_LEAVES(), $in));
|
||||
$this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in));
|
||||
|
||||
//legacy mess
|
||||
$this->map(Ids::LEAVES, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::OLD_LEAF_TYPE)){
|
||||
StringValues::OLD_LEAF_TYPE_BIRCH => Blocks::BIRCH_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_JUNGLE => Blocks::JUNGLE_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_OAK => Blocks::OAK_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_SPRUCE => Blocks::SPRUCE_LEAVES(),
|
||||
default => throw $in->badValueException(StateNames::OLD_LEAF_TYPE, $type),
|
||||
}, $in));
|
||||
$this->map(Ids::LEAVES2, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::NEW_LEAF_TYPE)){
|
||||
StringValues::NEW_LEAF_TYPE_ACACIA => Blocks::ACACIA_LEAVES(),
|
||||
StringValues::NEW_LEAF_TYPE_DARK_OAK => Blocks::DARK_OAK_LEAVES(),
|
||||
default => throw $in->badValueException(StateNames::NEW_LEAF_TYPE, $type),
|
||||
}, $in));
|
||||
}
|
||||
|
||||
private function registerSimpleDeserializers() : void{
|
||||
@ -286,8 +543,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS());
|
||||
$this->mapSimple(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS());
|
||||
$this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE());
|
||||
$this->mapSimple(Ids::CRIMSON_FENCE, fn() => Blocks::CRIMSON_FENCE());
|
||||
$this->mapSimple(Ids::CRIMSON_PLANKS, fn() => Blocks::CRIMSON_PLANKS());
|
||||
$this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN());
|
||||
$this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH());
|
||||
$this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS());
|
||||
@ -454,8 +709,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::LAPIS_BLOCK, fn() => Blocks::LAPIS_LAZULI());
|
||||
$this->mapSimple(Ids::LAPIS_ORE, fn() => Blocks::LAPIS_LAZULI_ORE());
|
||||
$this->mapSimple(Ids::MAGMA, fn() => Blocks::MAGMA());
|
||||
$this->mapSimple(Ids::MANGROVE_FENCE, fn() => Blocks::MANGROVE_FENCE());
|
||||
$this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS());
|
||||
$this->mapSimple(Ids::MANGROVE_ROOTS, fn() => Blocks::MANGROVE_ROOTS());
|
||||
$this->mapSimple(Ids::MELON_BLOCK, fn() => Blocks::MELON());
|
||||
$this->mapSimple(Ids::MOB_SPAWNER, fn() => Blocks::MONSTER_SPAWNER());
|
||||
@ -503,8 +756,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS());
|
||||
$this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF());
|
||||
$this->mapSimple(Ids::UNDYED_SHULKER_BOX, fn() => Blocks::SHULKER_BOX());
|
||||
$this->mapSimple(Ids::WARPED_FENCE, fn() => Blocks::WARPED_FENCE());
|
||||
$this->mapSimple(Ids::WARPED_PLANKS, fn() => Blocks::WARPED_PLANKS());
|
||||
$this->mapSimple(Ids::WARPED_WART_BLOCK, fn() => Blocks::WARPED_WART_BLOCK());
|
||||
$this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD());
|
||||
$this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB());
|
||||
@ -513,14 +764,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
}
|
||||
|
||||
private function registerDeserializers() : void{
|
||||
$this->map(Ids::ACACIA_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::ACACIA_BUTTON(), $in));
|
||||
$this->map(Ids::ACACIA_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::ACACIA_DOOR(), $in));
|
||||
$this->map(Ids::ACACIA_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::ACACIA_FENCE_GATE(), $in));
|
||||
$this->map(Ids::ACACIA_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::ACACIA_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS());
|
||||
$this->map(Ids::ACACIA_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::ACACIA_SIGN(), $in));
|
||||
$this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in));
|
||||
$this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in));
|
||||
$this->map(Ids::ACTIVATOR_RAIL, function(Reader $in) : Block{
|
||||
return Blocks::ACTIVATOR_RAIL()
|
||||
->setPowered($in->readBool(StateNames::RAIL_DATA_BIT))
|
||||
@ -538,8 +781,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
})
|
||||
->setFacing($in->readLegacyHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in));
|
||||
$this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in));
|
||||
$this->map(Ids::BAMBOO, function(Reader $in) : Block{
|
||||
return Blocks::BAMBOO()
|
||||
->setLeafSize(match($value = $in->readString(StateNames::BAMBOO_LEAF_SIZE)){
|
||||
@ -585,14 +826,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing($in->readLegacyHorizontalFacing())
|
||||
->setAttachmentType($in->readBellAttachmentType());
|
||||
});
|
||||
$this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in));
|
||||
$this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in));
|
||||
$this->map(Ids::BIRCH_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::BIRCH_FENCE_GATE(), $in));
|
||||
$this->map(Ids::BIRCH_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::BIRCH_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS());
|
||||
$this->map(Ids::BIRCH_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::BIRCH_SIGN(), $in));
|
||||
$this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in));
|
||||
$this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in));
|
||||
$this->mapSlab(Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::BLACKSTONE_SLAB());
|
||||
$this->mapStairs(Ids::BLACKSTONE_STAIRS, fn() => Blocks::BLACKSTONE_STAIRS());
|
||||
$this->map(Ids::BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BLACKSTONE_WALL(), $in));
|
||||
@ -621,14 +854,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::CAKE()
|
||||
->setBites($in->readBoundedInt(StateNames::BITE_COUNTER, 0, 6));
|
||||
});
|
||||
$this->map(Ids::CARPET, function(Reader $in) : Block{
|
||||
return Blocks::CARPET()
|
||||
->setColor($in->readColor());
|
||||
});
|
||||
$this->map(Ids::CARROTS, fn(Reader $in) => Helper::decodeCrops(Blocks::CARROTS(), $in));
|
||||
$this->map(Ids::CARVED_PUMPKIN, function(Reader $in) : Block{
|
||||
return Blocks::CARVED_PUMPKIN()
|
||||
->setFacing($in->readLegacyHorizontalFacing());
|
||||
->setFacing($in->readCardinalHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::CAVE_VINES, function(Reader $in) : CaveVines{
|
||||
return Blocks::CAVE_VINES()
|
||||
@ -688,10 +917,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing()) :
|
||||
Blocks::RED_TORCH()->setFacing($in->readTorchFacing());
|
||||
});
|
||||
$this->map(Ids::CONCRETE, function(Reader $in) : Block{
|
||||
return Blocks::CONCRETE()
|
||||
->setColor($in->readColor());
|
||||
});
|
||||
$this->map(Ids::CONCRETE_POWDER, function(Reader $in) : Block{
|
||||
return Blocks::CONCRETE_POWDER()
|
||||
->setColor($in->readColor());
|
||||
@ -700,11 +925,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE()));
|
||||
$this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE()));
|
||||
$this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE()));
|
||||
$this->map(Ids::CORAL, function(Reader $in) : Block{
|
||||
return Blocks::CORAL()
|
||||
->setCoralType($in->readCoralType())
|
||||
->setDead($in->readBool(StateNames::DEAD_BIT));
|
||||
});
|
||||
$this->map(Ids::CORAL_BLOCK, function(Reader $in) : Block{
|
||||
return Blocks::CORAL_BLOCK()
|
||||
->setCoralType($in->readCoralType())
|
||||
@ -723,26 +943,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
|
||||
->setCoralType(CoralType::HORN());
|
||||
});
|
||||
$this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in));
|
||||
$this->map(Ids::CRIMSON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CRIMSON_DOOR(), $in));
|
||||
$this->mapSlab(Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB, fn() => Blocks::CRIMSON_SLAB());
|
||||
$this->map(Ids::CRIMSON_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CRIMSON_FENCE_GATE(), $in));
|
||||
$this->map(Ids::CRIMSON_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_HYPHAE(), false, $in));
|
||||
$this->map(Ids::CRIMSON_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CRIMSON_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::CRIMSON_STAIRS, fn() => Blocks::CRIMSON_STAIRS());
|
||||
$this->map(Ids::CRIMSON_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CRIMSON_SIGN(), $in));
|
||||
$this->map(Ids::CRIMSON_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_STEM(), false, $in));
|
||||
$this->map(Ids::CRIMSON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CRIMSON_TRAPDOOR(), $in));
|
||||
$this->map(Ids::CRIMSON_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CRIMSON_WALL_SIGN(), $in));
|
||||
$this->map(Ids::DARK_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::DARK_OAK_BUTTON(), $in));
|
||||
$this->map(Ids::DARK_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::DARK_OAK_DOOR(), $in));
|
||||
$this->map(Ids::DARK_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::DARK_OAK_FENCE_GATE(), $in));
|
||||
$this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS());
|
||||
$this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in));
|
||||
$this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS());
|
||||
$this->map(Ids::DARKOAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::DARK_OAK_SIGN(), $in));
|
||||
$this->map(Ids::DARKOAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::DARK_OAK_WALL_SIGN(), $in));
|
||||
$this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in)
|
||||
->setInverted(false));
|
||||
$this->map(Ids::DAYLIGHT_DETECTOR_INVERTED, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in)
|
||||
@ -806,7 +1007,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::FARMLAND()
|
||||
->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7));
|
||||
});
|
||||
$this->map(Ids::FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::OAK_FENCE_GATE(), $in));
|
||||
$this->map(Ids::FIRE, function(Reader $in) : Block{
|
||||
return Blocks::FIRE()
|
||||
->setAge($in->readBoundedInt(StateNames::AGE, 0, 15));
|
||||
@ -827,6 +1027,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing($in->readHorizontalFacing())
|
||||
->setLit(false);
|
||||
});
|
||||
$this->map(Ids::GLOW_LICHEN, fn(Reader $in) => Blocks::GLOW_LICHEN()->setFaces($in->readFacingFlags()));
|
||||
$this->map(Ids::GLOW_FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::GLOWING_ITEM_FRAME(), $in));
|
||||
$this->map(Ids::GOLDEN_RAIL, function(Reader $in) : Block{
|
||||
return Blocks::POWERED_RAIL()
|
||||
@ -854,14 +1055,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
});
|
||||
$this->map(Ids::IRON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::IRON_DOOR(), $in));
|
||||
$this->map(Ids::IRON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::IRON_TRAPDOOR(), $in));
|
||||
$this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in));
|
||||
$this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in));
|
||||
$this->map(Ids::JUNGLE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::JUNGLE_FENCE_GATE(), $in));
|
||||
$this->map(Ids::JUNGLE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::JUNGLE_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS());
|
||||
$this->map(Ids::JUNGLE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::JUNGLE_SIGN(), $in));
|
||||
$this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in));
|
||||
$this->map(Ids::LADDER, function(Reader $in) : Block{
|
||||
return Blocks::LADDER()
|
||||
->setFacing($in->readHorizontalFacing());
|
||||
@ -871,18 +1064,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setHanging($in->readBool(StateNames::HANGING));
|
||||
});
|
||||
$this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in));
|
||||
$this->map(Ids::LEAVES, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::OLD_LEAF_TYPE)){
|
||||
StringValues::OLD_LEAF_TYPE_BIRCH => Blocks::BIRCH_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_JUNGLE => Blocks::JUNGLE_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_OAK => Blocks::OAK_LEAVES(),
|
||||
StringValues::OLD_LEAF_TYPE_SPRUCE => Blocks::SPRUCE_LEAVES(),
|
||||
default => throw $in->badValueException(StateNames::OLD_LEAF_TYPE, $type),
|
||||
}, $in));
|
||||
$this->map(Ids::LEAVES2, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::NEW_LEAF_TYPE)){
|
||||
StringValues::NEW_LEAF_TYPE_ACACIA => Blocks::ACACIA_LEAVES(),
|
||||
StringValues::NEW_LEAF_TYPE_DARK_OAK => Blocks::DARK_OAK_LEAVES(),
|
||||
default => throw $in->badValueException(StateNames::NEW_LEAF_TYPE, $type),
|
||||
}, $in));
|
||||
$this->map(Ids::LECTERN, function(Reader $in) : Block{
|
||||
return Blocks::LECTERN()
|
||||
->setFacing($in->readLegacyHorizontalFacing())
|
||||
@ -925,7 +1106,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
});
|
||||
$this->map(Ids::LIT_PUMPKIN, function(Reader $in) : Block{
|
||||
return Blocks::LIT_PUMPKIN()
|
||||
->setFacing($in->readLegacyHorizontalFacing());
|
||||
->setFacing($in->readCardinalHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::LIT_REDSTONE_LAMP, function() : Block{
|
||||
return Blocks::REDSTONE_LAMP()
|
||||
@ -944,21 +1125,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::LOOM()
|
||||
->setFacing($in->readLegacyHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in));
|
||||
$this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in));
|
||||
$this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB());
|
||||
$this->map(Ids::MANGROVE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::MANGROVE_FENCE_GATE(), $in));
|
||||
$this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in));
|
||||
$this->map(Ids::MANGROVE_LOG, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_LOG(), false, $in));
|
||||
$this->map(Ids::MANGROVE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::MANGROVE_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS());
|
||||
$this->map(Ids::MANGROVE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::MANGROVE_SIGN(), $in));
|
||||
$this->map(Ids::MANGROVE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::MANGROVE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::MANGROVE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::MANGROVE_WALL_SIGN(), $in));
|
||||
$this->map(Ids::MANGROVE_WOOD, function(Reader $in){
|
||||
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
|
||||
return Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in);
|
||||
});
|
||||
$this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in));
|
||||
$this->map(Ids::MONSTER_EGG, function(Reader $in) : Block{
|
||||
return match($type = $in->readString(StateNames::MONSTER_EGG_STONE_TYPE)){
|
||||
@ -986,24 +1152,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setAge($in->readBoundedInt(StateNames::AGE, 0, 3));
|
||||
});
|
||||
$this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS());
|
||||
$this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS());
|
||||
$this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE())->setAxis($in->readPillarAxis()));
|
||||
$this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED()));
|
||||
$this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED()));
|
||||
$this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED()));
|
||||
$this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED()));
|
||||
$this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT())->setAxis($in->readPillarAxis()));
|
||||
$this->map(Ids::PLANKS, function(Reader $in) : Block{
|
||||
return match($woodName = $in->readString(StateNames::WOOD_TYPE)){
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_PLANKS(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_PLANKS(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_PLANKS(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_PLANKS(),
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_PLANKS(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_PLANKS(),
|
||||
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodName),
|
||||
};
|
||||
});
|
||||
$this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS());
|
||||
$this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{
|
||||
return Blocks::POLISHED_BASALT()
|
||||
@ -1046,7 +1200,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapStairs(Ids::PRISMARINE_BRICKS_STAIRS, fn() => Blocks::PRISMARINE_BRICKS_STAIRS());
|
||||
$this->mapStairs(Ids::PRISMARINE_STAIRS, fn() => Blocks::PRISMARINE_STAIRS());
|
||||
$this->map(Ids::PUMPKIN, function(Reader $in) : Block{
|
||||
$in->ignored(StateNames::DIRECTION); //obsolete
|
||||
$in->ignored(StateNames::MC_CARDINAL_DIRECTION); //obsolete
|
||||
return Blocks::PUMPKIN();
|
||||
});
|
||||
$this->map(Ids::PUMPKIN_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::PUMPKIN_STEM(), $in));
|
||||
@ -1169,10 +1323,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1)
|
||||
->setUnderwater(!$in->readBool(StateNames::DEAD_BIT));
|
||||
});
|
||||
$this->map(Ids::SHULKER_BOX, function(Reader $in) : Block{
|
||||
return Blocks::DYED_SHULKER_BOX()
|
||||
->setColor($in->readColor());
|
||||
});
|
||||
$this->map(Ids::SKULL, function(Reader $in) : Block{
|
||||
return Blocks::MOB_HEAD()
|
||||
->setFacing($in->readFacingWithoutDown());
|
||||
@ -1208,14 +1358,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
default => throw $in->badValueException(StateNames::SPONGE_TYPE, $type),
|
||||
});
|
||||
});
|
||||
$this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in));
|
||||
$this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in));
|
||||
$this->map(Ids::SPRUCE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::SPRUCE_FENCE_GATE(), $in));
|
||||
$this->map(Ids::SPRUCE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::SPRUCE_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS());
|
||||
$this->map(Ids::SPRUCE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::SPRUCE_SIGN(), $in));
|
||||
$this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in));
|
||||
$this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in));
|
||||
$this->map(Ids::STAINED_GLASS, function(Reader $in) : Block{
|
||||
return Blocks::STAINED_GLASS()
|
||||
->setColor($in->readColor());
|
||||
@ -1232,7 +1374,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::BANNER()
|
||||
->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15));
|
||||
});
|
||||
$this->map(Ids::STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::OAK_SIGN(), $in));
|
||||
$this->map(Ids::STONE, function(Reader $in) : Block{
|
||||
return match($type = $in->readString(StateNames::STONE_TYPE)){
|
||||
StringValues::STONE_TYPE_ANDESITE => Blocks::ANDESITE(),
|
||||
@ -1267,12 +1408,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::STONECUTTER()
|
||||
->setFacing($in->readHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::STRIPPED_CRIMSON_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_HYPHAE(), true, $in));
|
||||
$this->map(Ids::STRIPPED_CRIMSON_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_STEM(), true, $in));
|
||||
$this->map(Ids::STRIPPED_MANGROVE_LOG, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_LOG(), true, $in));
|
||||
$this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in));
|
||||
$this->map(Ids::STRIPPED_WARPED_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_HYPHAE(), true, $in));
|
||||
$this->map(Ids::STRIPPED_WARPED_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_STEM(), true, $in));
|
||||
$this->map(Ids::SWEET_BERRY_BUSH, function(Reader $in) : Block{
|
||||
//berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7
|
||||
$growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7);
|
||||
@ -1295,7 +1430,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::TORCH()
|
||||
->setFacing($in->readTorchFacing());
|
||||
});
|
||||
$this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in));
|
||||
$this->map(Ids::TRAPPED_CHEST, function(Reader $in) : Block{
|
||||
return Blocks::TRAPPED_CHEST()
|
||||
->setFacing($in->readHorizontalFacing());
|
||||
@ -1342,18 +1476,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::WALL_BANNER()
|
||||
->setFacing($in->readHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in));
|
||||
$this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in));
|
||||
$this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in));
|
||||
$this->mapSlab(Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB, fn() => Blocks::WARPED_SLAB());
|
||||
$this->map(Ids::WARPED_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::WARPED_FENCE_GATE(), $in));
|
||||
$this->map(Ids::WARPED_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_HYPHAE(), false, $in));
|
||||
$this->map(Ids::WARPED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::WARPED_PRESSURE_PLATE(), $in));
|
||||
$this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS());
|
||||
$this->map(Ids::WARPED_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::WARPED_SIGN(), $in));
|
||||
$this->map(Ids::WARPED_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_STEM(), false, $in));
|
||||
$this->map(Ids::WARPED_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::WARPED_TRAPDOOR(), $in));
|
||||
$this->map(Ids::WARPED_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::WARPED_WALL_SIGN(), $in));
|
||||
$this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in));
|
||||
$this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE()));
|
||||
$this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE()));
|
||||
@ -1380,19 +1502,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25));
|
||||
});
|
||||
$this->map(Ids::WHEAT, fn(Reader $in) => Helper::decodeCrops(Blocks::WHEAT(), $in));
|
||||
$this->map(Ids::WOOD, fn(Reader $in) : Block => Helper::decodeLog(match($woodType = $in->readString(StateNames::WOOD_TYPE)){
|
||||
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_WOOD(),
|
||||
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_WOOD(),
|
||||
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_WOOD(),
|
||||
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_WOOD(),
|
||||
StringValues::WOOD_TYPE_OAK => Blocks::OAK_WOOD(),
|
||||
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_WOOD(),
|
||||
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodType),
|
||||
}, $in->readBool(StateNames::STRIPPED_BIT), $in));
|
||||
$this->map(Ids::WOODEN_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::OAK_BUTTON(), $in));
|
||||
$this->map(Ids::WOODEN_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::OAK_DOOR(), $in));
|
||||
$this->map(Ids::WOODEN_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::OAK_PRESSURE_PLATE(), $in));
|
||||
$this->mapSlab(Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB, fn(Reader $in) => Helper::mapWoodenSlabType($in));
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\utils\WallConnectionType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\data\bedrock\block\BlockStateNames;
|
||||
use pocketmine\data\bedrock\block\BlockStateSerializeException;
|
||||
@ -39,6 +40,7 @@ use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
|
||||
final class BlockStateWriter{
|
||||
|
||||
@ -88,6 +90,28 @@ final class BlockStateWriter{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
* @phpstan-param array<int, int> $faces
|
||||
* @return $this
|
||||
*/
|
||||
public function writeFacingFlags(array $faces) : self{
|
||||
$result = 0;
|
||||
foreach($faces as $face){
|
||||
$result |= match($face){
|
||||
Facing::DOWN => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN,
|
||||
Facing::UP => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP,
|
||||
Facing::NORTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH,
|
||||
Facing::SOUTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH,
|
||||
Facing::WEST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST,
|
||||
Facing::EAST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST,
|
||||
default => throw new AssumptionFailedError("Unhandled face $face")
|
||||
};
|
||||
}
|
||||
|
||||
return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result);
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function writeEndRodFacingDirection(int $value) : self{
|
||||
//end rods are stupid in bedrock and have everything except up/down the wrong way round
|
||||
@ -141,6 +165,20 @@ final class BlockStateWriter{
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by pumpkins as of 1.20.0.23 beta
|
||||
* @return $this
|
||||
*/
|
||||
public function writeCardinalHorizontalFacing(int $value) : self{
|
||||
return $this->writeString(BlockStateNames::MC_CARDINAL_DIRECTION, match($value){
|
||||
Facing::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH,
|
||||
Facing::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST,
|
||||
Facing::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH,
|
||||
Facing::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST,
|
||||
default => throw new BlockStateSerializeException("Invalid horizontal facing $value")
|
||||
});
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function writeColor(DyeColor $color) : self{
|
||||
$this->writeString(BlockStateNames::COLOR, match($color->id()){
|
||||
|
@ -24,6 +24,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_diff;
|
||||
use function count;
|
||||
|
||||
final class BlockStateUpgradeSchemaBlockRemap{
|
||||
/**
|
||||
@ -37,8 +40,43 @@ final class BlockStateUpgradeSchemaBlockRemap{
|
||||
*/
|
||||
public function __construct(
|
||||
public array $oldState,
|
||||
public string $newName,
|
||||
public string|BlockStateUpgradeSchemaFlattenedName $newName,
|
||||
public array $newState,
|
||||
public array $copiedState
|
||||
){}
|
||||
|
||||
public function equals(self $that) : bool{
|
||||
$sameName = $this->newName === $that->newName ||
|
||||
(
|
||||
$this->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
|
||||
$that->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
|
||||
$this->newName->equals($that->newName)
|
||||
);
|
||||
if(!$sameName){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(
|
||||
count($this->oldState) !== count($that->oldState) ||
|
||||
count($this->newState) !== count($that->newState) ||
|
||||
count($this->copiedState) !== count($that->copiedState) ||
|
||||
count(array_diff($this->copiedState, $that->copiedState)) !== 0
|
||||
){
|
||||
return false;
|
||||
}
|
||||
foreach(Utils::stringifyKeys($this->oldState) as $propertyName => $propertyValue){
|
||||
if(!isset($that->oldState[$propertyName]) || !$that->oldState[$propertyName]->equals($propertyValue)){
|
||||
//different filter value
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach(Utils::stringifyKeys($this->newState) as $propertyName => $propertyValue){
|
||||
if(!isset($that->newState[$propertyName]) || !$that->newState[$propertyName]->equals($propertyValue)){
|
||||
//different replacement value
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
<?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\data\bedrock\block\upgrade;
|
||||
|
||||
final class BlockStateUpgradeSchemaFlattenedName{
|
||||
|
||||
public function __construct(
|
||||
public string $prefix,
|
||||
public string $flattenedProperty,
|
||||
public string $suffix
|
||||
){}
|
||||
|
||||
public function equals(self $that) : bool{
|
||||
return $this->prefix === $that->prefix &&
|
||||
$this->flattenedProperty === $that->flattenedProperty &&
|
||||
$this->suffix === $that->suffix;
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModel;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenedName;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelTag;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
@ -43,12 +44,14 @@ use function get_debug_type;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function ksort;
|
||||
use function sort;
|
||||
use function str_pad;
|
||||
use function strval;
|
||||
use function usort;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const SORT_NUMERIC;
|
||||
use const STR_PAD_LEFT;
|
||||
@ -154,9 +157,17 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
|
||||
foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){
|
||||
foreach($remaps as $remap){
|
||||
if(isset($remap->newName) === isset($remap->newFlattenedName)){
|
||||
throw new \UnexpectedValueException("Expected exactly one of 'newName' or 'newFlattenedName' properties to be set");
|
||||
}
|
||||
|
||||
$result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []),
|
||||
$remap->newName,
|
||||
$remap->newName ?? new BlockStateUpgradeSchemaFlattenedName(
|
||||
$remap->newFlattenedName->prefix,
|
||||
$remap->newFlattenedName->flattenedProperty,
|
||||
$remap->newFlattenedName->suffix
|
||||
),
|
||||
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
|
||||
$remap->copiedState ?? []
|
||||
);
|
||||
@ -285,7 +296,13 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
foreach($remaps as $remap){
|
||||
$modelRemap = new BlockStateUpgradeSchemaModelBlockRemap(
|
||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
|
||||
$remap->newName,
|
||||
is_string($remap->newName) ?
|
||||
$remap->newName :
|
||||
new BlockStateUpgradeSchemaModelFlattenedName(
|
||||
$remap->newName->prefix,
|
||||
$remap->newName->flattenedProperty,
|
||||
$remap->newName->suffix
|
||||
),
|
||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
||||
$remap->copiedState
|
||||
);
|
||||
@ -299,7 +316,15 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
}
|
||||
$keyedRemaps[$key] = $modelRemap;
|
||||
}
|
||||
ksort($keyedRemaps);
|
||||
usort($keyedRemaps, function(BlockStateUpgradeSchemaModelBlockRemap $a, BlockStateUpgradeSchemaModelBlockRemap $b) : int{
|
||||
//remaps with more specific criteria must come first
|
||||
$filterSizeCompare = count($b->oldState ?? []) <=> count($a->oldState ?? []);
|
||||
if($filterSizeCompare !== 0){
|
||||
return $filterSizeCompare;
|
||||
}
|
||||
//remaps with the same number of criteria should be sorted alphabetically, but this is not strictly necessary
|
||||
return json_encode($a->oldState ?? []) <=> json_encode($b->oldState ?? []);
|
||||
});
|
||||
$result->remappedStates[$oldBlockName] = array_values($keyedRemaps);
|
||||
}
|
||||
if(isset($result->remappedStates)){
|
||||
|
@ -24,11 +24,14 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
use function is_string;
|
||||
use function ksort;
|
||||
use function max;
|
||||
use function sprintf;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
final class BlockStateUpgrader{
|
||||
@ -79,6 +82,20 @@ final class BlockStateUpgrader{
|
||||
continue 2; //try next state
|
||||
}
|
||||
}
|
||||
|
||||
if(is_string($remap->newName)){
|
||||
$newName = $remap->newName;
|
||||
}else{
|
||||
$flattenedValue = $oldState[$remap->newName->flattenedProperty];
|
||||
if($flattenedValue instanceof StringTag){
|
||||
$newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix);
|
||||
unset($oldState[$remap->newName->flattenedProperty]);
|
||||
}else{
|
||||
//flattened property is not a TAG_String, so this transformation is not applicable
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$newState = $remap->newState;
|
||||
foreach($remap->copiedState as $stateName){
|
||||
if(isset($oldState[$stateName])){
|
||||
@ -86,7 +103,7 @@ final class BlockStateUpgrader{
|
||||
}
|
||||
}
|
||||
|
||||
$blockStateData = new BlockStateData($remap->newName, $newState, $resultVersion);
|
||||
$blockStateData = new BlockStateData($newName, $newState, $resultVersion);
|
||||
continue 2; //try next schema
|
||||
}
|
||||
}
|
||||
|
@ -34,12 +34,21 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
|
||||
*/
|
||||
public ?array $oldState;
|
||||
|
||||
/** @required */
|
||||
/**
|
||||
* Either this or newFlattenedName must be present
|
||||
* Due to technical limitations of jsonmapper, we can't use a union type here
|
||||
*/
|
||||
public string $newName;
|
||||
/**
|
||||
* Either this or newName must be present
|
||||
* Due to technical limitations of jsonmapper, we can't use a union type here
|
||||
*/
|
||||
public BlockStateUpgradeSchemaModelFlattenedName $newFlattenedName;
|
||||
|
||||
/**
|
||||
* @var BlockStateUpgradeSchemaModelTag[]|null
|
||||
* @phpstan-var array<string, BlockStateUpgradeSchemaModelTag>|null
|
||||
* @required
|
||||
*/
|
||||
public ?array $newState;
|
||||
|
||||
@ -58,9 +67,13 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
|
||||
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
|
||||
* @phpstan-param list<string> $copiedState
|
||||
*/
|
||||
public function __construct(array $oldState, string $newName, array $newState, array $copiedState){
|
||||
public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenedName $newNameRule, array $newState, array $copiedState){
|
||||
$this->oldState = count($oldState) === 0 ? null : $oldState;
|
||||
$this->newName = $newName;
|
||||
if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenedName){
|
||||
$this->newFlattenedName = $newNameRule;
|
||||
}else{
|
||||
$this->newName = $newNameRule;
|
||||
}
|
||||
$this->newState = count($newState) === 0 ? null : $newState;
|
||||
$this->copiedState = $copiedState;
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block\upgrade\model;
|
||||
|
||||
final class BlockStateUpgradeSchemaModelFlattenedName{
|
||||
|
||||
/** @required */
|
||||
public string $prefix;
|
||||
/** @required */
|
||||
public string $flattenedProperty;
|
||||
/** @required */
|
||||
public string $suffix;
|
||||
|
||||
public function __construct(string $prefix, string $flattenedProperty, string $suffix){
|
||||
$this->prefix = $prefix;
|
||||
$this->flattenedProperty = $flattenedProperty;
|
||||
$this->suffix = $suffix;
|
||||
}
|
||||
}
|
@ -32,8 +32,6 @@ use pocketmine\item\CoralFan;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemBlock;
|
||||
use pocketmine\item\VanillaItems as Items;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function class_parents;
|
||||
use function get_class;
|
||||
|
||||
final class ItemSerializer{
|
||||
@ -41,14 +39,14 @@ final class ItemSerializer{
|
||||
* These callables actually accept Item, but for the sake of type completeness, it has to be never, since we can't
|
||||
* describe the bottom type of a type hierarchy only containing Item.
|
||||
*
|
||||
* @var \Closure[][]
|
||||
* @phpstan-var array<int, array<class-string, \Closure(never) : Data>>
|
||||
* @var \Closure[]
|
||||
* @phpstan-var array<int, \Closure(never) : Data>
|
||||
*/
|
||||
private array $itemSerializers = [];
|
||||
|
||||
/**
|
||||
* @var \Closure[][]
|
||||
* @phpstan-var array<int, array<class-string, \Closure(never) : Data>>
|
||||
* @var \Closure[]
|
||||
* @phpstan-var array<int, \Closure(never) : Data>
|
||||
*/
|
||||
private array $blockItemSerializers = [];
|
||||
|
||||
@ -69,7 +67,7 @@ final class ItemSerializer{
|
||||
if(isset($this->itemSerializers[$index])){
|
||||
throw new \InvalidArgumentException("Item type ID " . $index . " already has a serializer registered");
|
||||
}
|
||||
$this->itemSerializers[$index][get_class($item)] = $serializer;
|
||||
$this->itemSerializers[$index] = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,9 +78,9 @@ final class ItemSerializer{
|
||||
public function mapBlock(Block $block, \Closure $serializer) : void{
|
||||
$index = $block->getTypeId();
|
||||
if(isset($this->blockItemSerializers[$index])){
|
||||
throw new AssumptionFailedError("Registering the same blockitem twice!");
|
||||
throw new \InvalidArgumentException("Block type ID " . $index . " already has a serializer registered");
|
||||
}
|
||||
$this->blockItemSerializers[$index][get_class($block)] = $serializer;
|
||||
$this->blockItemSerializers[$index] = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,21 +98,16 @@ final class ItemSerializer{
|
||||
}else{
|
||||
$index = $item->getTypeId();
|
||||
|
||||
$locatedSerializer = $this->itemSerializers[$index][get_class($item)] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
foreach(class_parents($item) as $parent){
|
||||
if(isset($this->itemSerializers[$index][$parent])){
|
||||
$locatedSerializer = $this->itemSerializers[$index][$parent];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$locatedSerializer = $this->itemSerializers[$index] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
throw new ItemTypeSerializeException("No serializer registered for " . get_class($item) . " ($index) " . $item->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: there is no guarantee that this type actually matches that of $item - a plugin may have stolen
|
||||
* the type ID of the item (which never makes sense, even in a world where overriding item types is a thing).
|
||||
* In the future we'll need some way to guarantee that type IDs are never reused (perhaps spl_object_id()?)
|
||||
*
|
||||
* @var \Closure $serializer
|
||||
* @phpstan-var \Closure(TItemType) : Data $serializer
|
||||
*/
|
||||
@ -156,18 +149,15 @@ final class ItemSerializer{
|
||||
private function serializeBlockItem(Block $block) : Data{
|
||||
$index = $block->getTypeId();
|
||||
|
||||
$locatedSerializer = $this->blockItemSerializers[$index][get_class($block)] ?? null;
|
||||
if($locatedSerializer === null){
|
||||
foreach(class_parents($block) as $parent){
|
||||
if(isset($this->blockItemSerializers[$index][$parent])){
|
||||
$locatedSerializer = $this->blockItemSerializers[$index][$parent];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$locatedSerializer = $this->blockItemSerializers[$index] ?? null;
|
||||
if($locatedSerializer !== null){
|
||||
/** @phpstan-var \Closure(TBlockType) : Data $serializer */
|
||||
/**
|
||||
* TODO: there is no guarantee that this type actually matches that of $block - a plugin may have stolen
|
||||
* the type ID of the block (which never makes sense, even in a world where overriding block types is a thing).
|
||||
* In the future we'll need some way to guarantee that type IDs are never reused (perhaps spl_object_id()?)
|
||||
*
|
||||
* @phpstan-var \Closure(TBlockType) : Data $serializer
|
||||
*/
|
||||
$serializer = $locatedSerializer;
|
||||
$data = $serializer($block);
|
||||
}else{
|
||||
@ -187,6 +177,7 @@ final class ItemSerializer{
|
||||
throw new ItemTypeSerializeException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
//TODO: this really ought to throw if there's no blockitem ID
|
||||
$itemNameId = BlockItemIdMap::getInstance()->lookupItemId($blockStateData->getName()) ?? $blockStateData->getName();
|
||||
|
||||
return new Data($itemNameId, 0, $blockStateData);
|
||||
|
@ -25,15 +25,15 @@ namespace pocketmine\data\bedrock\item;
|
||||
|
||||
use pocketmine\block\Bed;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Skull;
|
||||
use pocketmine\block\MobHead;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SkullType;
|
||||
use pocketmine\block\VanillaBlocks as Blocks;
|
||||
use pocketmine\data\bedrock\CompoundTypeIds;
|
||||
use pocketmine\data\bedrock\DyeColorIdMap;
|
||||
use pocketmine\data\bedrock\item\ItemTypeNames as Ids;
|
||||
use pocketmine\data\bedrock\item\SavedItemData as Data;
|
||||
use pocketmine\data\bedrock\MedicineTypeIdMap;
|
||||
use pocketmine\data\bedrock\MobHeadTypeIdMap;
|
||||
use pocketmine\data\bedrock\PotionTypeIdMap;
|
||||
use pocketmine\data\bedrock\SuspiciousStewTypeIdMap;
|
||||
use pocketmine\item\Banner;
|
||||
@ -135,6 +135,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Block(Ids::CAKE, Blocks::CAKE());
|
||||
$this->map1to1Block(Ids::CAULDRON, Blocks::CAULDRON());
|
||||
$this->map1to1Block(Ids::CHAIN, Blocks::CHAIN());
|
||||
$this->map1to1Block(Ids::CHERRY_DOOR, Blocks::CHERRY_DOOR());
|
||||
$this->map1to1Block(Ids::COMPARATOR, Blocks::REDSTONE_COMPARATOR());
|
||||
$this->map1to1Block(Ids::CRIMSON_DOOR, Blocks::CRIMSON_DOOR());
|
||||
$this->map1to1Block(Ids::DARK_OAK_DOOR, Blocks::DARK_OAK_DOOR());
|
||||
@ -185,6 +186,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::CHAINMAIL_HELMET, Items::CHAINMAIL_HELMET());
|
||||
$this->map1to1Item(Ids::CHAINMAIL_LEGGINGS, Items::CHAINMAIL_LEGGINGS());
|
||||
$this->map1to1Item(Ids::CHARCOAL, Items::CHARCOAL());
|
||||
$this->map1to1Item(Ids::CHERRY_SIGN, Items::CHERRY_SIGN());
|
||||
$this->map1to1Item(Ids::CHICKEN, Items::RAW_CHICKEN());
|
||||
$this->map1to1Item(Ids::CHORUS_FRUIT, Items::CHORUS_FRUIT());
|
||||
$this->map1to1Item(Ids::CLAY_BALL, Items::CLAY());
|
||||
@ -444,15 +446,10 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1BlockWithMeta(
|
||||
Ids::SKULL,
|
||||
Blocks::MOB_HEAD(),
|
||||
function(Skull $block, int $meta) : void{
|
||||
try{
|
||||
$skullType = SkullType::fromMagicNumber($meta);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
throw new ItemTypeDeserializeException($e->getMessage(), 0, $e);
|
||||
}
|
||||
$block->setSkullType($skullType);
|
||||
function(MobHead $block, int $meta) : void{
|
||||
$block->setMobHeadType(MobHeadTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown mob head type ID $meta"));
|
||||
},
|
||||
fn(Skull $block) => $block->getSkullType()->getMagicNumber()
|
||||
fn(MobHead $block) => MobHeadTypeIdMap::getInstance()->toId($block->getMobHeadType())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -30,20 +30,24 @@ final class ItemTypeNames{
|
||||
public const ACACIA_BOAT = "minecraft:acacia_boat";
|
||||
public const ACACIA_CHEST_BOAT = "minecraft:acacia_chest_boat";
|
||||
public const ACACIA_DOOR = "minecraft:acacia_door";
|
||||
public const ACACIA_HANGING_SIGN = "minecraft:acacia_hanging_sign";
|
||||
public const ACACIA_SIGN = "minecraft:acacia_sign";
|
||||
public const AGENT_SPAWN_EGG = "minecraft:agent_spawn_egg";
|
||||
public const ALLAY_SPAWN_EGG = "minecraft:allay_spawn_egg";
|
||||
public const AMETHYST_SHARD = "minecraft:amethyst_shard";
|
||||
public const ANGLER_POTTERY_SHERD = "minecraft:angler_pottery_sherd";
|
||||
public const APPLE = "minecraft:apple";
|
||||
public const ARCHER_POTTERY_SHARD = "minecraft:archer_pottery_shard";
|
||||
public const ARCHER_POTTERY_SHERD = "minecraft:archer_pottery_sherd";
|
||||
public const ARMOR_STAND = "minecraft:armor_stand";
|
||||
public const ARMS_UP_POTTERY_SHARD = "minecraft:arms_up_pottery_shard";
|
||||
public const ARMS_UP_POTTERY_SHERD = "minecraft:arms_up_pottery_sherd";
|
||||
public const ARROW = "minecraft:arrow";
|
||||
public const AXOLOTL_BUCKET = "minecraft:axolotl_bucket";
|
||||
public const AXOLOTL_SPAWN_EGG = "minecraft:axolotl_spawn_egg";
|
||||
public const BAKED_POTATO = "minecraft:baked_potato";
|
||||
public const BALLOON = "minecraft:balloon";
|
||||
public const BAMBOO_CHEST_RAFT = "minecraft:bamboo_chest_raft";
|
||||
public const BAMBOO_DOOR = "minecraft:bamboo_door";
|
||||
public const BAMBOO_HANGING_SIGN = "minecraft:bamboo_hanging_sign";
|
||||
public const BAMBOO_RAFT = "minecraft:bamboo_raft";
|
||||
public const BAMBOO_SIGN = "minecraft:bamboo_sign";
|
||||
public const BANNER = "minecraft:banner";
|
||||
@ -58,8 +62,10 @@ final class ItemTypeNames{
|
||||
public const BIRCH_BOAT = "minecraft:birch_boat";
|
||||
public const BIRCH_CHEST_BOAT = "minecraft:birch_chest_boat";
|
||||
public const BIRCH_DOOR = "minecraft:birch_door";
|
||||
public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign";
|
||||
public const BIRCH_SIGN = "minecraft:birch_sign";
|
||||
public const BLACK_DYE = "minecraft:black_dye";
|
||||
public const BLADE_POTTERY_SHERD = "minecraft:blade_pottery_sherd";
|
||||
public const BLAZE_POWDER = "minecraft:blaze_powder";
|
||||
public const BLAZE_ROD = "minecraft:blaze_rod";
|
||||
public const BLAZE_SPAWN_EGG = "minecraft:blaze_spawn_egg";
|
||||
@ -73,15 +79,18 @@ final class ItemTypeNames{
|
||||
public const BOW = "minecraft:bow";
|
||||
public const BOWL = "minecraft:bowl";
|
||||
public const BREAD = "minecraft:bread";
|
||||
public const BREWER_POTTERY_SHERD = "minecraft:brewer_pottery_sherd";
|
||||
public const BREWING_STAND = "minecraft:brewing_stand";
|
||||
public const BRICK = "minecraft:brick";
|
||||
public const BROWN_DYE = "minecraft:brown_dye";
|
||||
public const BRUSH = "minecraft:brush";
|
||||
public const BUCKET = "minecraft:bucket";
|
||||
public const BURN_POTTERY_SHERD = "minecraft:burn_pottery_sherd";
|
||||
public const CAKE = "minecraft:cake";
|
||||
public const CAMEL_SPAWN_EGG = "minecraft:camel_spawn_egg";
|
||||
public const CAMERA = "minecraft:camera";
|
||||
public const CAMPFIRE = "minecraft:campfire";
|
||||
public const CARPET = "minecraft:carpet";
|
||||
public const CARROT = "minecraft:carrot";
|
||||
public const CARROT_ON_A_STICK = "minecraft:carrot_on_a_stick";
|
||||
public const CAT_SPAWN_EGG = "minecraft:cat_spawn_egg";
|
||||
@ -93,6 +102,11 @@ final class ItemTypeNames{
|
||||
public const CHAINMAIL_HELMET = "minecraft:chainmail_helmet";
|
||||
public const CHAINMAIL_LEGGINGS = "minecraft:chainmail_leggings";
|
||||
public const CHARCOAL = "minecraft:charcoal";
|
||||
public const CHERRY_BOAT = "minecraft:cherry_boat";
|
||||
public const CHERRY_CHEST_BOAT = "minecraft:cherry_chest_boat";
|
||||
public const CHERRY_DOOR = "minecraft:cherry_door";
|
||||
public const CHERRY_HANGING_SIGN = "minecraft:cherry_hanging_sign";
|
||||
public const CHERRY_SIGN = "minecraft:cherry_sign";
|
||||
public const CHEST_BOAT = "minecraft:chest_boat";
|
||||
public const CHEST_MINECART = "minecraft:chest_minecart";
|
||||
public const CHICKEN = "minecraft:chicken";
|
||||
@ -101,6 +115,7 @@ final class ItemTypeNames{
|
||||
public const CLAY_BALL = "minecraft:clay_ball";
|
||||
public const CLOCK = "minecraft:clock";
|
||||
public const COAL = "minecraft:coal";
|
||||
public const COAST_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:coast_armor_trim_smithing_template";
|
||||
public const COCOA_BEANS = "minecraft:cocoa_beans";
|
||||
public const COD = "minecraft:cod";
|
||||
public const COD_BUCKET = "minecraft:cod_bucket";
|
||||
@ -109,6 +124,7 @@ final class ItemTypeNames{
|
||||
public const COMPARATOR = "minecraft:comparator";
|
||||
public const COMPASS = "minecraft:compass";
|
||||
public const COMPOUND = "minecraft:compound";
|
||||
public const CONCRETE = "minecraft:concrete";
|
||||
public const COOKED_BEEF = "minecraft:cooked_beef";
|
||||
public const COOKED_CHICKEN = "minecraft:cooked_chicken";
|
||||
public const COOKED_COD = "minecraft:cooked_cod";
|
||||
@ -118,18 +134,21 @@ final class ItemTypeNames{
|
||||
public const COOKED_SALMON = "minecraft:cooked_salmon";
|
||||
public const COOKIE = "minecraft:cookie";
|
||||
public const COPPER_INGOT = "minecraft:copper_ingot";
|
||||
public const CORAL = "minecraft:coral";
|
||||
public const COW_SPAWN_EGG = "minecraft:cow_spawn_egg";
|
||||
public const CREEPER_BANNER_PATTERN = "minecraft:creeper_banner_pattern";
|
||||
public const CREEPER_SPAWN_EGG = "minecraft:creeper_spawn_egg";
|
||||
public const CRIMSON_DOOR = "minecraft:crimson_door";
|
||||
public const CRIMSON_HANGING_SIGN = "minecraft:crimson_hanging_sign";
|
||||
public const CRIMSON_SIGN = "minecraft:crimson_sign";
|
||||
public const CROSSBOW = "minecraft:crossbow";
|
||||
public const CYAN_DYE = "minecraft:cyan_dye";
|
||||
public const DANGER_POTTERY_SHERD = "minecraft:danger_pottery_sherd";
|
||||
public const DARK_OAK_BOAT = "minecraft:dark_oak_boat";
|
||||
public const DARK_OAK_CHEST_BOAT = "minecraft:dark_oak_chest_boat";
|
||||
public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
|
||||
public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign";
|
||||
public const DARK_OAK_SIGN = "minecraft:dark_oak_sign";
|
||||
public const DEBUG_STICK = "minecraft:debug_stick";
|
||||
public const DIAMOND = "minecraft:diamond";
|
||||
public const DIAMOND_AXE = "minecraft:diamond_axe";
|
||||
public const DIAMOND_BOOTS = "minecraft:diamond_boots";
|
||||
@ -147,6 +166,7 @@ final class ItemTypeNames{
|
||||
public const DRAGON_BREATH = "minecraft:dragon_breath";
|
||||
public const DRIED_KELP = "minecraft:dried_kelp";
|
||||
public const DROWNED_SPAWN_EGG = "minecraft:drowned_spawn_egg";
|
||||
public const DUNE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:dune_armor_trim_smithing_template";
|
||||
public const DYE = "minecraft:dye";
|
||||
public const ECHO_SHARD = "minecraft:echo_shard";
|
||||
public const EGG = "minecraft:egg";
|
||||
@ -164,7 +184,10 @@ final class ItemTypeNames{
|
||||
public const ENDERMITE_SPAWN_EGG = "minecraft:endermite_spawn_egg";
|
||||
public const EVOKER_SPAWN_EGG = "minecraft:evoker_spawn_egg";
|
||||
public const EXPERIENCE_BOTTLE = "minecraft:experience_bottle";
|
||||
public const EXPLORER_POTTERY_SHERD = "minecraft:explorer_pottery_sherd";
|
||||
public const EYE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:eye_armor_trim_smithing_template";
|
||||
public const FEATHER = "minecraft:feather";
|
||||
public const FENCE = "minecraft:fence";
|
||||
public const FERMENTED_SPIDER_EYE = "minecraft:fermented_spider_eye";
|
||||
public const FIELD_MASONED_BANNER_PATTERN = "minecraft:field_masoned_banner_pattern";
|
||||
public const FILLED_MAP = "minecraft:filled_map";
|
||||
@ -178,6 +201,7 @@ final class ItemTypeNames{
|
||||
public const FLOWER_POT = "minecraft:flower_pot";
|
||||
public const FOX_SPAWN_EGG = "minecraft:fox_spawn_egg";
|
||||
public const FRAME = "minecraft:frame";
|
||||
public const FRIEND_POTTERY_SHERD = "minecraft:friend_pottery_sherd";
|
||||
public const FROG_SPAWN_EGG = "minecraft:frog_spawn_egg";
|
||||
public const GHAST_SPAWN_EGG = "minecraft:ghast_spawn_egg";
|
||||
public const GHAST_TEAR = "minecraft:ghast_tear";
|
||||
@ -211,12 +235,16 @@ final class ItemTypeNames{
|
||||
public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg";
|
||||
public const GUNPOWDER = "minecraft:gunpowder";
|
||||
public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea";
|
||||
public const HEART_POTTERY_SHERD = "minecraft:heart_pottery_sherd";
|
||||
public const HEARTBREAK_POTTERY_SHERD = "minecraft:heartbreak_pottery_sherd";
|
||||
public const HOGLIN_SPAWN_EGG = "minecraft:hoglin_spawn_egg";
|
||||
public const HONEY_BOTTLE = "minecraft:honey_bottle";
|
||||
public const HONEYCOMB = "minecraft:honeycomb";
|
||||
public const HOPPER = "minecraft:hopper";
|
||||
public const HOPPER_MINECART = "minecraft:hopper_minecart";
|
||||
public const HORSE_SPAWN_EGG = "minecraft:horse_spawn_egg";
|
||||
public const HOST_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:host_armor_trim_smithing_template";
|
||||
public const HOWL_POTTERY_SHERD = "minecraft:howl_pottery_sherd";
|
||||
public const HUSK_SPAWN_EGG = "minecraft:husk_spawn_egg";
|
||||
public const ICE_BOMB = "minecraft:ice_bomb";
|
||||
public const INK_SAC = "minecraft:ink_sac";
|
||||
@ -237,6 +265,7 @@ final class ItemTypeNames{
|
||||
public const JUNGLE_BOAT = "minecraft:jungle_boat";
|
||||
public const JUNGLE_CHEST_BOAT = "minecraft:jungle_chest_boat";
|
||||
public const JUNGLE_DOOR = "minecraft:jungle_door";
|
||||
public const JUNGLE_HANGING_SIGN = "minecraft:jungle_hanging_sign";
|
||||
public const JUNGLE_SIGN = "minecraft:jungle_sign";
|
||||
public const KELP = "minecraft:kelp";
|
||||
public const LAPIS_LAZULI = "minecraft:lapis_lazuli";
|
||||
@ -254,20 +283,25 @@ final class ItemTypeNames{
|
||||
public const LINGERING_POTION = "minecraft:lingering_potion";
|
||||
public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg";
|
||||
public const LODESTONE_COMPASS = "minecraft:lodestone_compass";
|
||||
public const LOG = "minecraft:log";
|
||||
public const LOG2 = "minecraft:log2";
|
||||
public const MAGENTA_DYE = "minecraft:magenta_dye";
|
||||
public const MAGMA_CREAM = "minecraft:magma_cream";
|
||||
public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg";
|
||||
public const MANGROVE_BOAT = "minecraft:mangrove_boat";
|
||||
public const MANGROVE_CHEST_BOAT = "minecraft:mangrove_chest_boat";
|
||||
public const MANGROVE_DOOR = "minecraft:mangrove_door";
|
||||
public const MANGROVE_HANGING_SIGN = "minecraft:mangrove_hanging_sign";
|
||||
public const MANGROVE_SIGN = "minecraft:mangrove_sign";
|
||||
public const MEDICINE = "minecraft:medicine";
|
||||
public const MELON_SEEDS = "minecraft:melon_seeds";
|
||||
public const MELON_SLICE = "minecraft:melon_slice";
|
||||
public const MILK_BUCKET = "minecraft:milk_bucket";
|
||||
public const MINECART = "minecraft:minecart";
|
||||
public const MINER_POTTERY_SHERD = "minecraft:miner_pottery_sherd";
|
||||
public const MOJANG_BANNER_PATTERN = "minecraft:mojang_banner_pattern";
|
||||
public const MOOSHROOM_SPAWN_EGG = "minecraft:mooshroom_spawn_egg";
|
||||
public const MOURNER_POTTERY_SHERD = "minecraft:mourner_pottery_sherd";
|
||||
public const MULE_SPAWN_EGG = "minecraft:mule_spawn_egg";
|
||||
public const MUSHROOM_STEW = "minecraft:mushroom_stew";
|
||||
public const MUSIC_DISC_11 = "minecraft:music_disc_11";
|
||||
@ -281,6 +315,7 @@ final class ItemTypeNames{
|
||||
public const MUSIC_DISC_MELLOHI = "minecraft:music_disc_mellohi";
|
||||
public const MUSIC_DISC_OTHERSIDE = "minecraft:music_disc_otherside";
|
||||
public const MUSIC_DISC_PIGSTEP = "minecraft:music_disc_pigstep";
|
||||
public const MUSIC_DISC_RELIC = "minecraft:music_disc_relic";
|
||||
public const MUSIC_DISC_STAL = "minecraft:music_disc_stal";
|
||||
public const MUSIC_DISC_STRAD = "minecraft:music_disc_strad";
|
||||
public const MUSIC_DISC_WAIT = "minecraft:music_disc_wait";
|
||||
@ -303,9 +338,11 @@ final class ItemTypeNames{
|
||||
public const NETHERITE_SCRAP = "minecraft:netherite_scrap";
|
||||
public const NETHERITE_SHOVEL = "minecraft:netherite_shovel";
|
||||
public const NETHERITE_SWORD = "minecraft:netherite_sword";
|
||||
public const NETHERITE_UPGRADE_SMITHING_TEMPLATE = "minecraft:netherite_upgrade_smithing_template";
|
||||
public const NPC_SPAWN_EGG = "minecraft:npc_spawn_egg";
|
||||
public const OAK_BOAT = "minecraft:oak_boat";
|
||||
public const OAK_CHEST_BOAT = "minecraft:oak_chest_boat";
|
||||
public const OAK_HANGING_SIGN = "minecraft:oak_hanging_sign";
|
||||
public const OAK_SIGN = "minecraft:oak_sign";
|
||||
public const OCELOT_SPAWN_EGG = "minecraft:ocelot_spawn_egg";
|
||||
public const ORANGE_DYE = "minecraft:orange_dye";
|
||||
@ -321,6 +358,8 @@ final class ItemTypeNames{
|
||||
public const PIGLIN_SPAWN_EGG = "minecraft:piglin_spawn_egg";
|
||||
public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg";
|
||||
public const PINK_DYE = "minecraft:pink_dye";
|
||||
public const PITCHER_POD = "minecraft:pitcher_pod";
|
||||
public const PLENTY_POTTERY_SHERD = "minecraft:plenty_pottery_sherd";
|
||||
public const POISONOUS_POTATO = "minecraft:poisonous_potato";
|
||||
public const POLAR_BEAR_SPAWN_EGG = "minecraft:polar_bear_spawn_egg";
|
||||
public const POPPED_CHORUS_FRUIT = "minecraft:popped_chorus_fruit";
|
||||
@ -330,7 +369,7 @@ final class ItemTypeNames{
|
||||
public const POWDER_SNOW_BUCKET = "minecraft:powder_snow_bucket";
|
||||
public const PRISMARINE_CRYSTALS = "minecraft:prismarine_crystals";
|
||||
public const PRISMARINE_SHARD = "minecraft:prismarine_shard";
|
||||
public const PRIZE_POTTERY_SHARD = "minecraft:prize_pottery_shard";
|
||||
public const PRIZE_POTTERY_SHERD = "minecraft:prize_pottery_sherd";
|
||||
public const PUFFERFISH = "minecraft:pufferfish";
|
||||
public const PUFFERFISH_BUCKET = "minecraft:pufferfish_bucket";
|
||||
public const PUFFERFISH_SPAWN_EGG = "minecraft:pufferfish_spawn_egg";
|
||||
@ -343,6 +382,7 @@ final class ItemTypeNames{
|
||||
public const RABBIT_HIDE = "minecraft:rabbit_hide";
|
||||
public const RABBIT_SPAWN_EGG = "minecraft:rabbit_spawn_egg";
|
||||
public const RABBIT_STEW = "minecraft:rabbit_stew";
|
||||
public const RAISER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:raiser_armor_trim_smithing_template";
|
||||
public const RAPID_FERTILIZER = "minecraft:rapid_fertilizer";
|
||||
public const RAVAGER_SPAWN_EGG = "minecraft:ravager_spawn_egg";
|
||||
public const RAW_COPPER = "minecraft:raw_copper";
|
||||
@ -352,26 +392,35 @@ final class ItemTypeNames{
|
||||
public const RED_DYE = "minecraft:red_dye";
|
||||
public const REDSTONE = "minecraft:redstone";
|
||||
public const REPEATER = "minecraft:repeater";
|
||||
public const RIB_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:rib_armor_trim_smithing_template";
|
||||
public const ROTTEN_FLESH = "minecraft:rotten_flesh";
|
||||
public const SADDLE = "minecraft:saddle";
|
||||
public const SALMON = "minecraft:salmon";
|
||||
public const SALMON_BUCKET = "minecraft:salmon_bucket";
|
||||
public const SALMON_SPAWN_EGG = "minecraft:salmon_spawn_egg";
|
||||
public const SCUTE = "minecraft:scute";
|
||||
public const SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:sentry_armor_trim_smithing_template";
|
||||
public const SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:shaper_armor_trim_smithing_template";
|
||||
public const SHEAF_POTTERY_SHERD = "minecraft:sheaf_pottery_sherd";
|
||||
public const SHEARS = "minecraft:shears";
|
||||
public const SHEEP_SPAWN_EGG = "minecraft:sheep_spawn_egg";
|
||||
public const SHELTER_POTTERY_SHERD = "minecraft:shelter_pottery_sherd";
|
||||
public const SHIELD = "minecraft:shield";
|
||||
public const SHULKER_BOX = "minecraft:shulker_box";
|
||||
public const SHULKER_SHELL = "minecraft:shulker_shell";
|
||||
public const SHULKER_SPAWN_EGG = "minecraft:shulker_spawn_egg";
|
||||
public const SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:silence_armor_trim_smithing_template";
|
||||
public const SILVERFISH_SPAWN_EGG = "minecraft:silverfish_spawn_egg";
|
||||
public const SKELETON_HORSE_SPAWN_EGG = "minecraft:skeleton_horse_spawn_egg";
|
||||
public const SKELETON_SPAWN_EGG = "minecraft:skeleton_spawn_egg";
|
||||
public const SKULL = "minecraft:skull";
|
||||
public const SKULL_BANNER_PATTERN = "minecraft:skull_banner_pattern";
|
||||
public const SKULL_POTTERY_SHARD = "minecraft:skull_pottery_shard";
|
||||
public const SKULL_POTTERY_SHERD = "minecraft:skull_pottery_sherd";
|
||||
public const SLIME_BALL = "minecraft:slime_ball";
|
||||
public const SLIME_SPAWN_EGG = "minecraft:slime_spawn_egg";
|
||||
public const SNIFFER_SPAWN_EGG = "minecraft:sniffer_spawn_egg";
|
||||
public const SNORT_POTTERY_SHERD = "minecraft:snort_pottery_sherd";
|
||||
public const SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:snout_armor_trim_smithing_template";
|
||||
public const SNOW_GOLEM_SPAWN_EGG = "minecraft:snow_golem_spawn_egg";
|
||||
public const SNOWBALL = "minecraft:snowball";
|
||||
public const SOUL_CAMPFIRE = "minecraft:soul_campfire";
|
||||
@ -379,10 +428,12 @@ final class ItemTypeNames{
|
||||
public const SPAWN_EGG = "minecraft:spawn_egg";
|
||||
public const SPIDER_EYE = "minecraft:spider_eye";
|
||||
public const SPIDER_SPAWN_EGG = "minecraft:spider_spawn_egg";
|
||||
public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:spire_armor_trim_smithing_template";
|
||||
public const SPLASH_POTION = "minecraft:splash_potion";
|
||||
public const SPRUCE_BOAT = "minecraft:spruce_boat";
|
||||
public const SPRUCE_CHEST_BOAT = "minecraft:spruce_chest_boat";
|
||||
public const SPRUCE_DOOR = "minecraft:spruce_door";
|
||||
public const SPRUCE_HANGING_SIGN = "minecraft:spruce_hanging_sign";
|
||||
public const SPRUCE_SIGN = "minecraft:spruce_sign";
|
||||
public const SPYGLASS = "minecraft:spyglass";
|
||||
public const SQUID_SPAWN_EGG = "minecraft:squid_spawn_egg";
|
||||
@ -401,6 +452,7 @@ final class ItemTypeNames{
|
||||
public const SWEET_BERRIES = "minecraft:sweet_berries";
|
||||
public const TADPOLE_BUCKET = "minecraft:tadpole_bucket";
|
||||
public const TADPOLE_SPAWN_EGG = "minecraft:tadpole_spawn_egg";
|
||||
public const TIDE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:tide_armor_trim_smithing_template";
|
||||
public const TNT_MINECART = "minecraft:tnt_minecart";
|
||||
public const TORCHFLOWER_SEEDS = "minecraft:torchflower_seeds";
|
||||
public const TOTEM_OF_UNDYING = "minecraft:totem_of_undying";
|
||||
@ -411,18 +463,23 @@ final class ItemTypeNames{
|
||||
public const TROPICAL_FISH_SPAWN_EGG = "minecraft:tropical_fish_spawn_egg";
|
||||
public const TURTLE_HELMET = "minecraft:turtle_helmet";
|
||||
public const TURTLE_SPAWN_EGG = "minecraft:turtle_spawn_egg";
|
||||
public const VEX_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:vex_armor_trim_smithing_template";
|
||||
public const VEX_SPAWN_EGG = "minecraft:vex_spawn_egg";
|
||||
public const VILLAGER_SPAWN_EGG = "minecraft:villager_spawn_egg";
|
||||
public const VINDICATOR_SPAWN_EGG = "minecraft:vindicator_spawn_egg";
|
||||
public const WANDERING_TRADER_SPAWN_EGG = "minecraft:wandering_trader_spawn_egg";
|
||||
public const WARD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:ward_armor_trim_smithing_template";
|
||||
public const WARDEN_SPAWN_EGG = "minecraft:warden_spawn_egg";
|
||||
public const WARPED_DOOR = "minecraft:warped_door";
|
||||
public const WARPED_FUNGUS_ON_A_STICK = "minecraft:warped_fungus_on_a_stick";
|
||||
public const WARPED_HANGING_SIGN = "minecraft:warped_hanging_sign";
|
||||
public const WARPED_SIGN = "minecraft:warped_sign";
|
||||
public const WATER_BUCKET = "minecraft:water_bucket";
|
||||
public const WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wayfinder_armor_trim_smithing_template";
|
||||
public const WHEAT = "minecraft:wheat";
|
||||
public const WHEAT_SEEDS = "minecraft:wheat_seeds";
|
||||
public const WHITE_DYE = "minecraft:white_dye";
|
||||
public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wild_armor_trim_smithing_template";
|
||||
public const WITCH_SPAWN_EGG = "minecraft:witch_spawn_egg";
|
||||
public const WITHER_SKELETON_SPAWN_EGG = "minecraft:wither_skeleton_spawn_egg";
|
||||
public const WITHER_SPAWN_EGG = "minecraft:wither_spawn_egg";
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock\item;
|
||||
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\VersionInfo;
|
||||
|
||||
final class SavedItemData{
|
||||
|
||||
@ -59,6 +60,7 @@ final class SavedItemData{
|
||||
if($this->tag !== null){
|
||||
$result->setTag(self::TAG_TAG, $this->tag);
|
||||
}
|
||||
$result->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ final class ItemDataUpgrader{
|
||||
[$newNameId, $newMeta] = $this->idMetaUpgrader->upgrade($rawNameId, $meta);
|
||||
|
||||
//TODO: this won't account for spawn eggs from before 1.16.100 - perhaps we're lucky and they just left the meta in there anyway?
|
||||
//TODO: read version from VersionInfo::TAG_WORLD_DATA_VERSION - we may need it to fix up old items
|
||||
|
||||
return new SavedItemData($newNameId, $newMeta, $blockStateData, $tag->getCompoundTag(SavedItemData::TAG_TAG));
|
||||
}
|
||||
|
@ -27,6 +27,14 @@ use pocketmine\block\utils\BrewingStandSlot;
|
||||
use pocketmine\block\utils\WallConnectionType;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
/**
|
||||
* Interface implemented by {@link RuntimeDataReader}, {@link RuntimeDataWriter} and {@link RuntimeDataSizeCalculator}.
|
||||
* Used to describe the structure of runtime data to an implementation.
|
||||
*
|
||||
* This interface should be considered **sealed**.
|
||||
* You may use it as a type for parameters and return values, but it should not be implemented outside of this package.
|
||||
* New methods may be added without warning.
|
||||
*/
|
||||
interface RuntimeDataDescriber extends RuntimeEnumDescriber{
|
||||
public function int(int $bits, int &$value) : void;
|
||||
|
||||
@ -36,6 +44,11 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{
|
||||
|
||||
public function horizontalFacing(int &$facing) : void;
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
*/
|
||||
public function facingFlags(array &$faces) : void;
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
*/
|
||||
|
@ -86,6 +86,20 @@ final class RuntimeDataReader implements RuntimeDataDescriber{
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
*/
|
||||
public function facingFlags(array &$faces) : void{
|
||||
$result = [];
|
||||
foreach(Facing::ALL as $facing){
|
||||
if($this->readBool()){
|
||||
$result[$facing] = $facing;
|
||||
}
|
||||
}
|
||||
|
||||
$faces = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
*/
|
||||
|
@ -56,12 +56,12 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{
|
||||
$this->addBits(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function facingFlags(array &$faces) : void{
|
||||
$this->addBits(count(Facing::ALL));
|
||||
}
|
||||
|
||||
public function horizontalFacingFlags(array &$faces) : void{
|
||||
$this->addBits(count(Facing::HORIZONTAL));
|
||||
// TODO: Implement horizontalFacingFlags() method.
|
||||
}
|
||||
|
||||
public function facing(int &$facing) : void{
|
||||
|
@ -85,6 +85,16 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
*/
|
||||
public function facingFlags(array &$faces) : void{
|
||||
$uniqueFaces = array_flip($faces);
|
||||
foreach(Facing::ALL as $facing){
|
||||
$this->writeBool(isset($uniqueFaces[$facing]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $faces
|
||||
*/
|
||||
|
@ -45,12 +45,12 @@ interface RuntimeEnumDescriber{
|
||||
|
||||
public function medicineType(\pocketmine\item\MedicineType &$value) : void;
|
||||
|
||||
public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void;
|
||||
|
||||
public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void;
|
||||
|
||||
public function potionType(\pocketmine\item\PotionType &$value) : void;
|
||||
|
||||
public function skullType(\pocketmine\block\utils\SkullType &$value) : void;
|
||||
|
||||
public function slabType(\pocketmine\block\utils\SlabType &$value) : void;
|
||||
|
||||
public function suspiciousStewType(\pocketmine\item\SuspiciousStewType &$value) : void;
|
||||
|
@ -126,6 +126,19 @@ trait RuntimeEnumDeserializerTrait{
|
||||
};
|
||||
}
|
||||
|
||||
public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{
|
||||
$value = match($this->readInt(3)){
|
||||
0 => \pocketmine\block\utils\MobHeadType::CREEPER(),
|
||||
1 => \pocketmine\block\utils\MobHeadType::DRAGON(),
|
||||
2 => \pocketmine\block\utils\MobHeadType::PIGLIN(),
|
||||
3 => \pocketmine\block\utils\MobHeadType::PLAYER(),
|
||||
4 => \pocketmine\block\utils\MobHeadType::SKELETON(),
|
||||
5 => \pocketmine\block\utils\MobHeadType::WITHER_SKELETON(),
|
||||
6 => \pocketmine\block\utils\MobHeadType::ZOMBIE(),
|
||||
default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MobHeadType")
|
||||
};
|
||||
}
|
||||
|
||||
public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
|
||||
$value = match($this->readInt(4)){
|
||||
0 => \pocketmine\block\utils\MushroomBlockType::ALL_CAP(),
|
||||
@ -191,18 +204,6 @@ trait RuntimeEnumDeserializerTrait{
|
||||
};
|
||||
}
|
||||
|
||||
public function skullType(\pocketmine\block\utils\SkullType &$value) : void{
|
||||
$value = match($this->readInt(3)){
|
||||
0 => \pocketmine\block\utils\SkullType::CREEPER(),
|
||||
1 => \pocketmine\block\utils\SkullType::DRAGON(),
|
||||
2 => \pocketmine\block\utils\SkullType::PLAYER(),
|
||||
3 => \pocketmine\block\utils\SkullType::SKELETON(),
|
||||
4 => \pocketmine\block\utils\SkullType::WITHER_SKELETON(),
|
||||
5 => \pocketmine\block\utils\SkullType::ZOMBIE(),
|
||||
default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for SkullType")
|
||||
};
|
||||
}
|
||||
|
||||
public function slabType(\pocketmine\block\utils\SlabType &$value) : void{
|
||||
$value = match($this->readInt(2)){
|
||||
0 => \pocketmine\block\utils\SlabType::BOTTOM(),
|
||||
|
@ -126,6 +126,19 @@ trait RuntimeEnumSerializerTrait{
|
||||
});
|
||||
}
|
||||
|
||||
public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{
|
||||
$this->writeInt(3, match($value){
|
||||
\pocketmine\block\utils\MobHeadType::CREEPER() => 0,
|
||||
\pocketmine\block\utils\MobHeadType::DRAGON() => 1,
|
||||
\pocketmine\block\utils\MobHeadType::PIGLIN() => 2,
|
||||
\pocketmine\block\utils\MobHeadType::PLAYER() => 3,
|
||||
\pocketmine\block\utils\MobHeadType::SKELETON() => 4,
|
||||
\pocketmine\block\utils\MobHeadType::WITHER_SKELETON() => 5,
|
||||
\pocketmine\block\utils\MobHeadType::ZOMBIE() => 6,
|
||||
default => throw new \pocketmine\utils\AssumptionFailedError("All MobHeadType cases should be covered")
|
||||
});
|
||||
}
|
||||
|
||||
public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
|
||||
$this->writeInt(4, match($value){
|
||||
\pocketmine\block\utils\MushroomBlockType::ALL_CAP() => 0,
|
||||
@ -191,18 +204,6 @@ trait RuntimeEnumSerializerTrait{
|
||||
});
|
||||
}
|
||||
|
||||
public function skullType(\pocketmine\block\utils\SkullType &$value) : void{
|
||||
$this->writeInt(3, match($value){
|
||||
\pocketmine\block\utils\SkullType::CREEPER() => 0,
|
||||
\pocketmine\block\utils\SkullType::DRAGON() => 1,
|
||||
\pocketmine\block\utils\SkullType::PLAYER() => 2,
|
||||
\pocketmine\block\utils\SkullType::SKELETON() => 3,
|
||||
\pocketmine\block\utils\SkullType::WITHER_SKELETON() => 4,
|
||||
\pocketmine\block\utils\SkullType::ZOMBIE() => 5,
|
||||
default => throw new \pocketmine\utils\AssumptionFailedError("All SkullType cases should be covered")
|
||||
});
|
||||
}
|
||||
|
||||
public function slabType(\pocketmine\block\utils\SlabType &$value) : void{
|
||||
$this->writeInt(2, match($value){
|
||||
\pocketmine\block\utils\SlabType::BOTTOM() => 0,
|
||||
|
@ -63,6 +63,10 @@ trait RuntimeEnumSizeCalculatorTrait{
|
||||
$this->addBits(2);
|
||||
}
|
||||
|
||||
public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{
|
||||
$this->addBits(3);
|
||||
}
|
||||
|
||||
public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
|
||||
$this->addBits(4);
|
||||
}
|
||||
@ -71,10 +75,6 @@ trait RuntimeEnumSizeCalculatorTrait{
|
||||
$this->addBits(6);
|
||||
}
|
||||
|
||||
public function skullType(\pocketmine\block\utils\SkullType &$value) : void{
|
||||
$this->addBits(3);
|
||||
}
|
||||
|
||||
public function slabType(\pocketmine\block\utils\SlabType &$value) : void{
|
||||
$this->addBits(2);
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\VersionInfo;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\Sound;
|
||||
@ -170,7 +171,7 @@ abstract class Entity{
|
||||
|
||||
protected bool $canClimb = false;
|
||||
protected bool $canClimbWalls = false;
|
||||
protected bool $immobile = false;
|
||||
protected bool $noClientPredictions = false;
|
||||
protected bool $invisible = false;
|
||||
protected bool $silent = false;
|
||||
|
||||
@ -314,12 +315,24 @@ abstract class Entity{
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
public function isImmobile() : bool{
|
||||
return $this->immobile;
|
||||
/**
|
||||
* Returns whether clients may predict this entity's behaviour and movement. Used for things like water movement,
|
||||
* burning, and movement smoothing (interpolation).
|
||||
*/
|
||||
public function hasNoClientPredictions() : bool{
|
||||
return $this->noClientPredictions;
|
||||
}
|
||||
|
||||
public function setImmobile(bool $value = true) : void{
|
||||
$this->immobile = $value;
|
||||
/**
|
||||
* Things such as movement in water, burning, etc. may be predicted by the client. This is sometimes not desirable,
|
||||
* since server-side logic may differ from client-side prediction. However, things like movement smoothing
|
||||
* (interpolation) are also controlled by this, so it should be used with care.
|
||||
*
|
||||
* Setting this flag will also disable player movement inputs, but this should not be relied on, as cheat clients
|
||||
* will be able to bypass it.
|
||||
*/
|
||||
public function setNoClientPredictions(bool $value = true) : void{
|
||||
$this->noClientPredictions = $value;
|
||||
$this->networkPropertiesDirty = true;
|
||||
}
|
||||
|
||||
@ -477,6 +490,8 @@ abstract class Entity{
|
||||
$nbt->setShort(self::TAG_FIRE, $this->fireTicks);
|
||||
$nbt->setByte(self::TAG_ON_GROUND, $this->onGround ? 1 : 0);
|
||||
|
||||
$nbt->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
|
||||
|
||||
return $nbt;
|
||||
}
|
||||
|
||||
@ -730,7 +745,7 @@ abstract class Entity{
|
||||
$wasStill = $this->lastMotion->lengthSquared() == 0.0;
|
||||
if($wasStill !== $still){
|
||||
//TODO: hack for client-side AI interference: prevent client sided movement when motion is 0
|
||||
$this->setImmobile($still);
|
||||
$this->setNoClientPredictions($still);
|
||||
}
|
||||
|
||||
if($teleport || $diffPosition > 0.0001 || $diffRotation > 1.0 || (!$wasStill && $still)){
|
||||
@ -751,29 +766,21 @@ abstract class Entity{
|
||||
}
|
||||
|
||||
protected function broadcastMovement(bool $teleport = false) : void{
|
||||
if($teleport){
|
||||
//TODO: HACK! workaround for https://github.com/pmmp/PocketMine-MP/issues/4394
|
||||
//this happens because MoveActor*Packet doesn't clear interpolation targets on the client, so the entity
|
||||
//snaps to the teleport position, but then lerps back to the original position if a normal movement for the
|
||||
//entity was recently broadcasted. This can be seen with players throwing ender pearls.
|
||||
//TODO: remove this if the bug ever gets fixed (lol)
|
||||
foreach($this->hasSpawned as $player){
|
||||
$this->despawnFrom($player);
|
||||
$this->spawnTo($player);
|
||||
}
|
||||
}else{
|
||||
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
|
||||
$this->id,
|
||||
$this->getOffsetPosition($this->location),
|
||||
$this->location->pitch,
|
||||
$this->location->yaw,
|
||||
$this->location->yaw,
|
||||
(
|
||||
//TODO: if the above hack for #4394 gets removed, we should be setting FLAG_TELEPORT here
|
||||
($this->onGround ? MoveActorAbsolutePacket::FLAG_GROUND : 0)
|
||||
)
|
||||
)]);
|
||||
}
|
||||
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
|
||||
$this->id,
|
||||
$this->getOffsetPosition($this->location),
|
||||
$this->location->pitch,
|
||||
$this->location->yaw,
|
||||
$this->location->yaw,
|
||||
(
|
||||
//TODO: We should be setting FLAG_TELEPORT here to disable client-side movement interpolation, but it
|
||||
//breaks player teleporting (observers see the player rubberband back to the pre-teleport position while
|
||||
//the teleported player sees themselves at the correct position), and does nothing whatsoever for
|
||||
//non-player entities (movement is still interpolated). Both of these are client bugs.
|
||||
//See https://github.com/pmmp/PocketMine-MP/issues/4394
|
||||
($this->onGround ? MoveActorAbsolutePacket::FLAG_GROUND : 0)
|
||||
)
|
||||
)]);
|
||||
}
|
||||
|
||||
protected function broadcastMotion() : void{
|
||||
@ -1651,7 +1658,7 @@ abstract class Entity{
|
||||
$properties->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG, $this->nameTagVisible);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, true);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::IMMOBILE, $this->immobile);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::NO_AI, $this->noClientPredictions);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::INVISIBLE, $this->invisible);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::SILENT, $this->silent);
|
||||
$properties->setGenericFlag(EntityMetadataFlags::ONFIRE, $this->isOnFire());
|
||||
|
@ -484,14 +484,16 @@ abstract class Living extends Entity{
|
||||
public function damageArmor(float $damage) : void{
|
||||
$durabilityRemoved = (int) max(floor($damage / 4), 1);
|
||||
|
||||
$armor = $this->armorInventory->getContents(true);
|
||||
foreach($armor as $item){
|
||||
$armor = $this->armorInventory->getContents();
|
||||
foreach($armor as $slotId => $item){
|
||||
if($item instanceof Armor){
|
||||
$oldItem = clone $item;
|
||||
$this->damageItem($item, $durabilityRemoved);
|
||||
if(!$item->equalsExact($oldItem)){
|
||||
$this->armorInventory->setItem($slotId, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->armorInventory->setContents($armor);
|
||||
}
|
||||
|
||||
private function damageItem(Durable $item, int $durabilityRemoved) : void{
|
||||
@ -637,9 +639,12 @@ abstract class Living extends Entity{
|
||||
}
|
||||
|
||||
foreach($this->armorInventory->getContents() as $index => $item){
|
||||
$oldItem = clone $item;
|
||||
if($item->onTickWorn($this)){
|
||||
$hasUpdate = true;
|
||||
$this->armorInventory->setItem($index, $item);
|
||||
if(!$item->equalsExact($oldItem)){
|
||||
$this->armorInventory->setItem($index, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,12 @@ class BlockPlaceEvent extends Event implements Cancellable{
|
||||
protected BlockTransaction $transaction,
|
||||
protected Block $blockAgainst,
|
||||
protected Item $item
|
||||
){}
|
||||
){
|
||||
$world = $this->blockAgainst->getPosition()->getWorld();
|
||||
foreach($this->transaction->getBlocks() as [$x, $y, $z, $block]){
|
||||
$block->position($world, $x, $y, $z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player who is placing the block.
|
||||
|
@ -30,6 +30,11 @@ use pocketmine\block\Block;
|
||||
*/
|
||||
class BlockSpreadEvent extends BaseBlockChangeEvent{
|
||||
|
||||
/**
|
||||
* @param Block $block Block being replaced (TODO: rename this)
|
||||
* @param Block $source Origin of the spread
|
||||
* @param Block $newState Replacement block
|
||||
*/
|
||||
public function __construct(
|
||||
Block $block,
|
||||
private Block $source,
|
||||
|
@ -39,6 +39,7 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
protected Player $player;
|
||||
|
||||
private Translatable|string $deathMessage;
|
||||
private Translatable|string $deathScreenMessage;
|
||||
private bool $keepInventory = false;
|
||||
private bool $keepXp = false;
|
||||
|
||||
@ -50,6 +51,7 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
parent::__construct($entity, $drops, $xp);
|
||||
$this->player = $entity;
|
||||
$this->deathMessage = $deathMessage ?? self::deriveMessage($entity->getDisplayName(), $entity->getLastDamageCause());
|
||||
$this->deathScreenMessage = $this->deathMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,6 +73,14 @@ class PlayerDeathEvent extends EntityDeathEvent{
|
||||
$this->deathMessage = $deathMessage;
|
||||
}
|
||||
|
||||
public function getDeathScreenMessage() : Translatable|string{
|
||||
return $this->deathScreenMessage;
|
||||
}
|
||||
|
||||
public function setDeathScreenMessage(Translatable|string $deathScreenMessage) : void{
|
||||
$this->deathScreenMessage = $deathScreenMessage;
|
||||
}
|
||||
|
||||
public function getKeepInventory() : bool{
|
||||
return $this->keepInventory;
|
||||
}
|
||||
|
@ -27,16 +27,23 @@ use pocketmine\crafting\CraftingManagerFromDataHelper;
|
||||
use pocketmine\crafting\json\ItemStackData;
|
||||
use pocketmine\data\bedrock\BedrockDataFiles;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\utils\DestructorCallbackTrait;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
final class CreativeInventory{
|
||||
use SingletonTrait;
|
||||
use DestructorCallbackTrait;
|
||||
|
||||
/** @var Item[] */
|
||||
private array $creative = [];
|
||||
|
||||
/** @phpstan-var ObjectSet<\Closure() : void> */
|
||||
private ObjectSet $contentChangedCallbacks;
|
||||
|
||||
private function __construct(){
|
||||
$this->contentChangedCallbacks = new ObjectSet();
|
||||
$creativeItems = CraftingManagerFromDataHelper::loadJsonArrayOfObjectsFile(
|
||||
BedrockDataFiles::CREATIVEITEMS_JSON,
|
||||
ItemStackData::class
|
||||
@ -57,6 +64,7 @@ final class CreativeInventory{
|
||||
*/
|
||||
public function clear() : void{
|
||||
$this->creative = [];
|
||||
$this->onContentChange();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,6 +94,7 @@ final class CreativeInventory{
|
||||
*/
|
||||
public function add(Item $item) : void{
|
||||
$this->creative[] = clone $item;
|
||||
$this->onContentChange();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,10 +105,22 @@ final class CreativeInventory{
|
||||
$index = $this->getItemIndex($item);
|
||||
if($index !== -1){
|
||||
unset($this->creative[$index]);
|
||||
$this->onContentChange();
|
||||
}
|
||||
}
|
||||
|
||||
public function contains(Item $item) : bool{
|
||||
return $this->getItemIndex($item) !== -1;
|
||||
}
|
||||
|
||||
/** @phpstan-return ObjectSet<\Closure() : void> */
|
||||
public function getContentChangedCallbacks() : ObjectSet{
|
||||
return $this->contentChangedCallbacks;
|
||||
}
|
||||
|
||||
private function onContentChange() : void{
|
||||
foreach($this->contentChangedCallbacks as $callback){
|
||||
$callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,11 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
/**
|
||||
* Enum of all the item runtime IDs used by PocketMine-MP. These IDs are specific to PocketMine-MP and have no
|
||||
* relevance to any Minecraft vanilla things.
|
||||
* Every item in {@link VanillaItems} has a corresponding constant in this class. These constants can be used to
|
||||
* identify and compare item types efficiently using {@link Item::getTypeId()}.
|
||||
*
|
||||
* WARNING: DO NOT STORE THESE IDS. They can and will change without warning.
|
||||
* They should ONLY be used to IDENTIFY items at runtime.
|
||||
* WARNING: These are NOT a replacement for Minecraft legacy IDs. Do **NOT** hardcode their values, or store them in
|
||||
* configs or databases. They will change without warning.
|
||||
*/
|
||||
final class ItemTypeIds{
|
||||
|
||||
@ -302,8 +302,9 @@ final class ItemTypeIds{
|
||||
public const MEDICINE = 20263;
|
||||
public const MANGROVE_BOAT = 20264;
|
||||
public const GLOW_BERRIES = 20265;
|
||||
public const CHERRY_SIGN = 20266;
|
||||
|
||||
public const FIRST_UNUSED_ITEM_ID = 20266;
|
||||
public const FIRST_UNUSED_ITEM_ID = 20267;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;
|
||||
|
||||
|
@ -30,7 +30,7 @@ use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DirtType;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FroglightType;
|
||||
use pocketmine\block\utils\SkullType;
|
||||
use pocketmine\block\utils\MobHeadType;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\VanillaBlocks as Blocks;
|
||||
use pocketmine\item\VanillaItems as Items;
|
||||
@ -208,6 +208,19 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("cauldron", fn() => Blocks::CAULDRON());
|
||||
$result->registerBlock("cave_vines", fn() => Blocks::CAVE_VINES());
|
||||
$result->registerBlock("chain", fn() => Blocks::CHAIN());
|
||||
$result->registerBlock("cherry_button", fn() => Blocks::CHERRY_BUTTON());
|
||||
$result->registerBlock("cherry_door", fn() => Blocks::CHERRY_DOOR());
|
||||
$result->registerBlock("cherry_fence", fn() => Blocks::CHERRY_FENCE());
|
||||
$result->registerBlock("cherry_fence_gate", fn() => Blocks::CHERRY_FENCE_GATE());
|
||||
$result->registerBlock("cherry_leaves", fn() => Blocks::CHERRY_LEAVES());
|
||||
$result->registerBlock("cherry_log", fn() => Blocks::CHERRY_LOG());
|
||||
$result->registerBlock("cherry_planks", fn() => Blocks::CHERRY_PLANKS());
|
||||
$result->registerBlock("cherry_pressure_plate", fn() => Blocks::CHERRY_PRESSURE_PLATE());
|
||||
$result->registerBlock("cherry_sign", fn() => Blocks::CHERRY_SIGN());
|
||||
$result->registerBlock("cherry_slab", fn() => Blocks::CHERRY_SLAB());
|
||||
$result->registerBlock("cherry_stairs", fn() => Blocks::CHERRY_STAIRS());
|
||||
$result->registerBlock("cherry_trapdoor", fn() => Blocks::CHERRY_TRAPDOOR());
|
||||
$result->registerBlock("cherry_wood", fn() => Blocks::CHERRY_WOOD());
|
||||
$result->registerBlock("chemical_heat", fn() => Blocks::CHEMICAL_HEAT());
|
||||
$result->registerBlock("chemistry_table", fn() => Blocks::COMPOUND_CREATOR());
|
||||
$result->registerBlock("chest", fn() => Blocks::CHEST());
|
||||
@ -264,7 +277,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("cracked_polished_blackstone_bricks", fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS());
|
||||
$result->registerBlock("cracked_stone_bricks", fn() => Blocks::CRACKED_STONE_BRICKS());
|
||||
$result->registerBlock("crafting_table", fn() => Blocks::CRAFTING_TABLE());
|
||||
$result->registerBlock("creeper_head", fn() => Blocks::MOB_HEAD()->setSkullType(SkullType::CREEPER()));
|
||||
$result->registerBlock("creeper_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::CREEPER()));
|
||||
$result->registerBlock("crimson_button", fn() => Blocks::CRIMSON_BUTTON());
|
||||
$result->registerBlock("crimson_door", fn() => Blocks::CRIMSON_DOOR());
|
||||
$result->registerBlock("crimson_fence", fn() => Blocks::CRIMSON_FENCE());
|
||||
@ -356,7 +369,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("double_wooden_slab", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE()));
|
||||
$result->registerBlock("double_wooden_slabs", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE()));
|
||||
$result->registerBlock("dragon_egg", fn() => Blocks::DRAGON_EGG());
|
||||
$result->registerBlock("dragon_head", fn() => Blocks::MOB_HEAD()->setSkullType(SkullType::DRAGON()));
|
||||
$result->registerBlock("dragon_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::DRAGON()));
|
||||
$result->registerBlock("dried_kelp_block", fn() => Blocks::DRIED_KELP());
|
||||
$result->registerBlock("dyed_shulker_box", fn() => Blocks::DYED_SHULKER_BOX());
|
||||
$result->registerBlock("element_0", fn() => Blocks::ELEMENT_ZERO());
|
||||
@ -647,6 +660,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("glowingobsidian", fn() => Blocks::GLOWING_OBSIDIAN());
|
||||
$result->registerBlock("glowstone", fn() => Blocks::GLOWSTONE());
|
||||
$result->registerBlock("glowstone_block", fn() => Blocks::GLOWSTONE());
|
||||
$result->registerBlock("glow_lichen", fn() => Blocks::GLOW_LICHEN());
|
||||
$result->registerBlock("gold", fn() => Blocks::GOLD());
|
||||
$result->registerBlock("gold_block", fn() => Blocks::GOLD());
|
||||
$result->registerBlock("gold_ore", fn() => Blocks::GOLD_ORE());
|
||||
@ -842,9 +856,10 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("packed_mud", fn() => Blocks::PACKED_MUD());
|
||||
$result->registerBlock("peony", fn() => Blocks::PEONY());
|
||||
$result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP());
|
||||
$result->registerBlock("piglin_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PIGLIN()));
|
||||
$result->registerBlock("plank", fn() => Blocks::OAK_PLANKS());
|
||||
$result->registerBlock("planks", fn() => Blocks::OAK_PLANKS());
|
||||
$result->registerBlock("player_head", fn() => Blocks::MOB_HEAD()->setSkullType(SkullType::PLAYER()));
|
||||
$result->registerBlock("player_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PLAYER()));
|
||||
$result->registerBlock("podzol", fn() => Blocks::PODZOL());
|
||||
$result->registerBlock("polished_andesite", fn() => Blocks::POLISHED_ANDESITE());
|
||||
$result->registerBlock("polished_andesite_slab", fn() => Blocks::POLISHED_ANDESITE_SLAB());
|
||||
@ -950,8 +965,8 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("shulker_box", fn() => Blocks::SHULKER_BOX());
|
||||
$result->registerBlock("sign", fn() => Blocks::OAK_SIGN());
|
||||
$result->registerBlock("sign_post", fn() => Blocks::OAK_SIGN());
|
||||
$result->registerBlock("skeleton_skull", fn() => Blocks::MOB_HEAD()->setSkullType(SkullType::SKELETON()));
|
||||
$result->registerBlock("skull", fn() => Blocks::MOB_HEAD()->setSkullType(SkullType::SKELETON()));
|
||||
$result->registerBlock("skeleton_skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::SKELETON()));
|
||||
$result->registerBlock("skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::SKELETON()));
|
||||
$result->registerBlock("skull_block", fn() => Blocks::MOB_HEAD());
|
||||
$result->registerBlock("slab", fn() => Blocks::SMOOTH_STONE_SLAB());
|
||||
$result->registerBlock("slabs", fn() => Blocks::SMOOTH_STONE_SLAB());
|
||||
@ -1064,6 +1079,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("trunk2", fn() => Blocks::ACACIA_LOG()->setStripped(false));
|
||||
$result->registerBlock("tuff", fn() => Blocks::TUFF());
|
||||
$result->registerBlock("twisting_vines", fn() => Blocks::TWISTING_VINES());
|
||||
$result->registerBlock("underwater_tnt", fn() => Blocks::TNT()->setWorksUnderwater(true));
|
||||
$result->registerBlock("underwater_torch", fn() => Blocks::UNDERWATER_TORCH());
|
||||
$result->registerBlock("undyed_shulker_box", fn() => Blocks::SHULKER_BOX());
|
||||
$result->registerBlock("unlit_redstone_torch", fn() => Blocks::REDSTONE_TORCH());
|
||||
@ -1100,7 +1116,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("wheat_block", fn() => Blocks::WHEAT());
|
||||
$result->registerBlock("white_tulip", fn() => Blocks::WHITE_TULIP());
|
||||
$result->registerBlock("wither_rose", fn() => Blocks::WITHER_ROSE());
|
||||
$result->registerBlock("wither_skeleton_skull", fn() => Blocks::MOB_HEAD()->setSkullType(SkullType::WITHER_SKELETON()));
|
||||
$result->registerBlock("wither_skeleton_skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::WITHER_SKELETON()));
|
||||
$result->registerBlock("wood", fn() => Blocks::OAK_LOG()->setStripped(false));
|
||||
$result->registerBlock("wood2", fn() => Blocks::ACACIA_LOG()->setStripped(false));
|
||||
$result->registerBlock("wood_door_block", fn() => Blocks::OAK_DOOR());
|
||||
@ -1120,7 +1136,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("wool", fn() => Blocks::WOOL());
|
||||
$result->registerBlock("workbench", fn() => Blocks::CRAFTING_TABLE());
|
||||
$result->registerBlock("yellow_flower", fn() => Blocks::DANDELION());
|
||||
$result->registerBlock("zombie_head", fn() => Blocks::MOB_HEAD()->setSkullType(SkullType::ZOMBIE()));
|
||||
$result->registerBlock("zombie_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::ZOMBIE()));
|
||||
}
|
||||
|
||||
private static function registerDynamicItems(self $result) : void{
|
||||
|
@ -114,6 +114,7 @@ use pocketmine\world\World;
|
||||
* @method static Item CHEMICAL_SULPHATE()
|
||||
* @method static Item CHEMICAL_TUNGSTEN_CHLORIDE()
|
||||
* @method static Item CHEMICAL_WATER()
|
||||
* @method static ItemBlockWallOrFloor CHERRY_SIGN()
|
||||
* @method static ChorusFruit CHORUS_FRUIT()
|
||||
* @method static Item CLAY()
|
||||
* @method static Clock CLOCK()
|
||||
@ -362,6 +363,7 @@ final class VanillaItems{
|
||||
self::register("bucket", new Bucket(new IID(Ids::BUCKET), "Bucket"));
|
||||
self::register("carrot", new Carrot(new IID(Ids::CARROT), "Carrot"));
|
||||
self::register("charcoal", new Coal(new IID(Ids::CHARCOAL), "Charcoal"));
|
||||
self::register("cherry_sign", new ItemBlockWallOrFloor(new IID(Ids::CHERRY_SIGN), Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN()));
|
||||
self::register("chemical_aluminium_oxide", new Item(new IID(Ids::CHEMICAL_ALUMINIUM_OXIDE), "Aluminium Oxide"));
|
||||
self::register("chemical_ammonia", new Item(new IID(Ids::CHEMICAL_AMMONIA), "Ammonia"));
|
||||
self::register("chemical_barium_sulphate", new Item(new IID(Ids::CHEMICAL_BARIUM_SULPHATE), "Barium Sulphate"));
|
||||
|
@ -39,7 +39,6 @@ use pocketmine\world\format\io\FastChunkSerializer;
|
||||
|
||||
class ChunkRequestTask extends AsyncTask{
|
||||
private const TLS_KEY_PROMISE = "promise";
|
||||
private const TLS_KEY_ERROR_HOOK = "errorHook";
|
||||
|
||||
protected string $chunk;
|
||||
protected int $chunkX;
|
||||
@ -48,10 +47,7 @@ class ChunkRequestTask extends AsyncTask{
|
||||
protected NonThreadSafeValue $compressor;
|
||||
private string $tiles;
|
||||
|
||||
/**
|
||||
* @phpstan-param (\Closure() : void)|null $onError
|
||||
*/
|
||||
public function __construct(int $chunkX, int $chunkZ, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor, ?\Closure $onError = null){
|
||||
public function __construct(int $chunkX, int $chunkZ, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor){
|
||||
$this->compressor = new NonThreadSafeValue($compressor);
|
||||
|
||||
$this->chunk = FastChunkSerializer::serializeTerrain($chunk);
|
||||
@ -60,7 +56,6 @@ class ChunkRequestTask extends AsyncTask{
|
||||
$this->tiles = ChunkSerializer::serializeTiles($chunk);
|
||||
|
||||
$this->storeLocal(self::TLS_KEY_PROMISE, $promise);
|
||||
$this->storeLocal(self::TLS_KEY_ERROR_HOOK, $onError);
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
@ -75,17 +70,6 @@ class ChunkRequestTask extends AsyncTask{
|
||||
$this->setResult($this->compressor->deserialize()->compress($stream->getBuffer()));
|
||||
}
|
||||
|
||||
public function onError() : void{
|
||||
/**
|
||||
* @var \Closure|null $hook
|
||||
* @phpstan-var (\Closure() : void)|null $hook
|
||||
*/
|
||||
$hook = $this->fetchLocal(self::TLS_KEY_ERROR_HOOK);
|
||||
if($hook !== null){
|
||||
$hook();
|
||||
}
|
||||
}
|
||||
|
||||
public function onCompletion() : void{
|
||||
/** @var CompressBatchPromise $promise */
|
||||
$promise = $this->fetchLocal(self::TLS_KEY_PROMISE);
|
||||
|
@ -39,18 +39,16 @@ use pocketmine\inventory\CreativeInventory;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\cache\CreativeInventoryCache;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\CreativeContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
|
||||
@ -605,16 +603,7 @@ class InventoryManager{
|
||||
}
|
||||
|
||||
public function syncCreative() : void{
|
||||
$typeConverter = $this->session->getTypeConverter();
|
||||
|
||||
$entries = [];
|
||||
if(!$this->player->isSpectator()){
|
||||
//creative inventory may have holes if items were unregistered - ensure network IDs used are always consistent
|
||||
foreach(CreativeInventory::getInstance()->getAll() as $k => $item){
|
||||
$entries[] = new CreativeContentEntry($k, $typeConverter->coreItemStackToNet($item));
|
||||
}
|
||||
}
|
||||
$this->session->sendDataPacket(CreativeContentPacket::create($entries));
|
||||
$this->session->sendDataPacket(CreativeInventoryCache::getInstance()->getCache(CreativeInventory::getInstance()));
|
||||
}
|
||||
|
||||
private function newItemStackId() : int{
|
||||
|
@ -61,6 +61,7 @@ use const OPENSSL_ALGO_SHA384;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
final class JwtUtils{
|
||||
public const BEDROCK_SIGNING_KEY_CURVE_NAME = "secp384r1";
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
@ -203,6 +204,17 @@ final class JwtUtils{
|
||||
if($signingKeyOpenSSL === false){
|
||||
throw new JwtException("OpenSSL failed to parse key: " . openssl_error_string());
|
||||
}
|
||||
$details = openssl_pkey_get_details($signingKeyOpenSSL);
|
||||
if($details === false){
|
||||
throw new JwtException("OpenSSL failed to get details from key: " . openssl_error_string());
|
||||
}
|
||||
if(!isset($details['ec']['curve_name'])){
|
||||
throw new JwtException("Expected an EC key");
|
||||
}
|
||||
$curve = $details['ec']['curve_name'];
|
||||
if($curve !== self::BEDROCK_SIGNING_KEY_CURVE_NAME){
|
||||
throw new JwtException("Key must belong to curve " . self::BEDROCK_SIGNING_KEY_CURVE_NAME . ", got $curve");
|
||||
}
|
||||
return $signingKeyOpenSSL;
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ use pocketmine\network\mcpe\protocol\types\AbilitiesLayer;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandData;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandOverload;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
@ -809,7 +810,7 @@ class NetworkSession{
|
||||
|
||||
private function beginSpawnSequence() : void{
|
||||
$this->setHandler(new PreSpawnPacketHandler($this->server, $this->player, $this, $this->invManager));
|
||||
$this->player->setImmobile(); //TODO: HACK: fix client-side falling pre-spawn
|
||||
$this->player->setNoClientPredictions(); //TODO: HACK: fix client-side falling pre-spawn
|
||||
|
||||
$this->logger->debug("Waiting for chunk radius request");
|
||||
}
|
||||
@ -824,7 +825,7 @@ class NetworkSession{
|
||||
|
||||
private function onClientSpawnResponse() : void{
|
||||
$this->logger->debug("Received spawn response, entering in-game phase");
|
||||
$this->player->setImmobile(false); //TODO: HACK: we set this during the spawn sequence to prevent the client sending junk movements
|
||||
$this->player->setNoClientPredictions(false); //TODO: HACK: we set this during the spawn sequence to prevent the client sending junk movements
|
||||
$this->player->doFirstSpawn();
|
||||
$this->forceAsyncCompression = false;
|
||||
$this->setHandler(new InGamePacketHandler($this->player, $this, $this->invManager));
|
||||
@ -985,8 +986,9 @@ class NetworkSession{
|
||||
0,
|
||||
$aliasObj,
|
||||
[
|
||||
[CommandParameter::standard("args", AvailableCommandsPacket::ARG_TYPE_RAWTEXT, 0, true)]
|
||||
]
|
||||
new CommandOverload(chaining: false, parameters: [CommandParameter::standard("args", AvailableCommandsPacket::ARG_TYPE_RAWTEXT, 0, true)])
|
||||
],
|
||||
chainedSubCommandData: []
|
||||
);
|
||||
|
||||
$commandData[$command->getLabel()] = $data;
|
||||
|
@ -51,7 +51,7 @@ use const SORT_NUMERIC;
|
||||
final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
|
||||
|
||||
public function __construct(
|
||||
private StandardPacketBroadcaster $broadcaster,
|
||||
private PacketBroadcaster $broadcaster,
|
||||
private TypeConverter $typeConverter
|
||||
){}
|
||||
|
||||
@ -139,6 +139,6 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
|
||||
}
|
||||
|
||||
public function onEmote(array $recipients, Human $from, string $emoteId) : void{
|
||||
$this->sendDataPacket($recipients, EmotePacket::create($from->getId(), $emoteId, EmotePacket::FLAG_SERVER));
|
||||
$this->sendDataPacket($recipients, EmotePacket::create($from->getId(), $emoteId, "", "", EmotePacket::FLAG_SERVER));
|
||||
}
|
||||
}
|
||||
|
@ -34,13 +34,27 @@ use pocketmine\thread\NonThreadSafeValue;
|
||||
use function base64_decode;
|
||||
use function igbinary_serialize;
|
||||
use function igbinary_unserialize;
|
||||
use function openssl_error_string;
|
||||
use function time;
|
||||
|
||||
class ProcessLoginTask extends AsyncTask{
|
||||
private const TLS_KEY_ON_COMPLETION = "completion";
|
||||
|
||||
public const MOJANG_ROOT_PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V";
|
||||
/**
|
||||
* Old Mojang root auth key. This was used since the introduction of Xbox Live authentication in 0.15.0.
|
||||
* This key is expected to be replaced by the key below in the future, but this has not yet happened as of
|
||||
* 2023-07-01.
|
||||
* Ideally we would place a time expiry on this key, but since Mojang have not given a hard date for the key change,
|
||||
* and one bad guess has already caused a major outage, we can't do this.
|
||||
* TODO: This needs to be removed as soon as the new key is deployed by Mojang's authentication servers.
|
||||
*/
|
||||
public const MOJANG_OLD_ROOT_PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V";
|
||||
|
||||
/**
|
||||
* New Mojang root auth key. Mojang notified third-party developers of this change prior to the release of 1.20.0.
|
||||
* Expectations were that this would be used starting a "couple of weeks" after the release, but as of 2023-07-01,
|
||||
* it has not yet been deployed.
|
||||
*/
|
||||
public const MOJANG_ROOT_PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAECRXueJeTDqNRRgJi/vlRufByu/2G0i2Ebt6YMar5QX/R0DIIyrJMcUpruK4QveTfJSTp3Shlq4Gk34cD/4GUWwkv0DVuzeuB+tXija7HBxii03NHDbPAD0AKnLr2wdAp";
|
||||
|
||||
private const CLOCK_DRIFT_MAX = 60;
|
||||
|
||||
@ -149,7 +163,8 @@ class ProcessLoginTask extends AsyncTask{
|
||||
try{
|
||||
$signingKeyOpenSSL = JwtUtils::parseDerPublicKey($headerDerKey);
|
||||
}catch(JwtException $e){
|
||||
throw new VerifyLoginException("Invalid JWT public key: " . openssl_error_string());
|
||||
//TODO: we shouldn't be showing this internal information to the client
|
||||
throw new VerifyLoginException("Invalid JWT public key: " . $e->getMessage(), null, 0, $e);
|
||||
}
|
||||
try{
|
||||
if(!JwtUtils::verify($jwt, $signingKeyOpenSSL)){
|
||||
@ -159,7 +174,7 @@ class ProcessLoginTask extends AsyncTask{
|
||||
throw new VerifyLoginException($e->getMessage(), null, 0, $e);
|
||||
}
|
||||
|
||||
if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY){
|
||||
if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY || $headers->x5u === self::MOJANG_OLD_ROOT_PUBLIC_KEY){
|
||||
$this->authenticated = true; //we're signed into xbox live
|
||||
}
|
||||
|
||||
@ -189,6 +204,12 @@ class ProcessLoginTask extends AsyncTask{
|
||||
if($identityPublicKey === false){
|
||||
throw new VerifyLoginException("Invalid identityPublicKey: base64 error decoding");
|
||||
}
|
||||
try{
|
||||
//verify key format and parameters
|
||||
JwtUtils::parseDerPublicKey($identityPublicKey);
|
||||
}catch(JwtException $e){
|
||||
throw new VerifyLoginException("Invalid identityPublicKey: " . $e->getMessage(), null, 0, $e);
|
||||
}
|
||||
$currentPublicKey = $identityPublicKey; //if there are further links, the next link should be signed with this
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user