diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index dc282ab71..a3921f820 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -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.18.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.18.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.18.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.18.0 with: push: true context: ./pocketmine-mp diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..ef0b122e1 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,47 @@ +name: "Copilot Agent environment setup" + +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: pmmp/setup-php-action@3.2.0 + with: + php-version: 8.3 + install-path: "./bin" + pm-version-major: 5 + + - name: Restore Composer package cache + uses: actions/cache@v4 + with: + path: | + ~/.cache/composer/files + ~/.cache/composer/vcs + key: "composer-v2-cache-8.3-${{ hashFiles('./composer.lock') }}" + restore-keys: | + composer-v2-cache- + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-interaction + + - name: Clone extension stubs + uses: actions/checkout@v4 + with: + repository: pmmp/phpstorm-stubs + path: extension-stubs diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index 93b2978aa..35a8b46fa 100644 --- a/.github/workflows/discord-release-notify.yml +++ b/.github/workflows/discord-release-notify.yml @@ -13,9 +13,9 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.33.0 + uses: shivammathur/setup-php@2.34.1 with: - php-version: 8.2 + php-version: 8.3 - name: Restore Composer package cache uses: actions/cache@v4 diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml index 20b2200e6..866de64ab 100644 --- a/.github/workflows/draft-release-pr-check.yml +++ b/.github/workflows/draft-release-pr-check.yml @@ -49,9 +49,9 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP - uses: shivammathur/setup-php@2.33.0 + uses: shivammathur/setup-php@2.34.1 with: - php-version: 8.2 + php-version: 8.3 - name: Restore Composer package cache uses: actions/cache@v4 diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index fa20d1912..2e48a7c08 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -18,7 +18,7 @@ on: - "*" env: - PHP_VERSION: "8.2" + PHP_VERSION: "8.3" jobs: skip: @@ -87,7 +87,7 @@ jobs: submodules: true - name: Setup PHP - uses: shivammathur/setup-php@2.33.0 + uses: shivammathur/setup-php@2.34.1 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.18.0 id: create-draft with: artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst @@ -188,4 +188,4 @@ jobs: if: github.event_name == 'pull_request_target' uses: thollander/actions-comment-pull-request@v3 with: - message: "[Draft release ${{ steps.get-pm-version.outputs.PM_VERSION }}](${{ steps.create-draft.outputs.html_url }}) has been created for commit ${{ github.sha }}. Please review and publish it." + message: "${{ vars.DRAFT_RELEASE_NOTIFICATION_MENTION }} [Draft release ${{ steps.get-pm-version.outputs.PM_VERSION }}](${{ steps.create-draft.outputs.html_url }}) has been created for commit ${{ github.sha }}. Please review and publish it." diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cabda54be..9253afef3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1", "8.2", "8.3"] + php: ["8.3"] uses: ./.github/workflows/main-php-matrix.yml with: @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.33.0 + uses: shivammathur/setup-php@2.34.1 with: php-version: 8.3 tools: php-cs-fixer:3.75 diff --git a/README.md b/README.md index 6f2b715ab..98f569346 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@

- CI + CI GitHub release (latest SemVer) Discord
diff --git a/build/php b/build/php index 154943379..ce1b095a9 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 15494337976e645499e2e3e8c8b491227522be91 +Subproject commit ce1b095a9c6f47dadc7b5812da4e469d52f272bc diff --git a/changelogs/5.29.md b/changelogs/5.29.md new file mode 100644 index 000000000..cb6e50da3 --- /dev/null +++ b/changelogs/5.29.md @@ -0,0 +1,25 @@ +# 5.29.0 +Released 18th June 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.90. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.90. +- Removed support for earlier versions. + +## Fixes +- Fixed thread crashes sometimes not reporting proper cause information in crashdumps. +- Fixed crash when a plugin replaced a player's held tool with a different tool with a damage exceeding the old tool's max damage during an action. +- Fixed performance issue of `PlayerAuthInputPacket` input flags handling (broken change detection). +- Fixed `BaseInventory->addItem()` triggering updates on empty slots when no items were added. +- Fixed slow check in `SubChunk` block layer garbage collection. + +## Internals +- `LoginPacketHandler->processLogin()` signature has changed. This will break any plugins overriding `LoginPacketHandler`. As noted above, this is _not_ covered by the API version guarantee. +- Automated branch sync for `minor-next` and `major-next` is now triggered by `repository_dispatch` from a cron job in this repository instead of `RestrictedActions`. The `RestrictedActions` cron job was getting automatically disabled by GitHub due to repo inactivity. diff --git a/changelogs/5.30.md b/changelogs/5.30.md new file mode 100644 index 000000000..cc2ecbc1f --- /dev/null +++ b/changelogs/5.30.md @@ -0,0 +1,73 @@ +# 5.30.0 +Released 18th June 2025. + +This is a minor feature release containing API additions, internals cleanup and user experience improvements. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Significantly reduced log spam when unknown blocks, tiles and entities are found in saved worlds. +- The file name structure for crashdumps has been changed to improve sorting order in file browsers. +- Buffering is now skipped on the RakLib layer. In theory this could reduce player network latency by 10 ms (YMMV). + +## Gameplay +### Blocks +- Many blocks have had their hardness and blast resistance updated to match vanilla. +- Implemented Respawn Anchor. +- Melon Stem and Pumpkin Stem drop amounts should now match vanilla (using binomial distribution). + +## API +## General +- Verification of save registration has been added for blocks, entities and tiles. This is intended to make it easier to find mistakes when registering custom things, which previously would produce obscure core crashes. + +### `pocketmine\event\block` +- The following classes have been added: + - `BlockPreExplodeEvent` - called before a block tries to explode + - `BlockExplodeEvent` - called when after a block's explosion calculation has been done, but before any changes are applied + +### `pocketmine\event\entity` +- The following classes have been added: + - `EntityExtinguishEvent` - called when a burning entity is extinguished by water or other sources + - `EntityFrostWalkerEvent` - called every tick upon which an entity wearing Frost Walker boots moves; this can be used to customise or cancel the behaviour of the Frost Walker enchantment + +### `pocketmine\event\player` +- The following classes have been added: + - `PlayerRespawnAnchorUseEvent` - called when a player interacts with a charged respawn anchor + +### `pocketmine\entity` +- The following methods have been added: + - `public Entity->getStepHeight() : float` + - `public Entity->setStepHeight(float $stepHeight) : void` + +### `pocketmine\world\generator` +- Generator execution has been decoupled from `PopulationTask` and async tasks in general. The following classes have been added: + - `executor\GeneratorExecutor` + - `executor\SyncGeneratorExecutor` - runs a generator on the main thread (used for flat world generation, which doesn't need threads) + - `executor\AsyncGeneratorExecutor` - runs a generator inside an async task, as before + - `PopulationUtils` - contains population business logic previously baked into `PopulationTask` - this permits the reuse of that logic outside async tasks +- The following methods have signature changes: + - `GeneratorManager->addGenerator()` now accepts an optional `bool $fast` parameter, defaulting to `false`; setting this to `true` will cause your generator to run on the main thread +- The following methods have been added: + - `public GeneratorManagerEntry->isFast() : bool` - returns whether this generator should run on the main thread +- `PopulationTask` has been marked as `@internal`. In the next major version, it will move to the `generator\executor` namespace; however, for now it stays put because plugins currently have no other way to regenerate chunks. + +## Internals +- World data version numbers have been consolidated in `pocketmine\data\bedrock\WorldDataVersions`. This removes the need to modify several different files to support new world versions, and reduces the chances of things getting missed. +- Block hardness and blast resistance is now unit-tested against `block_properties_table.json` in `BedrockData`. This file comes from vanilla BDS, so we can use it to verify compliance. +- Protocol-layer "server auth block breaking" has been enabled. Functionally, this is no different from the previous system, it just works differently on the network layer. +- Various internal classes in the `pocketmine\world\generator` namespace have been moved to the `generator\executor` namespace. +- Removed `World->registerGenerator()` and `World->unregisterGenerator()`. +- Removed redundant calls to `curl_close()` (obsolete since PHP 8.0). + +# 5.30.1 +Released 23rd June 2025. + +## Fixes +- Fixed accidental break of backwards compatibility in `EntityExplodeEvent` introduced in the previous release. +- Fixed placement of player holding block when exploding respawn anchor. +- Updated BedrockProtocol to fix incorrect encoding of `ServerScriptDebugDrawerPacket`. +- Disabled client-side locator bar, allowing plugins to write their own implementations. diff --git a/changelogs/5.31.md b/changelogs/5.31.md new file mode 100644 index 000000000..60e797425 --- /dev/null +++ b/changelogs/5.31.md @@ -0,0 +1,14 @@ +# 5.31.0 +Released 8th July 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.93. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.93. +- Removed support for earlier versions. diff --git a/composer.json b/composer.json index e205a0798..e34db64a2 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "homepage": "https://pmmp.io", "license": "LGPL-3.0", "require": { - "php": "^8.1", + "php": "^8.3", "php-64bit": "*", "ext-chunkutils2": "^0.3.1", "ext-crypto": "^0.3.1", @@ -34,28 +34,28 @@ "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": "~5.2.0+bedrock-1.21.93", "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": "~39.1.0+bedrock-1.21.93", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.7.0", - "pocketmine/locale-data": "~2.24.0", + "pocketmine/locale-data": "~2.25.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.1.0", - "pocketmine/raklib": "~1.1.2", + "pocketmine/raklib": "~1.2.0", "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", - "ramsey/uuid": "~4.7.0", - "symfony/filesystem": "~6.4.0" + "ramsey/uuid": "~4.9.0", + "symfony/filesystem": "~7.3.0" }, "require-dev": { - "phpstan/phpstan": "2.1.16", + "phpstan/phpstan": "2.1.17", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", - "phpunit/phpunit": "^10.5.24" + "phpunit/phpunit": "^12.2.1" }, "replace": { "symfony/polyfill-ctype": "*", @@ -77,7 +77,7 @@ }, "config": { "platform": { - "php": "8.1.0" + "php": "8.3.0" }, "sort-packages": true }, diff --git a/composer.lock b/composer.lock index 9cb0721fc..f308e2254 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "ceb98091ac3f61f1a4b87708c48dc75a", + "content-hash": "3609ef73f066fbcfc0593d8aa6c94a51", "packages": [ { "name": "adhocore/json-comment", @@ -67,16 +67,16 @@ }, { "name": "brick/math", - "version": "0.12.3", + "version": "0.13.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", "shasum": "" }, "require": { @@ -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.13.1" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2025-03-29T13:50:30+00:00" }, { "name": "netresearch/jsonmapper", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "5.0.0+bedrock-1.21.80", + "version": "5.2.0+bedrock-1.21.93", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080" + "reference": "740e18e490c6a102b774518ff2224a06762bcaf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e38d5ea19f794ec5216e5f96742237e8c4e7f080", - "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/740e18e490c6a102b774518ff2224a06762bcaf8", + "reference": "740e18e490c6a102b774518ff2224a06762bcaf8", "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.93" }, - "time": "2025-05-09T14:15:18+00:00" + "time": "2025-07-08T12:30:28+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "38.0.1+bedrock-1.21.80", + "version": "39.1.0+bedrock-1.21.93", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f" + "reference": "e9bc5fb691d18dab229a158462c13f0c6fea79c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f", - "reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e9bc5fb691d18dab229a158462c13f0c6fea79c8", + "reference": "e9bc5fb691d18dab229a158462c13f0c6fea79c8", "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.1+bedrock-1.21.80" + "source": "https://github.com/pmmp/BedrockProtocol/tree/39.1.0+bedrock-1.21.93" }, - "time": "2025-05-17T11:56:33+00:00" + "time": "2025-07-08T12:31:39+00:00" }, { "name": "pocketmine/binaryutils", @@ -471,16 +471,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.24.2", + "version": "2.25.1", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "2a00c44c52bce98e7a43aa31517df78cbb2ba23b" + "reference": "8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/2a00c44c52bce98e7a43aa31517df78cbb2ba23b", - "reference": "2a00c44c52bce98e7a43aa31517df78cbb2ba23b", + "url": "https://api.github.com/repos/pmmp/Language/zipball/8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd", + "reference": "8e6514f5a9638e69cdc2219c775fc7d3bb4c9fdd", "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.25.1" }, - "time": "2025-04-03T01:23:27+00:00" + "time": "2025-04-16T11:15:32+00:00" }, { "name": "pocketmine/log", @@ -618,16 +618,16 @@ }, { "name": "pocketmine/raklib", - "version": "1.1.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/pmmp/RakLib.git", - "reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f" + "reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/RakLib/zipball/4145a31cd812fe8931c3c9c691fcd2ded2f47e7f", - "reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f", + "url": "https://api.github.com/repos/pmmp/RakLib/zipball/a28d05216d34dbd00e8aed827a58df6b4c11510b", + "reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b", "shasum": "" }, "require": { @@ -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.2.0" }, - "time": "2025-04-06T03:38:21+00:00" + "time": "2025-06-08T17:36:06+00:00" }, { "name": "pocketmine/raklib-ipc", @@ -818,21 +818,20 @@ }, { "name": "ramsey/uuid", - "version": "4.7.6", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", - "ext-json": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -840,26 +839,23 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.10", + "captainhook/captainhook": "^5.25", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -894,41 +890,31 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.6" + "source": "https://github.com/ramsey/uuid/tree/4.9.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" - } - ], - "time": "2024-04-27T21:32:50+00:00" + "time": "2025-06-25T14:20:11+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.13", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "symfony/process": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -956,7 +942,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/filesystem/tree/v7.3.0" }, "funding": [ { @@ -972,7 +958,7 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2024-10-25T15:15:23+00:00" } ], "packages-dev": [ @@ -1038,16 +1024,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", "shasum": "" }, "require": { @@ -1090,9 +1076,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-05-31T08:24:38+00:00" }, { "name": "phar-io/manifest", @@ -1214,16 +1200,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.16", + "version": "2.1.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9" + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", "shasum": "" }, "require": { @@ -1268,7 +1254,7 @@ "type": "github" } ], - "time": "2025-05-16T09:40:10+00:00" + "time": "2025-05-21T20:55:28+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -1325,16 +1311,16 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.4", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a" + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094" }, "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/f9f77efa9de31992a832ff77ea52eb42d675b094", + "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094", "shasum": "" }, "require": { @@ -1367,41 +1353,40 @@ "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.6" }, - "time": "2025-03-18T11:42:40+00:00" + "time": "2025-07-21T12:19:29+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.16", + "version": "12.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + "reference": "9075a8efc66e11bc55c319062e147bdb06777267" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9075a8efc66e11bc55c319062e147bdb06777267", + "reference": "9075a8efc66e11bc55c319062e147bdb06777267", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-text-template": "^3.0.1", - "sebastian/code-unit-reverse-lookup": "^3.0.0", - "sebastian/complexity": "^3.2.0", - "sebastian/environment": "^6.1.0", - "sebastian/lines-of-code": "^2.0.2", - "sebastian/version": "^4.0.1", + "nikic/php-parser": "^5.4.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^10.1" + "phpunit/phpunit": "^12.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -1410,7 +1395,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1.x-dev" + "dev-main": "12.3.x-dev" } }, "autoload": { @@ -1439,40 +1424,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2024-08-22T04:31:57+00:00" + "time": "2025-05-23T15:49:03+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.1.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1500,7 +1497,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" }, "funding": [ { @@ -1508,28 +1505,28 @@ "type": "github" } ], - "time": "2023-08-31T06:24:48+00:00" + "time": "2025-02-07T04:58:37+00:00" }, { "name": "phpunit/php-invoker", - "version": "4.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -1537,7 +1534,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1563,7 +1560,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -1571,32 +1569,32 @@ "type": "github" } ], - "time": "2023-02-03T06:56:09+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "3.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -1623,7 +1621,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -1631,32 +1629,32 @@ "type": "github" } ], - "time": "2023-08-31T14:07:24+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "6.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -1682,7 +1680,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -1690,20 +1689,20 @@ "type": "github" } ], - "time": "2023-02-03T06:57:52+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "10.5.46", + "version": "12.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" + "reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", - "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f09fda04e7caea93cff50b4e90319184f3e6ee3", + "reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3", "shasum": "" }, "require": { @@ -1716,26 +1715,22 @@ "myclabs/deep-copy": "^1.13.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.16", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-invoker": "^4.0.0", - "phpunit/php-text-template": "^3.0.1", - "phpunit/php-timer": "^6.0.0", - "sebastian/cli-parser": "^2.0.1", - "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.3", - "sebastian/diff": "^5.1.1", - "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", - "sebastian/global-state": "^6.0.2", - "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", - "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.3.0", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.0.0", + "sebastian/comparator": "^7.0.1", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.2", + "sebastian/exporter": "^7.0.0", + "sebastian/global-state": "^8.0.0", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.2", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -1743,7 +1738,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "12.2-dev" } }, "autoload": { @@ -1775,7 +1770,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.46" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.1" }, "funding": [ { @@ -1799,32 +1794,32 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:46:24+00:00" + "time": "2025-06-07T05:17:47+00:00" }, { "name": "sebastian/cli-parser", - "version": "2.0.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1848,7 +1843,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" }, "funding": [ { @@ -1856,147 +1851,39 @@ "type": "github" } ], - "time": "2024-03-02T07:12:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:43+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2025-02-07T04:53:50+00:00" }, { "name": "sebastian/comparator", - "version": "5.0.3", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b478f34614f934e0291598d0c08cbaba9644bee5", + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2036,7 +1923,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.0.1" }, "funding": [ { @@ -2044,33 +1931,33 @@ "type": "github" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2025-03-07T07:00:32+00:00" }, { "name": "sebastian/complexity", - "version": "3.2.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2094,7 +1981,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -2102,33 +1989,33 @@ "type": "github" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "5.1.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2161,7 +2048,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -2169,27 +2056,27 @@ "type": "github" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "6.1.0", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d364b9e5d0d3b18a2573351a1786fbf96b7e0792", + "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-posix": "*" @@ -2197,7 +2084,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2225,42 +2112,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2025-05-21T15:05:44+00:00" }, { "name": "sebastian/exporter", - "version": "5.1.2", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2303,7 +2202,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" }, "funding": [ { @@ -2311,35 +2210,35 @@ "type": "github" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2025-02-07T04:56:42+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.2", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2365,7 +2264,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" }, "funding": [ { @@ -2373,33 +2272,33 @@ "type": "github" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2025-02-07T04:56:59+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.2", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -2423,7 +2322,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" }, "funding": [ { @@ -2431,34 +2330,34 @@ "type": "github" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2025-02-07T04:57:28+00:00" }, { "name": "sebastian/object-enumerator", - "version": "5.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2480,7 +2379,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -2488,32 +2388,32 @@ "type": "github" } ], - "time": "2023-02-03T07:08:32+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "3.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2535,7 +2435,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -2543,32 +2444,32 @@ "type": "github" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "5.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2598,7 +2499,8 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" }, "funding": [ { @@ -2606,32 +2508,32 @@ "type": "github" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-02-07T05:00:01+00:00" }, { "name": "sebastian/type", - "version": "4.0.0", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069", + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2654,7 +2556,8 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.2" }, "funding": [ { @@ -2662,29 +2565,29 @@ "type": "github" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2025-03-18T13:37:31+00:00" }, { "name": "sebastian/version", - "version": "4.0.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2707,7 +2610,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -2715,7 +2619,59 @@ "type": "github" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" }, { "name": "theseer/tokenizer", @@ -2774,7 +2730,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.1", + "php": "^8.3", "php-64bit": "*", "ext-chunkutils2": "^0.3.1", "ext-crypto": "^0.3.1", @@ -2803,7 +2759,7 @@ }, "platform-dev": {}, "platform-overrides": { - "php": "8.1.0" + "php": "8.3.0" }, "plugin-api-version": "2.6.0" } diff --git a/src/PocketMine.php b/src/PocketMine.php index a71c9768d..04b95dd32 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -55,7 +55,7 @@ namespace pocketmine { require_once __DIR__ . '/VersionInfo.php'; - const MIN_PHP_VERSION = "8.1.0"; + const MIN_PHP_VERSION = "8.3.0"; /** * @param string $message diff --git a/src/Server.php b/src/Server.php index 0d58bd579..a3b00f461 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1624,7 +1624,7 @@ class Server{ if(!is_dir($crashFolder)){ mkdir($crashFolder); } - $crashDumpPath = Path::join($crashFolder, date("D_M_j-H.i.s-T_Y", (int) $dump->getData()->time) . ".log"); + $crashDumpPath = Path::join($crashFolder, date("Y-m-d_H.i.s_T", (int) $dump->getData()->time) . ".log"); $fp = @fopen($crashDumpPath, "wb"); if(!is_resource($fp)){ diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 615024656..aeb4d9ff8 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,7 +31,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.28.3"; + public const BASE_VERSION = "5.31.1"; public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; diff --git a/src/block/ActivatorRail.php b/src/block/ActivatorRail.php index dcd0ef93b..da15eb1e8 100644 --- a/src/block/ActivatorRail.php +++ b/src/block/ActivatorRail.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\RailPoweredByRedstoneTrait; -class ActivatorRail extends StraightOnlyRail{ +class ActivatorRail extends StraightOnlyRail implements PoweredByRedstone{ use RailPoweredByRedstoneTrait; //TODO diff --git a/src/block/AmethystCluster.php b/src/block/AmethystCluster.php index 639490456..8a750e974 100644 --- a/src/block/AmethystCluster.php +++ b/src/block/AmethystCluster.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\AmethystTrait; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\block\utils\SupportType; @@ -38,7 +39,7 @@ use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\world\BlockTransaction; -final class AmethystCluster extends Transparent{ +final class AmethystCluster extends Transparent implements AnyFacing{ use AmethystTrait; use AnyFacingTrait; diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 2c48f9a7c..fcb4d045c 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\AnvilInventory; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -41,7 +42,7 @@ use pocketmine\world\sound\AnvilFallSound; use pocketmine\world\sound\Sound; use function round; -class Anvil extends Transparent implements Fallable{ +class Anvil extends Transparent implements Fallable, HorizontalFacing{ use FallableTrait; use HorizontalFacingTrait; diff --git a/src/block/Barrel.php b/src/block/Barrel.php index 0f0499ab9..7b2ea356e 100644 --- a/src/block/Barrel.php +++ b/src/block/Barrel.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Barrel as TileBarrel; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function abs; -class Barrel extends Opaque{ +class Barrel extends Opaque implements AnyFacing{ use AnyFacingTrait; protected bool $open = false; diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index b56323453..376f1f9dc 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Banner as TileBanner; use pocketmine\block\utils\BannerPatternLayer; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Banner as ItemBanner; @@ -36,7 +37,7 @@ use pocketmine\world\BlockTransaction; use function assert; use function count; -abstract class BaseBanner extends Transparent{ +abstract class BaseBanner extends Transparent implements Colored{ use ColoredTrait; /** diff --git a/src/block/BaseBigDripleaf.php b/src/block/BaseBigDripleaf.php index f0ff59cf0..94e2c12a2 100644 --- a/src/block/BaseBigDripleaf.php +++ b/src/block/BaseBigDripleaf.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\event\block\StructureGrowEvent; @@ -33,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -abstract class BaseBigDripleaf extends Transparent{ +abstract class BaseBigDripleaf extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; abstract protected function isHead() : bool; diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php index b9c595a97..c1cc9bb25 100644 --- a/src/block/BaseCoral.php +++ b/src/block/BaseCoral.php @@ -24,12 +24,13 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\CoralMaterial; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use function mt_rand; -abstract class BaseCoral extends Transparent{ +abstract class BaseCoral extends Transparent implements CoralMaterial{ use CoralTypeTrait; public function onNearbyBlockChange() : void{ diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 0f5d77d58..0efaa603c 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -27,6 +27,7 @@ use pocketmine\block\tile\Sign as TileSign; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SignText; use pocketmine\block\utils\SupportType; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodTypeTrait; use pocketmine\color\Color; @@ -44,7 +45,7 @@ use function array_map; use function assert; use function strlen; -abstract class BaseSign extends Transparent{ +abstract class BaseSign extends Transparent implements WoodMaterial{ use WoodTypeTrait; protected SignText $text; diff --git a/src/block/Bed.php b/src/block/Bed.php index 133c4a9cc..21bd3a172 100644 --- a/src/block/Bed.php +++ b/src/block/Bed.php @@ -24,8 +24,10 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Bed as TileBed; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -41,7 +43,7 @@ use pocketmine\utils\TextFormat; use pocketmine\world\BlockTransaction; use pocketmine\world\World; -class Bed extends Transparent{ +class Bed extends Transparent implements Colored, HorizontalFacing{ use ColoredTrait; use HorizontalFacingTrait; diff --git a/src/block/Bell.php b/src/block/Bell.php index 53a6fc7fb..258abc628 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Bell as TileBell; use pocketmine\block\utils\BellAttachmentType; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -38,7 +39,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\BellRingSound; -final class Bell extends Transparent{ +final class Bell extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR; diff --git a/src/block/BlockBreakInfo.php b/src/block/BlockBreakInfo.php index e77e06cfd..3d45caf3c 100644 --- a/src/block/BlockBreakInfo.php +++ b/src/block/BlockBreakInfo.php @@ -73,7 +73,7 @@ class BlockBreakInfo{ return new self(0.0, $toolType, $toolHarvestLevel, 0.0); } - public static function indestructible(float $blastResistance = 18000000.0) : self{ + public static function indestructible(float $blastResistance = 18000003.75) : self{ return new self(-1.0, BlockToolType::NONE, 0, $blastResistance); } diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index c440cefdc..4af1894bd 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -786,8 +786,9 @@ final class BlockTypeIds{ public const RESIN_BRICKS = 10756; public const RESIN_CLUMP = 10757; public const CHISELED_RESIN_BRICKS = 10758; + public const RESPAWN_ANCHOR = 10759; - public const FIRST_UNUSED_BLOCK_ID = 10759; + public const FIRST_UNUSED_BLOCK_ID = 10760; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/BoneBlock.php b/src/block/BoneBlock.php index 247bdb748..465d96033 100644 --- a/src/block/BoneBlock.php +++ b/src/block/BoneBlock.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; -class BoneBlock extends Opaque{ +class BoneBlock extends Opaque implements PillarRotation{ use PillarRotationTrait; } diff --git a/src/block/Button.php b/src/block/Button.php index 73bd1d556..4d1f1bbc5 100644 --- a/src/block/Button.php +++ b/src/block/Button.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\world\BlockTransaction; use pocketmine\world\sound\RedstonePowerOffSound; use pocketmine\world\sound\RedstonePowerOnSound; -abstract class Button extends Flowable{ +abstract class Button extends Flowable implements AnyFacing{ use AnyFacingTrait; protected bool $pressed = false; diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 67b15b946..51c98786b 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -33,7 +34,7 @@ use pocketmine\event\entity\EntityDamageEvent; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -class Cactus extends Transparent{ +class Cactus extends Transparent implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php index 546843d6c..1462776c2 100644 --- a/src/block/CakeWithCandle.php +++ b/src/block/CakeWithCandle.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\CandleTrait; +use pocketmine\block\utils\Lightable; use pocketmine\entity\Living; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -31,7 +32,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class CakeWithCandle extends BaseCake{ +class CakeWithCandle extends BaseCake implements Lightable{ use CandleTrait { onInteract as onInteractCandle; } diff --git a/src/block/CakeWithDyedCandle.php b/src/block/CakeWithDyedCandle.php index 0dff358e1..04ab0c6eb 100644 --- a/src/block/CakeWithDyedCandle.php +++ b/src/block/CakeWithDyedCandle.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; -class CakeWithDyedCandle extends CakeWithCandle{ +class CakeWithDyedCandle extends CakeWithCandle implements Colored{ use ColoredTrait; public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){ diff --git a/src/block/Campfire.php b/src/block/Campfire.php index 9f4c42a9c..edfc87ecb 100644 --- a/src/block/Campfire.php +++ b/src/block/Campfire.php @@ -25,7 +25,9 @@ namespace pocketmine\block; use pocketmine\block\inventory\CampfireInventory; use pocketmine\block\tile\Campfire as TileCampfire; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\block\utils\SupportType; use pocketmine\crafting\FurnaceRecipe; @@ -59,7 +61,7 @@ use function count; use function min; use function mt_rand; -class Campfire extends Transparent{ +class Campfire extends Transparent implements Lightable, HorizontalFacing{ use HorizontalFacingTrait{ HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState; } diff --git a/src/block/Candle.php b/src/block/Candle.php index 7f22641e1..977f13a09 100644 --- a/src/block/Candle.php +++ b/src/block/Candle.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\CandleTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -35,7 +36,7 @@ use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\world\BlockTransaction; -class Candle extends Transparent{ +class Candle extends Transparent implements Lightable{ use CandleTrait { describeBlockOnlyState as encodeLitState; getLightLevel as getBaseLightLevel; diff --git a/src/block/Carpet.php b/src/block/Carpet.php index 2d8e7ea47..fd8b42a09 100644 --- a/src/block/Carpet.php +++ b/src/block/Carpet.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -class Carpet extends Flowable{ +class Carpet extends Flowable implements Colored{ use ColoredTrait; use StaticSupportTrait; diff --git a/src/block/CarvedPumpkin.php b/src/block/CarvedPumpkin.php index 98f3c2307..cfdb8d49d 100644 --- a/src/block/CarvedPumpkin.php +++ b/src/block/CarvedPumpkin.php @@ -24,7 +24,8 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; -class CarvedPumpkin extends Opaque{ +class CarvedPumpkin extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; } diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php index daa973507..84d7d3169 100644 --- a/src/block/CaveVines.php +++ b/src/block/CaveVines.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -39,7 +40,7 @@ use pocketmine\world\BlockTransaction; use pocketmine\world\sound\GlowBerriesPickSound; use function mt_rand; -class CaveVines extends Flowable{ +class CaveVines extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/Chain.php b/src/block/Chain.php index e9cc2c9be..5ec3b1e2a 100644 --- a/src/block/Chain.php +++ b/src/block/Chain.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; use pocketmine\block\utils\SupportType; use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -final class Chain extends Transparent{ +final class Chain extends Transparent implements PillarRotation{ use PillarRotationTrait; public function getSupportType(int $facing) : SupportType{ diff --git a/src/block/ChemistryTable.php b/src/block/ChemistryTable.php index 058e40288..9b5e78f92 100644 --- a/src/block/ChemistryTable.php +++ b/src/block/ChemistryTable.php @@ -24,11 +24,12 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\player\Player; -final class ChemistryTable extends Opaque{ +final class ChemistryTable extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ diff --git a/src/block/Chest.php b/src/block/Chest.php index 7d2650007..c0dc09d9e 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Chest as TileChest; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\event\block\ChestPairEvent; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class Chest extends Transparent{ +class Chest extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; protected function recalculateCollisionBoxes() : array{ diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php index 73c4861bf..be49c7a91 100644 --- a/src/block/ChiseledBookshelf.php +++ b/src/block/ChiseledBookshelf.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf; use pocketmine\block\utils\ChiseledBookshelfSlot; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Book; @@ -38,7 +39,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function spl_object_id; -class ChiseledBookshelf extends Opaque{ +class ChiseledBookshelf extends Opaque implements HorizontalFacing{ use HorizontalFacingTrait; use FacesOppositePlacingPlayerTrait; diff --git a/src/block/ChorusFlower.php b/src/block/ChorusFlower.php index cc3c606d9..ef48d5a89 100644 --- a/src/block/ChorusFlower.php +++ b/src/block/ChorusFlower.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\entity\projectile\Projectile; @@ -40,7 +41,7 @@ use function array_rand; use function min; use function mt_rand; -final class ChorusFlower extends Flowable{ +final class ChorusFlower extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index 83e1de34b..ae09ccb0a 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\WoodType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -39,7 +41,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function mt_rand; -class CocoaBlock extends Flowable{ +class CocoaBlock extends Flowable implements Ageable, HorizontalFacing{ use HorizontalFacingTrait; use AgeableTrait; diff --git a/src/block/Concrete.php b/src/block/Concrete.php index fae6f8e2f..6cad11193 100644 --- a/src/block/Concrete.php +++ b/src/block/Concrete.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -class Concrete extends Opaque{ +class Concrete extends Opaque implements Colored{ use ColoredTrait; } diff --git a/src/block/ConcretePowder.php b/src/block/ConcretePowder.php index 59f14bc72..89b53053b 100644 --- a/src/block/ConcretePowder.php +++ b/src/block/ConcretePowder.php @@ -24,12 +24,13 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; use pocketmine\math\Facing; -class ConcretePowder extends Opaque implements Fallable{ +class ConcretePowder extends Opaque implements Fallable, Colored{ use ColoredTrait; use FallableTrait { onNearbyBlockChange as protected startFalling; diff --git a/src/block/CopperBulb.php b/src/block/CopperBulb.php index 97fc209fe..d0bdedf3c 100644 --- a/src/block/CopperBulb.php +++ b/src/block/CopperBulb.php @@ -26,11 +26,13 @@ namespace pocketmine\block; use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\CopperTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class CopperBulb extends Opaque implements CopperMaterial{ +class CopperBulb extends Opaque implements CopperMaterial, Lightable, PoweredByRedstone{ use CopperTrait; use PoweredByRedstoneTrait; use LightableTrait{ diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php index 3e7ca8224..c21209998 100644 --- a/src/block/CoralBlock.php +++ b/src/block/CoralBlock.php @@ -24,11 +24,12 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\CoralMaterial; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\item\Item; use function mt_rand; -final class CoralBlock extends Opaque{ +final class CoralBlock extends Opaque implements CoralMaterial{ use CoralTypeTrait; public function onNearbyBlockChange() : void{ diff --git a/src/block/Crops.php b/src/block/Crops.php index e90ac6236..b0488d150 100644 --- a/src/block/Crops.php +++ b/src/block/Crops.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CropGrowthHelper; @@ -34,7 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; -abstract class Crops extends Flowable{ +abstract class Crops extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php index 5720af529..ed56d2f44 100644 --- a/src/block/DaylightSensor.php +++ b/src/block/DaylightSensor.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -36,7 +37,7 @@ use function max; use function round; use const M_PI; -class DaylightSensor extends Transparent{ +class DaylightSensor extends Transparent implements AnalogRedstoneSignalEmitter{ use AnalogRedstoneSignalEmitterTrait; protected bool $inverted = false; diff --git a/src/block/Door.php b/src/block/Door.php index fa88267e1..2ff53645c 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\DoorSound; -class Door extends Transparent{ +class Door extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $top = false; diff --git a/src/block/DoublePitcherCrop.php b/src/block/DoublePitcherCrop.php index 1233ed05d..89d9e98b5 100644 --- a/src/block/DoublePitcherCrop.php +++ b/src/block/DoublePitcherCrop.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\CropGrowthHelper; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -37,7 +38,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class DoublePitcherCrop extends DoublePlant{ +final class DoublePitcherCrop extends DoublePlant implements Ageable{ use AgeableTrait { describeBlockOnlyState as describeAge; } diff --git a/src/block/DyedCandle.php b/src/block/DyedCandle.php index a495e8d00..57a8b5923 100644 --- a/src/block/DyedCandle.php +++ b/src/block/DyedCandle.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -class DyedCandle extends Candle{ +class DyedCandle extends Candle implements Colored{ use ColoredTrait; protected function getCandleIfCompatibleType(Block $block) : ?Candle{ diff --git a/src/block/DyedShulkerBox.php b/src/block/DyedShulkerBox.php index 5eae9237e..8be2718a2 100644 --- a/src/block/DyedShulkerBox.php +++ b/src/block/DyedShulkerBox.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class DyedShulkerBox extends ShulkerBox{ +final class DyedShulkerBox extends ShulkerBox implements Colored{ use ColoredTrait; } diff --git a/src/block/EndPortalFrame.php b/src/block/EndPortalFrame.php index ed5b77433..6c4865fdd 100644 --- a/src/block/EndPortalFrame.php +++ b/src/block/EndPortalFrame.php @@ -24,11 +24,12 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; -class EndPortalFrame extends Opaque{ +class EndPortalFrame extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; protected bool $eye = false; diff --git a/src/block/EndRod.php b/src/block/EndRod.php index a6770f370..e3d439f6a 100644 --- a/src/block/EndRod.php +++ b/src/block/EndRod.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class EndRod extends Flowable{ +class EndRod extends Flowable implements AnyFacing{ use AnyFacingTrait; public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php index 6a8cf108c..5dec4f3af 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\EnderChestInventory; use pocketmine\block\tile\EnderChest as TileEnderChest; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -33,7 +34,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class EnderChest extends Transparent{ +class EnderChest extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function getLightLevel() : int{ diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php index 2bbfdf892..d8cdfe4e2 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -35,7 +37,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\DoorSound; -class FenceGate extends Transparent{ +class FenceGate extends Transparent implements HorizontalFacing, WoodMaterial{ use WoodTypeTrait; use HorizontalFacingTrait; diff --git a/src/block/Fire.php b/src/block/Fire.php index 35a7a696c..62809fb9d 100644 --- a/src/block/Fire.php +++ b/src/block/Fire.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; @@ -35,7 +36,7 @@ use function max; use function min; use function mt_rand; -class Fire extends BaseFire{ +class Fire extends BaseFire implements Ageable{ use AgeableTrait; public const MAX_AGE = 15; diff --git a/src/block/FloorBanner.php b/src/block/FloorBanner.php index 73bc45787..ba089b6c0 100644 --- a/src/block/FloorBanner.php +++ b/src/block/FloorBanner.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SignLikeRotationTrait; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -30,7 +31,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class FloorBanner extends BaseBanner{ +final class FloorBanner extends BaseBanner implements SignLikeRotation{ use SignLikeRotationTrait; protected function getSupportingFace() : int{ diff --git a/src/block/FloorSign.php b/src/block/FloorSign.php index 5615d15d8..94e51ffe8 100644 --- a/src/block/FloorSign.php +++ b/src/block/FloorSign.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SignLikeRotationTrait; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -30,7 +31,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class FloorSign extends BaseSign{ +final class FloorSign extends BaseSign implements SignLikeRotation{ use SignLikeRotationTrait; protected function getSupportingFace() : int{ diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php index 3e8592306..046d75811 100644 --- a/src/block/FrostedIce.php +++ b/src/block/FrostedIce.php @@ -23,11 +23,12 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use function mt_rand; -class FrostedIce extends Ice{ +class FrostedIce extends Ice implements Ageable{ use AgeableTrait; public const MAX_AGE = 3; diff --git a/src/block/Furnace.php b/src/block/Furnace.php index 7a64e3cd3..54480e62c 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Furnace as TileFurnace; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\crafting\FurnaceType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -33,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; -class Furnace extends Opaque{ +class Furnace extends Opaque implements Lightable{ use FacesOppositePlacingPlayerTrait; use LightableTrait; diff --git a/src/block/GlazedTerracotta.php b/src/block/GlazedTerracotta.php index 15b3254e5..ccb928875 100644 --- a/src/block/GlazedTerracotta.php +++ b/src/block/GlazedTerracotta.php @@ -23,10 +23,12 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; -class GlazedTerracotta extends Opaque{ +class GlazedTerracotta extends Opaque implements Colored, HorizontalFacing{ use ColoredTrait; use FacesOppositePlacingPlayerTrait; } diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index a44c4d035..6ad8d3748 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\MultiAnySupportTrait; +use pocketmine\block\utils\MultiFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Fertilizer; use pocketmine\item\Item; @@ -35,7 +36,7 @@ use pocketmine\world\World; use function count; use function shuffle; -class GlowLichen extends Transparent{ +class GlowLichen extends Transparent implements MultiFacing{ use MultiAnySupportTrait; public function getLightLevel() : int{ diff --git a/src/block/HayBale.php b/src/block/HayBale.php index 6fdd2cb63..dacfe92fa 100644 --- a/src/block/HayBale.php +++ b/src/block/HayBale.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; use pocketmine\entity\Entity; -class HayBale extends Opaque{ +class HayBale extends Opaque implements PillarRotation{ use PillarRotationTrait; public function getFlameEncouragement() : int{ diff --git a/src/block/Hopper.php b/src/block/Hopper.php index 0d823674b..4956b668f 100644 --- a/src/block/Hopper.php +++ b/src/block/Hopper.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Hopper as TileHopper; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class Hopper extends Transparent{ +class Hopper extends Transparent implements PoweredByRedstone{ use PoweredByRedstoneTrait; private int $facing = Facing::DOWN; diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index c03806a3b..0fda77758 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\ItemFrame as TileItemFrame; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -39,7 +40,7 @@ use pocketmine\world\sound\ItemFrameRotateItemSound; use function is_infinite; use function is_nan; -class ItemFrame extends Flowable{ +class ItemFrame extends Flowable implements AnyFacing{ use AnyFacingTrait; public const ROTATIONS = 8; diff --git a/src/block/Ladder.php b/src/block/Ladder.php index 09c0b8f6b..6edaebbf2 100644 --- a/src/block/Ladder.php +++ b/src/block/Ladder.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\entity\Entity; @@ -35,7 +36,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class Ladder extends Transparent{ +class Ladder extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; public function hasEntityCollision() : bool{ diff --git a/src/block/Lectern.php b/src/block/Lectern.php index 03880b3c5..9ba01c1c5 100644 --- a/src/block/Lectern.php +++ b/src/block/Lectern.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Lectern as TileLectern; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -36,7 +37,7 @@ use pocketmine\player\Player; use pocketmine\world\sound\LecternPlaceBookSound; use function count; -class Lectern extends Transparent{ +class Lectern extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; protected int $viewedPage = 0; diff --git a/src/block/LightningRod.php b/src/block/LightningRod.php index a0dd50542..9c1971229 100644 --- a/src/block/LightningRod.php +++ b/src/block/LightningRod.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class LightningRod extends Transparent{ +final class LightningRod extends Transparent implements AnyFacing{ use AnyFacingTrait; protected function recalculateCollisionBoxes() : array{ diff --git a/src/block/Loom.php b/src/block/Loom.php index d3dd4f3c7..a2b9fc235 100644 --- a/src/block/Loom.php +++ b/src/block/Loom.php @@ -25,11 +25,12 @@ namespace pocketmine\block; use pocketmine\block\inventory\LoomInventory; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\player\Player; -final class Loom extends Opaque{ +final class Loom extends Opaque implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ diff --git a/src/block/NetherVines.php b/src/block/NetherVines.php index e8729c00f..67a0b6f94 100644 --- a/src/block/NetherVines.php +++ b/src/block/NetherVines.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -41,7 +42,7 @@ use function mt_rand; /** * This class is used for Weeping & Twisting vines, because they have same behaviour */ -class NetherVines extends Flowable{ +class NetherVines extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/NetherWartPlant.php b/src/block/NetherWartPlant.php index 34e6fd57e..df0609754 100644 --- a/src/block/NetherWartPlant.php +++ b/src/block/NetherWartPlant.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; @@ -31,7 +32,7 @@ use pocketmine\item\Item; use pocketmine\math\Facing; use function mt_rand; -class NetherWartPlant extends Flowable{ +class NetherWartPlant extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php index 17bc4c50a..b8b6e248c 100644 --- a/src/block/PinkPetals.php +++ b/src/block/PinkPetals.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class PinkPetals extends Flowable{ +class PinkPetals extends Flowable implements HorizontalFacing{ use HorizontalFacingTrait; use StaticSupportTrait { canBePlacedAt as supportedWhenPlacedAt; diff --git a/src/block/PitcherCrop.php b/src/block/PitcherCrop.php index d41aed284..1c771bb8a 100644 --- a/src/block/PitcherCrop.php +++ b/src/block/PitcherCrop.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CropGrowthHelper; @@ -38,7 +39,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class PitcherCrop extends Flowable{ +final class PitcherCrop extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/Planks.php b/src/block/Planks.php index 1074f8adf..ad7956361 100644 --- a/src/block/Planks.php +++ b/src/block/Planks.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class Planks extends Opaque{ +class Planks extends Opaque implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/PoweredRail.php b/src/block/PoweredRail.php index 25d6dc9e3..321b19a46 100644 --- a/src/block/PoweredRail.php +++ b/src/block/PoweredRail.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\RailPoweredByRedstoneTrait; -class PoweredRail extends StraightOnlyRail{ +class PoweredRail extends StraightOnlyRail implements PoweredByRedstone{ use RailPoweredByRedstoneTrait; } diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php index 40e1ef510..88050b85a 100644 --- a/src/block/RedstoneComparator.php +++ b/src/block/RedstoneComparator.php @@ -24,8 +24,11 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Comparator; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\block\utils\SupportType; @@ -38,7 +41,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use function assert; -class RedstoneComparator extends Flowable{ +class RedstoneComparator extends Flowable implements AnalogRedstoneSignalEmitter, PoweredByRedstone, HorizontalFacing{ use HorizontalFacingTrait; use AnalogRedstoneSignalEmitterTrait; use PoweredByRedstoneTrait; diff --git a/src/block/RedstoneLamp.php b/src/block/RedstoneLamp.php index 58098c395..33a97801d 100644 --- a/src/block/RedstoneLamp.php +++ b/src/block/RedstoneLamp.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class RedstoneLamp extends Opaque{ +class RedstoneLamp extends Opaque implements PoweredByRedstone{ use PoweredByRedstoneTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ diff --git a/src/block/RedstoneOre.php b/src/block/RedstoneOre.php index 10e701a6f..3477a3519 100644 --- a/src/block/RedstoneOre.php +++ b/src/block/RedstoneOre.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\FortuneDropHelper; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -31,7 +32,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use function mt_rand; -class RedstoneOre extends Opaque{ +class RedstoneOre extends Opaque implements Lightable{ use LightableTrait; public function getLightLevel() : int{ diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php index bf9d0c5da..4059ff1cc 100644 --- a/src/block/RedstoneRepeater.php +++ b/src/block/RedstoneRepeater.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\block\utils\SupportType; @@ -35,7 +37,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class RedstoneRepeater extends Flowable{ +class RedstoneRepeater extends Flowable implements PoweredByRedstone, HorizontalFacing{ use HorizontalFacingTrait; use PoweredByRedstoneTrait; use StaticSupportTrait; diff --git a/src/block/RedstoneTorch.php b/src/block/RedstoneTorch.php index 26c86038b..f73076ccc 100644 --- a/src/block/RedstoneTorch.php +++ b/src/block/RedstoneTorch.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Lightable; use pocketmine\block\utils\LightableTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class RedstoneTorch extends Torch{ +class RedstoneTorch extends Torch implements Lightable{ use LightableTrait; public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){ diff --git a/src/block/RedstoneWire.php b/src/block/RedstoneWire.php index a2d293fca..6be27bcb4 100644 --- a/src/block/RedstoneWire.php +++ b/src/block/RedstoneWire.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; use pocketmine\block\utils\StaticSupportTrait; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\Facing; -class RedstoneWire extends Flowable{ +class RedstoneWire extends Flowable implements AnalogRedstoneSignalEmitter{ use AnalogRedstoneSignalEmitterTrait; use StaticSupportTrait; diff --git a/src/block/ResinClump.php b/src/block/ResinClump.php index 75126edf3..2855a7cc3 100644 --- a/src/block/ResinClump.php +++ b/src/block/ResinClump.php @@ -24,9 +24,10 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\MultiAnySupportTrait; +use pocketmine\block\utils\MultiFacing; use pocketmine\block\utils\SupportType; -final class ResinClump extends Transparent{ +final class ResinClump extends Transparent implements MultiFacing{ use MultiAnySupportTrait; public function isSolid() : bool{ diff --git a/src/block/RespawnAnchor.php b/src/block/RespawnAnchor.php new file mode 100644 index 000000000..e19ea8f6d --- /dev/null +++ b/src/block/RespawnAnchor.php @@ -0,0 +1,123 @@ +boundedIntAuto(self::MIN_CHARGES, self::MAX_CHARGES, $this->charges); + } + + public function getCharges() : int{ + return $this->charges; + } + + /** @return $this */ + public function setCharges(int $charges) : self{ + if($charges < self::MIN_CHARGES || $charges > self::MAX_CHARGES){ + throw new \InvalidArgumentException("Charges must be between " . self::MIN_CHARGES . " and " . self::MAX_CHARGES . ", given: $charges"); + } + $this->charges = $charges; + return $this; + } + + public function getLightLevel() : int{ + return $this->charges > 0 ? ($this->charges * 4) - 1 : 0; + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($item->getTypeId() === ItemTypeIds::fromBlockTypeId(BlockTypeIds::GLOWSTONE) && $this->charges < self::MAX_CHARGES){ + $this->position->getWorld()->setBlock($this->position, $this->setCharges($this->charges + 1)); + $this->position->getWorld()->addSound($this->position, new RespawnAnchorChargeSound()); + return true; + } + + if($this->charges > self::MIN_CHARGES){ + if($player === null){ + return false; + } + + $ev = new PlayerRespawnAnchorUseEvent($player, $this, PlayerRespawnAnchorUseEvent::ACTION_EXPLODE); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + + switch($ev->getAction()){ + case PlayerRespawnAnchorUseEvent::ACTION_EXPLODE: + $this->explode($player); + return true; + + case PlayerRespawnAnchorUseEvent::ACTION_SET_SPAWN: + if($player->getSpawn() !== null && $player->getSpawn()->equals($this->position)){ + return true; + } + + $player->setSpawn($this->position); + $this->position->getWorld()->addSound($this->position, new RespawnAnchorSetSpawnSound()); + $player->sendMessage(KnownTranslationFactory::tile_respawn_anchor_respawnSet()->prefix(TextFormat::GRAY)); + return true; + } + } + return false; + } + + private function explode(?Player $player) : void{ + $ev = new BlockPreExplodeEvent($this, 5, $player); + $ev->setIncendiary(true); + + $ev->call(); + if($ev->isCancelled()){ + return; + } + + $this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR()); + + $explosion = new Explosion(Position::fromObject($this->position->add(0.5, 0.5, 0.5), $this->position->getWorld()), $ev->getRadius(), $this); + $explosion->setFireChance($ev->getFireChance()); + + if($ev->isBlockBreaking()){ + $explosion->explodeA(); + } + $explosion->explodeB(); + } +} diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php index d557401ee..52a3b83cd 100644 --- a/src/block/ShulkerBox.php +++ b/src/block/ShulkerBox.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\ShulkerBox as TileShulkerBox; +use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class ShulkerBox extends Opaque{ +class ShulkerBox extends Opaque implements AnyFacing{ use AnyFacingTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ diff --git a/src/block/SimplePillar.php b/src/block/SimplePillar.php index 98c89f89c..6a7af96ed 100644 --- a/src/block/SimplePillar.php +++ b/src/block/SimplePillar.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; /** * @internal This class provides a general base for pillar-like blocks. It **should not** be used for contract binding * in APIs, because not all pillar-like blocks extend this class. */ -class SimplePillar extends Opaque{ +class SimplePillar extends Opaque implements PillarRotation{ use PillarRotationTrait; } diff --git a/src/block/SmallDripleaf.php b/src/block/SmallDripleaf.php index d192e43db..846be5c93 100644 --- a/src/block/SmallDripleaf.php +++ b/src/block/SmallDripleaf.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -36,7 +37,7 @@ use pocketmine\world\BlockTransaction; use pocketmine\world\Position; use function mt_rand; -class SmallDripleaf extends Transparent{ +class SmallDripleaf extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $top = false; diff --git a/src/block/StainedGlass.php b/src/block/StainedGlass.php index bc0d84877..baf4755bb 100644 --- a/src/block/StainedGlass.php +++ b/src/block/StainedGlass.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedGlass extends Glass{ +final class StainedGlass extends Glass implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedGlassPane.php b/src/block/StainedGlassPane.php index 18ecfdee0..897c291c7 100644 --- a/src/block/StainedGlassPane.php +++ b/src/block/StainedGlassPane.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedGlassPane extends GlassPane{ +final class StainedGlassPane extends GlassPane implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedHardenedClay.php b/src/block/StainedHardenedClay.php index 2c2c01ba3..765e97e4f 100644 --- a/src/block/StainedHardenedClay.php +++ b/src/block/StainedHardenedClay.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedHardenedClay extends HardenedClay{ +final class StainedHardenedClay extends HardenedClay implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedHardenedGlass.php b/src/block/StainedHardenedGlass.php index cc609a49a..c3a794e4d 100644 --- a/src/block/StainedHardenedGlass.php +++ b/src/block/StainedHardenedGlass.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedHardenedGlass extends HardenedGlass{ +final class StainedHardenedGlass extends HardenedGlass implements Colored{ use ColoredTrait; } diff --git a/src/block/StainedHardenedGlassPane.php b/src/block/StainedHardenedGlassPane.php index 63dbe1f77..9631ff5a9 100644 --- a/src/block/StainedHardenedGlassPane.php +++ b/src/block/StainedHardenedGlassPane.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -final class StainedHardenedGlassPane extends HardenedGlassPane{ +final class StainedHardenedGlassPane extends HardenedGlassPane implements Colored{ use ColoredTrait; } diff --git a/src/block/Stair.php b/src/block/Stair.php index d66a9ce5c..56101de13 100644 --- a/src/block/Stair.php +++ b/src/block/Stair.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\StairShape; use pocketmine\block\utils\SupportType; @@ -35,7 +36,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class Stair extends Transparent{ +class Stair extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $upsideDown = false; diff --git a/src/block/Stem.php b/src/block/Stem.php index 2ac95aa3f..2b6f2150c 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -25,11 +25,12 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CropGrowthHelper; +use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\math\Facing; use function array_rand; -use function mt_rand; abstract class Stem extends Crops{ protected int $facing = Facing::UP; @@ -90,8 +91,10 @@ abstract class Stem extends Crops{ } public function getDropsForCompatibleTool(Item $item) : array{ + //TODO: bit annoying we have to pass an Item instance here + //this should not be affected by Fortune, but still follows a binomial distribution return [ - $this->asItem()->setCount(mt_rand(0, 2)) + $this->asItem()->setCount(FortuneDropHelper::binomial(VanillaItems::AIR(), 0, chance: ($this->age + 1) / 15)) ]; } } diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php index 30c19d25d..0fd259326 100644 --- a/src/block/Stonecutter.php +++ b/src/block/Stonecutter.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\StonecutterInventory; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -32,7 +33,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class Stonecutter extends Transparent{ +class Stonecutter extends Transparent implements HorizontalFacing{ use FacesOppositePlacingPlayerTrait; public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 2da2dc9b9..0874413c5 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\StaticSupportTrait; @@ -34,7 +35,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\Position; -class Sugarcane extends Flowable{ +class Sugarcane extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait { onNearbyBlockChange as onSupportBlockChange; diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index 5fd7fcaa1..baebb7588 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Ageable; use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; @@ -39,7 +40,7 @@ use pocketmine\player\Player; use pocketmine\world\sound\SweetBerriesPickSound; use function mt_rand; -class SweetBerryBush extends Flowable{ +class SweetBerryBush extends Flowable implements Ageable{ use AgeableTrait; use StaticSupportTrait; diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php index a903e1b5e..5e8a7dc3f 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -34,7 +35,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\DoorSound; -class Trapdoor extends Transparent{ +class Trapdoor extends Transparent implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $open = false; diff --git a/src/block/TripwireHook.php b/src/block/TripwireHook.php index 325819825..4e40cf62e 100644 --- a/src/block/TripwireHook.php +++ b/src/block/TripwireHook.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -32,7 +33,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -class TripwireHook extends Flowable{ +class TripwireHook extends Flowable implements HorizontalFacing{ use HorizontalFacingTrait; protected bool $connected = false; diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index f4c318f32..20107d41f 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -694,6 +694,7 @@ use function strtolower; * @method static Stair RESIN_BRICK_STAIRS() * @method static Wall RESIN_BRICK_WALL() * @method static ResinClump RESIN_CLUMP() + * @method static RespawnAnchor RESPAWN_ANCHOR() * @method static DoublePlant ROSE_BUSH() * @method static Sand SAND() * @method static Opaque SANDSTONE() @@ -859,7 +860,7 @@ final class VanillaBlocks{ $railBreakInfo = new Info(new BreakInfo(0.7)); self::register("activator_rail", fn(BID $id) => new ActivatorRail($id, "Activator Rail", $railBreakInfo)); self::register("anvil", fn(BID $id) => new Anvil($id, "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); - self::register("bamboo", fn(BID $id) => new Bamboo($id, "Bamboo", new Info(new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{ + self::register("bamboo", fn(BID $id) => new Bamboo($id, "Bamboo", new Info(new class(1.0, ToolType::AXE) extends BreakInfo{ public function getBreakTime(Item $item) : float{ if($item->getBlockToolType() === ToolType::SWORD){ return 0.0; @@ -867,7 +868,7 @@ final class VanillaBlocks{ return parent::getBreakTime($item); } }, [Tags::POTTABLE_PLANTS]))); - self::register("bamboo_sapling", fn(BID $id) => new BambooSapling($id, "Bamboo Sapling", new Info(BreakInfo::instant()))); + self::register("bamboo_sapling", fn(BID $id) => new BambooSapling($id, "Bamboo Sapling", new Info(new BreakInfo(1.0)))); $bannerBreakInfo = new Info(BreakInfo::axe(1.0)); self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class); @@ -876,7 +877,7 @@ final class VanillaBlocks{ self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible()))); self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class); self::register("bed", fn(BID $id) => new Bed($id, "Bed Block", new Info(new BreakInfo(0.2))), TileBed::class); - self::register("bedrock", fn(BID $id) => new Bedrock($id, "Bedrock", new Info(BreakInfo::indestructible()))); + self::register("bedrock", fn(BID $id) => new Bedrock($id, "Bedrock", new Info(BreakInfo::indestructible(18000000.0)))); self::register("beetroots", fn(BID $id) => new Beetroot($id, "Beetroot Block", new Info(BreakInfo::instant()))); self::register("bell", fn(BID $id) => new Bell($id, "Bell", new Info(BreakInfo::pickaxe(5.0))), TileBell::class); @@ -913,7 +914,7 @@ final class VanillaBlocks{ self::register("cobweb", fn(BID $id) => new Cobweb($id, "Cobweb", new Info(new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)))); self::register("cocoa_pod", fn(BID $id) => new CocoaBlock($id, "Cocoa Block", new Info(BreakInfo::axe(0.2, null, 15.0)))); - self::register("coral_block", fn(BID $id) => new CoralBlock($id, "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD)))); + self::register("coral_block", fn(BID $id) => new CoralBlock($id, "Coral Block", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)))); self::register("daylight_sensor", fn(BID $id) => new DaylightSensor($id, "Daylight Sensor", new Info(BreakInfo::axe(0.2))), TileDaylightSensor::class); self::register("dead_bush", fn(BID $id) => new DeadBush($id, "Dead Bush", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); self::register("detector_rail", fn(BID $id) => new DetectorRail($id, "Detector Rail", $railBreakInfo)); @@ -930,15 +931,15 @@ final class VanillaBlocks{ self::register("pitcher_plant", fn(BID $id) => new DoublePlant($id, "Pitcher Plant", new Info(BreakInfo::instant()))); self::register("pitcher_crop", fn(BID $id) => new PitcherCrop($id, "Pitcher Crop", new Info(BreakInfo::instant()))); self::register("double_pitcher_crop", fn(BID $id) => new DoublePitcherCrop($id, "Double Pitcher Crop", new Info(BreakInfo::instant()))); - self::register("dragon_egg", fn(BID $id) => new DragonEgg($id, "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); + self::register("dragon_egg", fn(BID $id) => new DragonEgg($id, "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, blastResistance: 45.0)))); self::register("dried_kelp", fn(BID $id) => new DriedKelp($id, "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5)))); self::register("emerald", fn(BID $id) => new Opaque($id, "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); self::register("enchanting_table", fn(BID $id) => new EnchantingTable($id, "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))), TileEnchantingTable::class); - self::register("end_portal_frame", fn(BID $id) => new EndPortalFrame($id, "End Portal Frame", new Info(BreakInfo::indestructible()))); + self::register("end_portal_frame", fn(BID $id) => new EndPortalFrame($id, "End Portal Frame", new Info(BreakInfo::indestructible(18000000.0)))); self::register("end_rod", fn(BID $id) => new EndRod($id, "End Rod", new Info(BreakInfo::instant()))); self::register("end_stone", fn(BID $id) => new Opaque($id, "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); - $endBrickBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD, 4.0)); + $endBrickBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)); self::register("end_stone_bricks", fn(BID $id) => new Opaque($id, "End Stone Bricks", $endBrickBreakInfo)); self::register("end_stone_brick_stairs", fn(BID $id) => new Stair($id, "End Stone Brick Stairs", $endBrickBreakInfo)); @@ -962,7 +963,7 @@ final class VanillaBlocks{ self::register("torchflower", fn(BID $id) => new Flower($id, "Torchflower", $flowerTypeInfo)); self::register("torchflower_crop", fn(BID $id) => new TorchflowerCrop($id, "Torchflower Crop", new Info(BreakInfo::instant()))); self::register("flower_pot", fn(BID $id) => new FlowerPot($id, "Flower Pot", new Info(BreakInfo::instant())), TileFlowerPot::class); - self::register("frosted_ice", fn(BID $id) => new FrostedIce($id, "Frosted Ice", new Info(BreakInfo::pickaxe(2.5)))); + self::register("frosted_ice", fn(BID $id) => new FrostedIce($id, "Frosted Ice", new Info(BreakInfo::pickaxe(0.5)))); self::register("furnace", fn(BID $id) => new Furnace($id, "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE), TileNormalFurnace::class); self::register("blast_furnace", fn(BID $id) => new Furnace($id, "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::BLAST_FURNACE), TileBlastFurnace::class); self::register("smoker", fn(BID $id) => new Furnace($id, "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::SMOKER), TileSmoker::class); @@ -970,30 +971,28 @@ final class VanillaBlocks{ $glassBreakInfo = new Info(new BreakInfo(0.3)); self::register("glass", fn(BID $id) => new Glass($id, "Glass", $glassBreakInfo)); self::register("glass_pane", fn(BID $id) => new GlassPane($id, "Glass Pane", $glassBreakInfo)); - self::register("glowing_obsidian", fn(BID $id) => new GlowingObsidian($id, "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND, 50.0)))); + self::register("glowing_obsidian", fn(BID $id) => new GlowingObsidian($id, "Glowing Obsidian", new Info(BreakInfo::pickaxe(35.0, ToolTier::DIAMOND, 6000.0)))); self::register("glowstone", fn(BID $id) => new Glowstone($id, "Glowstone", new Info(BreakInfo::pickaxe(0.3)))); - self::register("glow_lichen", fn(BID $id) => new GlowLichen($id, "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2)))); + self::register("glow_lichen", fn(BID $id) => new GlowLichen($id, "Glow Lichen", new Info(BreakInfo::axe(0.2)))); self::register("gold", fn(BID $id) => new Opaque($id, "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON, 30.0)))); - $grassBreakInfo = BreakInfo::shovel(0.6); - self::register("grass", fn(BID $id) => new Grass($id, "Grass", new Info($grassBreakInfo, [Tags::DIRT]))); - self::register("grass_path", fn(BID $id) => new GrassPath($id, "Grass Path", new Info($grassBreakInfo))); + self::register("grass", fn(BID $id) => new Grass($id, "Grass", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); + self::register("grass_path", fn(BID $id) => new GrassPath($id, "Grass Path", new Info(BreakInfo::shovel(0.65)))); self::register("gravel", fn(BID $id) => new Gravel($id, "Gravel", new Info(BreakInfo::shovel(0.6)))); - $hardenedClayBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)); - self::register("hardened_clay", fn(BID $id) => new HardenedClay($id, "Hardened Clay", $hardenedClayBreakInfo)); + self::register("hardened_clay", fn(BID $id) => new HardenedClay($id, "Hardened Clay", new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)))); $hardenedGlassBreakInfo = new Info(new BreakInfo(10.0)); self::register("hardened_glass", fn(BID $id) => new HardenedGlass($id, "Hardened Glass", $hardenedGlassBreakInfo)); self::register("hardened_glass_pane", fn(BID $id) => new HardenedGlassPane($id, "Hardened Glass Pane", $hardenedGlassBreakInfo)); self::register("hay_bale", fn(BID $id) => new HayBale($id, "Hay Bale", new Info(new BreakInfo(0.5)))); - self::register("hopper", fn(BID $id) => new Hopper($id, "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 15.0))), TileHopper::class); + self::register("hopper", fn(BID $id) => new Hopper($id, "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 24.0))), TileHopper::class); self::register("ice", fn(BID $id) => new Ice($id, "Ice", new Info(BreakInfo::pickaxe(0.5)))); $updateBlockBreakInfo = new Info(new BreakInfo(1.0)); self::register("info_update", fn(BID $id) => new Opaque($id, "update!", $updateBlockBreakInfo)); self::register("info_update2", fn(BID $id) => new Opaque($id, "ate!upd", $updateBlockBreakInfo)); - self::register("invisible_bedrock", fn(BID $id) => new Transparent($id, "Invisible Bedrock", new Info(BreakInfo::indestructible()))); + self::register("invisible_bedrock", fn(BID $id) => new Transparent($id, "Invisible Bedrock", new Info(BreakInfo::indestructible(18000000.0)))); $ironBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::STONE, 30.0)); self::register("iron", fn(BID $id) => new Opaque($id, "Iron Block", $ironBreakInfo)); @@ -1006,16 +1005,16 @@ final class VanillaBlocks{ self::register("item_frame", fn(BID $id) => new ItemFrame($id, "Item Frame", $itemFrameInfo), TileItemFrame::class); self::register("glowing_item_frame", fn(BID $id) => new ItemFrame($id, "Glow Item Frame", $itemFrameInfo), TileGlowingItemFrame::class); - self::register("jukebox", fn(BID $id) => new Jukebox($id, "Jukebox", new Info(BreakInfo::axe(0.8))), TileJukebox::class); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not + self::register("jukebox", fn(BID $id) => new Jukebox($id, "Jukebox", new Info(BreakInfo::axe(2.0, blastResistance: 30.0))), TileJukebox::class); self::register("ladder", fn(BID $id) => new Ladder($id, "Ladder", new Info(BreakInfo::axe(0.4)))); - $lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0)); + $lanternBreakInfo = new Info(BreakInfo::pickaxe(3.5)); self::register("lantern", fn(BID $id) => new Lantern($id, "Lantern", $lanternBreakInfo, 15)); self::register("soul_lantern", fn(BID $id) => new Lantern($id, "Soul Lantern", $lanternBreakInfo, 10)); self::register("lapis_lazuli", fn(BID $id) => new Opaque($id, "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE)))); self::register("lava", fn(BID $id) => new Lava($id, "Lava", new Info(BreakInfo::indestructible(500.0)))); - self::register("lectern", fn(BID $id) => new Lectern($id, "Lectern", new Info(BreakInfo::axe(2.0))), TileLectern::class); + self::register("lectern", fn(BID $id) => new Lectern($id, "Lectern", new Info(BreakInfo::axe(2.5))), TileLectern::class); self::register("lever", fn(BID $id) => new Lever($id, "Lever", new Info(new BreakInfo(0.5)))); self::register("magma", fn(BID $id) => new Magma($id, "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); self::register("melon", fn(BID $id) => new Melon($id, "Melon Block", new Info(BreakInfo::axe(1.0)))); @@ -1065,14 +1064,15 @@ final class VanillaBlocks{ self::register("purpur_stairs", fn(BID $id) => new Stair($id, "Purpur Stairs", $purpurBreakInfo)); $quartzBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); + $smoothQuartzBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); self::register("quartz", fn(BID $id) => new Opaque($id, "Quartz Block", $quartzBreakInfo)); self::register("chiseled_quartz", fn(BID $id) => new SimplePillar($id, "Chiseled Quartz Block", $quartzBreakInfo)); self::register("quartz_pillar", fn(BID $id) => new SimplePillar($id, "Quartz Pillar", $quartzBreakInfo)); - self::register("smooth_quartz", fn(BID $id) => new Opaque($id, "Smooth Quartz Block", $quartzBreakInfo)); + self::register("smooth_quartz", fn(BID $id) => new Opaque($id, "Smooth Quartz Block", $smoothQuartzBreakInfo)); self::register("quartz_bricks", fn(BID $id) => new Opaque($id, "Quartz Bricks", $quartzBreakInfo)); self::register("quartz_stairs", fn(BID $id) => new Stair($id, "Quartz Stairs", $quartzBreakInfo)); - self::register("smooth_quartz_stairs", fn(BID $id) => new Stair($id, "Smooth Quartz Stairs", $quartzBreakInfo)); + self::register("smooth_quartz_stairs", fn(BID $id) => new Stair($id, "Smooth Quartz Stairs", $smoothQuartzBreakInfo)); self::register("rail", fn(BID $id) => new Rail($id, "Rail", $railBreakInfo)); self::register("red_mushroom", fn(BID $id) => new RedMushroom($id, "Red Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); @@ -1127,13 +1127,13 @@ final class VanillaBlocks{ $infestedStoneBreakInfo = new Info(BreakInfo::pickaxe(0.75)); self::register("infested_stone", fn(BID $id) => new InfestedStone($id, "Infested Stone", $infestedStoneBreakInfo, $stone)); self::register("infested_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick)); - self::register("infested_cobblestone", fn(BID $id) => new InfestedStone($id, "Infested Cobblestone", $infestedStoneBreakInfo, $cobblestone)); + self::register("infested_cobblestone", fn(BID $id) => new InfestedStone($id, "Infested Cobblestone", new Info(BreakInfo::pickaxe(1.0, blastResistance: 3.75)), $cobblestone)); self::register("infested_mossy_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Mossy Stone Brick", $infestedStoneBreakInfo, $mossyStoneBrick)); self::register("infested_cracked_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Cracked Stone Brick", $infestedStoneBreakInfo, $crackedStoneBrick)); self::register("infested_chiseled_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Chiseled Stone Brick", $infestedStoneBreakInfo, $chiseledStoneBrick)); self::register("stone_stairs", fn(BID $id) => new Stair($id, "Stone Stairs", $stoneBreakInfo)); - self::register("smooth_stone", fn(BID $id) => new Opaque($id, "Smooth Stone", $stoneBreakInfo)); + self::register("smooth_stone", fn(BID $id) => new Opaque($id, "Smooth Stone", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)))); self::register("andesite_stairs", fn(BID $id) => new Stair($id, "Andesite Stairs", $stoneBreakInfo)); self::register("diorite_stairs", fn(BID $id) => new Stair($id, "Diorite Stairs", $stoneBreakInfo)); self::register("granite_stairs", fn(BID $id) => new Stair($id, "Granite Stairs", $stoneBreakInfo)); @@ -1146,7 +1146,6 @@ final class VanillaBlocks{ self::register("stonecutter", fn(BID $id) => new Stonecutter($id, "Stonecutter", new Info(BreakInfo::pickaxe(3.5)))); self::register("stone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5)))); - //TODO: in the future this won't be the same for all the types $stoneSlabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); self::register("brick_slab", fn(BID $id) => new Slab($id, "Brick", $stoneSlabBreakInfo)); @@ -1157,28 +1156,31 @@ final class VanillaBlocks{ self::register("sandstone_slab", fn(BID $id) => new Slab($id, "Sandstone", $stoneSlabBreakInfo)); self::register("smooth_stone_slab", fn(BID $id) => new Slab($id, "Smooth Stone", $stoneSlabBreakInfo)); self::register("stone_brick_slab", fn(BID $id) => new Slab($id, "Stone Brick", $stoneSlabBreakInfo)); - self::register("dark_prismarine_slab", fn(BID $id) => new Slab($id, "Dark Prismarine", $stoneSlabBreakInfo)); - self::register("mossy_cobblestone_slab", fn(BID $id) => new Slab($id, "Mossy Cobblestone", $stoneSlabBreakInfo)); - self::register("prismarine_slab", fn(BID $id) => new Slab($id, "Prismarine", $stoneSlabBreakInfo)); - self::register("prismarine_bricks_slab", fn(BID $id) => new Slab($id, "Prismarine Bricks", $stoneSlabBreakInfo)); - self::register("purpur_slab", fn(BID $id) => new Slab($id, "Purpur", $stoneSlabBreakInfo)); self::register("red_nether_brick_slab", fn(BID $id) => new Slab($id, "Red Nether Brick", $stoneSlabBreakInfo)); self::register("red_sandstone_slab", fn(BID $id) => new Slab($id, "Red Sandstone", $stoneSlabBreakInfo)); self::register("smooth_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Sandstone", $stoneSlabBreakInfo)); - self::register("andesite_slab", fn(BID $id) => new Slab($id, "Andesite", $stoneSlabBreakInfo)); - self::register("diorite_slab", fn(BID $id) => new Slab($id, "Diorite", $stoneSlabBreakInfo)); - self::register("end_stone_brick_slab", fn(BID $id) => new Slab($id, "End Stone Brick", $stoneSlabBreakInfo)); - self::register("granite_slab", fn(BID $id) => new Slab($id, "Granite", $stoneSlabBreakInfo)); - self::register("polished_andesite_slab", fn(BID $id) => new Slab($id, "Polished Andesite", $stoneSlabBreakInfo)); - self::register("polished_diorite_slab", fn(BID $id) => new Slab($id, "Polished Diorite", $stoneSlabBreakInfo)); - self::register("polished_granite_slab", fn(BID $id) => new Slab($id, "Polished Granite", $stoneSlabBreakInfo)); - self::register("smooth_red_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Red Sandstone", $stoneSlabBreakInfo)); self::register("cut_red_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Red Sandstone", $stoneSlabBreakInfo)); self::register("cut_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Sandstone", $stoneSlabBreakInfo)); - self::register("mossy_stone_brick_slab", fn(BID $id) => new Slab($id, "Mossy Stone Brick", $stoneSlabBreakInfo)); + self::register("mossy_cobblestone_slab", fn(BID $id) => new Slab($id, "Mossy Cobblestone", $stoneSlabBreakInfo)); + self::register("purpur_slab", fn(BID $id) => new Slab($id, "Purpur", $stoneSlabBreakInfo)); + self::register("smooth_red_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Red Sandstone", $stoneSlabBreakInfo)); self::register("smooth_quartz_slab", fn(BID $id) => new Slab($id, "Smooth Quartz", $stoneSlabBreakInfo)); self::register("stone_slab", fn(BID $id) => new Slab($id, "Stone", $stoneSlabBreakInfo)); + self::register("end_stone_brick_slab", fn(BID $id) => new Slab($id, "End Stone Brick", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 30.0)))); + + $lightStoneSlabBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); + self::register("dark_prismarine_slab", fn(BID $id) => new Slab($id, "Dark Prismarine", $lightStoneSlabBreakInfo)); + self::register("prismarine_slab", fn(BID $id) => new Slab($id, "Prismarine", $lightStoneSlabBreakInfo)); + self::register("prismarine_bricks_slab", fn(BID $id) => new Slab($id, "Prismarine Bricks", $lightStoneSlabBreakInfo)); + self::register("andesite_slab", fn(BID $id) => new Slab($id, "Andesite", $lightStoneSlabBreakInfo)); + self::register("diorite_slab", fn(BID $id) => new Slab($id, "Diorite", $lightStoneSlabBreakInfo)); + self::register("granite_slab", fn(BID $id) => new Slab($id, "Granite", $lightStoneSlabBreakInfo)); + self::register("polished_andesite_slab", fn(BID $id) => new Slab($id, "Polished Andesite", $lightStoneSlabBreakInfo)); + self::register("polished_diorite_slab", fn(BID $id) => new Slab($id, "Polished Diorite", $lightStoneSlabBreakInfo)); + self::register("polished_granite_slab", fn(BID $id) => new Slab($id, "Polished Granite", $lightStoneSlabBreakInfo)); + self::register("mossy_stone_brick_slab", fn(BID $id) => new Slab($id, "Mossy Stone Brick", $lightStoneSlabBreakInfo)); + self::register("legacy_stonecutter", fn(BID $id) => new Opaque($id, "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)))); self::register("sugarcane", fn(BID $id) => new Sugarcane($id, "Sugarcane", new Info(BreakInfo::instant()))); self::register("sweet_berry_bush", fn(BID $id) => new SweetBerryBush($id, "Sweet Berry Bush", new Info(BreakInfo::instant()))); @@ -1237,25 +1239,26 @@ final class VanillaBlocks{ } $sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); + $smoothSandstoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); self::register("red_sandstone_stairs", fn(BID $id) => new Stair($id, "Red Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Red Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Red Sandstone Stairs", $smoothSandstoneBreakInfo)); self::register("red_sandstone", fn(BID $id) => new Opaque($id, "Red Sandstone", $sandstoneBreakInfo)); self::register("chiseled_red_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Red Sandstone", $sandstoneBreakInfo)); self::register("cut_red_sandstone", fn(BID $id) => new Opaque($id, "Cut Red Sandstone", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone", fn(BID $id) => new Opaque($id, "Smooth Red Sandstone", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone", fn(BID $id) => new Opaque($id, "Smooth Red Sandstone", $smoothSandstoneBreakInfo)); self::register("sandstone_stairs", fn(BID $id) => new Stair($id, "Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Sandstone Stairs", $smoothSandstoneBreakInfo)); self::register("sandstone", fn(BID $id) => new Opaque($id, "Sandstone", $sandstoneBreakInfo)); self::register("chiseled_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Sandstone", $sandstoneBreakInfo)); self::register("cut_sandstone", fn(BID $id) => new Opaque($id, "Cut Sandstone", $sandstoneBreakInfo)); - self::register("smooth_sandstone", fn(BID $id) => new Opaque($id, "Smooth Sandstone", $sandstoneBreakInfo)); + self::register("smooth_sandstone", fn(BID $id) => new Opaque($id, "Smooth Sandstone", $smoothSandstoneBreakInfo)); self::register("glazed_terracotta", fn(BID $id) => new GlazedTerracotta($id, "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD)))); self::register("dyed_shulker_box", fn(BID $id) => new DyedShulkerBox($id, "Dyed Shulker Box", $shulkerBoxBreakInfo), TileShulkerBox::class); self::register("stained_glass", fn(BID $id) => new StainedGlass($id, "Stained Glass", $glassBreakInfo)); self::register("stained_glass_pane", fn(BID $id) => new StainedGlassPane($id, "Stained Glass Pane", $glassBreakInfo)); - self::register("stained_clay", fn(BID $id) => new StainedHardenedClay($id, "Stained Clay", $hardenedClayBreakInfo)); + self::register("stained_clay", fn(BID $id) => new StainedHardenedClay($id, "Stained Clay", new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 6.25)))); self::register("stained_hardened_glass", fn(BID $id) => new StainedHardenedGlass($id, "Stained Hardened Glass", $hardenedGlassBreakInfo)); self::register("stained_hardened_glass_pane", fn(BID $id) => new StainedHardenedGlassPane($id, "Stained Hardened Glass Pane", $hardenedGlassBreakInfo)); self::register("carpet", fn(BID $id) => new Carpet($id, "Carpet", new Info(new BreakInfo(0.1)))); @@ -1272,22 +1275,26 @@ final class VanillaBlocks{ } }))); - //TODO: in the future these won't all have the same hardness; they only do now because of the old metadata crap - $wallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("cobblestone_wall", fn(BID $id) => new Wall($id, "Cobblestone Wall", $wallBreakInfo)); - self::register("andesite_wall", fn(BID $id) => new Wall($id, "Andesite Wall", $wallBreakInfo)); - self::register("brick_wall", fn(BID $id) => new Wall($id, "Brick Wall", $wallBreakInfo)); - self::register("diorite_wall", fn(BID $id) => new Wall($id, "Diorite Wall", $wallBreakInfo)); - self::register("end_stone_brick_wall", fn(BID $id) => new Wall($id, "End Stone Brick Wall", $wallBreakInfo)); - self::register("granite_wall", fn(BID $id) => new Wall($id, "Granite Wall", $wallBreakInfo)); - self::register("mossy_stone_brick_wall", fn(BID $id) => new Wall($id, "Mossy Stone Brick Wall", $wallBreakInfo)); - self::register("mossy_cobblestone_wall", fn(BID $id) => new Wall($id, "Mossy Cobblestone Wall", $wallBreakInfo)); - self::register("nether_brick_wall", fn(BID $id) => new Wall($id, "Nether Brick Wall", $wallBreakInfo)); - self::register("prismarine_wall", fn(BID $id) => new Wall($id, "Prismarine Wall", $wallBreakInfo)); - self::register("red_nether_brick_wall", fn(BID $id) => new Wall($id, "Red Nether Brick Wall", $wallBreakInfo)); - self::register("red_sandstone_wall", fn(BID $id) => new Wall($id, "Red Sandstone Wall", $wallBreakInfo)); - self::register("sandstone_wall", fn(BID $id) => new Wall($id, "Sandstone Wall", $wallBreakInfo)); - self::register("stone_brick_wall", fn(BID $id) => new Wall($id, "Stone Brick Wall", $wallBreakInfo)); + self::register("end_stone_brick_wall", fn(BID $id) => new Wall($id, "End Stone Brick Wall", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); + + $brickWallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); + self::register("cobblestone_wall", fn(BID $id) => new Wall($id, "Cobblestone Wall", $brickWallBreakInfo)); + self::register("brick_wall", fn(BID $id) => new Wall($id, "Brick Wall", $brickWallBreakInfo)); + self::register("mossy_cobblestone_wall", fn(BID $id) => new Wall($id, "Mossy Cobblestone Wall", $brickWallBreakInfo)); + self::register("nether_brick_wall", fn(BID $id) => new Wall($id, "Nether Brick Wall", $brickWallBreakInfo)); + self::register("red_nether_brick_wall", fn(BID $id) => new Wall($id, "Red Nether Brick Wall", $brickWallBreakInfo)); + + $stoneWallBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); + self::register("stone_brick_wall", fn(BID $id) => new Wall($id, "Stone Brick Wall", $stoneWallBreakInfo)); + self::register("mossy_stone_brick_wall", fn(BID $id) => new Wall($id, "Mossy Stone Brick Wall", $stoneWallBreakInfo)); + self::register("granite_wall", fn(BID $id) => new Wall($id, "Granite Wall", $stoneWallBreakInfo)); + self::register("diorite_wall", fn(BID $id) => new Wall($id, "Diorite Wall", $stoneWallBreakInfo)); + self::register("andesite_wall", fn(BID $id) => new Wall($id, "Andesite Wall", $stoneWallBreakInfo)); + self::register("prismarine_wall", fn(BID $id) => new Wall($id, "Prismarine Wall", $stoneWallBreakInfo)); + + $sandstoneWallBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD, 4.0)); + self::register("red_sandstone_wall", fn(BID $id) => new Wall($id, "Red Sandstone Wall", $sandstoneWallBreakInfo)); + self::register("sandstone_wall", fn(BID $id) => new Wall($id, "Sandstone Wall", $sandstoneWallBreakInfo)); self::registerElements(); @@ -1320,8 +1327,8 @@ final class VanillaBlocks{ self::register("mangrove_roots", fn(BID $id) => new MangroveRoots($id, "Mangrove Roots", new Info(BreakInfo::axe(0.7)))); self::register("muddy_mangrove_roots", fn(BID $id) => new SimplePillar($id, "Muddy Mangrove Roots", new Info(BreakInfo::shovel(0.7), [Tags::MUD]))); self::register("froglight", fn(BID $id) => new Froglight($id, "Froglight", new Info(new BreakInfo(0.3)))); - self::register("sculk", fn(BID $id) => new Sculk($id, "Sculk", new Info(new BreakInfo(0.6, ToolType::HOE)))); - self::register("reinforced_deepslate", fn(BID $id) => new class($id, "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 3600.0))) extends Opaque{ + self::register("sculk", fn(BID $id) => new Sculk($id, "Sculk", new Info(new BreakInfo(0.2, ToolType::HOE)))); + self::register("reinforced_deepslate", fn(BID $id) => new class($id, "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 6000.0))) extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ return []; } @@ -1537,7 +1544,7 @@ final class VanillaBlocks{ self::register("lapis_lazuli_ore", fn(BID $id) => new LapisOre($id, "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE))); self::register("redstone_ore", fn(BID $id) => new RedstoneOre($id, "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON))); - $deepslateOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(4.5, $toolTier)); + $deepslateOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(4.5, $toolTier, 15.0)); self::register("deepslate_coal_ore", fn(BID $id) => new CoalOre($id, "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD))); self::register("deepslate_copper_ore", fn(BID $id) => new CopperOre($id, "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE))); self::register("deepslate_diamond_ore", fn(BID $id) => new DiamondOre($id, "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON))); @@ -1581,10 +1588,10 @@ final class VanillaBlocks{ //for some reason, slabs have weird hardness like the legacy ones $slabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("ancient_debris", fn(BID $id) => new class($id, "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))) extends Opaque{ + self::register("ancient_debris", fn(BID $id) => new class($id, "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 6000.0))) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); - $netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 3600.0)); + $netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 6000.0)); self::register("netherite", fn(BID $id) => new class($id, "Netherite Block", $netheriteBreakInfo) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); @@ -1602,14 +1609,14 @@ final class VanillaBlocks{ self::register("gilded_blackstone", fn(BID $id) => new GildedBlackstone($id, "Gilded Blackstone", $blackstoneBreakInfo)); - //TODO: polished blackstone ought to have 2.0 hardness (as per java) but it's 1.5 in Bedrock (probably parity bug) + $polishedBlackstoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); $prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : ""); - self::register("polished_blackstone", fn(BID $id) => new Opaque($id, $prefix(""), $blackstoneBreakInfo)); + self::register("polished_blackstone", fn(BID $id) => new Opaque($id, $prefix(""), $polishedBlackstoneBreakInfo)); self::register("polished_blackstone_button", fn(BID $id) => new StoneButton($id, $prefix("Button"), new Info(BreakInfo::pickaxe(0.5)))); self::register("polished_blackstone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5)), 20)); self::register("polished_blackstone_slab", fn(BID $id) => new Slab($id, $prefix(""), $slabBreakInfo)); - self::register("polished_blackstone_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $blackstoneBreakInfo)); - self::register("polished_blackstone_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $blackstoneBreakInfo)); + self::register("polished_blackstone_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $polishedBlackstoneBreakInfo)); + self::register("polished_blackstone_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $polishedBlackstoneBreakInfo)); self::register("chiseled_polished_blackstone", fn(BID $id) => new Opaque($id, "Chiseled Polished Blackstone", $blackstoneBreakInfo)); $prefix = fn(string $thing) => "Polished Blackstone Brick" . ($thing !== "" ? " $thing" : ""); @@ -1622,8 +1629,7 @@ final class VanillaBlocks{ self::register("soul_torch", fn(BID $id) => new Torch($id, "Soul Torch", new Info(BreakInfo::instant()))); self::register("soul_fire", fn(BID $id) => new SoulFire($id, "Soul Fire", new Info(BreakInfo::instant(), [Tags::FIRE]))); - //TODO: soul soul ought to have 0.5 hardness (as per java) but it's 1.0 in Bedrock (probably parity bug) - self::register("soul_soil", fn(BID $id) => new Opaque($id, "Soul Soil", new Info(BreakInfo::shovel(1.0)))); + self::register("soul_soil", fn(BID $id) => new Opaque($id, "Soul Soil", new Info(BreakInfo::shovel(0.5)))); self::register("shroomlight", fn(BID $id) => new class($id, "Shroomlight", new Info(new BreakInfo(1.0, ToolType::HOE))) extends Opaque{ public function getLightLevel() : int{ return 15; } @@ -1641,7 +1647,9 @@ final class VanillaBlocks{ self::register("crimson_roots", fn(BID $id) => new NetherRoots($id, "Crimson Roots", $netherRootsInfo)); self::register("warped_roots", fn(BID $id) => new NetherRoots($id, "Warped Roots", $netherRootsInfo)); - self::register("chain", fn(BID $id) => new Chain($id, "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); + self::register("chain", fn(BID $id) => new Chain($id, "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); + + self::register("respawn_anchor", fn(BID $id) => new RespawnAnchor($id, "Respawn Anchor", new Info(BreakInfo::pickaxe(50.0, ToolTier::DIAMOND, 6000.0)))); } private static function registerBlocksR17() : void{ @@ -1659,7 +1667,7 @@ final class VanillaBlocks{ self::register("raw_gold", fn(BID $id) => new Opaque($id, "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0)))); self::register("raw_iron", fn(BID $id) => new Opaque($id, "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); - $deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD, 18.0)); + $deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD, 30.0)); self::register("deepslate", fn(BID $id) => new class($id, "Deepslate", $deepslateBreakInfo) extends SimplePillar{ public function getDropsForCompatibleTool(Item $item) : array{ return [VanillaBlocks::COBBLED_DEEPSLATE()->asItem()]; @@ -1671,29 +1679,29 @@ final class VanillaBlocks{ }); //TODO: parity issue here - in Java this has a hardness of 3.0, but in bedrock it's 3.5 - self::register("chiseled_deepslate", fn(BID $id) => new Opaque($id, "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)))); + self::register("chiseled_deepslate", fn(BID $id) => new Opaque($id, "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)))); - $deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("deepslate_bricks", fn(BID $id) => new Opaque($id, "Deepslate Bricks", $deepslateBrickBreakInfo)); self::register("deepslate_brick_slab", fn(BID $id) => new Slab($id, "Deepslate Brick", $deepslateBrickBreakInfo)); self::register("deepslate_brick_stairs", fn(BID $id) => new Stair($id, "Deepslate Brick Stairs", $deepslateBrickBreakInfo)); self::register("deepslate_brick_wall", fn(BID $id) => new Wall($id, "Deepslate Brick Wall", $deepslateBrickBreakInfo)); self::register("cracked_deepslate_bricks", fn(BID $id) => new Opaque($id, "Cracked Deepslate Bricks", $deepslateBrickBreakInfo)); - $deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("deepslate_tiles", fn(BID $id) => new Opaque($id, "Deepslate Tiles", $deepslateTilesBreakInfo)); self::register("deepslate_tile_slab", fn(BID $id) => new Slab($id, "Deepslate Tile", $deepslateTilesBreakInfo)); self::register("deepslate_tile_stairs", fn(BID $id) => new Stair($id, "Deepslate Tile Stairs", $deepslateTilesBreakInfo)); self::register("deepslate_tile_wall", fn(BID $id) => new Wall($id, "Deepslate Tile Wall", $deepslateTilesBreakInfo)); self::register("cracked_deepslate_tiles", fn(BID $id) => new Opaque($id, "Cracked Deepslate Tiles", $deepslateTilesBreakInfo)); - $cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("cobbled_deepslate", fn(BID $id) => new Opaque($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); self::register("cobbled_deepslate_slab", fn(BID $id) => new Slab($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); self::register("cobbled_deepslate_stairs", fn(BID $id) => new Stair($id, "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo)); self::register("cobbled_deepslate_wall", fn(BID $id) => new Wall($id, "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo)); - $polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); + $polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 30.0)); self::register("polished_deepslate", fn(BID $id) => new Opaque($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); self::register("polished_deepslate_slab", fn(BID $id) => new Slab($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); self::register("polished_deepslate_stairs", fn(BID $id) => new Stair($id, "Polished Deepslate Stairs", $polishedDeepslateBreakInfo)); @@ -1702,7 +1710,7 @@ final class VanillaBlocks{ self::register("tinted_glass", fn(BID $id) => new TintedGlass($id, "Tinted Glass", new Info(new BreakInfo(0.3)))); //blast resistance should be 30 if we were matched with java :( - $copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 18.0)); + $copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0)); self::register("lightning_rod", fn(BID $id) => new LightningRod($id, "Lightning Rod", $copperBreakInfo)); self::register("copper", fn(BID $id) => new Copper($id, "Copper Block", $copperBreakInfo)); @@ -1730,8 +1738,8 @@ final class VanillaBlocks{ self::register("cave_vines", fn(BID $id) => new CaveVines($id, "Cave Vines", new Info(BreakInfo::instant()))); self::register("small_dripleaf", fn(BID $id) => new SmallDripleaf($id, "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1)))); - self::register("big_dripleaf_head", fn(BID $id) => new BigDripleafHead($id, "Big Dripleaf", new Info(BreakInfo::instant()))); - self::register("big_dripleaf_stem", fn(BID $id) => new BigDripleafStem($id, "Big Dripleaf Stem", new Info(BreakInfo::instant()))); + self::register("big_dripleaf_head", fn(BID $id) => new BigDripleafHead($id, "Big Dripleaf", new Info(new BreakInfo(0.1)))); + self::register("big_dripleaf_stem", fn(BID $id) => new BigDripleafStem($id, "Big Dripleaf Stem", new Info(new BreakInfo(0.1)))); } private static function registerBlocksR18() : void{ @@ -1742,7 +1750,7 @@ final class VanillaBlocks{ self::register("mud", fn(BID $id) => new Opaque($id, "Mud", new Info(BreakInfo::shovel(0.5), [Tags::MUD]))); self::register("packed_mud", fn(BID $id) => new Opaque($id, "Packed Mud", new Info(BreakInfo::pickaxe(1.0, null, 15.0)))); - $mudBricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); + $mudBricksBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 15.0)); self::register("mud_bricks", fn(BID $id) => new Opaque($id, "Mud Bricks", $mudBricksBreakInfo)); self::register("mud_brick_slab", fn(BID $id) => new Slab($id, "Mud Brick", $mudBricksBreakInfo)); @@ -1754,7 +1762,7 @@ final class VanillaBlocks{ self::register("resin", fn(BID $id) => new Opaque($id, "Block of Resin", new Info(BreakInfo::instant()))); self::register("resin_clump", fn(BID $id) => new ResinClump($id, "Resin Clump", new Info(BreakInfo::instant()))); - $resinBricksInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD)); + $resinBricksInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); self::register("resin_brick_slab", fn(BID $id) => new Slab($id, "Resin Brick", $resinBricksInfo)); self::register("resin_brick_stairs", fn(BID $id) => new Stair($id, "Resin Brick Stairs", $resinBricksInfo)); self::register("resin_brick_wall", fn(BID $id) => new Wall($id, "Resin Brick Wall", $resinBricksInfo)); diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php index 5182fe9f8..ddb157cda 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -31,7 +32,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class WallBanner extends BaseBanner{ +final class WallBanner extends BaseBanner implements HorizontalFacing{ use HorizontalFacingTrait; protected function getSupportingFace() : int{ diff --git a/src/block/WallCoralFan.php b/src/block/WallCoralFan.php index f9dece1cd..67745a537 100644 --- a/src/block/WallCoralFan.php +++ b/src/block/WallCoralFan.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -33,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class WallCoralFan extends BaseCoral{ +final class WallCoralFan extends BaseCoral implements HorizontalFacing{ use HorizontalFacingTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ diff --git a/src/block/WallSign.php b/src/block/WallSign.php index 07826eae7..c6b42608d 100644 --- a/src/block/WallSign.php +++ b/src/block/WallSign.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; @@ -31,7 +32,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -final class WallSign extends BaseSign{ +final class WallSign extends BaseSign implements HorizontalFacing{ use HorizontalFacingTrait; protected function getSupportingFace() : int{ diff --git a/src/block/WeightedPressurePlate.php b/src/block/WeightedPressurePlate.php index 726b31f6b..065f8b2c8 100644 --- a/src/block/WeightedPressurePlate.php +++ b/src/block/WeightedPressurePlate.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\AnalogRedstoneSignalEmitter; use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait; use function ceil; use function count; use function max; use function min; -class WeightedPressurePlate extends PressurePlate{ +class WeightedPressurePlate extends PressurePlate implements AnalogRedstoneSignalEmitter{ use AnalogRedstoneSignalEmitterTrait; private readonly float $signalStrengthFactor; diff --git a/src/block/Wood.php b/src/block/Wood.php index 127533b98..7aa667bc8 100644 --- a/src/block/Wood.php +++ b/src/block/Wood.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\PillarRotation; use pocketmine\block\utils\PillarRotationTrait; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Axe; @@ -32,7 +34,7 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\ItemUseOnBlockSound; -class Wood extends Opaque{ +class Wood extends Opaque implements PillarRotation, WoodMaterial{ use PillarRotationTrait; use WoodTypeTrait; diff --git a/src/block/WoodenButton.php b/src/block/WoodenButton.php index 7ba8a7af0..4eed3a50e 100644 --- a/src/block/WoodenButton.php +++ b/src/block/WoodenButton.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenButton extends Button{ +class WoodenButton extends Button implements WoodMaterial{ use WoodTypeTrait; protected function getActivationTime() : int{ diff --git a/src/block/WoodenDoor.php b/src/block/WoodenDoor.php index 96f349e49..b7918a820 100644 --- a/src/block/WoodenDoor.php +++ b/src/block/WoodenDoor.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenDoor extends Door{ +class WoodenDoor extends Door implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenFence.php b/src/block/WoodenFence.php index c12043a86..73c8da44d 100644 --- a/src/block/WoodenFence.php +++ b/src/block/WoodenFence.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenFence extends Fence{ +class WoodenFence extends Fence implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenPressurePlate.php b/src/block/WoodenPressurePlate.php index a629c2f1c..6d638788b 100644 --- a/src/block/WoodenPressurePlate.php +++ b/src/block/WoodenPressurePlate.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodTypeTrait; -class WoodenPressurePlate extends SimplePressurePlate{ +class WoodenPressurePlate extends SimplePressurePlate implements WoodMaterial{ use WoodTypeTrait; public function __construct( diff --git a/src/block/WoodenSlab.php b/src/block/WoodenSlab.php index b388a36ea..3ed606c64 100644 --- a/src/block/WoodenSlab.php +++ b/src/block/WoodenSlab.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenSlab extends Slab{ +class WoodenSlab extends Slab implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenStairs.php b/src/block/WoodenStairs.php index 0d9ba62ce..8da3fceb6 100644 --- a/src/block/WoodenStairs.php +++ b/src/block/WoodenStairs.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenStairs extends Stair{ +class WoodenStairs extends Stair implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/WoodenTrapdoor.php b/src/block/WoodenTrapdoor.php index c0a6623a1..2b796df52 100644 --- a/src/block/WoodenTrapdoor.php +++ b/src/block/WoodenTrapdoor.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\WoodMaterial; use pocketmine\block\utils\WoodTypeTrait; -class WoodenTrapdoor extends Trapdoor{ +class WoodenTrapdoor extends Trapdoor implements WoodMaterial{ use WoodTypeTrait; public function getFuelTime() : int{ diff --git a/src/block/Wool.php b/src/block/Wool.php index 0b008ac04..d47c27d27 100644 --- a/src/block/Wool.php +++ b/src/block/Wool.php @@ -23,9 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\Colored; use pocketmine\block\utils\ColoredTrait; -class Wool extends Opaque{ +class Wool extends Opaque implements Colored{ use ColoredTrait; public function getFlameEncouragement() : int{ diff --git a/src/block/utils/Ageable.php b/src/block/utils/Ageable.php new file mode 100644 index 000000000..31fe3f459 --- /dev/null +++ b/src/block/utils/Ageable.php @@ -0,0 +1,34 @@ +writeFacingFlags($block->getFaces()); }); + $this->map(Blocks::RESPAWN_ANCHOR(), function(RespawnAnchor $block) : Writer{ + return Writer::create(Ids::RESPAWN_ANCHOR) + ->writeInt(StateNames::RESPAWN_ANCHOR_CHARGE, $block->getCharges()); + }); $this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH))); $this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index ed45a47d3..c55fde77a 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -1717,6 +1717,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapStairs(Ids::RESIN_BRICK_STAIRS, fn() => Blocks::RESIN_BRICK_STAIRS()); $this->map(Ids::RESIN_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RESIN_BRICK_WALL(), $in)); $this->map(Ids::RESIN_CLUMP, fn(Reader $in) => Blocks::RESIN_CLUMP()->setFaces($in->readFacingFlags())); + $this->map(Ids::RESPAWN_ANCHOR, function(Reader $in) : Block{ + return Blocks::RESPAWN_ANCHOR() + ->setCharges($in->readBoundedInt(StateNames::RESPAWN_ANCHOR_CHARGE, 0, 4)); + }); $this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB()); $this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS()); $this->map(Ids::SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::SANDSTONE_WALL(), $in)); diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index 5f86cde96..3178386ca 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -372,6 +372,7 @@ final class ItemTypeNames{ public const MUSIC_DISC_CREATOR = "minecraft:music_disc_creator"; public const MUSIC_DISC_CREATOR_MUSIC_BOX = "minecraft:music_disc_creator_music_box"; public const MUSIC_DISC_FAR = "minecraft:music_disc_far"; + public const MUSIC_DISC_LAVA_CHICKEN = "minecraft:music_disc_lava_chicken"; public const MUSIC_DISC_MALL = "minecraft:music_disc_mall"; public const MUSIC_DISC_MELLOHI = "minecraft:music_disc_mellohi"; public const MUSIC_DISC_OTHERSIDE = "minecraft:music_disc_otherside"; @@ -380,6 +381,7 @@ final class ItemTypeNames{ public const MUSIC_DISC_RELIC = "minecraft:music_disc_relic"; public const MUSIC_DISC_STAL = "minecraft:music_disc_stal"; public const MUSIC_DISC_STRAD = "minecraft:music_disc_strad"; + public const MUSIC_DISC_TEARS = "minecraft:music_disc_tears"; public const MUSIC_DISC_WAIT = "minecraft:music_disc_wait"; public const MUSIC_DISC_WARD = "minecraft:music_disc_ward"; public const MUTTON = "minecraft:mutton"; diff --git a/src/entity/Entity.php b/src/entity/Entity.php index f89186d19..18cd75306 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -1191,12 +1191,14 @@ abstract class Entity{ $moveBB->offset(0, 0, $dz); - if($this->stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ + $stepHeight = $this->getStepHeight(); + + if($stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ $cx = $dx; $cy = $dy; $cz = $dz; $dx = $wantedX; - $dy = $this->stepHeight; + $dy = $stepHeight; $dz = $wantedZ; $stepBB = clone $this->boundingBox; @@ -1266,6 +1268,14 @@ abstract class Entity{ Timings::$entityMove->stopTiming(); } + public function setStepHeight(float $stepHeight) : void{ + $this->stepHeight = $stepHeight; + } + + public function getStepHeight() : float{ + return $this->stepHeight; + } + protected function checkGroundState(float $wantedX, float $wantedY, float $wantedZ, float $dx, float $dy, float $dz) : void{ $this->isCollidedVertically = $wantedY !== $dy; $this->isCollidedHorizontally = ($wantedX !== $dx || $wantedZ !== $dz); diff --git a/src/entity/Human.php b/src/entity/Human.php index 1bed6d0a1..fd3287cdf 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -30,7 +30,7 @@ use pocketmine\entity\effect\EffectInstance; use pocketmine\entity\effect\VanillaEffects; use pocketmine\entity\projectile\ProjectileSource; use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\player\PlayerExhaustEvent; +use pocketmine\event\entity\EntityExhaustEvent; use pocketmine\inventory\CallbackInventoryListener; use pocketmine\inventory\Inventory; use pocketmine\inventory\InventoryHolder; @@ -173,9 +173,9 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ public function jump() : void{ parent::jump(); if($this->isSprinting()){ - $this->hungerManager->exhaust(0.2, PlayerExhaustEvent::CAUSE_SPRINT_JUMPING); + $this->hungerManager->exhaust(0.2, EntityExhaustEvent::CAUSE_SPRINT_JUMPING); }else{ - $this->hungerManager->exhaust(0.05, PlayerExhaustEvent::CAUSE_JUMPING); + $this->hungerManager->exhaust(0.05, EntityExhaustEvent::CAUSE_JUMPING); } } diff --git a/src/entity/HungerManager.php b/src/entity/HungerManager.php index 7e3b40e74..3167eaec0 100644 --- a/src/entity/HungerManager.php +++ b/src/entity/HungerManager.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace pocketmine\entity; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\event\entity\EntityExhaustEvent; use pocketmine\event\entity\EntityRegainHealthEvent; -use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\world\World; use function max; use function min; @@ -130,13 +130,13 @@ class HungerManager{ * * @return float the amount of exhaustion level increased */ - public function exhaust(float $amount, int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{ + public function exhaust(float $amount, int $cause = EntityExhaustEvent::CAUSE_CUSTOM) : float{ if(!$this->enabled){ return 0; } $evAmount = $amount; - if(PlayerExhaustEvent::hasHandlers()){ - $ev = new PlayerExhaustEvent($this->entity, $amount, $cause); + if(EntityExhaustEvent::hasHandlers()){ + $ev = new EntityExhaustEvent($this->entity, $amount, $cause); $ev->call(); if($ev->isCancelled()){ return 0.0; @@ -205,7 +205,7 @@ class HungerManager{ if($food >= 18){ if($health < $this->entity->getMaxHealth()){ $this->entity->heal(new EntityRegainHealthEvent($this->entity, 1, EntityRegainHealthEvent::CAUSE_SATURATION)); - $this->exhaust(6.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN); + $this->exhaust(6.0, EntityExhaustEvent::CAUSE_HEALTH_REGEN); } }elseif($food <= 0){ if(($difficulty === World::DIFFICULTY_EASY && $health > 10) || ($difficulty === World::DIFFICULTY_NORMAL && $health > 1) || $difficulty === World::DIFFICULTY_HARD){ diff --git a/src/entity/Living.php b/src/entity/Living.php index 852344784..6d62c85d2 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -38,6 +38,7 @@ use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDeathEvent; +use pocketmine\event\entity\EntityFrostWalkerEvent; use pocketmine\inventory\ArmorInventory; use pocketmine\inventory\CallbackInventoryListener; use pocketmine\inventory\Inventory; @@ -721,19 +722,30 @@ abstract class Living extends Entity{ $y = $this->location->getFloorY() - 1; $baseZ = $this->location->getFloorZ(); - $frostedIce = VanillaBlocks::FROSTED_ICE(); + $liquid = VanillaBlocks::WATER(); + $targetBlock = VanillaBlocks::FROSTED_ICE(); + if(EntityFrostWalkerEvent::hasHandlers()){ + $ev = new EntityFrostWalkerEvent($this, $radius, $liquid, $targetBlock); + $ev->call(); + if($ev->isCancelled()){ + return; + } + $radius = $ev->getRadius(); + $liquid = $ev->getLiquid(); + $targetBlock = $ev->getTargetBlock(); + } + for($x = $baseX - $radius; $x <= $baseX + $radius; $x++){ for($z = $baseZ - $radius; $z <= $baseZ + $radius; $z++){ $block = $world->getBlockAt($x, $y, $z); if( - !$block instanceof Water || - !$block->isSource() || + !$block->isSameState($liquid) || $world->getBlockAt($x, $y + 1, $z)->getTypeId() !== BlockTypeIds::AIR || count($world->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z))) !== 0 ){ continue; } - $world->setBlockAt($x, $y, $z, $frostedIce); + $world->setBlockAt($x, $y, $z, $targetBlock); } } } diff --git a/src/entity/effect/HungerEffect.php b/src/entity/effect/HungerEffect.php index 949b148bc..5ce85dfe8 100644 --- a/src/entity/effect/HungerEffect.php +++ b/src/entity/effect/HungerEffect.php @@ -26,7 +26,7 @@ namespace pocketmine\entity\effect; use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\entity\Living; -use pocketmine\event\player\PlayerExhaustEvent; +use pocketmine\event\entity\EntityExhaustEvent; class HungerEffect extends Effect{ @@ -36,7 +36,7 @@ class HungerEffect extends Effect{ public function applyEffect(Living $entity, EffectInstance $instance, float $potency = 1.0, ?Entity $source = null) : void{ if($entity instanceof Human){ - $entity->getHungerManager()->exhaust(0.1 * $instance->getEffectLevel(), PlayerExhaustEvent::CAUSE_POTION); + $entity->getHungerManager()->exhaust(0.1 * $instance->getEffectLevel(), EntityExhaustEvent::CAUSE_POTION); } } } diff --git a/src/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php index 937a4023f..5ea74bfac 100644 --- a/src/entity/object/EndCrystal.php +++ b/src/entity/object/EndCrystal.php @@ -129,7 +129,7 @@ class EndCrystal extends Entity implements Explosive{ $ev = new EntityPreExplodeEvent($this, 6); $ev->call(); if(!$ev->isCancelled()){ - $explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this); + $explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this, $ev->getFireChance()); if($ev->isBlockBreaking()){ $explosion->explodeA(); } diff --git a/src/entity/object/PrimedTNT.php b/src/entity/object/PrimedTNT.php index 29cbf59b9..dea6f70fb 100644 --- a/src/entity/object/PrimedTNT.php +++ b/src/entity/object/PrimedTNT.php @@ -121,7 +121,7 @@ class PrimedTNT extends Entity implements Explosive{ $ev->call(); if(!$ev->isCancelled()){ //TODO: deal with underwater TNT (underwater TNT treats water as if it has a blast resistance of 0) - $explosion = new Explosion(Position::fromObject($this->location->add(0, $this->size->getHeight() / 2, 0), $this->getWorld()), $ev->getRadius(), $this); + $explosion = new Explosion(Position::fromObject($this->location->add(0, $this->size->getHeight() / 2, 0), $this->getWorld()), $ev->getRadius(), $this, $ev->getFireChance()); if($ev->isBlockBreaking()){ $explosion->explodeA(); } diff --git a/src/event/block/BlockExplodeEvent.php b/src/event/block/BlockExplodeEvent.php new file mode 100644 index 000000000..a8501d475 --- /dev/null +++ b/src/event/block/BlockExplodeEvent.php @@ -0,0 +1,122 @@ + 100.0){ + throw new \InvalidArgumentException("Yield must be in range 0.0 - 100.0"); + } + } + + public function getPosition() : Position{ + return $this->position; + } + + /** + * Returns the percentage chance of drops from each block destroyed by the explosion. + * + * @return float 0-100 + */ + public function getYield() : float{ + return $this->yield; + } + + /** + * Sets the percentage chance of drops from each block destroyed by the explosion. + * + * @param float $yield 0-100 + */ + public function setYield(float $yield) : void{ + Utils::checkFloatNotInfOrNaN("yield", $yield); + if($yield < 0.0 || $yield > 100.0){ + throw new \InvalidArgumentException("Yield must be in range 0.0 - 100.0"); + } + $this->yield = $yield; + } + + /** + * Returns a list of blocks destroyed by the explosion. + * + * @return Block[] + */ + public function getAffectedBlocks() : array{ + return $this->blocks; + } + + /** + * Sets the blocks destroyed by the explosion. + * + * @param Block[] $blocks + */ + public function setAffectedBlocks(array $blocks) : void{ + Utils::validateArrayValueType($blocks, fn(Block $block) => null); + $this->blocks = $blocks; + } + + /** + * Returns a list of affected blocks that will be replaced by fire. + * + * @return Block[] + */ + public function getIgnitions() : array{ + return $this->ignitions; + } + + /** + * Set the list of blocks that will be replaced by fire. + * + * @param Block[] $ignitions + */ + public function setIgnitions(array $ignitions) : void{ + Utils::validateArrayValueType($ignitions, fn(Block $block) => null); + $this->ignitions = $ignitions; + } +} diff --git a/src/event/block/BlockPreExplodeEvent.php b/src/event/block/BlockPreExplodeEvent.php new file mode 100644 index 000000000..f41cb8a63 --- /dev/null +++ b/src/event/block/BlockPreExplodeEvent.php @@ -0,0 +1,129 @@ + 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } + parent::__construct($block); + } + + public function getRadius() : float{ + return $this->radius; + } + + public function setRadius(float $radius) : void{ + Utils::checkFloatNotInfOrNaN("radius", $radius); + if($radius <= 0){ + throw new \InvalidArgumentException("Explosion radius must be positive"); + } + $this->radius = $radius; + } + + public function isBlockBreaking() : bool{ + return $this->blockBreaking; + } + + public function setBlockBreaking(bool $affectsBlocks) : void{ + $this->blockBreaking = $affectsBlocks; + } + + /** + * Returns whether the explosion will create a fire. + */ + public function isIncendiary() : bool{ + return $this->fireChance > 0; + } + + /** + * Sets whether the explosion will create a fire by filling fireChance with default values. + * + * If $incendiary is true, the fire chance will be filled only if explosion isn't currently creating a fire (if fire chance is 0). + */ + public function setIncendiary(bool $incendiary) : void{ + if(!$incendiary){ + $this->fireChance = 0; + }elseif($this->fireChance <= 0){ + $this->fireChance = Explosion::DEFAULT_FIRE_CHANCE; + } + } + + /** + * Returns a chance between 0 and 1 of creating a fire. + */ + public function getFireChance() : float{ + return $this->fireChance; + } + + /** + * Sets a chance between 0 and 1 of creating a fire. + * For example, if the chance is 1/3, then that amount of affected blocks will be ignited. + * + * @param float $fireChance 0 ... 1 + */ + public function setFireChance(float $fireChance) : void{ + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } + $this->fireChance = $fireChance; + } + + /** + * Returns the player who triggered the block explosion. + * Returns null if the block was exploded by other means. + */ + public function getPlayer() : ?Player{ + return $this->player; + } +} diff --git a/src/event/player/PlayerExhaustEvent.php b/src/event/entity/EntityExhaustEvent.php similarity index 82% rename from src/event/player/PlayerExhaustEvent.php rename to src/event/entity/EntityExhaustEvent.php index 9a13ff900..6a4954a21 100644 --- a/src/event/player/PlayerExhaustEvent.php +++ b/src/event/entity/EntityExhaustEvent.php @@ -21,17 +21,16 @@ declare(strict_types=1); -namespace pocketmine\event\player; +namespace pocketmine\event\entity; -use pocketmine\entity\Human; +use pocketmine\entity\Entity; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; -use pocketmine\event\entity\EntityEvent; /** - * @phpstan-extends EntityEvent + * @phpstan-extends EntityEvent */ -class PlayerExhaustEvent extends EntityEvent implements Cancellable{ +class EntityExhaustEvent extends EntityEvent implements Cancellable{ use CancellableTrait; public const CAUSE_ATTACK = 1; @@ -47,18 +46,11 @@ class PlayerExhaustEvent extends EntityEvent implements Cancellable{ public const CAUSE_CUSTOM = 11; public function __construct( - protected Human $human, + Entity $entity, private float $amount, private int $cause ){ - $this->entity = $human; - } - - /** - * @return Human - */ - public function getPlayer(){ - return $this->human; + $this->entity = $entity; } public function getAmount() : float{ diff --git a/src/event/entity/EntityExplodeEvent.php b/src/event/entity/EntityExplodeEvent.php index 0a0e4f696..779ab879a 100644 --- a/src/event/entity/EntityExplodeEvent.php +++ b/src/event/entity/EntityExplodeEvent.php @@ -43,13 +43,15 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{ /** * @param Block[] $blocks - * @param float $yield 0-100 + * @param float $yield 0-100 + * @param Block[] $ignitions */ public function __construct( Entity $entity, protected Position $position, protected array $blocks, - protected float $yield + protected float $yield, + private array $ignitions = [] ){ $this->entity = $entity; if($yield < 0.0 || $yield > 100.0){ @@ -98,4 +100,23 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{ } $this->yield = $yield; } + + /** + * Set the list of blocks that will be replaced by fire. + * + * @param Block[] $ignitions + */ + public function setIgnitions(array $ignitions) : void{ + Utils::validateArrayValueType($ignitions, fn(Block $block) => null); + $this->ignitions = $ignitions; + } + + /** + * Returns a list of affected blocks that will be replaced by fire. + * + * @return Block[] + */ + public function getIgnitions() : array{ + return $this->ignitions; + } } diff --git a/src/event/entity/EntityFrostWalkerEvent.php b/src/event/entity/EntityFrostWalkerEvent.php new file mode 100644 index 000000000..15ba28268 --- /dev/null +++ b/src/event/entity/EntityFrostWalkerEvent.php @@ -0,0 +1,84 @@ + + */ +class EntityFrostWalkerEvent extends EntityEvent implements Cancellable{ + use CancellableTrait; + + public function __construct( + Living $entity, + private int $radius, + private Liquid $liquid, + private Block $targetBlock + ){ + $this->entity = $entity; + } + + public function getRadius() : int{ + return $this->radius; + } + + public function setRadius(int $radius) : void{ + $this->radius = $radius; + } + + /** + * Returns the liquid that gets frozen + */ + public function getLiquid() : Liquid{ + return $this->liquid; + } + + /** + * Sets the liquid that gets frozen + */ + public function setLiquid(Liquid $liquid) : void{ + $this->liquid = $liquid; + } + + /** + * Returns the block that replaces the liquid + */ + public function getTargetBlock() : Block{ + return $this->targetBlock; + } + + /** + * Sets the block that replaces the liquid + */ + public function setTargetBlock(Block $targetBlock) : void{ + $this->targetBlock = $targetBlock; + } +} diff --git a/src/event/entity/EntityPreExplodeEvent.php b/src/event/entity/EntityPreExplodeEvent.php index f02a85ecd..c88e83304 100644 --- a/src/event/entity/EntityPreExplodeEvent.php +++ b/src/event/entity/EntityPreExplodeEvent.php @@ -26,6 +26,8 @@ namespace pocketmine\event\entity; use pocketmine\entity\Entity; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\utils\Utils; +use pocketmine\world\Explosion; /** * Called when an entity decides to explode, before the explosion's impact is calculated. @@ -42,11 +44,16 @@ class EntityPreExplodeEvent extends EntityEvent implements Cancellable{ public function __construct( Entity $entity, - protected float $radius + protected float $radius, + private float $fireChance = 0.0, ){ if($radius <= 0){ throw new \InvalidArgumentException("Explosion radius must be positive"); } + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be between 0 and 1."); + } $this->entity = $entity; } @@ -61,6 +68,47 @@ class EntityPreExplodeEvent extends EntityEvent implements Cancellable{ $this->radius = $radius; } + /** + * Returns whether the explosion will create a fire. + */ + public function isIncendiary() : bool{ + return $this->fireChance > 0; + } + + /** + * Sets whether the explosion will create a fire by filling fireChance with default values. + * + * If $incendiary is true, the fire chance will be filled only if explosion isn't currently creating a fire (if fire chance is 0). + */ + public function setIncendiary(bool $incendiary) : void{ + if(!$incendiary){ + $this->fireChance = 0; + }elseif($this->fireChance <= 0){ + $this->fireChance = Explosion::DEFAULT_FIRE_CHANCE; + } + } + + /** + * Returns a chance between 0 and 1 of creating a fire. + */ + public function getFireChance() : float{ + return $this->fireChance; + } + + /** + * Sets a chance between 0 and 1 of creating a fire. + * For example, if the chance is 1/3, then that amount of affected blocks will be ignited. + * + * @param float $fireChance 0 ... 1 + */ + public function setFireChance(float $fireChance) : void{ + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be between 0 and 1."); + } + $this->fireChance = $fireChance; + } + public function isBlockBreaking() : bool{ return $this->blockBreaking; } diff --git a/src/event/player/PlayerRespawnAnchorUseEvent.php b/src/event/player/PlayerRespawnAnchorUseEvent.php new file mode 100644 index 000000000..be7697f11 --- /dev/null +++ b/src/event/player/PlayerRespawnAnchorUseEvent.php @@ -0,0 +1,56 @@ +player = $player; + } + + public function getBlock() : Block{ + return $this->block; + } + + public function getAction() : int{ + return $this->action; + } + + public function setAction(int $action) : void{ + $this->action = $action; + } +} diff --git a/src/inventory/BaseInventory.php b/src/inventory/BaseInventory.php index 0d5d1ffe6..c4afda43a 100644 --- a/src/inventory/BaseInventory.php +++ b/src/inventory/BaseInventory.php @@ -256,7 +256,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ $slotItem->setCount($slotItem->getCount() + $amount); $this->setItem($i, $slotItem); if($newItem->getCount() <= 0){ - break; + return $newItem; } } } @@ -270,7 +270,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ $slotItem->setCount($amount); $this->setItem($slotIndex, $slotItem); if($newItem->getCount() <= 0){ - break; + return $newItem; } } } diff --git a/src/item/Item.php b/src/item/Item.php index 205f15e13..8f8623c79 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -125,7 +125,7 @@ class Item implements \JsonSerializable{ /** * @return $this */ - public function clearCustomBlockData(){ + public function clearCustomBlockData() : Item{ $this->blockEntityTag = null; return $this; } @@ -202,11 +202,12 @@ class Item implements \JsonSerializable{ /** * @param string[] $canPlaceOn */ - public function setCanPlaceOn(array $canPlaceOn) : void{ + public function setCanPlaceOn(array $canPlaceOn) : Item{ $this->canPlaceOn = []; foreach($canPlaceOn as $value){ $this->canPlaceOn[$value] = $value; } + return $this; } /** @@ -220,11 +221,12 @@ class Item implements \JsonSerializable{ /** * @param string[] $canDestroy */ - public function setCanDestroy(array $canDestroy) : void{ + public function setCanDestroy(array $canDestroy) : Item{ $this->canDestroy = []; foreach($canDestroy as $value){ $this->canDestroy[$value] = $value; } + return $this; } /** @@ -234,8 +236,9 @@ class Item implements \JsonSerializable{ return $this->keepOnDeath; } - public function setKeepOnDeath(bool $keepOnDeath) : void{ + public function setKeepOnDeath(bool $keepOnDeath) : Item{ $this->keepOnDeath = $keepOnDeath; + return $this; } /** diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index a3bd7b872..7a90babed 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -993,6 +993,7 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("resin_brick_wall", fn() => Blocks::RESIN_BRICK_WALL()); $result->registerBlock("resin_bricks", fn() => Blocks::RESIN_BRICKS()); $result->registerBlock("resin_clump", fn() => Blocks::RESIN_CLUMP()); + $result->registerBlock("respawn_anchor", fn() => Blocks::RESPAWN_ANCHOR()); $result->registerBlock("rooted_dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED)); $result->registerBlock("rose", fn() => Blocks::POPPY()); $result->registerBlock("rose_bush", fn() => Blocks::ROSE_BUSH()); diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index 4e42419ea..eadd74f32 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -3035,6 +3035,14 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::TILE_BED_TOOFAR, []); } + public static function tile_respawn_anchor_notValid() : Translatable{ + return new Translatable(KnownTranslationKeys::TILE_RESPAWN_ANCHOR_NOTVALID, []); + } + + public static function tile_respawn_anchor_respawnSet() : Translatable{ + return new Translatable(KnownTranslationKeys::TILE_RESPAWN_ANCHOR_RESPAWNSET, []); + } + public static function view_distance() : Translatable{ return new Translatable(KnownTranslationKeys::VIEW_DISTANCE, []); } diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index 6fbb32ecb..44a64c489 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -658,6 +658,8 @@ final class KnownTranslationKeys{ public const TILE_BED_NOSLEEP = "tile.bed.noSleep"; public const TILE_BED_OCCUPIED = "tile.bed.occupied"; public const TILE_BED_TOOFAR = "tile.bed.tooFar"; + public const TILE_RESPAWN_ANCHOR_NOTVALID = "tile.respawn_anchor.notValid"; + public const TILE_RESPAWN_ANCHOR_RESPAWNSET = "tile.respawn_anchor.respawnSet"; public const VIEW_DISTANCE = "view_distance"; public const WELCOME_TO_POCKETMINE = "welcome_to_pocketmine"; public const WHITELIST_ENABLE = "whitelist_enable"; diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 2ff23a73a..19bd94fce 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -41,6 +41,7 @@ use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\item\enchantment\EnchantingOption; use pocketmine\item\enchantment\EnchantmentInstance; +use pocketmine\item\Item; use pocketmine\network\mcpe\cache\CreativeInventoryCache; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; @@ -228,17 +229,25 @@ class InventoryManager{ return null; } - private function addPredictedSlotChange(Inventory $inventory, int $slot, ItemStack $item) : void{ + private function addPredictedSlotChangeInternal(Inventory $inventory, int $slot, ItemStack $item) : void{ $this->inventories[spl_object_id($inventory)]->predictions[$slot] = $item; } - public function addTransactionPredictedSlotChanges(InventoryTransaction $tx) : void{ + public function addPredictedSlotChange(Inventory $inventory, int $slot, Item $item) : void{ $typeConverter = $this->session->getTypeConverter(); + $itemStack = $typeConverter->coreItemStackToNet($item); + $this->addPredictedSlotChangeInternal($inventory, $slot, $itemStack); + } + + public function addTransactionPredictedSlotChanges(InventoryTransaction $tx) : void{ foreach($tx->getActions() as $action){ if($action instanceof SlotChangeAction){ //TODO: ItemStackRequestExecutor can probably build these predictions with much lower overhead - $itemStack = $typeConverter->coreItemStackToNet($action->getTargetItem()); - $this->addPredictedSlotChange($action->getInventory(), $action->getSlot(), $itemStack); + $this->addPredictedSlotChange( + $action->getInventory(), + $action->getSlot(), + $action->getTargetItem() + ); } } } @@ -267,7 +276,7 @@ class InventoryManager{ } [$inventory, $slot] = $info; - $this->addPredictedSlotChange($inventory, $slot, $action->newItem->getItemStack()); + $this->addPredictedSlotChangeInternal($inventory, $slot, $action->newItem->getItemStack()); } } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index eec200e4b..b5990d38f 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -136,6 +136,8 @@ class InGamePacketHandler extends PacketHandler{ protected ?float $lastPlayerAuthInputPitch = null; protected ?BitSet $lastPlayerAuthInputFlags = null; + protected ?BlockPosition $lastBlockAttacked = null; + public bool $forceMoveSync = false; protected ?string $lastRequestedFullSkinId = null; @@ -211,7 +213,7 @@ class InGamePacketHandler extends PacketHandler{ } $inputFlags = $packet->getInputFlags(); - if($inputFlags !== $this->lastPlayerAuthInputFlags){ + if($this->lastPlayerAuthInputFlags === null || !$inputFlags->equals($this->lastPlayerAuthInputFlags)){ $this->lastPlayerAuthInputFlags = $inputFlags; $sneaking = $inputFlags->get(PlayerAuthInputFlags::SNEAKING); @@ -248,6 +250,28 @@ class InGamePacketHandler extends PacketHandler{ $packetHandled = true; + $useItemTransaction = $packet->getItemInteractionData(); + if($useItemTransaction !== null){ + if(count($useItemTransaction->getTransactionData()->getActions()) > 100){ + throw new PacketHandlingException("Too many actions in item use transaction"); + } + + $this->inventoryManager->setCurrentItemStackRequestId($useItemTransaction->getRequestId()); + $this->inventoryManager->addRawPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions()); + if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){ + $packetHandled = false; + $this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")"); + }else{ + $this->inventoryManager->syncMismatchedPredictedSlotChanges(); + } + $this->inventoryManager->setCurrentItemStackRequestId(null); + } + + $itemStackRequest = $packet->getItemStackRequest(); + $itemStackResponseBuilder = $itemStackRequest !== null ? $this->handleSingleItemStackRequest($itemStackRequest) : null; + + //itemstack request or transaction may set predictions for the outcome of these actions, so these need to be + //processed last $blockActions = $packet->getBlockActions(); if($blockActions !== null){ if(count($blockActions) > 100){ @@ -268,27 +292,9 @@ class InGamePacketHandler extends PacketHandler{ } } - $useItemTransaction = $packet->getItemInteractionData(); - if($useItemTransaction !== null){ - if(count($useItemTransaction->getTransactionData()->getActions()) > 100){ - throw new PacketHandlingException("Too many actions in item use transaction"); - } - - $this->inventoryManager->setCurrentItemStackRequestId($useItemTransaction->getRequestId()); - $this->inventoryManager->addRawPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions()); - if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){ - $packetHandled = false; - $this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")"); - }else{ - $this->inventoryManager->syncMismatchedPredictedSlotChanges(); - } - $this->inventoryManager->setCurrentItemStackRequestId(null); - } - - $itemStackRequest = $packet->getItemStackRequest(); if($itemStackRequest !== null){ - $result = $this->handleSingleItemStackRequest($itemStackRequest); - $this->session->sendDataPacket(ItemStackResponsePacket::create([$result])); + $itemStackResponse = $itemStackResponseBuilder?->build() ?? new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $itemStackRequest->getRequestId()); + $this->session->sendDataPacket(ItemStackResponsePacket::create([$itemStackResponse])); } return $packetHandled; @@ -498,13 +504,6 @@ class InGamePacketHandler extends PacketHandler{ //if only the client would tell us what blocks it thinks changed... $this->syncBlocksNearby($vBlockPos, $data->getFace()); return true; - case UseItemTransactionData::ACTION_BREAK_BLOCK: - $blockPos = $data->getBlockPosition(); - $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); - if(!$this->player->breakBlock($vBlockPos)){ - $this->syncBlocksNearby($vBlockPos, null); - } - return true; case UseItemTransactionData::ACTION_CLICK_AIR: if($this->player->isUsingItem()){ if(!$this->player->consumeHeldItem()){ @@ -580,7 +579,7 @@ class InGamePacketHandler extends PacketHandler{ return false; } - private function handleSingleItemStackRequest(ItemStackRequest $request) : ItemStackResponse{ + private function handleSingleItemStackRequest(ItemStackRequest $request) : ?ItemStackResponseBuilder{ if(count($request->getActions()) > 60){ //recipe book auto crafting can affect all slots of the inventory when consuming inputs or producing outputs //this means there could be as many as 50 CraftingConsumeInput actions or Place (taking the result) actions @@ -597,7 +596,11 @@ class InGamePacketHandler extends PacketHandler{ $executor = new ItemStackRequestExecutor($this->player, $this->inventoryManager, $request); try{ $transaction = $executor->generateInventoryTransaction(); - $result = $this->executeInventoryTransaction($transaction, $request->getRequestId()); + if($transaction !== null){ + $result = $this->executeInventoryTransaction($transaction, $request->getRequestId()); + }else{ + $result = true; //predictions only, just send responses + } }catch(ItemStackRequestProcessException $e){ $result = false; $this->session->getLogger()->debug("ItemStackRequest #" . $request->getRequestId() . " failed: " . $e->getMessage()); @@ -605,10 +608,7 @@ class InGamePacketHandler extends PacketHandler{ $this->inventoryManager->requestSyncAll(); } - if(!$result){ - return new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $request->getRequestId()); - } - return $executor->buildItemStackResponse(); + return $result ? $executor->getItemStackResponseBuilder() : null; } public function handleItemStackRequest(ItemStackRequestPacket $packet) : bool{ @@ -618,7 +618,7 @@ class InGamePacketHandler extends PacketHandler{ throw new PacketHandlingException("Too many requests in ItemStackRequestPacket"); } foreach($packet->getRequests() as $request){ - $responses[] = $this->handleSingleItemStackRequest($request); + $responses[] = $this->handleSingleItemStackRequest($request)?->build() ?? new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $request->getRequestId()); } $this->session->sendDataPacket(ItemStackResponsePacket::create($responses)); @@ -681,16 +681,27 @@ class InGamePacketHandler extends PacketHandler{ switch($action){ case PlayerAction::START_BREAK: + case PlayerAction::CONTINUE_DESTROY_BLOCK: //destroy the next block while holding down left click self::validateFacing($face); + if($this->lastBlockAttacked !== null && $blockPosition->equals($this->lastBlockAttacked)){ + //the client will send CONTINUE_DESTROY_BLOCK for the currently targeted block directly before it + //sends PREDICT_DESTROY_BLOCK, but also when it starts to break the block + //this seems like a bug in the client and would cause spurious left-click events if we allowed it to + //be delivered to the player + $this->session->getLogger()->debug("Ignoring PlayerAction $action on $pos because we were already destroying this block"); + break; + } if(!$this->player->attackBlock($pos, $face)){ $this->syncBlocksNearby($pos, $face); } + $this->lastBlockAttacked = $blockPosition; break; case PlayerAction::ABORT_BREAK: case PlayerAction::STOP_BREAK: $this->player->stopBreakBlock($pos); + $this->lastBlockAttacked = null; break; case PlayerAction::START_SLEEPING: //unused @@ -701,11 +712,17 @@ class InGamePacketHandler extends PacketHandler{ case PlayerAction::CRACK_BREAK: self::validateFacing($face); $this->player->continueBreakBlock($pos, $face); + $this->lastBlockAttacked = $blockPosition; break; case PlayerAction::INTERACT_BLOCK: //TODO: ignored (for now) break; case PlayerAction::CREATIVE_PLAYER_DESTROY_BLOCK: //TODO: do we need to handle this? + case PlayerAction::PREDICT_DESTROY_BLOCK: + if(!$this->player->breakBlock($pos)){ + $this->syncBlocksNearby($pos, $face); + } + $this->lastBlockAttacked = null; break; case PlayerAction::START_ITEM_USE_ON: case PlayerAction::STOP_ITEM_USE_ON: diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 6db8f1e12..d71a1c6bf 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -33,9 +33,11 @@ use pocketmine\inventory\transaction\EnchantingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionBuilder; use pocketmine\inventory\transaction\TransactionBuilderInventory; +use pocketmine\item\Durable; use pocketmine\item\Item; use pocketmine\network\mcpe\InventoryManager; use pocketmine\network\mcpe\protocol\types\inventory\ContainerUIIds; +use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftingConsumeInputStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftingCreateSpecificResultStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftRecipeAutoStackRequestAction; @@ -47,6 +49,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\DropStackReque use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequest; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequestSlotInfo; +use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\MineBlockStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\PlaceStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\SwapStackRequestAction; use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\TakeStackRequestAction; @@ -362,6 +365,16 @@ class ItemStackRequestExecutor{ $this->setNextCreatedItem($nextResultItem); }elseif($action instanceof DeprecatedCraftingResultsStackRequestAction){ //no obvious use + }elseif($action instanceof MineBlockStackRequestAction){ + $slot = $action->getHotbarSlot(); + $this->requestSlotInfos[] = new ItemStackRequestSlotInfo(new FullContainerName(ContainerUIIds::HOTBAR), $slot, $action->getStackId()); + $inventory = $this->player->getInventory(); + $usedItem = $inventory->slotExists($slot) ? $inventory->getItem($slot) : null; + $predictedDamage = $action->getPredictedDurability(); + if($usedItem instanceof Durable && $predictedDamage >= 0 && $predictedDamage <= $usedItem->getMaxDurability()){ + $usedItem->setDamage($predictedDamage); + $this->inventoryManager->addPredictedSlotChange($inventory, $slot, $usedItem); + } }else{ throw new ItemStackRequestProcessException("Unhandled item stack request action"); } @@ -370,7 +383,7 @@ class ItemStackRequestExecutor{ /** * @throws ItemStackRequestProcessException */ - public function generateInventoryTransaction() : InventoryTransaction{ + public function generateInventoryTransaction() : ?InventoryTransaction{ foreach(Utils::promoteKeys($this->request->getActions()) as $k => $action){ try{ $this->processItemStackRequestAction($action); @@ -380,6 +393,9 @@ class ItemStackRequestExecutor{ } $this->setNextCreatedItem(null); $inventoryActions = $this->builder->generateActions(); + if(count($inventoryActions) === 0){ + return null; + } $transaction = $this->specialTransaction ?? new InventoryTransaction($this->player); foreach($inventoryActions as $action){ @@ -389,12 +405,16 @@ class ItemStackRequestExecutor{ return $transaction; } - public function buildItemStackResponse() : ItemStackResponse{ + public function getItemStackResponseBuilder() : ItemStackResponseBuilder{ $builder = new ItemStackResponseBuilder($this->request->getRequestId(), $this->inventoryManager); foreach($this->requestSlotInfos as $requestInfo){ $builder->addSlot($requestInfo->getContainerName()->getContainerId(), $requestInfo->getSlotId()); } - return $builder->build(); + return $builder; + } + + public function buildItemStackResponse() : ItemStackResponse{ + return $this->getItemStackResponseBuilder()->build(); } } diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index c15753dad..5c467f2d4 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -33,6 +33,8 @@ use pocketmine\network\mcpe\JwtUtils; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\LoginPacket; use pocketmine\network\mcpe\protocol\types\login\AuthenticationData; +use pocketmine\network\mcpe\protocol\types\login\AuthenticationInfo; +use pocketmine\network\mcpe\protocol\types\login\AuthenticationType; use pocketmine\network\mcpe\protocol\types\login\ClientData; use pocketmine\network\mcpe\protocol\types\login\ClientDataToSkinDataHelper; use pocketmine\network\mcpe\protocol\types\login\JwtChain; @@ -42,7 +44,11 @@ use pocketmine\player\PlayerInfo; use pocketmine\player\XboxLivePlayerInfo; use pocketmine\Server; use Ramsey\Uuid\Uuid; +use function gettype; use function is_array; +use function is_object; +use function json_decode; +use const JSON_THROW_ON_ERROR; /** * Handles the initial login phase of the session. This handler is used as the initial state. @@ -60,7 +66,9 @@ class LoginPacketHandler extends PacketHandler{ ){} public function handleLogin(LoginPacket $packet) : bool{ - $extraData = $this->fetchAuthData($packet->chainDataJwt); + $authInfo = $this->parseAuthInfo($packet->authInfoJson); + $jwtChain = $this->parseJwtChain($authInfo->Certificate); + $extraData = $this->fetchAuthData($jwtChain); if(!Player::isValidUserName($extraData->displayName)){ $this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_invalidName()); @@ -139,11 +147,61 @@ class LoginPacketHandler extends PacketHandler{ return true; } - $this->processLogin($packet, $ev->isAuthRequired()); + $this->processLogin($authInfo->Token, AuthenticationType::from($authInfo->AuthenticationType), $jwtChain->chain, $packet->clientDataJwt, $ev->isAuthRequired()); return true; } + /** + * @throws PacketHandlingException + */ + protected function parseAuthInfo(string $authInfo) : AuthenticationInfo{ + try{ + $authInfoJson = json_decode($authInfo, associative: false, flags: JSON_THROW_ON_ERROR); + }catch(\JsonException $e){ + throw PacketHandlingException::wrap($e); + } + if(!is_object($authInfoJson)){ + throw new \RuntimeException("Unexpected type for auth info data: " . gettype($authInfoJson) . ", expected object"); + } + + $mapper = new \JsonMapper(); + $mapper->bExceptionOnMissingData = true; + $mapper->bExceptionOnUndefinedProperty = true; + $mapper->bStrictObjectTypeChecking = true; + try{ + $clientData = $mapper->map($authInfoJson, new AuthenticationInfo()); + }catch(\JsonMapper_Exception $e){ + throw PacketHandlingException::wrap($e); + } + return $clientData; + } + + /** + * @throws PacketHandlingException + */ + protected function parseJwtChain(string $chainDataJwt) : JwtChain{ + try{ + $jwtChainJson = json_decode($chainDataJwt, associative: false, flags: JSON_THROW_ON_ERROR); + }catch(\JsonException $e){ + throw PacketHandlingException::wrap($e); + } + if(!is_object($jwtChainJson)){ + throw new \RuntimeException("Unexpected type for JWT chain data: " . gettype($jwtChainJson) . ", expected object"); + } + + $mapper = new \JsonMapper(); + $mapper->bExceptionOnMissingData = true; + $mapper->bExceptionOnUndefinedProperty = true; + $mapper->bStrictObjectTypeChecking = true; + try{ + $clientData = $mapper->map($jwtChainJson, new JwtChain()); + }catch(\JsonMapper_Exception $e){ + throw PacketHandlingException::wrap($e); + } + return $clientData; + } + /** * @throws PacketHandlingException */ @@ -211,10 +269,15 @@ class LoginPacketHandler extends PacketHandler{ * TODO: This is separated for the purposes of allowing plugins (like Specter) to hack it and bypass authentication. * In the future this won't be necessary. * + * @param null|string[] $legacyCertificate + * * @throws \InvalidArgumentException */ - protected function processLogin(LoginPacket $packet, bool $authRequired) : void{ - $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($packet->chainDataJwt->chain, $packet->clientDataJwt, $authRequired, $this->authCallback)); + protected function processLogin(string $token, AuthenticationType $authType, ?array $legacyCertificate, string $clientData, bool $authRequired) : void{ + if($legacyCertificate === null){ + throw new PacketHandlingException("Legacy certificate cannot be null"); + } + $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($legacyCertificate, $clientData, $authRequired, $this->authCallback)); $this->session->setHandler(null); //drop packets received during login verification } } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 9aa302c0c..99f65e78f 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -40,7 +40,6 @@ use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\LevelSettings; use pocketmine\network\mcpe\protocol\types\NetworkPermissions; use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings; -use pocketmine\network\mcpe\protocol\types\ServerAuthMovementMode; use pocketmine\network\mcpe\protocol\types\SpawnSettings; use pocketmine\player\Player; use pocketmine\Server; @@ -82,7 +81,8 @@ class PreSpawnPacketHandler extends PacketHandler{ $levelSettings->lightningLevel = 0; $levelSettings->commandsEnabled = true; $levelSettings->gameRules = [ - "naturalregeneration" => new BoolGameRule(false, false) //Hack for client side regeneration + "naturalregeneration" => new BoolGameRule(false, false), //Hack for client side regeneration + "locatorbar" => new BoolGameRule(false, false) //Disable client-side tracking of nearby players ]; $levelSettings->experiments = new Experiments([], false); @@ -99,7 +99,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->server->getMotd(), "", false, - new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false), + new PlayerMovementSettings(0, true), 0, 0, "", diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index a1df394da..a9ffae6f7 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -120,7 +120,8 @@ class ResourcePacksPacketHandler extends PacketHandler{ hasAddons: false, hasScripts: false, worldTemplateId: Uuid::fromString(Uuid::NIL), - worldTemplateVersion: "" + worldTemplateVersion: "", + forceDisableVibrantVisuals: true, )); $this->session->getLogger()->debug("Waiting for client to accept resource packs"); } diff --git a/src/player/Player.php b/src/player/Player.php index ff8a67c94..762187b24 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -27,6 +27,7 @@ use DateTimeImmutable; use pocketmine\block\BaseSign; use pocketmine\block\Bed; use pocketmine\block\BlockTypeTags; +use pocketmine\block\RespawnAnchor; use pocketmine\block\UnknownBlock; use pocketmine\block\VanillaBlocks; use pocketmine\command\CommandSender; @@ -46,6 +47,7 @@ use pocketmine\entity\projectile\Arrow; use pocketmine\entity\Skin; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\event\entity\EntityExhaustEvent; use pocketmine\event\entity\EntityExtinguishEvent; use pocketmine\event\inventory\InventoryCloseEvent; use pocketmine\event\inventory\InventoryOpenEvent; @@ -60,7 +62,6 @@ use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerEmoteEvent; use pocketmine\event\player\PlayerEntityInteractEvent; use pocketmine\event\player\PlayerEntityPickEvent; -use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\event\player\PlayerGameModeChangeEvent; use pocketmine\event\player\PlayerInteractEvent; use pocketmine\event\player\PlayerItemConsumeEvent; @@ -137,6 +138,7 @@ use pocketmine\world\sound\EntityAttackNoDamageSound; use pocketmine\world\sound\EntityAttackSound; use pocketmine\world\sound\FireExtinguishSound; use pocketmine\world\sound\ItemBreakSound; +use pocketmine\world\sound\RespawnAnchorDepleteSound; use pocketmine\world\sound\Sound; use pocketmine\world\World; use pocketmine\YmlServerProperties; @@ -340,7 +342,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4) ** 2) * M_PI); $this->chunkSelector = new ChunkSelector(); - $this->chunkLoader = new class implements ChunkLoader{}; + $this->chunkLoader = new ChunkLoader(); $this->chunkTicker = new ChunkTicker(); $world = $spawnLocation->getWorld(); //load the spawn chunk so we can see the terrain @@ -1442,9 +1444,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($horizontalDistanceTravelled > 0){ //TODO: check for swimming if($this->isSprinting()){ - $this->hungerManager->exhaust(0.01 * $horizontalDistanceTravelled, PlayerExhaustEvent::CAUSE_SPRINTING); + $this->hungerManager->exhaust(0.01 * $horizontalDistanceTravelled, EntityExhaustEvent::CAUSE_SPRINTING); }else{ - $this->hungerManager->exhaust(0.0, PlayerExhaustEvent::CAUSE_WALKING); + $this->hungerManager->exhaust(0.0, EntityExhaustEvent::CAUSE_WALKING); } if($this->nextChunkOrderRun > 20){ @@ -1647,7 +1649,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $newReplica = clone $oldHeldItem; $newReplica->setCount($newHeldItem->getCount()); if($newReplica instanceof Durable && $newHeldItem instanceof Durable){ - $newReplica->setDamage($newHeldItem->getDamage()); + $newDamage = $newHeldItem->getDamage(); + if($newDamage >= 0 && $newDamage <= $newReplica->getMaxDurability()){ + $newReplica->setDamage($newDamage); + } } $damagedOrDeducted = $newReplica->equalsExact($newHeldItem); @@ -1910,7 +1915,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $returnedItems = []; if($this->getWorld()->useBreakOn($pos, $item, $this, true, $returnedItems)){ $this->returnItemsFromAction($oldItem, $item, $returnedItems); - $this->hungerManager->exhaust(0.005, PlayerExhaustEvent::CAUSE_MINING); + $this->hungerManager->exhaust(0.005, EntityExhaustEvent::CAUSE_MINING); return true; } }else{ @@ -2013,7 +2018,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $heldItem->onAttackEntity($entity, $returnedItems); $this->returnItemsFromAction($oldItem, $heldItem, $returnedItems); - $this->hungerManager->exhaust(0.1, PlayerExhaustEvent::CAUSE_ATTACK); + $this->hungerManager->exhaust(0.1, EntityExhaustEvent::CAUSE_ATTACK); } return true; @@ -2543,6 +2548,21 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } $this->logger->debug("Respawn position located, completing respawn"); $ev = new PlayerRespawnEvent($this, $safeSpawn); + $spawnPosition = $ev->getRespawnPosition(); + $spawnBlock = $spawnPosition->getWorld()->getBlock($spawnPosition); + if($spawnBlock instanceof RespawnAnchor){ + if($spawnBlock->getCharges() > 0){ + $spawnPosition->getWorld()->setBlock($spawnPosition, $spawnBlock->setCharges($spawnBlock->getCharges() - 1)); + $spawnPosition->getWorld()->addSound($spawnPosition, new RespawnAnchorDepleteSound()); + }else{ + $defaultSpawn = $this->server->getWorldManager()->getDefaultWorld()?->getSpawnLocation(); + if($defaultSpawn !== null){ + $this->setSpawn($defaultSpawn); + $ev->setRespawnPosition($defaultSpawn); + $this->sendMessage(KnownTranslationFactory::tile_respawn_anchor_notValid()->prefix(TextFormat::GRAY)); + } + } + } $ev->call(); $realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getWorld()); @@ -2584,7 +2604,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ protected function applyPostDamageEffects(EntityDamageEvent $source) : void{ parent::applyPostDamageEffects($source); - $this->hungerManager->exhaust(0.1, PlayerExhaustEvent::CAUSE_DAMAGE); + $this->hungerManager->exhaust(0.1, EntityExhaustEvent::CAUSE_DAMAGE); } public function attack(EntityDamageEvent $source) : void{ diff --git a/src/plugin/FolderPluginLoader.php b/src/plugin/FolderPluginLoader.php index 73f6b8841..b881b117b 100644 --- a/src/plugin/FolderPluginLoader.php +++ b/src/plugin/FolderPluginLoader.php @@ -41,11 +41,8 @@ class FolderPluginLoader implements PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void{ - $description = $this->getPluginDescription($path); - if($description !== null){ - $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); - } + public function loadPlugin(string $path, PluginDescription $description) : void{ + $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); } /** diff --git a/src/plugin/PharPluginLoader.php b/src/plugin/PharPluginLoader.php index 1ef8f2b84..865ab2edc 100644 --- a/src/plugin/PharPluginLoader.php +++ b/src/plugin/PharPluginLoader.php @@ -42,11 +42,8 @@ class PharPluginLoader implements PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void{ - $description = $this->getPluginDescription($path); - if($description !== null){ - $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); - } + public function loadPlugin(string $path, PluginDescription $description) : void{ + $this->loader->addPath($description->getSrcNamespacePrefix(), "$path/src"); } /** diff --git a/src/plugin/PluginLoader.php b/src/plugin/PluginLoader.php index d87daf9fc..b29448965 100644 --- a/src/plugin/PluginLoader.php +++ b/src/plugin/PluginLoader.php @@ -36,7 +36,7 @@ interface PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void; + public function loadPlugin(string $path, PluginDescription $description) : void; /** * Gets the PluginDescription from the file diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 3750af3ef..5e4196d7d 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -150,7 +150,7 @@ class PluginManager{ } $prefixed = $loader->getAccessProtocol() . $path; - $loader->loadPlugin($prefixed); + $loader->loadPlugin($prefixed, $description); $mainClass = $description->getMain(); if(!class_exists($mainClass, true)){ diff --git a/src/plugin/ScriptPluginLoader.php b/src/plugin/ScriptPluginLoader.php index 8e45eaab8..1d0e42d53 100644 --- a/src/plugin/ScriptPluginLoader.php +++ b/src/plugin/ScriptPluginLoader.php @@ -46,7 +46,7 @@ class ScriptPluginLoader implements PluginLoader{ /** * Loads the plugin contained in $file */ - public function loadPlugin(string $path) : void{ + public function loadPlugin(string $path, PluginDescription $description) : void{ include_once $path; } diff --git a/src/world/ChunkLoader.php b/src/world/ChunkLoader.php index 9e909e928..c81c8e3ea 100644 --- a/src/world/ChunkLoader.php +++ b/src/world/ChunkLoader.php @@ -32,6 +32,6 @@ namespace pocketmine\world; * WARNING: When moving this object around in the world or destroying it, * be sure to unregister the loader from chunks you're not using, otherwise you'll leak memory. */ -interface ChunkLoader{ +final class ChunkLoader{ } diff --git a/src/world/Explosion.php b/src/world/Explosion.php index 601f9109e..9e83d06be 100644 --- a/src/world/Explosion.php +++ b/src/world/Explosion.php @@ -26,16 +26,20 @@ namespace pocketmine\world; use pocketmine\block\Block; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\TNT; +use pocketmine\block\utils\SupportType; use pocketmine\block\VanillaBlocks; use pocketmine\entity\Entity; +use pocketmine\event\block\BlockExplodeEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityExplodeEvent; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; +use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Utils; use pocketmine\world\format\SubChunk; use pocketmine\world\particle\HugeExplodeSeedParticle; use pocketmine\world\sound\ExplodeSound; @@ -48,25 +52,36 @@ use function mt_rand; use function sqrt; class Explosion{ + public const DEFAULT_FIRE_CHANCE = 1.0 / 3.0; + private int $rays = 16; public World $world; - /** @var Block[] */ + /** + * @var Block[] + * @phpstan-var array + */ public array $affectedBlocks = []; public float $stepLen = 0.3; + /** @var Block[] */ + private array $fireIgnitions = []; private SubChunkExplorer $subChunkExplorer; public function __construct( public Position $source, public float $radius, - private Entity|Block|null $what = null + private Entity|Block|null $what = null, + private float $fireChance = 0.0 ){ if(!$this->source->isValid()){ throw new \InvalidArgumentException("Position does not have a valid world"); } $this->world = $this->source->getWorld(); - + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } if($radius <= 0){ throw new \InvalidArgumentException("Explosion radius must be greater than 0, got $radius"); } @@ -85,6 +100,7 @@ class Explosion{ $blockFactory = RuntimeBlockStateRegistry::getInstance(); $mRays = $this->rays - 1; + $incendiary = $this->fireChance > 0; for($i = 0; $i < $this->rays; ++$i){ for($j = 0; $j < $this->rays; ++$j){ for($k = 0; $k < $this->rays; ++$k){ @@ -127,7 +143,12 @@ class Explosion{ $_block = $this->world->getBlockAt($vBlockX, $vBlockY, $vBlockZ, true, false); foreach($_block->getAffectedBlocks() as $_affectedBlock){ $_affectedBlockPos = $_affectedBlock->getPosition(); - $this->affectedBlocks[World::blockHash($_affectedBlockPos->x, $_affectedBlockPos->y, $_affectedBlockPos->z)] = $_affectedBlock; + $posHash = World::blockHash($_affectedBlockPos->x, $_affectedBlockPos->y, $_affectedBlockPos->z); + $this->affectedBlocks[$posHash] = $_affectedBlock; + + if($incendiary && Utils::getRandomFloat() <= $this->fireChance){ + $this->fireIgnitions[$posHash] = $_affectedBlock; + } } } } @@ -150,13 +171,32 @@ class Explosion{ $yield = min(100, (1 / $this->radius) * 100); if($this->what instanceof Entity){ - $ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield); + $ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield, $this->fireIgnitions); + + $ev->call(); + if($ev->isCancelled()){ + return false; + } + + $yield = $ev->getYield(); + $this->affectedBlocks = $ev->getBlockList(); + $this->fireIgnitions = $ev->getIgnitions(); + }elseif($this->what instanceof Block){ + $ev = new BlockExplodeEvent( + $this->what, + $this->source, + $this->affectedBlocks, + $yield, + $this->fireIgnitions, + ); + $ev->call(); if($ev->isCancelled()){ return false; }else{ $yield = $ev->getYield(); - $this->affectedBlocks = $ev->getBlockList(); + $this->affectedBlocks = $ev->getAffectedBlocks(); + $this->fireIgnitions = $ev->getIgnitions(); } } @@ -198,8 +238,9 @@ class Explosion{ $air = VanillaItems::AIR(); $airBlock = VanillaBlocks::AIR(); + $fireBlock = VanillaBlocks::FIRE(); - foreach($this->affectedBlocks as $block){ + foreach($this->affectedBlocks as $hash => $block){ $pos = $block->getPosition(); if($block instanceof TNT){ $block->ignite(mt_rand(10, 30)); @@ -212,7 +253,13 @@ class Explosion{ if(($t = $this->world->getTileAt($pos->x, $pos->y, $pos->z)) !== null){ $t->onBlockDestroyed(); //needed to create drops for inventories } - $this->world->setBlockAt($pos->x, $pos->y, $pos->z, $airBlock); + $targetBlock = + isset($this->fireIgnitions[$hash]) && + $block->getSide(Facing::DOWN)->getSupportType(Facing::UP) === SupportType::FULL ? + $fireBlock : + $airBlock; + + $this->world->setBlockAt($pos->x, $pos->y, $pos->z, $targetBlock); } } @@ -221,4 +268,18 @@ class Explosion{ return true; } + + /** + * Sets a chance between 0 and 1 of creating a fire. + * For example, if the chance is 1/3, then that amount of affected blocks will be ignited. + * + * @param float $fireChance 0 ... 1 + */ + public function setFireChance(float $fireChance) : void{ + Utils::checkFloatNotInfOrNaN("fireChance", $fireChance); + if($fireChance < 0.0 || $fireChance > 1.0){ + throw new \InvalidArgumentException("Fire chance must be a number between 0 and 1."); + } + $this->fireChance = $fireChance; + } } diff --git a/src/world/World.php b/src/world/World.php index 8179d96c6..56324c195 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -93,9 +93,11 @@ use pocketmine\world\format\io\GlobalBlockStateHandlers; use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\format\LightArray; use pocketmine\world\format\SubChunk; +use pocketmine\world\generator\executor\AsyncGeneratorExecutor; +use pocketmine\world\generator\executor\GeneratorExecutor; +use pocketmine\world\generator\executor\GeneratorExecutorSetupParameters; +use pocketmine\world\generator\executor\SyncGeneratorExecutor; use pocketmine\world\generator\GeneratorManager; -use pocketmine\world\generator\GeneratorRegisterTask; -use pocketmine\world\generator\GeneratorUnregisterTask; use pocketmine\world\generator\PopulationTask; use pocketmine\world\light\BlockLightUpdate; use pocketmine\world\light\LightPopulationTask; @@ -287,6 +289,12 @@ class World implements ChunkManager{ */ private array $chunks = []; + /** + * @var true[] + * @phpstan-var array + */ + private array $knownUngeneratedChunks = []; + /** * @var Vector3[][] chunkHash => [relativeBlockHash => Vector3] * @phpstan-var array> @@ -336,11 +344,7 @@ class World implements ChunkManager{ */ private array $chunkPopulationRequestQueueIndex = []; - /** - * @var true[] - * @phpstan-var array - */ - private array $generatorRegisteredWorkers = []; + private readonly GeneratorExecutor $generatorExecutor; private bool $autoSave = true; @@ -360,9 +364,6 @@ class World implements ChunkManager{ private bool $doingTick = false; - /** @phpstan-var class-string */ - private string $generator; - private bool $unloaded = false; /** * @var \Closure[] @@ -498,7 +499,23 @@ class World implements ChunkManager{ $generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator()) ?? throw new AssumptionFailedError("WorldManager should already have checked that the generator exists"); $generator->validateGeneratorOptions($this->provider->getWorldData()->getGeneratorOptions()); - $this->generator = $generator->getGeneratorClass(); + + $executorSetupParameters = new GeneratorExecutorSetupParameters( + worldMinY: $this->minY, + worldMaxY: $this->maxY, + generatorSeed: $this->getSeed(), + generatorClass: $generator->getGeneratorClass(), + generatorSettings: $this->provider->getWorldData()->getGeneratorOptions() + ); + $this->generatorExecutor = $generator->isFast() ? + new SyncGeneratorExecutor($executorSetupParameters) : + new AsyncGeneratorExecutor( + $this->logger, + $this->workerPool, + $executorSetupParameters, + $this->worldId + ); + $this->chunkPopulationRequestQueue = new \SplQueue(); $this->addOnUnloadCallback(function() : void{ $this->logger->debug("Cancelling unfulfilled generation requests"); @@ -534,17 +551,6 @@ class World implements ChunkManager{ $this->initRandomTickBlocksFromConfig($cfg); $this->timings = new WorldTimings($this); - - $this->workerPool->addWorkerStartHook($workerStartHook = function(int $workerId) : void{ - if(array_key_exists($workerId, $this->generatorRegisteredWorkers)){ - $this->logger->debug("Worker $workerId with previously registered generator restarted, flagging as unregistered"); - unset($this->generatorRegisteredWorkers[$workerId]); - } - }); - $workerPool = $this->workerPool; - $this->addOnUnloadCallback(static function() use ($workerPool, $workerStartHook) : void{ - $workerPool->removeWorkerStartHook($workerStartHook); - }); } private function initRandomTickBlocksFromConfig(ServerConfigGroup $cfg) : void{ @@ -585,21 +591,6 @@ class World implements ChunkManager{ return $this->tickRateTime; } - public function registerGeneratorToWorker(int $worker) : void{ - $this->logger->debug("Registering generator on worker $worker"); - $this->workerPool->submitTaskToWorker(new GeneratorRegisterTask($this, $this->generator, $this->provider->getWorldData()->getGeneratorOptions()), $worker); - $this->generatorRegisteredWorkers[$worker] = true; - } - - public function unregisterGenerator() : void{ - foreach($this->workerPool->getRunningWorkers() as $i){ - if(isset($this->generatorRegisteredWorkers[$i])){ - $this->workerPool->submitTaskToWorker(new GeneratorUnregisterTask($this), $i); - } - } - $this->generatorRegisteredWorkers = []; - } - public function getServer() : Server{ return $this->server; } @@ -640,6 +631,7 @@ class World implements ChunkManager{ self::getXZ($chunkHash, $chunkX, $chunkZ); $this->unloadChunk($chunkX, $chunkZ, false); } + $this->knownUngeneratedChunks = []; foreach($this->entitiesByChunk as $chunkHash => $entities){ self::getXZ($chunkHash, $chunkX, $chunkZ); @@ -657,7 +649,7 @@ class World implements ChunkManager{ $this->save(); - $this->unregisterGenerator(); + $this->generatorExecutor->shutdown(); $this->provider->close(); $this->blockCache = []; @@ -2621,6 +2613,16 @@ class World implements ChunkManager{ } public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk) : void{ + foreach($chunk->getSubChunks() as $subChunk){ + foreach($subChunk->getBlockLayers() as $blockLayer){ + foreach($blockLayer->getPalette() as $blockStateId){ + if(!$this->blockStateRegistry->hasStateId($blockStateId)){ + throw new \InvalidArgumentException("Provided chunk contains unknown/unregistered blocks (found unknown state ID $blockStateId)"); + } + } + } + } + $chunkHash = World::chunkHash($chunkX, $chunkZ); $oldChunk = $this->loadChunk($chunkX, $chunkZ); if($oldChunk !== null && $oldChunk !== $chunk){ @@ -2653,6 +2655,7 @@ class World implements ChunkManager{ } $this->chunks[$chunkHash] = $chunk; + unset($this->knownUngeneratedChunks[$chunkHash]); $this->blockCacheSize -= count($this->blockCache[$chunkHash] ?? []); unset($this->blockCache[$chunkHash]); @@ -2917,6 +2920,9 @@ class World implements ChunkManager{ if(isset($this->chunks[$chunkHash = World::chunkHash($x, $z)])){ return $this->chunks[$chunkHash]; } + if(isset($this->knownUngeneratedChunks[$chunkHash])){ + return null; + } $this->timings->syncChunkLoad->startTiming(); @@ -2936,6 +2942,7 @@ class World implements ChunkManager{ if($loadedChunkData === null){ $this->timings->syncChunkLoad->stopTiming(); + $this->knownUngeneratedChunks[$chunkHash] = true; return null; } @@ -2978,6 +2985,8 @@ class World implements ChunkManager{ if(count($chunkData->getEntityNBT()) !== 0){ $this->timings->syncChunkLoadEntities->startTiming(); $entityFactory = EntityFactory::getInstance(); + + $deletedEntities = []; foreach($chunkData->getEntityNBT() as $k => $nbt){ try{ $entity = $entityFactory->createFromData($this, $nbt); @@ -2994,18 +3003,23 @@ class World implements ChunkManager{ }elseif($saveIdTag instanceof IntTag){ //legacy MCPE format $saveId = "legacy(" . $saveIdTag->getValue() . ")"; } - $logger->warning("Deleted unknown entity type $saveId"); + $deletedEntities[$saveId] = ($deletedEntities[$saveId] ?? 0) + 1; } //TODO: we can't prevent entities getting added to unloaded chunks if they were saved in the wrong place //here, because entities currently add themselves to the world } + foreach(Utils::promoteKeys($deletedEntities) as $saveId => $count){ + $logger->warning("Deleted unknown entity type $saveId x$count"); + } $this->timings->syncChunkLoadEntities->stopTiming(); } if(count($chunkData->getTileNBT()) !== 0){ $this->timings->syncChunkLoadTileEntities->startTiming(); $tileFactory = TileFactory::getInstance(); + + $deletedTiles = []; foreach($chunkData->getTileNBT() as $k => $nbt){ try{ $tile = $tileFactory->createFromData($this, $nbt); @@ -3015,7 +3029,8 @@ class World implements ChunkManager{ continue; } if($tile === null){ - $logger->warning("Deleted unknown tile entity type " . $nbt->getString("id", "")); + $saveId = $nbt->getString("id", ""); + $deletedTiles[$saveId] = ($deletedTiles[$saveId] ?? 0) + 1; continue; } @@ -3031,6 +3046,10 @@ class World implements ChunkManager{ } } + foreach(Utils::promoteKeys($deletedTiles) as $saveId => $count){ + $logger->warning("Deleted unknown tile entity type $saveId x$count"); + } + $this->timings->syncChunkLoadTileEntities->stopTiming(); } } @@ -3324,7 +3343,7 @@ class World implements ChunkManager{ /** @phpstan-var PromiseResolver $resolver */ $resolver = $this->chunkPopulationRequestMap[$chunkHash] = new PromiseResolver(); if($associatedChunkLoader === null){ - $temporaryLoader = new class implements ChunkLoader{}; + $temporaryLoader = new ChunkLoader(); $this->registerChunkLoader($temporaryLoader, $chunkX, $chunkZ); $resolver->getPromise()->onCompletion( fn() => $this->unregisterChunkLoader($temporaryLoader, $chunkX, $chunkZ), @@ -3371,7 +3390,7 @@ class World implements ChunkManager{ return [$resolver, false]; } - $temporaryChunkLoader = new class implements ChunkLoader{}; + $temporaryChunkLoader = new ChunkLoader(); $this->registerChunkLoader($temporaryChunkLoader, $chunkX, $chunkZ); $chunk = $this->loadChunk($chunkX, $chunkZ); $this->unregisterChunkLoader($temporaryChunkLoader, $chunkX, $chunkZ); @@ -3456,8 +3475,7 @@ class World implements ChunkManager{ $chunkPopulationLockId = new ChunkLockId(); - $temporaryChunkLoader = new class implements ChunkLoader{ - }; + $temporaryChunkLoader = new ChunkLoader(); for($xx = -1; $xx <= 1; ++$xx){ for($zz = -1; $zz <= 1; ++$zz){ $this->lockChunk($chunkX + $xx, $chunkZ + $zz, $chunkPopulationLockId); @@ -3467,8 +3485,8 @@ class World implements ChunkManager{ $centerChunk = $this->loadChunk($chunkX, $chunkZ); $adjacentChunks = $this->getAdjacentChunks($chunkX, $chunkZ); - $task = new PopulationTask( - $this->worldId, + + $this->generatorExecutor->populate( $chunkX, $chunkZ, $centerChunk, @@ -3481,15 +3499,6 @@ class World implements ChunkManager{ $this->generateChunkCallback($chunkPopulationLockId, $chunkX, $chunkZ, $centerChunk, $adjacentChunks, $temporaryChunkLoader); } ); - $workerId = $this->workerPool->selectWorker(); - if(!isset($this->workerPool->getRunningWorkers()[$workerId]) && isset($this->generatorRegisteredWorkers[$workerId])){ - $this->logger->debug("Selected worker $workerId previously had generator registered, but is now offline"); - unset($this->generatorRegisteredWorkers[$workerId]); - } - if(!isset($this->generatorRegisteredWorkers[$workerId])){ - $this->registerGeneratorToWorker($workerId); - } - $this->workerPool->submitTaskToWorker($task, $workerId); return $resolver->getPromise(); }finally{ diff --git a/src/world/format/SubChunk.php b/src/world/format/SubChunk.php index d8546e7e9..cc6673430 100644 --- a/src/world/format/SubChunk.php +++ b/src/world/format/SubChunk.php @@ -134,11 +134,8 @@ class SubChunk{ foreach($this->blockLayers as $layer){ $layer->collectGarbage(); - foreach($layer->getPalette() as $p){ - if($p !== $this->emptyBlockId){ - $cleanedLayers[] = $layer; - continue 2; - } + if($layer->getBitsPerBlock() !== 0 || $layer->get(0, 0, 0) !== $this->emptyBlockId){ + $cleanedLayers[] = $layer; } } $this->blockLayers = $cleanedLayers; diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index 6223e66b8..3a64f93f6 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -36,6 +36,7 @@ use pocketmine\nbt\TreeRoot; use pocketmine\utils\Binary; use pocketmine\utils\BinaryDataException; use pocketmine\utils\BinaryStream; +use pocketmine\utils\Utils; use pocketmine\VersionInfo; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\BaseWorldProvider; @@ -204,23 +205,29 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ $blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt); }catch(BlockStateDeserializeException $e){ //while not ideal, this is not a fatal error - $blockDecodeErrors[] = "Palette offset $i / Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $errorMessage = "Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $blockDecodeErrors[$errorMessage][] = $i; $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); continue; } try{ $palette[] = $this->blockStateDeserializer->deserialize($blockStateData); }catch(UnsupportedBlockStateException $e){ - $blockDecodeErrors[] = "Palette offset $i / " . $e->getMessage(); + $blockDecodeErrors[$e->getMessage()][] = $i; $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); }catch(BlockStateDeserializeException $e){ - $blockDecodeErrors[] = "Palette offset $i / Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $errorMessage = "Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); + $blockDecodeErrors[$errorMessage][] = $i; $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); } } if(count($blockDecodeErrors) > 0){ - $logger->error("Errors decoding blocks:\n - " . implode("\n - ", $blockDecodeErrors)); + $finalErrors = []; + foreach(Utils::promoteKeys($blockDecodeErrors) as $errorMessage => $paletteOffsets){ + $finalErrors[] = "$errorMessage (palette offsets: " . implode(", ", $paletteOffsets) . ")"; + } + $logger->error("Errors decoding blocks:\n - " . implode("\n - ", $finalErrors)); } //TODO: exceptions diff --git a/src/world/generator/GeneratorManager.php b/src/world/generator/GeneratorManager.php index 291ea91de..a1b00480e 100644 --- a/src/world/generator/GeneratorManager.php +++ b/src/world/generator/GeneratorManager.php @@ -50,7 +50,7 @@ final class GeneratorManager{ }catch(InvalidGeneratorOptionsException $e){ return $e; } - }); + }, fast: true); $this->addGenerator(Normal::class, "normal", fn() => null); $this->addAlias("normal", "default"); $this->addGenerator(Nether::class, "nether", fn() => null); @@ -62,6 +62,7 @@ final class GeneratorManager{ * @param string $name Alias for this generator type that can be written in configs * @param \Closure $presetValidator Callback to validate generator options for new worlds * @param bool $overwrite Whether to force overwriting any existing registered generator with the same name + * @param bool $fast Whether this generator is fast enough to run without async tasks * * @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator * @@ -69,7 +70,7 @@ final class GeneratorManager{ * * @throws \InvalidArgumentException */ - public function addGenerator(string $class, string $name, \Closure $presetValidator, bool $overwrite = false) : void{ + public function addGenerator(string $class, string $name, \Closure $presetValidator, bool $overwrite = false, bool $fast = false) : void{ Utils::testValidInstance($class, Generator::class); $name = strtolower($name); @@ -77,7 +78,7 @@ final class GeneratorManager{ throw new \InvalidArgumentException("Alias \"$name\" is already assigned"); } - $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator); + $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator, $fast); } /** diff --git a/src/world/generator/GeneratorManagerEntry.php b/src/world/generator/GeneratorManagerEntry.php index 256ed27d5..942f6ee79 100644 --- a/src/world/generator/GeneratorManagerEntry.php +++ b/src/world/generator/GeneratorManagerEntry.php @@ -31,12 +31,15 @@ final class GeneratorManagerEntry{ */ public function __construct( private string $generatorClass, - private \Closure $presetValidator + private \Closure $presetValidator, + private readonly bool $fast ){} /** @phpstan-return class-string */ public function getGeneratorClass() : string{ return $this->generatorClass; } + public function isFast() : bool{ return $this->fast; } + /** * @throws InvalidGeneratorOptionsException */ diff --git a/src/world/generator/PopulationTask.php b/src/world/generator/PopulationTask.php index a8366a306..971349a5b 100644 --- a/src/world/generator/PopulationTask.php +++ b/src/world/generator/PopulationTask.php @@ -27,11 +27,18 @@ use pocketmine\scheduler\AsyncTask; use pocketmine\utils\AssumptionFailedError; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; +use pocketmine\world\generator\executor\ThreadLocalGeneratorContext; use function array_map; use function igbinary_serialize; use function igbinary_unserialize; /** + * @internal + * + * TODO: this should be moved to the executor namespace, but plugins have unfortunately used it directly due to the + * difficulty of regenerating chunks. This should be addressed in the future. + * For the remainder of PM5, we can't relocate this class. + * * @phpstan-type OnCompletion \Closure(Chunk $centerChunk, array $adjacentChunks) : void */ class PopulationTask extends AsyncTask{ diff --git a/src/world/generator/executor/AsyncGeneratorExecutor.php b/src/world/generator/executor/AsyncGeneratorExecutor.php new file mode 100644 index 000000000..d19b6e661 --- /dev/null +++ b/src/world/generator/executor/AsyncGeneratorExecutor.php @@ -0,0 +1,106 @@ + + */ + private array $generatorRegisteredWorkers = []; + + public function __construct( + \Logger $logger, + private readonly AsyncPool $workerPool, + private readonly GeneratorExecutorSetupParameters $setupParameters, + int $asyncContextId = null + ){ + $this->logger = new \PrefixedLogger($logger, "AsyncGeneratorExecutor"); + + //TODO: we only allow setting this for PM5 because of PopulationTask uses in plugins + $this->asyncContextId = $asyncContextId ?? self::$nextAsyncContextId++; + + $this->workerStartHook = function(int $workerId) : void{ + if(array_key_exists($workerId, $this->generatorRegisteredWorkers)){ + $this->logger->debug("Worker $workerId with previously registered generator restarted, flagging as unregistered"); + unset($this->generatorRegisteredWorkers[$workerId]); + } + }; + $this->workerPool->addWorkerStartHook($this->workerStartHook); + } + + private function registerGeneratorToWorker(int $worker) : void{ + $this->logger->debug("Registering generator on worker $worker"); + $this->workerPool->submitTaskToWorker(new AsyncGeneratorRegisterTask($this->setupParameters, $this->asyncContextId), $worker); + $this->generatorRegisteredWorkers[$worker] = true; + } + + private function unregisterGenerator() : void{ + foreach($this->workerPool->getRunningWorkers() as $i){ + if(isset($this->generatorRegisteredWorkers[$i])){ + $this->workerPool->submitTaskToWorker(new AsyncGeneratorUnregisterTask($this->asyncContextId), $i); + } + } + $this->generatorRegisteredWorkers = []; + } + + public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void{ + $task = new PopulationTask( + $this->asyncContextId, + $chunkX, + $chunkZ, + $centerChunk, + $adjacentChunks, + $onCompletion + ); + $workerId = $this->workerPool->selectWorker(); + if(!isset($this->workerPool->getRunningWorkers()[$workerId]) && isset($this->generatorRegisteredWorkers[$workerId])){ + $this->logger->debug("Selected worker $workerId previously had generator registered, but is now offline"); + unset($this->generatorRegisteredWorkers[$workerId]); + } + if(!isset($this->generatorRegisteredWorkers[$workerId])){ + $this->registerGeneratorToWorker($workerId); + } + $this->workerPool->submitTaskToWorker($task, $workerId); + } + + public function shutdown() : void{ + $this->unregisterGenerator(); + $this->workerPool->removeWorkerStartHook($this->workerStartHook); + } +} diff --git a/src/world/generator/GeneratorRegisterTask.php b/src/world/generator/executor/AsyncGeneratorRegisterTask.php similarity index 54% rename from src/world/generator/GeneratorRegisterTask.php rename to src/world/generator/executor/AsyncGeneratorRegisterTask.php index e2e773a35..5bc67834d 100644 --- a/src/world/generator/GeneratorRegisterTask.php +++ b/src/world/generator/executor/AsyncGeneratorRegisterTask.php @@ -21,37 +21,20 @@ declare(strict_types=1); -namespace pocketmine\world\generator; +namespace pocketmine\world\generator\executor; use pocketmine\scheduler\AsyncTask; -use pocketmine\world\World; -class GeneratorRegisterTask extends AsyncTask{ - public int $seed; - public int $worldId; - public int $worldMinY; - public int $worldMaxY; +class AsyncGeneratorRegisterTask extends AsyncTask{ - /** - * @phpstan-param class-string $generatorClass - */ public function __construct( - World $world, - public string $generatorClass, - public string $generatorSettings - ){ - $this->seed = $world->getSeed(); - $this->worldId = $world->getId(); - $this->worldMinY = $world->getMinY(); - $this->worldMaxY = $world->getMaxY(); - } + private readonly GeneratorExecutorSetupParameters $setupParameters, + private readonly int $contextId + ){} public function onRun() : void{ - /** - * @var Generator $generator - * @see Generator::__construct() - */ - $generator = new $this->generatorClass($this->seed, $this->generatorSettings); - ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldMinY, $this->worldMaxY), $this->worldId); + $setupParameters = $this->setupParameters; + $generator = $setupParameters->createGenerator(); + ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $setupParameters->worldMinY, $setupParameters->worldMaxY), $this->contextId); } } diff --git a/src/world/generator/GeneratorUnregisterTask.php b/src/world/generator/executor/AsyncGeneratorUnregisterTask.php similarity index 74% rename from src/world/generator/GeneratorUnregisterTask.php rename to src/world/generator/executor/AsyncGeneratorUnregisterTask.php index 41b4cd808..c771903f5 100644 --- a/src/world/generator/GeneratorUnregisterTask.php +++ b/src/world/generator/executor/AsyncGeneratorUnregisterTask.php @@ -21,19 +21,16 @@ declare(strict_types=1); -namespace pocketmine\world\generator; +namespace pocketmine\world\generator\executor; use pocketmine\scheduler\AsyncTask; -use pocketmine\world\World; -class GeneratorUnregisterTask extends AsyncTask{ - public int $worldId; - - public function __construct(World $world){ - $this->worldId = $world->getId(); - } +class AsyncGeneratorUnregisterTask extends AsyncTask{ + public function __construct( + private readonly int $contextId + ){} public function onRun() : void{ - ThreadLocalGeneratorContext::unregister($this->worldId); + ThreadLocalGeneratorContext::unregister($this->contextId); } } diff --git a/src/world/generator/executor/GeneratorExecutor.php b/src/world/generator/executor/GeneratorExecutor.php new file mode 100644 index 000000000..d3f62d410 --- /dev/null +++ b/src/world/generator/executor/GeneratorExecutor.php @@ -0,0 +1,38 @@ + $adjacentChunks + * @phpstan-param \Closure(Chunk $centerChunk, array $adjacentChunks) : void $onCompletion + */ + public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void; + + public function shutdown() : void; + +} diff --git a/src/world/generator/executor/GeneratorExecutorSetupParameters.php b/src/world/generator/executor/GeneratorExecutorSetupParameters.php new file mode 100644 index 000000000..b5fdb7bf9 --- /dev/null +++ b/src/world/generator/executor/GeneratorExecutorSetupParameters.php @@ -0,0 +1,50 @@ + $generatorClass + */ + public function __construct( + public readonly int $worldMinY, + public readonly int $worldMaxY, + public readonly int $generatorSeed, + public readonly string $generatorClass, + public readonly string $generatorSettings, + ){} + + public function createGenerator() : Generator{ + /** + * @var Generator $generator + * @see Generator::__construct() + */ + $generator = new $this->generatorClass($this->generatorSeed, $this->generatorSettings); + return $generator; + } +} diff --git a/src/world/generator/executor/SyncGeneratorExecutor.php b/src/world/generator/executor/SyncGeneratorExecutor.php new file mode 100644 index 000000000..79b5fdd00 --- /dev/null +++ b/src/world/generator/executor/SyncGeneratorExecutor.php @@ -0,0 +1,61 @@ +generator = $setupParameters->createGenerator(); + $this->worldMinY = $setupParameters->worldMinY; + $this->worldMaxY = $setupParameters->worldMaxY; + } + + public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void{ + [$centerChunk, $adjacentChunks] = PopulationUtils::populateChunkWithAdjacents( + $this->worldMinY, + $this->worldMaxY, + $this->generator, + $chunkX, + $chunkZ, + $centerChunk, + $adjacentChunks + ); + + $onCompletion($centerChunk, $adjacentChunks); + } + + public function shutdown() : void{ + //NOOP + } +} diff --git a/src/world/generator/ThreadLocalGeneratorContext.php b/src/world/generator/executor/ThreadLocalGeneratorContext.php similarity index 94% rename from src/world/generator/ThreadLocalGeneratorContext.php rename to src/world/generator/executor/ThreadLocalGeneratorContext.php index bcf99882b..bea8bb032 100644 --- a/src/world/generator/ThreadLocalGeneratorContext.php +++ b/src/world/generator/executor/ThreadLocalGeneratorContext.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\world\generator; +namespace pocketmine\world\generator\executor; + +use pocketmine\world\generator\Generator; /** * Manages thread-local caches for generators and the things needed to support them diff --git a/src/world/sound/RespawnAnchorChargeSound.php b/src/world/sound/RespawnAnchorChargeSound.php new file mode 100644 index 000000000..5a5731262 --- /dev/null +++ b/src/world/sound/RespawnAnchorChargeSound.php @@ -0,0 +1,35 @@ +blockFactory->getAllKnownStates() as $block){ + $vanillaId = $serializer->serializeBlock($block)->getName(); + if(isset($exceptions[$vanillaId]) || isset($testedBlocks[$vanillaId])){ + continue; + } + if(!isset($propertiesTable[$vanillaId]) || !is_array($propertiesTable[$vanillaId])){ + throw new AssumptionFailedError("$vanillaId does not exist in the vanilla block properties table or is not an array"); + } + if(!isset($propertiesTable[$vanillaId]["hardness"]) || !is_float($propertiesTable[$vanillaId]["hardness"])){ + throw new AssumptionFailedError("Hardness property is missing for $vanillaId or is not a float value"); + } + if(!isset($propertiesTable[$vanillaId]["blastResistance"]) || !is_float($propertiesTable[$vanillaId]["blastResistance"])){ + throw new AssumptionFailedError("Blast resistance property is missing for $vanillaId or is not a float value"); + } + $testedBlocks[$vanillaId] = true; + + $vanillaHardness = round($propertiesTable[$vanillaId]["hardness"], 5); + $vanillaBlastResistance = round($propertiesTable[$vanillaId]["blastResistance"], 5) * 5; + + $breakInfo = $block->getBreakInfo(); + if($breakInfo->getHardness() !== $vanillaHardness){ + $hardnessErrors[] = "Hardness mismatch for $vanillaId (expected: $vanillaHardness, got " . $breakInfo->getHardness() . ")"; + } + if($breakInfo->getBlastResistance() !== $vanillaBlastResistance){ + $blastResistanceErrors[] = "Blast resistance mismatch for $vanillaId (expected: $vanillaBlastResistance, got " . $breakInfo->getBlastResistance() . ")"; + } + } + self::assertEmpty($hardnessErrors, "Block hardness test failed:\n" . implode("\n", $hardnessErrors)); + self::assertEmpty($blastResistanceErrors, "Block blast resistance test failed:\n" . implode("\n", $blastResistanceErrors)); + } + /** * @return int[][]|string[][] * @phpstan-return array{array, array} diff --git a/tests/phpunit/block/BrewingStandTest.php b/tests/phpunit/block/BrewingStandTest.php index 85cdd90e1..d36326344 100644 --- a/tests/phpunit/block/BrewingStandTest.php +++ b/tests/phpunit/block/BrewingStandTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\block\utils\BrewingStandSlot; use function count; @@ -39,11 +40,10 @@ class BrewingStandTest extends TestCase{ } /** - * @dataProvider slotsProvider - * * @param BrewingStandSlot[] $slots * @phpstan-param list $slots */ + #[DataProvider("slotsProvider")] public function testHasAndSetSlot(array $slots) : void{ $block = VanillaBlocks::BREWING_STAND(); foreach($slots as $slot){ @@ -62,11 +62,10 @@ class BrewingStandTest extends TestCase{ } /** - * @dataProvider slotsProvider - * * @param BrewingStandSlot[] $slots * @phpstan-param list $slots */ + #[DataProvider("slotsProvider")] public function testGetSlots(array $slots) : void{ $block = VanillaBlocks::BREWING_STAND(); @@ -83,11 +82,10 @@ class BrewingStandTest extends TestCase{ } /** - * @dataProvider slotsProvider - * * @param BrewingStandSlot[] $slots * @phpstan-param list $slots */ + #[DataProvider("slotsProvider")] public function testSetSlots(array $slots) : void{ $block = VanillaBlocks::BREWING_STAND(); diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 0b9150988..c7629656d 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -615,6 +615,7 @@ "RESIN_BRICK_STAIRS": 8, "RESIN_BRICK_WALL": 162, "RESIN_CLUMP": 64, + "RESPAWN_ANCHOR": 5, "ROSE_BUSH": 2, "SAND": 1, "SANDSTONE": 1, diff --git a/tests/phpunit/command/utils/CommandStringHelperTest.php b/tests/phpunit/command/utils/CommandStringHelperTest.php index 047dc7ceb..01a869ad7 100644 --- a/tests/phpunit/command/utils/CommandStringHelperTest.php +++ b/tests/phpunit/command/utils/CommandStringHelperTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\command\utils; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class CommandStringHelperTest extends TestCase{ @@ -47,9 +48,9 @@ class CommandStringHelperTest extends TestCase{ } /** - * @dataProvider parseQuoteAwareProvider * @param string[] $expected */ + #[DataProvider("parseQuoteAwareProvider")] public function testParseQuoteAware(string $commandLine, array $expected) : void{ $actual = CommandStringHelper::parseQuoteAware($commandLine); diff --git a/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php index 31ae2e27a..5b9209861 100644 --- a/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php +++ b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\console; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function mt_rand; use function str_repeat; @@ -40,9 +41,7 @@ final class ConsoleReaderChildProcessUtilsTest extends TestCase{ yield ["give \"Steve\" golden_apple"]; } - /** - * @dataProvider commandStringProvider - */ + #[DataProvider("commandStringProvider")] public function testCreateParseSymmetry(string $input) : void{ $counterCreate = $counterParse = mt_rand(); $message = ConsoleReaderChildProcessUtils::createMessage($input, $counterCreate); @@ -74,9 +73,7 @@ final class ConsoleReaderChildProcessUtilsTest extends TestCase{ yield ["a" . ConsoleReaderChildProcessUtils::TOKEN_DELIMITER . "b", false]; //message with delimiter but not a valid IPC message } - /** - * @dataProvider parseMessageProvider - */ + #[DataProvider("parseMessageProvider")] public static function testParseMessage(string $message, bool $valid) : void{ $counter = $oldCounter = 0; diff --git a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php index 91afd8ed9..9f4416a08 100644 --- a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php +++ b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\block\Block; use pocketmine\data\bedrock\block\BlockStateData; @@ -126,9 +127,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider removePropertyProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("removePropertyProvider")] public function testRemoveProperty(\Closure $getStateData) : void{ $this->prepareRemovePropertySchema($this->getNewSchema()); @@ -151,9 +152,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider renamePropertyProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("renamePropertyProvider")] public function testRenameProperty(\Closure $getStateData, ?int $valueAfter) : void{ $this->prepareRenamePropertySchema($this->getNewSchema()); @@ -187,9 +188,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider remapPropertyValueProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("remapPropertyValueProvider")] public function testRemapPropertyValue(\Closure $getStateData, ?int $valueAfter) : void{ $this->prepareRemapPropertyValueSchema($this->getNewSchema()); @@ -199,9 +200,9 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @dataProvider remapPropertyValueProvider * @phpstan-param \Closure() : BlockStateData $getStateData */ + #[DataProvider("remapPropertyValueProvider")] public function testRemapAndRenameProperty(\Closure $getStateData, ?int $valueAfter) : void{ $schema = $this->getNewSchema(); $this->prepareRenamePropertySchema($schema); @@ -239,9 +240,7 @@ class BlockStateUpgraderTest extends TestCase{ yield [0x1_00_00_00, 0x1_00_01_00, false, 1]; //Block newer than schema: block must NOT be altered } - /** - * @dataProvider upgraderVersionCompatibilityProvider - */ + #[DataProvider("upgraderVersionCompatibilityProvider")] public function testUpgraderVersionCompatibility(int $schemaVersion, int $stateVersion, bool $shouldChange, int $schemaCount) : void{ for($i = 0; $i < $schemaCount; $i++){ $schema = $this->getNewSchemaVersion($schemaVersion, $i); diff --git a/tests/phpunit/event/HandlerListManagerTest.php b/tests/phpunit/event/HandlerListManagerTest.php index c61043dab..9a3c8e505 100644 --- a/tests/phpunit/event/HandlerListManagerTest.php +++ b/tests/phpunit/event/HandlerListManagerTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\event; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\event\fixtures\TestAbstractAllowHandleEvent; use pocketmine\event\fixtures\TestAbstractEvent; @@ -63,10 +64,9 @@ class HandlerListManagerTest extends TestCase{ } /** - * @dataProvider isValidClassProvider - * * @phpstan-param \ReflectionClass $class */ + #[DataProvider("isValidClassProvider")] public function testIsValidClass(\ReflectionClass $class, bool $isValid, string $reason) : void{ self::assertSame($isValid, ($this->isValidFunc)($class), $reason); } @@ -83,11 +83,10 @@ class HandlerListManagerTest extends TestCase{ } /** - * @dataProvider resolveParentClassProvider - * * @phpstan-param \ReflectionClass $class * @phpstan-param \ReflectionClass|null $expect */ + #[DataProvider("resolveParentClassProvider")] public function testResolveParentClass(\ReflectionClass $class, ?\ReflectionClass $expect) : void{ if($expect === null){ self::assertNull(($this->resolveParentFunc)($class)); diff --git a/tests/phpunit/item/LegacyStringToItemParserTest.php b/tests/phpunit/item/LegacyStringToItemParserTest.php index f9243df7e..78f9e16b3 100644 --- a/tests/phpunit/item/LegacyStringToItemParserTest.php +++ b/tests/phpunit/item/LegacyStringToItemParserTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\item; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\block\VanillaBlocks; @@ -43,9 +44,7 @@ class LegacyStringToItemParserTest extends TestCase{ ]; } - /** - * @dataProvider itemFromStringProvider - */ + #[DataProvider("itemFromStringProvider")] public function testFromStringSingle(string $string, Item $expected) : void{ $item = LegacyStringToItemParser::getInstance()->parse($string); diff --git a/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php b/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php index ef8e297c4..864acbfeb 100644 --- a/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php +++ b/tests/phpunit/network/mcpe/convert/BlockTranslatorTest.php @@ -23,14 +23,13 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\convert; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use pocketmine\block\RuntimeBlockStateRegistry; class BlockTranslatorTest extends TestCase{ - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testAllBlockStatesSerialize() : void{ $blockTranslator = TypeConverter::getInstance()->getBlockTranslator(); foreach(RuntimeBlockStateRegistry::getInstance()->getAllKnownStates() as $state){ diff --git a/tests/phpunit/plugin/ApiVersionTest.php b/tests/phpunit/plugin/ApiVersionTest.php index faa68200d..55afdbc73 100644 --- a/tests/phpunit/plugin/ApiVersionTest.php +++ b/tests/phpunit/plugin/ApiVersionTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\plugin; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function sort; @@ -47,9 +48,7 @@ class ApiVersionTest extends TestCase{ yield ["3.0.0-ALPHA1", "4.0.0-ALPHA1", false]; } - /** - * @dataProvider compatibleApiProvider - */ + #[DataProvider("compatibleApiProvider")] public function testCompatibleApi(string $myVersion, string $wantVersion, bool $expected) : void{ self::assertSame($expected, ApiVersion::isCompatible($myVersion, [$wantVersion]), "my version: $myVersion, their version: $wantVersion, expect " . ($expected ? "yes" : "no")); } @@ -67,11 +66,10 @@ class ApiVersionTest extends TestCase{ } /** - * @dataProvider ambiguousVersionsProvider - * * @param string[] $input * @param string[] $expectedOutput */ + #[DataProvider("ambiguousVersionsProvider")] public function testFindAmbiguousVersions(array $input, array $expectedOutput) : void{ $ambiguous = ApiVersion::checkAmbiguousVersions($input); diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 0f2e8554d..5f026c243 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\scheduler; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use pmmp\thread\ThreadSafeArray; use pocketmine\promise\PromiseResolver; @@ -84,9 +85,8 @@ class AsyncPoolTest extends TestCase{ * * Due to an unset() in the function body, other AsyncTask::__destruct() calls could be triggered during * an AsyncTask's destruction. If done in the wrong way, this could lead to a crash. - * - * @doesNotPerformAssertions This test is checking for a crash condition, not a specific output. */ + #[DoesNotPerformAssertions] public function testTaskDestructorReentrancy() : void{ $this->pool->submitTask(new class extends AsyncTask{ public function __construct(){ diff --git a/tests/phpunit/utils/CloningRegistryTraitTest.php b/tests/phpunit/utils/CloningRegistryTraitTest.php index e3b53ecb5..a343d0033 100644 --- a/tests/phpunit/utils/CloningRegistryTraitTest.php +++ b/tests/phpunit/utils/CloningRegistryTraitTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\utils; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class CloningRegistryTraitTest extends TestCase{ @@ -37,9 +38,9 @@ final class CloningRegistryTraitTest extends TestCase{ } /** - * @dataProvider cloningRegistryMembersProvider * @phpstan-param \Closure() : \stdClass $provider */ + #[DataProvider("cloningRegistryMembersProvider")] public function testEachMemberClone(\Closure $provider) : void{ self::assertNotSame($provider(), $provider(), "Cloning registry should never return the same object twice"); } diff --git a/tests/phpunit/utils/ConfigTest.php b/tests/phpunit/utils/ConfigTest.php index 55b4bc2d4..102a8dcf7 100644 --- a/tests/phpunit/utils/ConfigTest.php +++ b/tests/phpunit/utils/ConfigTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\utils; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function yaml_parse; @@ -60,10 +61,9 @@ class ConfigTest extends TestCase{ } /** - * @dataProvider fixYamlIndexesProvider - * * @param mixed[] $expected */ + #[DataProvider("fixYamlIndexesProvider")] public function testFixYamlIndexes(string $test, array $expected) : void{ $fixed = Config::fixYAMLIndexes($test); $decoded = yaml_parse($fixed); diff --git a/tests/phpunit/utils/UtilsTest.php b/tests/phpunit/utils/UtilsTest.php index f061abac2..96254ca01 100644 --- a/tests/phpunit/utils/UtilsTest.php +++ b/tests/phpunit/utils/UtilsTest.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace pocketmine\utils; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use pocketmine\utils\fixtures\TestAbstractClass; use pocketmine\utils\fixtures\TestInstantiableClass; @@ -52,9 +54,7 @@ class UtilsTest extends TestCase{ ]; } - /** - * @dataProvider parseDocCommentNewlineProvider - */ + #[DataProvider("parseDocCommentNewlineProvider")] public function testParseDocCommentNewlines(string $docComment) : void{ $tags = Utils::parseDocComment($docComment); @@ -76,9 +76,7 @@ class UtilsTest extends TestCase{ ]; } - /** - * @dataProvider parseDocCommentOneLineProvider - */ + #[DataProvider("parseDocCommentOneLineProvider")] public function testParseOneLineDocComment(string $comment) : void{ $tags = Utils::parseDocComment($comment); self::assertArrayHasKey("ignoreCancelled", $tags); @@ -120,11 +118,11 @@ class UtilsTest extends TestCase{ } /** - * @dataProvider validInstanceProvider - * @doesNotPerformAssertions * @phpstan-param class-string $className * @phpstan-param class-string $baseName */ + #[DataProvider("validInstanceProvider")] + #[DoesNotPerformAssertions] public function testValidInstanceWithValidCombinations(string $className, string $baseName) : void{ Utils::testValidInstance($className, $baseName); } @@ -146,9 +144,7 @@ class UtilsTest extends TestCase{ ]; } - /** - * @dataProvider validInstanceInvalidCombinationsProvider - */ + #[DataProvider("validInstanceInvalidCombinationsProvider")] public function testValidInstanceInvalidParameters(string $className, string $baseName) : void{ $this->expectException(\InvalidArgumentException::class); Utils::testValidInstance($className, $baseName); //@phpstan-ignore-line diff --git a/tests/phpunit/world/format/io/region/RegionLoaderTest.php b/tests/phpunit/world/format/io/region/RegionLoaderTest.php index ee96b6bc0..6d715867d 100644 --- a/tests/phpunit/world/format/io/region/RegionLoaderTest.php +++ b/tests/phpunit/world/format/io/region/RegionLoaderTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\format\io\region; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use pocketmine\world\format\ChunkException; use Symfony\Component\Filesystem\Path; @@ -82,11 +83,10 @@ class RegionLoaderTest extends TestCase{ } /** - * @dataProvider outOfBoundsCoordsProvider - * * @throws ChunkException * @throws \InvalidArgumentException */ + #[DataProvider("outOfBoundsCoordsProvider")] public function testWriteChunkOutOfBounds(int $x, int $z) : void{ $this->expectException(\InvalidArgumentException::class); $this->region->writeChunk($x, $z, str_repeat("\x00", 1000)); @@ -107,11 +107,10 @@ class RegionLoaderTest extends TestCase{ } /** - * @dataProvider outOfBoundsCoordsProvider - * * @throws \InvalidArgumentException * @throws \pocketmine\world\format\io\exception\CorruptedChunkException */ + #[DataProvider("outOfBoundsCoordsProvider")] public function testReadChunkOutOfBounds(int $x, int $z) : void{ $this->expectException(\InvalidArgumentException::class); $this->region->readChunk($x, $z); diff --git a/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php b/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php index 498e2767e..7aa67fabc 100644 --- a/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php +++ b/tests/phpunit/world/format/io/region/RegionLocationTableEntryTest.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\format\io\region; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function sprintf; @@ -40,9 +41,7 @@ class RegionLocationTableEntryTest extends TestCase{ yield [new RegionLocationTableEntry(2, 4, 0), new RegionLocationTableEntry(3, 1, 0), true]; } - /** - * @dataProvider overlapDataProvider - */ + #[DataProvider("overlapDataProvider")] public function testOverlap(RegionLocationTableEntry $entry1, RegionLocationTableEntry $entry2, bool $overlaps) : void{ $stringify = function(RegionLocationTableEntry $entry) : string{ return sprintf("entry first=%d last=%d size=%d", $entry->getFirstSector(), $entry->getLastSector(), $entry->getSectorCount());