Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor 2024-03-14 12:47:18 +00:00
commit e781c64540
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
70 changed files with 1196 additions and 555 deletions

View File

@ -34,7 +34,10 @@ Requires translations:
## Tests
<!--
Details should be provided of tests done. Simply saying "tested" or equivalent is not acceptable.
Attach scripts or actions to test this pull request, as well as the result
PRs which have not been tested MUST be marked as draft.
-->
I tested this PR by doing the following (tick all that apply):
- [ ] Writing PHPUnit tests (commit these in the `tests/phpunit` folder)
- [ ] Playtesting using a Minecraft client (provide screenshots or a video)
- [ ] Writing a test plugin (provide the code and sample output)
- [ ] Other (provide details)

View File

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

View File

@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.29.0
uses: shivammathur/setup-php@2.30.0
with:
php-version: 8.2

View File

@ -20,7 +20,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.29.0
uses: shivammathur/setup-php@2.30.0
with:
php-version: ${{ matrix.php-version }}
@ -86,7 +86,7 @@ jobs:
${{ github.workspace }}/build_info.json
- name: Create draft release
uses: ncipollo/release-action@v1.13.0
uses: ncipollo/release-action@v1.14.0
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }}

View File

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

View File

@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.29.0
uses: shivammathur/setup-php@2.30.0
with:
php-version: 8.2
tools: php-cs-fixer:3.49

View File

@ -0,0 +1,133 @@
<?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\build\generate_biome_ids;
use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use function asort;
use function dirname;
use function fclose;
use function fopen;
use function fwrite;
use function is_array;
use function is_int;
use function is_string;
use function json_decode;
use function str_replace;
use function strtoupper;
use const SORT_NUMERIC;
require dirname(__DIR__) . '/vendor/autoload.php';
const HEADER = <<<'HEADER'
<?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);
HEADER;
/** @return resource */
function safe_fopen(string $file, string $flags){
$result = fopen($file, $flags);
if($result === false){
throw new \RuntimeException("Failed to open file");
}
return $result;
}
function make_const_name(string $name) : string{
return strtoupper(str_replace(['.', 'minecraft:'], ['_', ''], $name));
}
/**
* @param int[] $map
* @phpstan-param array<string, int> $map
*/
function generate(array $map, string $outputFile) : void{
$file = safe_fopen($outputFile, 'wb');
fwrite($file, HEADER);
fwrite($file, <<<'CLASSHEADER'
namespace pocketmine\data\bedrock;
final class BiomeIds{
private function __construct(){
//NOOP
}
CLASSHEADER
);
$list = $map;
asort($list, SORT_NUMERIC);
$lastId = -1;
foreach(Utils::stringifyKeys($list) as $name => $id){
if($name === ""){
continue;
}
if($id !== $lastId + 1){
fwrite($file, "\n");
}
$lastId = $id;
fwrite($file, "\tpublic const " . make_const_name($name) . ' = ' . $id . ';' . "\n");
}
fwrite($file, "}\n");
fclose($file);
}
$ids = json_decode(Filesystem::fileGetContents(BedrockDataFiles::BIOME_ID_MAP_JSON), true);
if(!is_array($ids)){
throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object");
}
$cleanedIds = [];
foreach($ids as $name => $id){
if(!is_string($name) || !is_int($id)){
throw new \RuntimeException("Invalid biome ID map, expected string => int map");
}
$cleanedIds[$name] = $id;
}
generate($cleanedIds, dirname(__DIR__) . '/src/data/bedrock/BiomeIds.php');
echo "Done. Don't forget to run CS fixup after generating code.\n";

View File

@ -24,3 +24,22 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if
- Restructured GitHub Actions CI workflows to make them easier to maintain (no need to update PHP versions in multiple places anymore).
- GitHub Actions CodeStyle workflow now uses php-cs-fixer 3.49.x.
- Dependabot updates are now processed weekly instead of daily.
# 5.11.1
Released 23rd February 2024.
## Fixes
- Fixed subchunk count calculation in `ChunkSerializer` for non-overworld dimension (useful for dimension plugins).
- Harden options used for processing JSON data, particularly on the network, to close security issues.
## Documentation
- Fixed PHPStan signature for `Utils::cloneObjectArray()`.
## Internals
- Updated GitHub Actions versions to get rid of deprecation warnings.
# 5.11.2
Released 26th February 2024.
## Fixes
- Added extra checks for `BookEditPacket` handling.

63
changelogs/5.12.md Normal file
View File

@ -0,0 +1,63 @@
# 5.12.0
Released 28th February 2024
**For Minecraft: Bedrock Edition 1.20.60**
This is a minor feature release, with a few new features and improvements.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added a `--version` command-line option to display the server version and exit.
## Tools
- Added `tools/generate-biome-ids.php` to generate `pocketmine\data\bedrock\BiomeIds`.
- Fixed ordering of property values generated by `tools/generate-block-palette-spec.php`.
## API
### `pocketmine\block`
- The following new classes have been added:
- `utils\LightableTrait` - used by blocks with `getLit()` and `setLit()` methods
- The following methods have been deprecated:
- `Block->isSolid()` - this method returns confusing results which don't match expectations and no one really knows what it actually means
- `CocoaBlock` now extends `Flowable` to match vanilla Minecraft behaviour.
### `pocketmine\plugin`
- `PluginManager->registerEvent()` now throws an exception when given a generator function for the event handler.
- `PluginManager->registerEvents()` now throws an exception if any of the detected event handlers are generator functions. Use `@notHandler` to have the function ignored if intended.
### `pocketmine\promise`
- The following methods have been added:
- `public static Promise::all(list<Promise> $promises) : Promise` - returns a promise that is resolved once all given promises are resolved, or is rejected if any of the promises are rejected.
### `pocketmine\scheduler`
- The following methods have been deprecated:
- `AsyncWorker->getFromThreadStore()` - use class static properties for thread-local storage
- `AsyncWorker->removeFromThreadStore()`
- `AsyncWorker->saveToThreadStore()`
## Documentation
- Improved documentation of various methods in `Block`.
## Gameplay
- The following new items have been added:
- Name Tag
## Internals
- Removed specialization of shutdown logic for `Thread` vs `Worker` (no specialization is required).
- Authentication system no longer accepts logins signed with the old Mojang root public key.
- ID to enum mappings in `pocketmine\data` now use a new `match` convention to allow static analysis to ensure that all enum cases are handled.
- Updated version of `pocketmine/bedrock-protocol` allows avoiding decoding of some itemstack data from the client in most cases, improving performance.
# 5.12.1
Released 13th March 2024.
## Fixes
- Fixed `Player Network Receive - Decompression` timings not being stopped correctly when receiving an uncompressed packet.
## Internals
- Removed hardcoded batch packet size limit. This was already covered by other limits anyway.

16
changelogs/5.13.md Normal file
View File

@ -0,0 +1,16 @@
# 5.13.0
Released 13th March 2024.
**For Minecraft: Bedrock Edition 1.20.70**
This is a support release for Minecraft: Bedrock Edition 1.20.70.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.70.
- Removed support for earlier versions.

View File

@ -32,11 +32,11 @@
"ext-zlib": ">=1.2.11",
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
"pocketmine/bedrock-block-upgrade-schema": "~3.5.0+bedrock-1.20.60",
"pocketmine/bedrock-data": "~2.8.0+bedrock-1.20.60",
"pocketmine/bedrock-item-upgrade-schema": "~1.7.0+bedrock-1.20.60",
"pocketmine/bedrock-protocol": "~27.0.0+bedrock-1.20.60",
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
"pocketmine/bedrock-block-upgrade-schema": "~3.6.0+bedrock-1.20.70",
"pocketmine/bedrock-data": "~2.9.0+bedrock-1.20.70",
"pocketmine/bedrock-item-upgrade-schema": "~1.8.0+bedrock-1.20.70",
"pocketmine/bedrock-protocol": "~29.0.0+bedrock-1.20.70",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",
@ -45,14 +45,14 @@
"pocketmine/log": "^0.4.0",
"pocketmine/math": "~1.0.0",
"pocketmine/nbt": "~1.0.0",
"pocketmine/raklib": "^0.15.0",
"pocketmine/raklib-ipc": "^0.2.0",
"pocketmine/raklib": "~1.1.0",
"pocketmine/raklib-ipc": "~1.0.0",
"pocketmine/snooze": "^0.5.0",
"ramsey/uuid": "~4.7.0",
"symfony/filesystem": "~6.4.0"
},
"require-dev": {
"phpstan/phpstan": "1.10.57",
"phpstan/phpstan": "1.10.60",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"

287
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d923f5fd75f0d33eb5198268a74a58b4",
"content-hash": "f3dd41a25226b5ff1030d822b2c46d02",
"packages": [
{
"name": "adhocore/json-comment",
@ -122,16 +122,16 @@
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "3.5.0",
"version": "3.6.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3"
"reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1496e275db5148cb96bdaa998115e5e31a5c1e4d",
"reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d",
"shasum": ""
},
"type": "library",
@ -142,22 +142,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/3.5.0"
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.6.0"
},
"time": "2024-02-07T11:46:50+00:00"
"time": "2024-02-28T19:25:25+00:00"
},
{
"name": "pocketmine/bedrock-data",
"version": "2.8.0+bedrock-1.20.60",
"version": "2.9.0+bedrock-1.20.70",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2"
"reference": "10b6696b662fd80a282eff7dca6c99d321c5b9e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d8ea0355b7c835564af9fe6e273e650ac62c84a2",
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/10b6696b662fd80a282eff7dca6c99d321c5b9e3",
"reference": "10b6696b662fd80a282eff7dca6c99d321c5b9e3",
"shasum": ""
},
"type": "library",
@ -168,22 +168,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.60"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.70"
},
"time": "2024-02-07T11:23:46+00:00"
"time": "2024-03-13T13:55:05+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
"version": "1.7.0",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c"
"reference": "4c4dc3bbceb944c5de429b6e752ab7a15652078c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/69772dd58e2b2c7b7513fa2bcdc46e782228641c",
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/4c4dc3bbceb944c5de429b6e752ab7a15652078c",
"reference": "4c4dc3bbceb944c5de429b6e752ab7a15652078c",
"shasum": ""
},
"type": "library",
@ -194,27 +194,26 @@
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.7.0"
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.8.0"
},
"time": "2024-02-07T11:58:05+00:00"
"time": "2024-02-28T19:25:53+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "27.0.1+bedrock-1.20.60",
"version": "29.0.0+bedrock-1.20.70",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "0cebb55f6e904f722b14d420f6b2c84c7fa69f10"
"reference": "8d63f39bb2cded3d3e578fd3cf7bc769b9674857"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/0cebb55f6e904f722b14d420f6b2c84c7fa69f10",
"reference": "0cebb55f6e904f722b14d420f6b2c84c7fa69f10",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/8d63f39bb2cded3d3e578fd3cf7bc769b9674857",
"reference": "8d63f39bb2cded3d3e578fd3cf7bc769b9674857",
"shasum": ""
},
"require": {
"ext-json": "*",
"netresearch/jsonmapper": "^4.0",
"php": "^8.1",
"pocketmine/binaryutils": "^0.2.0",
"pocketmine/color": "^0.2.0 || ^0.3.0",
@ -223,7 +222,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
"phpstan/phpstan": "1.10.39",
"phpstan/phpstan": "1.10.59",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5 || ^10.0"
@ -241,22 +240,22 @@
"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/27.0.1+bedrock-1.20.60"
"source": "https://github.com/pmmp/BedrockProtocol/tree/29.0.0+bedrock-1.20.70"
},
"time": "2024-02-07T11:53:50+00:00"
"time": "2024-03-13T14:35:54+00:00"
},
{
"name": "pocketmine/binaryutils",
"version": "0.2.4",
"version": "0.2.6",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BinaryUtils.git",
"reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a"
"reference": "ccfc1899b859d45814ea3592e20ebec4cb731c84"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
"reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a",
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/ccfc1899b859d45814ea3592e20ebec4cb731c84",
"reference": "ccfc1899b859d45814ea3592e20ebec4cb731c84",
"shasum": ""
},
"require": {
@ -265,10 +264,10 @@
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "1.3.0",
"phpstan/phpstan": "~1.10.3",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5"
"phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
},
"type": "library",
"autoload": {
@ -283,9 +282,9 @@
"description": "Classes and methods for conveniently handling binary data",
"support": {
"issues": "https://github.com/pmmp/BinaryUtils/issues",
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.4"
"source": "https://github.com/pmmp/BinaryUtils/tree/0.2.6"
},
"time": "2022-01-12T18:06:33+00:00"
"time": "2024-03-04T15:04:17+00:00"
},
{
"name": "pocketmine/callback-validator",
@ -563,16 +562,16 @@
},
{
"name": "pocketmine/netresearch-jsonmapper",
"version": "v4.2.1000",
"version": "v4.4.999",
"source": {
"type": "git",
"url": "https://github.com/pmmp/netresearch-jsonmapper.git",
"reference": "078764e869e9b732f97206ec9363480a77c35532"
"reference": "9a6610033d56e358e86a3e4fd5f87063c7318833"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/078764e869e9b732f97206ec9363480a77c35532",
"reference": "078764e869e9b732f97206ec9363480a77c35532",
"url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/9a6610033d56e358e86a3e4fd5f87063c7318833",
"reference": "9a6610033d56e358e86a3e4fd5f87063c7318833",
"shasum": ""
},
"require": {
@ -586,7 +585,7 @@
"netresearch/jsonmapper": "~4.2.0"
},
"require-dev": {
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
"squizlabs/php_codesniffer": "~3.5"
},
"type": "library",
@ -611,34 +610,34 @@
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.2.1000"
"source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.4.999"
},
"time": "2023-07-14T10:44:14+00:00"
"time": "2024-02-23T13:17:01+00:00"
},
{
"name": "pocketmine/raklib",
"version": "0.15.1",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca"
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/79b7b4d1d7516dc6e322514453645ad9452b20ca",
"reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97",
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97",
"shasum": ""
},
"require": {
"ext-sockets": "*",
"php": "^8.0",
"php": "^8.1",
"php-64bit": "*",
"php-ipv6": "*",
"pocketmine/binaryutils": "^0.2.0",
"pocketmine/log": "^0.3.0 || ^0.4.0"
},
"require-dev": {
"phpstan/phpstan": "1.9.17",
"phpstan/phpstan": "1.10.1",
"phpstan/phpstan-strict-rules": "^1.0"
},
"type": "library",
@ -654,32 +653,32 @@
"description": "A RakNet server implementation written in PHP",
"support": {
"issues": "https://github.com/pmmp/RakLib/issues",
"source": "https://github.com/pmmp/RakLib/tree/0.15.1"
"source": "https://github.com/pmmp/RakLib/tree/1.1.1"
},
"time": "2023-03-07T15:10:34+00:00"
"time": "2024-03-04T14:02:14+00:00"
},
{
"name": "pocketmine/raklib-ipc",
"version": "0.2.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLibIpc.git",
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c"
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
"reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c",
"url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/ce632ef2c6743e71eddb5dc329c49af6555f90bc",
"reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc",
"shasum": ""
},
"require": {
"php": "^8.0",
"php-64bit": "*",
"pocketmine/binaryutils": "^0.2.0",
"pocketmine/raklib": "^0.15.0"
"pocketmine/raklib": "^0.15.0 || ^1.0.0"
},
"require-dev": {
"phpstan/phpstan": "1.9.17",
"phpstan/phpstan": "1.10.1",
"phpstan/phpstan-strict-rules": "^1.0.0"
},
"type": "library",
@ -695,9 +694,9 @@
"description": "Channel-based protocols for inter-thread/inter-process communication with RakLib",
"support": {
"issues": "https://github.com/pmmp/RakLibIpc/issues",
"source": "https://github.com/pmmp/RakLibIpc/tree/0.2.0"
"source": "https://github.com/pmmp/RakLibIpc/tree/1.0.1"
},
"time": "2023-02-13T13:40:40+00:00"
"time": "2024-03-01T15:55:05+00:00"
},
{
"name": "pocketmine/snooze",
@ -922,16 +921,16 @@
},
{
"name": "symfony/filesystem",
"version": "v6.4.0",
"version": "v6.4.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59"
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59",
"reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb",
"shasum": ""
},
"require": {
@ -965,7 +964,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.4.0"
"source": "https://github.com/symfony/filesystem/tree/v6.4.3"
},
"funding": [
{
@ -981,20 +980,20 @@
"type": "tidelift"
}
],
"time": "2023-07-26T17:27:13+00:00"
"time": "2024-01-23T14:51:35+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"shasum": ""
},
"require": {
@ -1008,9 +1007,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1047,7 +1043,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
},
"funding": [
{
@ -1063,20 +1059,20 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "42292d99c55abe617799667f454222c54c60e229"
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"shasum": ""
},
"require": {
@ -1090,9 +1086,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1130,7 +1123,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
},
"funding": [
{
@ -1146,7 +1139,7 @@
"type": "tidelift"
}
],
"time": "2023-07-28T09:04:16+00:00"
"time": "2024-01-29T20:11:03+00:00"
}
],
"packages-dev": [
@ -1211,16 +1204,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.0.0",
"version": "v5.0.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
"reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
"shasum": ""
},
"require": {
@ -1263,26 +1256,27 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
},
"time": "2024-01-07T17:17:35+00:00"
"time": "2024-03-05T20:51:40+00:00"
},
{
"name": "phar-io/manifest",
"version": "2.0.3",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
"reference": "97803eca37d319dfa7826cc2437fc020857acb53"
"reference": "54750ef60c58e43759730615a392c31c80e23176"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
"reference": "97803eca37d319dfa7826cc2437fc020857acb53",
"url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
"reference": "54750ef60c58e43759730615a392c31c80e23176",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-phar": "*",
"ext-xmlwriter": "*",
"phar-io/version": "^3.0.1",
@ -1323,9 +1317,15 @@
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
"support": {
"issues": "https://github.com/phar-io/manifest/issues",
"source": "https://github.com/phar-io/manifest/tree/2.0.3"
"source": "https://github.com/phar-io/manifest/tree/2.0.4"
},
"time": "2021-07-20T11:28:43+00:00"
"funding": [
{
"url": "https://github.com/theseer",
"type": "github"
}
],
"time": "2024-03-03T12:33:53+00:00"
},
{
"name": "phar-io/version",
@ -1380,16 +1380,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.57",
"version": "1.10.60",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "1627b1d03446904aaa77593f370c5201d2ecc34e"
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1627b1d03446904aaa77593f370c5201d2ecc34e",
"reference": "1627b1d03446904aaa77593f370c5201d2ecc34e",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
"reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
"shasum": ""
},
"require": {
@ -1438,20 +1438,20 @@
"type": "tidelift"
}
],
"time": "2024-01-24T11:51:34+00:00"
"time": "2024-03-07T13:30:19+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.3.15",
"version": "1.3.16",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a"
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d5242a59d035e46774f2e634b374bc39ff62cb95",
"reference": "d5242a59d035e46774f2e634b374bc39ff62cb95",
"shasum": ""
},
"require": {
@ -1488,9 +1488,9 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.16"
},
"time": "2023-10-09T18:58:39+00:00"
"time": "2024-02-23T09:51:20+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
@ -1543,16 +1543,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.11",
"version": "10.1.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "78c3b7625965c2513ee96569a4dbb62601784145"
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145",
"reference": "78c3b7625965c2513ee96569a4dbb62601784145",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
"reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
"shasum": ""
},
"require": {
@ -1609,7 +1609,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14"
},
"funding": [
{
@ -1617,7 +1617,7 @@
"type": "github"
}
],
"time": "2023-12-21T15:38:30+00:00"
"time": "2024-03-12T15:33:41+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1965,16 +1965,16 @@
},
{
"name": "sebastian/cli-parser",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
"reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae"
"reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae",
"reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
"reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
"shasum": ""
},
"require": {
@ -2009,7 +2009,8 @@
"homepage": "https://github.com/sebastianbergmann/cli-parser",
"support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
"source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0"
"security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
"source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
},
"funding": [
{
@ -2017,7 +2018,7 @@
"type": "github"
}
],
"time": "2023-02-03T06:58:15+00:00"
"time": "2024-03-02T07:12:49+00:00"
},
{
"name": "sebastian/code-unit",
@ -2267,16 +2268,16 @@
},
{
"name": "sebastian/diff",
"version": "5.1.0",
"version": "5.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
"reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
"reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
"shasum": ""
},
"require": {
@ -2284,7 +2285,7 @@
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"symfony/process": "^4.2 || ^5"
"symfony/process": "^6.4"
},
"type": "library",
"extra": {
@ -2322,7 +2323,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
"source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
},
"funding": [
{
@ -2330,7 +2331,7 @@
"type": "github"
}
],
"time": "2023-12-22T10:55:06+00:00"
"time": "2024-03-02T07:15:17+00:00"
},
{
"name": "sebastian/environment",
@ -2398,16 +2399,16 @@
},
{
"name": "sebastian/exporter",
"version": "5.1.1",
"version": "5.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc"
"reference": "955288482d97c19a372d3f31006ab3f37da47adf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc",
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
"reference": "955288482d97c19a372d3f31006ab3f37da47adf",
"shasum": ""
},
"require": {
@ -2464,7 +2465,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1"
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
},
"funding": [
{
@ -2472,20 +2473,20 @@
"type": "github"
}
],
"time": "2023-09-24T13:22:09+00:00"
"time": "2024-03-02T07:17:12+00:00"
},
{
"name": "sebastian/global-state",
"version": "6.0.1",
"version": "6.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4"
"reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4",
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
"reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
"shasum": ""
},
"require": {
@ -2519,14 +2520,14 @@
}
],
"description": "Snapshotting of global state",
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"homepage": "https://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"security": "https://github.com/sebastianbergmann/global-state/security/policy",
"source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1"
"source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
},
"funding": [
{
@ -2534,7 +2535,7 @@
"type": "github"
}
],
"time": "2023-07-19T07:19:23+00:00"
"time": "2024-03-02T07:19:19+00:00"
},
{
"name": "sebastian/lines-of-code",
@ -2880,16 +2881,16 @@
},
{
"name": "theseer/tokenizer",
"version": "1.2.2",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
"shasum": ""
},
"require": {
@ -2918,7 +2919,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
"source": "https://github.com/theseer/tokenizer/tree/1.2.2"
"source": "https://github.com/theseer/tokenizer/tree/1.2.3"
},
"funding": [
{
@ -2926,7 +2927,7 @@
"type": "github"
}
],
"time": "2023-11-20T00:12:19+00:00"
"time": "2024-03-03T12:36:25+00:00"
}
],
"aliases": [],

View File

@ -59,7 +59,6 @@ use pocketmine\network\mcpe\EntityEventBroadcaster;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\PacketBroadcaster;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
use pocketmine\network\mcpe\raklib\RakLibInterface;
use pocketmine\network\mcpe\StandardEntityEventBroadcaster;
@ -1189,12 +1188,11 @@ class Server{
bool $useQuery,
PacketBroadcaster $packetBroadcaster,
EntityEventBroadcaster $entityEventBroadcaster,
PacketSerializerContext $packetSerializerContext,
TypeConverter $typeConverter
) : bool{
$prettyIp = $ipV6 ? "[$ip]" : $ip;
try{
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter));
$rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $typeConverter));
}catch(NetworkInterfaceStartException $e){
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed(
$ip,
@ -1221,15 +1219,14 @@ class Server{
$useQuery = $this->configGroup->getConfigBool(ServerProperties::ENABLE_QUERY, true);
$typeConverter = TypeConverter::getInstance();
$packetSerializerContext = new PacketSerializerContext($typeConverter->getItemTypeDictionary());
$packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext);
$packetBroadcaster = new StandardPacketBroadcaster($this);
$entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster, $typeConverter);
if(
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) ||
!$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter) ||
(
$this->configGroup->getConfigBool(ServerProperties::ENABLE_IPV6, true) &&
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter)
!$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter)
)
){
return false;

View File

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

View File

@ -111,4 +111,15 @@ final class BiomeIds{
public const CRIMSON_FOREST = 179;
public const WARPED_FOREST = 180;
public const BASALT_DELTAS = 181;
public const JAGGED_PEAKS = 182;
public const FROZEN_PEAKS = 183;
public const SNOWY_SLOPES = 184;
public const GROVE = 185;
public const MEADOW = 186;
public const LUSH_CAVES = 187;
public const DRIPSTONE_CAVES = 188;
public const STONY_PEAKS = 189;
public const DEEP_DARK = 190;
public const MANGROVE_SWAMP = 191;
public const CHERRY_GROVE = 192;
}

View File

@ -42,8 +42,8 @@ final class BlockStateData{
public const CURRENT_VERSION =
(1 << 24) | //major
(20 << 16) | //minor
(60 << 8) | //patch
(1); //revision
(70 << 8) | //patch
(4); //revision
public const TAG_NAME = "name";
public const TAG_STATES = "states";

View File

@ -105,9 +105,7 @@ final class BlockStateNames{
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";
public const NEW_LEAF_TYPE = "new_leaf_type";
public const OCCUPIED_BIT = "occupied_bit";
public const OLD_LEAF_TYPE = "old_leaf_type";
public const OPEN_BIT = "open_bit";
public const ORIENTATION = "orientation";
public const OUTPUT_LIT_BIT = "output_lit_bit";
@ -151,6 +149,7 @@ final class BlockStateNames{
public const UPDATE_BIT = "update_bit";
public const UPPER_BLOCK_BIT = "upper_block_bit";
public const UPSIDE_DOWN_BIT = "upside_down_bit";
public const VAULT_STATE = "vault_state";
public const VINE_DIRECTION_BITS = "vine_direction_bits";
public const WALL_BLOCK_TYPE = "wall_block_type";
public const WALL_CONNECTION_TYPE_EAST = "wall_connection_type_east";
@ -160,5 +159,4 @@ final class BlockStateNames{
public const WALL_POST_BIT = "wall_post_bit";
public const WEEPING_VINES_AGE = "weeping_vines_age";
public const WEIRDO_DIRECTION = "weirdo_direction";
public const WOOD_TYPE = "wood_type";
}

View File

@ -143,14 +143,6 @@ final class BlockStateStringValues{
public const MONSTER_EGG_STONE_TYPE_STONE = "stone";
public const MONSTER_EGG_STONE_TYPE_STONE_BRICK = "stone_brick";
public const NEW_LEAF_TYPE_ACACIA = "acacia";
public const NEW_LEAF_TYPE_DARK_OAK = "dark_oak";
public const OLD_LEAF_TYPE_BIRCH = "birch";
public const OLD_LEAF_TYPE_JUNGLE = "jungle";
public const OLD_LEAF_TYPE_OAK = "oak";
public const OLD_LEAF_TYPE_SPRUCE = "spruce";
public const ORIENTATION_DOWN_EAST = "down_east";
public const ORIENTATION_DOWN_NORTH = "down_north";
public const ORIENTATION_DOWN_SOUTH = "down_south";
@ -264,6 +256,11 @@ final class BlockStateStringValues{
public const TURTLE_EGG_COUNT_THREE_EGG = "three_egg";
public const TURTLE_EGG_COUNT_TWO_EGG = "two_egg";
public const VAULT_STATE_ACTIVE = "active";
public const VAULT_STATE_EJECTING = "ejecting";
public const VAULT_STATE_INACTIVE = "inactive";
public const VAULT_STATE_UNLOCKING = "unlocking";
public const WALL_BLOCK_TYPE_ANDESITE = "andesite";
public const WALL_BLOCK_TYPE_BRICK = "brick";
public const WALL_BLOCK_TYPE_COBBLESTONE = "cobblestone";
@ -295,11 +292,4 @@ final class BlockStateStringValues{
public const WALL_CONNECTION_TYPE_WEST_SHORT = "short";
public const WALL_CONNECTION_TYPE_WEST_TALL = "tall";
public const WOOD_TYPE_ACACIA = "acacia";
public const WOOD_TYPE_BIRCH = "birch";
public const WOOD_TYPE_DARK_OAK = "dark_oak";
public const WOOD_TYPE_JUNGLE = "jungle";
public const WOOD_TYPE_OAK = "oak";
public const WOOD_TYPE_SPRUCE = "spruce";
}

View File

@ -33,16 +33,20 @@ final class BlockTypeNames{
public const ACACIA_BUTTON = "minecraft:acacia_button";
public const ACACIA_DOOR = "minecraft:acacia_door";
public const ACACIA_DOUBLE_SLAB = "minecraft:acacia_double_slab";
public const ACACIA_FENCE = "minecraft:acacia_fence";
public const ACACIA_FENCE_GATE = "minecraft:acacia_fence_gate";
public const ACACIA_HANGING_SIGN = "minecraft:acacia_hanging_sign";
public const ACACIA_LEAVES = "minecraft:acacia_leaves";
public const ACACIA_LOG = "minecraft:acacia_log";
public const ACACIA_PLANKS = "minecraft:acacia_planks";
public const ACACIA_PRESSURE_PLATE = "minecraft:acacia_pressure_plate";
public const ACACIA_SLAB = "minecraft:acacia_slab";
public const ACACIA_STAIRS = "minecraft:acacia_stairs";
public const ACACIA_STANDING_SIGN = "minecraft:acacia_standing_sign";
public const ACACIA_TRAPDOOR = "minecraft:acacia_trapdoor";
public const ACACIA_WALL_SIGN = "minecraft:acacia_wall_sign";
public const ACACIA_WOOD = "minecraft:acacia_wood";
public const ACTIVATOR_RAIL = "minecraft:activator_rail";
public const AIR = "minecraft:air";
public const ALLOW = "minecraft:allow";
@ -88,16 +92,20 @@ final class BlockTypeNames{
public const BIG_DRIPLEAF = "minecraft:big_dripleaf";
public const BIRCH_BUTTON = "minecraft:birch_button";
public const BIRCH_DOOR = "minecraft:birch_door";
public const BIRCH_DOUBLE_SLAB = "minecraft:birch_double_slab";
public const BIRCH_FENCE = "minecraft:birch_fence";
public const BIRCH_FENCE_GATE = "minecraft:birch_fence_gate";
public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign";
public const BIRCH_LEAVES = "minecraft:birch_leaves";
public const BIRCH_LOG = "minecraft:birch_log";
public const BIRCH_PLANKS = "minecraft:birch_planks";
public const BIRCH_PRESSURE_PLATE = "minecraft:birch_pressure_plate";
public const BIRCH_SLAB = "minecraft:birch_slab";
public const BIRCH_STAIRS = "minecraft:birch_stairs";
public const BIRCH_STANDING_SIGN = "minecraft:birch_standing_sign";
public const BIRCH_TRAPDOOR = "minecraft:birch_trapdoor";
public const BIRCH_WALL_SIGN = "minecraft:birch_wall_sign";
public const BIRCH_WOOD = "minecraft:birch_wood";
public const BLACK_CANDLE = "minecraft:black_candle";
public const BLACK_CANDLE_CAKE = "minecraft:black_candle_cake";
public const BLACK_CARPET = "minecraft:black_carpet";
@ -266,14 +274,18 @@ final class BlockTypeNames{
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";
public const DARK_OAK_DOUBLE_SLAB = "minecraft:dark_oak_double_slab";
public const DARK_OAK_FENCE = "minecraft:dark_oak_fence";
public const DARK_OAK_FENCE_GATE = "minecraft:dark_oak_fence_gate";
public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign";
public const DARK_OAK_LEAVES = "minecraft:dark_oak_leaves";
public const DARK_OAK_LOG = "minecraft:dark_oak_log";
public const DARK_OAK_PLANKS = "minecraft:dark_oak_planks";
public const DARK_OAK_PRESSURE_PLATE = "minecraft:dark_oak_pressure_plate";
public const DARK_OAK_SLAB = "minecraft:dark_oak_slab";
public const DARK_OAK_STAIRS = "minecraft:dark_oak_stairs";
public const DARK_OAK_TRAPDOOR = "minecraft:dark_oak_trapdoor";
public const DARK_OAK_WOOD = "minecraft:dark_oak_wood";
public const DARK_PRISMARINE_STAIRS = "minecraft:dark_prismarine_stairs";
public const DARKOAK_STANDING_SIGN = "minecraft:darkoak_standing_sign";
public const DARKOAK_WALL_SIGN = "minecraft:darkoak_wall_sign";
@ -320,7 +332,6 @@ final class BlockTypeNames{
public const DOUBLE_STONE_BLOCK_SLAB2 = "minecraft:double_stone_block_slab2";
public const DOUBLE_STONE_BLOCK_SLAB3 = "minecraft:double_stone_block_slab3";
public const DOUBLE_STONE_BLOCK_SLAB4 = "minecraft:double_stone_block_slab4";
public const DOUBLE_WOODEN_SLAB = "minecraft:double_wooden_slab";
public const DRAGON_EGG = "minecraft:dragon_egg";
public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block";
public const DRIPSTONE_BLOCK = "minecraft:dripstone_block";
@ -490,7 +501,7 @@ final class BlockTypeNames{
public const GOLDEN_RAIL = "minecraft:golden_rail";
public const GRANITE = "minecraft:granite";
public const GRANITE_STAIRS = "minecraft:granite_stairs";
public const GRASS = "minecraft:grass";
public const GRASS_BLOCK = "minecraft:grass_block";
public const GRASS_PATH = "minecraft:grass_path";
public const GRAVEL = "minecraft:gravel";
public const GRAY_CANDLE = "minecraft:gray_candle";
@ -572,16 +583,20 @@ final class BlockTypeNames{
public const JUKEBOX = "minecraft:jukebox";
public const JUNGLE_BUTTON = "minecraft:jungle_button";
public const JUNGLE_DOOR = "minecraft:jungle_door";
public const JUNGLE_DOUBLE_SLAB = "minecraft:jungle_double_slab";
public const JUNGLE_FENCE = "minecraft:jungle_fence";
public const JUNGLE_FENCE_GATE = "minecraft:jungle_fence_gate";
public const JUNGLE_HANGING_SIGN = "minecraft:jungle_hanging_sign";
public const JUNGLE_LEAVES = "minecraft:jungle_leaves";
public const JUNGLE_LOG = "minecraft:jungle_log";
public const JUNGLE_PLANKS = "minecraft:jungle_planks";
public const JUNGLE_PRESSURE_PLATE = "minecraft:jungle_pressure_plate";
public const JUNGLE_SLAB = "minecraft:jungle_slab";
public const JUNGLE_STAIRS = "minecraft:jungle_stairs";
public const JUNGLE_STANDING_SIGN = "minecraft:jungle_standing_sign";
public const JUNGLE_TRAPDOOR = "minecraft:jungle_trapdoor";
public const JUNGLE_WALL_SIGN = "minecraft:jungle_wall_sign";
public const JUNGLE_WOOD = "minecraft:jungle_wood";
public const KELP = "minecraft:kelp";
public const LADDER = "minecraft:ladder";
public const LANTERN = "minecraft:lantern";
@ -589,8 +604,6 @@ final class BlockTypeNames{
public const LAPIS_ORE = "minecraft:lapis_ore";
public const LARGE_AMETHYST_BUD = "minecraft:large_amethyst_bud";
public const LAVA = "minecraft:lava";
public const LEAVES = "minecraft:leaves";
public const LEAVES2 = "minecraft:leaves2";
public const LECTERN = "minecraft:lectern";
public const LEVER = "minecraft:lever";
public const LIGHT_BLOCK = "minecraft:light_block";
@ -698,11 +711,15 @@ final class BlockTypeNames{
public const NETHERREACTOR = "minecraft:netherreactor";
public const NORMAL_STONE_STAIRS = "minecraft:normal_stone_stairs";
public const NOTEBLOCK = "minecraft:noteblock";
public const OAK_DOUBLE_SLAB = "minecraft:oak_double_slab";
public const OAK_FENCE = "minecraft:oak_fence";
public const OAK_HANGING_SIGN = "minecraft:oak_hanging_sign";
public const OAK_LEAVES = "minecraft:oak_leaves";
public const OAK_LOG = "minecraft:oak_log";
public const OAK_PLANKS = "minecraft:oak_planks";
public const OAK_SLAB = "minecraft:oak_slab";
public const OAK_STAIRS = "minecraft:oak_stairs";
public const OAK_WOOD = "minecraft:oak_wood";
public const OBSERVER = "minecraft:observer";
public const OBSIDIAN = "minecraft:obsidian";
public const OCHRE_FROGLIGHT = "minecraft:ochre_froglight";
@ -875,16 +892,20 @@ final class BlockTypeNames{
public const SPORE_BLOSSOM = "minecraft:spore_blossom";
public const SPRUCE_BUTTON = "minecraft:spruce_button";
public const SPRUCE_DOOR = "minecraft:spruce_door";
public const SPRUCE_DOUBLE_SLAB = "minecraft:spruce_double_slab";
public const SPRUCE_FENCE = "minecraft:spruce_fence";
public const SPRUCE_FENCE_GATE = "minecraft:spruce_fence_gate";
public const SPRUCE_HANGING_SIGN = "minecraft:spruce_hanging_sign";
public const SPRUCE_LEAVES = "minecraft:spruce_leaves";
public const SPRUCE_LOG = "minecraft:spruce_log";
public const SPRUCE_PLANKS = "minecraft:spruce_planks";
public const SPRUCE_PRESSURE_PLATE = "minecraft:spruce_pressure_plate";
public const SPRUCE_SLAB = "minecraft:spruce_slab";
public const SPRUCE_STAIRS = "minecraft:spruce_stairs";
public const SPRUCE_STANDING_SIGN = "minecraft:spruce_standing_sign";
public const SPRUCE_TRAPDOOR = "minecraft:spruce_trapdoor";
public const SPRUCE_WALL_SIGN = "minecraft:spruce_wall_sign";
public const SPRUCE_WOOD = "minecraft:spruce_wood";
public const STANDING_BANNER = "minecraft:standing_banner";
public const STANDING_SIGN = "minecraft:standing_sign";
public const STICKY_PISTON = "minecraft:sticky_piston";
@ -902,18 +923,24 @@ final class BlockTypeNames{
public const STONECUTTER = "minecraft:stonecutter";
public const STONECUTTER_BLOCK = "minecraft:stonecutter_block";
public const STRIPPED_ACACIA_LOG = "minecraft:stripped_acacia_log";
public const STRIPPED_ACACIA_WOOD = "minecraft:stripped_acacia_wood";
public const STRIPPED_BAMBOO_BLOCK = "minecraft:stripped_bamboo_block";
public const STRIPPED_BIRCH_LOG = "minecraft:stripped_birch_log";
public const STRIPPED_BIRCH_WOOD = "minecraft:stripped_birch_wood";
public const STRIPPED_CHERRY_LOG = "minecraft:stripped_cherry_log";
public const STRIPPED_CHERRY_WOOD = "minecraft:stripped_cherry_wood";
public const STRIPPED_CRIMSON_HYPHAE = "minecraft:stripped_crimson_hyphae";
public const STRIPPED_CRIMSON_STEM = "minecraft:stripped_crimson_stem";
public const STRIPPED_DARK_OAK_LOG = "minecraft:stripped_dark_oak_log";
public const STRIPPED_DARK_OAK_WOOD = "minecraft:stripped_dark_oak_wood";
public const STRIPPED_JUNGLE_LOG = "minecraft:stripped_jungle_log";
public const STRIPPED_JUNGLE_WOOD = "minecraft:stripped_jungle_wood";
public const STRIPPED_MANGROVE_LOG = "minecraft:stripped_mangrove_log";
public const STRIPPED_MANGROVE_WOOD = "minecraft:stripped_mangrove_wood";
public const STRIPPED_OAK_LOG = "minecraft:stripped_oak_log";
public const STRIPPED_OAK_WOOD = "minecraft:stripped_oak_wood";
public const STRIPPED_SPRUCE_LOG = "minecraft:stripped_spruce_log";
public const STRIPPED_SPRUCE_WOOD = "minecraft:stripped_spruce_wood";
public const STRIPPED_WARPED_HYPHAE = "minecraft:stripped_warped_hyphae";
public const STRIPPED_WARPED_STEM = "minecraft:stripped_warped_stem";
public const STRUCTURE_BLOCK = "minecraft:structure_block";
@ -952,6 +979,7 @@ final class BlockTypeNames{
public const UNLIT_REDSTONE_TORCH = "minecraft:unlit_redstone_torch";
public const UNPOWERED_COMPARATOR = "minecraft:unpowered_comparator";
public const UNPOWERED_REPEATER = "minecraft:unpowered_repeater";
public const VAULT = "minecraft:vault";
public const VERDANT_FROGLIGHT = "minecraft:verdant_froglight";
public const VINE = "minecraft:vine";
public const WALL_BANNER = "minecraft:wall_banner";
@ -1042,11 +1070,9 @@ final class BlockTypeNames{
public const WHITE_TERRACOTTA = "minecraft:white_terracotta";
public const WHITE_WOOL = "minecraft:white_wool";
public const WITHER_ROSE = "minecraft:wither_rose";
public const WOOD = "minecraft:wood";
public const WOODEN_BUTTON = "minecraft:wooden_button";
public const WOODEN_DOOR = "minecraft:wooden_door";
public const WOODEN_PRESSURE_PLATE = "minecraft:wooden_pressure_plate";
public const WOODEN_SLAB = "minecraft:wooden_slab";
public const YELLOW_CANDLE = "minecraft:yellow_candle";
public const YELLOW_CANDLE_CAKE = "minecraft:yellow_candle_cake";
public const YELLOW_CARPET = "minecraft:yellow_carpet";

View File

@ -202,7 +202,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->registerFlatCoralSerializers();
$this->registerCauldronSerializers();
$this->registerFlatWoodBlockSerializers();
$this->registerLegacyWoodBlockSerializers();
$this->registerLeavesSerializers();
$this->registerSimpleSerializers();
$this->registerSerializers();
@ -557,9 +556,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR)));
$this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN)));
$this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG);
$this->mapLog(Blocks::ACACIA_WOOD(), Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD);
$this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE);
$this->mapSimple(Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS);
//wood and slabs still use the old way of storing wood type
$this->mapSlab(Blocks::ACACIA_SLAB(), Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB);
$this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON)));
$this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR)));
@ -569,10 +569,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR)));
$this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN)));
$this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG);
$this->mapLog(Blocks::BIRCH_WOOD(), Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD);
$this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE);
$this->mapSimple(Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS);
$this->mapSlab(Blocks::BIRCH_SLAB(), Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB);
$this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS);
//wood 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)));
@ -620,10 +621,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR)));
$this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN)));
$this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG);
$this->mapLog(Blocks::DARK_OAK_WOOD(), Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD);
$this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE);
$this->mapSimple(Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS);
$this->mapSlab(Blocks::DARK_OAK_SLAB(), Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB);
$this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS);
//wood 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)));
@ -633,10 +635,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR)));
$this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN)));
$this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG);
$this->mapLog(Blocks::JUNGLE_WOOD(), Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD);
$this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE);
$this->mapSimple(Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS);
$this->mapSlab(Blocks::JUNGLE_SLAB(), Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB);
$this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS);
//wood 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)));
@ -670,10 +673,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR)));
$this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN)));
$this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG);
$this->mapLog(Blocks::OAK_WOOD(), Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD);
$this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE);
$this->mapSimple(Blocks::OAK_PLANKS(), Ids::OAK_PLANKS);
$this->mapSlab(Blocks::OAK_SLAB(), Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB);
$this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS);
//wood and slabs still use the old way of storing wood type
$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)));
@ -683,8 +687,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR)));
$this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN)));
$this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG);
$this->mapLog(Blocks::SPRUCE_WOOD(), Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD);
$this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE);
$this->mapSimple(Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS);
$this->mapSlab(Blocks::SPRUCE_SLAB(), Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB);
$this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS);
//wood and slabs still use the old way of storing wood type
@ -703,30 +709,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS);
}
private function registerLegacyWoodBlockSerializers() : void{
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)));
@ -735,12 +717,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$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));
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::ACACIA_LEAVES)));
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::BIRCH_LEAVES)));
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::DARK_OAK_LEAVES)));
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::JUNGLE_LEAVES)));
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::OAK_LEAVES)));
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::SPRUCE_LEAVES)));
}
private function registerSimpleSerializers() : void{
@ -926,7 +908,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::GOLD(), Ids::GOLD_BLOCK);
$this->mapSimple(Blocks::GOLD_ORE(), Ids::GOLD_ORE);
$this->mapSimple(Blocks::GRANITE(), Ids::GRANITE);
$this->mapSimple(Blocks::GRASS(), Ids::GRASS);
$this->mapSimple(Blocks::GRASS(), Ids::GRASS_BLOCK);
$this->mapSimple(Blocks::GRASS_PATH(), Ids::GRASS_PATH);
$this->mapSimple(Blocks::GRAVEL(), Ids::GRAVEL);
$this->mapSimple(Blocks::HANGING_ROOTS(), Ids::HANGING_ROOTS);

View File

@ -362,18 +362,4 @@ final class BlockStateDeserializerHelper{
default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_4, $type),
};
}
/** @throws BlockStateDeserializeException */
public static function mapWoodenSlabType(BlockStateReader $in) : Slab{
// * wood_type (StringTag) = acacia, birch, dark_oak, jungle, oak, spruce
return match($type = $in->readString(BlockStateNames::WOOD_TYPE)){
StringValues::WOOD_TYPE_ACACIA => VanillaBlocks::ACACIA_SLAB(),
StringValues::WOOD_TYPE_BIRCH => VanillaBlocks::BIRCH_SLAB(),
StringValues::WOOD_TYPE_DARK_OAK => VanillaBlocks::DARK_OAK_SLAB(),
StringValues::WOOD_TYPE_JUNGLE => VanillaBlocks::JUNGLE_SLAB(),
StringValues::WOOD_TYPE_OAK => VanillaBlocks::OAK_SLAB(),
StringValues::WOOD_TYPE_SPRUCE => VanillaBlocks::SPRUCE_SLAB(),
default => throw $in->badValueException(BlockStateNames::WOOD_TYPE, $type),
};
}
}

View File

@ -56,14 +56,6 @@ use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Facing;
final class BlockStateSerializerHelper{
public static function encodeAllSidedLog(Wood $block) : Writer{
return Writer::create(Ids::WOOD)
->writeBool(BlockStateNames::STRIPPED_BIT, $block->isStripped())
->writePillarAxis($block->getAxis())
->writeLegacyWoodType($block->getWoodType());
}
public static function encodeButton(Button $block, Writer $out) : Writer{
return $out
->writeFacingDirection($block->getFacing())
@ -151,16 +143,6 @@ final class BlockStateSerializerHelper{
->writeBool(BlockStateNames::UPDATE_BIT, $block->isCheckDecay());
}
public static function encodeLeaves1(Leaves $block, string $type) : Writer{
return self::encodeLeaves($block, Writer::create(Ids::LEAVES)
->writeString(BlockStateNames::OLD_LEAF_TYPE, $type));
}
public static function encodeLeaves2(Leaves $block, string $type) : Writer{
return self::encodeLeaves($block, Writer::create(Ids::LEAVES2)
->writeString(BlockStateNames::NEW_LEAF_TYPE, $type));
}
public static function encodeLiquid(Liquid $block, string $stillId, string $flowingId) : Writer{
return Writer::create($block->isStill() ? $stillId : $flowingId)
->writeInt(BlockStateNames::LIQUID_DEPTH, $block->getDecay() | ($block->isFalling() ? 0x8 : 0));
@ -279,9 +261,4 @@ final class BlockStateSerializerHelper{
return $out
->writeHorizontalFacing($block->getFacing());
}
public static function encodeWoodenSlab(Slab $block, string $typeValue) : Writer{
return self::encodeSlab($block, Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB)
->writeString(BlockStateNames::WOOD_TYPE, $typeValue);
}
}

View File

@ -82,7 +82,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->registerFlatCoralDeserializers();
$this->registerCauldronDeserializers();
$this->registerFlatWoodBlockDeserializers();
$this->registerLegacyWoodBlockDeserializers();
$this->registerLeavesDeserializers();
$this->registerSimpleDeserializers();
$this->registerDeserializers();
@ -468,10 +467,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in));
$this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in));
$this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG());
$this->mapLog(Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD, fn() => Blocks::ACACIA_WOOD());
$this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE());
$this->mapSimple(Ids::ACACIA_PLANKS, fn() => Blocks::ACACIA_PLANKS());
$this->mapSlab(Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB, fn() => Blocks::ACACIA_SLAB());
$this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS());
//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));
@ -481,10 +481,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in));
$this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in));
$this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG());
$this->mapLog(Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD, fn() => Blocks::BIRCH_WOOD());
$this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE());
$this->mapSimple(Ids::BIRCH_PLANKS, fn() => Blocks::BIRCH_PLANKS());
$this->mapSlab(Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB, fn() => Blocks::BIRCH_SLAB());
$this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS());
//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));
@ -526,10 +527,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in));
$this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in));
$this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG());
$this->mapLog(Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD, fn() => Blocks::DARK_OAK_WOOD());
$this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE());
$this->mapSimple(Ids::DARK_OAK_PLANKS, fn() => Blocks::DARK_OAK_PLANKS());
$this->mapSlab(Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB, fn() => Blocks::DARK_OAK_SLAB());
$this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS());
//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));
@ -539,10 +541,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in));
$this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in));
$this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG());
$this->mapLog(Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD, fn() => Blocks::JUNGLE_WOOD());
$this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE());
$this->mapSimple(Ids::JUNGLE_PLANKS, fn() => Blocks::JUNGLE_PLANKS());
$this->mapSlab(Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB, fn() => Blocks::JUNGLE_SLAB());
$this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS());
//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));
@ -571,10 +574,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in));
$this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in));
$this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG());
$this->mapLog(Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD, fn() => Blocks::OAK_WOOD());
$this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE());
$this->mapSimple(Ids::OAK_PLANKS, fn() => Blocks::OAK_PLANKS());
$this->mapSlab(Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB, fn() => Blocks::OAK_SLAB());
$this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS());
//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));
@ -584,10 +588,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in));
$this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in));
$this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG());
$this->mapLog(Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD, fn() => Blocks::SPRUCE_WOOD());
$this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE());
$this->mapSimple(Ids::SPRUCE_PLANKS, fn() => Blocks::SPRUCE_PLANKS());
$this->mapSlab(Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB, fn() => Blocks::SPRUCE_SLAB());
$this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS());
//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));
@ -604,40 +609,17 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS());
}
private function registerLegacyWoodBlockDeserializers() : void{
$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::ACACIA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::ACACIA_LEAVES(), $in));
$this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in));
$this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in));
$this->map(Ids::BIRCH_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::BIRCH_LEAVES(), $in));
$this->map(Ids::CHERRY_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::CHERRY_LEAVES(), $in));
$this->map(Ids::DARK_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::DARK_OAK_LEAVES(), $in));
$this->map(Ids::JUNGLE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::JUNGLE_LEAVES(), $in));
$this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in));
//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));
$this->map(Ids::OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::OAK_LEAVES(), $in));
$this->map(Ids::SPRUCE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::SPRUCE_LEAVES(), $in));
}
private function registerSimpleDeserializers() : void{
@ -821,7 +803,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::GOLD_BLOCK, fn() => Blocks::GOLD());
$this->mapSimple(Ids::GOLD_ORE, fn() => Blocks::GOLD_ORE());
$this->mapSimple(Ids::GRANITE, fn() => Blocks::GRANITE());
$this->mapSimple(Ids::GRASS, fn() => Blocks::GRASS());
$this->mapSimple(Ids::GRASS_BLOCK, fn() => Blocks::GRASS());
$this->mapSimple(Ids::GRASS_PATH, fn() => Blocks::GRASS_PATH());
$this->mapSimple(Ids::GRAVEL, fn() => Blocks::GRAVEL());
$this->mapSimple(Ids::HANGING_ROOTS, fn() => Blocks::HANGING_ROOTS());

View File

@ -27,7 +27,6 @@ use pocketmine\block\utils\BellAttachmentType;
use pocketmine\block\utils\CoralType;
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;
@ -257,20 +256,6 @@ final class BlockStateWriter{
return $this;
}
/** @return $this */
public function writeLegacyWoodType(WoodType $treeType) : self{
$this->writeString(BlockStateNames::WOOD_TYPE, match($treeType){
WoodType::OAK => StringValues::WOOD_TYPE_OAK,
WoodType::SPRUCE => StringValues::WOOD_TYPE_SPRUCE,
WoodType::BIRCH => StringValues::WOOD_TYPE_BIRCH,
WoodType::JUNGLE => StringValues::WOOD_TYPE_JUNGLE,
WoodType::ACACIA => StringValues::WOOD_TYPE_ACACIA,
WoodType::DARK_OAK => StringValues::WOOD_TYPE_DARK_OAK,
default => throw new BlockStateSerializeException("Invalid legacy wood type " . $treeType->name)
});
return $this;
}
/** @return $this */
public function writeCoralType(CoralType $coralType) : self{
$this->writeString(BlockStateNames::CORAL_COLOR, match($coralType){

View File

@ -64,20 +64,24 @@ final class BlockStateUpgradeSchema{
*/
public array $remappedStates = [];
public readonly int $versionId;
public function __construct(
public int $maxVersionMajor,
public int $maxVersionMinor,
public int $maxVersionPatch,
public int $maxVersionRevision,
public readonly int $maxVersionMajor,
public readonly int $maxVersionMinor,
public readonly int $maxVersionPatch,
public readonly int $maxVersionRevision,
private int $schemaId
){}
){
$this->versionId = ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision;
}
/**
* @deprecated This is defined by Mojang, and therefore cannot be relied on. Use getSchemaId() instead for
* internal version management.
*/
public function getVersionId() : int{
return ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision;
return $this->versionId;
}
public function getSchemaId() : int{ return $this->schemaId; }

View File

@ -389,6 +389,9 @@ final class BlockStateUpgradeSchemaUtils{
}
$jsonMapper = new \JsonMapper();
$jsonMapper->bExceptionOnMissingData = true;
$jsonMapper->bExceptionOnUndefinedProperty = true;
$jsonMapper->bStrictObjectTypeChecking = true;
try{
$model = $jsonMapper->map($json, new BlockStateUpgradeSchemaModel());
}catch(\JsonMapper_Exception $e){

View File

@ -35,9 +35,14 @@ use function sprintf;
use const SORT_NUMERIC;
final class BlockStateUpgrader{
/** @var BlockStateUpgradeSchema[] */
/**
* @var BlockStateUpgradeSchema[][] versionId => [schemaId => schema]
* @phpstan-var array<int, array<int, BlockStateUpgradeSchema>>
*/
private array $upgradeSchemas = [];
private int $outputVersion = 0;
/**
* @param BlockStateUpgradeSchema[] $upgradeSchemas
* @phpstan-param array<int, BlockStateUpgradeSchema> $upgradeSchemas
@ -50,27 +55,76 @@ final class BlockStateUpgrader{
public function addSchema(BlockStateUpgradeSchema $schema) : void{
$schemaId = $schema->getSchemaId();
if(isset($this->upgradeSchemas[$schemaId])){
throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID");
$versionId = $schema->getVersionId();
if(isset($this->upgradeSchemas[$versionId][$schemaId])){
throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID and version ID");
}
$this->upgradeSchemas[$schemaId] = $schema;
//schema ID tells us the order when multiple schemas use the same version ID
$this->upgradeSchemas[$versionId][$schemaId] = $schema;
ksort($this->upgradeSchemas, SORT_NUMERIC);
ksort($this->upgradeSchemas[$versionId], SORT_NUMERIC);
$this->outputVersion = max($this->outputVersion, $schema->getVersionId());
}
public function upgrade(BlockStateData $blockStateData) : BlockStateData{
$version = $blockStateData->getVersion();
$highestVersion = $version;
foreach($this->upgradeSchemas as $schema){
$resultVersion = $schema->getVersionId();
$highestVersion = max($highestVersion, $resultVersion);
if($version > $resultVersion){
//even if this is actually the same version, we have to apply it anyway because mojang are dumb and
//didn't always bump the blockstate version when changing it :(
foreach($this->upgradeSchemas as $resultVersion => $schemaList){
/*
* Sometimes Mojang made changes without bumping the version ID.
* A notable example is 0131_1.18.20.27_beta_to_1.18.30.json, which renamed a bunch of blockIDs.
* When this happens, all the schemas must be applied even if the version is the same, because the input
* version doesn't tell us which of the schemas have already been applied.
* If there's only one schema for a version (the norm), we can safely assume it's already been applied if
* the version is the same, and skip over it.
*/
if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){
continue;
}
foreach($schemaList as $schema){
$blockStateData = $this->applySchema($schema, $blockStateData);
}
}
if($this->outputVersion > $version){
//always update the version number of the blockstate, even if it didn't change - this is needed for
//external tools
$blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $this->outputVersion);
}
return $blockStateData;
}
private function applySchema(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : BlockStateData{
$newStateData = $this->applyStateRemapped($schema, $blockStateData);
if($newStateData !== null){
return $newStateData;
}
$oldName = $blockStateData->getName();
$newName = $schema->renamedIds[$oldName] ?? null;
$stateChanges = 0;
$states = $blockStateData->getStates();
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
if($newName !== null || $stateChanges > 0){
return new BlockStateData($newName ?? $oldName, $states, $schema->getVersionId());
}
return $blockStateData;
}
private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{
$oldName = $blockStateData->getName();
$oldState = $blockStateData->getStates();
if(isset($schema->remappedStates[$oldName])){
foreach($schema->remappedStates[$oldName] as $remap){
if(count($remap->oldState) > count($oldState)){
@ -103,32 +157,11 @@ final class BlockStateUpgrader{
}
}
$blockStateData = new BlockStateData($newName, $newState, $resultVersion);
continue 2; //try next schema
}
}
$newName = $schema->renamedIds[$oldName] ?? null;
$stateChanges = 0;
$states = $blockStateData->getStates();
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
$states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
if($newName !== null || $stateChanges > 0){
$blockStateData = new BlockStateData($newName ?? $oldName, $states, $resultVersion);
//don't break out; we may need to further upgrade the state
return new BlockStateData($newName, $newState, $schema->getVersionId());
}
}
if($highestVersion > $version){
//always update the version number of the blockstate, even if it didn't change - this is needed for
//external tools
$blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $highestVersion);
}
return $blockStateData;
return null;
}
/**

View File

@ -305,6 +305,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::MUSIC_DISC_WAIT, Items::RECORD_WAIT());
$this->map1to1Item(Ids::MUSIC_DISC_WARD, Items::RECORD_WARD());
$this->map1to1Item(Ids::MUTTON, Items::RAW_MUTTON());
$this->map1to1Item(Ids::NAME_TAG, Items::NAME_TAG());
$this->map1to1Item(Ids::NAUTILUS_SHELL, Items::NAUTILUS_SHELL());
$this->map1to1Item(Ids::NETHER_STAR, Items::NETHER_STAR());
$this->map1to1Item(Ids::NETHERBRICK, Items::NETHER_BRICK());

View File

@ -74,6 +74,7 @@ final class ItemTypeNames{
public const BLEACH = "minecraft:bleach";
public const BLUE_DYE = "minecraft:blue_dye";
public const BOAT = "minecraft:boat";
public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg";
public const BONE = "minecraft:bone";
public const BONE_MEAL = "minecraft:bone_meal";
public const BOOK = "minecraft:book";
@ -286,6 +287,8 @@ final class ItemTypeNames{
public const LEATHER_HELMET = "minecraft:leather_helmet";
public const LEATHER_HORSE_ARMOR = "minecraft:leather_horse_armor";
public const LEATHER_LEGGINGS = "minecraft:leather_leggings";
public const LEAVES = "minecraft:leaves";
public const LEAVES2 = "minecraft:leaves2";
public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye";
public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye";
public const LIME_DYE = "minecraft:lime_dye";
@ -500,16 +503,19 @@ final class ItemTypeNames{
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 WIND_CHARGE = "minecraft:wind_charge";
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";
public const WOLF_ARMOR = "minecraft:wolf_armor";
public const WOLF_SPAWN_EGG = "minecraft:wolf_spawn_egg";
public const WOOD = "minecraft:wood";
public const WOODEN_AXE = "minecraft:wooden_axe";
public const WOODEN_DOOR = "minecraft:wooden_door";
public const WOODEN_HOE = "minecraft:wooden_hoe";
public const WOODEN_PICKAXE = "minecraft:wooden_pickaxe";
public const WOODEN_SHOVEL = "minecraft:wooden_shovel";
public const WOODEN_SLAB = "minecraft:wooden_slab";
public const WOODEN_SWORD = "minecraft:wooden_sword";
public const WOOL = "minecraft:wool";
public const WRITABLE_BOOK = "minecraft:writable_book";

View File

@ -88,6 +88,9 @@ final class ItemIdMetaUpgradeSchemaUtils{
}
$jsonMapper = new \JsonMapper();
$jsonMapper->bExceptionOnMissingData = true;
$jsonMapper->bExceptionOnUndefinedProperty = true;
$jsonMapper->bStrictObjectTypeChecking = true;
try{
$model = $jsonMapper->map($json, new ItemIdMetaUpgradeSchemaModel());
}catch(\JsonMapper_Exception $e){

View File

@ -252,6 +252,14 @@ abstract class Entity{
return $this->alwaysShowNameTag;
}
/**
* Returns whether players can rename this entity using a name tag.
* Note that plugins can still name entities using setNameTag().
*/
public function canBeRenamed() : bool{
return false;
}
public function setNameTag(string $name) : void{
$this->nameTag = $name;
$this->networkPropertiesDirty = true;
@ -792,7 +800,7 @@ abstract class Entity{
}
protected function broadcastMotion() : void{
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion())]);
NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->id, $this->getMotion(), tick: 0)]);
}
public function getGravity() : float{

View File

@ -132,6 +132,10 @@ abstract class Living extends Entity{
abstract public function getName() : string;
public function canBeRenamed() : bool{
return true;
}
protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt);

View File

@ -0,0 +1,106 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event\player;
use pocketmine\event\Event;
use pocketmine\player\PlayerInfo;
use pocketmine\resourcepacks\ResourcePack;
use function array_unshift;
/**
* Called after a player authenticates and is being offered resource packs to download.
*
* This event should be used to decide which resource packs to offer the player and whether to require the player to
* download the packs before they can join the server.
*/
class PlayerResourcePackOfferEvent extends Event{
/**
* @param ResourcePack[] $resourcePacks
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
*
* @phpstan-param list<ResourcePack> $resourcePacks
* @phpstan-param array<string, string> $encryptionKeys
*/
public function __construct(
private readonly PlayerInfo $playerInfo,
private array $resourcePacks,
private array $encryptionKeys,
private bool $mustAccept
){}
public function getPlayerInfo() : PlayerInfo{
return $this->playerInfo;
}
/**
* Adds a resource pack to the top of the stack.
* The resources in this pack will be applied over the top of any existing packs.
*/
public function addResourcePack(ResourcePack $entry, ?string $encryptionKey = null) : void{
array_unshift($this->resourcePacks, $entry);
if($encryptionKey !== null){
$this->encryptionKeys[$entry->getPackId()] = $encryptionKey;
}
}
/**
* Sets the resource packs to offer. Packs are applied from the highest key to the lowest, with each pack
* overwriting any resources from the previous pack. This means that the pack at index 0 gets the final say on which
* resources are used.
*
* @param ResourcePack[] $resourcePacks
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
*
* @phpstan-param list<ResourcePack> $resourcePacks
* @phpstan-param array<string, string> $encryptionKeys
*/
public function setResourcePacks(array $resourcePacks, array $encryptionKeys) : void{
$this->resourcePacks = $resourcePacks;
$this->encryptionKeys = $encryptionKeys;
}
/**
* @return ResourcePack[]
* @phpstan-return list<ResourcePack>
*/
public function getResourcePacks() : array{
return $this->resourcePacks;
}
/**
* @return string[]
* @phpstan-return array<string, string>
*/
public function getEncryptionKeys() : array{
return $this->encryptionKeys;
}
public function setMustAccept(bool $mustAccept) : void{
$this->mustAccept = $mustAccept;
}
public function mustAccept() : bool{
return $this->mustAccept;
}
}

View File

@ -323,8 +323,9 @@ final class ItemTypeIds{
public const EYE_ARMOR_TRIM_SMITHING_TEMPLATE = 20284;
public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = 20285;
public const PITCHER_POD = 20286;
public const NAME_TAG = 20287;
public const FIRST_UNUSED_ITEM_ID = 20287;
public const FIRST_UNUSED_ITEM_ID = 20288;
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

40
src/item/NameTag.php Normal file
View File

@ -0,0 +1,40 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Entity;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
class NameTag extends Item{
public function onInteractEntity(Player $player, Entity $entity, Vector3 $clickVector) : bool{
if($entity->canBeRenamed() && $this->hasCustomName()){
$entity->setNameTag($this->getCustomName());
$this->pop();
return true;
}
return false;
}
}

View File

@ -1384,6 +1384,7 @@ final class StringToItemParser extends StringToTParser{
$result->register("mutton_raw", fn() => Items::RAW_MUTTON());
$result->register("muttoncooked", fn() => Items::COOKED_MUTTON());
$result->register("muttonraw", fn() => Items::RAW_MUTTON());
$result->register("name_tag", fn() => Items::NAME_TAG());
$result->register("nautilus_shell", fn() => Items::NAUTILUS_SHELL());
$result->register("nether_brick", fn() => Items::NETHER_BRICK());
$result->register("nether_quartz", fn() => Items::NETHER_QUARTZ());

View File

@ -219,6 +219,7 @@ use function strtolower;
* @method static MilkBucket MILK_BUCKET()
* @method static Minecart MINECART()
* @method static MushroomStew MUSHROOM_STEW()
* @method static NameTag NAME_TAG()
* @method static Item NAUTILUS_SHELL()
* @method static Axe NETHERITE_AXE()
* @method static Armor NETHERITE_BOOTS()
@ -490,6 +491,7 @@ final class VanillaItems{
self::register("milk_bucket", new MilkBucket(new IID(Ids::MILK_BUCKET), "Milk Bucket"));
self::register("minecart", new Minecart(new IID(Ids::MINECART), "Minecart"));
self::register("mushroom_stew", new MushroomStew(new IID(Ids::MUSHROOM_STEW), "Mushroom Stew"));
self::register("name_tag", new NameTag(new IID(Ids::NAME_TAG), "Name Tag"));
self::register("nautilus_shell", new Item(new IID(Ids::NAUTILUS_SHELL), "Nautilus Shell"));
self::register("nether_brick", new Item(new IID(Ids::NETHER_BRICK), "Nether Brick"));
self::register("nether_quartz", new Item(new IID(Ids::NETHER_QUARTZ), "Nether Quartz"));

View File

@ -28,7 +28,6 @@ use pocketmine\network\mcpe\compression\Compressor;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\LevelChunkPacket;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\types\ChunkPosition;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\serializer\ChunkSerializer;
@ -72,11 +71,10 @@ class ChunkRequestTask extends AsyncTask{
$subCount = ChunkSerializer::getSubChunkCount($chunk, $dimensionId);
$converter = TypeConverter::getInstance();
$encoderContext = new PacketSerializerContext($converter->getItemTypeDictionary());
$payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter->getBlockTranslator(), $encoderContext, $this->tiles);
$payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter->getBlockTranslator(), $this->tiles);
$stream = new BinaryStream();
PacketBatch::encodePackets($stream, $encoderContext, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]);
PacketBatch::encodePackets($stream, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]);
$compressor = $this->compressor->deserialize();
$this->setResult(chr($compressor->getNetworkId()) . $compressor->compress($stream->getBuffer()));

View File

@ -423,6 +423,41 @@ class InventoryManager{
}
}
/**
* Compares itemstack extra data for equality. This is used to verify legacy InventoryTransaction slot predictions.
*
* TODO: It would be preferable if we didn't have to deserialize this, to improve performance and reduce attack
* surface. However, the raw data may not match due to differences in ordering. Investigate whether the
* client-provided NBT is consistently sorted.
*/
private function itemStackExtraDataEqual(ItemStack $left, ItemStack $right) : bool{
if($left->getRawExtraData() === $right->getRawExtraData()){
return true;
}
$typeConverter = $this->session->getTypeConverter();
$leftExtraData = $typeConverter->deserializeItemStackExtraData($left->getRawExtraData(), $left->getId());
$rightExtraData = $typeConverter->deserializeItemStackExtraData($right->getRawExtraData(), $right->getId());
$leftNbt = $leftExtraData->getNbt();
$rightNbt = $rightExtraData->getNbt();
return
$leftExtraData->getCanPlaceOn() === $rightExtraData->getCanPlaceOn() &&
$leftExtraData->getCanDestroy() === $rightExtraData->getCanDestroy() && (
$leftNbt === $rightNbt || //this covers null === null and fast object identity
($leftNbt !== null && $rightNbt !== null && $leftNbt->equals($rightNbt))
);
}
private function itemStacksEqual(ItemStack $left, ItemStack $right) : bool{
return
$left->getId() === $right->getId() &&
$left->getMeta() === $right->getMeta() &&
$left->getBlockRuntimeId() === $right->getBlockRuntimeId() &&
$left->getCount() === $right->getCount() &&
$this->itemStackExtraDataEqual($left, $right);
}
public function onSlotChange(Inventory $inventory, int $slot) : void{
$inventoryEntry = $this->inventories[spl_object_id($inventory)] ?? null;
if($inventoryEntry === null){
@ -432,7 +467,7 @@ class InventoryManager{
}
$currentItem = $this->session->getTypeConverter()->coreItemStackToNet($inventory->getItem($slot));
$clientSideItem = $inventoryEntry->predictions[$slot] ?? null;
if($clientSideItem === null || !$clientSideItem->equals($currentItem)){
if($clientSideItem === null || !$this->itemStacksEqual($currentItem, $clientSideItem)){
//no prediction or incorrect - do not associate this with the currently active itemstack request
$this->trackItemStack($inventoryEntry, $slot, $currentItem, null);
$inventoryEntry->pendingSyncs[$slot] = $currentItem;

View File

@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\event\player\PlayerDuplicateLoginEvent;
use pocketmine\event\player\PlayerResourcePackOfferEvent;
use pocketmine\event\server\DataPacketDecodeEvent;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\event\server\DataPacketSendEvent;
@ -67,7 +68,6 @@ use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\ServerboundPacket;
use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket;
use pocketmine\network\mcpe\protocol\SetDifficultyPacket;
@ -101,6 +101,8 @@ use pocketmine\player\Player;
use pocketmine\player\PlayerInfo;
use pocketmine\player\UsedChunkStatus;
use pocketmine\player\XboxLivePlayerInfo;
use pocketmine\promise\Promise;
use pocketmine\promise\PromiseResolver;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
@ -159,15 +161,24 @@ class NetworkSession{
/** @var string[] */
private array $sendBuffer = [];
/**
* @var \SplQueue|CompressBatchPromise[]|string[]
* @phpstan-var \SplQueue<CompressBatchPromise|string>
* @var PromiseResolver[]
* @phpstan-var list<PromiseResolver<true>>
*/
private array $sendBufferAckPromises = [];
/** @phpstan-var \SplQueue<array{CompressBatchPromise|string, list<PromiseResolver<true>>}> */
private \SplQueue $compressedQueue;
private bool $forceAsyncCompression = true;
private bool $enableCompression = false; //disabled until handshake completed
private int $nextAckReceiptId = 0;
/**
* @var PromiseResolver[][]
* @phpstan-var array<int, list<PromiseResolver<true>>>
*/
private array $ackPromisesByReceiptId = [];
private ?InventoryManager $invManager = null;
/**
@ -180,7 +191,6 @@ class NetworkSession{
private Server $server,
private NetworkSessionManager $manager,
private PacketPool $packetPool,
private PacketSerializerContext $packetSerializerContext,
private PacketSender $sender,
private PacketBroadcaster $broadcaster,
private EntityEventBroadcaster $entityEventBroadcaster,
@ -362,12 +372,12 @@ class NetworkSession{
}
if($this->enableCompression){
Timings::$playerNetworkReceiveDecompress->startTiming();
$compressionType = ord($payload[0]);
$compressed = substr($payload, 1);
if($compressionType === CompressionAlgorithm::NONE){
$decompressed = $compressed;
}elseif($compressionType === $this->compressor->getNetworkId()){
Timings::$playerNetworkReceiveDecompress->startTiming();
try{
$decompressed = $this->compressor->decompress($compressed);
}catch(DecompressionException $e){
@ -385,12 +395,8 @@ class NetworkSession{
try{
$stream = new BinaryStream($decompressed);
$count = 0;
foreach(PacketBatch::decodeRaw($stream) as $buffer){
$this->gamePacketLimiter->decrement();
if(++$count > 100){
throw new PacketHandlingException("Too many packets in batch");
}
$packet = $this->packetPool->getPacket($buffer);
if($packet === null){
$this->logger->debug("Unknown packet: " . base64_encode($buffer));
@ -435,7 +441,7 @@ class NetworkSession{
$decodeTimings = Timings::getDecodeDataPacketTimings($packet);
$decodeTimings->startTiming();
try{
$stream = PacketSerializer::decoder($buffer, 0, $this->packetSerializerContext);
$stream = PacketSerializer::decoder($buffer, 0);
try{
$packet->decode($stream);
}catch(PacketDecodeException $e){
@ -470,7 +476,23 @@ class NetworkSession{
}
}
public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{
public function handleAckReceipt(int $receiptId) : void{
if(!$this->connected){
return;
}
if(isset($this->ackPromisesByReceiptId[$receiptId])){
$promises = $this->ackPromisesByReceiptId[$receiptId];
unset($this->ackPromisesByReceiptId[$receiptId]);
foreach($promises as $promise){
$promise->resolve(true);
}
}
}
/**
* @phpstan-param PromiseResolver<true>|null $ackReceiptResolver
*/
private function sendDataPacketInternal(ClientboundPacket $packet, bool $immediate, ?PromiseResolver $ackReceiptResolver) : bool{
if(!$this->connected){
return false;
}
@ -493,8 +515,11 @@ class NetworkSession{
$packets = [$packet];
}
if($ackReceiptResolver !== null){
$this->sendBufferAckPromises[] = $ackReceiptResolver;
}
foreach($packets as $evPacket){
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket));
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder(), $evPacket));
}
if($immediate){
$this->flushSendBuffer(true);
@ -506,6 +531,23 @@ class NetworkSession{
}
}
public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{
return $this->sendDataPacketInternal($packet, $immediate, null);
}
/**
* @phpstan-return Promise<true>
*/
public function sendDataPacketWithReceipt(ClientboundPacket $packet, bool $immediate = false) : Promise{
$resolver = new PromiseResolver();
if(!$this->sendDataPacketInternal($packet, $immediate, $resolver)){
$resolver->reject();
}
return $resolver->getPromise();
}
/**
* @internal
*/
@ -547,15 +589,15 @@ class NetworkSession{
$batch = $stream->getBuffer();
}
$this->sendBuffer = [];
$this->queueCompressedNoBufferFlush($batch, $immediate);
$ackPromises = $this->sendBufferAckPromises;
$this->sendBufferAckPromises = [];
$this->queueCompressedNoBufferFlush($batch, $immediate, $ackPromises);
}finally{
Timings::$playerNetworkSend->stopTiming();
}
}
}
public function getPacketSerializerContext() : PacketSerializerContext{ return $this->packetSerializerContext; }
public function getBroadcaster() : PacketBroadcaster{ return $this->broadcaster; }
public function getEntityEventBroadcaster() : EntityEventBroadcaster{ return $this->entityEventBroadcaster; }
@ -576,22 +618,27 @@ class NetworkSession{
}
}
private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false) : void{
/**
* @param PromiseResolver[] $ackPromises
*
* @phpstan-param list<PromiseResolver<true>> $ackPromises
*/
private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false, array $ackPromises = []) : void{
Timings::$playerNetworkSend->startTiming();
try{
if(is_string($batch)){
if($immediate){
//Skips all queues
$this->sendEncoded($batch, true);
$this->sendEncoded($batch, true, $ackPromises);
}else{
$this->compressedQueue->enqueue($batch);
$this->compressedQueue->enqueue([$batch, $ackPromises]);
$this->flushCompressedQueue();
}
}elseif($immediate){
//Skips all queues
$this->sendEncoded($batch->getResult(), true);
$this->sendEncoded($batch->getResult(), true, $ackPromises);
}else{
$this->compressedQueue->enqueue($batch);
$this->compressedQueue->enqueue([$batch, $ackPromises]);
$batch->onResolve(function() : void{
if($this->connected){
$this->flushCompressedQueue();
@ -608,14 +655,14 @@ class NetworkSession{
try{
while(!$this->compressedQueue->isEmpty()){
/** @var CompressBatchPromise|string $current */
$current = $this->compressedQueue->bottom();
[$current, $ackPromises] = $this->compressedQueue->bottom();
if(is_string($current)){
$this->compressedQueue->dequeue();
$this->sendEncoded($current);
$this->sendEncoded($current, false, $ackPromises);
}elseif($current->hasResult()){
$this->compressedQueue->dequeue();
$this->sendEncoded($current->getResult());
$this->sendEncoded($current->getResult(), false, $ackPromises);
}else{
//can't send any more queued until this one is ready
@ -627,13 +674,24 @@ class NetworkSession{
}
}
private function sendEncoded(string $payload, bool $immediate = false) : void{
/**
* @param PromiseResolver[] $ackPromises
* @phpstan-param list<PromiseResolver<true>> $ackPromises
*/
private function sendEncoded(string $payload, bool $immediate, array $ackPromises) : void{
if($this->cipher !== null){
Timings::$playerNetworkSendEncrypt->startTiming();
$payload = $this->cipher->encrypt($payload);
Timings::$playerNetworkSendEncrypt->stopTiming();
}
$this->sender->send($payload, $immediate);
if(count($ackPromises) > 0){
$ackReceiptId = $this->nextAckReceiptId++;
$this->ackPromisesByReceiptId[$ackReceiptId] = $ackPromises;
}else{
$ackReceiptId = null;
}
$this->sender->send($payload, $immediate, $ackReceiptId);
}
/**
@ -653,6 +711,19 @@ class NetworkSession{
$this->setHandler(null);
$this->connected = false;
$ackPromisesByReceiptId = $this->ackPromisesByReceiptId;
$this->ackPromisesByReceiptId = [];
foreach($ackPromisesByReceiptId as $resolvers){
foreach($resolvers as $resolver){
$resolver->reject();
}
}
$sendBufferAckPromises = $this->sendBufferAckPromises;
$this->sendBufferAckPromises = [];
foreach($sendBufferAckPromises as $resolver){
$resolver->reject();
}
$this->logger->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_network_session_close($reason)));
}
}
@ -848,7 +919,19 @@ class NetworkSession{
$this->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_SUCCESS));
$this->logger->debug("Initiating resource packs phase");
$this->setHandler(new ResourcePacksPacketHandler($this, $this->server->getResourcePackManager(), function() : void{
$packManager = $this->server->getResourcePackManager();
$resourcePacks = $packManager->getResourceStack();
$keys = [];
foreach($resourcePacks as $resourcePack){
$key = $packManager->getPackEncryptionKey($resourcePack->getPackId());
if($key !== null){
$keys[$resourcePack->getPackId()] = $key;
}
}
$event = new PlayerResourcePackOfferEvent($this->info, $resourcePacks, $keys, $packManager->resourcePacksRequired());
$event->call();
$this->setHandler(new ResourcePacksPacketHandler($this, $event->getResourcePacks(), $event->getEncryptionKeys(), $event->mustAccept(), function() : void{
$this->createPlayer();
}));
}

View File

@ -28,7 +28,7 @@ interface PacketSender{
/**
* Pushes a packet into the channel to be processed.
*/
public function send(string $payload, bool $immediate) : void;
public function send(string $payload, bool $immediate, ?int $receiptId) : void;
/**
* Closes the channel, terminating the connection.

View File

@ -87,12 +87,13 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
EffectIdMap::getInstance()->toId($effect->getType()),
$effect->getAmplifier(),
$effect->isVisible(),
$effect->getDuration()
$effect->getDuration(),
tick: 0
));
}
public function onEntityEffectRemoved(array $recipients, Living $entity, EffectInstance $effect) : void{
$this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType())));
$this->sendDataPacket($recipients, MobEffectPacket::remove($entity->getId(), EffectIdMap::getInstance()->toId($effect->getType()), tick: 0));
}
public function onEntityRemoved(array $recipients, Entity $entity) : void{

View File

@ -26,7 +26,6 @@ namespace pocketmine\network\mcpe;
use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\utils\BinaryStream;
@ -37,8 +36,7 @@ use function strlen;
final class StandardPacketBroadcaster implements PacketBroadcaster{
public function __construct(
private Server $server,
private PacketSerializerContext $protocolContext
private Server $server
){}
public function broadcastPackets(array $recipients, array $packets) : void{
@ -58,10 +56,6 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
/** @var NetworkSession[][] $targetsByCompressor */
$targetsByCompressor = [];
foreach($recipients as $recipient){
if($recipient->getPacketSerializerContext() !== $this->protocolContext){
throw new \InvalidArgumentException("Only recipients with the same protocol context as the broadcaster can be broadcast to by this broadcaster");
}
//TODO: different compressors might be compatible, it might not be necessary to split them up by object
$compressor = $recipient->getCompressor();
$compressors[spl_object_id($compressor)] = $compressor;
@ -72,7 +66,7 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
$totalLength = 0;
$packetBuffers = [];
foreach($packets as $packet){
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($this->protocolContext), $packet);
$buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder(), $packet);
//varint length prefix + packet buffer
$totalLength += (((int) log(strlen($buffer), 128)) + 1) + strlen($buffer);
$packetBuffers[] = $buffer;

View File

@ -124,6 +124,7 @@ class ProcessLoginTask extends AsyncTask{
$mapper = new \JsonMapper();
$mapper->bExceptionOnMissingData = true;
$mapper->bExceptionOnUndefinedProperty = true;
$mapper->bStrictObjectTypeChecking = true;
$mapper->bEnforceMapType = false;
try{
@ -167,6 +168,7 @@ class ProcessLoginTask extends AsyncTask{
$mapper = new \JsonMapper();
$mapper->bExceptionOnUndefinedProperty = false; //we only care about the properties we're using in this case
$mapper->bExceptionOnMissingData = true;
$mapper->bStrictObjectTypeChecking = true;
$mapper->bEnforceMapType = false;
$mapper->bRemoveUndefinedAttributes = true;
try{

View File

@ -38,7 +38,7 @@ final class ZlibCompressor implements Compressor{
public const DEFAULT_LEVEL = 7;
public const DEFAULT_THRESHOLD = 256;
public const DEFAULT_MAX_DECOMPRESSION_SIZE = 2 * 1024 * 1024;
public const DEFAULT_MAX_DECOMPRESSION_SIZE = 8 * 1024 * 1024;
/**
* @see SingletonTrait::make()

View File

@ -30,13 +30,17 @@ use pocketmine\crafting\RecipeIngredient;
use pocketmine\crafting\TagWildcardRecipeIngredient;
use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\data\bedrock\item\BlockItemIdMap;
use pocketmine\data\bedrock\item\ItemTypeNames;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\types\GameMode as ProtocolGameMode;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraDataShield;
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient as ProtocolRecipeIngredient;
use pocketmine\network\mcpe\protocol\types\recipe\StringIdMetaItemDescriptor;
@ -76,7 +80,7 @@ class TypeConverter{
);
$this->itemTypeDictionary = ItemTypeDictionaryFromDataHelper::loadFromString(Filesystem::fileGetContents(BedrockDataFiles::REQUIRED_ITEM_LIST_JSON));
$this->shieldRuntimeId = $this->itemTypeDictionary->fromStringId("minecraft:shield");
$this->shieldRuntimeId = $this->itemTypeDictionary->fromStringId(ItemTypeNames::SHIELD);
$this->itemTranslator = new ItemTranslator(
$this->itemTypeDictionary,
@ -217,26 +221,36 @@ class TypeConverter{
[$id, $meta, $blockRuntimeId] = $idMeta;
}
$extraData = $id === $this->shieldRuntimeId ?
new ItemStackExtraDataShield($nbt, canPlaceOn: [], canDestroy: [], blockingTick: 0) :
new ItemStackExtraData($nbt, canPlaceOn: [], canDestroy: []);
$extraDataSerializer = PacketSerializer::encoder();
$extraData->write($extraDataSerializer);
return new ItemStack(
$id,
$meta,
$itemStack->getCount(),
$blockRuntimeId ?? ItemTranslator::NO_BLOCK_RUNTIME_ID,
$nbt,
[],
[],
$id === $this->shieldRuntimeId ? 0 : null
$extraDataSerializer->getBuffer(),
);
}
/**
* WARNING: Avoid this in server-side code. If you need to compare ItemStacks provided by the client to the
* server, prefer encoding the server's itemstack and comparing the serialized ItemStack, instead of converting
* the client's ItemStack to a core Item.
* This method will fully decode the item's extra data, which can be very costly if the item has a lot of NBT data.
*
* @throws TypeConversionException
*/
public function netItemStackToCore(ItemStack $itemStack) : Item{
if($itemStack->getId() === 0){
return VanillaItems::AIR();
}
$compound = $itemStack->getNbt();
$extraData = $this->deserializeItemStackExtraData($itemStack->getRawExtraData(), $itemStack->getId());
$compound = $extraData->getNbt();
$itemResult = $this->itemTranslator->fromNetworkId($itemStack->getId(), $itemStack->getMeta(), $itemStack->getBlockRuntimeId());
@ -255,4 +269,11 @@ class TypeConverter{
return $itemResult;
}
public function deserializeItemStackExtraData(string $extraData, int $id) : ItemStackExtraData{
$extraDataDeserializer = PacketSerializer::decoder($extraData, 0);
return $id === $this->shieldRuntimeId ?
ItemStackExtraDataShield::read($extraDataDeserializer) :
ItemStackExtraData::read($extraDataDeserializer);
}
}

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\handler;
use pocketmine\block\BaseSign;
use pocketmine\block\ItemFrame;
use pocketmine\block\Lectern;
use pocketmine\block\tile\Sign;
use pocketmine\block\utils\SignText;
@ -60,7 +59,6 @@ use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\EmotePacket;
use pocketmine\network\mcpe\protocol\InteractPacket;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
use pocketmine\network\mcpe\protocol\ItemStackRequestPacket;
use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
use pocketmine\network\mcpe\protocol\LabTablePacket;
@ -444,9 +442,18 @@ class InGamePacketHandler extends PacketHandler{
return false;
}
$serverItemStack = $this->session->getTypeConverter()->coreItemStackToNet($sourceSlotItem);
//because the client doesn't tell us the expected itemstack ID, we have to deep-compare our known
//itemstack info with the one the client sent. This is costly, but we don't have any other option :(
if(!$serverItemStack->equals($clientItemStack)){
//Sadly we don't have itemstack IDs here, so we have to compare the basic item properties to ensure that we're
//dropping the item the client expects (inventory might be out of sync with the client).
if(
$serverItemStack->getId() !== $clientItemStack->getId() ||
$serverItemStack->getMeta() !== $clientItemStack->getMeta() ||
$serverItemStack->getCount() !== $clientItemStack->getCount() ||
$serverItemStack->getBlockRuntimeId() !== $clientItemStack->getBlockRuntimeId()
//Raw extraData may not match because of TAG_Compound key ordering differences, and decoding it to compare
//is costly. Assume that we're in sync if id+meta+count+runtimeId match.
//NB: Make sure $clientItemStack isn't used to create the dropped item, as that would allow the client
//to change the item NBT since we're not validating it.
){
return false;
}
@ -799,15 +806,6 @@ class InGamePacketHandler extends PacketHandler{
return true;
}
public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{
$blockPosition = $packet->blockPosition;
$block = $this->player->getWorld()->getBlockAt($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ());
if($block instanceof ItemFrame && $block->getFramedItem() !== null){
return $this->player->attackBlock(new Vector3($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ()), $block->getFacing());
}
return false;
}
public function handleBossEvent(BossEventPacket $packet) : bool{
return false; //TODO
}
@ -869,8 +867,12 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleBookEdit(BookEditPacket $packet) : bool{
$inventory = $this->player->getInventory();
if(!$inventory->slotExists($packet->inventorySlot)){
return false;
}
//TODO: break this up into book API things
$oldBook = $this->player->getInventory()->getItem($packet->inventorySlot);
$oldBook = $inventory->getItem($packet->inventorySlot);
if(!($oldBook instanceof WritableBook)){
return false;
}
@ -985,11 +987,6 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleLecternUpdate(LecternUpdatePacket $packet) : bool{
if($packet->dropBook){
//Drop book is handled with an interact event on use item transaction
return true;
}
$pos = $packet->blockPosition;
$chunkX = $pos->getX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $pos->getZ() >> Chunk::COORD_BIT_SIZE;

View File

@ -169,6 +169,7 @@ class LoginPacketHandler extends PacketHandler{
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
$mapper->bExceptionOnMissingData = true;
$mapper->bExceptionOnUndefinedProperty = true;
$mapper->bStrictObjectTypeChecking = true;
try{
/** @var AuthenticationData $extraData */
$extraData = $mapper->map($claims["extraData"], new AuthenticationData());
@ -197,6 +198,7 @@ class LoginPacketHandler extends PacketHandler{
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
$mapper->bExceptionOnMissingData = true;
$mapper->bExceptionOnUndefinedProperty = true;
$mapper->bStrictObjectTypeChecking = true;
try{
$clientData = $mapper->map($clientDataClaims, new ClientData());
}catch(\JsonMapper_Exception $e){

View File

@ -37,12 +37,13 @@ use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry;
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry;
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType;
use pocketmine\resourcepacks\ResourcePack;
use pocketmine\resourcepacks\ResourcePackManager;
use function array_keys;
use function array_map;
use function ceil;
use function count;
use function implode;
use function strpos;
use function strtolower;
use function substr;
/**
@ -50,37 +51,77 @@ use function substr;
* packs to the client.
*/
class ResourcePacksPacketHandler extends PacketHandler{
private const PACK_CHUNK_SIZE = 128 * 1024; //128KB
private const PACK_CHUNK_SIZE = 256 * 1024; //256KB
/**
* Larger values allow downloading more chunks at the same time, increasing download speed, but the client may choke
* and cause the download speed to drop (due to ACKs taking too long to arrive).
*/
private const MAX_CONCURRENT_CHUNK_REQUESTS = 1;
/**
* @var ResourcePack[]
* @phpstan-var array<string, ResourcePack>
*/
private array $resourcePacksById = [];
/** @var bool[][] uuid => [chunk index => hasSent] */
private array $downloadedChunks = [];
/** @phpstan-var \SplQueue<array{ResourcePack, int}> */
private \SplQueue $requestQueue;
private int $activeRequests = 0;
/**
* @param ResourcePack[] $resourcePackStack
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
*
* @phpstan-param list<ResourcePack> $resourcePackStack
* @phpstan-param array<string, string> $encryptionKeys
* @phpstan-param \Closure() : void $completionCallback
*/
public function __construct(
private NetworkSession $session,
private ResourcePackManager $resourcePackManager,
private array $resourcePackStack,
private array $encryptionKeys,
private bool $mustAccept,
private \Closure $completionCallback
){}
){
$this->requestQueue = new \SplQueue();
foreach($resourcePackStack as $pack){
$this->resourcePacksById[$pack->getPackId()] = $pack;
}
}
private function getPackById(string $id) : ?ResourcePack{
return $this->resourcePacksById[strtolower($id)] ?? null;
}
public function setUp() : void{
$resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{
//TODO: more stuff
$encryptionKey = $this->resourcePackManager->getPackEncryptionKey($pack->getPackId());
return new ResourcePackInfoEntry(
$pack->getPackId(),
$pack->getPackVersion(),
$pack->getPackSize(),
$encryptionKey ?? "",
$this->encryptionKeys[$pack->getPackId()] ?? "",
"",
$pack->getPackId(),
false
);
}, $this->resourcePackManager->getResourceStack());
}, $this->resourcePackStack);
//TODO: support forcing server packs
$this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false, []));
$this->session->sendDataPacket(ResourcePacksInfoPacket::create(
resourcePackEntries: $resourcePackEntries,
behaviorPackEntries: [],
mustAccept: $this->mustAccept,
hasAddons: false,
hasScripts: false,
forceServerPacks: false,
cdnUrls: []
));
$this->session->getLogger()->debug("Waiting for client to accept resource packs");
}
@ -104,11 +145,11 @@ class ResourcePacksPacketHandler extends PacketHandler{
if($splitPos !== false){
$uuid = substr($uuid, 0, $splitPos);
}
$pack = $this->resourcePackManager->getPackById($uuid);
$pack = $this->getPackById($uuid);
if(!($pack instanceof ResourcePack)){
//Client requested a resource pack but we don't have it available on the server
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
return false;
}
@ -128,7 +169,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
$stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{
return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks
}, $this->resourcePackManager->getResourceStack());
}, $this->resourcePackStack);
//we support chemistry blocks by default, the client should already have this installed
$stack[] = new ResourcePackStackEntry("0fba4063-dba1-4281-9b89-ff9390653530", "1.0.0", "");
@ -151,9 +192,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
}
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
$pack = $this->resourcePackManager->getPackById($packet->packId);
$pack = $this->getPackById($packet->packId);
if(!($pack instanceof ResourcePack)){
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
return false;
}
@ -176,8 +217,37 @@ class ResourcePacksPacketHandler extends PacketHandler{
$this->downloadedChunks[$packId][$packet->chunkIndex] = true;
}
$this->session->sendDataPacket(ResourcePackChunkDataPacket::create($packId, $packet->chunkIndex, $offset, $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE)));
$this->requestQueue->enqueue([$pack, $packet->chunkIndex]);
$this->processChunkRequestQueue();
return true;
}
private function processChunkRequestQueue() : void{
if($this->activeRequests >= self::MAX_CONCURRENT_CHUNK_REQUESTS || $this->requestQueue->isEmpty()){
return;
}
/**
* @var ResourcePack $pack
* @var int $chunkIndex
*/
[$pack, $chunkIndex] = $this->requestQueue->dequeue();
$packId = $pack->getPackId();
$offset = $chunkIndex * self::PACK_CHUNK_SIZE;
$chunkData = $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE);
$this->activeRequests++;
$this->session
->sendDataPacketWithReceipt(ResourcePackChunkDataPacket::create($packId, $chunkIndex, $offset, $chunkData))
->onCompletion(
function() : void{
$this->activeRequests--;
$this->processChunkRequestQueue();
},
function() : void{
//this may have been rejected because of a disconnection - this will do nothing in that case
$this->disconnectWithError("Plugin interrupted sending of resource packs");
}
);
}
}

View File

@ -33,7 +33,6 @@ use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\PacketBroadcaster;
use pocketmine\network\mcpe\protocol\PacketPool;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\Network;
use pocketmine\network\NetworkInterfaceStartException;
use pocketmine\network\PacketHandlingException;
@ -84,7 +83,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
private PacketBroadcaster $packetBroadcaster;
private EntityEventBroadcaster $entityEventBroadcaster;
private PacketSerializerContext $packetSerializerContext;
private TypeConverter $typeConverter;
public function __construct(
@ -94,12 +92,10 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
bool $ipV6,
PacketBroadcaster $packetBroadcaster,
EntityEventBroadcaster $entityEventBroadcaster,
PacketSerializerContext $packetSerializerContext,
TypeConverter $typeConverter
){
$this->server = $server;
$this->packetBroadcaster = $packetBroadcaster;
$this->packetSerializerContext = $packetSerializerContext;
$this->entityEventBroadcaster = $entityEventBroadcaster;
$this->typeConverter = $typeConverter;
@ -192,7 +188,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->server,
$this->network->getSessionManager(),
PacketPool::getInstance(),
$this->packetSerializerContext,
new RakLibPacketSender($sessionId, $this),
$this->packetBroadcaster,
$this->entityEventBroadcaster,
@ -257,7 +252,9 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
}
public function onPacketAck(int $sessionId, int $identifierACK) : void{
if(isset($this->sessions[$sessionId])){
$this->sessions[$sessionId]->handleAckReceipt($identifierACK);
}
}
public function setName(string $name) : void{
@ -294,12 +291,13 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->network->getBandwidthTracker()->add($bytesSentDiff, $bytesReceivedDiff);
}
public function putPacket(int $sessionId, string $payload, bool $immediate = true) : void{
public function putPacket(int $sessionId, string $payload, bool $immediate = true, ?int $receiptId = null) : void{
if(isset($this->sessions[$sessionId])){
$pk = new EncapsulatedPacket();
$pk->buffer = self::MCPE_RAKNET_PACKET_ID . $payload;
$pk->reliability = PacketReliability::RELIABLE_ORDERED;
$pk->orderChannel = 0;
$pk->identifierACK = $receiptId;
$this->interface->sendEncapsulated($sessionId, $pk, $immediate);
}

View File

@ -33,9 +33,9 @@ class RakLibPacketSender implements PacketSender{
private RakLibInterface $handler
){}
public function send(string $payload, bool $immediate) : void{
public function send(string $payload, bool $immediate, ?int $receiptId) : void{
if(!$this->closed){
$this->handler->putPacket($this->sessionId, $payload, $immediate);
$this->handler->putPacket($this->sessionId, $payload, $immediate, $receiptId);
}
}

View File

@ -96,7 +96,8 @@ class RakLibServer extends Thread{
new SimpleProtocolAcceptor($this->protocolVersion),
new UserToRakLibThreadMessageReceiver(new PthreadsChannelReader($this->mainToThreadBuffer)),
new RakLibToUserThreadMessageSender(new SnoozeAwarePthreadsChannelWriter($this->threadToMainBuffer, $this->sleeperEntry->createNotifier())),
new ExceptionTraceCleaner($this->mainPath)
new ExceptionTraceCleaner($this->mainPath),
recvMaxSplitParts: 512
);
$this->synchronized(function() : void{
$this->ready = true;

View File

@ -30,7 +30,6 @@ use pocketmine\nbt\TreeRoot;
use pocketmine\network\mcpe\convert\BlockTranslator;
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
@ -84,8 +83,8 @@ final class ChunkSerializer{
/**
* @phpstan-param DimensionIds::* $dimensionId
*/
public static function serializeFullChunk(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{
$stream = PacketSerializer::encoder($encoderContext);
public static function serializeFullChunk(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, ?string $tiles = null) : string{
$stream = PacketSerializer::encoder();
$subChunkCount = self::getSubChunkCount($chunk, $dimensionId);
$writtenCount = 0;

View File

@ -635,6 +635,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->displayName = $ev->getNewName();
}
public function canBeRenamed() : bool{
return false;
}
/**
* Returns the player's locale, e.g. en_US.
*/
@ -1394,7 +1398,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
public function setMotion(Vector3 $motion) : bool{
if(parent::setMotion($motion)){
$this->broadcastMotion();
$this->getNetworkSession()->sendDataPacket(SetActorMotionPacket::create($this->id, $motion));
$this->getNetworkSession()->sendDataPacket(SetActorMotionPacket::create($this->id, $motion, tick: 0));
return true;
}

View File

@ -637,6 +637,11 @@ class PluginManager{
$handlerName = Utils::getNiceClosureName($handler);
$reflect = new \ReflectionFunction($handler);
if($reflect->isGenerator()){
throw new PluginException("Generator function $handlerName cannot be used as an event handler");
}
if(!$plugin->isEnabled()){
throw new PluginException("Plugin attempted to register event handler " . $handlerName . "() to event " . $event . " while not enabled");
}

View File

@ -108,6 +108,7 @@ class ZippedResourcePack implements ResourcePack{
$mapper = new \JsonMapper();
$mapper->bExceptionOnMissingData = true;
$mapper->bStrictObjectTypeChecking = true;
try{
/** @var Manifest $manifest */

View File

@ -55,6 +55,7 @@ class UpdateCheckTask extends AsyncTask{
}else{
$mapper = new \JsonMapper();
$mapper->bExceptionOnMissingData = true;
$mapper->bStrictObjectTypeChecking = true;
$mapper->bEnforceMapType = false;
try{
/** @var UpdateInfo $responseObj */

View File

@ -111,6 +111,13 @@ trait RegistryTrait{
if(count($arguments) > 0){
throw new \ArgumentCountError("Expected exactly 0 arguments, " . count($arguments) . " passed");
}
//fast path
if(self::$members !== null && isset(self::$members[$name])){
return self::preprocessMember(self::$members[$name]);
}
//fallback
try{
return self::_registryFromString($name);
}catch(\InvalidArgumentException $e){

View File

@ -173,16 +173,17 @@ final class Utils{
}
/**
* @phpstan-template T of object
* @phpstan-template TKey of array-key
* @phpstan-template TValue of object
*
* @param object[] $array
* @phpstan-param T[] $array
* @phpstan-param array<TKey, TValue> $array
*
* @return object[]
* @phpstan-return T[]
* @phpstan-return array<TKey, TValue>
*/
public static function cloneObjectArray(array $array) : array{
/** @phpstan-var \Closure(T) : T $callback */
/** @phpstan-var \Closure(TValue) : TValue $callback */
$callback = self::cloneCallback();
return array_map($callback, $array);
}

View File

@ -31,7 +31,9 @@ use pocketmine\world\format\io\exception\CorruptedWorldException;
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
use pocketmine\world\format\PalettedBlockArray;
use pocketmine\world\WorldException;
use function count;
use function file_exists;
use function implode;
abstract class BaseWorldProvider implements WorldProvider{
protected WorldData $worldData;
@ -62,27 +64,35 @@ abstract class BaseWorldProvider implements WorldProvider{
*/
abstract protected function loadLevelData() : WorldData;
private function translatePalette(PalettedBlockArray $blockArray) : PalettedBlockArray{
private function translatePalette(PalettedBlockArray $blockArray, \Logger $logger) : PalettedBlockArray{
$palette = $blockArray->getPalette();
$newPalette = [];
$blockDecodeErrors = [];
foreach($palette as $k => $legacyIdMeta){
//TODO: remember data for unknown states so we can implement them later
$id = $legacyIdMeta >> 4;
$meta = $legacyIdMeta & 0xf;
try{
$newStateData = $this->blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf);
$newStateData = $this->blockDataUpgrader->upgradeIntIdMeta($id, $meta);
}catch(BlockStateDeserializeException $e){
$blockDecodeErrors[] = "Palette offset $k / Failed to upgrade legacy ID/meta $id:$meta: " . $e->getMessage();
$newStateData = GlobalBlockStateHandlers::getUnknownBlockStateData();
}
try{
$newPalette[$k] = $this->blockStateDeserializer->deserialize($newStateData);
}catch(BlockStateDeserializeException){
//TODO: this needs to be logged
//TODO: maybe we can remember unknown states for later saving instead of discarding them and destroying maps...
}catch(BlockStateDeserializeException $e){
//this should never happen anyway - if the upgrader returned an invalid state, we have bigger problems
$blockDecodeErrors[] = "Palette offset $k / Failed to deserialize upgraded state $id:$meta: " . $e->getMessage();
$newPalette[$k] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
}
}
if(count($blockDecodeErrors) > 0){
$logger->error("Errors decoding/upgrading blocks:\n - " . implode("\n - ", $blockDecodeErrors));
}
//TODO: this is sub-optimal since it reallocates the offset table multiple times
return PalettedBlockArray::fromData(
$blockArray->getBitsPerBlock(),
@ -91,16 +101,16 @@ abstract class BaseWorldProvider implements WorldProvider{
);
}
protected function palettizeLegacySubChunkXZY(string $idArray, string $metaArray) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkXZY($idArray, $metaArray));
protected function palettizeLegacySubChunkXZY(string $idArray, string $metaArray, \Logger $logger) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkXZY($idArray, $metaArray), $logger);
}
protected function palettizeLegacySubChunkYZX(string $idArray, string $metaArray) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkYZX($idArray, $metaArray));
protected function palettizeLegacySubChunkYZX(string $idArray, string $metaArray, \Logger $logger) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkYZX($idArray, $metaArray), $logger);
}
protected function palettizeLegacySubChunkFromColumn(string $idArray, string $metaArray, int $yOffset) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkFromLegacyColumn($idArray, $metaArray, $yOffset));
protected function palettizeLegacySubChunkFromColumn(string $idArray, string $metaArray, int $yOffset, \Logger $logger) : PalettedBlockArray{
return $this->translatePalette(SubChunkConverter::convertSubChunkFromLegacyColumn($idArray, $metaArray, $yOffset), $logger);
}
public function getPath() : string{

View File

@ -51,12 +51,12 @@ use function time;
class BedrockWorldData extends BaseNbtWorldData{
public const CURRENT_STORAGE_VERSION = 10;
public const CURRENT_STORAGE_NETWORK_VERSION = 649;
public const CURRENT_STORAGE_NETWORK_VERSION = 662;
public const CURRENT_CLIENT_VERSION_TARGET = [
1, //major
20, //minor
60, //patch
4, //revision
71, //patch
1, //revision
0 //is beta
];

View File

@ -26,6 +26,7 @@ namespace pocketmine\world\format\io\leveldb;
use pocketmine\block\Block;
use pocketmine\data\bedrock\BiomeIds;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\convert\UnsupportedBlockStateException;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NbtDataException;
@ -58,6 +59,7 @@ use function count;
use function defined;
use function extension_loaded;
use function file_exists;
use function implode;
use function is_dir;
use function mkdir;
use function ord;
@ -184,6 +186,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$paletteSize = $stream->getLInt();
}
$blockDecodeErrors = [];
for($i = 0; $i < $paletteSize; ++$i){
try{
$offset = $stream->getOffset();
@ -199,18 +203,25 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt);
}catch(BlockStateDeserializeException $e){
//while not ideal, this is not a fatal error
$logger->error("Failed to upgrade blockstate: " . $e->getMessage() . " offset $i in palette, blockstate NBT: " . $blockStateNbt->toString());
$blockDecodeErrors[] = "Palette offset $i / Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
continue;
}
try{
$palette[] = $this->blockStateDeserializer->deserialize($blockStateData);
}catch(UnsupportedBlockStateException $e){
$blockDecodeErrors[] = "Palette offset $i / " . $e->getMessage();
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
}catch(BlockStateDeserializeException $e){
$logger->error("Failed to deserialize blockstate: " . $e->getMessage() . " offset $i in palette, blockstate NBT: " . $blockStateNbt->toString());
$blockDecodeErrors[] = "Palette offset $i / Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
}
}
if(count($blockDecodeErrors) > 0){
$logger->error("Errors decoding blocks:\n - " . implode("\n - ", $blockDecodeErrors));
}
//TODO: exceptions
return PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
}
@ -443,7 +454,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$subChunks = [];
for($yy = 0; $yy < 8; ++$yy){
$storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy)];
$storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy, new \PrefixedLogger($logger, "Subchunk y=$yy"))];
if(isset($convertedLegacyExtraData[$yy])){
$storages[] = $convertedLegacyExtraData[$yy];
}
@ -482,7 +493,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
}
$storages = [$this->palettizeLegacySubChunkXZY($blocks, $blockData)];
$storages = [$this->palettizeLegacySubChunkXZY($blocks, $blockData, $logger)];
if($convertedLegacyExtraData !== null){
$storages[] = $convertedLegacyExtraData;
}

View File

@ -31,10 +31,11 @@ use pocketmine\world\format\SubChunk;
class Anvil extends RegionWorldProvider{
use LegacyAnvilChunkTrait;
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk{
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkYZX(
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
self::readFixedSizeByteArray($subChunk, "Data", 2048)
self::readFixedSizeByteArray($subChunk, "Data", 2048),
$logger
)], $biomes3d);
//ignore legacy light information
}

View File

@ -54,7 +54,7 @@ trait LegacyAnvilChunkTrait{
/**
* @throws CorruptedChunkException
*/
protected function deserializeChunk(string $data) : ?LoadedChunkData{
protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData{
$decompressed = @zlib_decode($data);
if($decompressed === false){
throw new CorruptedChunkException("Failed to decompress chunk NBT");
@ -90,7 +90,8 @@ trait LegacyAnvilChunkTrait{
$subChunksTag = $chunk->getListTag("Sections") ?? [];
foreach($subChunksTag as $subChunk){
if($subChunk instanceof CompoundTag){
$subChunks[$subChunk->getByte("Y")] = $this->deserializeSubChunk($subChunk, clone $biomes3d);
$y = $subChunk->getByte("Y");
$subChunks[$y] = $this->deserializeSubChunk($subChunk, clone $biomes3d, new \PrefixedLogger($logger, "Subchunk y=$y"));
}
}
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
@ -111,6 +112,6 @@ trait LegacyAnvilChunkTrait{
);
}
abstract protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk;
abstract protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk;
}

View File

@ -46,7 +46,7 @@ class McRegion extends RegionWorldProvider{
/**
* @throws CorruptedChunkException
*/
protected function deserializeChunk(string $data) : ?LoadedChunkData{
protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData{
$decompressed = @zlib_decode($data);
if($decompressed === false){
throw new CorruptedChunkException("Failed to decompress chunk NBT");
@ -90,7 +90,12 @@ class McRegion extends RegionWorldProvider{
$fullData = self::readFixedSizeByteArray($chunk, "Data", 16384);
for($y = 0; $y < 8; ++$y){
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $y)], clone $biomes3d);
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn(
$fullIds,
$fullData,
$y,
new \PrefixedLogger($logger, "Subchunk y=$y"),
)], clone $biomes3d);
}
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
if(!isset($subChunks[$y])){

View File

@ -35,10 +35,11 @@ use pocketmine\world\format\SubChunk;
class PMAnvil extends RegionWorldProvider{
use LegacyAnvilChunkTrait;
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk{
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkXZY(
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
self::readFixedSizeByteArray($subChunk, "Data", 2048)
self::readFixedSizeByteArray($subChunk, "Data", 2048),
$logger
)], $biomes3d);
}

View File

@ -147,7 +147,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
/**
* @throws CorruptedChunkException
*/
abstract protected function deserializeChunk(string $data) : ?LoadedChunkData;
abstract protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData;
/**
* @return CompoundTag[]
@ -200,7 +200,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
$chunkData = $this->loadRegion($regionX, $regionZ)->readChunk($chunkX & 0x1f, $chunkZ & 0x1f);
if($chunkData !== null){
return $this->deserializeChunk($chunkData);
return $this->deserializeChunk($chunkData, new \PrefixedLogger($this->logger, "Loading chunk x=$chunkX z=$chunkZ"));
}
return null;

View File

@ -650,6 +650,11 @@ parameters:
count: 2
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#1 \\$playerInfo of class pocketmine\\\\event\\\\player\\\\PlayerResourcePackOfferEvent constructor expects pocketmine\\\\player\\\\PlayerInfo, pocketmine\\\\player\\\\PlayerInfo\\|null given\\.$#"
count: 1
path: ../../../src/network/mcpe/NetworkSession.php
-
message: "#^Parameter \\#1 \\$target of method pocketmine\\\\command\\\\Command\\:\\:testPermissionSilent\\(\\) expects pocketmine\\\\command\\\\CommandSender, pocketmine\\\\player\\\\Player\\|null given\\.$#"
count: 1

View File

@ -47,11 +47,11 @@ class BlockStateUpgraderTest extends TestCase{
}
private function getNewSchema() : BlockStateUpgradeSchema{
return $this->getNewSchemaVersion(PHP_INT_MAX);
return $this->getNewSchemaVersion(PHP_INT_MAX, 0);
}
private function getNewSchemaVersion(int $versionId) : BlockStateUpgradeSchema{
$schema = new BlockStateUpgradeSchema(($versionId >> 24) & 0xff, ($versionId >> 16) & 0xff, ($versionId >> 8) & 0xff, $versionId & 0xff, 0);
private function getNewSchemaVersion(int $versionId, int $schemaId) : BlockStateUpgradeSchema{
$schema = new BlockStateUpgradeSchema(($versionId >> 24) & 0xff, ($versionId >> 16) & 0xff, ($versionId >> 8) & 0xff, $versionId & 0xff, $schemaId);
$this->upgrader->addSchema($schema);
return $schema;
}
@ -211,20 +211,23 @@ class BlockStateUpgraderTest extends TestCase{
}
/**
* @phpstan-return \Generator<int, array{int, int, bool}, void, void>
* @phpstan-return \Generator<int, array{int, int, bool, int}, void, void>
*/
public static function upgraderVersionCompatibilityProvider() : \Generator{
yield [0x1_00_00_00, 0x1_00_00_00, true]; //Same version: must be altered - this may be a backwards-compatible change that Mojang didn't bother to bump for
yield [0x1_00_01_00, 0x1_00_00_00, true]; //Schema newer than block: must be altered
yield [0x1_00_00_00, 0x1_00_01_00, false]; //Block newer than schema: block must NOT be altered
yield [0x1_00_00_00, 0x1_00_00_00, true, 2]; //Same version, multiple schemas targeting version - must be altered, we don't know which schemas are applicable
yield [0x1_00_00_00, 0x1_00_00_00, false, 1]; //Same version, one schema targeting version - do not change
yield [0x1_00_01_00, 0x1_00_00_00, true, 1]; //Schema newer than block: must be altered
yield [0x1_00_00_00, 0x1_00_01_00, false, 1]; //Block newer than schema: block must NOT be altered
}
/**
* @dataProvider upgraderVersionCompatibilityProvider
*/
public function testUpgraderVersionCompatibility(int $schemaVersion, int $stateVersion, bool $shouldChange) : void{
$schema = $this->getNewSchemaVersion($schemaVersion);
public function testUpgraderVersionCompatibility(int $schemaVersion, int $stateVersion, bool $shouldChange, int $schemaCount) : void{
for($i = 0; $i < $schemaCount; $i++){
$schema = $this->getNewSchemaVersion($schemaVersion, $i);
$schema->renamedIds[self::TEST_BLOCK] = self::TEST_BLOCK_2;
}
$getStateData = fn() => new BlockStateData(
self::TEST_BLOCK,

View File

@ -34,6 +34,7 @@ use pocketmine\crafting\json\SmithingTransformRecipeData;
use pocketmine\crafting\json\SmithingTrimRecipeData;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\item\BlockItemIdMap;
use pocketmine\data\bedrock\item\ItemTypeNames;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
@ -50,12 +51,12 @@ use pocketmine\network\mcpe\protocol\CreativeContentPacket;
use pocketmine\network\mcpe\protocol\PacketPool;
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
use pocketmine\network\mcpe\protocol\StartGamePacket;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraDataShield;
use pocketmine\network\mcpe\protocol\types\recipe\ComplexAliasItemDescriptor;
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
@ -170,16 +171,21 @@ class ParserPacketHandler extends PacketHandler{
$data->meta = $meta;
}
$nbt = $itemStack->getNbt();
$rawExtraData = $itemStack->getRawExtraData();
if($rawExtraData !== ""){
$decoder = PacketSerializer::decoder($rawExtraData, 0);
$extraData = $itemStringId === ItemTypeNames::SHIELD ? ItemStackExtraDataShield::read($decoder) : ItemStackExtraData::read($decoder);
$nbt = $extraData->getNbt();
if($nbt !== null && count($nbt) > 0){
$data->nbt = base64_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($nbt)));
}
if(count($itemStack->getCanPlaceOn()) > 0){
$data->can_place_on = $itemStack->getCanPlaceOn();
if(count($extraData->getCanPlaceOn()) > 0){
$data->can_place_on = $extraData->getCanPlaceOn();
}
if(count($extraData->getCanDestroy()) > 0){
$data->can_destroy = $extraData->getCanDestroy();
}
if(count($itemStack->getCanDestroy()) > 0){
$data->can_destroy = $itemStack->getCanDestroy();
}
return $data;
@ -421,7 +427,7 @@ class ParserPacketHandler extends PacketHandler{
$recipes["potion_type"][] = new PotionTypeRecipeData(
$this->recipeIngredientToJson(new RecipeIngredient(new IntIdMetaItemDescriptor($recipe->getInputItemId(), $recipe->getInputItemMeta()), 1)),
$this->recipeIngredientToJson(new RecipeIngredient(new IntIdMetaItemDescriptor($recipe->getIngredientItemId(), $recipe->getIngredientItemMeta()), 1)),
$this->itemStackToJson(new ItemStack($recipe->getOutputItemId(), $recipe->getOutputItemMeta(), 1, 0, null, [], [], null)),
$this->itemStackToJson(new ItemStack($recipe->getOutputItemId(), $recipe->getOutputItemMeta(), 1, 0, "")),
);
}
@ -571,10 +577,7 @@ function main(array $argv) : int{
fwrite(STDERR, "Unknown packet on line " . ($lineNum + 1) . ": " . $parts[1]);
continue;
}
$serializer = PacketSerializer::decoder($raw, 0, new PacketSerializerContext(
$handler->itemTypeDictionary ??
new ItemTypeDictionary([new ItemTypeEntry("minecraft:shield", 0, false)]))
);
$serializer = PacketSerializer::decoder($raw, 0);
$pk->decode($serializer);
$pk->handle($handler);