mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-11 12:08:05 +00:00
Compare commits
111 Commits
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 | |||
833f9401f9 | |||
57cbc25080 | |||
b9bdfe580b | |||
6d7f44d8fe |
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
|
||||
|
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
|
||||
|
@ -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)
|
||||
|
@ -139,7 +139,7 @@ function generateBlockStateNames(BlockPaletteReport $data) : void{
|
||||
|
||||
fwrite($output, generateClassHeader(BlockStateNames::class));
|
||||
foreach(Utils::stringifyKeys($data->seenStateValues) as $state => $values){
|
||||
$constName = mb_strtoupper(preg_replace("/^minecraft:/", "", $state) ?? throw new AssumptionFailedError("This regex is not invalid"), '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");
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ function generateBlockStringValues(BlockPaletteReport $data) : void{
|
||||
continue;
|
||||
}
|
||||
$anyWritten = true;
|
||||
$constName = mb_strtoupper(preg_replace("/^minecraft:/", "", $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";
|
||||
|
Submodule build/php updated: fcbc15f23e...e0c918d137
@ -27,8 +27,32 @@ If you're upgrading from 4.20.x directly to 4.22.x, please also read the followi
|
||||
Released 9th June 2023.
|
||||
|
||||
## Fixes
|
||||
- Reokaced workaround for an old teleporting client bug:
|
||||
- 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.
|
@ -28,3 +28,25 @@ Released 9th June 2023.
|
||||
- [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-pmmpthread": "^6.0.1",
|
||||
"ext-pmmpthread": "^6.0.4",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
@ -31,13 +31,13 @@
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"fgrosse/phpasn1": "^2.3",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.2.999",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~2.2.0+bedrock-1.20.0",
|
||||
"pocketmine/bedrock-data": "~2.3.0+bedrock-1.20.0",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.3.0+bedrock-1.20.0",
|
||||
"pocketmine/bedrock-protocol": "~22.0.0+bedrock-1.20.0",
|
||||
"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/color": "^0.3.0",
|
||||
@ -49,8 +49,8 @@
|
||||
"pocketmine/raklib": "^0.15.0",
|
||||
"pocketmine/raklib-ipc": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "^4.1",
|
||||
"symfony/filesystem": "^6.2"
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"symfony/filesystem": "~6.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.16",
|
||||
|
100
composer.lock
generated
100
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5483e35c09044ab9d989cec8fdf4a399",
|
||||
"content-hash": "ee46ec27f8dfc8c767527b7776fe9992",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -198,16 +198,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "2.2.0",
|
||||
"version": "3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||
"reference": "79bb3ad542ef19e828fdf1fa6adc54f1fa4b3bb5"
|
||||
"reference": "6d4ae416043337946a22fc31e8065ca2c21f472d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/79bb3ad542ef19e828fdf1fa6adc54f1fa4b3bb5",
|
||||
"reference": "79bb3ad542ef19e828fdf1fa6adc54f1fa4b3bb5",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/6d4ae416043337946a22fc31e8065ca2c21f472d",
|
||||
"reference": "6d4ae416043337946a22fc31e8065ca2c21f472d",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -218,22 +218,22 @@
|
||||
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/2.2.0"
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.1.0"
|
||||
},
|
||||
"time": "2023-05-04T21:49:36+00:00"
|
||||
"time": "2023-07-12T12:05:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "2.3.0+bedrock-1.20.0",
|
||||
"version": "2.4.0+bedrock-1.20.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "b3dd3f4b8e3b6759c5d84de6ec85bb20b668c3a9"
|
||||
"reference": "f98bd1cae46d2920058acf3b23c0bedeac79f4ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/b3dd3f4b8e3b6759c5d84de6ec85bb20b668c3a9",
|
||||
"reference": "b3dd3f4b8e3b6759c5d84de6ec85bb20b668c3a9",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/f98bd1cae46d2920058acf3b23c0bedeac79f4ab",
|
||||
"reference": "f98bd1cae46d2920058acf3b23c0bedeac79f4ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -244,22 +244,22 @@
|
||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.0"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.10"
|
||||
},
|
||||
"time": "2023-06-07T19:06:47+00:00"
|
||||
"time": "2023-07-12T11:51:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||
"reference": "b16c59cfae08833f180dd82f88de7c1f43bc67c9"
|
||||
"reference": "60d199afe5e371fd189b21d685ec1fed6ba54230"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/b16c59cfae08833f180dd82f88de7c1f43bc67c9",
|
||||
"reference": "b16c59cfae08833f180dd82f88de7c1f43bc67c9",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/60d199afe5e371fd189b21d685ec1fed6ba54230",
|
||||
"reference": "60d199afe5e371fd189b21d685ec1fed6ba54230",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -270,22 +270,22 @@
|
||||
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.3.0"
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.4.0"
|
||||
},
|
||||
"time": "2023-05-18T15:34:32+00:00"
|
||||
"time": "2023-07-12T12:08:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "22.0.0+bedrock-1.20.0",
|
||||
"version": "23.0.1+bedrock-1.20.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "ceff28a0bd5d248f37fb97be3e836d536e37526e"
|
||||
"reference": "db48400736799cc3833a2644a02e308992a98fa8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/ceff28a0bd5d248f37fb97be3e836d536e37526e",
|
||||
"reference": "ceff28a0bd5d248f37fb97be3e836d536e37526e",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/db48400736799cc3833a2644a02e308992a98fa8",
|
||||
"reference": "db48400736799cc3833a2644a02e308992a98fa8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -317,9 +317,9 @@
|
||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/22.0.0+bedrock-1.20.0"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.1+bedrock-1.20.10"
|
||||
},
|
||||
"time": "2023-06-07T19:22:05+00:00"
|
||||
"time": "2023-07-18T21:07:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -639,16 +639,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/netresearch-jsonmapper",
|
||||
"version": "v4.2.999",
|
||||
"version": "v4.2.1000",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/netresearch-jsonmapper.git",
|
||||
"reference": "f700806dec756ed825a8200dc2950ead98265956"
|
||||
"reference": "078764e869e9b732f97206ec9363480a77c35532"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/f700806dec756ed825a8200dc2950ead98265956",
|
||||
"reference": "f700806dec756ed825a8200dc2950ead98265956",
|
||||
"url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/078764e869e9b732f97206ec9363480a77c35532",
|
||||
"reference": "078764e869e9b732f97206ec9363480a77c35532",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -687,9 +687,9 @@
|
||||
"support": {
|
||||
"email": "cweiske@cweiske.de",
|
||||
"issues": "https://github.com/cweiske/jsonmapper/issues",
|
||||
"source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.2.999"
|
||||
"source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.2.1000"
|
||||
},
|
||||
"time": "2023-06-01T13:43:01+00:00"
|
||||
"time": "2023-07-14T10:44:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
@ -998,16 +998,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v6.2.10",
|
||||
"version": "v6.2.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
|
||||
"reference": "b0818e7203e53540f2a5c9a5017d97897df1e9bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
|
||||
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b0818e7203e53540f2a5c9a5017d97897df1e9bb",
|
||||
"reference": "b0818e7203e53540f2a5c9a5017d97897df1e9bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1041,7 +1041,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.2.10"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.2.12"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1057,7 +1057,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-18T13:46:08+00:00"
|
||||
"time": "2023-06-01T08:29:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@ -1287,16 +1287,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.15.5",
|
||||
"version": "v4.16.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e"
|
||||
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e",
|
||||
"reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
|
||||
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1337,9 +1337,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
|
||||
},
|
||||
"time": "2023-05-19T20:20:00+00:00"
|
||||
"time": "2023-06-25T14:52:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -1937,16 +1937,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "10.2.1",
|
||||
"version": "10.2.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "599b33294350e8f51163119d5670512f98b0490d"
|
||||
"reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/599b33294350e8f51163119d5670512f98b0490d",
|
||||
"reference": "599b33294350e8f51163119d5670512f98b0490d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c17815c129f133f3019cc18e8d0c8622e6d9bcd",
|
||||
"reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2018,7 +2018,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.1"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2034,7 +2034,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-06-05T05:15:51+00:00"
|
||||
"time": "2023-07-17T12:08:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@ -3020,7 +3020,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-phar": "*",
|
||||
"ext-pmmpthread": "^6.0.1",
|
||||
"ext-pmmpthread": "^6.0.4",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
|
@ -120,8 +120,8 @@ namespace pocketmine {
|
||||
}
|
||||
|
||||
if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
|
||||
if(version_compare($pmmpthread_version, "6.0.1") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
|
||||
$messages[] = "pmmpthread ^6.0.1 is required, while you have $pmmpthread_version.";
|
||||
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.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.1.2";
|
||||
public const BASE_VERSION = "5.3.2";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
|
@ -467,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;
|
||||
|
@ -717,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){
|
||||
|
@ -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()
|
||||
@ -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);
|
||||
|
@ -58,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")
|
||||
});
|
||||
}
|
||||
@ -73,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")
|
||||
});
|
||||
}
|
||||
@ -88,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")
|
||||
});
|
||||
}
|
||||
@ -103,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")
|
||||
});
|
||||
}
|
||||
@ -118,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")
|
||||
});
|
||||
}
|
||||
@ -133,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")
|
||||
});
|
||||
}
|
||||
@ -209,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");
|
||||
}
|
||||
@ -225,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")
|
||||
});
|
||||
}
|
||||
@ -240,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")
|
||||
});
|
||||
}
|
||||
@ -255,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")
|
||||
});
|
||||
}
|
||||
@ -270,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")
|
||||
});
|
||||
}
|
||||
@ -285,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")
|
||||
});
|
||||
}
|
||||
@ -300,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")
|
||||
});
|
||||
}
|
||||
|
@ -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")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\utils\EnumTrait;
|
||||
*
|
||||
* @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()
|
||||
@ -50,7 +51,8 @@ final class MobHeadType{
|
||||
new MobHeadType("zombie", "Zombie Head"),
|
||||
new MobHeadType("player", "Player Head"),
|
||||
new MobHeadType("creeper", "Creeper Head"),
|
||||
new MobHeadType("dragon", "Dragon Head")
|
||||
new MobHeadType("dragon", "Dragon Head"),
|
||||
new MobHeadType("piglin", "Piglin Head")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,7 @@ final class MobHeadTypeIdMap{
|
||||
$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{
|
||||
|
@ -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;
|
||||
|
@ -42,8 +42,8 @@ final class BlockStateData{
|
||||
public const CURRENT_VERSION =
|
||||
(1 << 24) | //major
|
||||
(20 << 16) | //minor
|
||||
(0 << 8) | //patch
|
||||
(33); //revision
|
||||
(10 << 8) | //patch
|
||||
(32); //revision
|
||||
|
||||
public const TAG_NAME = "name";
|
||||
public const TAG_STATES = "states";
|
||||
@ -111,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);
|
||||
@ -119,7 +122,15 @@ final class BlockStateData{
|
||||
return CompoundTag::create()
|
||||
->setString(self::TAG_NAME, $this->name)
|
||||
->setInt(self::TAG_VERSION, $this->version)
|
||||
->setTag(self::TAG_STATES, $statesTag)
|
||||
->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);
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,8 @@ final class BlockStateNames{
|
||||
public const LEVER_DIRECTION = "lever_direction";
|
||||
public const LIQUID_DEPTH = "liquid_depth";
|
||||
public const LIT = "lit";
|
||||
public const CARDINAL_DIRECTION = "minecraft:cardinal_direction";
|
||||
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";
|
||||
|
@ -131,10 +131,17 @@ final class BlockStateStringValues{
|
||||
public const LEVER_DIRECTION_UP_NORTH_SOUTH = "up_north_south";
|
||||
public const LEVER_DIRECTION_WEST = "west";
|
||||
|
||||
public const CARDINAL_DIRECTION_EAST = "east";
|
||||
public const CARDINAL_DIRECTION_NORTH = "north";
|
||||
public const CARDINAL_DIRECTION_SOUTH = "south";
|
||||
public const CARDINAL_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";
|
||||
|
@ -98,7 +98,9 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -109,8 +111,10 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -122,9 +126,11 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -188,7 +194,6 @@ 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";
|
||||
@ -229,7 +234,9 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -455,12 +462,16 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -513,18 +524,24 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -538,7 +555,9 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -600,7 +619,9 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -613,8 +634,10 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -657,7 +680,9 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -672,6 +697,7 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -680,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";
|
||||
@ -705,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";
|
||||
@ -856,7 +882,9 @@ final class BlockTypeNames{
|
||||
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";
|
||||
@ -867,7 +895,9 @@ final class BlockTypeNames{
|
||||
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;
|
||||
@ -192,7 +193,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->registerFlatColorBlockSerializers();
|
||||
$this->registerFlatCoralSerializers();
|
||||
$this->registerCauldronSerializers();
|
||||
$this->registerWoodBlockSerializers();
|
||||
$this->registerFlatWoodBlockSerializers();
|
||||
$this->registerLegacyWoodBlockSerializers();
|
||||
$this->registerLeavesSerializers();
|
||||
$this->registerSimpleSerializers();
|
||||
$this->registerSerializers();
|
||||
}
|
||||
@ -367,6 +370,46 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
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{
|
||||
@ -390,20 +433,206 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$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{
|
||||
@ -436,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);
|
||||
@ -607,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);
|
||||
@ -655,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())
|
||||
@ -698,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)
|
||||
@ -749,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)));
|
||||
@ -910,10 +1106,6 @@ 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());
|
||||
@ -934,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));
|
||||
@ -1004,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)
|
||||
@ -1040,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,
|
||||
@ -1055,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));
|
||||
@ -1086,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)
|
||||
@ -1149,28 +1303,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
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(MobHead $block) : Writer{
|
||||
@ -1206,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)));
|
||||
@ -1367,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());
|
||||
@ -1477,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();
|
||||
@ -190,12 +214,12 @@ final class BlockStateReader{
|
||||
* @throws BlockStateDeserializeException
|
||||
*/
|
||||
public function readCardinalHorizontalFacing() : int{
|
||||
return match($raw = $this->readString(BlockStateNames::CARDINAL_DIRECTION)){
|
||||
StringValues::CARDINAL_DIRECTION_NORTH => Facing::NORTH,
|
||||
StringValues::CARDINAL_DIRECTION_SOUTH => Facing::SOUTH,
|
||||
StringValues::CARDINAL_DIRECTION_WEST => Facing::WEST,
|
||||
StringValues::CARDINAL_DIRECTION_EAST => Facing::EAST,
|
||||
default => throw $this->badValueException(BlockStateNames::CARDINAL_DIRECTION, $raw)
|
||||
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)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->registerFlatColorBlockDeserializers();
|
||||
$this->registerFlatCoralDeserializers();
|
||||
$this->registerCauldronDeserializers();
|
||||
$this->registerWoodBlockDeserializers();
|
||||
$this->registerFlatWoodBlockDeserializers();
|
||||
$this->registerLegacyWoodBlockDeserializers();
|
||||
$this->registerLeavesDeserializers();
|
||||
$this->registerSimpleDeserializers();
|
||||
$this->registerDeserializers();
|
||||
}
|
||||
@ -242,6 +244,48 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
] 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{
|
||||
@ -283,20 +327,191 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->map(Ids::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{
|
||||
@ -328,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());
|
||||
@ -496,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());
|
||||
@ -545,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());
|
||||
@ -555,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))
|
||||
@ -580,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)){
|
||||
@ -627,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));
|
||||
@ -726,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());
|
||||
@ -756,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)
|
||||
@ -839,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));
|
||||
@ -860,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()
|
||||
@ -887,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());
|
||||
@ -904,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())
|
||||
@ -977,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)){
|
||||
@ -1019,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()
|
||||
@ -1079,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::CARDINAL_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));
|
||||
@ -1202,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());
|
||||
@ -1241,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());
|
||||
@ -1265,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(),
|
||||
@ -1300,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);
|
||||
@ -1328,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());
|
||||
@ -1375,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()));
|
||||
@ -1413,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
|
||||
@ -146,11 +170,11 @@ final class BlockStateWriter{
|
||||
* @return $this
|
||||
*/
|
||||
public function writeCardinalHorizontalFacing(int $value) : self{
|
||||
return $this->writeString(BlockStateNames::CARDINAL_DIRECTION, match($value){
|
||||
Facing::SOUTH => StringValues::CARDINAL_DIRECTION_SOUTH,
|
||||
Facing::WEST => StringValues::CARDINAL_DIRECTION_WEST,
|
||||
Facing::NORTH => StringValues::CARDINAL_DIRECTION_NORTH,
|
||||
Facing::EAST => StringValues::CARDINAL_DIRECTION_EAST,
|
||||
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")
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -177,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);
|
||||
|
@ -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());
|
||||
|
@ -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";
|
||||
|
@ -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,6 +56,10 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{
|
||||
$this->addBits(2);
|
||||
}
|
||||
|
||||
public function facingFlags(array &$faces) : void{
|
||||
$this->addBits(count(Facing::ALL));
|
||||
}
|
||||
|
||||
public function horizontalFacingFlags(array &$faces) : void{
|
||||
$this->addBits(count(Facing::HORIZONTAL));
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -130,10 +130,11 @@ trait RuntimeEnumDeserializerTrait{
|
||||
$value = match($this->readInt(3)){
|
||||
0 => \pocketmine\block\utils\MobHeadType::CREEPER(),
|
||||
1 => \pocketmine\block\utils\MobHeadType::DRAGON(),
|
||||
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(),
|
||||
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")
|
||||
};
|
||||
}
|
||||
|
@ -130,10 +130,11 @@ trait RuntimeEnumSerializerTrait{
|
||||
$this->writeInt(3, match($value){
|
||||
\pocketmine\block\utils\MobHeadType::CREEPER() => 0,
|
||||
\pocketmine\block\utils\MobHeadType::DRAGON() => 1,
|
||||
\pocketmine\block\utils\MobHeadType::PLAYER() => 2,
|
||||
\pocketmine\block\utils\MobHeadType::SKELETON() => 3,
|
||||
\pocketmine\block\utils\MobHeadType::WITHER_SKELETON() => 4,
|
||||
\pocketmine\block\utils\MobHeadType::ZOMBIE() => 5,
|
||||
\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")
|
||||
});
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
@ -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,6 +856,7 @@ 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()->setMobHeadType(MobHeadType::PLAYER()));
|
||||
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
@ -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
|
||||
){}
|
||||
|
||||
|
@ -34,15 +34,26 @@ 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";
|
||||
|
||||
/**
|
||||
* 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";
|
||||
public const MOJANG_OLD_KEY_EXPIRY = 1688169600; //2023-07-01 00:00:00 UTC - there is no official date for the changeover to the new key, so this is a guess
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@ -152,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)){
|
||||
@ -162,7 +174,7 @@ class ProcessLoginTask extends AsyncTask{
|
||||
throw new VerifyLoginException($e->getMessage(), null, 0, $e);
|
||||
}
|
||||
|
||||
if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY || (time() < self::MOJANG_OLD_KEY_EXPIRY && $headers->x5u === self::MOJANG_OLD_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
|
||||
}
|
||||
|
||||
@ -192,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
|
||||
}
|
||||
}
|
||||
|
9
src/network/mcpe/cache/ChunkCache.php
vendored
9
src/network/mcpe/cache/ChunkCache.php
vendored
@ -118,14 +118,7 @@ class ChunkCache implements ChunkListener{
|
||||
$chunkZ,
|
||||
$chunk,
|
||||
$this->caches[$chunkHash],
|
||||
$this->compressor,
|
||||
function() use ($chunkHash, $chunkX, $chunkZ) : void{
|
||||
$this->world->getLogger()->error("Failed preparing chunk $chunkX $chunkZ, retrying");
|
||||
|
||||
if(isset($this->caches[$chunkHash])){
|
||||
$this->restartPendingRequest($chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
$this->compressor
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\convert;
|
||||
|
||||
use pocketmine\data\bedrock\item\BlockItemIdMap;
|
||||
use pocketmine\data\bedrock\item\ItemDeserializer;
|
||||
use pocketmine\data\bedrock\item\ItemSerializer;
|
||||
use pocketmine\data\bedrock\item\ItemTypeDeserializeException;
|
||||
@ -37,18 +38,19 @@ use pocketmine\utils\AssumptionFailedError;
|
||||
* This class handles translation between network item ID+metadata to PocketMine-MP internal ID+metadata and vice versa.
|
||||
*/
|
||||
final class ItemTranslator{
|
||||
public const NO_BLOCK_RUNTIME_ID = 0;
|
||||
public const NO_BLOCK_RUNTIME_ID = 0; //this is technically a valid block runtime ID, but is used to represent "no block" (derp mojang)
|
||||
|
||||
public function __construct(
|
||||
private ItemTypeDictionary $itemTypeDictionary,
|
||||
private BlockStateDictionary $blockStateDictionary,
|
||||
private ItemSerializer $itemSerializer,
|
||||
private ItemDeserializer $itemDeserializer
|
||||
private ItemDeserializer $itemDeserializer,
|
||||
private BlockItemIdMap $blockItemIdMap
|
||||
){}
|
||||
|
||||
/**
|
||||
* @return int[]|null
|
||||
* @phpstan-return array{int, int, int}|null
|
||||
* @phpstan-return array{int, int, ?int}|null
|
||||
*/
|
||||
public function toNetworkIdQuiet(Item $item) : ?array{
|
||||
try{
|
||||
@ -60,7 +62,7 @@ final class ItemTranslator{
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int, int}
|
||||
* @phpstan-return array{int, int, ?int}
|
||||
*
|
||||
* @throws ItemTypeSerializeException
|
||||
*/
|
||||
@ -78,7 +80,7 @@ final class ItemTranslator{
|
||||
throw new AssumptionFailedError("Unmapped blockstate returned by blockstate serializer: " . $blockStateData->toNbt());
|
||||
}
|
||||
}else{
|
||||
$blockRuntimeId = self::NO_BLOCK_RUNTIME_ID; //this is technically a valid block runtime ID, but is used to represent "no block" (derp mojang)
|
||||
$blockRuntimeId = null;
|
||||
}
|
||||
|
||||
return [$numericId, $itemData->getMeta(), $blockRuntimeId];
|
||||
@ -106,11 +108,13 @@ final class ItemTranslator{
|
||||
}
|
||||
|
||||
$blockStateData = null;
|
||||
if($networkBlockRuntimeId !== self::NO_BLOCK_RUNTIME_ID){
|
||||
if($this->blockItemIdMap->lookupBlockId($stringId) !== null){
|
||||
$blockStateData = $this->blockStateDictionary->generateDataFromStateId($networkBlockRuntimeId);
|
||||
if($blockStateData === null){
|
||||
throw new TypeConversionException("Blockstate runtimeID $networkBlockRuntimeId does not correspond to any known blockstate");
|
||||
}
|
||||
}elseif($networkBlockRuntimeId !== self::NO_BLOCK_RUNTIME_ID){
|
||||
throw new TypeConversionException("Item $stringId is not a blockitem, but runtime ID $networkBlockRuntimeId was provided");
|
||||
}
|
||||
|
||||
try{
|
||||
|
@ -82,7 +82,8 @@ class TypeConverter{
|
||||
$this->itemTypeDictionary,
|
||||
$this->blockTranslator->getBlockStateDictionary(),
|
||||
GlobalItemDataHandlers::getSerializer(),
|
||||
GlobalItemDataHandlers::getDeserializer()
|
||||
GlobalItemDataHandlers::getDeserializer(),
|
||||
$this->blockItemIdMap
|
||||
);
|
||||
|
||||
$this->skinAdapter = new LegacySkinAdapter();
|
||||
@ -147,7 +148,7 @@ class TypeConverter{
|
||||
}elseif($ingredient instanceof ExactRecipeIngredient){
|
||||
$item = $ingredient->getItem();
|
||||
[$id, $meta, $blockRuntimeId] = $this->itemTranslator->toNetworkId($item);
|
||||
if($blockRuntimeId !== ItemTranslator::NO_BLOCK_RUNTIME_ID){
|
||||
if($blockRuntimeId !== null){
|
||||
$meta = $this->blockTranslator->getBlockStateDictionary()->getMetaFromStateId($blockRuntimeId);
|
||||
if($meta === null){
|
||||
throw new AssumptionFailedError("Every block state should have an associated meta value");
|
||||
@ -230,7 +231,7 @@ class TypeConverter{
|
||||
$id,
|
||||
$meta,
|
||||
$itemStack->getCount(),
|
||||
$blockRuntimeId,
|
||||
$blockRuntimeId ?? ItemTranslator::NO_BLOCK_RUNTIME_ID,
|
||||
$nbt,
|
||||
[],
|
||||
[],
|
||||
|
@ -33,6 +33,7 @@ use function hex2bin;
|
||||
use function openssl_digest;
|
||||
use function openssl_error_string;
|
||||
use function openssl_pkey_derive;
|
||||
use function openssl_pkey_get_details;
|
||||
use function str_pad;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
@ -42,7 +43,20 @@ final class EncryptionUtils{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
private static function validateKey(\OpenSSLAsymmetricKey $key) : void{
|
||||
$keyDetails = Utils::assumeNotFalse(openssl_pkey_get_details($key));
|
||||
if(!isset($keyDetails["ec"]["curve_name"])){
|
||||
throw new \InvalidArgumentException("Key must be an EC key");
|
||||
}
|
||||
$curveName = $keyDetails["ec"]["curve_name"];
|
||||
if($curveName !== JwtUtils::BEDROCK_SIGNING_KEY_CURVE_NAME){
|
||||
throw new \InvalidArgumentException("Key must belong to the " . JwtUtils::BEDROCK_SIGNING_KEY_CURVE_NAME . " elliptic curve, got $curveName");
|
||||
}
|
||||
}
|
||||
|
||||
public static function generateSharedSecret(\OpenSSLAsymmetricKey $localPriv, \OpenSSLAsymmetricKey $remotePub) : \GMP{
|
||||
self::validateKey($localPriv);
|
||||
self::validateKey($remotePub);
|
||||
$hexSecret = openssl_pkey_derive($remotePub, $localPriv, 48);
|
||||
if($hexSecret === false){
|
||||
throw new \InvalidArgumentException("Failed to derive shared secret: " . openssl_error_string());
|
||||
|
@ -111,9 +111,9 @@ use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use function array_push;
|
||||
use function base64_encode;
|
||||
use function count;
|
||||
use function fmod;
|
||||
use function get_debug_type;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_bool;
|
||||
@ -743,27 +743,32 @@ class InGamePacketHandler extends PacketHandler{
|
||||
if(!($nbt instanceof CompoundTag)) throw new AssumptionFailedError("PHPStan should ensure this is a CompoundTag"); //for phpstorm's benefit
|
||||
|
||||
if($block instanceof BaseSign){
|
||||
if(($textBlobTag = $nbt->getCompoundTag(Sign::TAG_FRONT_TEXT)?->getTag(Sign::TAG_TEXT_BLOB)) instanceof StringTag){
|
||||
try{
|
||||
$text = SignText::fromBlob($textBlobTag->getValue());
|
||||
}catch(\InvalidArgumentException $e){
|
||||
throw PacketHandlingException::wrap($e, "Invalid sign text update");
|
||||
}
|
||||
|
||||
try{
|
||||
if(!$block->updateText($this->player, $text)){
|
||||
foreach($this->player->getWorld()->createBlockUpdatePackets([$pos]) as $updatePacket){
|
||||
$this->session->sendDataPacket($updatePacket);
|
||||
}
|
||||
}
|
||||
}catch(\UnexpectedValueException $e){
|
||||
throw PacketHandlingException::wrap($e);
|
||||
}
|
||||
|
||||
return true;
|
||||
$frontTextTag = $nbt->getTag(Sign::TAG_FRONT_TEXT);
|
||||
if(!$frontTextTag instanceof CompoundTag){
|
||||
throw new PacketHandlingException("Invalid tag type " . get_debug_type($frontTextTag) . " for tag \"" . Sign::TAG_FRONT_TEXT . "\" in sign update data");
|
||||
}
|
||||
$textBlobTag = $frontTextTag->getTag(Sign::TAG_TEXT_BLOB);
|
||||
if(!$textBlobTag instanceof StringTag){
|
||||
throw new PacketHandlingException("Invalid tag type " . get_debug_type($textBlobTag) . " for tag \"" . Sign::TAG_TEXT_BLOB . "\" in sign update data");
|
||||
}
|
||||
|
||||
$this->session->getLogger()->debug("Invalid sign update data: " . base64_encode($packet->nbt->getEncodedNbt()));
|
||||
try{
|
||||
$text = SignText::fromBlob($textBlobTag->getValue());
|
||||
}catch(\InvalidArgumentException $e){
|
||||
throw PacketHandlingException::wrap($e, "Invalid sign text update");
|
||||
}
|
||||
|
||||
try{
|
||||
if(!$block->updateText($this->player, $text)){
|
||||
foreach($this->player->getWorld()->createBlockUpdatePackets([$pos]) as $updatePacket){
|
||||
$this->session->sendDataPacket($updatePacket);
|
||||
}
|
||||
}
|
||||
}catch(\UnexpectedValueException $e){
|
||||
throw PacketHandlingException::wrap($e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1129,7 +1129,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the gamemode, and if needed, kicks the Player.
|
||||
* Sets the provided gamemode.
|
||||
*/
|
||||
public function setGamemode(GameMode $gm) : bool{
|
||||
if($this->gamemode->equals($gm)){
|
||||
|
@ -24,15 +24,17 @@ declare(strict_types=1);
|
||||
namespace pocketmine\scheduler;
|
||||
|
||||
use pmmp\thread\Thread as NativeThread;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\thread\log\ThreadSafeLogger;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function spl_object_id;
|
||||
use function time;
|
||||
use const PHP_INT_MAX;
|
||||
@ -130,6 +132,8 @@ class AsyncPool{
|
||||
foreach($this->workerStartHooks as $hook){
|
||||
$hook($workerId);
|
||||
}
|
||||
}else{
|
||||
$this->checkCrashedWorker($workerId, null);
|
||||
}
|
||||
|
||||
return $this->workers[$workerId];
|
||||
@ -146,7 +150,6 @@ class AsyncPool{
|
||||
throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once");
|
||||
}
|
||||
|
||||
$task->progressUpdates = new ThreadSafeArray();
|
||||
$task->setSubmitted();
|
||||
|
||||
$this->getWorker($worker)->submit($task);
|
||||
@ -199,6 +202,28 @@ class AsyncPool{
|
||||
return $worker;
|
||||
}
|
||||
|
||||
private function checkCrashedWorker(int $workerId, ?AsyncTask $crashedTask) : void{
|
||||
$entry = $this->workers[$workerId];
|
||||
if($entry->worker->isTerminated()){
|
||||
if($crashedTask === null){
|
||||
foreach($entry->tasks as $task){
|
||||
if($task->isTerminated()){
|
||||
$crashedTask = $task;
|
||||
break;
|
||||
}elseif(!$task->isFinished()){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($crashedTask !== null){
|
||||
$message = "Worker $workerId crashed while running task " . get_class($crashedTask) . "#" . spl_object_id($crashedTask);
|
||||
}else{
|
||||
$message = "Worker $workerId crashed for unknown reason";
|
||||
}
|
||||
throw new \RuntimeException($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects finished and/or crashed tasks from the workers, firing their on-completion hooks where appropriate.
|
||||
*
|
||||
@ -231,9 +256,9 @@ class AsyncPool{
|
||||
if($task->isFinished()){ //make sure the task actually executed before trying to collect
|
||||
$queue->dequeue();
|
||||
|
||||
if($task->isCrashed()){
|
||||
$this->logger->critical("Could not execute asynchronous task " . (new \ReflectionClass($task))->getShortName() . ": Task crashed");
|
||||
$task->onError();
|
||||
if($task->isTerminated()){
|
||||
$this->checkCrashedWorker($worker, $task);
|
||||
throw new AssumptionFailedError("checkCrashedWorker() should have thrown an exception, making this unreachable");
|
||||
}elseif(!$task->hasCancelledRun()){
|
||||
/*
|
||||
* It's possible for a task to submit a progress update and then finish before the progress
|
||||
@ -244,11 +269,13 @@ class AsyncPool{
|
||||
* lost. Thus, it's necessary to do one last check here to make sure all progress updates have
|
||||
* been consumed before completing.
|
||||
*/
|
||||
$task->checkProgressUpdates();
|
||||
$task->onCompletion();
|
||||
$this->checkTaskProgressUpdates($task);
|
||||
Timings::getAsyncTaskCompletionTimings($task)->time(function() use ($task) : void{
|
||||
$task->onCompletion();
|
||||
});
|
||||
}
|
||||
}else{
|
||||
$task->checkProgressUpdates();
|
||||
$this->checkTaskProgressUpdates($task);
|
||||
$more = true;
|
||||
break; //current task is still running, skip to next worker
|
||||
}
|
||||
@ -296,4 +323,10 @@ class AsyncPool{
|
||||
}
|
||||
$this->workers = [];
|
||||
}
|
||||
|
||||
private function checkTaskProgressUpdates(AsyncTask $task) : void{
|
||||
Timings::getAsyncTaskProgressUpdateTimings($task)->time(function() use ($task) : void{
|
||||
$task->checkProgressUpdates();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -61,34 +61,27 @@ use function spl_object_id;
|
||||
*/
|
||||
abstract class AsyncTask extends Runnable{
|
||||
/**
|
||||
* @var \ArrayObject|mixed[]|null object hash => mixed data
|
||||
* @phpstan-var \ArrayObject<int, array<string, mixed>>|null
|
||||
* @var mixed[][] object hash => mixed data
|
||||
* @phpstan-var array<int, array<string, mixed>>
|
||||
*
|
||||
* Used to store objects which are only needed on one thread and should not be serialized.
|
||||
* Used to store thread-local data to be used by onCompletion().
|
||||
*/
|
||||
private static ?\ArrayObject $threadLocalStorage = null;
|
||||
private static array $threadLocalStorage = [];
|
||||
|
||||
/** @phpstan-var ThreadSafeArray<int, string> */
|
||||
public ThreadSafeArray $progressUpdates;
|
||||
/** @phpstan-var ThreadSafeArray<int, string>|null */
|
||||
private ?ThreadSafeArray $progressUpdates = null;
|
||||
|
||||
private ThreadSafe|string|int|bool|null|float $result = null;
|
||||
private bool $cancelRun = false;
|
||||
private bool $submitted = false;
|
||||
|
||||
private bool $crashed = false;
|
||||
private bool $finished = false;
|
||||
|
||||
public function run() : void{
|
||||
$this->result = null;
|
||||
|
||||
if(!$this->cancelRun){
|
||||
try{
|
||||
$this->onRun();
|
||||
}catch(\Throwable $e){
|
||||
$this->crashed = true;
|
||||
|
||||
\GlobalLogger::get()->logException($e);
|
||||
}
|
||||
$this->onRun();
|
||||
}
|
||||
|
||||
$this->finished = true;
|
||||
@ -97,8 +90,11 @@ abstract class AsyncTask extends Runnable{
|
||||
$worker->getNotifier()->wakeupSleeper();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function isCrashed() : bool{
|
||||
return $this->crashed || $this->isTerminated();
|
||||
return $this->isTerminated();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +102,7 @@ abstract class AsyncTask extends Runnable{
|
||||
* because it is not true prior to task execution.
|
||||
*/
|
||||
public function isFinished() : bool{
|
||||
return $this->finished || $this->isCrashed();
|
||||
return $this->finished || $this->isTerminated();
|
||||
}
|
||||
|
||||
public function hasResult() : bool{
|
||||
@ -163,15 +159,22 @@ abstract class AsyncTask extends Runnable{
|
||||
* @param mixed $progress A value that can be safely serialize()'ed.
|
||||
*/
|
||||
public function publishProgress(mixed $progress) : void{
|
||||
$this->progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable");
|
||||
$progressUpdates = $this->progressUpdates;
|
||||
if($progressUpdates === null){
|
||||
$progressUpdates = $this->progressUpdates = new ThreadSafeArray();
|
||||
}
|
||||
$progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable");
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Only call from AsyncPool.php on the main thread
|
||||
*/
|
||||
public function checkProgressUpdates() : void{
|
||||
while(($progress = $this->progressUpdates->shift()) !== null){
|
||||
$this->onProgressUpdate(igbinary_unserialize($progress));
|
||||
$progressUpdates = $this->progressUpdates;
|
||||
if($progressUpdates !== null){
|
||||
while(($progress = $progressUpdates->shift()) !== null){
|
||||
$this->onProgressUpdate(igbinary_unserialize($progress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,8 +191,7 @@ abstract class AsyncTask extends Runnable{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the main thread when the async task experiences an error during onRun(). Use this for things like
|
||||
* promise rejection.
|
||||
* @deprecated No longer used
|
||||
*/
|
||||
public function onError() : void{
|
||||
|
||||
@ -210,15 +212,6 @@ abstract class AsyncTask extends Runnable{
|
||||
* from.
|
||||
*/
|
||||
protected function storeLocal(string $key, mixed $complexData) : void{
|
||||
if(self::$threadLocalStorage === null){
|
||||
/*
|
||||
* It's necessary to use an object (not array) here because pthreads is stupid. Non-default array statics
|
||||
* will be inherited when task classes are copied to the worker thread, which would cause unwanted
|
||||
* inheritance of primitive thread-locals, which we really don't want for various reasons.
|
||||
* It won't try to inherit objects though, so this is the easiest solution.
|
||||
*/
|
||||
self::$threadLocalStorage = new \ArrayObject();
|
||||
}
|
||||
self::$threadLocalStorage[spl_object_id($this)][$key] = $complexData;
|
||||
}
|
||||
|
||||
@ -234,7 +227,7 @@ abstract class AsyncTask extends Runnable{
|
||||
*/
|
||||
protected function fetchLocal(string $key){
|
||||
$id = spl_object_id($this);
|
||||
if(self::$threadLocalStorage === null || !isset(self::$threadLocalStorage[$id][$key])){
|
||||
if(!isset(self::$threadLocalStorage[$id][$key])){
|
||||
throw new \InvalidArgumentException("No matching thread-local data found on this thread");
|
||||
}
|
||||
|
||||
@ -243,12 +236,7 @@ abstract class AsyncTask extends Runnable{
|
||||
|
||||
final public function __destruct(){
|
||||
$this->reallyDestruct();
|
||||
if(self::$threadLocalStorage !== null && isset(self::$threadLocalStorage[$h = spl_object_id($this)])){
|
||||
unset(self::$threadLocalStorage[$h]);
|
||||
if(self::$threadLocalStorage->count() === 0){
|
||||
self::$threadLocalStorage = null;
|
||||
}
|
||||
}
|
||||
unset(self::$threadLocalStorage[spl_object_id($this)]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\thread\Worker;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use function gc_enable;
|
||||
use function ini_set;
|
||||
use function set_exception_handler;
|
||||
|
||||
class AsyncWorker extends Worker{
|
||||
/** @var mixed[] */
|
||||
@ -67,6 +68,10 @@ class AsyncWorker extends Worker{
|
||||
}
|
||||
|
||||
$this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier());
|
||||
|
||||
set_exception_handler(function(\Throwable $e){
|
||||
$this->logger->logException($e);
|
||||
});
|
||||
}
|
||||
|
||||
public function getLogger() : ThreadSafeLogger{
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\event\Event;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\ServerboundPacket;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\scheduler\TaskHandler;
|
||||
use function get_class;
|
||||
use function str_starts_with;
|
||||
@ -115,6 +116,17 @@ abstract class Timings{
|
||||
/** @var TimingsHandler[][] */
|
||||
private static array $eventHandlers = [];
|
||||
|
||||
private static TimingsHandler $asyncTaskProgressUpdateParent;
|
||||
private static TimingsHandler $asyncTaskCompletionParent;
|
||||
private static TimingsHandler $asyncTaskErrorParent;
|
||||
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $asyncTaskProgressUpdate = [];
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $asyncTaskCompletion = [];
|
||||
/** @var TimingsHandler[] */
|
||||
private static array $asyncTaskError = [];
|
||||
|
||||
public static function init() : void{
|
||||
if(self::$initialized){
|
||||
return;
|
||||
@ -168,7 +180,11 @@ abstract class Timings{
|
||||
self::$itemEntityBaseTick = new TimingsHandler("Entity Base Tick - ItemEntity", group: self::GROUP_BREAKDOWN);
|
||||
|
||||
self::$schedulerSync = new TimingsHandler("Scheduler - Sync Tasks", group: self::GROUP_BREAKDOWN);
|
||||
|
||||
self::$schedulerAsync = new TimingsHandler("Scheduler - Async Tasks", group: self::GROUP_BREAKDOWN);
|
||||
self::$asyncTaskProgressUpdateParent = new TimingsHandler("Async Tasks - Progress Updates", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
|
||||
self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
|
||||
self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
|
||||
|
||||
self::$playerCommand = new TimingsHandler("Player Command", group: self::GROUP_BREAKDOWN);
|
||||
self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache", group: self::GROUP_BREAKDOWN);
|
||||
@ -299,4 +315,46 @@ abstract class Timings{
|
||||
|
||||
return self::$eventHandlers[$event][$handlerName];
|
||||
}
|
||||
|
||||
public static function getAsyncTaskProgressUpdateTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskProgressUpdate[$taskClass])){
|
||||
self::init();
|
||||
self::$asyncTaskProgressUpdate[$taskClass] = new TimingsHandler(
|
||||
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Progress Updates",
|
||||
self::$asyncTaskProgressUpdateParent,
|
||||
$group
|
||||
);
|
||||
}
|
||||
|
||||
return self::$asyncTaskProgressUpdate[$taskClass];
|
||||
}
|
||||
|
||||
public static function getAsyncTaskCompletionTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskCompletion[$taskClass])){
|
||||
self::init();
|
||||
self::$asyncTaskCompletion[$taskClass] = new TimingsHandler(
|
||||
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Completion Handler",
|
||||
self::$asyncTaskCompletionParent,
|
||||
$group
|
||||
);
|
||||
}
|
||||
|
||||
return self::$asyncTaskCompletion[$taskClass];
|
||||
}
|
||||
|
||||
public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskError[$taskClass])){
|
||||
self::init();
|
||||
self::$asyncTaskError[$taskClass] = new TimingsHandler(
|
||||
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Error Handler",
|
||||
self::$asyncTaskErrorParent,
|
||||
$group
|
||||
);
|
||||
}
|
||||
|
||||
return self::$asyncTaskError[$taskClass];
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ abstract class StringToTParser{
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse the specified string into an enchantment.
|
||||
* Tries to parse the specified string into a corresponding instance of T.
|
||||
* @phpstan-return T|null
|
||||
*/
|
||||
public function parse(string $input){
|
||||
|
@ -2032,6 +2032,12 @@ class World implements ChunkManager{
|
||||
|
||||
if($clickVector === null){
|
||||
$clickVector = new Vector3(0.0, 0.0, 0.0);
|
||||
}else{
|
||||
$clickVector = new Vector3(
|
||||
min(1.0, max(0.0, $clickVector->x)),
|
||||
min(1.0, max(0.0, $clickVector->y)),
|
||||
min(1.0, max(0.0, $clickVector->z))
|
||||
);
|
||||
}
|
||||
|
||||
if(!$this->isInWorld($blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z)){
|
||||
|
@ -70,7 +70,7 @@ final class GlobalBlockStateHandlers{
|
||||
BlockIdMetaUpgrader::loadFromString(
|
||||
Filesystem::fileGetContents(Path::join(
|
||||
BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH,
|
||||
'1.12.0_to_1.18.10_blockstate_map.bin'
|
||||
'id_meta_to_nbt/1.12.0.bin'
|
||||
)),
|
||||
LegacyBlockIdToStringIdMap::getInstance(),
|
||||
$blockStateUpgrader
|
||||
|
@ -43,6 +43,7 @@ use pocketmine\world\WorldCreationOptions;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_map;
|
||||
use function file_put_contents;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function time;
|
||||
@ -50,11 +51,11 @@ use function time;
|
||||
class BedrockWorldData extends BaseNbtWorldData{
|
||||
|
||||
public const CURRENT_STORAGE_VERSION = 10;
|
||||
public const CURRENT_STORAGE_NETWORK_VERSION = 589;
|
||||
public const CURRENT_STORAGE_NETWORK_VERSION = 594;
|
||||
public const CURRENT_CLIENT_VERSION_TARGET = [
|
||||
1, //major
|
||||
20, //minor
|
||||
0, //patch
|
||||
10, //patch
|
||||
1, //revision
|
||||
0 //is beta
|
||||
];
|
||||
@ -154,12 +155,18 @@ class BedrockWorldData extends BaseNbtWorldData{
|
||||
}
|
||||
|
||||
$version = $worldData->getInt(self::TAG_STORAGE_VERSION, Limits::INT32_MAX);
|
||||
if($version === Limits::INT32_MAX){
|
||||
throw new CorruptedWorldException(sprintf("Missing '%s' tag in level.dat", self::TAG_STORAGE_VERSION));
|
||||
}
|
||||
if($version > self::CURRENT_STORAGE_VERSION){
|
||||
throw new UnsupportedWorldFormatException("LevelDB world format version $version is currently unsupported");
|
||||
}
|
||||
//StorageVersion is rarely updated - instead, the game relies on the NetworkVersion tag, which is synced with
|
||||
//the network protocol version for that version.
|
||||
$protocolVersion = $worldData->getInt(self::TAG_NETWORK_VERSION, Limits::INT32_MAX);
|
||||
if($protocolVersion === Limits::INT32_MAX){
|
||||
throw new CorruptedWorldException(sprintf("Missing '%s' tag in level.dat", self::TAG_NETWORK_VERSION));
|
||||
}
|
||||
if($protocolVersion > self::CURRENT_STORAGE_NETWORK_VERSION){
|
||||
throw new UnsupportedWorldFormatException("LevelDB world protocol version $protocolVersion is currently unsupported");
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_unique;
|
||||
use function max;
|
||||
|
||||
@ -43,4 +44,14 @@ class BlockTypeIdsTest extends TestCase{
|
||||
|
||||
self::assertSameSize($idTable, array_unique($idTable), "Every BlockTypeID must be unique");
|
||||
}
|
||||
|
||||
public function testVanillaBlocksParity() : void{
|
||||
$reflect = new \ReflectionClass(BlockTypeIds::class);
|
||||
|
||||
foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $block){
|
||||
$expected = $block->getTypeId();
|
||||
$actual = $reflect->getConstant($name);
|
||||
self::assertSame($expected, $actual, "VanillaBlocks::$name() does not match BlockTypeIds::$name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_unique;
|
||||
use function max;
|
||||
|
||||
@ -43,4 +44,17 @@ class ItemTypeIdsTest extends TestCase{
|
||||
|
||||
self::assertSameSize($idTable, array_unique($idTable), "Every ItemTypeID must be unique");
|
||||
}
|
||||
|
||||
public function testVanillaItemsParity() : void{
|
||||
$reflect = new \ReflectionClass(ItemTypeIds::class);
|
||||
|
||||
foreach(Utils::stringifyKeys(VanillaItems::getAll()) as $name => $item){
|
||||
if($item instanceof ItemBlock){
|
||||
continue;
|
||||
}
|
||||
$expected = $item->getTypeId();
|
||||
$actual = $reflect->getConstant($name);
|
||||
self::assertSame($expected, $actual, "VanillaItems::$name() type ID does not match ItemTypeIds::$name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,4 +89,36 @@ class AsyncPoolTest extends TestCase{
|
||||
usleep(50 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that the fix for an exotic AsyncTask::__destruct() reentrancy bug has not regressed.
|
||||
*
|
||||
* Due to an unset() in the function body, other AsyncTask::__destruct() calls could be triggered during
|
||||
* an AsyncTask's destruction. If done in the wrong way, this could lead to a crash.
|
||||
*
|
||||
* @doesNotPerformAssertions This test is checking for a crash condition, not a specific output.
|
||||
*/
|
||||
public function testTaskDestructorReentrancy() : void{
|
||||
$this->pool->submitTask(new class extends AsyncTask{
|
||||
public function __construct(){
|
||||
$this->storeLocal("task", new class extends AsyncTask{
|
||||
|
||||
public function __construct(){
|
||||
$this->storeLocal("dummy", 1);
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
//dummy
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
//dummy
|
||||
}
|
||||
});
|
||||
while($this->pool->collectTasks()){
|
||||
usleep(50 * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\crafting\json\ShapelessRecipeData;
|
||||
use pocketmine\crafting\json\SmithingTransformRecipeData;
|
||||
use pocketmine\crafting\json\SmithingTrimRecipeData;
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\data\bedrock\item\BlockItemIdMap;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -40,6 +41,7 @@ use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\convert\BlockStateDictionary;
|
||||
use pocketmine\network\mcpe\convert\BlockTranslator;
|
||||
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||
use pocketmine\network\mcpe\handler\PacketHandler;
|
||||
use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
|
||||
@ -110,6 +112,7 @@ class ParserPacketHandler extends PacketHandler{
|
||||
|
||||
public ?ItemTypeDictionary $itemTypeDictionary = null;
|
||||
private BlockTranslator $blockTranslator;
|
||||
private BlockItemIdMap $blockItemIdMap;
|
||||
|
||||
public function __construct(private string $bedrockDataPath){
|
||||
$this->blockTranslator = new BlockTranslator(
|
||||
@ -119,6 +122,7 @@ class ParserPacketHandler extends PacketHandler{
|
||||
),
|
||||
GlobalBlockStateHandlers::getSerializer()
|
||||
);
|
||||
$this->blockItemIdMap = BlockItemIdMap::getInstance();
|
||||
}
|
||||
|
||||
private static function blockStatePropertiesToString(BlockStateData $blockStateData) : string{
|
||||
@ -136,7 +140,8 @@ class ParserPacketHandler extends PacketHandler{
|
||||
if($this->itemTypeDictionary === null){
|
||||
throw new PacketHandlingException("Can't process item yet; haven't received item type dictionary");
|
||||
}
|
||||
$data = new ItemStackData($this->itemTypeDictionary->fromIntId($itemStack->getId()));
|
||||
$itemStringId = $this->itemTypeDictionary->fromIntId($itemStack->getId());
|
||||
$data = new ItemStackData($itemStringId);
|
||||
|
||||
if($itemStack->getCount() !== 1){
|
||||
$data->count = $itemStack->getCount();
|
||||
@ -146,7 +151,7 @@ class ParserPacketHandler extends PacketHandler{
|
||||
if($meta === 32767){
|
||||
$meta = 0; //kick wildcard magic bullshit
|
||||
}
|
||||
if($itemStack->getBlockRuntimeId() !== 0){
|
||||
if($this->blockItemIdMap->lookupBlockId($itemStringId) !== null){
|
||||
if($meta !== 0){
|
||||
throw new PacketHandlingException("Unexpected non-zero blockitem meta");
|
||||
}
|
||||
@ -159,6 +164,8 @@ class ParserPacketHandler extends PacketHandler{
|
||||
if(count($stateProperties) > 0){
|
||||
$data->block_states = self::blockStatePropertiesToString($blockState);
|
||||
}
|
||||
}elseif($itemStack->getBlockRuntimeId() !== ItemTranslator::NO_BLOCK_RUNTIME_ID){
|
||||
throw new PacketHandlingException("Non-blockitems should have a zero block runtime ID");
|
||||
}elseif($meta !== 0){
|
||||
$data->meta = $meta;
|
||||
}
|
||||
|
@ -26,20 +26,29 @@ namespace pocketmine\tools\generate_blockstate_upgrade_schema;
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenedName;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_filter;
|
||||
use function array_key_first;
|
||||
use function array_merge;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function explode;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function implode;
|
||||
use function json_encode;
|
||||
use function ksort;
|
||||
use function usort;
|
||||
@ -56,9 +65,22 @@ class BlockStateMapping{
|
||||
){}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag[] $properties
|
||||
* @phpstan-param array<string, Tag> $properties
|
||||
*/
|
||||
function encodeOrderedProperties(array $properties) : string{
|
||||
ksort($properties, SORT_STRING);
|
||||
return implode("", array_map(fn(Tag $tag) => encodeProperty($tag), array_values($properties)));
|
||||
}
|
||||
|
||||
function encodeProperty(Tag $tag) : string{
|
||||
return (new LittleEndianNbtSerializer())->write(new TreeRoot($tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BlockStateMapping[][]
|
||||
* @phpstan-return array<string, list<BlockStateMapping>>
|
||||
* @phpstan-return array<string, array<string, BlockStateMapping>>
|
||||
*/
|
||||
function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
$contents = Filesystem::fileGetContents($file);
|
||||
@ -72,7 +94,7 @@ function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
$old = BlockStateData::fromNbt($reverse ? $newTag : $oldTag);
|
||||
$new = BlockStateData::fromNbt($reverse ? $oldTag : $newTag);
|
||||
|
||||
$result[$old->getName()][] = new BlockStateMapping(
|
||||
$result[$old->getName()][encodeOrderedProperties($old->getStates())] = new BlockStateMapping(
|
||||
$old,
|
||||
$new
|
||||
);
|
||||
@ -82,111 +104,176 @@ function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param true[] $removedPropertiesCache
|
||||
* @param Tag[][] $remappedPropertyValuesCache
|
||||
* @phpstan-param array<string, true> $removedPropertiesCache
|
||||
* @phpstan-param array<string, array<string, Tag>> $remappedPropertyValuesCache
|
||||
* @param BlockStateData[] $states
|
||||
* @phpstan-param array<string, BlockStateData> $states
|
||||
*
|
||||
* @return Tag[][]
|
||||
* @phpstan-return array<string, array<string, Tag>>
|
||||
*/
|
||||
function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgradeSchema $result, array &$removedPropertiesCache, array &$remappedPropertyValuesCache) : void{
|
||||
function buildStateGroupSchema(array $states) : ?array{
|
||||
$first = $states[array_key_first($states)];
|
||||
|
||||
//new and old IDs are the same; compare states
|
||||
$oldName = $old->getName();
|
||||
$properties = [];
|
||||
foreach(Utils::stringifyKeys($first->getStates()) as $propertyName => $propertyValue){
|
||||
$properties[$propertyName][encodeProperty($propertyValue)] = $propertyValue;
|
||||
}
|
||||
foreach($states as $state){
|
||||
if(count($state->getStates()) !== count($properties)){
|
||||
return null;
|
||||
}
|
||||
foreach(Utils::stringifyKeys($state->getStates()) as $propertyName => $propertyValue){
|
||||
if(!isset($properties[$propertyName])){
|
||||
return null;
|
||||
}
|
||||
$properties[$propertyName][encodeProperty($propertyValue)] = $propertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
$oldStates = $old->getStates();
|
||||
$newStates = $new->getStates();
|
||||
return $properties;
|
||||
}
|
||||
|
||||
$propertyRemoved = [];
|
||||
$propertyAdded = [];
|
||||
foreach(Utils::stringifyKeys($oldStates) as $propertyName => $oldProperty){
|
||||
$newProperty = $new->getState($propertyName);
|
||||
if($newProperty === null){
|
||||
$propertyRemoved[$propertyName] = $oldProperty;
|
||||
}elseif(!$newProperty->equals($oldProperty)){
|
||||
if(!isset($remappedPropertyValuesCache[$propertyName][$oldProperty->getValue()])){
|
||||
$result->remappedPropertyValues[$oldName][$propertyName][] = new BlockStateUpgradeSchemaValueRemap(
|
||||
$oldProperty,
|
||||
$newProperty
|
||||
);
|
||||
$remappedPropertyValuesCache[$propertyName][$oldProperty->getValue()] = $newProperty;
|
||||
/**
|
||||
* @param BlockStateMapping[] $upgradeTable
|
||||
* @phpstan-param array<string, BlockStateMapping> $upgradeTable
|
||||
*/
|
||||
function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgradeSchema $result) : bool{
|
||||
$newProperties = buildStateGroupSchema(array_map(fn(BlockStateMapping $m) => $m->new, $upgradeTable));
|
||||
if($newProperties === null){
|
||||
\GlobalLogger::get()->warning("New states for $oldName don't all have the same set of properties - processing as remaps instead");
|
||||
return false;
|
||||
}
|
||||
$oldProperties = buildStateGroupSchema(array_map(fn(BlockStateMapping $m) => $m->old, $upgradeTable));
|
||||
if($oldProperties === null){
|
||||
//TODO: not sure if this is actually required - we may be able to apply some transformations even if the states are not consistent
|
||||
//however, this should never normally occur anyway
|
||||
\GlobalLogger::get()->warning("Old states for $oldName don't all have the same set of properties - processing as remaps instead");
|
||||
return false;
|
||||
}
|
||||
|
||||
$remappedPropertyValues = [];
|
||||
$addedProperties = [];
|
||||
$removedProperties = [];
|
||||
$renamedProperties = [];
|
||||
|
||||
foreach(Utils::stringifyKeys($newProperties) as $newPropertyName => $newPropertyValues){
|
||||
if(count($newPropertyValues) === 1){
|
||||
$newPropertyValue = $newPropertyValues[array_key_first($newPropertyValues)];
|
||||
if(isset($oldProperties[$newPropertyName])){
|
||||
//all the old values for this property were mapped to the same new value
|
||||
//it would be more space-efficient to represent this as a remove+add, but we can't guarantee that the
|
||||
//removal of the old value will be done before the addition of the new value
|
||||
foreach($oldProperties[$newPropertyName] as $oldPropertyValue){
|
||||
$remappedPropertyValues[$newPropertyName][encodeProperty($oldPropertyValue)] = $newPropertyValue;
|
||||
}
|
||||
}else{
|
||||
//this property has no relation to any property value in any of the old states - it's a new property
|
||||
$addedProperties[$newPropertyName] = $newPropertyValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($newStates) as $propertyName => $value){
|
||||
if($old->getState($propertyName) === null){
|
||||
$propertyAdded[$propertyName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($propertyAdded) === 0 && count($propertyRemoved) === 0){
|
||||
return;
|
||||
}
|
||||
if(count($propertyAdded) === 1 && count($propertyRemoved) === 1){
|
||||
$propertyOldName = array_key_first($propertyRemoved);
|
||||
$propertyNewName = array_key_first($propertyAdded);
|
||||
|
||||
$propertyOldValue = $propertyRemoved[$propertyOldName];
|
||||
$propertyNewValue = $propertyAdded[$propertyNewName];
|
||||
|
||||
$existingPropertyValueMap = $remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] ?? null;
|
||||
if($propertyOldName !== $propertyNewName){
|
||||
if(!$propertyOldValue->equals($propertyNewValue) && $existingPropertyValueMap === null){
|
||||
\GlobalLogger::get()->warning("warning: guessing that $oldName has $propertyOldName renamed to $propertyNewName with a value map of $propertyOldValue mapped to $propertyNewValue");;
|
||||
}
|
||||
//this is a guess; it might not be reliable if the value changed as well
|
||||
//this will probably never be an issue, but it might rear its ugly head in the future
|
||||
$result->renamedProperties[$oldName][$propertyOldName] = $propertyNewName;
|
||||
}
|
||||
if(!$propertyOldValue->equals($propertyNewValue)){
|
||||
$mapped = true;
|
||||
if($existingPropertyValueMap !== null && !$existingPropertyValueMap->equals($propertyNewValue)){
|
||||
if($existingPropertyValueMap->equals($propertyOldValue)){
|
||||
\GlobalLogger::get()->warning("warning: guessing that the value $propertyOldValue of $propertyNewValue did not change");;
|
||||
$mapped = false;
|
||||
}else{
|
||||
\GlobalLogger::get()->warning("warning: mismatch of new value for $propertyNewName for $oldName: $propertyOldValue seen mapped to $propertyNewValue and $existingPropertyValueMap");;
|
||||
foreach(Utils::stringifyKeys($oldProperties) as $oldPropertyName => $oldPropertyValues){
|
||||
$mappingsContainingOldValue = [];
|
||||
foreach($upgradeTable as $mapping){
|
||||
$mappingOldValue = $mapping->old->getState($oldPropertyName) ?? throw new AssumptionFailedError("This should never happen");
|
||||
foreach($oldPropertyValues as $oldPropertyValue){
|
||||
if($mappingOldValue->equals($oldPropertyValue)){
|
||||
$mappingsContainingOldValue[encodeProperty($oldPropertyValue)][] = $mapping;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($mapped && !isset($remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()])){
|
||||
//value remap
|
||||
$result->remappedPropertyValues[$oldName][$propertyOldName][] = new BlockStateUpgradeSchemaValueRemap(
|
||||
$propertyRemoved[$propertyOldName],
|
||||
$propertyAdded[$propertyNewName]
|
||||
);
|
||||
$remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] = $propertyNewValue;
|
||||
}
|
||||
}elseif($existingPropertyValueMap !== null){
|
||||
\GlobalLogger::get()->warning("warning: multiple values found for value $propertyOldValue of $propertyNewName on block $oldName, guessing it did not change");;
|
||||
$remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] = $propertyNewValue;
|
||||
}
|
||||
}else{
|
||||
if(count($propertyAdded) !== 0 && count($propertyRemoved) === 0){
|
||||
foreach(Utils::stringifyKeys($propertyAdded) as $propertyAddedName => $propertyAddedValue){
|
||||
$existingDefault = $result->addedProperties[$oldName][$propertyAddedName] ?? null;
|
||||
if($existingDefault !== null && !$existingDefault->equals($propertyAddedValue)){
|
||||
throw new \UnexpectedValueException("Ambiguous default value for added property $propertyAddedName on block $oldName");
|
||||
}
|
||||
|
||||
$result->addedProperties[$oldName][$propertyAddedName] = $propertyAddedValue;
|
||||
$candidateNewPropertyNames = [];
|
||||
//foreach mappings by unique value, compute the diff across all the states in the list
|
||||
foreach(Utils::stringifyKeys($mappingsContainingOldValue) as $rawOldValue => $mappingList){
|
||||
$first = array_shift($mappingList);
|
||||
foreach(Utils::stringifyKeys($first->new->getStates()) as $newPropertyName => $newPropertyValue){
|
||||
if(isset($addedProperties[$newPropertyName])){
|
||||
//this property was already determined to be unrelated to any old property
|
||||
continue;
|
||||
}
|
||||
foreach($mappingList as $pair){
|
||||
if(!($pair->new->getState($newPropertyName)?->equals($newPropertyValue) ?? false)){
|
||||
//if the new property is different with an unchanged old value,
|
||||
//the property may be influenced by multiple old properties, or be unrelated entirely
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$candidateNewPropertyNames[$newPropertyName][$rawOldValue] = $newPropertyValue;
|
||||
}
|
||||
}elseif(count($propertyRemoved) !== 0 && count($propertyAdded) === 0){
|
||||
foreach(Utils::stringifyKeys($propertyRemoved) as $propertyRemovedName => $propertyRemovedValue){
|
||||
if(!isset($removedPropertiesCache[$propertyRemovedName])){
|
||||
//to avoid having useless keys in the output
|
||||
$result->removedProperties[$oldName][] = $propertyRemovedName;
|
||||
$removedPropertiesCache[$propertyRemovedName] = $propertyRemovedName;
|
||||
}
|
||||
|
||||
if(count($candidateNewPropertyNames) === 0){
|
||||
$removedProperties[$oldPropertyName] = $oldPropertyName;
|
||||
}elseif(count($candidateNewPropertyNames) === 1){
|
||||
$newPropertyName = array_key_first($candidateNewPropertyNames);
|
||||
$newPropertyValues = $candidateNewPropertyNames[$newPropertyName];
|
||||
|
||||
if($oldPropertyName !== $newPropertyName){
|
||||
$renamedProperties[$oldPropertyName] = $newPropertyName;
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($newPropertyValues) as $rawOldValue => $newPropertyValue){
|
||||
if(!$newPropertyValue->equals($oldPropertyValues[$rawOldValue])){
|
||||
$remappedPropertyValues[$oldPropertyName][$rawOldValue] = $newPropertyValue;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$result->remappedStates[$oldName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||
$oldStates,
|
||||
$new->getName(),
|
||||
$newStates,
|
||||
[]
|
||||
);
|
||||
\GlobalLogger::get()->warning("warning: multiple properties added and removed for $oldName; added full state remap");;
|
||||
$split = true;
|
||||
if(isset($candidateNewPropertyNames[$oldPropertyName])){
|
||||
//In 1.10, direction wasn't changed at all, but not all state permutations were present in the palette,
|
||||
//making it appear that door_hinge_bit was correlated with direction.
|
||||
//If a new property is present with the same name and values as an old property, we can assume that
|
||||
//the property was unchanged, and that any extra matches properties are probably unrelated.
|
||||
$changedValues = false;
|
||||
foreach(Utils::stringifyKeys($candidateNewPropertyNames[$oldPropertyName]) as $rawOldValue => $newPropertyValue){
|
||||
if(!$newPropertyValue->equals($oldPropertyValues[$rawOldValue])){
|
||||
//if any of the new values are different, we may be dealing with a property being split into
|
||||
//multiple new properties - hand this off to the remap handler
|
||||
$changedValues = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!$changedValues){
|
||||
$split = false;
|
||||
}
|
||||
}
|
||||
if($split){
|
||||
\GlobalLogger::get()->warning(
|
||||
"Multiple new properties (" . (implode(", ", array_keys($candidateNewPropertyNames))) . ") are correlated with $oldName property $oldPropertyName, processing as remaps instead"
|
||||
);
|
||||
return false;
|
||||
}else{
|
||||
//is it safe to ignore the rest?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//finally, write the results to the schema
|
||||
|
||||
if(count($remappedPropertyValues) !== 0){
|
||||
foreach(Utils::stringifyKeys($remappedPropertyValues) as $oldPropertyName => $propertyValues){
|
||||
foreach(Utils::stringifyKeys($propertyValues) as $rawOldValue => $newPropertyValue){
|
||||
$oldPropertyValue = $oldProperties[$oldPropertyName][$rawOldValue];
|
||||
$result->remappedPropertyValues[$oldName][$oldPropertyName][] = new BlockStateUpgradeSchemaValueRemap(
|
||||
$oldPropertyValue,
|
||||
$newPropertyValue
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(count($addedProperties) !== 0){
|
||||
$result->addedProperties[$oldName] = $addedProperties;
|
||||
}
|
||||
if(count($removedProperties) !== 0){
|
||||
$result->removedProperties[$oldName] = array_values($removedProperties);
|
||||
}
|
||||
if(count($renamedProperties) !== 0){
|
||||
$result->renamedProperties[$oldName] = $renamedProperties;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,17 +281,18 @@ function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgrad
|
||||
* This significantly reduces the output size during flattening when the flattened block has many permutations
|
||||
* (e.g. walls).
|
||||
*
|
||||
* @param BlockStateUpgradeSchemaBlockRemap[] $stateRemaps
|
||||
* @param BlockStateMapping[] $upgradeTable
|
||||
* @param BlockStateMapping[] $upgradeTable
|
||||
* @phpstan-param array<string, BlockStateMapping> $upgradeTable
|
||||
*
|
||||
* @return BlockStateUpgradeSchemaBlockRemap[]
|
||||
* @phpstan-return list<BlockStateUpgradeSchemaBlockRemap>
|
||||
*/
|
||||
function compressRemappedStates(array $upgradeTable, array $stateRemaps) : array{
|
||||
function processRemappedStates(array $upgradeTable) : array{
|
||||
$unchangedStatesByNewName = [];
|
||||
|
||||
foreach($upgradeTable as $pair){
|
||||
if(count($pair->old->getStates()) === 0 || count($pair->new->getStates()) === 0){
|
||||
//all states have changed in some way - compression not possible
|
||||
//all states have changed in some way - no states are copied over
|
||||
$unchangedStatesByNewName[$pair->new->getName()] = [];
|
||||
continue;
|
||||
}
|
||||
@ -240,78 +328,115 @@ function compressRemappedStates(array $upgradeTable, array $stateRemaps) : array
|
||||
$unchangedStatesByNewName[$newName] = $unchangedStates;
|
||||
}
|
||||
|
||||
$compressedRemaps = [];
|
||||
$flattenedProperties = [];
|
||||
$notFlattenedProperties = [];
|
||||
$notFlattenedPropertyValues = [];
|
||||
foreach($upgradeTable as $pair){
|
||||
foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){
|
||||
if(isset($notFlattenedProperties[$propertyName])){
|
||||
continue;
|
||||
}
|
||||
if(!$propertyValue instanceof StringTag){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
$rawValue = $propertyValue->getValue();
|
||||
if($rawValue === ""){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
$parts = explode($rawValue, $pair->new->getName(), 2);
|
||||
if(count($parts) !== 2){
|
||||
//the new name does not contain the property value, but it may still be able to be flattened in other cases
|
||||
$notFlattenedPropertyValues[$propertyName][$rawValue] = $rawValue;
|
||||
continue;
|
||||
}
|
||||
[$prefix, $suffix] = $parts;
|
||||
|
||||
foreach($stateRemaps as $remap){
|
||||
$oldState = $remap->oldState;
|
||||
$newState = $remap->newState;
|
||||
|
||||
if($oldState === null || $newState === null){
|
||||
//no unchanged states - no compression possible
|
||||
assert(!isset($unchangedStatesByNewName[$remap->newName]));
|
||||
$compressedRemaps[$remap->newName][] = $remap;
|
||||
continue;
|
||||
$filter = $pair->old->getStates();
|
||||
foreach($unchangedStatesByNewName[$pair->new->getName()] as $unchangedPropertyName){
|
||||
unset($filter[$unchangedPropertyName]);
|
||||
}
|
||||
unset($filter[$propertyName]);
|
||||
$rawFilter = encodeOrderedProperties($filter);
|
||||
$flattenRule = new BlockStateUpgradeSchemaFlattenedName(
|
||||
prefix: $prefix,
|
||||
flattenedProperty: $propertyName,
|
||||
suffix: $suffix
|
||||
);
|
||||
if(!isset($flattenedProperties[$propertyName][$rawFilter])){
|
||||
$flattenedProperties[$propertyName][$rawFilter] = $flattenRule;
|
||||
}elseif(!$flattenRule->equals($flattenedProperties[$propertyName][$rawFilter])){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach(Utils::stringifyKeys($notFlattenedProperties) as $propertyName => $_){
|
||||
unset($flattenedProperties[$propertyName]);
|
||||
}
|
||||
|
||||
ksort($flattenedProperties, SORT_STRING);
|
||||
$flattenProperty = array_key_first($flattenedProperties);
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach($upgradeTable as $pair){
|
||||
$oldState = $pair->old->getStates();
|
||||
$newState = $pair->new->getStates();
|
||||
|
||||
$cleanedOldState = $oldState;
|
||||
$cleanedNewState = $newState;
|
||||
$newName = $pair->new->getName();
|
||||
|
||||
foreach($unchangedStatesByNewName[$remap->newName] as $propertyName){
|
||||
foreach($unchangedStatesByNewName[$newName] as $propertyName){
|
||||
unset($cleanedOldState[$propertyName]);
|
||||
unset($cleanedNewState[$propertyName]);
|
||||
}
|
||||
ksort($cleanedOldState);
|
||||
ksort($cleanedNewState);
|
||||
|
||||
$duplicate = false;
|
||||
$compressedRemaps[$remap->newName] ??= [];
|
||||
foreach($compressedRemaps[$remap->newName] as $k => $compressedRemap){
|
||||
assert($compressedRemap->oldState !== null && $compressedRemap->newState !== null);
|
||||
|
||||
if(
|
||||
count($compressedRemap->oldState) !== count($cleanedOldState) ||
|
||||
count($compressedRemap->newState) !== count($cleanedNewState)
|
||||
){
|
||||
continue;
|
||||
$flattened = false;
|
||||
if($flattenProperty !== null){
|
||||
$flattenedValue = $cleanedOldState[$flattenProperty] ?? null;
|
||||
if(!$flattenedValue instanceof StringTag){
|
||||
throw new AssumptionFailedError("This should always be a TAG_String");
|
||||
}
|
||||
foreach(Utils::stringifyKeys($cleanedOldState) as $propertyName => $propertyValue){
|
||||
if(!isset($compressedRemap->oldState[$propertyName]) || !$compressedRemap->oldState[$propertyName]->equals($propertyValue)){
|
||||
//different filter value
|
||||
continue 2;
|
||||
}
|
||||
if(!isset($notFlattenedPropertyValues[$flattenProperty][$flattenedValue->getValue()])){
|
||||
unset($cleanedOldState[$flattenProperty]);
|
||||
$flattened = true;
|
||||
}
|
||||
foreach(Utils::stringifyKeys($cleanedNewState) as $propertyName => $propertyValue){
|
||||
if(!isset($compressedRemap->newState[$propertyName]) || !$compressedRemap->newState[$propertyName]->equals($propertyValue)){
|
||||
//different replacement value
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$duplicate = true;
|
||||
break;
|
||||
}
|
||||
if(!$duplicate){
|
||||
$compressedRemaps[$remap->newName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||
$cleanedOldState,
|
||||
$remap->newName,
|
||||
$cleanedNewState,
|
||||
$unchangedStatesByNewName[$remap->newName]
|
||||
);
|
||||
$rawOldState = encodeOrderedProperties($cleanedOldState);
|
||||
$newNameRule = $flattenProperty !== null && $flattened ?
|
||||
$flattenedProperties[$flattenProperty][$rawOldState] ?? throw new AssumptionFailedError("This should always be set") :
|
||||
$newName;
|
||||
|
||||
$remap = new BlockStateUpgradeSchemaBlockRemap(
|
||||
$cleanedOldState,
|
||||
$newNameRule,
|
||||
$cleanedNewState,
|
||||
$unchangedStatesByNewName[$pair->new->getName()]
|
||||
);
|
||||
|
||||
$existing = $list[$rawOldState] ?? null;
|
||||
if($existing === null || $existing->equals($remap)){
|
||||
$list[$rawOldState] = $remap;
|
||||
}else{
|
||||
//match criteria is borked
|
||||
throw new AssumptionFailedError("Match criteria resulted in two ambiguous remaps");
|
||||
}
|
||||
}
|
||||
|
||||
$list = array_merge(...array_values($compressedRemaps));
|
||||
|
||||
//more specific filters must come before less specific ones, in case of a remap on a certain value which is
|
||||
//otherwise unchanged
|
||||
usort($list, function(BlockStateUpgradeSchemaBlockRemap $a, BlockStateUpgradeSchemaBlockRemap $b) : int{
|
||||
return count($b->oldState) <=> count($a->oldState);
|
||||
});
|
||||
return $list;
|
||||
return array_values($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockStateMapping[][] $upgradeTable
|
||||
* @phpstan-param array<string, list<BlockStateMapping>> $upgradeTable
|
||||
* @phpstan-param array<string, array<string, BlockStateMapping>> $upgradeTable
|
||||
*/
|
||||
function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgradeSchema{
|
||||
$foundVersion = -1;
|
||||
@ -343,8 +468,6 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
||||
foreach(Utils::stringifyKeys($upgradeTable) as $oldName => $blockStateMappings){
|
||||
$newNameFound = [];
|
||||
|
||||
$removedPropertiesCache = [];
|
||||
$remappedPropertyValuesCache = [];
|
||||
foreach($blockStateMappings as $mapping){
|
||||
$newName = $mapping->new->getName();
|
||||
$newNameFound[$newName] = true;
|
||||
@ -354,35 +477,23 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
||||
if($newName !== $oldName){
|
||||
$result->renamedIds[$oldName] = array_key_first($newNameFound);
|
||||
}
|
||||
foreach($blockStateMappings as $mapping){
|
||||
processState($mapping->old, $mapping->new, $result, $removedPropertiesCache, $remappedPropertyValuesCache);
|
||||
if(!processStateGroup($oldName, $blockStateMappings, $result)){
|
||||
throw new \RuntimeException("States with the same ID should be fully consistent");
|
||||
}
|
||||
}else{
|
||||
if(isset($newNameFound[$oldName])){
|
||||
//some of the states stayed under the same ID - we can process these as normal states
|
||||
foreach($blockStateMappings as $k => $mapping){
|
||||
if($mapping->new->getName() === $oldName){
|
||||
processState($mapping->old, $mapping->new, $result, $removedPropertiesCache, $remappedPropertyValuesCache);
|
||||
$stateGroup = array_filter($blockStateMappings, fn(BlockStateMapping $m) => $m->new->getName() === $oldName);
|
||||
if(processStateGroup($oldName, $stateGroup, $result)){
|
||||
foreach(Utils::stringifyKeys($stateGroup) as $k => $mapping){
|
||||
unset($blockStateMappings[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap
|
||||
foreach($blockStateMappings as $mapping){
|
||||
if(!$mapping->old->equals($mapping->new)){
|
||||
$result->remappedStates[$mapping->old->getName()][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||
$mapping->old->getStates(),
|
||||
$mapping->new->getName(),
|
||||
$mapping->new->getStates(),
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
||||
$result->remappedStates[$oldName] = processRemappedStates($blockStateMappings);
|
||||
}
|
||||
}
|
||||
foreach(Utils::stringifyKeys($result->remappedStates) as $oldName => $remap){
|
||||
$result->remappedStates[$oldName] = compressRemappedStates($upgradeTable[$oldName], $remap);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
Reference in New Issue
Block a user