Compare commits

..

6 Commits

Author SHA1 Message Date
016614e714 ... 2025-02-17 02:05:09 +00:00
9e7e76a609 Fix PHPStan 2025-02-17 02:01:52 +00:00
b1e4e44f77 Ensure overloaded members can't conflict with static ones and vice versa 2025-02-17 01:58:08 +00:00
f4f31b654b Generate parameter name 2025-02-17 01:56:13 +00:00
799dfecdd9 typo 2025-02-17 01:13:18 +00:00
1415e2492a First look at enum-overloaded registry members
this allows us to reference registry members with dynamic enum cases, which has a lot of potential applications.

this enables us to keep the blocks as unique types, but with some of the power of dynamic state properties.

this is a rough initial proof of concept; it will need to be improved for further integration.
2025-02-17 00:58:07 +00:00
80 changed files with 711 additions and 1273 deletions

View File

@ -57,9 +57,6 @@ body:
attributes:
value: |
## Version, OS and game info
> [!WARNING]
> "Latest" is not a valid version.
> Failure to fill these fields with valid information may result in your issue being closed.
- type: input
attributes:

View File

@ -12,10 +12,6 @@ updates:
update-types:
- "version-update:semver-major"
- "version-update:semver-minor"
#since we lock this to exact versions, it causes conflicts with minor-next & major-next in composer.lock
#better to just test updates to this locally anyway since almost every version breaks something
- dependency-name: phpstan/phpstan
groups:
production-patch-updates:
dependency-type: production

View File

@ -8,7 +8,7 @@ on:
jobs:
build:
name: Update Docker Hub images
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- name: Set up Docker Buildx
@ -53,7 +53,7 @@ jobs:
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v6.16.0
uses: docker/build-push-action@v6.13.0
with:
push: true
context: ./pocketmine-mp
@ -66,7 +66,7 @@ jobs:
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.16.0
uses: docker/build-push-action@v6.13.0
with:
push: true
context: ./pocketmine-mp
@ -79,7 +79,7 @@ jobs:
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.16.0
uses: docker/build-push-action@v6.13.0
with:
push: true
context: ./pocketmine-mp
@ -92,7 +92,7 @@ jobs:
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v6.16.0
uses: docker/build-push-action@v6.13.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.33.0
uses: shivammathur/setup-php@2.32.0
with:
php-version: 8.2

View File

@ -24,7 +24,7 @@ permissions:
jobs:
check-intent:
name: Check release trigger
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
outputs:
valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }}
@ -43,13 +43,13 @@ jobs:
#don't do these checks if this isn't a release - we don't want to generate unnecessary failed statuses
if: needs.check-intent.outputs.valid == 'true'
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@2.33.0
uses: shivammathur/setup-php@2.32.0
with:
php-version: 8.2

View File

@ -23,7 +23,7 @@ env:
jobs:
skip:
name: Check whether to ignore this tag
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
outputs:
skip: ${{ steps.exists.outputs.exists == 'true' }}
@ -54,12 +54,12 @@ jobs:
needs: [check]
if: needs.check.outputs.valid == 'true' && github.ref_type != 'tag' #can't do post-commit for a tag
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}
@ -79,7 +79,7 @@ jobs:
needs: [check]
if: needs.check.outputs.valid == 'true' || github.ref_type == 'tag' #ignore validity check for tags
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
@ -87,7 +87,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.33.0
uses: shivammathur/setup-php@2.32.0
with:
php-version: ${{ env.PHP_VERSION }}
@ -165,7 +165,7 @@ jobs:
${{ github.workspace }}/core-permissions.rst
- name: Create draft release
uses: ncipollo/release-action@v1.16.0
uses: ncipollo/release-action@v1.15.0
id: create-draft
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst
@ -182,8 +182,6 @@ jobs:
:information_source: Download the recommended PHP binary [here](${{ steps.php-binary-url.outputs.PHP_BINARY_URL }}).
:warning: Found a bug? Report it on our [issue tracker](${{ github.server_url }}/${{ github.repository }}/issues). **We can't fix bugs if you don't report them.**
- name: Post draft release URL on PR
if: github.event_name == 'pull_request_target'
uses: thollander/actions-comment-pull-request@v3

View File

@ -15,7 +15,7 @@ on:
type: number
image:
description: 'Runner image to use'
default: 'ubuntu-22.04'
default: 'ubuntu-20.04'
type: string
jobs:

View File

@ -20,7 +20,7 @@ jobs:
codestyle:
name: Code Style checks
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
strategy:
fail-fast: false
@ -28,10 +28,10 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.33.0
uses: shivammathur/setup-php@2.32.0
with:
php-version: 8.3
tools: php-cs-fixer:3.75
php-version: 8.2
tools: php-cs-fixer:3.49
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -40,7 +40,7 @@ jobs:
shellcheck:
name: ShellCheck
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
strategy:
fail-fast: false

View File

@ -20,7 +20,10 @@ jobs:
- Check our [Documentation](https://doc.pmmp.io) to see if you can find answers there
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG)
- Ask the community on our [Discord server](https://discord.gg/bmSAZBG) or our [Forums](https://forums.pmmp.io)
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io)
close-issue: true
lock-issue: false

View File

@ -22,7 +22,7 @@ jobs:
steps:
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }}
private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }}

View File

@ -6,12 +6,6 @@ $finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/tests')
->in(__DIR__ . '/tools')
->notPath('plugins/DevTools')
//JsonMapper will break if the FQNs in the doc comments for these are shortened :(
->notPath('crafting/json')
->notPath('inventory/json')
->notPath('data/bedrock/block/upgrade/model')
->notPath('data/bedrock/item/upgrade/model')
->notName('PocketMine.php');
return (new PhpCsFixer\Config)

View File

@ -65,8 +65,6 @@ PocketMine-MP accepts community contributions! The following resources will be u
* [Building and running PocketMine-MP from source](BUILDING.md)
* [Contributing Guidelines](CONTRIBUTING.md)
New here? Check out [issues with the "Easy task" label](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22Easy%20task%22) for things you could work to familiarise yourself with the codebase.
## Donate
PocketMine-MP is free, but it requires a lot of time and effort from unpaid volunteers to develop. Donations enable us to keep delivering support for new versions and adding features your players love.

View File

@ -31,12 +31,12 @@ require dirname(__DIR__) . '/vendor/autoload.php';
*/
/**
* @var string[]|Closure[] $options
* @phpstan-var array<string, string|Closure() : string> $options
* @var string[]|\Closure[] $options
* @phpstan-var array<string, string|\Closure() : string> $options
*/
$options = [
"base_version" => VersionInfo::BASE_VERSION,
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION, limit: 2)[0],
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0],
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
"changelog_file_name" => function() : string{

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\build\update_registry_annotations;
use pocketmine\utils\OverloadedRegistryMember;
use pocketmine\utils\Utils;
use function basename;
use function class_exists;
@ -34,6 +35,7 @@ use function fwrite;
use function implode;
use function is_dir;
use function ksort;
use function lcfirst;
use function mb_strtoupper;
use function preg_match;
use function sprintf;
@ -47,11 +49,19 @@ if(count($argv) !== 2){
exit(1);
}
/**
* @phpstan-param \ReflectionClass<*> $class
*/
function makeTypehint(string $namespaceName, \ReflectionClass $class) : string{
return $class->getNamespaceName() === $namespaceName ? $class->getShortName() : '\\' . $class->getName();
}
/**
* @param object[] $members
* @phpstan-param array<string, object> $members
* @phpstan-param array<string, OverloadedRegistryMember> $overloadedMembers
*/
function generateMethodAnnotations(string $namespaceName, array $members) : string{
function generateMethodAnnotations(string $namespaceName, array $members, array $overloadedMembers) : string{
$selfName = basename(__FILE__);
$lines = ["/**"];
$lines[] = " * This doc-block is generated automatically, do not modify it manually.";
@ -69,14 +79,20 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
}
if($reflect === false){
$typehint = "object";
}elseif($reflect->getNamespaceName() === $namespaceName){
$typehint = $reflect->getShortName();
}else{
$typehint = '\\' . $reflect->getName();
$typehint = makeTypehint($namespaceName, $reflect);
}
$accessor = mb_strtoupper($name);
$memberLines[$accessor] = sprintf($lineTmpl, $accessor, $typehint);
}
foreach(Utils::stringifyKeys($overloadedMembers) as $baseName => $member){
$accessor = mb_strtoupper($baseName);
$returnTypehint = makeTypehint($namespaceName, new \ReflectionClass($member->memberClass));
$enumReflect = new \ReflectionClass($member->enumClass);
$paramTypehint = makeTypehint($namespaceName, $enumReflect);
$memberLines[] = sprintf(" * @method static %s %s(%s \$%s)", $returnTypehint, $accessor, $paramTypehint, lcfirst($enumReflect->getShortName()));
}
ksort($memberLines, SORT_STRING);
foreach($memberLines as $line){
@ -107,7 +123,7 @@ function processFile(string $file) : void{
}
echo "Found registry in $file\n";
$replacement = generateMethodAnnotations($matches[1], $className::getAll());
$replacement = generateMethodAnnotations($matches[1], $className::getAll(), $className::getAllOverloaded());
$newContents = str_replace($docComment, $replacement, $contents);
if($newContents !== $contents){

View File

@ -36,17 +36,3 @@ It also allows creating new collapsible groups of items, and modifying or removi
- `BedrockDataFiles` now includes constants for folders at the top level of `BedrockData` as well as files.
- The structure of creative data in `BedrockData` was changed to accommodate item category and grouping information. `creativeitems.json` has been replaced by `creative/*.json`, which contain information about item grouping and also segregates item lists per category.
- New information was added to `required_item_list.json` in `BedrockData`, as the server is now required to send item component NBT data in some cases.
# 5.25.1
Released 26th February 2025.
## Fixes
- Fixed confusing exception message when a block-breaking tool has an efficiency value of zero.
- Fixed incorrect facing of doors since 1.21.60 (resulted in mismatched AABBs between client & server, rendering glitches etc.)
- Resource pack UUIDs are now validated on load. Previously, invalid UUIDs would be accepted, and potentially cause a server crash on player join.
# 5.25.2
Released 4th March 2025.
## Fixes
- Added limits to various `explode()` calls.

View File

@ -1,71 +0,0 @@
# 5.26.0
Released 22nd March 2025.
This is a minor feature release focused on performance 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.
## Performance
- Significantly improved performance of entity movement. Load testing with item entities showed a 3x increase in the number of entities supported without lag.
- Significantly improved performance of on-ground checks for player movement. This still needs further work, but optimisations implemented in this version should improve performance substantially.
- Updated `pocketmine/nbt` dependency with performance improvements to `TAG_Compound` and `TAG_List` comparison. This should improve performance of inventory-related actions.
- `InventoryTransaction` now avoids useless item clones when processing transactions, which should improve performance of inventory-related actions.
## Dependencies
- `pocketmine/bedrock-protocol` has been updated to `36.2.0`, which adds new functions to access some packet fields.
- `pocketmine/nbt` has been updated to `1.1.0`, which improves performance when comparing NBT object trees.
## Gameplay
- Block breaking animation speed now takes into account the following: jumping, being in water, haste, mining fatigue
## Tools
- `blockstate-upgrade-schema-utils.php` now has a new `dump-table` command, which turns a `.bin` palette table file into human-readable text for debugging.
## API
### `pocketmine\block`
- The following methods have been added:
- `public RuntimeBlockStateRegistry->hasStateId(int $stateId) : bool` - checks whether the given state ID is registered
### `pocketmine\crafting`
- The following methods have been deprecated:
- `CraftingManager::sort()` - this was implicitly internal anyway
### `pocketmine\utils`
- The following constants have been added:
- `TextFormat::MATERIAL_RESIN`
- The following static properties have been added:
- `Terminal::$COLOR_MATERIAL_RESIN`
### `pocketmine\data\bedrock\block`
- `BlockStateToObjectDeserializer` now permits overriding **deserializers** for Bedrock IDs. This may be useful to implement custom state handling, or to implement missing block variants (such as snow cauldron).
- This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
- Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
- If you want to make a custom version of a vanilla block, create a custom type ID for it, exactly as you would for a regular custom block.
- The following methods have been added:
- `public BlockStateToObjectDeserializer->getDeserializerForId(string $id) : ?(\Closure(BlockStateReader) : Block)`
### `pocketmine\data\bedrock\item`
- `ItemDeserializer` now permits overriding **deserializers** for Bedrock IDs. As above, this may be useful to implement custom data handling, or to implement missing variants of existing items.
- This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**.
- Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts.
- As above, if you want to make a custom version of a vanilla item, create a custom type ID for it, exactly as you would for a regular custom item.
- The following methods have been added:
- `public ItemDeserializer->getDeserializerForId(string $id) : ?(\Closure(SavedItemData) : Item)`
## Internals
- `new $class` is now banned on new internals code by a PHPStan rule. Closures or factory objects should be used instead for greater flexibility and better static analysis.
- `CraftingManager` now uses a more stable hash function for recipe output filtering.
- `ChunkCache` now accepts `int $dimensionId` in the constructor. This may be useful for plugins which implement the nether.
- `RuntimeBlockStateRegistry` now precomputes basic collision info about known states for fast paths.
- This permits specialization for common shapes like cubes and collisionless blocks, which allows skipping complex logic in entity movement calculation. This vastly improves performance.
- Any block whose class overrides `readStateFromWorld()` or `getModelPositionOffset()` will *not* be optimised.
- `Block->recalculateCollisionBoxes()` now has a hard requirement not to depend on anything other than available properties. It must not use `World` or its position.
- This change was problematic for `ChorusPlant`, which used nearby blocks to calculate its collision boxes.
- Blocks which need nearby blocks should override `readStateFromWorld()` and set dynamic state properties, similar to fences.
- This design flaw will be corrected with a major change to `Block` internals currently in planning for a future major version.
- `Block->getCollisionBoxes()` may not be called at all during gameplay for blocks with shapes determined to be simple, like cubes and collisionless blocks.
- `BlockStateToObjectDeserializer` now checks if the returned blockstate is registered in `RuntimeBlockStateRegistry` to promote earlier error detection (instead of crashing in random code paths).

View File

@ -1,24 +0,0 @@
# 5.27.0
Released 27th March 2025.
This is a support release for Minecraft: Bedrock Edition 1.21.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.
## Interim releases
If you're upgrading from 5.25.x directly to 5.27.0, please also read the following changelogs, as the interim releases contain important changes:
- [5.26.0](https://github.com/pmmp/PocketMine-MP/blob/5.26.0/changelogs/5.26.md#5260) - Performance improvements and other internal improvements
## General
- Aded support for Minecraft: Bedrock Edition 1.21.70.
- Removed support for earlier versions.
# 5.27.1
Released 6th April 2025.
## Fixes
- Updated RakLib to get ping timestamp handling fixes.

View File

@ -1,21 +0,0 @@
# 5.28.0
Released 9th May 2025.
This is a support release for Minecraft: Bedrock Edition 1.21.80.
**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
- Aded support for Minecraft: Bedrock Edition 1.21.70.
- Removed support for earlier versions.
## Fixes
- `AvailableEnchantmentRegistry` now requires provided tags to always be `string`. Previously, this wasn't enforced, leading to random crashes in core code related to enchanting.
- `Entity->setFireTicks()` and `Entity->setOnFire()` now truncate the fire time to the max value instead of throwing exceptions.
## Internals
- Improved PHPStan error reporting for unsafe foreaches. Foreach on an array with implicit keys now generates different errors than foreach on an array with string keys.

View File

@ -34,9 +34,9 @@
"adhocore/json-comment": "~1.2.0",
"netresearch/jsonmapper": "~v5.0.0",
"pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60",
"pocketmine/bedrock-data": "5.0.0+bedrock-1.21.80",
"pocketmine/bedrock-data": "~4.0.0+bedrock-1.21.60",
"pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50",
"pocketmine/bedrock-protocol": "38.0.0+bedrock-1.21.80",
"pocketmine/bedrock-protocol": "~36.0.0+bedrock-1.21.60",
"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.1.0",
"pocketmine/raklib": "~1.1.2",
"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": "2.1.11",
"phpstan/phpstan": "2.1.4",
"phpstan/phpstan-phpunit": "^2.0.0",
"phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^10.5.24"

197
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": "4dfc7b8c912d8d5fa194ddc0e97903fb",
"content-hash": "663122b8f03ef5ec6718a419923a0851",
"packages": [
{
"name": "adhocore/json-comment",
@ -67,16 +67,16 @@
},
{
"name": "brick/math",
"version": "0.12.3",
"version": "0.12.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
"reference": "f510c0a40911935b77b86859eb5223d58d660df1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
"url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
"reference": "f510c0a40911935b77b86859eb5223d58d660df1",
"shasum": ""
},
"require": {
@ -85,7 +85,7 @@
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.1",
"vimeo/psalm": "6.8.8"
"vimeo/psalm": "5.16.0"
},
"type": "library",
"autoload": {
@ -115,7 +115,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.12.3"
"source": "https://github.com/brick/math/tree/0.12.1"
},
"funding": [
{
@ -123,7 +123,7 @@
"type": "github"
}
],
"time": "2025-02-28T13:11:00+00:00"
"time": "2023-11-29T23:19:16+00:00"
},
{
"name": "netresearch/jsonmapper",
@ -204,16 +204,16 @@
},
{
"name": "pocketmine/bedrock-data",
"version": "5.0.0+bedrock-1.21.80",
"version": "4.0.0+bedrock-1.21.60",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080"
"reference": "2e5f16ec2facac653f3f894f22eb831d880ba98e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e38d5ea19f794ec5216e5f96742237e8c4e7f080",
"reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/2e5f16ec2facac653f3f894f22eb831d880ba98e",
"reference": "2e5f16ec2facac653f3f894f22eb831d880ba98e",
"shasum": ""
},
"type": "library",
@ -224,9 +224,9 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.80"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.60"
},
"time": "2025-05-09T14:15:18+00:00"
"time": "2025-02-16T15:56:56+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
@ -256,16 +256,16 @@
},
{
"name": "pocketmine/bedrock-protocol",
"version": "38.0.0+bedrock-1.21.80",
"version": "36.0.0+bedrock-1.21.60",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "a626561eaefeb6333c0d2726e2789ceb0aac0724"
"reference": "2057de319c5c551001c2a544e08d1bc7727d9963"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a626561eaefeb6333c0d2726e2789ceb0aac0724",
"reference": "a626561eaefeb6333c0d2726e2789ceb0aac0724",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2057de319c5c551001c2a544e08d1bc7727d9963",
"reference": "2057de319c5c551001c2a544e08d1bc7727d9963",
"shasum": ""
},
"require": {
@ -296,9 +296,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/38.0.0+bedrock-1.21.80"
"source": "https://github.com/pmmp/BedrockProtocol/tree/36.0.0+bedrock-1.21.60"
},
"time": "2025-05-09T14:17:07+00:00"
"time": "2025-02-16T15:59:08+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -471,16 +471,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.24.2",
"version": "2.24.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "2a00c44c52bce98e7a43aa31517df78cbb2ba23b"
"reference": "6ec5e92c77a2102b2692763733e4763012facae9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/2a00c44c52bce98e7a43aa31517df78cbb2ba23b",
"reference": "2a00c44c52bce98e7a43aa31517df78cbb2ba23b",
"url": "https://api.github.com/repos/pmmp/Language/zipball/6ec5e92c77a2102b2692763733e4763012facae9",
"reference": "6ec5e92c77a2102b2692763733e4763012facae9",
"shasum": ""
},
"type": "library",
@ -488,9 +488,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.24.2"
"source": "https://github.com/pmmp/Language/tree/2.24.0"
},
"time": "2025-04-03T01:23:27+00:00"
"time": "2025-02-16T20:46:34+00:00"
},
{
"name": "pocketmine/log",
@ -576,16 +576,16 @@
},
{
"name": "pocketmine/nbt",
"version": "1.1.1",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/NBT.git",
"reference": "c3c7b0a7295daeaf7873d90fed5c5d10381d12e1"
"reference": "cfd53a86166b851786967fc560cdb372e66fde96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/c3c7b0a7295daeaf7873d90fed5c5d10381d12e1",
"reference": "c3c7b0a7295daeaf7873d90fed5c5d10381d12e1",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/cfd53a86166b851786967fc560cdb372e66fde96",
"reference": "cfd53a86166b851786967fc560cdb372e66fde96",
"shasum": ""
},
"require": {
@ -612,22 +612,22 @@
"description": "PHP library for working with Named Binary Tags",
"support": {
"issues": "https://github.com/pmmp/NBT/issues",
"source": "https://github.com/pmmp/NBT/tree/1.1.1"
"source": "https://github.com/pmmp/NBT/tree/1.1.0"
},
"time": "2025-03-09T01:46:03+00:00"
"time": "2025-02-01T21:20:26+00:00"
},
{
"name": "pocketmine/raklib",
"version": "1.1.2",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/pmmp/RakLib.git",
"reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f"
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/4145a31cd812fe8931c3c9c691fcd2ded2f47e7f",
"reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f",
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97",
"reference": "be2783be516bf6e2872ff5c81fb9048596617b97",
"shasum": ""
},
"require": {
@ -639,8 +639,8 @@
"pocketmine/log": "^0.3.0 || ^0.4.0"
},
"require-dev": {
"phpstan/phpstan": "2.1.0",
"phpstan/phpstan-strict-rules": "^2.0"
"phpstan/phpstan": "1.10.1",
"phpstan/phpstan-strict-rules": "^1.0"
},
"type": "library",
"autoload": {
@ -655,9 +655,9 @@
"description": "A RakNet server implementation written in PHP",
"support": {
"issues": "https://github.com/pmmp/RakLib/issues",
"source": "https://github.com/pmmp/RakLib/tree/1.1.2"
"source": "https://github.com/pmmp/RakLib/tree/1.1.1"
},
"time": "2025-04-06T03:38:21+00:00"
"time": "2024-03-04T14:02:14+00:00"
},
{
"name": "pocketmine/raklib-ipc",
@ -742,16 +742,16 @@
},
{
"name": "ramsey/collection",
"version": "2.1.1",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/collection.git",
"reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
"reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
"url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"shasum": ""
},
"require": {
@ -759,22 +759,25 @@
},
"require-dev": {
"captainhook/plugin-composer": "^5.3",
"ergebnis/composer-normalize": "^2.45",
"fakerphp/faker": "^1.24",
"ergebnis/composer-normalize": "^2.28.3",
"fakerphp/faker": "^1.21",
"hamcrest/hamcrest-php": "^2.0",
"jangregor/phpstan-prophecy": "^2.1",
"mockery/mockery": "^1.6",
"jangregor/phpstan-prophecy": "^1.0",
"mockery/mockery": "^1.5",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.4",
"phpspec/prophecy-phpunit": "^2.3",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-mockery": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5",
"ramsey/coding-standard": "^2.3",
"ramsey/conventional-commits": "^1.6",
"roave/security-advisories": "dev-latest"
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18.4",
"ramsey/coding-standard": "^2.0.3",
"ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
},
"type": "library",
"extra": {
@ -812,9 +815,19 @@
],
"support": {
"issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/2.1.1"
"source": "https://github.com/ramsey/collection/tree/2.0.0"
},
"time": "2025-03-22T05:38:12+00:00"
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
"type": "tidelift"
}
],
"time": "2022-12-31T21:50:55+00:00"
},
{
"name": "ramsey/uuid",
@ -1137,16 +1150,16 @@
"packages-dev": [
{
"name": "myclabs/deep-copy",
"version": "1.13.0",
"version": "1.12.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
"shasum": ""
},
"require": {
@ -1185,7 +1198,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
},
"funding": [
{
@ -1193,7 +1206,7 @@
"type": "tidelift"
}
],
"time": "2025-02-12T12:17:51+00:00"
"time": "2024-11-08T17:47:46+00:00"
},
{
"name": "nikic/php-parser",
@ -1373,16 +1386,16 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.11",
"version": "2.1.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30"
"reference": "8f99e18eb775dbaf6460c95fa0b65312da9c746a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30",
"reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8f99e18eb775dbaf6460c95fa0b65312da9c746a",
"reference": "8f99e18eb775dbaf6460c95fa0b65312da9c746a",
"shasum": ""
},
"require": {
@ -1427,20 +1440,20 @@
"type": "github"
}
],
"time": "2025-03-24T13:45:00+00:00"
"time": "2025-02-10T08:25:21+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "2.0.6",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "6b92469f8a7995e626da3aa487099617b8dfa260"
"reference": "d09e152f403c843998d7a52b5d87040c937525dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6b92469f8a7995e626da3aa487099617b8dfa260",
"reference": "6b92469f8a7995e626da3aa487099617b8dfa260",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d09e152f403c843998d7a52b5d87040c937525dd",
"reference": "d09e152f403c843998d7a52b5d87040c937525dd",
"shasum": ""
},
"require": {
@ -1451,9 +1464,7 @@
"phpunit/phpunit": "<7.0"
},
"require-dev": {
"nikic/php-parser": "^5",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.6"
},
@ -1478,22 +1489,22 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.6"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.4"
},
"time": "2025-03-26T12:47:06+00:00"
"time": "2025-01-22T13:07:38+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "2.0.4",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a"
"reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
"reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/8b88b5f818bfa301e0c99154ab622dace071c3ba",
"reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba",
"shasum": ""
},
"require": {
@ -1526,9 +1537,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.3"
},
"time": "2025-03-18T11:42:40+00:00"
"time": "2025-01-21T10:52:14+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1853,16 +1864,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.5.45",
"version": "10.5.44",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8"
"reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8",
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1381c62769be4bb88fa4c5aec1366c7c66ca4f36",
"reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36",
"shasum": ""
},
"require": {
@ -1934,7 +1945,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.44"
},
"funding": [
{
@ -1950,7 +1961,7 @@
"type": "tidelift"
}
],
"time": "2025-02-06T16:08:12+00:00"
"time": "2025-01-31T07:00:38+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2921,7 +2932,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@ -2952,7 +2963,7 @@
"ext-zlib": ">=1.2.11",
"composer-runtime-api": "^2.0"
},
"platform-dev": {},
"platform-dev": [],
"platform-overrides": {
"php": "8.1.0"
},

View File

@ -11,11 +11,9 @@ includes:
rules:
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
- pocketmine\phpstan\rules\DisallowDynamicNewRule
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
- pocketmine\phpstan\rules\ExplodeLimitRule
- pocketmine\phpstan\rules\UnsafeForeachRule
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
parameters:

View File

@ -31,7 +31,6 @@ use function hrtime;
use function max;
use function min;
use function number_format;
use function sprintf;
/**
* Allows threads to manually trigger the cyclic garbage collector using a threshold like PHP's own garbage collector,
@ -49,7 +48,6 @@ final class GarbageCollectorManager{
private int $threshold = self::GC_THRESHOLD_DEFAULT;
private int $collectionTimeTotalNs = 0;
private int $runs = 0;
private \Logger $logger;
private TimingsHandler $timings;
@ -98,16 +96,7 @@ final class GarbageCollectorManager{
$time = $end - $start;
$this->collectionTimeTotalNs += $time;
$this->runs++;
$this->logger->info(sprintf(
"Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms",
$this->runs,
number_format($time / 1_000_000, 2),
$rootsBefore,
$rootsAfter,
$cycles,
number_format($this->collectionTimeTotalNs / 1_000_000, 2)
));
$this->logger->debug("gc_collect_cycles: " . number_format($time) . " ns ($rootsBefore -> $rootsAfter roots, $cycles cycles collected) - total GC time: " . number_format($this->collectionTimeTotalNs) . " ns");
return $cycles;
}

View File

@ -264,7 +264,7 @@ JIT_WARNING
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
if($composerGitHash !== null){
//we can't verify dependency versions if we were installed without using git
$currentGitHash = explode("-", VersionInfo::GIT_HASH(), 2)[0];
$currentGitHash = explode("-", VersionInfo::GIT_HASH())[0];
if($currentGitHash !== $composerGitHash){
critical_error("Composer dependencies and/or autoloader are out of sync.");
critical_error("- Current revision is $currentGitHash");

View File

@ -699,7 +699,7 @@ class Server{
public function removeOp(string $name) : void{
$lowercaseName = strtolower($name);
foreach(Utils::promoteKeys($this->operators->getAll()) as $operatorName => $_){
foreach($this->operators->getAll() as $operatorName => $_){
$operatorName = (string) $operatorName;
if($lowercaseName === strtolower($operatorName)){
$this->operators->remove($operatorName);

View File

@ -31,8 +31,8 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.28.0";
public const IS_DEVELOPMENT_BUILD = false;
public const BASE_VERSION = "5.25.1";
public const IS_DEVELOPMENT_BUILD = true;
public const BUILD_CHANNEL = "stable";
/**

View File

@ -154,7 +154,7 @@ class BlockBreakInfo{
$efficiency = $item->getMiningEfficiency(($this->toolType & $item->getBlockToolType()) !== 0);
if($efficiency <= 0){
throw new \InvalidArgumentException(get_class($item) . " must have a positive mining efficiency, but got $efficiency");
throw new \InvalidArgumentException(get_class($item) . " has invalid mining efficiency: expected >= 0, got $efficiency");
}
$base /= $efficiency;

View File

@ -209,10 +209,6 @@ class RuntimeBlockStateRegistry{
return $block;
}
public function hasStateId(int $stateId) : bool{
return isset($this->fullList[$stateId]);
}
/**
* @return Block[]
* @phpstan-return array<int, Block>

View File

@ -79,6 +79,10 @@ use function strtolower;
* @see build/generate-registry-annotations.php
* @generate-registry-docblock
*
* @method static Sapling SAPLING(\pocketmine\block\utils\SaplingType $saplingType)
* @method static Leaves LEAVES(\pocketmine\block\utils\LeavesType $leavesType)
* @method static FloorSign SIGN(\pocketmine\block\utils\WoodType $woodType)
* @method static WallSign WALL_SIGN(\pocketmine\block\utils\WoodType $woodType)
* @method static WoodenButton ACACIA_BUTTON()
* @method static WoodenDoor ACACIA_DOOR()
* @method static WoodenFence ACACIA_FENCE()
@ -1231,10 +1235,12 @@ final class VanillaBlocks{
$name = $saplingType->getDisplayName();
self::register(strtolower($saplingType->name) . "_sapling", fn(BID $id) => new Sapling($id, $name . " Sapling", $saplingTypeInfo, $saplingType));
}
self::registerOverloaded("sapling", SaplingType::class, Sapling::class);
foreach(LeavesType::cases() as $leavesType){
$name = $leavesType->getDisplayName();
self::register(strtolower($leavesType->name) . "_leaves", fn(BID $id) => new Leaves($id, $name . " Leaves", $leavesBreakInfo, $leavesType));
}
self::registerOverloaded("leaves", LeavesType::class, Leaves::class);
$sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD));
self::register("red_sandstone_stairs", fn(BID $id) => new Stair($id, "Red Sandstone Stairs", $sandstoneBreakInfo));
@ -1370,22 +1376,12 @@ final class VanillaBlocks{
self::register($idName("pressure_plate"), fn(BID $id) => new WoodenPressurePlate($id, $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20));
self::register($idName("trapdoor"), fn(BID $id) => new WoodenTrapdoor($id, $name . " Trapdoor", $woodenDoorBreakInfo, $woodType));
$signAsItem = match($woodType){
WoodType::OAK => VanillaItems::OAK_SIGN(...),
WoodType::SPRUCE => VanillaItems::SPRUCE_SIGN(...),
WoodType::BIRCH => VanillaItems::BIRCH_SIGN(...),
WoodType::JUNGLE => VanillaItems::JUNGLE_SIGN(...),
WoodType::ACACIA => VanillaItems::ACACIA_SIGN(...),
WoodType::DARK_OAK => VanillaItems::DARK_OAK_SIGN(...),
WoodType::MANGROVE => VanillaItems::MANGROVE_SIGN(...),
WoodType::CRIMSON => VanillaItems::CRIMSON_SIGN(...),
WoodType::WARPED => VanillaItems::WARPED_SIGN(...),
WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...),
WoodType::PALE_OAK => VanillaItems::PALE_OAK_SIGN(...),
};
$signAsItem = fn() => VanillaItems::SIGN($woodType);
self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
}
self::registerOverloaded("sign", WoodType::class, FloorSign::class);
self::registerOverloaded("wall_sign", WoodType::class, WallSign::class);
}
private static function registerMushroomBlocks() : void{

View File

@ -62,10 +62,9 @@ class Sign extends Spawnable{
/**
* @return string[]
* @deprecated
*/
public static function fixTextBlob(string $blob) : array{
return array_slice(array_pad(explode("\n", $blob, limit: 5), 4, ""), 0, 4);
return array_slice(array_pad(explode("\n", $blob), 4, ""), 0, 4);
}
protected SignText $text;

View File

@ -32,7 +32,7 @@ use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use function get_class;
abstract class Spawnable extends Tile{
/** @phpstan-var CacheableNbt<CompoundTag>|null */
/** @phpstan-var CacheableNbt<\pocketmine\nbt\tag\CompoundTag>|null */
private ?CacheableNbt $spawnCompoundCache = null;
/**
@ -73,7 +73,7 @@ abstract class Spawnable extends Tile{
* Returns encoded NBT (varint, little-endian) used to spawn this tile to clients. Uses cache where possible,
* populates cache if it is null.
*
* @phpstan-return CacheableNbt<CompoundTag>
* @phpstan-return CacheableNbt<\pocketmine\nbt\tag\CompoundTag>
*/
final public function getSerializedSpawnCompound() : CacheableNbt{
if($this->spawnCompoundCache === null){

View File

@ -30,15 +30,9 @@ interface CopperMaterial{
public function getOxidation() : CopperOxidation;
/**
* @return $this
*/
public function setOxidation(CopperOxidation $oxidation) : CopperMaterial;
public function isWaxed() : bool;
/**
* @return $this
*/
public function setWaxed(bool $waxed) : CopperMaterial;
}

View File

@ -79,7 +79,7 @@ class SignText{
* @throws \InvalidArgumentException if the text is not valid UTF-8
*/
public static function fromBlob(string $blob, ?Color $baseColor = null, bool $glowing = false) : SignText{
return new self(array_slice(array_pad(explode("\n", $blob, limit: self::LINE_COUNT + 1), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing);
return new self(array_slice(array_pad(explode("\n", $blob), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing);
}
/**

View File

@ -37,7 +37,6 @@ use function array_values;
use function explode;
use function implode;
use function str_replace;
use const PHP_INT_MAX;
abstract class Command{
@ -114,7 +113,7 @@ abstract class Command{
}
public function setPermission(?string $permission) : void{
$this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
$this->setPermissions($permission === null ? [] : explode(";", $permission));
}
public function testPermission(CommandSender $target, ?string $permission = null) : bool{

View File

@ -39,7 +39,6 @@ use function ksort;
use function min;
use function sort;
use function strtolower;
use const PHP_INT_MAX;
use const SORT_FLAG_CASE;
use const SORT_NATURAL;
@ -109,7 +108,7 @@ class HelpCommand extends VanillaCommand{
$usage = $cmd->getUsage();
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString)))
->prefix(TextFormat::GOLD));
$aliases = $cmd->getAliases();

View File

@ -219,11 +219,7 @@ class ParticleCommand extends VanillaCommand{
break;
case "blockdust":
if($data !== null){
//to preserve the old unlimited explode behaviour, allow this to split into at most 5 parts
//this allows the 4th argument to be processed normally if given without forcing it to also consume
//any unexpected parts
//we probably ought to error in this case, but this will do for now
$d = explode("_", $data, limit: 5);
$d = explode("_", $data);
if(count($d) >= 3){
return new DustParticle(new Color(
((int) $d[0]) & 0xff,

View File

@ -62,7 +62,7 @@ class ConsoleCommandSender implements CommandSender{
$message = $this->getLanguage()->translate($message);
}
foreach(explode("\n", trim($message), limit: PHP_INT_MAX) as $line){
foreach(explode("\n", trim($message)) as $line){
Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::addBase(TextFormat::WHITE, $line));
}
}

View File

@ -29,12 +29,8 @@ use pocketmine\nbt\TreeRoot;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\DestructorCallbackTrait;
use pocketmine\utils\ObjectSet;
use function array_shift;
use function count;
use function implode;
use function ksort;
use function spl_object_id;
use const SORT_STRING;
use function usort;
class CraftingManager{
use DestructorCallbackTrait;
@ -104,7 +100,6 @@ class CraftingManager{
/**
* Function used to arrange Shapeless Recipe ingredient lists into a consistent order.
* @deprecated
*/
public static function sort(Item $i1, Item $i2) : int{
//Use spaceship operator to compare each property, then try the next one if they are equivalent.
@ -113,30 +108,45 @@ class CraftingManager{
return $retval;
}
private static function hashOutput(Item $output) : string{
$write = new BinaryStream();
$write->putVarInt($output->getStateId());
$write->put((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag())));
/**
* @param Item[] $items
*
* @return Item[]
* @phpstan-return list<Item>
*/
private static function pack(array $items) : array{
$result = [];
return $write->getBuffer();
foreach($items as $item){
foreach($result as $otherItem){
if($item->canStackWith($otherItem)){
$otherItem->setCount($otherItem->getCount() + $item->getCount());
continue 2;
}
}
//No matching item found
$result[] = clone $item;
}
return $result;
}
/**
* @param Item[] $outputs
*/
private static function hashOutputs(array $outputs) : string{
if(count($outputs) === 1){
return self::hashOutput(array_shift($outputs));
}
$unique = [];
$outputs = self::pack($outputs);
usort($outputs, [self::class, "sort"]);
$result = new BinaryStream();
foreach($outputs as $o){
//count is not written because the outputs might be from multiple repetitions of a single recipe
//this reduces the accuracy of the hash, but it won't matter in most cases.
$hash = self::hashOutput($o);
$unique[$hash] = $hash;
$result->putVarInt($o->getStateId());
$result->put((new LittleEndianNbtSerializer())->write(new TreeRoot($o->getNamedTag())));
}
ksort($unique, SORT_STRING);
return implode("", $unique);
return $result->getBuffer();
}
/**

View File

@ -97,7 +97,6 @@ class ShapedRecipe implements CraftingRecipe{
$this->shape = $shape;
Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{});
foreach(Utils::stringifyKeys($ingredients) as $char => $i){
if(!str_contains(implode($this->shape), $char)){
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
@ -106,7 +105,6 @@ class ShapedRecipe implements CraftingRecipe{
$this->ingredientList[$char] = clone $i;
}
Utils::validateArrayValueType($results, function(Item $_) : void{});
$this->results = Utils::cloneObjectArray($results);
}

View File

@ -53,9 +53,7 @@ class ShapelessRecipe implements CraftingRecipe{
if(count($ingredients) > 9){
throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients");
}
Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{});
$this->ingredients = $ingredients;
Utils::validateArrayValueType($results, function(Item $_) : void{});
$this->results = Utils::cloneObjectArray($results);
}

View File

@ -31,7 +31,8 @@ final class BedrockDataFiles{
}
public const BANNER_PATTERNS_JSON = BEDROCK_DATA_PATH . '/banner_patterns.json';
public const BIOME_DEFINITIONS_JSON = BEDROCK_DATA_PATH . '/biome_definitions.json';
public const BIOME_DEFINITIONS_NBT = BEDROCK_DATA_PATH . '/biome_definitions.nbt';
public const BIOME_DEFINITIONS_FULL_NBT = BEDROCK_DATA_PATH . '/biome_definitions_full.nbt';
public const BIOME_ID_MAP_JSON = BEDROCK_DATA_PATH . '/biome_id_map.json';
public const BLOCK_ID_TO_ITEM_ID_MAP_JSON = BEDROCK_DATA_PATH . '/block_id_to_item_id_map.json';
public const BLOCK_PROPERTIES_TABLE_JSON = BEDROCK_DATA_PATH . '/block_properties_table.json';

View File

@ -113,7 +113,6 @@ final class BlockStateNames{
public const RAIL_DATA_BIT = "rail_data_bit";
public const RAIL_DIRECTION = "rail_direction";
public const REDSTONE_SIGNAL = "redstone_signal";
public const REHYDRATION_LEVEL = "rehydration_level";
public const REPEATER_DELAY = "repeater_delay";
public const RESPAWN_ANCHOR_CHARGE = "respawn_anchor_charge";
public const ROTATION = "rotation";

View File

@ -175,9 +175,7 @@ final class BlockTypeNames{
public const BUBBLE_CORAL_FAN = "minecraft:bubble_coral_fan";
public const BUBBLE_CORAL_WALL_FAN = "minecraft:bubble_coral_wall_fan";
public const BUDDING_AMETHYST = "minecraft:budding_amethyst";
public const BUSH = "minecraft:bush";
public const CACTUS = "minecraft:cactus";
public const CACTUS_FLOWER = "minecraft:cactus_flower";
public const CAKE = "minecraft:cake";
public const CALCITE = "minecraft:calcite";
public const CALIBRATED_SCULK_SENSOR = "minecraft:calibrated_sculk_sensor";
@ -392,7 +390,6 @@ final class BlockTypeNames{
public const DOUBLE_CUT_COPPER_SLAB = "minecraft:double_cut_copper_slab";
public const DRAGON_EGG = "minecraft:dragon_egg";
public const DRAGON_HEAD = "minecraft:dragon_head";
public const DRIED_GHAST = "minecraft:dried_ghast";
public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block";
public const DRIPSTONE_BLOCK = "minecraft:dripstone_block";
public const DROPPER = "minecraft:dropper";
@ -548,7 +545,6 @@ final class BlockTypeNames{
public const FIRE_CORAL_BLOCK = "minecraft:fire_coral_block";
public const FIRE_CORAL_FAN = "minecraft:fire_coral_fan";
public const FIRE_CORAL_WALL_FAN = "minecraft:fire_coral_wall_fan";
public const FIREFLY_BUSH = "minecraft:firefly_bush";
public const FLETCHING_TABLE = "minecraft:fletching_table";
public const FLOWER_POT = "minecraft:flower_pot";
public const FLOWERING_AZALEA = "minecraft:flowering_azalea";
@ -689,7 +685,6 @@ final class BlockTypeNames{
public const LARGE_AMETHYST_BUD = "minecraft:large_amethyst_bud";
public const LARGE_FERN = "minecraft:large_fern";
public const LAVA = "minecraft:lava";
public const LEAF_LITTER = "minecraft:leaf_litter";
public const LECTERN = "minecraft:lectern";
public const LEVER = "minecraft:lever";
public const LIGHT_BLOCK_0 = "minecraft:light_block_0";
@ -1048,7 +1043,6 @@ final class BlockTypeNames{
public const SEA_LANTERN = "minecraft:sea_lantern";
public const SEA_PICKLE = "minecraft:sea_pickle";
public const SEAGRASS = "minecraft:seagrass";
public const SHORT_DRY_GRASS = "minecraft:short_dry_grass";
public const SHORT_GRASS = "minecraft:short_grass";
public const SHROOMLIGHT = "minecraft:shroomlight";
public const SILVER_GLAZED_TERRACOTTA = "minecraft:silver_glazed_terracotta";
@ -1146,7 +1140,6 @@ final class BlockTypeNames{
public const SUSPICIOUS_GRAVEL = "minecraft:suspicious_gravel";
public const SUSPICIOUS_SAND = "minecraft:suspicious_sand";
public const SWEET_BERRY_BUSH = "minecraft:sweet_berry_bush";
public const TALL_DRY_GRASS = "minecraft:tall_dry_grass";
public const TALL_GRASS = "minecraft:tall_grass";
public const TARGET = "minecraft:target";
public const TINTED_GLASS = "minecraft:tinted_glass";
@ -1274,7 +1267,6 @@ final class BlockTypeNames{
public const WHITE_TERRACOTTA = "minecraft:white_terracotta";
public const WHITE_TULIP = "minecraft:white_tulip";
public const WHITE_WOOL = "minecraft:white_wool";
public const WILDFLOWERS = "minecraft:wildflowers";
public const WITHER_ROSE = "minecraft:wither_rose";
public const WITHER_SKELETON_SKULL = "minecraft:wither_skeleton_skull";
public const WOODEN_BUTTON = "minecraft:wooden_button";

View File

@ -214,7 +214,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->registerLeavesSerializers();
$this->registerSaplingSerializers();
$this->registerMobHeadSerializers();
$this->registerCopperSerializers();
$this->registerSimpleSerializers();
$this->registerSerializers();
}
@ -792,178 +791,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
})->writeFacingWithoutDown($block->getFacing()));
}
private function registerCopperSerializers() : void{
$this->map(Blocks::COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) :
Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER)
);
});
$this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_CHISELED_COPPER,
Ids::WAXED_EXPOSED_CHISELED_COPPER,
Ids::WAXED_WEATHERED_CHISELED_COPPER,
Ids::WAXED_OXIDIZED_CHISELED_COPPER
) :
Helper::selectCopperId($oxidation,
Ids::CHISELED_COPPER,
Ids::EXPOSED_CHISELED_COPPER,
Ids::WEATHERED_CHISELED_COPPER,
Ids::OXIDIZED_CHISELED_COPPER
)
);
});
$this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_GRATE,
Ids::WAXED_EXPOSED_COPPER_GRATE,
Ids::WAXED_WEATHERED_COPPER_GRATE,
Ids::WAXED_OXIDIZED_COPPER_GRATE
) :
Helper::selectCopperId($oxidation,
Ids::COPPER_GRATE,
Ids::EXPOSED_COPPER_GRATE,
Ids::WEATHERED_COPPER_GRATE,
Ids::OXIDIZED_COPPER_GRATE
)
);
});
$this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) :
Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER)
);
});
$this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeSlab(
$block,
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_SLAB,
Ids::EXPOSED_CUT_COPPER_SLAB,
Ids::WEATHERED_CUT_COPPER_SLAB,
Ids::OXIDIZED_CUT_COPPER_SLAB
)
),
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::DOUBLE_CUT_COPPER_SLAB,
Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB
)
)
);
});
$this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeStairs(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_STAIRS,
Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS,
Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS,
Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_STAIRS,
Ids::EXPOSED_CUT_COPPER_STAIRS,
Ids::WEATHERED_CUT_COPPER_STAIRS,
Ids::OXIDIZED_CUT_COPPER_STAIRS
)
)
);
});
$this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{
$oxidation = $block->getOxidation();
return Writer::create($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_BULB,
Ids::WAXED_EXPOSED_COPPER_BULB,
Ids::WAXED_WEATHERED_COPPER_BULB,
Ids::WAXED_OXIDIZED_COPPER_BULB) :
Helper::selectCopperId($oxidation,
Ids::COPPER_BULB,
Ids::EXPOSED_COPPER_BULB,
Ids::WEATHERED_COPPER_BULB,
Ids::OXIDIZED_COPPER_BULB
))
->writeBool(StateNames::LIT, $block->isLit())
->writeBool(StateNames::POWERED_BIT, $block->isPowered());
});
$this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeDoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_DOOR,
Ids::WAXED_EXPOSED_COPPER_DOOR,
Ids::WAXED_WEATHERED_COPPER_DOOR,
Ids::WAXED_OXIDIZED_COPPER_DOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_DOOR,
Ids::EXPOSED_COPPER_DOOR,
Ids::WEATHERED_COPPER_DOOR,
Ids::OXIDIZED_COPPER_DOOR
)
)
);
});
$this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeTrapdoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_TRAPDOOR,
Ids::WAXED_EXPOSED_COPPER_TRAPDOOR,
Ids::WAXED_WEATHERED_COPPER_TRAPDOOR,
Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_TRAPDOOR,
Ids::EXPOSED_COPPER_TRAPDOOR,
Ids::WEATHERED_COPPER_TRAPDOOR,
Ids::OXIDIZED_COPPER_TRAPDOOR
)
)
);
});
}
private function registerSimpleSerializers() : void{
$this->mapSimple(Blocks::AIR(), Ids::AIR);
$this->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK);
@ -1438,6 +1265,175 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSlab(Blocks::COBBLESTONE_SLAB(), Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS);
$this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL)));
$this->map(Blocks::COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) :
Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER)
);
});
$this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_CHISELED_COPPER,
Ids::WAXED_EXPOSED_CHISELED_COPPER,
Ids::WAXED_WEATHERED_CHISELED_COPPER,
Ids::WAXED_OXIDIZED_CHISELED_COPPER
) :
Helper::selectCopperId($oxidation,
Ids::CHISELED_COPPER,
Ids::EXPOSED_CHISELED_COPPER,
Ids::WEATHERED_CHISELED_COPPER,
Ids::OXIDIZED_CHISELED_COPPER
)
);
});
$this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_GRATE,
Ids::WAXED_EXPOSED_COPPER_GRATE,
Ids::WAXED_WEATHERED_COPPER_GRATE,
Ids::WAXED_OXIDIZED_COPPER_GRATE
) :
Helper::selectCopperId($oxidation,
Ids::COPPER_GRATE,
Ids::EXPOSED_COPPER_GRATE,
Ids::WEATHERED_COPPER_GRATE,
Ids::OXIDIZED_COPPER_GRATE
)
);
});
$this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{
$oxidation = $block->getOxidation();
return new Writer($block->isWaxed() ?
Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) :
Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER)
);
});
$this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeSlab(
$block,
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_SLAB,
Ids::EXPOSED_CUT_COPPER_SLAB,
Ids::WEATHERED_CUT_COPPER_SLAB,
Ids::OXIDIZED_CUT_COPPER_SLAB
)
),
($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB
) :
Helper::selectCopperId(
$oxidation,
Ids::DOUBLE_CUT_COPPER_SLAB,
Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB
)
)
);
});
$this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeStairs(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_CUT_COPPER_STAIRS,
Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS,
Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS,
Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS
) :
Helper::selectCopperId(
$oxidation,
Ids::CUT_COPPER_STAIRS,
Ids::EXPOSED_CUT_COPPER_STAIRS,
Ids::WEATHERED_CUT_COPPER_STAIRS,
Ids::OXIDIZED_CUT_COPPER_STAIRS
)
)
);
});
$this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{
$oxidation = $block->getOxidation();
return Writer::create($block->isWaxed() ?
Helper::selectCopperId($oxidation,
Ids::WAXED_COPPER_BULB,
Ids::WAXED_EXPOSED_COPPER_BULB,
Ids::WAXED_WEATHERED_COPPER_BULB,
Ids::WAXED_OXIDIZED_COPPER_BULB) :
Helper::selectCopperId($oxidation,
Ids::COPPER_BULB,
Ids::EXPOSED_COPPER_BULB,
Ids::WEATHERED_COPPER_BULB,
Ids::OXIDIZED_COPPER_BULB
))
->writeBool(StateNames::LIT, $block->isLit())
->writeBool(StateNames::POWERED_BIT, $block->isPowered());
});
$this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeDoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_DOOR,
Ids::WAXED_EXPOSED_COPPER_DOOR,
Ids::WAXED_WEATHERED_COPPER_DOOR,
Ids::WAXED_OXIDIZED_COPPER_DOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_DOOR,
Ids::EXPOSED_COPPER_DOOR,
Ids::WEATHERED_COPPER_DOOR,
Ids::OXIDIZED_COPPER_DOOR
)
)
);
});
$this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{
$oxidation = $block->getOxidation();
return Helper::encodeTrapdoor(
$block,
new Writer($block->isWaxed() ?
Helper::selectCopperId(
$oxidation,
Ids::WAXED_COPPER_TRAPDOOR,
Ids::WAXED_EXPOSED_COPPER_TRAPDOOR,
Ids::WAXED_WEATHERED_COPPER_TRAPDOOR,
Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR
) :
Helper::selectCopperId(
$oxidation,
Ids::COPPER_TRAPDOOR,
Ids::EXPOSED_COPPER_TRAPDOOR,
Ids::WEATHERED_COPPER_TRAPDOOR,
Ids::OXIDIZED_COPPER_TRAPDOOR
)
)
);
});
$this->map(Blocks::COCOA_POD(), function(CocoaBlock $block) : Writer{
return Writer::create(Ids::COCOA)
->writeInt(StateNames::AGE, $block->getAge())

View File

@ -126,19 +126,12 @@ final class BlockStateDeserializerHelper{
->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15));
}
/**
* @phpstan-template TDoor of Door
* @phpstan-param TDoor $block
* @phpstan-return TDoor
*
* @throws BlockStateDeserializeException
*/
/** @throws BlockStateDeserializeException */
public static function decodeDoor(Door $block, BlockStateReader $in) : Door{
//TODO: check if these need any special treatment to get the appropriate data to both halves of the door
return $block
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT))
//a door facing "east" is actually facing north - thanks mojang
->setFacing(Facing::rotateY($in->readCardinalHorizontalFacing(), clockwise: false))
->setFacing($in->readCardinalHorizontalFacing())
->setHingeRight($in->readBool(BlockStateNames::DOOR_HINGE_BIT))
->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
}
@ -243,36 +236,18 @@ final class BlockStateDeserializerHelper{
return $block->setPressed($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15) !== 0);
}
/**
* @phpstan-template TSlab of Slab
* @phpstan-param TSlab $block
* @phpstan-return TSlab
*
* @throws BlockStateDeserializeException
*/
/** @throws BlockStateDeserializeException */
public static function decodeSingleSlab(Slab $block, BlockStateReader $in) : Slab{
return $block->setSlabType($in->readSlabPosition());
}
/**
* @phpstan-template TSlab of Slab
* @phpstan-param TSlab $block
* @phpstan-return TSlab
*
* @throws BlockStateDeserializeException
*/
/** @throws BlockStateDeserializeException */
public static function decodeDoubleSlab(Slab $block, BlockStateReader $in) : Slab{
$in->ignored(StateNames::MC_VERTICAL_HALF);
return $block->setSlabType(SlabType::DOUBLE);
}
/**
* @phpstan-template TStair of Stair
* @phpstan-param TStair $block
* @phpstan-return TStair
*
* @throws BlockStateDeserializeException
*/
/** @throws BlockStateDeserializeException */
public static function decodeStairs(Stair $block, BlockStateReader $in) : Stair{
return $block
->setUpsideDown($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT))
@ -289,13 +264,7 @@ final class BlockStateDeserializerHelper{
->setFacing($facing === Facing::DOWN ? Facing::UP : $facing);
}
/**
* @phpstan-template TTrapdoor of Trapdoor
* @phpstan-param TTrapdoor $block
* @phpstan-return TTrapdoor
*
* @throws BlockStateDeserializeException
*/
/** @throws BlockStateDeserializeException */
public static function decodeTrapdoor(Trapdoor $block, BlockStateReader $in) : Trapdoor{
return $block
->setFacing($in->read5MinusHorizontalFacing())

View File

@ -100,8 +100,7 @@ final class BlockStateSerializerHelper{
public static function encodeDoor(Door $block, Writer $out) : Writer{
return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
//a door facing north is encoded as "east"
->writeCardinalHorizontalFacing(Facing::rotateY($block->getFacing(), clockwise: true))
->writeCardinalHorizontalFacing($block->getFacing())
->writeBool(BlockStateNames::DOOR_HINGE_BIT, $block->isHingeRight())
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
}

View File

@ -33,13 +33,11 @@ use pocketmine\block\DoublePitcherCrop;
use pocketmine\block\Opaque;
use pocketmine\block\PinkPetals;
use pocketmine\block\PitcherCrop;
use pocketmine\block\RuntimeBlockStateRegistry;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\SweetBerryBush;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DirtType;
@ -61,7 +59,6 @@ use pocketmine\data\bedrock\block\convert\BlockStateDeserializerHelper as Helper
use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\utils\Utils;
use function array_key_exists;
use function count;
use function min;
@ -90,7 +87,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->registerSaplingDeserializers();
$this->registerLightDeserializers();
$this->registerMobHeadDeserializers();
$this->registerCopperDeserializers();
$this->registerSimpleDeserializers();
$this->registerDeserializers();
}
@ -98,21 +94,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
public function deserialize(BlockStateData $stateData) : int{
if(count($stateData->getStates()) === 0){
//if a block has zero properties, we can keep a map of string ID -> internal blockstate ID
return $this->simpleCache[$stateData->getName()] ??= $this->deserializeToStateId($stateData);
return $this->simpleCache[$stateData->getName()] ??= $this->deserializeBlock($stateData)->getStateId();
}
//we can't cache blocks that have properties - go ahead and deserialize the slow way
return $this->deserializeToStateId($stateData);
}
private function deserializeToStateId(BlockStateData $stateData) : int{
$stateId = $this->deserializeBlock($stateData)->getStateId();
//plugin devs seem to keep missing this and causing core crashes, so we need to verify this at the earliest
//available opportunity
if(!RuntimeBlockStateRegistry::getInstance()->hasStateId($stateId)){
throw new \LogicException("State ID $stateId returned by deserializer for " . $stateData->getName() . " is not registered in RuntimeBlockStateRegistry");
}
return $stateId;
return $this->deserializeBlock($stateData)->getStateId();
}
/** @phpstan-param \Closure(Reader) : Block $c */
@ -737,150 +723,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
}
}
/**
* @phpstan-param \Closure(Reader) : (CopperMaterial&Block) $deserializer
*/
private function mapCopper(
string $normalId,
string $waxedNormalId,
string $exposedId,
string $waxedExposedId,
string $weatheredId,
string $waxedWeatheredId,
string $oxidizedId,
string $waxedOxidizedId,
\Closure $deserializer
) : void{
foreach(Utils::stringifyKeys([
$normalId => [CopperOxidation::NONE, false],
$waxedNormalId => [CopperOxidation::NONE, true],
$exposedId => [CopperOxidation::EXPOSED, false],
$waxedExposedId => [CopperOxidation::EXPOSED, true],
$weatheredId => [CopperOxidation::WEATHERED, false],
$waxedWeatheredId => [CopperOxidation::WEATHERED, true],
$oxidizedId => [CopperOxidation::OXIDIZED, false],
$waxedOxidizedId => [CopperOxidation::OXIDIZED, true],
]) as $id => [$oxidation, $waxed]){
$this->map($id, fn(Reader $in) => $deserializer($in)->setOxidation($oxidation)->setWaxed($waxed));
}
}
private function registerCopperDeserializers() : void{
$this->mapCopper(
Ids::CUT_COPPER_SLAB,
Ids::WAXED_CUT_COPPER_SLAB,
Ids::EXPOSED_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_CUT_COPPER_SLAB,
Ids::WEATHERED_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_CUT_COPPER_SLAB,
Ids::OXIDIZED_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB,
fn(Reader $in) => Helper::decodeSingleSlab(Blocks::CUT_COPPER_SLAB(), $in)
);
$this->mapCopper(
Ids::DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_DOUBLE_CUT_COPPER_SLAB,
Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB,
Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB,
Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB,
Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB,
fn(Reader $in) => Helper::decodeDoubleSlab(Blocks::CUT_COPPER_SLAB(), $in)
);
$this->mapCopper(
Ids::COPPER_BULB,
Ids::WAXED_COPPER_BULB,
Ids::EXPOSED_COPPER_BULB,
Ids::WAXED_EXPOSED_COPPER_BULB,
Ids::WEATHERED_COPPER_BULB,
Ids::WAXED_WEATHERED_COPPER_BULB,
Ids::OXIDIZED_COPPER_BULB,
Ids::WAXED_OXIDIZED_COPPER_BULB,
fn(Reader $in) => Blocks::COPPER_BULB()
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT))
);
$this->mapCopper(
Ids::COPPER_DOOR,
Ids::WAXED_COPPER_DOOR,
Ids::EXPOSED_COPPER_DOOR,
Ids::WAXED_EXPOSED_COPPER_DOOR,
Ids::WEATHERED_COPPER_DOOR,
Ids::WAXED_WEATHERED_COPPER_DOOR,
Ids::OXIDIZED_COPPER_DOOR,
Ids::WAXED_OXIDIZED_COPPER_DOOR,
fn(Reader $in) => Helper::decodeDoor(Blocks::COPPER_DOOR(), $in)
);
$this->mapCopper(
Ids::COPPER_TRAPDOOR,
Ids::WAXED_COPPER_TRAPDOOR,
Ids::EXPOSED_COPPER_TRAPDOOR,
Ids::WAXED_EXPOSED_COPPER_TRAPDOOR,
Ids::WEATHERED_COPPER_TRAPDOOR,
Ids::WAXED_WEATHERED_COPPER_TRAPDOOR,
Ids::OXIDIZED_COPPER_TRAPDOOR,
Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR,
fn(Reader $in) => Helper::decodeTrapdoor(Blocks::COPPER_TRAPDOOR(), $in)
);
$this->mapCopper(
Ids::COPPER_BLOCK,
Ids::WAXED_COPPER,
Ids::EXPOSED_COPPER,
Ids::WAXED_EXPOSED_COPPER,
Ids::WEATHERED_COPPER,
Ids::WAXED_WEATHERED_COPPER,
Ids::OXIDIZED_COPPER,
Ids::WAXED_OXIDIZED_COPPER,
fn(Reader $in) => Blocks::COPPER()
);
$this->mapCopper(
Ids::CHISELED_COPPER,
Ids::WAXED_CHISELED_COPPER,
Ids::EXPOSED_CHISELED_COPPER,
Ids::WAXED_EXPOSED_CHISELED_COPPER,
Ids::WEATHERED_CHISELED_COPPER,
Ids::WAXED_WEATHERED_CHISELED_COPPER,
Ids::OXIDIZED_CHISELED_COPPER,
Ids::WAXED_OXIDIZED_CHISELED_COPPER,
fn(Reader $in) => Blocks::CHISELED_COPPER()
);
$this->mapCopper(
Ids::COPPER_GRATE,
Ids::WAXED_COPPER_GRATE,
Ids::EXPOSED_COPPER_GRATE,
Ids::WAXED_EXPOSED_COPPER_GRATE,
Ids::WEATHERED_COPPER_GRATE,
Ids::WAXED_WEATHERED_COPPER_GRATE,
Ids::OXIDIZED_COPPER_GRATE,
Ids::WAXED_OXIDIZED_COPPER_GRATE,
fn(Reader $in) => Blocks::COPPER_GRATE()
);
$this->mapCopper(
Ids::CUT_COPPER,
Ids::WAXED_CUT_COPPER,
Ids::EXPOSED_CUT_COPPER,
Ids::WAXED_EXPOSED_CUT_COPPER,
Ids::WEATHERED_CUT_COPPER,
Ids::WAXED_WEATHERED_CUT_COPPER,
Ids::OXIDIZED_CUT_COPPER,
Ids::WAXED_OXIDIZED_CUT_COPPER,
fn(Reader $in) => Blocks::CUT_COPPER()
);
$this->mapCopper(
Ids::CUT_COPPER_STAIRS,
Ids::WAXED_CUT_COPPER_STAIRS,
Ids::EXPOSED_CUT_COPPER_STAIRS,
Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS,
Ids::WEATHERED_CUT_COPPER_STAIRS,
Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS,
Ids::OXIDIZED_CUT_COPPER_STAIRS,
Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS,
fn(Reader $in) => Helper::decodeStairs(Blocks::CUT_COPPER_STAIRS(), $in)
);
}
private function registerSimpleDeserializers() : void{
$this->mapSimple(Ids::AIR, fn() => Blocks::AIR());
$this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST());
@ -1386,6 +1228,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::COMPOUND_CREATOR, fn(Reader $in) => Blocks::COMPOUND_CREATOR()
->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()))
);
$this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE));
$this->map(Ids::COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
$this->map(Ids::COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
$this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
$this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
$this->mapSlab(Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_RED_SANDSTONE_SLAB());
$this->mapSlab(Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_SANDSTONE_SLAB());
$this->mapSlab(Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB, fn() => Blocks::DARK_PRISMARINE_SLAB());
@ -1440,6 +1294,19 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::ENDER_CHEST()
->setFacing($in->readCardinalHorizontalFacing());
});
$this->map(Ids::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
$this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
$this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
$this->map(Ids::EXPOSED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::FARMLAND, function(Reader $in) : Block{
return Blocks::FARMLAND()
->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7));
@ -1592,6 +1459,19 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSlab(Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB, fn() => Blocks::STONE_SLAB());
$this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS());
$this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis()));
$this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
$this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
$this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
$this->map(Ids::OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis()));
$this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB());
$this->map(Ids::PINK_PETALS, function(Reader $in) : Block{
@ -1864,7 +1744,71 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setFacing($in->readHorizontalFacing());
});
$this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in));
$this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE));
$this->map(Ids::WAXED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE));
$this->map(Ids::WAXED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
$this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
$this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
$this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
$this->map(Ids::WAXED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::WAXED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
$this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
$this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
$this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
$this->map(Ids::WAXED_EXPOSED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
$this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
$this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
$this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
$this->map(Ids::WAXED_OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
$this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
$this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
$this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
$this->map(Ids::WAXED_WEATHERED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WAXED_WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
$this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
$this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
$this->map(Ids::WEATHERED_COPPER_BULB, function(Reader $in) : Block{
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
->setLit($in->readBool(StateNames::LIT))
->setPowered($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
$this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{
return Blocks::WEEPING_VINES()
->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25));

View File

@ -68,7 +68,6 @@ final class ItemTypeNames{
public const BIRCH_SIGN = "minecraft:birch_sign";
public const BLACK_BUNDLE = "minecraft:black_bundle";
public const BLACK_DYE = "minecraft:black_dye";
public const BLACK_HARNESS = "minecraft:black_harness";
public const BLADE_POTTERY_SHERD = "minecraft:blade_pottery_sherd";
public const BLAZE_POWDER = "minecraft:blaze_powder";
public const BLAZE_ROD = "minecraft:blaze_rod";
@ -76,8 +75,6 @@ final class ItemTypeNames{
public const BLEACH = "minecraft:bleach";
public const BLUE_BUNDLE = "minecraft:blue_bundle";
public const BLUE_DYE = "minecraft:blue_dye";
public const BLUE_EGG = "minecraft:blue_egg";
public const BLUE_HARNESS = "minecraft:blue_harness";
public const BOARD = "minecraft:board";
public const BOAT = "minecraft:boat";
public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg";
@ -96,8 +93,6 @@ final class ItemTypeNames{
public const BRICK = "minecraft:brick";
public const BROWN_BUNDLE = "minecraft:brown_bundle";
public const BROWN_DYE = "minecraft:brown_dye";
public const BROWN_EGG = "minecraft:brown_egg";
public const BROWN_HARNESS = "minecraft:brown_harness";
public const BRUSH = "minecraft:brush";
public const BUCKET = "minecraft:bucket";
public const BUNDLE = "minecraft:bundle";
@ -169,7 +164,6 @@ final class ItemTypeNames{
public const CROSSBOW = "minecraft:crossbow";
public const CYAN_BUNDLE = "minecraft:cyan_bundle";
public const CYAN_DYE = "minecraft:cyan_dye";
public const CYAN_HARNESS = "minecraft:cyan_harness";
public const DANGER_POTTERY_SHERD = "minecraft:danger_pottery_sherd";
public const DARK_OAK_BOAT = "minecraft:dark_oak_boat";
public const DARK_OAK_CHEST_BOAT = "minecraft:dark_oak_chest_boat";
@ -269,15 +263,12 @@ final class ItemTypeNames{
public const GOLDEN_SWORD = "minecraft:golden_sword";
public const GRAY_BUNDLE = "minecraft:gray_bundle";
public const GRAY_DYE = "minecraft:gray_dye";
public const GRAY_HARNESS = "minecraft:gray_harness";
public const GREEN_BUNDLE = "minecraft:green_bundle";
public const GREEN_DYE = "minecraft:green_dye";
public const GREEN_HARNESS = "minecraft:green_harness";
public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg";
public const GUNPOWDER = "minecraft:gunpowder";
public const GUSTER_BANNER_PATTERN = "minecraft:guster_banner_pattern";
public const GUSTER_POTTERY_SHERD = "minecraft:guster_pottery_sherd";
public const HAPPY_GHAST_SPAWN_EGG = "minecraft:happy_ghast_spawn_egg";
public const HARD_STAINED_GLASS = "minecraft:hard_stained_glass";
public const HARD_STAINED_GLASS_PANE = "minecraft:hard_stained_glass_pane";
public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea";
@ -328,13 +319,10 @@ final class ItemTypeNames{
public const LIGHT_BLOCK = "minecraft:light_block";
public const LIGHT_BLUE_BUNDLE = "minecraft:light_blue_bundle";
public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye";
public const LIGHT_BLUE_HARNESS = "minecraft:light_blue_harness";
public const LIGHT_GRAY_BUNDLE = "minecraft:light_gray_bundle";
public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye";
public const LIGHT_GRAY_HARNESS = "minecraft:light_gray_harness";
public const LIME_BUNDLE = "minecraft:lime_bundle";
public const LIME_DYE = "minecraft:lime_dye";
public const LIME_HARNESS = "minecraft:lime_harness";
public const LINGERING_POTION = "minecraft:lingering_potion";
public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg";
public const LODESTONE_COMPASS = "minecraft:lodestone_compass";
@ -343,7 +331,6 @@ final class ItemTypeNames{
public const MACE = "minecraft:mace";
public const MAGENTA_BUNDLE = "minecraft:magenta_bundle";
public const MAGENTA_DYE = "minecraft:magenta_dye";
public const MAGENTA_HARNESS = "minecraft:magenta_harness";
public const MAGMA_CREAM = "minecraft:magma_cream";
public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg";
public const MANGROVE_BOAT = "minecraft:mangrove_boat";
@ -411,7 +398,6 @@ final class ItemTypeNames{
public const OMINOUS_TRIAL_KEY = "minecraft:ominous_trial_key";
public const ORANGE_BUNDLE = "minecraft:orange_bundle";
public const ORANGE_DYE = "minecraft:orange_dye";
public const ORANGE_HARNESS = "minecraft:orange_harness";
public const OXIDIZED_COPPER_DOOR = "minecraft:oxidized_copper_door";
public const PAINTING = "minecraft:painting";
public const PALE_OAK_BOAT = "minecraft:pale_oak_boat";
@ -431,7 +417,6 @@ final class ItemTypeNames{
public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg";
public const PINK_BUNDLE = "minecraft:pink_bundle";
public const PINK_DYE = "minecraft:pink_dye";
public const PINK_HARNESS = "minecraft:pink_harness";
public const PITCHER_POD = "minecraft:pitcher_pod";
public const PLANKS = "minecraft:planks";
public const PLENTY_POTTERY_SHERD = "minecraft:plenty_pottery_sherd";
@ -452,7 +437,6 @@ final class ItemTypeNames{
public const PUMPKIN_SEEDS = "minecraft:pumpkin_seeds";
public const PURPLE_BUNDLE = "minecraft:purple_bundle";
public const PURPLE_DYE = "minecraft:purple_dye";
public const PURPLE_HARNESS = "minecraft:purple_harness";
public const QUARTZ = "minecraft:quartz";
public const RABBIT = "minecraft:rabbit";
public const RABBIT_FOOT = "minecraft:rabbit_foot";
@ -469,7 +453,6 @@ final class ItemTypeNames{
public const RED_BUNDLE = "minecraft:red_bundle";
public const RED_DYE = "minecraft:red_dye";
public const RED_FLOWER = "minecraft:red_flower";
public const RED_HARNESS = "minecraft:red_harness";
public const REDSTONE = "minecraft:redstone";
public const REPEATER = "minecraft:repeater";
public const RESIN_BRICK = "minecraft:resin_brick";
@ -578,7 +561,6 @@ final class ItemTypeNames{
public const WHEAT_SEEDS = "minecraft:wheat_seeds";
public const WHITE_BUNDLE = "minecraft:white_bundle";
public const WHITE_DYE = "minecraft:white_dye";
public const WHITE_HARNESS = "minecraft:white_harness";
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";
@ -599,7 +581,6 @@ final class ItemTypeNames{
public const WRITTEN_BOOK = "minecraft:written_book";
public const YELLOW_BUNDLE = "minecraft:yellow_bundle";
public const YELLOW_DYE = "minecraft:yellow_dye";
public const YELLOW_HARNESS = "minecraft:yellow_harness";
public const ZOGLIN_SPAWN_EGG = "minecraft:zoglin_spawn_egg";
public const ZOMBIE_HORSE_SPAWN_EGG = "minecraft:zombie_horse_spawn_egg";
public const ZOMBIE_PIGMAN_SPAWN_EGG = "minecraft:zombie_pigman_spawn_egg";

View File

@ -60,7 +60,6 @@ use pocketmine\player\Player;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
use pocketmine\utils\Limits;
use pocketmine\utils\Utils;
use pocketmine\VersionInfo;
use pocketmine\world\format\Chunk;
@ -77,7 +76,6 @@ use function floatval;
use function floor;
use function fmod;
use function get_class;
use function min;
use function sin;
use function spl_object_id;
use const M_PI_2;
@ -702,16 +700,9 @@ abstract class Entity{
* @throws \InvalidArgumentException
*/
public function setFireTicks(int $fireTicks) : void{
if($fireTicks < 0){
throw new \InvalidArgumentException("Fire ticks cannot be negative");
if($fireTicks < 0 || $fireTicks > 0x7fff){
throw new \InvalidArgumentException("Fire ticks must be in range 0 ... " . 0x7fff . ", got $fireTicks");
}
//Since the max value is not externally obvious or intuitive, many plugins use this without being aware that
//reasonably large values are not accepted. We even have such usages within PM itself. It doesn't make sense
//to force all those calls to be aware of this limitation, as it's not a functional limit but a limitation of
//the Mojang save format. Truncating this to the max acceptable value is the next best thing we can do.
$fireTicks = min($fireTicks, Limits::INT16_MAX);
if(!$this->isFireProof()){
$this->fireTicks = $fireTicks;
$this->networkPropertiesDirty = true;
@ -1504,7 +1495,7 @@ abstract class Entity{
$this->getId(), //TODO: actor unique ID
$this->getId(),
static::getNetworkTypeId(),
$this->getOffsetPosition($this->location->asVector3()),
$this->location->asVector3(),
$this->getMotion(),
$this->location->pitch,
$this->location->yaw,

View File

@ -111,8 +111,7 @@ final class LegacyStringToItemParser{
*/
public function parse(string $input) : Item{
$key = $this->reprocess($input);
//TODO: this should be limited to 2 parts, but 3 preserves old behaviour when given a string like 351:4:1
$b = explode(":", $key, limit: 3);
$b = explode(":", $key);
if(!isset($b[1])){
$meta = 0;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\block\utils\RecordType;
use pocketmine\block\utils\WoodType;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\entity\Entity;
use pocketmine\entity\Location;
@ -47,6 +48,8 @@ use function strtolower;
* @see build/generate-registry-annotations.php
* @generate-registry-docblock
*
* @method static Boat BOAT(BoatType $boatType)
* @method static ItemBlockWallOrFloor SIGN(\pocketmine\block\utils\WoodType $woodType)
* @method static Boat ACACIA_BOAT()
* @method static ItemBlockWallOrFloor ACACIA_SIGN()
* @method static ItemBlock AIR()
@ -396,7 +399,6 @@ final class VanillaItems{
//in the future we'll probably want to dissociate this from the air block and make a proper null item
self::_registryRegister("air", Blocks::AIR()->asItem()->setCount(0));
self::register("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN()));
self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard"));
self::register("apple", fn(IID $id) => new Apple($id, "Apple"));
self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow"));
@ -406,7 +408,6 @@ final class VanillaItems{
self::register("beetroot", fn(IID $id) => new Beetroot($id, "Beetroot"));
self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds"));
self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup"));
self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN()));
self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder"));
self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod"));
self::register("bleach", fn(IID $id) => new Item($id, "Bleach"));
@ -420,7 +421,6 @@ final class VanillaItems{
self::register("bucket", fn(IID $id) => new Bucket($id, "Bucket"));
self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot"));
self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal"));
self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN()));
self::register("chemical_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide"));
self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia"));
self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate"));
@ -475,8 +475,6 @@ final class VanillaItems{
self::register("cookie", fn(IID $id) => new Cookie($id, "Cookie"));
self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot"));
self::register("coral_fan", fn(IID $id) => new CoralFan($id));
self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN()));
self::register("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN()));
self::register("diamond", fn(IID $id) => new Item($id, "Diamond"));
self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)"));
self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath"));
@ -516,12 +514,10 @@ final class VanillaItems{
self::register("ink_sac", fn(IID $id) => new Item($id, "Ink Sac"));
self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot"));
self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget"));
self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN()));
self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli"));
self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA()));
self::register("leather", fn(IID $id) => new Item($id, "Leather"));
self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream"));
self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN()));
self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine"));
self::register("melon", fn(IID $id) => new Melon($id, "Melon"));
self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds"));
@ -539,9 +535,7 @@ final class VanillaItems{
self::register("netherite_scrap", fn(IID $id) => new class($id, "Netherite Scrap") extends Item{
public function isFireProof() : bool{ return true; }
});
self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN()));
self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting"));
self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN()));
self::register("paper", fn(IID $id) => new Item($id, "Paper"));
self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane"));
self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod"));
@ -597,7 +591,6 @@ final class VanillaItems{
self::register("snowball", fn(IID $id) => new Snowball($id, "Snowball"));
self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye"));
self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion"));
self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN()));
self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass"));
self::register("steak", fn(IID $id) => new Steak($id, "Steak"));
self::register("stick", fn(IID $id) => new Stick($id, "Stick"));
@ -607,7 +600,6 @@ final class VanillaItems{
self::register("sweet_berries", fn(IID $id) => new SweetBerries($id, "Sweet Berries"));
self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds"));
self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying"));
self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN()));
self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER()));
self::register("wheat", fn(IID $id) => new Item($id, "Wheat"));
self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds"));
@ -618,6 +610,12 @@ final class VanillaItems{
//boat type is static, because different types of wood may have different properties
self::register(strtolower($type->name) . "_boat", fn(IID $id) => new Boat($id, $type->getDisplayName() . " Boat", $type));
}
self::registerOverloaded("boat", BoatType::class, Boat::class);
foreach(WoodType::cases() as $woodType){
self::register("{$woodType->name}_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SIGN($woodType), Blocks::WALL_SIGN($woodType)));
}
self::registerOverloaded("sign", WoodType::class, ItemBlockWallOrFloor::class);
}
private static function registerSpawnEggs() : void{

View File

@ -28,7 +28,6 @@ use pocketmine\item\enchantment\ItemEnchantmentTags as Tags;
use pocketmine\item\enchantment\VanillaEnchantments as Enchantments;
use pocketmine\item\Item;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils;
use function array_filter;
use function array_values;
use function count;
@ -130,7 +129,6 @@ final class AvailableEnchantmentRegistry{
if(!$this->isRegistered($enchantment)){
throw new \LogicException("Cannot set primary item tags for non-registered enchantment");
}
Utils::validateArrayValueType($tags, fn(string $v) => 1);
$this->primaryItemTags[spl_object_id($enchantment)] = array_values($tags);
}
@ -154,7 +152,6 @@ final class AvailableEnchantmentRegistry{
if(!$this->isRegistered($enchantment)){
throw new \LogicException("Cannot set secondary item tags for non-registered enchantment");
}
Utils::validateArrayValueType($tags, fn(string $v) => 1);
$this->secondaryItemTags[spl_object_id($enchantment)] = array_values($tags);
}

View File

@ -71,7 +71,7 @@ class Language{
foreach($files as $file){
try{
$code = explode(".", $file, limit: 2)[0];
$code = explode(".", $file)[0];
$strings = self::loadLang($path, $code);
if(isset($strings[KnownTranslationKeys::LANGUAGE_NAME])){
$result[$code] = $strings[KnownTranslationKeys::LANGUAGE_NAME];

View File

@ -72,11 +72,9 @@ final class JwtUtils{
* @throws JwtException
*/
public static function split(string $jwt) : array{
//limit of 4 allows us to detect too many parts without having to split the string up into a potentially large
//number of parts
$v = explode(".", $jwt, limit: 4);
$v = explode(".", $jwt);
if(count($v) !== 3){
throw new JwtException("Expected exactly 3 JWT parts delimited by a period");
throw new JwtException("Expected exactly 3 JWT parts, got " . count($v));
}
return [$v[0], $v[1], $v[2]]; //workaround phpstan bug
}

View File

@ -23,22 +23,13 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\cache;
use pocketmine\color\Color;
use pocketmine\data\bedrock\BedrockDataFiles;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket;
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
use pocketmine\network\mcpe\protocol\types\biome\BiomeDefinitionEntry;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\utils\Filesystem;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils;
use pocketmine\world\biome\model\BiomeDefinitionEntryData;
use function count;
use function get_debug_type;
use function is_array;
use function json_decode;
class StaticPacketCache{
use SingletonTrait;
@ -50,61 +41,9 @@ class StaticPacketCache{
return new CacheableNbt((new NetworkNbtSerializer())->read(Filesystem::fileGetContents($filePath))->mustGetCompoundTag());
}
/**
* @return list<BiomeDefinitionEntry>
*/
private static function loadBiomeDefinitionModel(string $filePath) : array{
$biomeEntries = json_decode(Filesystem::fileGetContents($filePath), associative: true);
if(!is_array($biomeEntries)){
throw new SavedDataLoadingException("$filePath root should be an array, got " . get_debug_type($biomeEntries));
}
$jsonMapper = new \JsonMapper();
$jsonMapper->bExceptionOnMissingData = true;
$jsonMapper->bStrictObjectTypeChecking = true;
$jsonMapper->bEnforceMapType = false;
$entries = [];
foreach(Utils::promoteKeys($biomeEntries) as $biomeName => $entry){
if(!is_array($entry)){
throw new SavedDataLoadingException("$filePath should be an array of objects, got " . get_debug_type($entry));
}
try{
$biomeDefinition = $jsonMapper->map($entry, new BiomeDefinitionEntryData());
$mapWaterColour = $biomeDefinition->mapWaterColour;
$entries[] = new BiomeDefinitionEntry(
(string) $biomeName,
$biomeDefinition->id,
$biomeDefinition->temperature,
$biomeDefinition->downfall,
$biomeDefinition->redSporeDensity,
$biomeDefinition->blueSporeDensity,
$biomeDefinition->ashDensity,
$biomeDefinition->whiteAshDensity,
$biomeDefinition->depth,
$biomeDefinition->scale,
new Color(
$mapWaterColour->r,
$mapWaterColour->g,
$mapWaterColour->b,
$mapWaterColour->a
),
$biomeDefinition->rain,
count($biomeDefinition->tags) > 0 ? $biomeDefinition->tags : null,
);
}catch(\JsonMapper_Exception $e){
throw new \RuntimeException($e->getMessage(), 0, $e);
}
}
return $entries;
}
private static function make() : self{
return new self(
BiomeDefinitionListPacket::fromDefinitions(self::loadBiomeDefinitionModel(BedrockDataFiles::BIOME_DEFINITIONS_JSON)),
BiomeDefinitionListPacket::create(self::loadCompoundFromFile(BedrockDataFiles::BIOME_DEFINITIONS_NBT)),
AvailableActorIdentifiersPacket::create(self::loadCompoundFromFile(BedrockDataFiles::ENTITY_IDENTIFIERS_NBT))
);
}

View File

@ -64,6 +64,7 @@ use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
use pocketmine\network\mcpe\protocol\LabTablePacket;
use pocketmine\network\mcpe\protocol\LecternUpdatePacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacketV1;
use pocketmine\network\mcpe\protocol\MapInfoRequestPacket;
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
@ -73,6 +74,7 @@ use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
use pocketmine\network\mcpe\protocol\serializer\BitSet;
@ -294,6 +296,10 @@ class InGamePacketHandler extends PacketHandler{
return $packetHandled;
}
public function handleLevelSoundEventPacketV1(LevelSoundEventPacketV1 $packet) : bool{
return true; //useless leftover from 1.8
}
public function handleActorEvent(ActorEventPacket $packet) : bool{
if($packet->actorRuntimeId !== $this->player->getId()){
//TODO HACK: EATING_ITEM is sent back to the server when the server sends it for other players (1.14 bug, maybe earlier)
@ -780,6 +786,10 @@ class InGamePacketHandler extends PacketHandler{
return false;
}
public function handlePlayerInput(PlayerInputPacket $packet) : bool{
return false; //TODO
}
public function handleSetPlayerGameType(SetPlayerGameTypePacket $packet) : bool{
$gameMode = $this->session->getTypeConverter()->protocolGameModeToCore($packet->gamemode);
if($gameMode !== $this->player->getGamemode()){

View File

@ -148,9 +148,7 @@ class BanEntry{
return null;
}
//we expect at most 5 parts, but accept 6 in case of an extra unexpected delimiter
//we don't want to include unexpected data into the ban reason
$parts = explode("|", trim($str), limit: 6);
$parts = explode("|", trim($str));
$entry = new BanEntry(trim(array_shift($parts)));
if(count($parts) > 0){
$entry->setCreated(self::parseDate(array_shift($parts)));

View File

@ -26,7 +26,6 @@ namespace pocketmine\resourcepacks;
use pocketmine\utils\Config;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Filesystem\Path;
use function array_keys;
use function copy;
@ -104,14 +103,9 @@ class ResourcePackManager{
try{
$newPack = $this->loadPackFromPath(Path::join($this->path, $pack));
$index = strtolower($newPack->getPackId());
if(!Uuid::isValid($index)){
//TODO: we should use Uuid in ResourcePack interface directly but that would break BC
//for now we need to validate this here to make sure it doesn't cause crashes later on
throw new ResourcePackException("Invalid UUID ($index)");
}
$this->uuidList[$index] = $newPack;
$this->resourcePacks[] = $newPack;
$index = strtolower($newPack->getPackId());
$this->uuidList[$index] = $newPack;
$keyPath = Path::join($this->path, $pack . ".key");
if(file_exists($keyPath)){
@ -196,11 +190,6 @@ class ResourcePackManager{
$resourcePacks = [];
foreach($resourceStack as $pack){
$uuid = strtolower($pack->getPackId());
if(!Uuid::isValid($uuid)){
//TODO: we should use Uuid in ResourcePack interface directly but that would break BC
//for now we need to validate this here to make sure it doesn't cause crashes later on
throw new \InvalidArgumentException("Invalid resource pack UUID ($uuid)");
}
if(isset($uuidList[$uuid])){
throw new \InvalidArgumentException("Cannot load two resource pack with the same UUID ($uuid)");
}

View File

@ -54,7 +54,6 @@ use const CASE_LOWER;
use const JSON_BIGINT_AS_STRING;
use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR;
use const PHP_INT_MAX;
use const YAML_UTF8_ENCODING;
/**
@ -340,7 +339,7 @@ class Config{
}
public function setNested(string $key, mixed $value) : void{
$vars = explode(".", $key, limit: PHP_INT_MAX);
$vars = explode(".", $key);
$base = array_shift($vars);
if(!isset($this->config[$base])){
@ -367,7 +366,7 @@ class Config{
return $this->nestedCache[$key];
}
$vars = explode(".", $key, limit: PHP_INT_MAX);
$vars = explode(".", $key);
$base = array_shift($vars);
if(isset($this->config[$base])){
$base = $this->config[$base];
@ -391,7 +390,7 @@ class Config{
$this->nestedCache = [];
$this->changed = true;
$vars = explode(".", $key, limit: PHP_INT_MAX);
$vars = explode(".", $key);
$currentNode = &$this->config;
while(count($vars) > 0){
@ -496,7 +495,7 @@ class Config{
*/
public static function parseList(string $content) : array{
$result = [];
foreach(explode("\n", trim(str_replace("\r\n", "\n", $content)), limit: PHP_INT_MAX) as $v){
foreach(explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v){
$v = trim($v);
if($v === ""){
continue;

View File

@ -60,7 +60,6 @@ use const CURLOPT_RETURNTRANSFER;
use const CURLOPT_SSL_VERIFYHOST;
use const CURLOPT_SSL_VERIFYPEER;
use const CURLOPT_TIMEOUT_MS;
use const PHP_INT_MAX;
use const SOCK_DGRAM;
use const SOL_UDP;
@ -228,10 +227,9 @@ class Internet{
$rawHeaders = substr($raw, 0, $headerSize);
$body = substr($raw, $headerSize);
$headers = [];
//TODO: explore if we can set these limits lower
foreach(explode("\r\n\r\n", $rawHeaders, limit: PHP_INT_MAX) as $rawHeaderGroup){
foreach(explode("\r\n\r\n", $rawHeaders) as $rawHeaderGroup){
$headerGroup = [];
foreach(explode("\r\n", $rawHeaderGroup, limit: PHP_INT_MAX) as $line){
foreach(explode("\r\n", $rawHeaderGroup) as $line){
$nameValue = explode(":", $line, 2);
if(isset($nameValue[1])){
$headerGroup[trim(strtolower($nameValue[0]))] = trim($nameValue[1]);

View File

@ -21,21 +21,19 @@
declare(strict_types=1);
namespace pocketmine\world\biome\model;
namespace pocketmine\utils;
/**
* Model for loading color data from JSON.
*/
final class ColorData{
/** @required */
public int $r;
final class OverloadedRegistryMember{
/** @required */
public int $g;
/** @required */
public int $b;
/** @required */
public int $a;
/**
* @phpstan-template TMember of object
* @phpstan-param class-string<covariant \UnitEnum> $enumClass
* @phpstan-param class-string<TMember> $memberClass
* @phpstan-param array<string, string> $enumToMemberMap
*/
public function __construct(
public readonly string $enumClass,
public readonly string $memberClass,
public readonly array $enumToMemberMap
){}
}

View File

@ -24,7 +24,9 @@ declare(strict_types=1);
namespace pocketmine\utils;
use function array_map;
use function assert;
use function count;
use function get_class;
use function mb_strtoupper;
use function preg_match;
@ -42,6 +44,12 @@ trait RegistryTrait{
*/
private static $members = null;
/**
* @var OverloadedRegistryMember[]
* @phpstan-var array<string, OverloadedRegistryMember>
*/
private static $overloadedMembers = [];
private static function verifyName(string $name) : void{
if(preg_match('/^(?!\d)[A-Za-z\d_]+$/u', $name) === 0){
throw new \InvalidArgumentException("Invalid member name \"$name\", should only contain letters, numbers and underscores, and must not start with a number");
@ -59,12 +67,37 @@ trait RegistryTrait{
}
self::verifyName($name);
$upperName = mb_strtoupper($name);
if(isset(self::$members[$upperName])){
if(isset(self::$members[$upperName]) || isset(self::$overloadedMembers[$upperName])){
throw new \InvalidArgumentException("\"$upperName\" is already reserved");
}
self::$members[$upperName] = $member;
}
/**
* @phpstan-template TEnum of \UnitEnum
* @phpstan-param class-string<TEnum> $enumClass
* @phpstan-param class-string $returnClass
*/
private static function registerOverloaded(string $baseName, string $enumClass, string $returnClass) : void{
self::verifyName($baseName);
$upperName = mb_strtoupper($baseName);
if(isset(self::$members[$upperName]) || isset(self::$overloadedMembers[$upperName])){
throw new \InvalidArgumentException("\"$upperName\" is already reserved");
}
$enumToMemberMap = [];
foreach($enumClass::cases() as $case){
$memberName = mb_strtoupper($case->name . "_" . $baseName);
if(!isset(self::$members[$memberName])){
throw new \LogicException("\"$memberName\" needs to be registered to define overloaded member with enum $enumClass");
}
if(!self::$members[$memberName] instanceof $returnClass){
throw new \LogicException("\"$memberName\" doesn't satisfy the desired type $returnClass");
}
$enumToMemberMap[$case->name] = $memberName;
}
self::$overloadedMembers[$upperName] = new OverloadedRegistryMember($enumClass, $returnClass, $enumToMemberMap);
}
/**
* Inserts default entries into the registry.
*
@ -111,20 +144,36 @@ trait RegistryTrait{
* @return object
*/
public static function __callStatic($name, $arguments){
if(count($arguments) > 0){
throw new \ArgumentCountError("Expected exactly 0 arguments, " . count($arguments) . " passed");
}
if(count($arguments) === 0){
//fast path
if(self::$members !== null && isset(self::$members[$name])){
return self::preprocessMember(self::$members[$name]);
}
//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){
throw new \Error($e->getMessage(), 0, $e);
}
}elseif(count($arguments) === 1 && $arguments[0] instanceof \UnitEnum){
$enum = $arguments[0];
self::checkInit();
//fallback
try{
return self::_registryFromString($name);
}catch(\InvalidArgumentException $e){
throw new \Error($e->getMessage(), 0, $e);
$overloadInfo = self::$overloadedMembers[$name] ?? self::$overloadedMembers[mb_strtoupper($name)] ?? null;
if($overloadInfo !== null){
if($enum::class !== $overloadInfo->enumClass){
throw new \Error("Wrong enum type for overloaded registry member " . self::class . "::" . mb_strtoupper($name) . "($overloadInfo->enumClass)");
}
$memberName = $overloadInfo->enumToMemberMap[$enum->name];
assert(self::$members !== null);
return self::preprocessMember(self::$members[$memberName]);
}
throw new \Error("No such overloaded registry member: " . self::class . "::" . mb_strtoupper($name) . "(" . get_class($enum) . ")");
}else{
throw new \LogicException("Incorrect arguments passed to overloaded registry member: " . self::class . "::" . mb_strtoupper($name));
}
}
@ -136,4 +185,12 @@ trait RegistryTrait{
self::checkInit();
return array_map(self::preprocessMember(...), self::$members ?? throw new AssumptionFailedError(self::class . "::checkInit() did not initialize self::\$members correctly"));
}
/**
* @return string[]
* @phpstan-return array<string, OverloadedRegistryMember>
*/
public static function getAllOverloaded() : array{
return self::$overloadedMembers;
}
}

View File

@ -369,7 +369,7 @@ final class Utils{
debug_zval_dump($value);
$contents = ob_get_contents();
if($contents === false) throw new AssumptionFailedError("ob_get_contents() should never return false here");
$ret = explode("\n", $contents, limit: 2);
$ret = explode("\n", $contents);
ob_end_clean();
if(preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){
@ -584,10 +584,10 @@ final class Utils{
/**
* @phpstan-template TMemberType
* @phpstan-param array<mixed, TMemberType> $array
* @phpstan-param \Closure(TMemberType) : mixed $validator
* @phpstan-param \Closure(TMemberType) : void $validator
*/
public static function validateArrayValueType(array $array, \Closure $validator) : void{
foreach(Utils::promoteKeys($array) as $k => $v){
foreach($array as $k => $v){
try{
$validator($v);
}catch(\TypeError $e){

View File

@ -360,7 +360,7 @@ class World implements ChunkManager{
private bool $doingTick = false;
/** @phpstan-var class-string<generator\Generator> */
/** @phpstan-var class-string<\pocketmine\world\generator\Generator> */
private string $generator;
private bool $unloaded = false;

View File

@ -1,69 +0,0 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\biome\model;
/**
* Model for loading biome definition entries data from JSON.
*/
final class BiomeDefinitionEntryData{
/** @required */
public ?int $id;
/** @required */
public float $temperature;
/** @required */
public float $downfall;
/** @required */
public float $redSporeDensity;
/** @required */
public float $blueSporeDensity;
/** @required */
public float $ashDensity;
/** @required */
public float $whiteAshDensity;
/** @required */
public float $depth;
/** @required */
public float $scale;
/** @required */
public ColorData $mapWaterColour;
/** @required */
public bool $rain;
/**
* @required
* @var string[]
* @phpstan-var list<string>
*/
public array $tags;
}

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 = 800;
public const CURRENT_STORAGE_NETWORK_VERSION = 776;
public const CURRENT_CLIENT_VERSION_TARGET = [
1, //major
21, //minor
80, //patch
3, //revision
60, //patch
33, //revision
0 //is beta
];

View File

@ -26,12 +26,10 @@ namespace pocketmine\world\generator;
use pocketmine\data\bedrock\BiomeIds;
use pocketmine\item\LegacyStringToItemParser;
use pocketmine\item\LegacyStringToItemParserException;
use pocketmine\world\World;
use function array_map;
use function explode;
use function preg_match;
use function preg_match_all;
use const PHP_INT_MAX;
/**
* @internal
@ -72,7 +70,7 @@ final class FlatGeneratorOptions{
*/
public static function parseLayers(string $layers) : array{
$result = [];
$split = array_map('\trim', explode(',', $layers, limit: World::Y_MAX - World::Y_MIN));
$split = array_map('\trim', explode(',', $layers));
$y = 0;
$itemParser = LegacyStringToItemParser::getInstance();
foreach($split as $line){
@ -98,7 +96,7 @@ final class FlatGeneratorOptions{
* @throws InvalidGeneratorOptionsException
*/
public static function parsePreset(string $presetString) : self{
$preset = explode(";", $presetString, limit: 4);
$preset = explode(";", $presetString);
$blocks = $preset[1] ?? "";
$biomeId = (int) ($preset[2] ?? BiomeIds::PLAINS);
$optionsString = $preset[3] ?? "";
@ -111,10 +109,9 @@ final class FlatGeneratorOptions{
$params = true;
if($matches[3][$i] !== ""){
$params = [];
$p = explode(" ", $matches[3][$i], limit: PHP_INT_MAX);
$p = explode(" ", $matches[3][$i]);
foreach($p as $k){
//TODO: this should be limited to 2 parts, but 3 preserves old behaviour when given e.g. treecount=20=1
$k = explode("=", $k, limit: 3);
$k = explode("=", $k);
if(isset($k[1])){
$params[$k[0]] = $k[1];
}

View File

@ -39,8 +39,7 @@ class EntityAttackNoDamageSound implements Sound{
-1,
"minecraft:player",
false,
false,
-1
false
)];
}
}

View File

@ -39,8 +39,7 @@ class EntityAttackSound implements Sound{
-1,
"minecraft:player",
false,
false,
-1
false
)];
}
}

View File

@ -46,8 +46,7 @@ class EntityLandSound implements Sound{
TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->blockLandedOn->getStateId()),
$this->entity::getNetworkTypeId(),
false, //TODO: does isBaby have any relevance here?
false,
$this->entity->getId()
false
)];
}
}

View File

@ -42,8 +42,7 @@ class EntityLongFallSound implements Sound{
-1,
$this->entity::getNetworkTypeId(),
false, //TODO: is isBaby relevant here?
false,
$this->entity->getId()
false
)];
}
}

View File

@ -41,8 +41,7 @@ class EntityShortFallSound implements Sound{
-1,
$this->entity::getNetworkTypeId(),
false, //TODO: does isBaby have any relevance here?
false,
-1
false
)];
}
}

View File

@ -30,6 +30,6 @@ use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
class ThrowSound implements Sound{
public function encode(Vector3 $pos) : array{
return [LevelSoundEventPacket::create(LevelSoundEvent::THROW, $pos, -1, "minecraft:player", false, false, -1)];
return [LevelSoundEventPacket::create(LevelSoundEvent::THROW, $pos, -1, "minecraft:player", false, false)];
}
}

View File

@ -42,8 +42,7 @@ final class WaterSplashSound implements Sound{
(int) ($this->volume * 16777215),
":",
false,
false,
-1
false
)];
}
}

View File

@ -18,12 +18,6 @@ parameters:
count: 1
path: ../../../src/Server.php
-
message: '#^Dynamic new is not allowed\.$#'
identifier: pocketmine.new.dynamic
count: 1
path: ../../../src/Server.php
-
message: '#^Method pocketmine\\Server\:\:getCommandAliases\(\) should return array\<string, list\<string\>\> but returns array\<string, array\<mixed\>\>\.$#'
identifier: return.type
@ -55,16 +49,16 @@ parameters:
path: ../../../src/VersionInfo.php
-
message: '#^Static property pocketmine\\VersionInfo\:\:\$gitHash \(string\|null\) does not accept mixed\.$#'
identifier: assign.propertyType
message: '#^Method pocketmine\\VersionInfo\:\:GIT_HASH\(\) should return string but returns mixed\.$#'
identifier: return.type
count: 1
path: ../../../src/VersionInfo.php
-
message: '#^Dynamic new is not allowed\.$#'
identifier: pocketmine.new.dynamic
message: '#^Static property pocketmine\\VersionInfo\:\:\$gitHash \(string\|null\) does not accept mixed\.$#'
identifier: assign.propertyType
count: 1
path: ../../../src/block/Block.php
path: ../../../src/VersionInfo.php
-
message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#'
@ -522,12 +516,6 @@ parameters:
count: 3
path: ../../../src/block/tile/Spawnable.php
-
message: '#^Dynamic new is not allowed\.$#'
identifier: pocketmine.new.dynamic
count: 1
path: ../../../src/block/tile/TileFactory.php
-
message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getPotentialLightAt\(\) expects int, float\|int given\.$#'
identifier: argument.type
@ -930,6 +918,24 @@ parameters:
count: 1
path: ../../../src/plugin/PluginDescription.php
-
message: '#^Parameter \#1 \$haystack of function stripos expects string, mixed given\.$#'
identifier: argument.type
count: 1
path: ../../../src/plugin/PluginDescription.php
-
message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#'
identifier: argument.type
count: 1
path: ../../../src/plugin/PluginDescription.php
-
message: '#^Parameter \#3 \$subject of function str_replace expects array\<string\>\|string, mixed given\.$#'
identifier: argument.type
count: 1
path: ../../../src/plugin/PluginDescription.php
-
message: '#^Property pocketmine\\plugin\\PluginDescription\:\:\$authors \(array\<string\>\) does not accept list\<mixed\>\.$#'
identifier: assign.propertyType
@ -955,13 +961,13 @@ parameters:
path: ../../../src/plugin/PluginManager.php
-
message: '#^Dynamic new is not allowed\.$#'
identifier: pocketmine.new.dynamic
message: '#^Method pocketmine\\resourcepacks\\ZippedResourcePack\:\:getPackSize\(\) should return int but returns int\<0, max\>\|false\.$#'
identifier: return.type
count: 1
path: ../../../src/plugin/PluginManager.php
path: ../../../src/resourcepacks/ZippedResourcePack.php
-
message: '#^Method pocketmine\\resourcepacks\\ZippedResourcePack\:\:getPackSize\(\) should return int but returns int\<0, max\>\|false\.$#'
message: '#^Method pocketmine\\resourcepacks\\ZippedResourcePack\:\:getSha256\(\) should return string but returns string\|false\.$#'
identifier: return.type
count: 1
path: ../../../src/resourcepacks/ZippedResourcePack.php
@ -1003,7 +1009,7 @@ parameters:
path: ../../../src/utils/Config.php
-
message: '#^Parameter \#1 \$config of static method pocketmine\\utils\\Config\:\:writeProperties\(\) expects array\<int\|string, bool\|float\|int\|string\>, array\<mixed\> given\.$#'
message: '#^Parameter \#1 \$config of static method pocketmine\\utils\\Config\:\:writeProperties\(\) expects array\<int\|string, bool\|float\|int\|string\>, array\<int\|string, mixed\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/utils/Config.php
@ -1272,12 +1278,6 @@ parameters:
count: 1
path: ../../../src/world/format/io/region/RegionLoader.php
-
message: '#^Dynamic new is not allowed\.$#'
identifier: pocketmine.new.dynamic
count: 1
path: ../../../src/world/generator/GeneratorRegisterTask.php
-
message: '#^Method pocketmine\\world\\generator\\biome\\BiomeSelector\:\:pickBiome\(\) should return pocketmine\\world\\biome\\Biome but returns pocketmine\\world\\biome\\Biome\|null\.$#'
identifier: return.type

View File

@ -114,6 +114,12 @@ parameters:
count: 1
path: ../../../src/crash/CrashDump.php
-
message: '#^Call to function assert\(\) with false and ''unknown hit type'' will always evaluate to false\.$#'
identifier: function.impossibleType
count: 1
path: ../../../src/entity/projectile/Projectile.php
-
message: '#^Property pocketmine\\item\\WritableBookBase\:\:\$pages \(list\<pocketmine\\item\\WritableBookPage\>\) does not accept non\-empty\-array\<int, pocketmine\\item\\WritableBookPage\>\.$#'
identifier: assign.propertyType

View File

@ -1,55 +0,0 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\phpstan\rules;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\New_;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
/**
* @phpstan-implements Rule<New_>
*/
final class DisallowDynamicNewRule implements Rule{
public function getNodeType() : string{
return New_::class;
}
public function processNode(Node $node, Scope $scope) : array{
/** @var New_ $node */
if($node->class instanceof Expr){
return [
RuleErrorBuilder::message("Dynamic new is not allowed.")
->tip("For factories, use closures instead. Closures can implement custom logic, are statically analyzable, and don't restrict the constructor signature.")
->identifier("pocketmine.new.dynamic")
->build()
];
}
return [];
}
}

View File

@ -1,92 +0,0 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\phpstan\rules;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\ArgumentsNormalizer;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function count;
/**
* @phpstan-implements Rule<FuncCall>
*/
final class ExplodeLimitRule implements Rule{
private ReflectionProvider $reflectionProvider;
public function __construct(
ReflectionProvider $reflectionProvider
){
$this->reflectionProvider = $reflectionProvider;
}
public function getNodeType() : string{
return FuncCall::class;
}
public function processNode(Node $node, Scope $scope) : array{
if(!$node->name instanceof Name){
return [];
}
if(!$this->reflectionProvider->hasFunction($node->name, $scope)){
return [];
}
$functionReflection = $this->reflectionProvider->getFunction($node->name, $scope);
if($functionReflection->getName() !== 'explode'){
return [];
}
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
$scope,
$node->getArgs(),
$functionReflection->getVariants(),
$functionReflection->getNamedArgumentsVariants(),
);
$normalizedFuncCall = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node);
if($normalizedFuncCall === null){
return [];
}
$count = count($normalizedFuncCall->getArgs());
if($count !== 3){
return [
RuleErrorBuilder::message('The $limit parameter of explode() must be set to prevent malicious client data wasting resources.')
->identifier("pocketmine.explode.limit")
->build()
];
}
return [];
}
}

View File

@ -41,7 +41,7 @@ use function sprintf;
/**
* @implements Rule<Foreach_>
*/
final class UnsafeForeachRule implements Rule{
final class UnsafeForeachArrayOfStringRule implements Rule{
public function getNodeType() : string{
return Foreach_::class;
@ -73,7 +73,7 @@ final class UnsafeForeachRule implements Rule{
$benevolentUnionDepth--;
return $result;
}
if($type instanceof IntegerType){
if($type instanceof IntegerType && $benevolentUnionDepth === 0){
$expectsIntKeyTypes = true;
return $type;
}
@ -87,31 +87,24 @@ final class UnsafeForeachRule implements Rule{
$hasCastableKeyTypes = true;
return $type;
});
$errors = [];
if($implicitType){
$errors[] = RuleErrorBuilder::message("Possible unreported errors in foreach on array with unspecified key type.")
->tip(sprintf(
<<<TIP
PHPStan might not be reporting some type errors if the key type is not specified.
Declare a key type using @phpstan-var or @phpstan-param, or use %s() to force PHPStan to report proper errors.
TIP,
Utils::getNiceClosureName(Utils::promoteKeys(...))
))->identifier('pocketmine.foreach.implicitKeys')->build();
}
if($hasCastableKeyTypes && !$expectsIntKeyTypes){
$errors[] = RuleErrorBuilder::message(sprintf(
"Unsafe foreach on array with key type %s.",
$iterableType->getIterableKeyType()->describe(VerbosityLevel::value())
))
->tip(sprintf(
<<<TIP
PHP coerces numeric strings to integers when used as array keys, which can lead to unexpected type errors when passing the key to a function.
Use %s() to wrap the array in a \Generator that will force the keys back to string during iteration.
TIP,
Utils::getNiceClosureName(Utils::stringifyKeys(...))
))->identifier('pocketmine.foreach.stringKeys')->build();
$tip = $implicitType ?
sprintf(
"Declare a key type using @phpstan-var or @phpstan-param, or use %s() to promote the key type to get proper error reporting",
Utils::getNiceClosureName(Utils::promoteKeys(...))
) :
sprintf(
"Use %s() to get a \Generator that will force the keys to string",
Utils::getNiceClosureName(Utils::stringifyKeys(...)),
);
return [
RuleErrorBuilder::message(sprintf(
"Unsafe foreach on array with key type %s (they might be casted to int).",
$iterableType->getIterableKeyType()->describe(VerbosityLevel::value())
))->tip($tip)->identifier('pocketmine.foreach.stringKeys')->build()
];
}
return $errors;
return [];
}
}

View File

@ -51,10 +51,8 @@ use function array_unique;
use function array_values;
use function count;
use function dirname;
use function fclose;
use function file_exists;
use function file_put_contents;
use function fopen;
use function fwrite;
use function get_class;
use function get_debug_type;
@ -887,44 +885,6 @@ function cmdUpdateAll(array $argv) : int{
return 0;
}
/**
* @param string[] $argv
*/
function cmdDumpTable(array $argv) : int{
$tableFile = $argv[2];
$outputFile = $argv[3];
$output = fopen($outputFile, 'wb');
if($output === false){
fwrite(STDERR, "Failed to open output file: $outputFile\n");
return 1;
}
$table = loadUpgradeTableFromFile($tableFile, reverse: false);
ksort($table, SORT_STRING);
foreach(Utils::stringifyKeys($table) as $oldName => $mappings){
fwrite($output, "---------- MAPPING LIST: $oldName ----------\n");
foreach($mappings as $mapping){
$oldNbt = $mapping->old->toVanillaNbt();
$oldNbt->setInt("version", $mapping->new->getVersion());
//intentionally not reused result of toVanillaNbt otherwise output wouldn't include version
fwrite($output, "OLD: " . $mapping->old->toVanillaNbt() . "\n");
if(!$oldNbt->equals($mapping->new->toVanillaNbt())){
fwrite($output, "NEW: " . $mapping->new->toVanillaNbt() . "\n");
}else{
fwrite($output, "NEW: version bump only (" . $mapping->new->getVersion() . ")\n");
}
fwrite($output, "-----\n");
}
}
fclose($output);
\GlobalLogger::get()->info("Table dump file $outputFile generated successfully.");
return 0;
}
/**
* @param string[] $argv
*/
@ -933,8 +893,7 @@ function main(array $argv) : int{
"generate" => [["palette upgrade table file", "schema output file"], cmdGenerate(...)],
"test" => [["palette upgrade table file", "schema output file"], cmdTest(...)],
"update" => [["schema input file", "old palette file", "updated schema output file"], cmdUpdate(...)],
"update-all" => [["schema folder", "path to BlockPaletteArchive"], cmdUpdateAll(...)],
"dump-table" => [["palette upgrade table file", "txt output file"], cmdDumpTable(...)]
"update-all" => [["schema folder", "path to BlockPaletteArchive"], cmdUpdateAll(...)]
];
$selected = $argv[1] ?? null;

View File

@ -54,6 +54,7 @@ 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\StartGamePacket;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeGroupEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData;
@ -75,8 +76,6 @@ use pocketmine\network\PacketHandlingException;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use pocketmine\world\biome\model\BiomeDefinitionEntryData;
use pocketmine\world\biome\model\ColorData;
use pocketmine\world\format\io\GlobalBlockStateHandlers;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Symfony\Component\Filesystem\Path;
@ -101,7 +100,6 @@ use function json_encode;
use function ksort;
use function mkdir;
use function ord;
use function round;
use function strlen;
use const FILE_IGNORE_NEW_LINES;
use const JSON_PRETTY_PRINT;
@ -574,34 +572,34 @@ class ParserPacketHandler extends PacketHandler{
public function handleBiomeDefinitionList(BiomeDefinitionListPacket $packet) : bool{
echo "storing biome definitions" . PHP_EOL;
$definitions = [];
foreach($packet->buildDefinitionsFromData() as $entry){
$mapWaterColor = new ColorData();
$mapWaterColor->r = $entry->getMapWaterColor()->getR();
$mapWaterColor->g = $entry->getMapWaterColor()->getG();
$mapWaterColor->b = $entry->getMapWaterColor()->getB();
$mapWaterColor->a = $entry->getMapWaterColor()->getA();
file_put_contents($this->bedrockDataPath . '/biome_definitions_full.nbt', $packet->definitions->getEncodedNbt());
$data = new BiomeDefinitionEntryData();
$data->id = $entry->getId();
$data->temperature = round($entry->getTemperature(), 3);
$data->downfall = round($entry->getDownfall(), 3);
$data->redSporeDensity = round($entry->getRedSporeDensity(), 3);
$data->blueSporeDensity = round($entry->getBlueSporeDensity(), 3);
$data->ashDensity = round($entry->getAshDensity(), 3);
$data->whiteAshDensity = round($entry->getWhiteAshDensity(), 3);
$data->depth = round($entry->getDepth(), 3);
$data->scale = round($entry->getScale(), 3);
$data->mapWaterColour = $mapWaterColor;
$data->rain = $entry->hasRain();
$data->tags = $entry->getTags() ?? [];
$definitions[$entry->getBiomeName()] = self::objectToOrderedArray($data);
$nbt = $packet->definitions->getRoot();
if(!$nbt instanceof CompoundTag){
throw new AssumptionFailedError();
}
$strippedNbt = clone $nbt;
foreach($strippedNbt as $compound){
if($compound instanceof CompoundTag){
foreach([
"minecraft:capped_surface",
"minecraft:consolidated_features",
"minecraft:frozen_ocean_surface",
"minecraft:legacy_world_generation_rules",
"minecraft:mesa_surface",
"minecraft:mountain_parameters",
"minecraft:multinoise_generation_rules",
"minecraft:overworld_generation_rules",
"minecraft:surface_material_adjustments",
"minecraft:surface_parameters",
"minecraft:swamp_surface",
] as $remove){
$compound->removeTag($remove);
}
}
}
ksort($definitions, SORT_STRING);
file_put_contents($this->bedrockDataPath . '/biome_definitions.json', json_encode($definitions, JSON_PRETTY_PRINT) . "\n");
file_put_contents($this->bedrockDataPath . '/biome_definitions.nbt', (new CacheableNbt($strippedNbt))->getEncodedNbt());
return true;
}
@ -626,7 +624,7 @@ function main(array $argv) : int{
}
foreach($packets as $lineNum => $line){
$parts = explode(':', $line, limit: 3);
$parts = explode(':', $line);
if(count($parts) !== 2){
fwrite(STDERR, 'Wrong packet format at line ' . ($lineNum + 1) . ', expected read:base64 or write:base64');
return 1;