mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-11 12:08:05 +00:00
Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
aee3656415 | |||
c58e599eb2 | |||
47f0119660 | |||
561ffd3da3 | |||
b744e09352 | |||
7b89dda420 | |||
db665fefdb | |||
6872661fd0 | |||
e06b042cd0 | |||
44ce9ca610 | |||
db894e3a4a | |||
53cbc44d70 | |||
be102dc95f | |||
eaab1a8784 | |||
169d3e0de8 | |||
ce8fecc6ec | |||
4fcb644c51 | |||
826cbea0bc | |||
fe06bfcda0 | |||
f54ed8362d | |||
8c7a4d720a | |||
6492e7f4a2 | |||
5709d727a2 | |||
234199d241 | |||
e28d20a68e | |||
81d5b9ba06 | |||
d97c8e2fd2 | |||
e77cd39316 | |||
4db38ee452 | |||
ee977c8001 | |||
5b5c73f660 | |||
f83280ece6 | |||
19556634e3 | |||
5718a1a20e | |||
7a55a6e6b6 | |||
90af8cfd69 | |||
a03013d582 | |||
053abfbb6f | |||
00a8ea267c | |||
daeba95101 | |||
a750af72db | |||
61decaa2f8 | |||
06b2e61d3c | |||
b4838f5b4e | |||
b2df405cc0 | |||
d596dc571d | |||
a1748a92ca | |||
fbcf4649eb | |||
0f620fad94 | |||
bc07778434 | |||
519784460f | |||
e9c5846a06 | |||
69f197dbec | |||
13f34a500c | |||
e5c96faa4b | |||
dd98e4aaed | |||
e525699dd4 | |||
923c922960 | |||
58ea94bab8 | |||
22b10e4cb0 | |||
7a4cf8ef68 | |||
269b3d89a2 | |||
b3766834c6 | |||
50592dc269 | |||
4103631bc1 | |||
d09af2e30d | |||
bbe66e8e09 | |||
457660235e | |||
d3b7861d1a | |||
a6b36d6c3c | |||
109673382d |
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -34,7 +34,10 @@ Requires translations:
|
||||
|
||||
## Tests
|
||||
<!--
|
||||
Details should be provided of tests done. Simply saying "tested" or equivalent is not acceptable.
|
||||
|
||||
Attach scripts or actions to test this pull request, as well as the result
|
||||
PRs which have not been tested MUST be marked as draft.
|
||||
-->
|
||||
I tested this PR by doing the following (tick all that apply):
|
||||
- [ ] Writing PHPUnit tests (commit these in the `tests/phpunit` folder)
|
||||
- [ ] Playtesting using a Minecraft client (provide screenshots or a video)
|
||||
- [ ] Writing a test plugin (provide the code and sample output)
|
||||
- [ ] Other (provide details)
|
||||
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -3,7 +3,7 @@ updates:
|
||||
- package-ecosystem: composer
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
interval: weekly
|
||||
time: "10:00"
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
@ -21,4 +21,4 @@ updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
interval: weekly
|
||||
|
6
.github/workflows/discord-release-notify.yml
vendored
6
.github/workflows/discord-release-notify.yml
vendored
@ -13,12 +13,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.28.0
|
||||
uses: shivammathur/setup-php@2.29.0
|
||||
with:
|
||||
php-version: 8.1
|
||||
php-version: 8.2
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
|
10
.github/workflows/draft-release.yml
vendored
10
.github/workflows/draft-release.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version: [8.1]
|
||||
php-version: [8.2]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -20,12 +20,12 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.28.0
|
||||
uses: shivammathur/setup-php@2.29.0
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
@ -77,7 +77,7 @@ jobs:
|
||||
> build_info.json
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release_artifacts
|
||||
path: |
|
||||
@ -86,7 +86,7 @@ jobs:
|
||||
${{ github.workspace }}/build_info.json
|
||||
|
||||
- name: Create draft release
|
||||
uses: ncipollo/release-action@v1.13.0
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
|
||||
commit: ${{ github.sha }}
|
||||
|
165
.github/workflows/main-php-matrix.yml
vendored
Normal file
165
.github/workflows/main-php-matrix.yml
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
name: CI (all supported PHP versions)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
php:
|
||||
description: 'PHP version in X.Y format'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
#these are parameterized to ease updating
|
||||
pm-version-major:
|
||||
description: 'PocketMine-MP major version'
|
||||
default: 5
|
||||
type: number
|
||||
image:
|
||||
description: 'Runner image to use'
|
||||
default: 'ubuntu-20.04'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
phpstan:
|
||||
name: PHPStan analysis
|
||||
runs-on: ${{ inputs.image }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: ${{ inputs.pm-version-major }}
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
|
||||
|
||||
phpunit:
|
||||
name: PHPUnit tests
|
||||
runs-on: ${{ inputs.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: ${{ inputs.pm-version-major }}
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPUnit tests
|
||||
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
|
||||
|
||||
integration:
|
||||
name: Integration tests
|
||||
runs-on: ${{ inputs.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: ${{ inputs.pm-version-major }}
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Run integration tests
|
||||
run: ./tests/travis.sh -t4
|
||||
|
||||
codegen:
|
||||
name: Generated Code consistency checks
|
||||
runs-on: ${{ inputs.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@3.1.0
|
||||
with:
|
||||
php-version: ${{ inputs.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: ${{ inputs.pm-version-major }}
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ inputs.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Regenerate registry annotations
|
||||
run: php build/generate-registry-annotations.php src
|
||||
|
||||
- name: Regenerate KnownTranslation APIs
|
||||
run: php build/generate-known-translation-apis.php
|
||||
|
||||
- name: Regenerate BedrockData available files constants
|
||||
run: php build/generate-bedrockdata-path-consts.php
|
||||
|
||||
- name: Regenerate YmlServerProperties constants
|
||||
run: php build/generate-pocketmine-yml-property-consts.php
|
||||
|
||||
- name: Verify code is unchanged
|
||||
run: |
|
||||
git diff
|
||||
git diff --quiet
|
163
.github/workflows/main.yml
vendored
163
.github/workflows/main.yml
vendored
@ -6,162 +6,17 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
phpstan:
|
||||
name: PHPStan analysis
|
||||
runs-on: ${{ matrix.image }}
|
||||
|
||||
all-php-versions:
|
||||
name: PHP ${{ matrix.php }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: ["8.1", "8.2", "8.3"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan analyze --no-progress --memory-limit=2G
|
||||
|
||||
phpunit:
|
||||
name: PHPUnit tests
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: ["8.1", "8.2", "8.3"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Run PHPUnit tests
|
||||
run: ./vendor/bin/phpunit --bootstrap vendor/autoload.php --fail-on-warning tests/phpunit
|
||||
|
||||
integration:
|
||||
name: Integration tests
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: ["8.1", "8.2", "8.3"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Run integration tests
|
||||
run: ./tests/travis.sh -t4
|
||||
|
||||
codegen:
|
||||
name: Generated Code consistency checks
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: ["8.1", "8.2", "8.3"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: pmmp/setup-php-action@2.0.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
install-path: "./bin"
|
||||
pm-version-major: "5"
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Regenerate registry annotations
|
||||
run: php build/generate-registry-annotations.php src
|
||||
|
||||
- name: Regenerate KnownTranslation APIs
|
||||
run: php build/generate-known-translation-apis.php
|
||||
|
||||
- name: Regenerate BedrockData available files constants
|
||||
run: php build/generate-bedrockdata-path-consts.php
|
||||
|
||||
- name: Regenerate YmlServerProperties constants
|
||||
run: php build/generate-pocketmine-yml-property-consts.php
|
||||
|
||||
- name: Verify code is unchanged
|
||||
run: |
|
||||
git diff
|
||||
git diff --quiet
|
||||
uses: ./.github/workflows/main-php-matrix.yml
|
||||
with:
|
||||
php: ${{ matrix.php }}
|
||||
secrets: inherit
|
||||
|
||||
codestyle:
|
||||
name: Code Style checks
|
||||
@ -173,10 +28,10 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.28.0
|
||||
uses: shivammathur/setup-php@2.29.0
|
||||
with:
|
||||
php-version: 8.1
|
||||
tools: php-cs-fixer:3.17
|
||||
php-version: 8.2
|
||||
tools: php-cs-fixer:3.49
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
## Pre-requisites
|
||||
- A bash shell (git bash is sufficient for Windows)
|
||||
- [`git`](https://git-scm.com) available in your shell
|
||||
- PHP 8.1 or newer available in your shell
|
||||
- PHP 8.2 or newer available in your shell
|
||||
- [`composer`](https://getcomposer.org) available in your shell
|
||||
|
||||
## Custom PHP binaries
|
||||
|
@ -21,6 +21,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\VersionInfo;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if(count($argv) !== 7){
|
||||
@ -30,12 +33,12 @@ if(count($argv) !== 7){
|
||||
|
||||
echo json_encode([
|
||||
"php_version" => sprintf("%d.%d", PHP_MAJOR_VERSION, PHP_MINOR_VERSION), //deprecated
|
||||
"base_version" => \pocketmine\VersionInfo::BASE_VERSION,
|
||||
"base_version" => VersionInfo::BASE_VERSION,
|
||||
"build" => (int) $argv[4],
|
||||
"is_dev" => \pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
"channel" => \pocketmine\VersionInfo::BUILD_CHANNEL,
|
||||
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
"channel" => VersionInfo::BUILD_CHANNEL,
|
||||
"git_commit" => $argv[1],
|
||||
"mcpe_version" => \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK,
|
||||
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
|
||||
"date" => time(), //TODO: maybe we should embed this in VersionInfo?
|
||||
"details_url" => "https://github.com/$argv[3]/releases/tag/$argv[2]",
|
||||
"download_url" => "https://github.com/$argv[3]/releases/download/$argv[2]/PocketMine-MP.phar",
|
||||
|
Submodule build/php updated: b0ffbdbe33...6f619bf7a0
@ -32,6 +32,7 @@ use function getcwd;
|
||||
use function getopt;
|
||||
use function implode;
|
||||
use function ini_get;
|
||||
use function is_string;
|
||||
use function microtime;
|
||||
use function preg_quote;
|
||||
use function realpath;
|
||||
@ -147,8 +148,17 @@ function main() : void{
|
||||
}else{
|
||||
$build = 0;
|
||||
}
|
||||
if(isset($opts["out"])){
|
||||
if(!is_string($opts["out"])){
|
||||
echo "--out cannot be specified multiple times" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
$pharPath = $opts["out"];
|
||||
}else{
|
||||
$pharPath = getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar";
|
||||
}
|
||||
foreach(buildPhar(
|
||||
$opts["out"] ?? getcwd() . DIRECTORY_SEPARATOR . "PocketMine-MP.phar",
|
||||
$pharPath,
|
||||
dirname(__DIR__) . DIRECTORY_SEPARATOR,
|
||||
[
|
||||
'resources',
|
||||
|
46
changelogs/5.10.md
Normal file
46
changelogs/5.10.md
Normal file
@ -0,0 +1,46 @@
|
||||
# 5.10.0
|
||||
Released 14th December 2023.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.50**
|
||||
|
||||
This is a minor feature release, including new gameplay features and minor performance improvements.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
## General
|
||||
- PHP 8.2 is now used by default. PHP 8.1 is still supported, but will be removed in a future 5.x release.
|
||||
- Improved timings reports by removing `Breakdown` timings group. This group serves no purpose with tree timings and made for confusing reading.
|
||||
|
||||
## Performance
|
||||
- Improved performance of `Block::encodeFullState()` in most conditions. This in turn improves performance of `World::setBlock()` and `World::setBlockAt()`.
|
||||
- Improved network compression performance by avoiding unnecessary object allocations.
|
||||
- Timings now report time spent in individual `Snooze` handlers, making it easier to debug performance issues.
|
||||
|
||||
## Gameplay
|
||||
### Blocks
|
||||
- Implemented crop growth speed modifiers.
|
||||
- The following things now positively affect crop growth speed:
|
||||
- Being planted on or being adjacent to farmland (hydrated farmland offers a larger benefit than dry farmland)
|
||||
- Potential light level of at least 9
|
||||
- Being planted in rows with space between them (or a different type of crop)
|
||||
- The following things now negatively affect crop growth speed:
|
||||
- Improper arrangement (e.g. the same crop on all sides)
|
||||
- Insufficient light level (below 9)
|
||||
- Poorly arranged crops will grow slower in this version. Past versions behaved as if crops were always planted in ideal conditions.
|
||||
- Crops planted in ideal conditions will grow at the same speed as before.
|
||||
|
||||
### Items
|
||||
- Added the following new items:
|
||||
- All types of Smithing Template
|
||||
- Pitcher Pod is now correctly registered. In previous versions, it was mapped to the Pitcher Crop block, causing incorrect name display in commands.
|
||||
|
||||
## Internals
|
||||
- Cleaned up various getter usages where direct property access is possible.
|
||||
- Avoided unnecessary repeated getter calls in some loops.
|
||||
- `NetworkSession` may now track `string` instead of `CompressBatchPromise` when a batch was synchronously compressed. This significantly reduces object allocations and improves performance.
|
||||
- `NetworkSession` now sends less information to clients on login validation failure. This avoids leaking potentially sensitive error information to clients.
|
||||
- Clients can correlate their disconnects with server-side logs using the `Error ID` shown on the disconnect screen.
|
45
changelogs/5.11.md
Normal file
45
changelogs/5.11.md
Normal file
@ -0,0 +1,45 @@
|
||||
# 5.11.0
|
||||
Released 7th February 2024.
|
||||
|
||||
**For Minecraft: Bedrock Edition 1.20.60**
|
||||
|
||||
This is a support release for Minecraft: Bedrock Edition 1.20.60.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
## General
|
||||
- Added support for Minecraft: Bedrock Edition 1.20.60.
|
||||
- Removed support for earlier versions.
|
||||
|
||||
## Fixes
|
||||
- Fixed `tools/generate-item-upgrade-schema.php` not correctly handling items whose IDs were changed multiple times.
|
||||
- Fixed `ServerKiller` not working correctly in some cases (incorrectly handled wake-up conditions).
|
||||
- `ItemBlock`s of `Air` blocks are now always considered as "null" items regardless of count, and don't occupy inventory slots.
|
||||
|
||||
## Internals
|
||||
- Restructured GitHub Actions CI workflows to make them easier to maintain (no need to update PHP versions in multiple places anymore).
|
||||
- GitHub Actions CodeStyle workflow now uses php-cs-fixer 3.49.x.
|
||||
- Dependabot updates are now processed weekly instead of daily.
|
||||
|
||||
# 5.11.1
|
||||
Released 23rd February 2024.
|
||||
|
||||
## Fixes
|
||||
- Fixed subchunk count calculation in `ChunkSerializer` for non-overworld dimension (useful for dimension plugins).
|
||||
- Harden options used for processing JSON data, particularly on the network, to close security issues.
|
||||
|
||||
## Documentation
|
||||
- Fixed PHPStan signature for `Utils::cloneObjectArray()`.
|
||||
|
||||
## Internals
|
||||
- Updated GitHub Actions versions to get rid of deprecation warnings.
|
||||
|
||||
# 5.11.2
|
||||
Released 26th February 2024.
|
||||
|
||||
## Fixes
|
||||
- Added extra checks for `BookEditPacket` handling.
|
@ -32,11 +32,11 @@
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.4.0+bedrock-1.20.50",
|
||||
"pocketmine/bedrock-data": "~2.7.0+bedrock-1.20.50",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.6.0+bedrock-1.20.50",
|
||||
"pocketmine/bedrock-protocol": "~26.0.0+bedrock-1.20.50",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.5.0+bedrock-1.20.60",
|
||||
"pocketmine/bedrock-data": "~2.8.0+bedrock-1.20.60",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.7.0+bedrock-1.20.60",
|
||||
"pocketmine/bedrock-protocol": "~27.0.0+bedrock-1.20.60",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
@ -49,10 +49,10 @@
|
||||
"pocketmine/raklib-ipc": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"symfony/filesystem": "~6.3.0"
|
||||
"symfony/filesystem": "~6.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.10.47",
|
||||
"phpstan/phpstan": "1.10.58",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
|
||||
|
165
composer.lock
generated
165
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "828ee775e4e3548ef3fb0d9690ca2f44",
|
||||
"content-hash": "14848cb7b70d0fa63ed46b30128c2320",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -122,16 +122,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "3.4.0",
|
||||
"version": "3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||
"reference": "9872eb37f15080b19c2b7861085e549c48dda92d"
|
||||
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/9872eb37f15080b19c2b7861085e549c48dda92d",
|
||||
"reference": "9872eb37f15080b19c2b7861085e549c48dda92d",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
|
||||
"reference": "1ed4ba738333c4b4afe4fef8e9326a45c89f12e3",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -142,22 +142,22 @@
|
||||
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.4.0"
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.5.0"
|
||||
},
|
||||
"time": "2023-11-08T15:22:06+00:00"
|
||||
"time": "2024-02-07T11:46:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
"version": "2.7.0+bedrock-1.20.50",
|
||||
"version": "2.8.0+bedrock-1.20.60",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockData.git",
|
||||
"reference": "36f975dfca7520b7d36b0b39429f274464c9bc13"
|
||||
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/36f975dfca7520b7d36b0b39429f274464c9bc13",
|
||||
"reference": "36f975dfca7520b7d36b0b39429f274464c9bc13",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d8ea0355b7c835564af9fe6e273e650ac62c84a2",
|
||||
"reference": "d8ea0355b7c835564af9fe6e273e650ac62c84a2",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -168,22 +168,22 @@
|
||||
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockData/issues",
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.50"
|
||||
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.60"
|
||||
},
|
||||
"time": "2023-12-06T13:59:08+00:00"
|
||||
"time": "2024-02-07T11:23:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-item-upgrade-schema",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
|
||||
"reference": "d374e5fd8302977675dcd2a42733abd3ee476ca1"
|
||||
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/d374e5fd8302977675dcd2a42733abd3ee476ca1",
|
||||
"reference": "d374e5fd8302977675dcd2a42733abd3ee476ca1",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/69772dd58e2b2c7b7513fa2bcdc46e782228641c",
|
||||
"reference": "69772dd58e2b2c7b7513fa2bcdc46e782228641c",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -194,27 +194,26 @@
|
||||
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.6.0"
|
||||
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.7.0"
|
||||
},
|
||||
"time": "2023-11-08T18:12:14+00:00"
|
||||
"time": "2024-02-07T11:58:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "26.0.0+bedrock-1.20.50",
|
||||
"version": "27.0.2+bedrock-1.20.60",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "f278a0b6d4fa1e2e0408a125f323a3118b1968df"
|
||||
"reference": "6905865133b69da8c95a13c563d349e1993c06b8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/f278a0b6d4fa1e2e0408a125f323a3118b1968df",
|
||||
"reference": "f278a0b6d4fa1e2e0408a125f323a3118b1968df",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6905865133b69da8c95a13c563d349e1993c06b8",
|
||||
"reference": "6905865133b69da8c95a13c563d349e1993c06b8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"netresearch/jsonmapper": "^4.0",
|
||||
"php": "^8.1",
|
||||
"pocketmine/binaryutils": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0 || ^0.3.0",
|
||||
@ -241,9 +240,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/26.0.0+bedrock-1.20.50"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/27.0.2+bedrock-1.20.60"
|
||||
},
|
||||
"time": "2023-12-06T14:08:37+00:00"
|
||||
"time": "2024-02-23T13:43:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -563,16 +562,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/netresearch-jsonmapper",
|
||||
"version": "v4.2.1000",
|
||||
"version": "v4.4.999",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/netresearch-jsonmapper.git",
|
||||
"reference": "078764e869e9b732f97206ec9363480a77c35532"
|
||||
"reference": "9a6610033d56e358e86a3e4fd5f87063c7318833"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/078764e869e9b732f97206ec9363480a77c35532",
|
||||
"reference": "078764e869e9b732f97206ec9363480a77c35532",
|
||||
"url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/9a6610033d56e358e86a3e4fd5f87063c7318833",
|
||||
"reference": "9a6610033d56e358e86a3e4fd5f87063c7318833",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -586,7 +585,7 @@
|
||||
"netresearch/jsonmapper": "~4.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
|
||||
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
|
||||
"squizlabs/php_codesniffer": "~3.5"
|
||||
},
|
||||
"type": "library",
|
||||
@ -611,9 +610,9 @@
|
||||
"support": {
|
||||
"email": "cweiske@cweiske.de",
|
||||
"issues": "https://github.com/cweiske/jsonmapper/issues",
|
||||
"source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.2.1000"
|
||||
"source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.4.999"
|
||||
},
|
||||
"time": "2023-07-14T10:44:14+00:00"
|
||||
"time": "2024-02-23T13:17:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
@ -922,16 +921,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v6.3.1",
|
||||
"version": "v6.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae"
|
||||
"reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
|
||||
"reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59",
|
||||
"reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -965,7 +964,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.3.1"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -981,7 +980,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-06-01T08:30:39+00:00"
|
||||
"time": "2023-07-26T17:27:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@ -1211,25 +1210,27 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.17.1",
|
||||
"version": "v5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
|
||||
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-json": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"php": ">=7.0"
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"ircmaxell/php-yacc": "^0.0.7",
|
||||
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
@ -1237,7 +1238,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.9-dev"
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1261,9 +1262,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
|
||||
},
|
||||
"time": "2023-08-13T19:53:39+00:00"
|
||||
"time": "2024-01-07T17:17:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -1378,16 +1379,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.47",
|
||||
"version": "1.10.58",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "84dbb33b520ea28b6cf5676a3941f4bae1c1ff39"
|
||||
"reference": "a23518379ec4defd9e47cbf81019526861623ec2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/84dbb33b520ea28b6cf5676a3941f4bae1c1ff39",
|
||||
"reference": "84dbb33b520ea28b6cf5676a3941f4bae1c1ff39",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/a23518379ec4defd9e47cbf81019526861623ec2",
|
||||
"reference": "a23518379ec4defd9e47cbf81019526861623ec2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1436,7 +1437,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-01T15:19:17+00:00"
|
||||
"time": "2024-02-12T20:02:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
@ -1541,23 +1542,23 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "10.1.9",
|
||||
"version": "10.1.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "a56a9ab2f680246adcf3db43f38ddf1765774735"
|
||||
"reference": "78c3b7625965c2513ee96569a4dbb62601784145"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a56a9ab2f680246adcf3db43f38ddf1765774735",
|
||||
"reference": "a56a9ab2f680246adcf3db43f38ddf1765774735",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145",
|
||||
"reference": "78c3b7625965c2513ee96569a4dbb62601784145",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.15",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=8.1",
|
||||
"phpunit/php-file-iterator": "^4.0",
|
||||
"phpunit/php-text-template": "^3.0",
|
||||
@ -1607,7 +1608,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.9"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1615,7 +1616,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-11-23T12:23:20+00:00"
|
||||
"time": "2023-12-21T15:38:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -2207,20 +2208,20 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/complexity",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/complexity.git",
|
||||
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957"
|
||||
"reference": "68ff824baeae169ec9f2137158ee529584553799"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957",
|
||||
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
|
||||
"reference": "68ff824baeae169ec9f2137158ee529584553799",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.10",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
@ -2229,7 +2230,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.1-dev"
|
||||
"dev-main": "3.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2253,7 +2254,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.1.0"
|
||||
"source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2261,20 +2262,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-09-28T11:50:59+00:00"
|
||||
"time": "2023-12-21T08:37:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "5.0.3",
|
||||
"version": "5.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b"
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
|
||||
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2287,7 +2288,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "5.0-dev"
|
||||
"dev-main": "5.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2320,7 +2321,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.0.3"
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2328,7 +2329,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-01T07:48:21+00:00"
|
||||
"time": "2023-12-22T10:55:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
@ -2536,20 +2537,20 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
|
||||
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d"
|
||||
"reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d",
|
||||
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
|
||||
"reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.10",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
@ -2582,7 +2583,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.1"
|
||||
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2590,7 +2591,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-31T09:25:50+00:00"
|
||||
"time": "2023-12-21T08:38:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/object-enumerator",
|
||||
|
48
src/BootstrapOptions.php
Normal file
48
src/BootstrapOptions.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
/**
|
||||
* Constants for all the command-line options that PocketMine-MP supports.
|
||||
* Other options not listed here can be used to override server.properties and pocketmine.yml values temporarily.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class BootstrapOptions{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/** Disables the setup wizard on first startup */
|
||||
public const NO_WIZARD = "no-wizard";
|
||||
/** Force-disables console text colour and formatting */
|
||||
public const DISABLE_ANSI = "disable-ansi";
|
||||
/** Force-enables console text colour and formatting */
|
||||
public const ENABLE_ANSI = "enable-ansi";
|
||||
/** Path to look in for plugins */
|
||||
public const PLUGINS = "plugins";
|
||||
/** Path to store and load server data */
|
||||
public const DATA = "data";
|
||||
}
|
@ -274,8 +274,8 @@ JIT_WARNING
|
||||
ErrorToExceptionHandler::set();
|
||||
|
||||
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
|
||||
$dataPath = getopt_string("data") ?? $cwd;
|
||||
$pluginPath = getopt_string("plugins") ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
|
||||
$dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd;
|
||||
$pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
|
||||
Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
|
||||
|
||||
if(!@mkdir($dataPath, 0777, true) && !is_dir($dataPath)){
|
||||
@ -308,10 +308,10 @@ JIT_WARNING
|
||||
//Logger has a dependency on timezone
|
||||
Timezone::init();
|
||||
|
||||
$opts = getopt("", ["no-wizard", "enable-ansi", "disable-ansi"]);
|
||||
if(isset($opts["enable-ansi"])){
|
||||
$opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI]);
|
||||
if(isset($opts[BootstrapOptions::ENABLE_ANSI])){
|
||||
Terminal::init(true);
|
||||
}elseif(isset($opts["disable-ansi"])){
|
||||
}elseif(isset($opts[BootstrapOptions::DISABLE_ANSI])){
|
||||
Terminal::init(false);
|
||||
}else{
|
||||
Terminal::init();
|
||||
@ -324,7 +324,7 @@ JIT_WARNING
|
||||
|
||||
$exitCode = 0;
|
||||
do{
|
||||
if(!file_exists(Path::join($dataPath, "server.properties")) && !isset($opts["no-wizard"])){
|
||||
if(!file_exists(Path::join($dataPath, "server.properties")) && !isset($opts[BootstrapOptions::NO_WIZARD])){
|
||||
$installer = new SetupWizard($dataPath);
|
||||
if(!$installer->run()){
|
||||
$exitCode = -1;
|
||||
|
114
src/Server.php
114
src/Server.php
@ -60,6 +60,7 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\PacketBroadcaster;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
|
||||
use pocketmine\network\mcpe\raklib\RakLibInterface;
|
||||
use pocketmine\network\mcpe\StandardEntityEventBroadcaster;
|
||||
use pocketmine\network\mcpe\StandardPacketBroadcaster;
|
||||
@ -125,6 +126,7 @@ use Symfony\Component\Filesystem\Path;
|
||||
use function array_fill;
|
||||
use function array_sum;
|
||||
use function base64_encode;
|
||||
use function chr;
|
||||
use function cli_set_process_title;
|
||||
use function copy;
|
||||
use function count;
|
||||
@ -523,7 +525,7 @@ class Server{
|
||||
return $this->playerDataProvider->loadData($name);
|
||||
}catch(PlayerDataLoadException $e){
|
||||
$this->logger->debug("Failed to load player data for $name: " . $e->getMessage());
|
||||
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
||||
$this->logger->error($this->language->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@ -542,7 +544,7 @@ class Server{
|
||||
try{
|
||||
$this->playerDataProvider->saveData($name, $ev->getSaveData());
|
||||
}catch(PlayerDataSaveException $e){
|
||||
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
||||
$this->logger->critical($this->language->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
});
|
||||
@ -737,7 +739,7 @@ class Server{
|
||||
* @return string[][]
|
||||
*/
|
||||
public function getCommandAliases() : array{
|
||||
$section = $this->configGroup->getProperty(YmlServerProperties::ALIASES);
|
||||
$section = $this->configGroup->getProperty(Yml::ALIASES);
|
||||
$result = [];
|
||||
if(is_array($section)){
|
||||
foreach($section as $key => $value){
|
||||
@ -854,14 +856,14 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::language_selected($this->getLanguage()->getName(), $this->getLanguage()->getLang())));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::language_selected($this->language->getName(), $this->language->getLang())));
|
||||
|
||||
if(VersionInfo::IS_DEVELOPMENT_BUILD){
|
||||
if(!$this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_DEV_BUILDS, false)){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error1(VersionInfo::NAME)));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error2()));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error3()));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4(YmlServerProperties::SETTINGS_ENABLE_DEV_BUILDS)));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4(Yml::SETTINGS_ENABLE_DEV_BUILDS)));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error5("https://github.com/pmmp/PocketMine-MP/releases")));
|
||||
$this->forceShutdownExit();
|
||||
|
||||
@ -877,7 +879,7 @@ class Server{
|
||||
|
||||
$this->memoryManager = new MemoryManager($this);
|
||||
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_start(TextFormat::AQUA . $this->getVersion() . TextFormat::RESET)));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_start(TextFormat::AQUA . $this->getVersion() . TextFormat::RESET)));
|
||||
|
||||
if(($poolSize = $this->configGroup->getPropertyString(Yml::SETTINGS_ASYNC_WORKERS, "auto")) === "auto"){
|
||||
$poolSize = 2;
|
||||
@ -937,11 +939,11 @@ class Server{
|
||||
|
||||
$this->onlineMode = $this->configGroup->getConfigBool(ServerProperties::XBOX_AUTH, true);
|
||||
if($this->onlineMode){
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_auth_enabled()));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_auth_enabled()));
|
||||
}else{
|
||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_auth_disabled()));
|
||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authWarning()));
|
||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
|
||||
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_auth_disabled()));
|
||||
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_authWarning()));
|
||||
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled()));
|
||||
}
|
||||
|
||||
if($this->configGroup->getConfigBool(ServerProperties::HARDCORE, false) && $this->getDifficulty() < World::DIFFICULTY_HARD){
|
||||
@ -952,17 +954,17 @@ class Server{
|
||||
|
||||
$this->serverID = Utils::getMachineUniqueId($this->getIp() . $this->getPort());
|
||||
|
||||
$this->getLogger()->debug("Server unique id: " . $this->getServerUniqueId());
|
||||
$this->getLogger()->debug("Machine unique id: " . Utils::getMachineUniqueId());
|
||||
$this->logger->debug("Server unique id: " . $this->getServerUniqueId());
|
||||
$this->logger->debug("Machine unique id: " . Utils::getMachineUniqueId());
|
||||
|
||||
$this->network = new Network($this->logger);
|
||||
$this->network->setName($this->getMotd());
|
||||
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_info(
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_info(
|
||||
$this->getName(),
|
||||
(VersionInfo::IS_DEVELOPMENT_BUILD ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET
|
||||
)));
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
||||
|
||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
|
||||
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
|
||||
@ -973,7 +975,7 @@ class Server{
|
||||
|
||||
$this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes"));
|
||||
|
||||
$this->resourceManager = new ResourcePackManager(Path::join($this->getDataPath(), "resource_packs"), $this->logger);
|
||||
$this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger);
|
||||
|
||||
$pluginGraylist = null;
|
||||
$graylistFile = Path::join($this->dataPath, "plugin_list.yml");
|
||||
@ -987,7 +989,7 @@ class Server{
|
||||
$this->forceShutdownExit();
|
||||
return;
|
||||
}
|
||||
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->getDataPath(), "plugin_data"), $pluginGraylist);
|
||||
$this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->dataPath, "plugin_data"), $pluginGraylist);
|
||||
$this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader));
|
||||
$this->pluginManager->registerInterface(new ScriptPluginLoader());
|
||||
|
||||
@ -1049,9 +1051,9 @@ class Server{
|
||||
|
||||
$this->configGroup->save();
|
||||
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_defaultGameMode($this->getGamemode()->getTranslatableName())));
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET)));
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3)))));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_defaultGameMode($this->getGamemode()->getTranslatableName())));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET)));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3)))));
|
||||
|
||||
$forwarder = new BroadcastLoggerForwarder($this, $this->logger, $this->language);
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder);
|
||||
@ -1139,13 +1141,13 @@ class Server{
|
||||
if($this->worldManager->getDefaultWorld() === null){
|
||||
$default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
|
||||
if(trim($default) == ""){
|
||||
$this->getLogger()->warning("level-name cannot be null, using default");
|
||||
$this->logger->warning("level-name cannot be null, using default");
|
||||
$default = "world";
|
||||
$this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($default, true)){
|
||||
if($this->worldManager->isWorldGenerated($default)){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1154,7 +1156,7 @@ class Server{
|
||||
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
|
||||
|
||||
if($generatorClass === null){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
return false;
|
||||
}
|
||||
$creationOptions = WorldCreationOptions::create()
|
||||
@ -1200,7 +1202,7 @@ class Server{
|
||||
return false;
|
||||
}
|
||||
if($rakLibRegistered){
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStart($prettyIp, (string) $port)));
|
||||
}
|
||||
if($useQuery){
|
||||
if(!$rakLibRegistered){
|
||||
@ -1208,7 +1210,7 @@ class Server{
|
||||
//if it's not registered we need to make sure Query still works
|
||||
$this->network->registerInterface(new DedicatedQueryNetworkInterface($ip, $port, $ipV6, new \PrefixedLogger($this->logger, "Dedicated Query Interface")));
|
||||
}
|
||||
$this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_query_running($prettyIp, (string) $port)));
|
||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_query_running($prettyIp, (string) $port)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1368,25 +1370,31 @@ class Server{
|
||||
*
|
||||
* @param bool|null $sync Compression on the main thread (true) or workers (false). Default is automatic (null).
|
||||
*/
|
||||
public function prepareBatch(string $buffer, Compressor $compressor, ?bool $sync = null, ?TimingsHandler $timings = null) : CompressBatchPromise{
|
||||
public function prepareBatch(string $buffer, Compressor $compressor, ?bool $sync = null, ?TimingsHandler $timings = null) : CompressBatchPromise|string{
|
||||
$timings ??= Timings::$playerNetworkSendCompress;
|
||||
try{
|
||||
$timings->startTiming();
|
||||
|
||||
if($sync === null){
|
||||
$threshold = $compressor->getCompressionThreshold();
|
||||
$sync = !$this->networkCompressionAsync || $threshold === null || strlen($buffer) < $threshold;
|
||||
}
|
||||
$threshold = $compressor->getCompressionThreshold();
|
||||
if($threshold === null || strlen($buffer) < $compressor->getCompressionThreshold()){
|
||||
$compressionType = CompressionAlgorithm::NONE;
|
||||
$compressed = $buffer;
|
||||
|
||||
$promise = new CompressBatchPromise();
|
||||
if(!$sync && strlen($buffer) >= $this->networkCompressionAsyncThreshold){
|
||||
$task = new CompressBatchTask($buffer, $promise, $compressor);
|
||||
$this->asyncPool->submitTask($task);
|
||||
}else{
|
||||
$promise->resolve($compressor->compress($buffer));
|
||||
$sync ??= !$this->networkCompressionAsync;
|
||||
|
||||
if(!$sync && strlen($buffer) >= $this->networkCompressionAsyncThreshold){
|
||||
$promise = new CompressBatchPromise();
|
||||
$task = new CompressBatchTask($buffer, $promise, $compressor);
|
||||
$this->asyncPool->submitTask($task);
|
||||
return $promise;
|
||||
}
|
||||
|
||||
$compressionType = $compressor->getNetworkId();
|
||||
$compressed = $compressor->compress($buffer);
|
||||
}
|
||||
|
||||
return $promise;
|
||||
return chr($compressionType) . $compressed;
|
||||
}finally{
|
||||
$timings->stopTiming();
|
||||
}
|
||||
@ -1463,43 +1471,43 @@ class Server{
|
||||
$this->shutdown();
|
||||
|
||||
if(isset($this->pluginManager)){
|
||||
$this->getLogger()->debug("Disabling all plugins");
|
||||
$this->logger->debug("Disabling all plugins");
|
||||
$this->pluginManager->disablePlugins();
|
||||
}
|
||||
|
||||
if(isset($this->network)){
|
||||
$this->network->getSessionManager()->close($this->configGroup->getPropertyString(YmlServerProperties::SETTINGS_SHUTDOWN_MESSAGE, "Server closed"));
|
||||
$this->network->getSessionManager()->close($this->configGroup->getPropertyString(Yml::SETTINGS_SHUTDOWN_MESSAGE, "Server closed"));
|
||||
}
|
||||
|
||||
if(isset($this->worldManager)){
|
||||
$this->getLogger()->debug("Unloading all worlds");
|
||||
$this->logger->debug("Unloading all worlds");
|
||||
foreach($this->worldManager->getWorlds() as $world){
|
||||
$this->worldManager->unloadWorld($world, true);
|
||||
}
|
||||
}
|
||||
|
||||
$this->getLogger()->debug("Removing event handlers");
|
||||
$this->logger->debug("Removing event handlers");
|
||||
HandlerListManager::global()->unregisterAll();
|
||||
|
||||
if(isset($this->asyncPool)){
|
||||
$this->getLogger()->debug("Shutting down async task worker pool");
|
||||
$this->logger->debug("Shutting down async task worker pool");
|
||||
$this->asyncPool->shutdown();
|
||||
}
|
||||
|
||||
if(isset($this->configGroup)){
|
||||
$this->getLogger()->debug("Saving properties");
|
||||
$this->logger->debug("Saving properties");
|
||||
$this->configGroup->save();
|
||||
}
|
||||
|
||||
if($this->console !== null){
|
||||
$this->getLogger()->debug("Closing console");
|
||||
$this->logger->debug("Closing console");
|
||||
$this->console->quit();
|
||||
}
|
||||
|
||||
if(isset($this->network)){
|
||||
$this->getLogger()->debug("Stopping network interfaces");
|
||||
$this->logger->debug("Stopping network interfaces");
|
||||
foreach($this->network->getInterfaces() as $interface){
|
||||
$this->getLogger()->debug("Stopping network interface " . get_class($interface));
|
||||
$this->logger->debug("Stopping network interface " . get_class($interface));
|
||||
$this->network->unregisterInterface($interface);
|
||||
}
|
||||
}
|
||||
@ -1567,7 +1575,7 @@ class Server{
|
||||
}
|
||||
|
||||
private function writeCrashDumpFile(CrashDump $dump) : string{
|
||||
$crashFolder = Path::join($this->getDataPath(), "crashdumps");
|
||||
$crashFolder = Path::join($this->dataPath, "crashdumps");
|
||||
if(!is_dir($crashFolder)){
|
||||
mkdir($crashFolder);
|
||||
}
|
||||
@ -1598,17 +1606,17 @@ class Server{
|
||||
ini_set("error_reporting", '0');
|
||||
ini_set("memory_limit", '-1'); //Fix error dump not dumped on memory problems
|
||||
try{
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_create()));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_crash_create()));
|
||||
$dump = new CrashDump($this, $this->pluginManager ?? null);
|
||||
|
||||
$crashDumpPath = $this->writeCrashDumpFile($dump);
|
||||
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath)));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath)));
|
||||
|
||||
if($this->configGroup->getPropertyBool(Yml::AUTO_REPORT_ENABLED, true)){
|
||||
$report = true;
|
||||
|
||||
$stamp = Path::join($this->getDataPath(), "crashdumps", ".last_crash");
|
||||
$stamp = Path::join($this->dataPath, "crashdumps", ".last_crash");
|
||||
$crashInterval = 120; //2 minutes
|
||||
if(($lastReportTime = @filemtime($stamp)) !== false && $lastReportTime + $crashInterval >= time()){
|
||||
$report = false;
|
||||
@ -1639,7 +1647,7 @@ class Server{
|
||||
if(isset($data->crashId) && is_int($data->crashId) && isset($data->crashUrl) && is_string($data->crashUrl)){
|
||||
$reportId = $data->crashId;
|
||||
$reportUrl = $data->crashUrl;
|
||||
$this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_archive($reportUrl, (string) $reportId)));
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_crash_archive($reportUrl, (string) $reportId)));
|
||||
}elseif(isset($data->error) && is_string($data->error)){
|
||||
$this->logger->emergency("Automatic crash report submission failed: $data->error");
|
||||
}else{
|
||||
@ -1653,7 +1661,7 @@ class Server{
|
||||
}catch(\Throwable $e){
|
||||
$this->logger->logException($e);
|
||||
try{
|
||||
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_error($e->getMessage())));
|
||||
$this->logger->critical($this->language->translate(KnownTranslationFactory::pocketmine_crash_error($e->getMessage())));
|
||||
}catch(\Throwable $e){}
|
||||
}
|
||||
|
||||
@ -1771,7 +1779,7 @@ class Server{
|
||||
|
||||
echo "\x1b]0;" . $this->getName() . " " .
|
||||
$this->getPocketMineVersion() .
|
||||
" | Online $online/" . $this->getMaxPlayers() .
|
||||
" | Online $online/" . $this->maxPlayers .
|
||||
($connecting > 0 ? " (+$connecting connecting)" : "") .
|
||||
" | Memory " . $usage .
|
||||
" | U " . round($bandwidthStats->getSend()->getAverageBytes() / 1024, 2) .
|
||||
@ -1836,10 +1844,10 @@ class Server{
|
||||
}
|
||||
|
||||
if(($this->tickCounter % self::TICKS_PER_TPS_OVERLOAD_WARNING) === 0 && $this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){
|
||||
$this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
|
||||
$this->logger->warning($this->language->translate(KnownTranslationFactory::pocketmine_server_tickOverload()));
|
||||
}
|
||||
|
||||
$this->getMemoryManager()->check();
|
||||
$this->memoryManager->check();
|
||||
|
||||
if($this->console !== null){
|
||||
Timings::$serverCommand->startTiming();
|
||||
|
@ -24,7 +24,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine;
|
||||
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\snooze\SleeperHandlerEntry;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\Utils;
|
||||
use function hrtime;
|
||||
|
||||
/**
|
||||
@ -35,12 +37,29 @@ final class TimeTrackingSleeperHandler extends SleeperHandler{
|
||||
|
||||
private int $notificationProcessingTimeNs = 0;
|
||||
|
||||
/**
|
||||
* @var TimingsHandler[]
|
||||
* @phpstan-var array<string, TimingsHandler>
|
||||
*/
|
||||
private static array $handlerTimings = [];
|
||||
|
||||
public function __construct(
|
||||
private TimingsHandler $timings
|
||||
){
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function addNotifier(\Closure $handler) : SleeperHandlerEntry{
|
||||
$name = Utils::getNiceClosureName($handler);
|
||||
$timings = self::$handlerTimings[$name] ??= new TimingsHandler("Snooze Handler: " . $name, $this->timings);
|
||||
|
||||
return parent::addNotifier(function() use ($timings, $handler) : void{
|
||||
$timings->startTiming();
|
||||
$handler();
|
||||
$timings->stopTiming();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time in nanoseconds spent processing notifications since the last reset.
|
||||
*/
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.9.0";
|
||||
public const BASE_VERSION = "5.11.2";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
|
@ -173,7 +173,7 @@ class Bamboo extends Transparent{
|
||||
$newHeight = $height + $growAmount;
|
||||
|
||||
$stemBlock = (clone $this)->setReady(false)->setLeafSize(self::NO_LEAVES);
|
||||
if($newHeight >= 4 && !$stemBlock->isThick()){ //don't change it to false if height is less, because it might have been chopped
|
||||
if($newHeight >= 4 && !$stemBlock->thick){ //don't change it to false if height is less, because it might have been chopped
|
||||
$stemBlock = $stemBlock->setThick(true);
|
||||
}
|
||||
$smallLeavesBlock = (clone $stemBlock)->setLeafSize(self::SMALL_LEAVES);
|
||||
|
@ -55,12 +55,12 @@ class Barrel extends Opaque{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player !== null){
|
||||
if(abs($player->getPosition()->getX() - $this->position->getX()) < 2 && abs($player->getPosition()->getZ() - $this->position->getZ()) < 2){
|
||||
$y = $player->getEyePos()->getY();
|
||||
if(abs($player->getPosition()->x - $this->position->x) < 2 && abs($player->getPosition()->z - $this->position->z) < 2){
|
||||
$y = $player->getEyePos()->y;
|
||||
|
||||
if($y - $this->position->getY() > 2){
|
||||
if($y - $this->position->y > 2){
|
||||
$this->facing = Facing::UP;
|
||||
}elseif($this->position->getY() - $y > 0){
|
||||
}elseif($this->position->y - $y > 0){
|
||||
$this->facing = Facing::DOWN;
|
||||
}else{
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
|
@ -65,8 +65,8 @@ abstract class BaseBigDripleaf extends Transparent{
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
if($block instanceof BaseBigDripleaf){
|
||||
$this->facing = $block->getFacing();
|
||||
$tx->addBlock($block->getPosition(), VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($this->facing));
|
||||
$this->facing = $block->facing;
|
||||
$tx->addBlock($block->position, VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($this->facing));
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -98,7 +98,7 @@ abstract class BaseBigDripleaf extends Transparent{
|
||||
if($head === null){
|
||||
return false;
|
||||
}
|
||||
$pos = $head->getPosition();
|
||||
$pos = $head->position;
|
||||
$up = $pos->up();
|
||||
$world = $pos->getWorld();
|
||||
if(
|
||||
@ -110,8 +110,8 @@ abstract class BaseBigDripleaf extends Transparent{
|
||||
|
||||
$tx = new BlockTransaction($world);
|
||||
|
||||
$tx->addBlock($pos, VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($head->getFacing()));
|
||||
$tx->addBlock($up, VanillaBlocks::BIG_DRIPLEAF_HEAD()->setFacing($head->getFacing()));
|
||||
$tx->addBlock($pos, VanillaBlocks::BIG_DRIPLEAF_STEM()->setFacing($head->facing));
|
||||
$tx->addBlock($up, VanillaBlocks::BIG_DRIPLEAF_HEAD()->setFacing($head->facing));
|
||||
|
||||
$ev = new StructureGrowEvent($head, $tx, $player);
|
||||
$ev->call();
|
||||
|
@ -145,7 +145,7 @@ class Bed extends Transparent{
|
||||
|
||||
$b = ($this->isHeadPart() ? $this : $other);
|
||||
|
||||
if($b->isOccupied()){
|
||||
if($b->occupied){
|
||||
$player->sendMessage(KnownTranslationFactory::tile_bed_occupied()->prefix(TextFormat::GRAY));
|
||||
|
||||
return true;
|
||||
|
@ -270,11 +270,22 @@ class Block{
|
||||
}
|
||||
|
||||
private function encodeFullState() : int{
|
||||
$writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits + $this->requiredBlockOnlyStateDataBits);
|
||||
$writer->writeInt($this->requiredBlockItemStateDataBits, $this->encodeBlockItemState());
|
||||
$writer->writeInt($this->requiredBlockOnlyStateDataBits, $this->encodeBlockOnlyState());
|
||||
$blockItemBits = $this->requiredBlockItemStateDataBits;
|
||||
$blockOnlyBits = $this->requiredBlockOnlyStateDataBits;
|
||||
|
||||
return $writer->getValue();
|
||||
if($blockOnlyBits === 0 && $blockItemBits === 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
$result = 0;
|
||||
if($blockItemBits > 0){
|
||||
$result |= $this->encodeBlockItemState();
|
||||
}
|
||||
if($blockOnlyBits > 0){
|
||||
$result |= $this->encodeBlockOnlyState() << $blockItemBits;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,7 +97,7 @@ class ChiseledBookshelf extends Opaque{
|
||||
return false;
|
||||
}
|
||||
|
||||
$x = Facing::axis($face) === Axis::X ? $clickVector->getZ() : $clickVector->getX();
|
||||
$x = Facing::axis($face) === Axis::X ? $clickVector->z : $clickVector->x;
|
||||
$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
|
||||
Facing::isPositive(Facing::rotateY($face, true)) ? 1 - $x : $x,
|
||||
$clickVector->y
|
||||
|
@ -54,7 +54,7 @@ final class ChorusFlower extends Flowable{
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$position = $block->getPosition();
|
||||
$position = $block->position;
|
||||
$world = $position->getWorld();
|
||||
$down = $world->getBlock($position->down());
|
||||
|
||||
@ -152,7 +152,7 @@ final class ChorusFlower extends Flowable{
|
||||
if($tx === null){
|
||||
$tx = new BlockTransaction($this->position->getWorld());
|
||||
}
|
||||
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->getAge() + $ageChange)));
|
||||
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->age + $ageChange)));
|
||||
|
||||
return $tx;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ final class ChorusPlant extends Flowable{
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$position = $block->getPosition();
|
||||
$position = $block->position;
|
||||
$world = $position->getWorld();
|
||||
|
||||
$down = $world->getBlock($position->down());
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
@ -66,7 +67,7 @@ abstract class Crops extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){
|
||||
if($this->age < self::MAX_AGE && CropGrowthHelper::canGrow($this)){
|
||||
$block = clone $this;
|
||||
++$block->age;
|
||||
BlockEventHelper::grow($this, $block, null);
|
||||
|
@ -24,17 +24,18 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
final class DoublePitcherCrop extends DoublePlant{
|
||||
use AgeableTrait {
|
||||
@ -101,10 +102,19 @@ final class DoublePitcherCrop extends DoublePlant{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
//TODO: the growth speed is influenced by farmland and nearby crops
|
||||
//only the bottom half of the plant can grow randomly
|
||||
if(mt_rand(0, 2) === 0 && !$this->top){
|
||||
if(CropGrowthHelper::canGrow($this) && !$this->top){
|
||||
$this->grow(null);
|
||||
}
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
$this->age >= self::MAX_AGE ? VanillaBlocks::PITCHER_PLANT()->asItem() : VanillaItems::PITCHER_POD()
|
||||
];
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
return VanillaItems::PITCHER_POD();
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ class Farmland extends Transparent{
|
||||
$ev = new EntityTrampleFarmlandEvent($entity, $this);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->getPosition()->getWorld()->setBlock($this->getPosition(), VanillaBlocks::DIRT());
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -140,7 +140,7 @@ class Fire extends BaseFire{
|
||||
$block->onIncinerate();
|
||||
|
||||
$world = $this->position->getWorld();
|
||||
if($world->getBlock($block->getPosition())->isSameState($block)){
|
||||
if($world->getBlock($block->position)->isSameState($block)){
|
||||
$spreadedFire = false;
|
||||
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
|
||||
$fire = clone $this;
|
||||
|
@ -58,7 +58,7 @@ final class FloorCoralFan extends BaseCoral{
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player !== null){
|
||||
$playerBlockPos = $player->getPosition()->floor();
|
||||
$directionVector = $blockReplace->getPosition()->subtractVector($playerBlockPos)->normalize();
|
||||
$directionVector = $blockReplace->position->subtractVector($playerBlockPos)->normalize();
|
||||
$angle = rad2deg(atan2($directionVector->getZ(), $directionVector->getX()));
|
||||
|
||||
if($angle <= 45 || 315 <= $angle || (135 <= $angle && $angle <= 225)){
|
||||
|
@ -61,7 +61,7 @@ class Jukebox extends Opaque{
|
||||
|
||||
public function ejectRecord() : void{
|
||||
if($this->record !== null){
|
||||
$this->getPosition()->getWorld()->dropItem($this->getPosition()->add(0.5, 1, 0.5), $this->record);
|
||||
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $this->record);
|
||||
$this->record = null;
|
||||
$this->stopSound();
|
||||
}
|
||||
@ -76,12 +76,12 @@ class Jukebox extends Opaque{
|
||||
|
||||
public function startSound() : void{
|
||||
if($this->record !== null){
|
||||
$this->getPosition()->getWorld()->addSound($this->getPosition(), new RecordSound($this->record->getRecordType()));
|
||||
$this->position->getWorld()->addSound($this->position, new RecordSound($this->record->getRecordType()));
|
||||
}
|
||||
}
|
||||
|
||||
public function stopSound() : void{
|
||||
$this->getPosition()->getWorld()->addSound($this->getPosition(), new RecordStopSound());
|
||||
$this->position->getWorld()->addSound($this->position, new RecordStopSound());
|
||||
}
|
||||
|
||||
public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -108,8 +108,8 @@ class NetherVines extends Flowable{
|
||||
|
||||
private function grow(?Player $player, int $growthAmount = 1) : bool{
|
||||
$top = $this->seekToTip();
|
||||
$age = $top->getAge();
|
||||
$pos = $top->getPosition();
|
||||
$age = $top->age;
|
||||
$pos = $top->position;
|
||||
$world = $pos->getWorld();
|
||||
$changedBlocks = 0;
|
||||
|
||||
|
@ -70,13 +70,13 @@ class PinkPetals extends Flowable{
|
||||
}
|
||||
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
return ($blockReplace instanceof PinkPetals && $blockReplace->count < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){
|
||||
$this->count = $blockReplace->getCount() + 1;
|
||||
$this->facing = $blockReplace->getFacing();
|
||||
if($blockReplace instanceof PinkPetals && $blockReplace->count < self::MAX_COUNT){
|
||||
$this->count = $blockReplace->count + 1;
|
||||
$this->facing = $blockReplace->facing;
|
||||
}elseif($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
|
@ -25,17 +25,18 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
final class PitcherCrop extends Flowable{
|
||||
use AgeableTrait;
|
||||
@ -97,9 +98,12 @@ final class PitcherCrop extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
//TODO: the growth speed is influenced by farmland and nearby crops
|
||||
if(mt_rand(0, 2) === 0){
|
||||
if(CropGrowthHelper::canGrow($this)){
|
||||
$this->grow(null);
|
||||
}
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
return VanillaItems::PITCHER_POD();
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class RedMushroom extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
$position = $this->getPosition();
|
||||
$position = $this->position;
|
||||
$lightLevel = $position->getWorld()->getFullLightAt($position->x, $position->y, $position->z);
|
||||
$downId = $down->getTypeId();
|
||||
//TODO: nylium support
|
||||
|
@ -83,7 +83,7 @@ class SmallDripleaf extends Transparent{
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
|
||||
$tx->addBlock($block->getPosition(), VanillaBlocks::SMALL_DRIPLEAF()
|
||||
$tx->addBlock($block->position, VanillaBlocks::SMALL_DRIPLEAF()
|
||||
->setFacing($this->facing)
|
||||
->setTop(true)
|
||||
);
|
||||
@ -117,7 +117,7 @@ class SmallDripleaf extends Transparent{
|
||||
$height = mt_rand(2, 5);
|
||||
$grown = 0;
|
||||
for($i = 0; $i < $height; $i++){
|
||||
$pos = $bottomBlock->getSide(Facing::UP, $i)->getPosition();
|
||||
$pos = $bottomBlock->getSide(Facing::UP, $i)->position;
|
||||
if(!$this->canGrowTo($pos)){
|
||||
break;
|
||||
}
|
||||
|
@ -106,8 +106,8 @@ class Stair extends Transparent{
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
if(
|
||||
$facing === Facing::UP && $this->isUpsideDown() ||
|
||||
$facing === Facing::DOWN && !$this->isUpsideDown() ||
|
||||
$facing === Facing::UP && $this->upsideDown ||
|
||||
$facing === Facing::DOWN && !$this->upsideDown ||
|
||||
($facing === $this->facing && $this->shape !== StairShape::OUTER_LEFT && $this->shape !== StairShape::OUTER_RIGHT) ||
|
||||
($facing === Facing::rotate($this->facing, Axis::Y, false) && $this->shape === StairShape::INNER_LEFT) ||
|
||||
($facing === Facing::rotate($this->facing, Axis::Y, true) && $this->shape === StairShape::INNER_RIGHT)
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
@ -63,7 +64,7 @@ abstract class Stem extends Crops{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->facing === Facing::UP && mt_rand(0, 2) === 1){
|
||||
if($this->facing === Facing::UP && CropGrowthHelper::canGrow($this)){
|
||||
$world = $this->position->getWorld();
|
||||
if($this->age < self::MAX_AGE){
|
||||
$block = clone $this;
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Fertilizer;
|
||||
@ -32,7 +33,6 @@ use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function mt_rand;
|
||||
|
||||
final class TorchflowerCrop extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
@ -79,7 +79,7 @@ final class TorchflowerCrop extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(mt_rand(0, 2) === 1){
|
||||
if(CropGrowthHelper::canGrow($this)){
|
||||
BlockEventHelper::grow($this, $this->getNextState(), null);
|
||||
}
|
||||
}
|
||||
|
@ -788,7 +788,7 @@ final class VanillaBlocks{
|
||||
}
|
||||
|
||||
protected static function setup() : void{
|
||||
$railBreakInfo = new Info(new BlockBreakInfo(0.7));
|
||||
$railBreakInfo = new Info(new BreakInfo(0.7));
|
||||
self::register("activator_rail", new ActivatorRail(new BID(Ids::ACTIVATOR_RAIL), "Activator Rail", $railBreakInfo));
|
||||
self::register("air", new Air(new BID(Ids::AIR), "Air", new Info(BreakInfo::indestructible(-1.0))));
|
||||
self::register("anvil", new Anvil(new BID(Ids::ANVIL), "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))));
|
||||
@ -1638,7 +1638,7 @@ final class VanillaBlocks{
|
||||
|
||||
self::register("cave_vines", new CaveVines(new BID(Ids::CAVE_VINES), "Cave Vines", new Info(BreakInfo::instant())));
|
||||
|
||||
self::register("small_dripleaf", new SmallDripleaf(new BID(Ids::SMALL_DRIPLEAF), "Small Dripleaf", new Info(BreakInfo::instant(BlockToolType::SHEARS, toolHarvestLevel: 1))));
|
||||
self::register("small_dripleaf", new SmallDripleaf(new BID(Ids::SMALL_DRIPLEAF), "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1))));
|
||||
self::register("big_dripleaf_head", new BigDripleafHead(new BID(Ids::BIG_DRIPLEAF_HEAD), "Big Dripleaf", new Info(BreakInfo::instant())));
|
||||
self::register("big_dripleaf_stem", new BigDripleafStem(new BID(Ids::BIG_DRIPLEAF_STEM), "Big Dripleaf Stem", new Info(BreakInfo::instant())));
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ final class WoodLikeBlockIdHelper{
|
||||
};
|
||||
}
|
||||
|
||||
public static function getTrapdoorIdentifier(WoodType $treeType) : BlockIdentifier{
|
||||
public static function getTrapdoorIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_TRAPDOOR,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_TRAPDOOR,
|
||||
@ -186,7 +186,7 @@ final class WoodLikeBlockIdHelper{
|
||||
});
|
||||
}
|
||||
|
||||
public static function getButtonIdentifier(WoodType $treeType) : BlockIdentifier{
|
||||
public static function getButtonIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_BUTTON,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_BUTTON,
|
||||
@ -201,7 +201,7 @@ final class WoodLikeBlockIdHelper{
|
||||
});
|
||||
}
|
||||
|
||||
public static function getPressurePlateIdentifier(WoodType $treeType) : BlockIdentifier{
|
||||
public static function getPressurePlateIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_PRESSURE_PLATE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_PRESSURE_PLATE,
|
||||
@ -216,7 +216,7 @@ final class WoodLikeBlockIdHelper{
|
||||
});
|
||||
}
|
||||
|
||||
public static function getDoorIdentifier(WoodType $treeType) : BlockIdentifier{
|
||||
public static function getDoorIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_DOOR,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_DOOR,
|
||||
@ -231,7 +231,7 @@ final class WoodLikeBlockIdHelper{
|
||||
});
|
||||
}
|
||||
|
||||
public static function getFenceGateIdentifier(WoodType $treeType) : BlockIdentifier{
|
||||
public static function getFenceGateIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_FENCE_GATE,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_FENCE_GATE,
|
||||
@ -246,7 +246,7 @@ final class WoodLikeBlockIdHelper{
|
||||
});
|
||||
}
|
||||
|
||||
public static function getStairsIdentifier(WoodType $treeType) : BlockIdentifier{
|
||||
public static function getStairsIdentifier(WoodType $treeType) : BID{
|
||||
return new BID(match($treeType){
|
||||
WoodType::OAK => Ids::OAK_STAIRS,
|
||||
WoodType::SPRUCE => Ids::SPRUCE_STAIRS,
|
||||
|
@ -47,20 +47,20 @@ trait AnimatedBlockInventoryTrait{
|
||||
public function onOpen(Player $who) : void{
|
||||
parent::onOpen($who);
|
||||
|
||||
if($this->getHolder()->isValid() && $this->getViewerCount() === 1){
|
||||
if($this->holder->isValid() && $this->getViewerCount() === 1){
|
||||
//TODO: this crap really shouldn't be managed by the inventory
|
||||
$this->animateBlock(true);
|
||||
$this->getHolder()->getWorld()->addSound($this->getHolder()->add(0.5, 0.5, 0.5), $this->getOpenSound());
|
||||
$this->holder->getWorld()->addSound($this->holder->add(0.5, 0.5, 0.5), $this->getOpenSound());
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function animateBlock(bool $isOpen) : void;
|
||||
|
||||
public function onClose(Player $who) : void{
|
||||
if($this->getHolder()->isValid() && $this->getViewerCount() === 1){
|
||||
if($this->holder->isValid() && $this->getViewerCount() === 1){
|
||||
//TODO: this crap really shouldn't be managed by the inventory
|
||||
$this->animateBlock(false);
|
||||
$this->getHolder()->getWorld()->addSound($this->getHolder()->add(0.5, 0.5, 0.5), $this->getCloseSound());
|
||||
$this->holder->getWorld()->addSound($this->holder->add(0.5, 0.5, 0.5), $this->getCloseSound());
|
||||
}
|
||||
parent::onClose($who);
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ class Chest extends Spawnable implements Container, Nameable{
|
||||
if($pair->doubleInventory !== null){
|
||||
$this->doubleInventory = $pair->doubleInventory;
|
||||
}else{
|
||||
if(($pair->getPosition()->x + ($pair->getPosition()->z << 15)) > ($this->position->x + ($this->position->z << 15))){ //Order them correctly
|
||||
if(($pair->position->x + ($pair->position->z << 15)) > ($this->position->x + ($this->position->z << 15))){ //Order them correctly
|
||||
$this->doubleInventory = $pair->doubleInventory = new DoubleChestInventory($pair->inventory, $this->inventory);
|
||||
}else{
|
||||
$this->doubleInventory = $pair->doubleInventory = new DoubleChestInventory($this->inventory, $pair->inventory);
|
||||
|
120
src/block/utils/CropGrowthHelper.php
Normal file
120
src/block/utils/CropGrowthHelper.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\utils;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Farmland;
|
||||
use function mt_rand;
|
||||
|
||||
final class CropGrowthHelper{
|
||||
|
||||
private const ON_HYDRATED_FARMLAND_BONUS = 3;
|
||||
private const ON_DRY_FARMLAND_BONUS = 1;
|
||||
private const ADJACENT_HYDRATED_FARMLAND_BONUS = 3 / 4;
|
||||
private const ADJACENT_DRY_FARMLAND_BONUS = 1 / 4;
|
||||
|
||||
private const IMPROPER_ARRANGEMENT_DIVISOR = 2;
|
||||
|
||||
private const MIN_LIGHT_LEVEL = 9;
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the speed at which this crop will grow, depending on its surroundings.
|
||||
* The default is once every 26 random ticks.
|
||||
*
|
||||
* Things which influence this include nearby farmland (bonus for hydrated farmland) and the position of other
|
||||
* nearby crops of the same type (nearby crops of the same type will negatively influence growth speed unless
|
||||
* planted in rows and properly spaced apart).
|
||||
*/
|
||||
public static function calculateMultiplier(Block $block) : float{
|
||||
$result = 1;
|
||||
|
||||
$position = $block->getPosition();
|
||||
|
||||
$world = $position->getWorld();
|
||||
$baseX = $position->getFloorX();
|
||||
$baseY = $position->getFloorY();
|
||||
$baseZ = $position->getFloorZ();
|
||||
|
||||
$farmland = $world->getBlockAt($baseX, $baseY - 1, $baseZ);
|
||||
|
||||
if($farmland instanceof Farmland){
|
||||
$result += $farmland->getWetness() > 0 ? self::ON_HYDRATED_FARMLAND_BONUS : self::ON_DRY_FARMLAND_BONUS;
|
||||
}
|
||||
|
||||
$xRow = false;
|
||||
$zRow = false;
|
||||
$improperArrangement = false;
|
||||
|
||||
for($x = -1; $x <= 1; $x++){
|
||||
for($z = -1; $z <= 1; $z++){
|
||||
if($x === 0 && $z === 0){
|
||||
continue;
|
||||
}
|
||||
$nextFarmland = $world->getBlockAt($baseX + $x, $baseY - 1, $baseZ + $z);
|
||||
|
||||
if(!$nextFarmland instanceof Farmland){
|
||||
continue;
|
||||
}
|
||||
|
||||
$result += $nextFarmland->getWetness() > 0 ? self::ADJACENT_HYDRATED_FARMLAND_BONUS : self::ADJACENT_DRY_FARMLAND_BONUS;
|
||||
|
||||
if(!$improperArrangement){
|
||||
$nextCrop = $world->getBlockAt($baseX + $x, $baseY, $baseZ + $z);
|
||||
if($nextCrop->hasSameTypeId($block)){
|
||||
match(0){
|
||||
$x => $zRow ? $improperArrangement = true : $xRow = true,
|
||||
$z => $xRow ? $improperArrangement = true : $zRow = true,
|
||||
default => $improperArrangement = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//crops can be arranged in rows, but the rows must not cross and must be spaced apart by at least one block
|
||||
if($improperArrangement){
|
||||
$result /= self::IMPROPER_ARRANGEMENT_DIVISOR;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function hasEnoughLight(Block $block, int $minLevel = self::MIN_LIGHT_LEVEL) : bool{
|
||||
$position = $block->getPosition();
|
||||
$world = $position->getWorld();
|
||||
|
||||
//crop growth is not affected by time of day since 1.11 or so
|
||||
return $world->getPotentialLightAt($position->x, $position->y, $position->z) >= $minLevel;
|
||||
}
|
||||
|
||||
public static function canGrow(Block $block) : bool{
|
||||
//while it may be tempting to use mt_rand(0, 25) < multiplier, this would make crops grow a bit faster than
|
||||
//vanilla in most cases due to the remainder of 25 / multiplier not being discarded
|
||||
return mt_rand(0, (int) (25 / self::calculateMultiplier($block))) === 0 && self::hasEnoughLight($block);
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ final class BlockStateData{
|
||||
public const CURRENT_VERSION =
|
||||
(1 << 24) | //major
|
||||
(20 << 16) | //minor
|
||||
(50 << 8) | //patch
|
||||
(60 << 8) | //patch
|
||||
(1); //revision
|
||||
|
||||
public const TAG_NAME = "name";
|
||||
|
@ -56,7 +56,6 @@ final class BlockStateNames{
|
||||
public const CHEMISTRY_TABLE_TYPE = "chemistry_table_type";
|
||||
public const CHISEL_TYPE = "chisel_type";
|
||||
public const CLUSTER_COUNT = "cluster_count";
|
||||
public const COLOR = "color";
|
||||
public const COLOR_BIT = "color_bit";
|
||||
public const COMPOSTER_FILL_LEVEL = "composter_fill_level";
|
||||
public const CONDITIONAL_BIT = "conditional_bit";
|
||||
@ -145,6 +144,7 @@ final class BlockStateNames{
|
||||
public const TALL_GRASS_TYPE = "tall_grass_type";
|
||||
public const TOGGLE_BIT = "toggle_bit";
|
||||
public const TORCH_FACING_DIRECTION = "torch_facing_direction";
|
||||
public const TRIAL_SPAWNER_STATE = "trial_spawner_state";
|
||||
public const TRIGGERED_BIT = "triggered_bit";
|
||||
public const TURTLE_EGG_COUNT = "turtle_egg_count";
|
||||
public const TWISTING_VINES_AGE = "twisting_vines_age";
|
||||
|
@ -62,23 +62,6 @@ final class BlockStateStringValues{
|
||||
public const CHISEL_TYPE_LINES = "lines";
|
||||
public const CHISEL_TYPE_SMOOTH = "smooth";
|
||||
|
||||
public const COLOR_BLACK = "black";
|
||||
public const COLOR_BLUE = "blue";
|
||||
public const COLOR_BROWN = "brown";
|
||||
public const COLOR_CYAN = "cyan";
|
||||
public const COLOR_GRAY = "gray";
|
||||
public const COLOR_GREEN = "green";
|
||||
public const COLOR_LIGHT_BLUE = "light_blue";
|
||||
public const COLOR_LIME = "lime";
|
||||
public const COLOR_MAGENTA = "magenta";
|
||||
public const COLOR_ORANGE = "orange";
|
||||
public const COLOR_PINK = "pink";
|
||||
public const COLOR_PURPLE = "purple";
|
||||
public const COLOR_RED = "red";
|
||||
public const COLOR_SILVER = "silver";
|
||||
public const COLOR_WHITE = "white";
|
||||
public const COLOR_YELLOW = "yellow";
|
||||
|
||||
public const CORAL_COLOR_BLUE = "blue";
|
||||
public const CORAL_COLOR_PINK = "pink";
|
||||
public const CORAL_COLOR_PURPLE = "purple";
|
||||
|
@ -517,10 +517,40 @@ final class BlockTypeNames{
|
||||
public const GREEN_WOOL = "minecraft:green_wool";
|
||||
public const GRINDSTONE = "minecraft:grindstone";
|
||||
public const HANGING_ROOTS = "minecraft:hanging_roots";
|
||||
public const HARD_BLACK_STAINED_GLASS = "minecraft:hard_black_stained_glass";
|
||||
public const HARD_BLACK_STAINED_GLASS_PANE = "minecraft:hard_black_stained_glass_pane";
|
||||
public const HARD_BLUE_STAINED_GLASS = "minecraft:hard_blue_stained_glass";
|
||||
public const HARD_BLUE_STAINED_GLASS_PANE = "minecraft:hard_blue_stained_glass_pane";
|
||||
public const HARD_BROWN_STAINED_GLASS = "minecraft:hard_brown_stained_glass";
|
||||
public const HARD_BROWN_STAINED_GLASS_PANE = "minecraft:hard_brown_stained_glass_pane";
|
||||
public const HARD_CYAN_STAINED_GLASS = "minecraft:hard_cyan_stained_glass";
|
||||
public const HARD_CYAN_STAINED_GLASS_PANE = "minecraft:hard_cyan_stained_glass_pane";
|
||||
public const HARD_GLASS = "minecraft:hard_glass";
|
||||
public const HARD_GLASS_PANE = "minecraft:hard_glass_pane";
|
||||
public const HARD_STAINED_GLASS = "minecraft:hard_stained_glass";
|
||||
public const HARD_STAINED_GLASS_PANE = "minecraft:hard_stained_glass_pane";
|
||||
public const HARD_GRAY_STAINED_GLASS = "minecraft:hard_gray_stained_glass";
|
||||
public const HARD_GRAY_STAINED_GLASS_PANE = "minecraft:hard_gray_stained_glass_pane";
|
||||
public const HARD_GREEN_STAINED_GLASS = "minecraft:hard_green_stained_glass";
|
||||
public const HARD_GREEN_STAINED_GLASS_PANE = "minecraft:hard_green_stained_glass_pane";
|
||||
public const HARD_LIGHT_BLUE_STAINED_GLASS = "minecraft:hard_light_blue_stained_glass";
|
||||
public const HARD_LIGHT_BLUE_STAINED_GLASS_PANE = "minecraft:hard_light_blue_stained_glass_pane";
|
||||
public const HARD_LIGHT_GRAY_STAINED_GLASS = "minecraft:hard_light_gray_stained_glass";
|
||||
public const HARD_LIGHT_GRAY_STAINED_GLASS_PANE = "minecraft:hard_light_gray_stained_glass_pane";
|
||||
public const HARD_LIME_STAINED_GLASS = "minecraft:hard_lime_stained_glass";
|
||||
public const HARD_LIME_STAINED_GLASS_PANE = "minecraft:hard_lime_stained_glass_pane";
|
||||
public const HARD_MAGENTA_STAINED_GLASS = "minecraft:hard_magenta_stained_glass";
|
||||
public const HARD_MAGENTA_STAINED_GLASS_PANE = "minecraft:hard_magenta_stained_glass_pane";
|
||||
public const HARD_ORANGE_STAINED_GLASS = "minecraft:hard_orange_stained_glass";
|
||||
public const HARD_ORANGE_STAINED_GLASS_PANE = "minecraft:hard_orange_stained_glass_pane";
|
||||
public const HARD_PINK_STAINED_GLASS = "minecraft:hard_pink_stained_glass";
|
||||
public const HARD_PINK_STAINED_GLASS_PANE = "minecraft:hard_pink_stained_glass_pane";
|
||||
public const HARD_PURPLE_STAINED_GLASS = "minecraft:hard_purple_stained_glass";
|
||||
public const HARD_PURPLE_STAINED_GLASS_PANE = "minecraft:hard_purple_stained_glass_pane";
|
||||
public const HARD_RED_STAINED_GLASS = "minecraft:hard_red_stained_glass";
|
||||
public const HARD_RED_STAINED_GLASS_PANE = "minecraft:hard_red_stained_glass_pane";
|
||||
public const HARD_WHITE_STAINED_GLASS = "minecraft:hard_white_stained_glass";
|
||||
public const HARD_WHITE_STAINED_GLASS_PANE = "minecraft:hard_white_stained_glass_pane";
|
||||
public const HARD_YELLOW_STAINED_GLASS = "minecraft:hard_yellow_stained_glass";
|
||||
public const HARD_YELLOW_STAINED_GLASS_PANE = "minecraft:hard_yellow_stained_glass_pane";
|
||||
public const HARDENED_CLAY = "minecraft:hardened_clay";
|
||||
public const HAY_BLOCK = "minecraft:hay_block";
|
||||
public const HEAVY_WEIGHTED_PRESSURE_PLATE = "minecraft:heavy_weighted_pressure_plate";
|
||||
@ -900,6 +930,7 @@ final class BlockTypeNames{
|
||||
public const TORCHFLOWER_CROP = "minecraft:torchflower_crop";
|
||||
public const TRAPDOOR = "minecraft:trapdoor";
|
||||
public const TRAPPED_CHEST = "minecraft:trapped_chest";
|
||||
public const TRIAL_SPAWNER = "minecraft:trial_spawner";
|
||||
public const TRIP_WIRE = "minecraft:trip_wire";
|
||||
public const TRIPWIRE_HOOK = "minecraft:tripwire_hook";
|
||||
public const TUBE_CORAL = "minecraft:tube_coral";
|
||||
|
@ -315,6 +315,44 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
}
|
||||
|
||||
public function registerFlatColorBlockSerializers() : void{
|
||||
$this->map(Blocks::STAINED_HARDENED_GLASS(), fn(StainedHardenedGlass $block) => Writer::create(match($block->getColor()){
|
||||
DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS,
|
||||
DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS,
|
||||
DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS,
|
||||
DyeColor::CYAN => Ids::HARD_CYAN_STAINED_GLASS,
|
||||
DyeColor::GRAY => Ids::HARD_GRAY_STAINED_GLASS,
|
||||
DyeColor::GREEN => Ids::HARD_GREEN_STAINED_GLASS,
|
||||
DyeColor::LIGHT_BLUE => Ids::HARD_LIGHT_BLUE_STAINED_GLASS,
|
||||
DyeColor::LIGHT_GRAY => Ids::HARD_LIGHT_GRAY_STAINED_GLASS,
|
||||
DyeColor::LIME => Ids::HARD_LIME_STAINED_GLASS,
|
||||
DyeColor::MAGENTA => Ids::HARD_MAGENTA_STAINED_GLASS,
|
||||
DyeColor::ORANGE => Ids::HARD_ORANGE_STAINED_GLASS,
|
||||
DyeColor::PINK => Ids::HARD_PINK_STAINED_GLASS,
|
||||
DyeColor::PURPLE => Ids::HARD_PURPLE_STAINED_GLASS,
|
||||
DyeColor::RED => Ids::HARD_RED_STAINED_GLASS,
|
||||
DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS,
|
||||
DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS,
|
||||
}));
|
||||
|
||||
$this->map(Blocks::STAINED_HARDENED_GLASS_PANE(), fn(StainedHardenedGlassPane $block) => Writer::create(match($block->getColor()){
|
||||
DyeColor::BLACK => Ids::HARD_BLACK_STAINED_GLASS_PANE,
|
||||
DyeColor::BLUE => Ids::HARD_BLUE_STAINED_GLASS_PANE,
|
||||
DyeColor::BROWN => Ids::HARD_BROWN_STAINED_GLASS_PANE,
|
||||
DyeColor::CYAN => Ids::HARD_CYAN_STAINED_GLASS_PANE,
|
||||
DyeColor::GRAY => Ids::HARD_GRAY_STAINED_GLASS_PANE,
|
||||
DyeColor::GREEN => Ids::HARD_GREEN_STAINED_GLASS_PANE,
|
||||
DyeColor::LIGHT_BLUE => Ids::HARD_LIGHT_BLUE_STAINED_GLASS_PANE,
|
||||
DyeColor::LIGHT_GRAY => Ids::HARD_LIGHT_GRAY_STAINED_GLASS_PANE,
|
||||
DyeColor::LIME => Ids::HARD_LIME_STAINED_GLASS_PANE,
|
||||
DyeColor::MAGENTA => Ids::HARD_MAGENTA_STAINED_GLASS_PANE,
|
||||
DyeColor::ORANGE => Ids::HARD_ORANGE_STAINED_GLASS_PANE,
|
||||
DyeColor::PINK => Ids::HARD_PINK_STAINED_GLASS_PANE,
|
||||
DyeColor::PURPLE => Ids::HARD_PURPLE_STAINED_GLASS_PANE,
|
||||
DyeColor::RED => Ids::HARD_RED_STAINED_GLASS_PANE,
|
||||
DyeColor::WHITE => Ids::HARD_WHITE_STAINED_GLASS_PANE,
|
||||
DyeColor::YELLOW => Ids::HARD_YELLOW_STAINED_GLASS_PANE,
|
||||
}));
|
||||
|
||||
$this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{
|
||||
return Writer::create(match($block->getColor()){
|
||||
DyeColor::BLACK => Ids::BLACK_GLAZED_TERRACOTTA,
|
||||
@ -1617,14 +1655,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY);
|
||||
});
|
||||
$this->map(Blocks::SPRUCE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_SPRUCE));
|
||||
$this->map(Blocks::STAINED_HARDENED_GLASS(), function(StainedHardenedGlass $block) : Writer{
|
||||
return Writer::create(Ids::HARD_STAINED_GLASS)
|
||||
->writeColor($block->getColor());
|
||||
});
|
||||
$this->map(Blocks::STAINED_HARDENED_GLASS_PANE(), function(StainedHardenedGlassPane $block) : Writer{
|
||||
return Writer::create(Ids::HARD_STAINED_GLASS_PANE)
|
||||
->writeColor($block->getColor());
|
||||
});
|
||||
$this->map(Blocks::STONECUTTER(), fn(Stonecutter $block) => Writer::create(Ids::STONECUTTER_BLOCK)
|
||||
->writeCardinalHorizontalFacing($block->getFacing()));
|
||||
$this->map(Blocks::STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_DEFAULT));
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\data\bedrock\block\convert;
|
||||
|
||||
use pocketmine\block\utils\BellAttachmentType;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\utils\WallConnectionType;
|
||||
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
|
||||
@ -236,30 +235,6 @@ final class BlockStateReader{
|
||||
};
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public function readColor() : DyeColor{
|
||||
// * color (StringTag) = black, blue, brown, cyan, gray, green, light_blue, lime, magenta, orange, pink, purple, red, silver, white, yellow
|
||||
return match($color = $this->readString(BlockStateNames::COLOR)){
|
||||
StringValues::COLOR_BLACK => DyeColor::BLACK,
|
||||
StringValues::COLOR_BLUE => DyeColor::BLUE,
|
||||
StringValues::COLOR_BROWN => DyeColor::BROWN,
|
||||
StringValues::COLOR_CYAN => DyeColor::CYAN,
|
||||
StringValues::COLOR_GRAY => DyeColor::GRAY,
|
||||
StringValues::COLOR_GREEN => DyeColor::GREEN,
|
||||
StringValues::COLOR_LIGHT_BLUE => DyeColor::LIGHT_BLUE,
|
||||
StringValues::COLOR_LIME => DyeColor::LIME,
|
||||
StringValues::COLOR_MAGENTA => DyeColor::MAGENTA,
|
||||
StringValues::COLOR_ORANGE => DyeColor::ORANGE,
|
||||
StringValues::COLOR_PINK => DyeColor::PINK,
|
||||
StringValues::COLOR_PURPLE => DyeColor::PURPLE,
|
||||
StringValues::COLOR_RED => DyeColor::RED,
|
||||
StringValues::COLOR_SILVER => DyeColor::LIGHT_GRAY,
|
||||
StringValues::COLOR_WHITE => DyeColor::WHITE,
|
||||
StringValues::COLOR_YELLOW => DyeColor::YELLOW,
|
||||
default => throw $this->badValueException(BlockStateNames::COLOR, $color),
|
||||
};
|
||||
}
|
||||
|
||||
/** @throws BlockStateDeserializeException */
|
||||
public function readCoralFacing() : int{
|
||||
return $this->parseFacingValue($this->readInt(BlockStateNames::CORAL_DIRECTION), [
|
||||
|
@ -57,42 +57,42 @@ use pocketmine\math\Facing;
|
||||
|
||||
final class BlockStateSerializerHelper{
|
||||
|
||||
public static function encodeAllSidedLog(Wood $block) : BlockStateWriter{
|
||||
return BlockStateWriter::create(Ids::WOOD)
|
||||
public static function encodeAllSidedLog(Wood $block) : Writer{
|
||||
return Writer::create(Ids::WOOD)
|
||||
->writeBool(BlockStateNames::STRIPPED_BIT, $block->isStripped())
|
||||
->writePillarAxis($block->getAxis())
|
||||
->writeLegacyWoodType($block->getWoodType());
|
||||
}
|
||||
|
||||
public static function encodeButton(Button $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeButton(Button $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeFacingDirection($block->getFacing())
|
||||
->writeBool(BlockStateNames::BUTTON_PRESSED_BIT, $block->isPressed());
|
||||
}
|
||||
|
||||
public static function encodeCandle(Candle $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeCandle(Candle $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(StateNames::LIT, $block->isLit())
|
||||
->writeInt(StateNames::CANDLES, $block->getCount() - 1);
|
||||
}
|
||||
|
||||
public static function encodeChemistryTable(ChemistryTable $block, string $chemistryTableType, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeChemistryTable(ChemistryTable $block, string $chemistryTableType, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeString(BlockStateNames::CHEMISTRY_TABLE_TYPE, $chemistryTableType)
|
||||
->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing()));
|
||||
}
|
||||
|
||||
public static function encodeCrops(Crops $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeCrops(Crops $block, Writer $out) : Writer{
|
||||
return $out->writeInt(BlockStateNames::GROWTH, $block->getAge());
|
||||
}
|
||||
|
||||
public static function encodeColoredTorch(Torch $block, bool $highBit, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeColoredTorch(Torch $block, bool $highBit, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::COLOR_BIT, $highBit)
|
||||
->writeTorchFacing($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeCauldron(string $liquid, int $fillLevel) : BlockStateWriter{
|
||||
public static function encodeCauldron(string $liquid, int $fillLevel) : Writer{
|
||||
return Writer::create(Ids::CAULDRON)
|
||||
->writeString(BlockStateNames::CAULDRON_LIQUID, $liquid)
|
||||
->writeInt(BlockStateNames::FILL_LEVEL, $fillLevel);
|
||||
@ -107,7 +107,7 @@ final class BlockStateSerializerHelper{
|
||||
};
|
||||
}
|
||||
|
||||
public static function encodeDoor(Door $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeDoor(Door $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
|
||||
->writeLegacyHorizontalFacing(Facing::rotateY($block->getFacing(), true))
|
||||
@ -115,111 +115,111 @@ final class BlockStateSerializerHelper{
|
||||
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
|
||||
}
|
||||
|
||||
public static function encodeDoublePlant(DoublePlant $block, string $doublePlantType, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeDoublePlant(DoublePlant $block, string $doublePlantType, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
|
||||
->writeString(BlockStateNames::DOUBLE_PLANT_TYPE, $doublePlantType);
|
||||
}
|
||||
|
||||
public static function encodeFenceGate(FenceGate $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeLegacyHorizontalFacing($block->getFacing())
|
||||
->writeBool(BlockStateNames::IN_WALL_BIT, $block->isInWall())
|
||||
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
|
||||
}
|
||||
|
||||
public static function encodeFloorSign(FloorSign $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation());
|
||||
}
|
||||
|
||||
public static function encodeFurnace(Furnace $block, string $unlitId, string $litId) : BlockStateWriter{
|
||||
return BlockStateWriter::create($block->isLit() ? $litId : $unlitId)
|
||||
public static function encodeFurnace(Furnace $block, string $unlitId, string $litId) : Writer{
|
||||
return Writer::create($block->isLit() ? $litId : $unlitId)
|
||||
->writeCardinalHorizontalFacing($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeItemFrame(ItemFrame $block, string $id) : BlockStateWriter{
|
||||
public static function encodeItemFrame(ItemFrame $block, string $id) : Writer{
|
||||
return Writer::create($id)
|
||||
->writeBool(StateNames::ITEM_FRAME_MAP_BIT, $block->hasMap())
|
||||
->writeBool(StateNames::ITEM_FRAME_PHOTO_BIT, false)
|
||||
->writeFacingDirection($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeLeaves(Leaves $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeLeaves(Leaves $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay())
|
||||
->writeBool(BlockStateNames::UPDATE_BIT, $block->isCheckDecay());
|
||||
}
|
||||
|
||||
public static function encodeLeaves1(Leaves $block, string $type) : BlockStateWriter{
|
||||
return self::encodeLeaves($block, BlockStateWriter::create(Ids::LEAVES)
|
||||
public static function encodeLeaves1(Leaves $block, string $type) : Writer{
|
||||
return self::encodeLeaves($block, Writer::create(Ids::LEAVES)
|
||||
->writeString(BlockStateNames::OLD_LEAF_TYPE, $type));
|
||||
}
|
||||
|
||||
public static function encodeLeaves2(Leaves $block, string $type) : BlockStateWriter{
|
||||
return self::encodeLeaves($block, BlockStateWriter::create(Ids::LEAVES2)
|
||||
public static function encodeLeaves2(Leaves $block, string $type) : Writer{
|
||||
return self::encodeLeaves($block, Writer::create(Ids::LEAVES2)
|
||||
->writeString(BlockStateNames::NEW_LEAF_TYPE, $type));
|
||||
}
|
||||
|
||||
public static function encodeLiquid(Liquid $block, string $stillId, string $flowingId) : BlockStateWriter{
|
||||
return BlockStateWriter::create($block->isStill() ? $stillId : $flowingId)
|
||||
public static function encodeLiquid(Liquid $block, string $stillId, string $flowingId) : Writer{
|
||||
return Writer::create($block->isStill() ? $stillId : $flowingId)
|
||||
->writeInt(BlockStateNames::LIQUID_DEPTH, $block->getDecay() | ($block->isFalling() ? 0x8 : 0));
|
||||
}
|
||||
|
||||
public static function encodeLog(Wood $block, string $unstrippedId, string $strippedId) : BlockStateWriter{
|
||||
public static function encodeLog(Wood $block, string $unstrippedId, string $strippedId) : Writer{
|
||||
$out = $block->isStripped() ?
|
||||
BlockStateWriter::create($strippedId) :
|
||||
BlockStateWriter::create($unstrippedId);
|
||||
Writer::create($strippedId) :
|
||||
Writer::create($unstrippedId);
|
||||
return $out
|
||||
->writePillarAxis($block->getAxis());
|
||||
}
|
||||
|
||||
public static function encodeMushroomBlock(RedMushroomBlock $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeMushroomBlock(RedMushroomBlock $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType()));
|
||||
}
|
||||
|
||||
public static function encodeQuartz(string $type, int $axis) : BlockStateWriter{
|
||||
return BlockStateWriter::create(Ids::QUARTZ_BLOCK)
|
||||
public static function encodeQuartz(string $type, int $axis) : Writer{
|
||||
return Writer::create(Ids::QUARTZ_BLOCK)
|
||||
->writeString(BlockStateNames::CHISEL_TYPE, $type)
|
||||
->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway
|
||||
}
|
||||
|
||||
public static function encodeRedFlower(string $type) : BlockStateWriter{
|
||||
return BlockStateWriter::create(Ids::RED_FLOWER)->writeString(BlockStateNames::FLOWER_TYPE, $type);
|
||||
public static function encodeRedFlower(string $type) : Writer{
|
||||
return Writer::create(Ids::RED_FLOWER)->writeString(BlockStateNames::FLOWER_TYPE, $type);
|
||||
}
|
||||
|
||||
public static function encodeSandstone(string $id, string $type) : BlockStateWriter{
|
||||
return BlockStateWriter::create($id)->writeString(BlockStateNames::SAND_STONE_TYPE, $type);
|
||||
public static function encodeSandstone(string $id, string $type) : Writer{
|
||||
return Writer::create($id)->writeString(BlockStateNames::SAND_STONE_TYPE, $type);
|
||||
}
|
||||
|
||||
public static function encodeSapling(Sapling $block, string $type) : BlockStateWriter{
|
||||
return BlockStateWriter::create(Ids::SAPLING)
|
||||
public static function encodeSapling(Sapling $block, string $type) : Writer{
|
||||
return Writer::create(Ids::SAPLING)
|
||||
->writeBool(BlockStateNames::AGE_BIT, $block->isReady())
|
||||
->writeString(BlockStateNames::SAPLING_TYPE, $type);
|
||||
}
|
||||
|
||||
public static function encodeSimplePressurePlate(SimplePressurePlate $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeSimplePressurePlate(SimplePressurePlate $block, Writer $out) : Writer{
|
||||
//TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation?
|
||||
//best to keep this separate from weighted plates anyway...
|
||||
return $out
|
||||
->writeInt(BlockStateNames::REDSTONE_SIGNAL, $block->isPressed() ? 15 : 0);
|
||||
}
|
||||
|
||||
public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : BlockStateWriter{
|
||||
public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : Writer{
|
||||
$slabType = $block->getSlabType();
|
||||
return BlockStateWriter::create($slabType === SlabType::DOUBLE ? $doubleId : $singleId)
|
||||
return Writer::create($slabType === SlabType::DOUBLE ? $doubleId : $singleId)
|
||||
//this is (intentionally) also written for double slabs (as zero) to maintain bug parity with MCPE
|
||||
->writeSlabPosition($slabType === SlabType::DOUBLE ? SlabType::BOTTOM : $slabType);
|
||||
}
|
||||
|
||||
public static function encodeStairs(Stair $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeStairs(Stair $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown())
|
||||
->writeWeirdoHorizontalFacing($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeStem(Stem $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeStem(Stem $block, Writer $out) : Writer{
|
||||
//In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the
|
||||
//most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which
|
||||
//is absurd, and I refuse to make our API similarly absurd.
|
||||
@ -228,40 +228,40 @@ final class BlockStateSerializerHelper{
|
||||
->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing);
|
||||
}
|
||||
|
||||
public static function encodeStoneBricks(string $type) : BlockStateWriter{
|
||||
return BlockStateWriter::create(Ids::STONEBRICK)
|
||||
public static function encodeStoneBricks(string $type) : Writer{
|
||||
return Writer::create(Ids::STONEBRICK)
|
||||
->writeString(BlockStateNames::STONE_BRICK_TYPE, $type);
|
||||
}
|
||||
|
||||
private static function encodeStoneSlab(Slab $block, string $singleId, string $doubleId, string $typeKey, string $typeValue) : BlockStateWriter{
|
||||
private static function encodeStoneSlab(Slab $block, string $singleId, string $doubleId, string $typeKey, string $typeValue) : Writer{
|
||||
return self::encodeSlab($block, $singleId, $doubleId)
|
||||
->writeString($typeKey, $typeValue);
|
||||
}
|
||||
|
||||
public static function encodeStoneSlab1(Slab $block, string $typeValue) : BlockStateWriter{
|
||||
public static function encodeStoneSlab1(Slab $block, string $typeValue) : Writer{
|
||||
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB, Ids::DOUBLE_STONE_BLOCK_SLAB, BlockStateNames::STONE_SLAB_TYPE, $typeValue);
|
||||
}
|
||||
|
||||
public static function encodeStoneSlab2(Slab $block, string $typeValue) : BlockStateWriter{
|
||||
public static function encodeStoneSlab2(Slab $block, string $typeValue) : Writer{
|
||||
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB2, Ids::DOUBLE_STONE_BLOCK_SLAB2, BlockStateNames::STONE_SLAB_TYPE_2, $typeValue);
|
||||
}
|
||||
|
||||
public static function encodeStoneSlab3(Slab $block, string $typeValue) : BlockStateWriter{
|
||||
public static function encodeStoneSlab3(Slab $block, string $typeValue) : Writer{
|
||||
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB3, Ids::DOUBLE_STONE_BLOCK_SLAB3, BlockStateNames::STONE_SLAB_TYPE_3, $typeValue);
|
||||
}
|
||||
|
||||
public static function encodeStoneSlab4(Slab $block, string $typeValue) : BlockStateWriter{
|
||||
public static function encodeStoneSlab4(Slab $block, string $typeValue) : Writer{
|
||||
return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB4, Ids::DOUBLE_STONE_BLOCK_SLAB4, BlockStateNames::STONE_SLAB_TYPE_4, $typeValue);
|
||||
}
|
||||
|
||||
public static function encodeTrapdoor(Trapdoor $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->write5MinusHorizontalFacing($block->getFacing())
|
||||
->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isTop())
|
||||
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
|
||||
}
|
||||
|
||||
public static function encodeWall(Wall $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeWall(Wall $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeBool(BlockStateNames::WALL_POST_BIT, $block->isPost())
|
||||
->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_EAST, $block->getConnection(Facing::EAST))
|
||||
@ -270,17 +270,17 @@ final class BlockStateSerializerHelper{
|
||||
->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST));
|
||||
}
|
||||
|
||||
public static function encodeLegacyWall(Wall $block, string $type) : BlockStateWriter{
|
||||
return self::encodeWall($block, BlockStateWriter::create(Ids::COBBLESTONE_WALL))
|
||||
public static function encodeLegacyWall(Wall $block, string $type) : Writer{
|
||||
return self::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL))
|
||||
->writeString(BlockStateNames::WALL_BLOCK_TYPE, $type);
|
||||
}
|
||||
|
||||
public static function encodeWallSign(WallSign $block, BlockStateWriter $out) : BlockStateWriter{
|
||||
public static function encodeWallSign(WallSign $block, Writer $out) : Writer{
|
||||
return $out
|
||||
->writeHorizontalFacing($block->getFacing());
|
||||
}
|
||||
|
||||
public static function encodeWoodenSlab(Slab $block, string $typeValue) : BlockStateWriter{
|
||||
public static function encodeWoodenSlab(Slab $block, string $typeValue) : Writer{
|
||||
return self::encodeSlab($block, Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB)
|
||||
->writeString(BlockStateNames::WOOD_TYPE, $typeValue);
|
||||
}
|
||||
|
@ -185,6 +185,48 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
}
|
||||
|
||||
private function registerFlatColorBlockDeserializers() : void{
|
||||
foreach([
|
||||
Ids::HARD_BLACK_STAINED_GLASS => DyeColor::BLACK,
|
||||
Ids::HARD_BLUE_STAINED_GLASS => DyeColor::BLUE,
|
||||
Ids::HARD_BROWN_STAINED_GLASS => DyeColor::BROWN,
|
||||
Ids::HARD_CYAN_STAINED_GLASS => DyeColor::CYAN,
|
||||
Ids::HARD_GRAY_STAINED_GLASS => DyeColor::GRAY,
|
||||
Ids::HARD_GREEN_STAINED_GLASS => DyeColor::GREEN,
|
||||
Ids::HARD_LIGHT_BLUE_STAINED_GLASS => DyeColor::LIGHT_BLUE,
|
||||
Ids::HARD_LIGHT_GRAY_STAINED_GLASS => DyeColor::LIGHT_GRAY,
|
||||
Ids::HARD_LIME_STAINED_GLASS => DyeColor::LIME,
|
||||
Ids::HARD_MAGENTA_STAINED_GLASS => DyeColor::MAGENTA,
|
||||
Ids::HARD_ORANGE_STAINED_GLASS => DyeColor::ORANGE,
|
||||
Ids::HARD_PINK_STAINED_GLASS => DyeColor::PINK,
|
||||
Ids::HARD_PURPLE_STAINED_GLASS => DyeColor::PURPLE,
|
||||
Ids::HARD_RED_STAINED_GLASS => DyeColor::RED,
|
||||
Ids::HARD_WHITE_STAINED_GLASS => DyeColor::WHITE,
|
||||
Ids::HARD_YELLOW_STAINED_GLASS => DyeColor::YELLOW,
|
||||
] as $id => $color){
|
||||
$this->map($id, fn(Reader $in) => Blocks::STAINED_HARDENED_GLASS()->setColor($color));
|
||||
}
|
||||
|
||||
foreach([
|
||||
Ids::HARD_BLACK_STAINED_GLASS_PANE => DyeColor::BLACK,
|
||||
Ids::HARD_BLUE_STAINED_GLASS_PANE => DyeColor::BLUE,
|
||||
Ids::HARD_BROWN_STAINED_GLASS_PANE => DyeColor::BROWN,
|
||||
Ids::HARD_CYAN_STAINED_GLASS_PANE => DyeColor::CYAN,
|
||||
Ids::HARD_GRAY_STAINED_GLASS_PANE => DyeColor::GRAY,
|
||||
Ids::HARD_GREEN_STAINED_GLASS_PANE => DyeColor::GREEN,
|
||||
Ids::HARD_LIGHT_BLUE_STAINED_GLASS_PANE => DyeColor::LIGHT_BLUE,
|
||||
Ids::HARD_LIGHT_GRAY_STAINED_GLASS_PANE => DyeColor::LIGHT_GRAY,
|
||||
Ids::HARD_LIME_STAINED_GLASS_PANE => DyeColor::LIME,
|
||||
Ids::HARD_MAGENTA_STAINED_GLASS_PANE => DyeColor::MAGENTA,
|
||||
Ids::HARD_ORANGE_STAINED_GLASS_PANE => DyeColor::ORANGE,
|
||||
Ids::HARD_PINK_STAINED_GLASS_PANE => DyeColor::PINK,
|
||||
Ids::HARD_PURPLE_STAINED_GLASS_PANE => DyeColor::PURPLE,
|
||||
Ids::HARD_RED_STAINED_GLASS_PANE => DyeColor::RED,
|
||||
Ids::HARD_WHITE_STAINED_GLASS_PANE => DyeColor::WHITE,
|
||||
Ids::HARD_YELLOW_STAINED_GLASS_PANE => DyeColor::YELLOW,
|
||||
] as $id => $color){
|
||||
$this->map($id, fn(Reader $in) => Blocks::STAINED_HARDENED_GLASS_PANE()->setColor($color));
|
||||
}
|
||||
|
||||
foreach([
|
||||
Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK,
|
||||
Ids::BLUE_GLAZED_TERRACOTTA => DyeColor::BLUE,
|
||||
@ -1159,14 +1201,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5));
|
||||
});
|
||||
$this->mapStairs(Ids::GRANITE_STAIRS, fn() => Blocks::GRANITE_STAIRS());
|
||||
$this->map(Ids::HARD_STAINED_GLASS, function(Reader $in) : Block{
|
||||
return Blocks::STAINED_HARDENED_GLASS()
|
||||
->setColor($in->readColor());
|
||||
});
|
||||
$this->map(Ids::HARD_STAINED_GLASS_PANE, function(Reader $in) : Block{
|
||||
return Blocks::STAINED_HARDENED_GLASS_PANE()
|
||||
->setColor($in->readColor());
|
||||
});
|
||||
$this->map(Ids::HAY_BLOCK, function(Reader $in) : Block{
|
||||
$in->ignored(StateNames::DEPRECATED);
|
||||
return Blocks::HAY_BALE()->setAxis($in->readPillarAxis());
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\data\bedrock\block\convert;
|
||||
|
||||
use pocketmine\block\utils\BellAttachmentType;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\utils\WallConnectionType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
@ -193,29 +192,6 @@ final class BlockStateWriter{
|
||||
});
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function writeColor(DyeColor $color) : self{
|
||||
$this->writeString(BlockStateNames::COLOR, match($color){
|
||||
DyeColor::BLACK => StringValues::COLOR_BLACK,
|
||||
DyeColor::BLUE => StringValues::COLOR_BLUE,
|
||||
DyeColor::BROWN => StringValues::COLOR_BROWN,
|
||||
DyeColor::CYAN => StringValues::COLOR_CYAN,
|
||||
DyeColor::GRAY => StringValues::COLOR_GRAY,
|
||||
DyeColor::GREEN => StringValues::COLOR_GREEN,
|
||||
DyeColor::LIGHT_BLUE => StringValues::COLOR_LIGHT_BLUE,
|
||||
DyeColor::LIGHT_GRAY => StringValues::COLOR_SILVER,
|
||||
DyeColor::LIME => StringValues::COLOR_LIME,
|
||||
DyeColor::MAGENTA => StringValues::COLOR_MAGENTA,
|
||||
DyeColor::ORANGE => StringValues::COLOR_ORANGE,
|
||||
DyeColor::PINK => StringValues::COLOR_PINK,
|
||||
DyeColor::PURPLE => StringValues::COLOR_PURPLE,
|
||||
DyeColor::RED => StringValues::COLOR_RED,
|
||||
DyeColor::WHITE => StringValues::COLOR_WHITE,
|
||||
DyeColor::YELLOW => StringValues::COLOR_YELLOW,
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function writeCoralFacing(int $value) : self{
|
||||
$this->writeInt(BlockStateNames::CORAL_DIRECTION, match($value){
|
||||
|
@ -389,6 +389,9 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
}
|
||||
|
||||
$jsonMapper = new \JsonMapper();
|
||||
$jsonMapper->bExceptionOnMissingData = true;
|
||||
$jsonMapper->bExceptionOnUndefinedProperty = true;
|
||||
$jsonMapper->bStrictObjectTypeChecking = true;
|
||||
try{
|
||||
$model = $jsonMapper->map($json, new BlockStateUpgradeSchemaModel());
|
||||
}catch(\JsonMapper_Exception $e){
|
||||
|
@ -147,7 +147,6 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Block(Ids::JUNGLE_DOOR, Blocks::JUNGLE_DOOR());
|
||||
$this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR());
|
||||
$this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART());
|
||||
$this->map1to1Block(Ids::PITCHER_POD, Blocks::PITCHER_CROP());
|
||||
$this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER());
|
||||
$this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR());
|
||||
$this->map1to1Block(Ids::SUGAR_CANE, Blocks::SUGARCANE());
|
||||
@ -193,6 +192,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::CLAY_BALL, Items::CLAY());
|
||||
$this->map1to1Item(Ids::CLOCK, Items::CLOCK());
|
||||
$this->map1to1Item(Ids::COAL, Items::COAL());
|
||||
$this->map1to1Item(Ids::COAST_ARMOR_TRIM_SMITHING_TEMPLATE, Items::COAST_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::COCOA_BEANS, Items::COCOA_BEANS());
|
||||
$this->map1to1Item(Ids::COD, Items::RAW_FISH());
|
||||
$this->map1to1Item(Ids::COMPASS, Items::COMPASS());
|
||||
@ -221,6 +221,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::DISC_FRAGMENT_5, Items::DISC_FRAGMENT_5());
|
||||
$this->map1to1Item(Ids::DRAGON_BREATH, Items::DRAGON_BREATH());
|
||||
$this->map1to1Item(Ids::DRIED_KELP, Items::DRIED_KELP());
|
||||
$this->map1to1Item(Ids::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::ECHO_SHARD, Items::ECHO_SHARD());
|
||||
$this->map1to1Item(Ids::EGG, Items::EGG());
|
||||
$this->map1to1Item(Ids::EMERALD, Items::EMERALD());
|
||||
@ -228,6 +229,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE());
|
||||
$this->map1to1Item(Ids::ENDER_PEARL, Items::ENDER_PEARL());
|
||||
$this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE());
|
||||
$this->map1to1Item(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::FEATHER, Items::FEATHER());
|
||||
$this->map1to1Item(Ids::FERMENTED_SPIDER_EYE, Items::FERMENTED_SPIDER_EYE());
|
||||
$this->map1to1Item(Ids::FIRE_CHARGE, Items::FIRE_CHARGE());
|
||||
@ -257,6 +259,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::HEART_OF_THE_SEA, Items::HEART_OF_THE_SEA());
|
||||
$this->map1to1Item(Ids::HONEY_BOTTLE, Items::HONEY_BOTTLE());
|
||||
$this->map1to1Item(Ids::HONEYCOMB, Items::HONEYCOMB());
|
||||
$this->map1to1Item(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE, Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::INK_SAC, Items::INK_SAC());
|
||||
$this->map1to1Item(Ids::IRON_AXE, Items::IRON_AXE());
|
||||
$this->map1to1Item(Ids::IRON_BOOTS, Items::IRON_BOOTS());
|
||||
@ -316,11 +319,13 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::NETHERITE_SCRAP, Items::NETHERITE_SCRAP());
|
||||
$this->map1to1Item(Ids::NETHERITE_SHOVEL, Items::NETHERITE_SHOVEL());
|
||||
$this->map1to1Item(Ids::NETHERITE_SWORD, Items::NETHERITE_SWORD());
|
||||
$this->map1to1Item(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items::NETHERITE_UPGRADE_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::OAK_BOAT, Items::OAK_BOAT());
|
||||
$this->map1to1Item(Ids::OAK_SIGN, Items::OAK_SIGN());
|
||||
$this->map1to1Item(Ids::PAINTING, Items::PAINTING());
|
||||
$this->map1to1Item(Ids::PAPER, Items::PAPER());
|
||||
$this->map1to1Item(Ids::PHANTOM_MEMBRANE, Items::PHANTOM_MEMBRANE());
|
||||
$this->map1to1Item(Ids::PITCHER_POD, Items::PITCHER_POD());
|
||||
$this->map1to1Item(Ids::POISONOUS_POTATO, Items::POISONOUS_POTATO());
|
||||
$this->map1to1Item(Ids::POPPED_CHORUS_FRUIT, Items::POPPED_CHORUS_FRUIT());
|
||||
$this->map1to1Item(Ids::PORKCHOP, Items::RAW_PORKCHOP());
|
||||
@ -335,18 +340,25 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::RABBIT_FOOT, Items::RABBIT_FOOT());
|
||||
$this->map1to1Item(Ids::RABBIT_HIDE, Items::RABBIT_HIDE());
|
||||
$this->map1to1Item(Ids::RABBIT_STEW, Items::RABBIT_STEW());
|
||||
$this->map1to1Item(Ids::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::RAW_COPPER, Items::RAW_COPPER());
|
||||
$this->map1to1Item(Ids::RAW_GOLD, Items::RAW_GOLD());
|
||||
$this->map1to1Item(Ids::RAW_IRON, Items::RAW_IRON());
|
||||
$this->map1to1Item(Ids::REDSTONE, Items::REDSTONE_DUST());
|
||||
$this->map1to1Item(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::ROTTEN_FLESH, Items::ROTTEN_FLESH());
|
||||
$this->map1to1Item(Ids::SALMON, Items::RAW_SALMON());
|
||||
$this->map1to1Item(Ids::SCUTE, Items::SCUTE());
|
||||
$this->map1to1Item(Ids::TURTLE_SCUTE, Items::SCUTE());
|
||||
$this->map1to1Item(Ids::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::SHEARS, Items::SHEARS());
|
||||
$this->map1to1Item(Ids::SHULKER_SHELL, Items::SHULKER_SHELL());
|
||||
$this->map1to1Item(Ids::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::SLIME_BALL, Items::SLIMEBALL());
|
||||
$this->map1to1Item(Ids::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::SNOWBALL, Items::SNOWBALL());
|
||||
$this->map1to1Item(Ids::SPIDER_EYE, Items::SPIDER_EYE());
|
||||
$this->map1to1Item(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::SPRUCE_BOAT, Items::SPRUCE_BOAT());
|
||||
$this->map1to1Item(Ids::SPRUCE_SIGN, Items::SPRUCE_SIGN());
|
||||
$this->map1to1Item(Ids::SPYGLASS, Items::SPYGLASS());
|
||||
@ -361,14 +373,19 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Item(Ids::SUGAR, Items::SUGAR());
|
||||
$this->map1to1Item(Ids::SWEET_BERRIES, Items::SWEET_BERRIES());
|
||||
$this->map1to1Item(Ids::TORCHFLOWER_SEEDS, Items::TORCHFLOWER_SEEDS());
|
||||
$this->map1to1Item(Ids::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::TOTEM_OF_UNDYING, Items::TOTEM());
|
||||
$this->map1to1Item(Ids::TROPICAL_FISH, Items::CLOWNFISH());
|
||||
$this->map1to1Item(Ids::TURTLE_HELMET, Items::TURTLE_HELMET());
|
||||
$this->map1to1Item(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE, Items::VEX_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::VILLAGER_SPAWN_EGG, Items::VILLAGER_SPAWN_EGG());
|
||||
$this->map1to1Item(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WARD_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::WARPED_SIGN, Items::WARPED_SIGN());
|
||||
$this->map1to1Item(Ids::WATER_BUCKET, Items::WATER_BUCKET());
|
||||
$this->map1to1Item(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::WHEAT, Items::WHEAT());
|
||||
$this->map1to1Item(Ids::WHEAT_SEEDS, Items::WHEAT_SEEDS());
|
||||
$this->map1to1Item(Ids::WILD_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WILD_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$this->map1to1Item(Ids::WOODEN_AXE, Items::WOODEN_AXE());
|
||||
$this->map1to1Item(Ids::WOODEN_HOE, Items::WOODEN_HOE());
|
||||
$this->map1to1Item(Ids::WOODEN_PICKAXE, Items::WOODEN_PICKAXE());
|
||||
|
@ -38,6 +38,8 @@ final class ItemTypeNames{
|
||||
public const ANGLER_POTTERY_SHERD = "minecraft:angler_pottery_sherd";
|
||||
public const APPLE = "minecraft:apple";
|
||||
public const ARCHER_POTTERY_SHERD = "minecraft:archer_pottery_sherd";
|
||||
public const ARMADILLO_SCUTE = "minecraft:armadillo_scute";
|
||||
public const ARMADILLO_SPAWN_EGG = "minecraft:armadillo_spawn_egg";
|
||||
public const ARMOR_STAND = "minecraft:armor_stand";
|
||||
public const ARMS_UP_POTTERY_SHERD = "minecraft:arms_up_pottery_sherd";
|
||||
public const ARROW = "minecraft:arrow";
|
||||
@ -79,6 +81,7 @@ final class ItemTypeNames{
|
||||
public const BOW = "minecraft:bow";
|
||||
public const BOWL = "minecraft:bowl";
|
||||
public const BREAD = "minecraft:bread";
|
||||
public const BREEZE_SPAWN_EGG = "minecraft:breeze_spawn_egg";
|
||||
public const BREWER_POTTERY_SHERD = "minecraft:brewer_pottery_sherd";
|
||||
public const BREWING_STAND = "minecraft:brewing_stand";
|
||||
public const BRICK = "minecraft:brick";
|
||||
@ -238,6 +241,8 @@ final class ItemTypeNames{
|
||||
public const GREEN_DYE = "minecraft:green_dye";
|
||||
public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg";
|
||||
public const GUNPOWDER = "minecraft:gunpowder";
|
||||
public const HARD_STAINED_GLASS = "minecraft:hard_stained_glass";
|
||||
public const HARD_STAINED_GLASS_PANE = "minecraft:hard_stained_glass_pane";
|
||||
public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea";
|
||||
public const HEART_POTTERY_SHERD = "minecraft:heart_pottery_sherd";
|
||||
public const HEARTBREAK_POTTERY_SHERD = "minecraft:heartbreak_pottery_sherd";
|
||||
@ -404,7 +409,6 @@ final class ItemTypeNames{
|
||||
public const SALMON = "minecraft:salmon";
|
||||
public const SALMON_BUCKET = "minecraft:salmon_bucket";
|
||||
public const SALMON_SPAWN_EGG = "minecraft:salmon_spawn_egg";
|
||||
public const SCUTE = "minecraft:scute";
|
||||
public const SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:sentry_armor_trim_smithing_template";
|
||||
public const SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:shaper_armor_trim_smithing_template";
|
||||
public const SHEAF_POTTERY_SHERD = "minecraft:sheaf_pottery_sherd";
|
||||
@ -466,11 +470,13 @@ final class ItemTypeNames{
|
||||
public const TORCHFLOWER_SEEDS = "minecraft:torchflower_seeds";
|
||||
public const TOTEM_OF_UNDYING = "minecraft:totem_of_undying";
|
||||
public const TRADER_LLAMA_SPAWN_EGG = "minecraft:trader_llama_spawn_egg";
|
||||
public const TRIAL_KEY = "minecraft:trial_key";
|
||||
public const TRIDENT = "minecraft:trident";
|
||||
public const TROPICAL_FISH = "minecraft:tropical_fish";
|
||||
public const TROPICAL_FISH_BUCKET = "minecraft:tropical_fish_bucket";
|
||||
public const TROPICAL_FISH_SPAWN_EGG = "minecraft:tropical_fish_spawn_egg";
|
||||
public const TURTLE_HELMET = "minecraft:turtle_helmet";
|
||||
public const TURTLE_SCUTE = "minecraft:turtle_scute";
|
||||
public const TURTLE_SPAWN_EGG = "minecraft:turtle_spawn_egg";
|
||||
public const VEX_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:vex_armor_trim_smithing_template";
|
||||
public const VEX_SPAWN_EGG = "minecraft:vex_spawn_egg";
|
||||
@ -497,6 +503,7 @@ final class ItemTypeNames{
|
||||
public const WITCH_SPAWN_EGG = "minecraft:witch_spawn_egg";
|
||||
public const WITHER_SKELETON_SPAWN_EGG = "minecraft:wither_skeleton_spawn_egg";
|
||||
public const WITHER_SPAWN_EGG = "minecraft:wither_spawn_egg";
|
||||
public const WOLF_ARMOR = "minecraft:wolf_armor";
|
||||
public const WOLF_SPAWN_EGG = "minecraft:wolf_spawn_egg";
|
||||
public const WOODEN_AXE = "minecraft:wooden_axe";
|
||||
public const WOODEN_DOOR = "minecraft:wooden_door";
|
||||
|
@ -88,6 +88,9 @@ final class ItemIdMetaUpgradeSchemaUtils{
|
||||
}
|
||||
|
||||
$jsonMapper = new \JsonMapper();
|
||||
$jsonMapper->bExceptionOnMissingData = true;
|
||||
$jsonMapper->bExceptionOnUndefinedProperty = true;
|
||||
$jsonMapper->bStrictObjectTypeChecking = true;
|
||||
try{
|
||||
$model = $jsonMapper->map($json, new ItemIdMetaUpgradeSchemaModel());
|
||||
}catch(\JsonMapper_Exception $e){
|
||||
|
@ -173,8 +173,9 @@ abstract class Projectile extends Entity{
|
||||
$entityHit = null;
|
||||
$hitResult = null;
|
||||
|
||||
$world = $this->getWorld();
|
||||
foreach(VoxelRayTrace::betweenPoints($start, $end) as $vector3){
|
||||
$block = $this->getWorld()->getBlockAt($vector3->x, $vector3->y, $vector3->z);
|
||||
$block = $world->getBlockAt($vector3->x, $vector3->y, $vector3->z);
|
||||
|
||||
$blockHitResult = $this->calculateInterceptWithBlock($block, $start, $end);
|
||||
if($blockHitResult !== null){
|
||||
@ -188,7 +189,7 @@ abstract class Projectile extends Entity{
|
||||
$entityDistance = PHP_INT_MAX;
|
||||
|
||||
$newDiff = $end->subtractVector($start);
|
||||
foreach($this->getWorld()->getCollidingEntities($this->boundingBox->addCoord($newDiff->x, $newDiff->y, $newDiff->z)->expand(1, 1, 1), $this) as $entity){
|
||||
foreach($world->getCollidingEntities($this->boundingBox->addCoord($newDiff->x, $newDiff->y, $newDiff->z)->expand(1, 1, 1), $this) as $entity){
|
||||
if($entity->getId() === $this->getOwningEntityId() && $this->ticksLived < 5){
|
||||
continue;
|
||||
}
|
||||
@ -256,7 +257,7 @@ abstract class Projectile extends Entity{
|
||||
);
|
||||
}
|
||||
|
||||
$this->getWorld()->onEntityMoved($this);
|
||||
$world->onEntityMoved($this);
|
||||
$this->checkBlockIntersections();
|
||||
|
||||
Timings::$projectileMove->stopTiming();
|
||||
|
@ -31,8 +31,9 @@ class Snowball extends Throwable{
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::SNOWBALL; }
|
||||
|
||||
protected function onHit(ProjectileHitEvent $event) : void{
|
||||
$world = $this->getWorld();
|
||||
for($i = 0; $i < 6; ++$i){
|
||||
$this->getWorld()->addParticle($this->location, new SnowballPoofParticle());
|
||||
$world->addParticle($this->location, new SnowballPoofParticle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockTypeIds;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
|
||||
/**
|
||||
@ -58,4 +59,12 @@ final class ItemBlock extends Item{
|
||||
public function getMaxStackSize() : int{
|
||||
return $this->block->getMaxStackSize();
|
||||
}
|
||||
|
||||
public function isNull() : bool{
|
||||
//TODO: we really shouldn't need to treat air as a special case here
|
||||
//this is needed because the "null" empty slot item is represented by an air block, but there's no real reason
|
||||
//why air should be needed at all. A separate special item type (or actual null) should be used instead, but
|
||||
//this would cause a lot of BC breaks, so we can't do it yet.
|
||||
return parent::isNull() || $this->block->getTypeId() === BlockTypeIds::AIR;
|
||||
}
|
||||
}
|
||||
|
@ -305,8 +305,26 @@ final class ItemTypeIds{
|
||||
public const CHERRY_SIGN = 20266;
|
||||
public const ENCHANTED_BOOK = 20267;
|
||||
public const TORCHFLOWER_SEEDS = 20268;
|
||||
public const NETHERITE_UPGRADE_SMITHING_TEMPLATE = 20269;
|
||||
public const SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE = 20270;
|
||||
public const VEX_ARMOR_TRIM_SMITHING_TEMPLATE = 20271;
|
||||
public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = 20272;
|
||||
public const COAST_ARMOR_TRIM_SMITHING_TEMPLATE = 20273;
|
||||
public const DUNE_ARMOR_TRIM_SMITHING_TEMPLATE = 20274;
|
||||
public const WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE = 20275;
|
||||
public const RAISER_ARMOR_TRIM_SMITHING_TEMPLATE = 20276;
|
||||
public const SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE = 20277;
|
||||
public const HOST_ARMOR_TRIM_SMITHING_TEMPLATE = 20278;
|
||||
public const WARD_ARMOR_TRIM_SMITHING_TEMPLATE = 20279;
|
||||
public const SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE = 20280;
|
||||
public const TIDE_ARMOR_TRIM_SMITHING_TEMPLATE = 20281;
|
||||
public const SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE = 20282;
|
||||
public const RIB_ARMOR_TRIM_SMITHING_TEMPLATE = 20283;
|
||||
public const EYE_ARMOR_TRIM_SMITHING_TEMPLATE = 20284;
|
||||
public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = 20285;
|
||||
public const PITCHER_POD = 20286;
|
||||
|
||||
public const FIRST_UNUSED_ITEM_ID = 20269;
|
||||
public const FIRST_UNUSED_ITEM_ID = 20287;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;
|
||||
|
||||
|
34
src/item/PitcherPod.php
Normal file
34
src/item/PitcherPod.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
|
||||
final class PitcherPod extends Item{
|
||||
|
||||
public function getBlock(?int $clickedFace = null) : Block{
|
||||
return VanillaBlocks::PITCHER_CROP();
|
||||
}
|
||||
}
|
@ -869,7 +869,6 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP());
|
||||
$result->registerBlock("piglin_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PIGLIN));
|
||||
$result->registerBlock("pitcher_plant", fn() => Blocks::PITCHER_PLANT());
|
||||
$result->registerBlock("pitcher_pod", fn() => Blocks::PITCHER_CROP());
|
||||
$result->registerBlock("plank", fn() => Blocks::OAK_PLANKS());
|
||||
$result->registerBlock("planks", fn() => Blocks::OAK_PLANKS());
|
||||
$result->registerBlock("player_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PLAYER));
|
||||
@ -1266,6 +1265,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("clown_fish", fn() => Items::CLOWNFISH());
|
||||
$result->register("clownfish", fn() => Items::CLOWNFISH());
|
||||
$result->register("coal", fn() => Items::COAL());
|
||||
$result->register("coast_armor_trim_smithing_template", fn() => Items::COAST_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("cocoa_beans", fn() => Items::COCOA_BEANS());
|
||||
$result->register("cod", fn() => Items::RAW_FISH());
|
||||
$result->register("compass", fn() => Items::COMPASS());
|
||||
@ -1294,6 +1294,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("disc_fragment_5", fn() => Items::DISC_FRAGMENT_5());
|
||||
$result->register("dragon_breath", fn() => Items::DRAGON_BREATH());
|
||||
$result->register("dried_kelp", fn() => Items::DRIED_KELP());
|
||||
$result->register("dune_armor_trim_smithing_template", fn() => Items::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("dye", fn() => Items::INK_SAC());
|
||||
$result->register("echo_shard", fn() => Items::ECHO_SHARD());
|
||||
$result->register("egg", fn() => Items::EGG());
|
||||
@ -1304,6 +1305,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE());
|
||||
$result->register("ender_pearl", fn() => Items::ENDER_PEARL());
|
||||
$result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE());
|
||||
$result->register("eye_armor_trim_smithing_template", fn() => Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("eye_drops", fn() => Items::MEDICINE()->setType(MedicineType::EYE_DROPS));
|
||||
$result->register("feather", fn() => Items::FEATHER());
|
||||
$result->register("fermented_spider_eye", fn() => Items::FERMENTED_SPIDER_EYE());
|
||||
@ -1345,6 +1347,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("gunpowder", fn() => Items::GUNPOWDER());
|
||||
$result->register("heart_of_the_sea", fn() => Items::HEART_OF_THE_SEA());
|
||||
$result->register("honey_bottle", fn() => Items::HONEY_BOTTLE());
|
||||
$result->register("host_armor_trim_smithing_template", fn() => Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("honeycomb", fn() => Items::HONEYCOMB());
|
||||
$result->register("ink_sac", fn() => Items::INK_SAC());
|
||||
$result->register("iron_axe", fn() => Items::IRON_AXE());
|
||||
@ -1398,10 +1401,12 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("netherite_shovel", fn() => Items::NETHERITE_SHOVEL());
|
||||
$result->register("netherite_sword", fn() => Items::NETHERITE_SWORD());
|
||||
$result->register("netherstar", fn() => Items::NETHER_STAR());
|
||||
$result->register("netherite_upgrade_smithing_template", fn() => Items::NETHERITE_UPGRADE_SMITHING_TEMPLATE());
|
||||
$result->register("oak_boat", fn() => Items::OAK_BOAT());
|
||||
$result->register("painting", fn() => Items::PAINTING());
|
||||
$result->register("paper", fn() => Items::PAPER());
|
||||
$result->register("phantom_membrane", fn() => Items::PHANTOM_MEMBRANE());
|
||||
$result->register("pitcher_pod", fn() => Items::PITCHER_POD());
|
||||
$result->register("poisonous_potato", fn() => Items::POISONOUS_POTATO());
|
||||
$result->register("popped_chorus_fruit", fn() => Items::POPPED_CHORUS_FRUIT());
|
||||
$result->register("porkchop", fn() => Items::RAW_PORKCHOP());
|
||||
@ -1418,6 +1423,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("rabbit_foot", fn() => Items::RABBIT_FOOT());
|
||||
$result->register("rabbit_hide", fn() => Items::RABBIT_HIDE());
|
||||
$result->register("rabbit_stew", fn() => Items::RABBIT_STEW());
|
||||
$result->register("raiser_armor_trim_smithing_template", fn() => Items::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("raw_beef", fn() => Items::RAW_BEEF());
|
||||
$result->register("raw_cod", fn() => Items::RAW_FISH());
|
||||
$result->register("raw_copper", fn() => Items::RAW_COPPER());
|
||||
@ -1446,17 +1452,23 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("record_ward", fn() => Items::RECORD_WARD());
|
||||
$result->register("redstone", fn() => Items::REDSTONE_DUST());
|
||||
$result->register("redstone_dust", fn() => Items::REDSTONE_DUST());
|
||||
$result->register("rib_armor_trim_smithing_template", fn() => Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("rotten_flesh", fn() => Items::ROTTEN_FLESH());
|
||||
$result->register("salmon", fn() => Items::RAW_SALMON());
|
||||
$result->register("scute", fn() => Items::SCUTE());
|
||||
$result->register("sentry_armor_trim_smithing_template", fn() => Items::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("shaper_armor_trim_smithing_template", fn() => Items::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("seeds", fn() => Items::WHEAT_SEEDS());
|
||||
$result->register("shears", fn() => Items::SHEARS());
|
||||
$result->register("shulker_shell", fn() => Items::SHULKER_SHELL());
|
||||
$result->register("silence_armor_trim_smithing_template", fn() => Items::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("slime_ball", fn() => Items::SLIMEBALL());
|
||||
$result->register("snout_armor_trim_smithing_template", fn() => Items::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("slimeball", fn() => Items::SLIMEBALL());
|
||||
$result->register("snowball", fn() => Items::SNOWBALL());
|
||||
$result->register("speckled_melon", fn() => Items::GLISTERING_MELON());
|
||||
$result->register("spider_eye", fn() => Items::SPIDER_EYE());
|
||||
$result->register("spire_armor_trim_smithing_template", fn() => Items::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("splash_potion", fn() => Items::SPLASH_POTION());
|
||||
$result->register("spruce_boat", fn() => Items::SPRUCE_BOAT());
|
||||
$result->register("spyglass", fn() => Items::SPYGLASS());
|
||||
@ -1475,13 +1487,18 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->register("sweet_berries", fn() => Items::SWEET_BERRIES());
|
||||
$result->register("tonic", fn() => Items::MEDICINE()->setType(MedicineType::TONIC));
|
||||
$result->register("torchflower_seeds", fn() => Items::TORCHFLOWER_SEEDS());
|
||||
$result->register("tide_armor_trim_smithing_template", fn() => Items::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("totem", fn() => Items::TOTEM());
|
||||
$result->register("turtle_helmet", fn() => Items::TURTLE_HELMET());
|
||||
$result->register("vex_armor_trim_smithing_template", fn() => Items::VEX_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("turtle_shell_piece", fn() => Items::SCUTE());
|
||||
$result->register("villager_spawn_egg", fn() => Items::VILLAGER_SPAWN_EGG());
|
||||
$result->register("ward_armor_trim_smithing_template", fn() => Items::WARD_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("water_bucket", fn() => Items::WATER_BUCKET());
|
||||
$result->register("wayfinder_armor_trim_smithing_template", fn() => Items::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("wheat", fn() => Items::WHEAT());
|
||||
$result->register("wheat_seeds", fn() => Items::WHEAT_SEEDS());
|
||||
$result->register("wild_armor_trim_smithing_template", fn() => Items::WILD_ARMOR_TRIM_SMITHING_TEMPLATE());
|
||||
$result->register("wooden_axe", fn() => Items::WOODEN_AXE());
|
||||
$result->register("wooden_hoe", fn() => Items::WOODEN_HOE());
|
||||
$result->register("wooden_pickaxe", fn() => Items::WOODEN_PICKAXE());
|
||||
|
@ -121,6 +121,7 @@ use function strtolower;
|
||||
* @method static Clock CLOCK()
|
||||
* @method static Clownfish CLOWNFISH()
|
||||
* @method static Coal COAL()
|
||||
* @method static Item COAST_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static CocoaBeans COCOA_BEANS()
|
||||
* @method static Compass COMPASS()
|
||||
* @method static CookedChicken COOKED_CHICKEN()
|
||||
@ -148,6 +149,7 @@ use function strtolower;
|
||||
* @method static Item DISC_FRAGMENT_5()
|
||||
* @method static Item DRAGON_BREATH()
|
||||
* @method static DriedKelp DRIED_KELP()
|
||||
* @method static Item DUNE_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Dye DYE()
|
||||
* @method static Item ECHO_SHARD()
|
||||
* @method static Egg EGG()
|
||||
@ -156,6 +158,7 @@ use function strtolower;
|
||||
* @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE()
|
||||
* @method static EnderPearl ENDER_PEARL()
|
||||
* @method static ExperienceBottle EXPERIENCE_BOTTLE()
|
||||
* @method static Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Item FEATHER()
|
||||
* @method static Item FERMENTED_SPIDER_EYE()
|
||||
* @method static FireCharge FIRE_CHARGE()
|
||||
@ -185,6 +188,7 @@ use function strtolower;
|
||||
* @method static Item HEART_OF_THE_SEA()
|
||||
* @method static Item HONEYCOMB()
|
||||
* @method static HoneyBottle HONEY_BOTTLE()
|
||||
* @method static Item HOST_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Item INK_SAC()
|
||||
* @method static Axe IRON_AXE()
|
||||
* @method static Armor IRON_BOOTS()
|
||||
@ -227,6 +231,7 @@ use function strtolower;
|
||||
* @method static Item NETHERITE_SCRAP()
|
||||
* @method static Shovel NETHERITE_SHOVEL()
|
||||
* @method static Sword NETHERITE_SWORD()
|
||||
* @method static Item NETHERITE_UPGRADE_SMITHING_TEMPLATE()
|
||||
* @method static Item NETHER_BRICK()
|
||||
* @method static Item NETHER_QUARTZ()
|
||||
* @method static Item NETHER_STAR()
|
||||
@ -235,6 +240,7 @@ use function strtolower;
|
||||
* @method static PaintingItem PAINTING()
|
||||
* @method static Item PAPER()
|
||||
* @method static Item PHANTOM_MEMBRANE()
|
||||
* @method static PitcherPod PITCHER_POD()
|
||||
* @method static PoisonousPotato POISONOUS_POTATO()
|
||||
* @method static Item POPPED_CHORUS_FRUIT()
|
||||
* @method static Potato POTATO()
|
||||
@ -247,6 +253,7 @@ use function strtolower;
|
||||
* @method static Item RABBIT_FOOT()
|
||||
* @method static Item RABBIT_HIDE()
|
||||
* @method static RabbitStew RABBIT_STEW()
|
||||
* @method static Item RAISER_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static RawBeef RAW_BEEF()
|
||||
* @method static RawChicken RAW_CHICKEN()
|
||||
* @method static Item RAW_COPPER()
|
||||
@ -273,13 +280,19 @@ use function strtolower;
|
||||
* @method static Record RECORD_WAIT()
|
||||
* @method static Record RECORD_WARD()
|
||||
* @method static Redstone REDSTONE_DUST()
|
||||
* @method static Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static RottenFlesh ROTTEN_FLESH()
|
||||
* @method static Item SCUTE()
|
||||
* @method static Item SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Item SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Shears SHEARS()
|
||||
* @method static Item SHULKER_SHELL()
|
||||
* @method static Item SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Item SLIMEBALL()
|
||||
* @method static Item SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Snowball SNOWBALL()
|
||||
* @method static SpiderEye SPIDER_EYE()
|
||||
* @method static Item SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static SplashPotion SPLASH_POTION()
|
||||
* @method static Boat SPRUCE_BOAT()
|
||||
* @method static ItemBlockWallOrFloor SPRUCE_SIGN()
|
||||
@ -296,14 +309,19 @@ use function strtolower;
|
||||
* @method static Item SUGAR()
|
||||
* @method static SuspiciousStew SUSPICIOUS_STEW()
|
||||
* @method static SweetBerries SWEET_BERRIES()
|
||||
* @method static Item TIDE_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static TorchflowerSeeds TORCHFLOWER_SEEDS()
|
||||
* @method static Totem TOTEM()
|
||||
* @method static TurtleHelmet TURTLE_HELMET()
|
||||
* @method static Item VEX_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static SpawnEgg VILLAGER_SPAWN_EGG()
|
||||
* @method static Item WARD_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static ItemBlockWallOrFloor WARPED_SIGN()
|
||||
* @method static LiquidBucket WATER_BUCKET()
|
||||
* @method static Item WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Item WHEAT()
|
||||
* @method static WheatSeeds WHEAT_SEEDS()
|
||||
* @method static Item WILD_ARMOR_TRIM_SMITHING_TEMPLATE()
|
||||
* @method static Axe WOODEN_AXE()
|
||||
* @method static Hoe WOODEN_HOE()
|
||||
* @method static Pickaxe WOODEN_PICKAXE()
|
||||
@ -339,6 +357,7 @@ final class VanillaItems{
|
||||
self::registerArmorItems();
|
||||
self::registerSpawnEggs();
|
||||
self::registerTierToolItems();
|
||||
self::registerSmithingTemplates();
|
||||
|
||||
self::register("air", Blocks::AIR()->asItem()->setCount(0));
|
||||
|
||||
@ -485,6 +504,7 @@ final class VanillaItems{
|
||||
self::register("painting", new PaintingItem(new IID(Ids::PAINTING), "Painting"));
|
||||
self::register("paper", new Item(new IID(Ids::PAPER), "Paper"));
|
||||
self::register("phantom_membrane", new Item(new IID(Ids::PHANTOM_MEMBRANE), "Phantom Membrane"));
|
||||
self::register("pitcher_pod", new PitcherPod(new IID(Ids::PITCHER_POD), "Pitcher Pod"));
|
||||
self::register("poisonous_potato", new PoisonousPotato(new IID(Ids::POISONOUS_POTATO), "Poisonous Potato"));
|
||||
self::register("popped_chorus_fruit", new Item(new IID(Ids::POPPED_CHORUS_FRUIT), "Popped Chorus Fruit"));
|
||||
self::register("potato", new Potato(new IID(Ids::POTATO), "Potato"));
|
||||
@ -644,4 +664,24 @@ final class VanillaItems{
|
||||
self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS]));
|
||||
}
|
||||
|
||||
private static function registerSmithingTemplates() : void{
|
||||
self::register("netherite_upgrade_smithing_template", new Item(new IID(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE), "Netherite Upgrade Smithing Template"));
|
||||
self::register("coast_armor_trim_smithing_template", new Item(new IID(Ids::COAST_ARMOR_TRIM_SMITHING_TEMPLATE), "Coast Armor Trim Smithing Template"));
|
||||
self::register("dune_armor_trim_smithing_template", new Item(new IID(Ids::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE), "Dune Armor Trim Smithing Template"));
|
||||
self::register("eye_armor_trim_smithing_template", new Item(new IID(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE), "Eye Armor Trim Smithing Template"));
|
||||
self::register("host_armor_trim_smithing_template", new Item(new IID(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE), "Host Armor Trim Smithing Template"));
|
||||
self::register("raiser_armor_trim_smithing_template", new Item(new IID(Ids::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE), "Raiser Armor Trim Smithing Template"));
|
||||
self::register("rib_armor_trim_smithing_template", new Item(new IID(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE), "Rib Armor Trim Smithing Template"));
|
||||
self::register("sentry_armor_trim_smithing_template", new Item(new IID(Ids::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE), "Sentry Armor Trim Smithing Template"));
|
||||
self::register("shaper_armor_trim_smithing_template", new Item(new IID(Ids::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE), "Shaper Armor Trim Smithing Template"));
|
||||
self::register("silence_armor_trim_smithing_template", new Item(new IID(Ids::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE), "Silence Armor Trim Smithing Template"));
|
||||
self::register("snout_armor_trim_smithing_template", new Item(new IID(Ids::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE), "Snout Armor Trim Smithing Template"));
|
||||
self::register("spire_armor_trim_smithing_template", new Item(new IID(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE), "Spire Armor Trim Smithing Template"));
|
||||
self::register("tide_armor_trim_smithing_template", new Item(new IID(Ids::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE), "Tide Armor Trim Smithing Template"));
|
||||
self::register("vex_armor_trim_smithing_template", new Item(new IID(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE), "Vex Armor Trim Smithing Template"));
|
||||
self::register("ward_armor_trim_smithing_template", new Item(new IID(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE), "Ward Armor Trim Smithing Template"));
|
||||
self::register("wayfinder_armor_trim_smithing_template", new Item(new IID(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE), "Wayfinder Armor Trim Smithing Template"));
|
||||
self::register("wild_armor_trim_smithing_template", new Item(new IID(Ids::WILD_ARMOR_TRIM_SMITHING_TEMPLATE), "Wild Armor Trim Smithing Template"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,12 +30,14 @@ use pocketmine\network\mcpe\protocol\LevelChunkPacket;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\mcpe\protocol\types\ChunkPosition;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\serializer\ChunkSerializer;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\thread\NonThreadSafeValue;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\io\FastChunkSerializer;
|
||||
use function chr;
|
||||
|
||||
class ChunkRequestTask extends AsyncTask{
|
||||
private const TLS_KEY_PROMISE = "promise";
|
||||
@ -43,16 +45,22 @@ class ChunkRequestTask extends AsyncTask{
|
||||
protected string $chunk;
|
||||
protected int $chunkX;
|
||||
protected int $chunkZ;
|
||||
/** @phpstan-var DimensionIds::* */
|
||||
private int $dimensionId;
|
||||
/** @phpstan-var NonThreadSafeValue<Compressor> */
|
||||
protected NonThreadSafeValue $compressor;
|
||||
private string $tiles;
|
||||
|
||||
public function __construct(int $chunkX, int $chunkZ, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor){
|
||||
/**
|
||||
* @phpstan-param DimensionIds::* $dimensionId
|
||||
*/
|
||||
public function __construct(int $chunkX, int $chunkZ, int $dimensionId, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor){
|
||||
$this->compressor = new NonThreadSafeValue($compressor);
|
||||
|
||||
$this->chunk = FastChunkSerializer::serializeTerrain($chunk);
|
||||
$this->chunkX = $chunkX;
|
||||
$this->chunkZ = $chunkZ;
|
||||
$this->dimensionId = $dimensionId;
|
||||
$this->tiles = ChunkSerializer::serializeTiles($chunk);
|
||||
|
||||
$this->storeLocal(self::TLS_KEY_PROMISE, $promise);
|
||||
@ -60,14 +68,18 @@ class ChunkRequestTask extends AsyncTask{
|
||||
|
||||
public function onRun() : void{
|
||||
$chunk = FastChunkSerializer::deserializeTerrain($this->chunk);
|
||||
$subCount = ChunkSerializer::getSubChunkCount($chunk);
|
||||
$dimensionId = $this->dimensionId;
|
||||
|
||||
$subCount = ChunkSerializer::getSubChunkCount($chunk, $dimensionId);
|
||||
$converter = TypeConverter::getInstance();
|
||||
$encoderContext = new PacketSerializerContext($converter->getItemTypeDictionary());
|
||||
$payload = ChunkSerializer::serializeFullChunk($chunk, $converter->getBlockTranslator(), $encoderContext, $this->tiles);
|
||||
$payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter->getBlockTranslator(), $encoderContext, $this->tiles);
|
||||
|
||||
$stream = new BinaryStream();
|
||||
PacketBatch::encodePackets($stream, $encoderContext, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $subCount, false, null, $payload)]);
|
||||
$this->setResult($this->compressor->deserialize()->compress($stream->getBuffer()));
|
||||
PacketBatch::encodePackets($stream, $encoderContext, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]);
|
||||
|
||||
$compressor = $this->compressor->deserialize();
|
||||
$this->setResult(chr($compressor->getNetworkId()) . $compressor->compress($stream->getBuffer()));
|
||||
}
|
||||
|
||||
public function onCompletion() : void{
|
||||
|
@ -86,6 +86,7 @@ use pocketmine\network\mcpe\protocol\types\command\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandOverload;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
|
||||
use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
@ -117,7 +118,9 @@ use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function ord;
|
||||
use function random_bytes;
|
||||
use function str_split;
|
||||
use function strcasecmp;
|
||||
@ -158,8 +161,8 @@ class NetworkSession{
|
||||
private array $sendBuffer = [];
|
||||
|
||||
/**
|
||||
* @var \SplQueue|CompressBatchPromise[]
|
||||
* @phpstan-var \SplQueue<CompressBatchPromise>
|
||||
* @var \SplQueue|CompressBatchPromise[]|string[]
|
||||
* @phpstan-var \SplQueue<CompressBatchPromise|string>
|
||||
*/
|
||||
private \SplQueue $compressedQueue;
|
||||
private bool $forceAsyncCompression = true;
|
||||
@ -235,8 +238,10 @@ class NetworkSession{
|
||||
$this->onPlayerCreated(...),
|
||||
function() : void{
|
||||
//TODO: this should never actually occur... right?
|
||||
$this->logger->error("Failed to create player");
|
||||
$this->disconnectWithError(KnownTranslationFactory::pocketmine_disconnect_error_internal());
|
||||
$this->disconnectWithError(
|
||||
reason: "Failed to create player",
|
||||
disconnectScreenMessage: KnownTranslationFactory::pocketmine_disconnect_error_internal()
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -352,15 +357,27 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen($payload) < 1){
|
||||
throw new PacketHandlingException("No bytes in payload");
|
||||
}
|
||||
|
||||
if($this->enableCompression){
|
||||
Timings::$playerNetworkReceiveDecompress->startTiming();
|
||||
try{
|
||||
$decompressed = $this->compressor->decompress($payload);
|
||||
}catch(DecompressionException $e){
|
||||
$this->logger->debug("Failed to decompress packet: " . base64_encode($payload));
|
||||
throw PacketHandlingException::wrap($e, "Compressed packet batch decode error");
|
||||
}finally{
|
||||
Timings::$playerNetworkReceiveDecompress->stopTiming();
|
||||
$compressionType = ord($payload[0]);
|
||||
$compressed = substr($payload, 1);
|
||||
if($compressionType === CompressionAlgorithm::NONE){
|
||||
$decompressed = $compressed;
|
||||
}elseif($compressionType === $this->compressor->getNetworkId()){
|
||||
try{
|
||||
$decompressed = $this->compressor->decompress($compressed);
|
||||
}catch(DecompressionException $e){
|
||||
$this->logger->debug("Failed to decompress packet: " . base64_encode($compressed));
|
||||
throw PacketHandlingException::wrap($e, "Compressed packet batch decode error");
|
||||
}finally{
|
||||
Timings::$playerNetworkReceiveDecompress->stopTiming();
|
||||
}
|
||||
}else{
|
||||
throw new PacketHandlingException("Packet compressed with unexpected compression type $compressionType");
|
||||
}
|
||||
}else{
|
||||
$decompressed = $payload;
|
||||
@ -525,13 +542,12 @@ class NetworkSession{
|
||||
PacketBatch::encodeRaw($stream, $this->sendBuffer);
|
||||
|
||||
if($this->enableCompression){
|
||||
$promise = $this->server->prepareBatch($stream->getBuffer(), $this->compressor, $syncMode, Timings::$playerNetworkSendCompressSessionBuffer);
|
||||
$batch = $this->server->prepareBatch($stream->getBuffer(), $this->compressor, $syncMode, Timings::$playerNetworkSendCompressSessionBuffer);
|
||||
}else{
|
||||
$promise = new CompressBatchPromise();
|
||||
$promise->resolve($stream->getBuffer());
|
||||
$batch = $stream->getBuffer();
|
||||
}
|
||||
$this->sendBuffer = [];
|
||||
$this->queueCompressedNoBufferFlush($promise, $immediate);
|
||||
$this->queueCompressedNoBufferFlush($batch, $immediate);
|
||||
}finally{
|
||||
Timings::$playerNetworkSend->stopTiming();
|
||||
}
|
||||
@ -550,7 +566,7 @@ class NetworkSession{
|
||||
|
||||
public function getTypeConverter() : TypeConverter{ return $this->typeConverter; }
|
||||
|
||||
public function queueCompressed(CompressBatchPromise $payload, bool $immediate = false) : void{
|
||||
public function queueCompressed(CompressBatchPromise|string $payload, bool $immediate = false) : void{
|
||||
Timings::$playerNetworkSend->startTiming();
|
||||
try{
|
||||
$this->flushSendBuffer($immediate); //Maintain ordering if possible
|
||||
@ -560,36 +576,25 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
private function queueCompressedNoBufferFlush(CompressBatchPromise $payload, bool $immediate = false) : void{
|
||||
private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false) : void{
|
||||
Timings::$playerNetworkSend->startTiming();
|
||||
try{
|
||||
if($immediate){
|
||||
if(is_string($batch)){
|
||||
if($immediate){
|
||||
//Skips all queues
|
||||
$this->sendEncoded($batch, true);
|
||||
}else{
|
||||
$this->compressedQueue->enqueue($batch);
|
||||
$this->flushCompressedQueue();
|
||||
}
|
||||
}elseif($immediate){
|
||||
//Skips all queues
|
||||
$this->sendEncoded($payload->getResult(), true);
|
||||
$this->sendEncoded($batch->getResult(), true);
|
||||
}else{
|
||||
$this->compressedQueue->enqueue($payload);
|
||||
$payload->onResolve(function(CompressBatchPromise $payload) : void{
|
||||
if($this->connected && $this->compressedQueue->bottom() === $payload){
|
||||
Timings::$playerNetworkSend->startTiming();
|
||||
try{
|
||||
$this->compressedQueue->dequeue(); //result unused
|
||||
$this->sendEncoded($payload->getResult());
|
||||
|
||||
while(!$this->compressedQueue->isEmpty()){
|
||||
/** @var CompressBatchPromise $current */
|
||||
$current = $this->compressedQueue->bottom();
|
||||
if($current->hasResult()){
|
||||
$this->compressedQueue->dequeue();
|
||||
|
||||
$this->sendEncoded($current->getResult());
|
||||
}else{
|
||||
//can't send any more queued until this one is ready
|
||||
break;
|
||||
}
|
||||
}
|
||||
}finally{
|
||||
Timings::$playerNetworkSend->stopTiming();
|
||||
}
|
||||
$this->compressedQueue->enqueue($batch);
|
||||
$batch->onResolve(function() : void{
|
||||
if($this->connected){
|
||||
$this->flushCompressedQueue();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -598,6 +603,30 @@ class NetworkSession{
|
||||
}
|
||||
}
|
||||
|
||||
private function flushCompressedQueue() : void{
|
||||
Timings::$playerNetworkSend->startTiming();
|
||||
try{
|
||||
while(!$this->compressedQueue->isEmpty()){
|
||||
/** @var CompressBatchPromise|string $current */
|
||||
$current = $this->compressedQueue->bottom();
|
||||
if(is_string($current)){
|
||||
$this->compressedQueue->dequeue();
|
||||
$this->sendEncoded($current);
|
||||
|
||||
}elseif($current->hasResult()){
|
||||
$this->compressedQueue->dequeue();
|
||||
$this->sendEncoded($current->getResult());
|
||||
|
||||
}else{
|
||||
//can't send any more queued until this one is ready
|
||||
break;
|
||||
}
|
||||
}
|
||||
}finally{
|
||||
Timings::$playerNetworkSend->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
private function sendEncoded(string $payload, bool $immediate = false) : void{
|
||||
if($this->cipher !== null){
|
||||
Timings::$playerNetworkSendEncrypt->startTiming();
|
||||
@ -662,8 +691,13 @@ class NetworkSession{
|
||||
}, $reason);
|
||||
}
|
||||
|
||||
public function disconnectWithError(Translatable|string $reason) : void{
|
||||
$this->disconnect(KnownTranslationFactory::pocketmine_disconnect_error($reason, implode("-", str_split(bin2hex(random_bytes(6)), 4))));
|
||||
public function disconnectWithError(Translatable|string $reason, Translatable|string|null $disconnectScreenMessage = null) : void{
|
||||
$errorId = implode("-", str_split(bin2hex(random_bytes(6)), 4));
|
||||
|
||||
$this->disconnect(
|
||||
reason: KnownTranslationFactory::pocketmine_disconnect_error($reason, $errorId)->prefix(TextFormat::RED),
|
||||
disconnectScreenMessage: KnownTranslationFactory::pocketmine_disconnect_error($disconnectScreenMessage ?? $reason, $errorId),
|
||||
);
|
||||
}
|
||||
|
||||
public function disconnectIncompatibleProtocol(int $protocolVersion) : void{
|
||||
@ -722,7 +756,10 @@ class NetworkSession{
|
||||
}
|
||||
|
||||
if($error !== null){
|
||||
$this->disconnectWithError(KnownTranslationFactory::pocketmine_disconnect_invalidSession($error));
|
||||
$this->disconnectWithError(
|
||||
reason: KnownTranslationFactory::pocketmine_disconnect_invalidSession($error),
|
||||
disconnectScreenMessage: KnownTranslationFactory::pocketmine_disconnect_error_authentication()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -1118,12 +1155,12 @@ class NetworkSession{
|
||||
*/
|
||||
public function syncPlayerList(array $players) : void{
|
||||
$this->sendDataPacket(PlayerListPacket::add(array_map(function(Player $player) : PlayerListEntry{
|
||||
return PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), TypeConverter::getInstance()->getSkinAdapter()->toSkinData($player->getSkin()), $player->getXuid());
|
||||
return PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $this->typeConverter->getSkinAdapter()->toSkinData($player->getSkin()), $player->getXuid());
|
||||
}, $players)));
|
||||
}
|
||||
|
||||
public function onPlayerAdded(Player $p) : void{
|
||||
$this->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($p->getUniqueId(), $p->getId(), $p->getDisplayName(), TypeConverter::getInstance()->getSkinAdapter()->toSkinData($p->getSkin()), $p->getXuid())]));
|
||||
$this->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($p->getUniqueId(), $p->getId(), $p->getDisplayName(), $this->typeConverter->getSkinAdapter()->toSkinData($p->getSkin()), $p->getXuid())]));
|
||||
}
|
||||
|
||||
public function onPlayerRemoved(Player $p) : void{
|
||||
|
@ -88,9 +88,9 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
PacketBatch::encodeRaw($stream, $packetBuffers);
|
||||
$batchBuffer = $stream->getBuffer();
|
||||
|
||||
$promise = $this->server->prepareBatch($batchBuffer, $compressor, timings: Timings::$playerNetworkSendCompressBroadcast);
|
||||
$batch = $this->server->prepareBatch($batchBuffer, $compressor, timings: Timings::$playerNetworkSendCompressBroadcast);
|
||||
foreach($compressorTargets as $target){
|
||||
$target->queueCompressed($promise);
|
||||
$target->queueCompressed($batch);
|
||||
}
|
||||
}else{
|
||||
foreach($compressorTargets as $target){
|
||||
|
@ -135,6 +135,7 @@ class ProcessLoginTask extends AsyncTask{
|
||||
$mapper = new \JsonMapper();
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bExceptionOnUndefinedProperty = true;
|
||||
$mapper->bStrictObjectTypeChecking = true;
|
||||
$mapper->bEnforceMapType = false;
|
||||
|
||||
try{
|
||||
@ -181,6 +182,7 @@ class ProcessLoginTask extends AsyncTask{
|
||||
$mapper = new \JsonMapper();
|
||||
$mapper->bExceptionOnUndefinedProperty = false; //we only care about the properties we're using in this case
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bStrictObjectTypeChecking = true;
|
||||
$mapper->bEnforceMapType = false;
|
||||
$mapper->bRemoveUndefinedAttributes = true;
|
||||
try{
|
||||
|
27
src/network/mcpe/cache/ChunkCache.php
vendored
27
src/network/mcpe/cache/ChunkCache.php
vendored
@ -27,6 +27,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\ChunkRequestTask;
|
||||
use pocketmine\network\mcpe\compression\CompressBatchPromise;
|
||||
use pocketmine\network\mcpe\compression\Compressor;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\world\ChunkListener;
|
||||
use pocketmine\world\ChunkListenerNoOpTrait;
|
||||
use pocketmine\world\format\Chunk;
|
||||
@ -116,6 +117,7 @@ class ChunkCache implements ChunkListener{
|
||||
new ChunkRequestTask(
|
||||
$chunkX,
|
||||
$chunkZ,
|
||||
DimensionIds::OVERWORLD, //TODO: not hardcode this
|
||||
$chunk,
|
||||
$this->caches[$chunkHash],
|
||||
$this->compressor
|
||||
@ -136,32 +138,19 @@ class ChunkCache implements ChunkListener{
|
||||
return $existing !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts an async request for an unresolved chunk.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function restartPendingRequest(int $chunkX, int $chunkZ) : void{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$existing = $this->caches[$chunkHash] ?? null;
|
||||
if($existing === null || $existing->hasResult()){
|
||||
throw new \InvalidArgumentException("Restart can only be applied to unresolved promises");
|
||||
}
|
||||
$existing->cancel();
|
||||
unset($this->caches[$chunkHash]);
|
||||
|
||||
$this->request($chunkX, $chunkZ)->onResolve(...$existing->getResolveCallbacks());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function destroyOrRestart(int $chunkX, int $chunkZ) : void{
|
||||
$cache = $this->caches[World::chunkHash($chunkX, $chunkZ)] ?? null;
|
||||
$chunkPosHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$cache = $this->caches[$chunkPosHash] ?? null;
|
||||
if($cache !== null){
|
||||
if(!$cache->hasResult()){
|
||||
//some requesters are waiting for this chunk, so their request needs to be fulfilled
|
||||
$this->restartPendingRequest($chunkX, $chunkZ);
|
||||
$cache->cancel();
|
||||
unset($this->caches[$chunkPosHash]);
|
||||
|
||||
$this->request($chunkX, $chunkZ)->onResolve(...$cache->getResolveCallbacks());
|
||||
}else{
|
||||
//dump the cache, it'll be regenerated the next time it's requested
|
||||
$this->destroy($chunkX, $chunkZ);
|
||||
|
2
src/network/mcpe/cache/CraftingDataCache.php
vendored
2
src/network/mcpe/cache/CraftingDataCache.php
vendored
@ -160,7 +160,7 @@ final class CraftingDataCache{
|
||||
}
|
||||
|
||||
$potionContainerChangeRecipes = [];
|
||||
$itemTypeDictionary = TypeConverter::getInstance()->getItemTypeDictionary();
|
||||
$itemTypeDictionary = $converter->getItemTypeDictionary();
|
||||
foreach($manager->getPotionContainerChangeRecipes() as $recipe){
|
||||
$input = $itemTypeDictionary->fromStringId($recipe->getInputItemId());
|
||||
$ingredient = $converter->coreRecipeIngredientToNet($recipe->getIngredient())->getDescriptor();
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe\compression;
|
||||
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\thread\NonThreadSafeValue;
|
||||
use function chr;
|
||||
|
||||
class CompressBatchTask extends AsyncTask{
|
||||
|
||||
@ -43,7 +44,8 @@ class CompressBatchTask extends AsyncTask{
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
$this->setResult($this->compressor->deserialize()->compress($this->data));
|
||||
$compressor = $this->compressor->deserialize();
|
||||
$this->setResult(chr($compressor->getNetworkId()) . $compressor->compress($this->data));
|
||||
}
|
||||
|
||||
public function onCompletion() : void{
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\compression;
|
||||
|
||||
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
|
||||
|
||||
interface Compressor{
|
||||
/**
|
||||
* @throws DecompressionException
|
||||
@ -31,6 +33,14 @@ interface Compressor{
|
||||
|
||||
public function compress(string $payload) : string;
|
||||
|
||||
/**
|
||||
* Returns the canonical ID of this compressor, used to tell the remote end how to decompress a packet compressed
|
||||
* with this compressor.
|
||||
*
|
||||
* @return CompressionAlgorithm::*
|
||||
*/
|
||||
public function getNetworkId() : int;
|
||||
|
||||
/**
|
||||
* Returns the minimum size of packet batch that the compressor will attempt to compress.
|
||||
*
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\compression;
|
||||
|
||||
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use function function_exists;
|
||||
@ -75,4 +76,8 @@ final class ZlibCompressor implements Compressor{
|
||||
libdeflate_deflate_compress($payload, $level) :
|
||||
Utils::assumeNotFalse(zlib_encode($payload, ZLIB_ENCODING_RAW, $level), "ZLIB compression failed");
|
||||
}
|
||||
|
||||
public function getNetworkId() : int{
|
||||
return CompressionAlgorithm::ZLIB;
|
||||
}
|
||||
}
|
||||
|
@ -869,8 +869,12 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
public function handleBookEdit(BookEditPacket $packet) : bool{
|
||||
$inventory = $this->player->getInventory();
|
||||
if(!$inventory->slotExists($packet->inventorySlot)){
|
||||
return false;
|
||||
}
|
||||
//TODO: break this up into book API things
|
||||
$oldBook = $this->player->getInventory()->getItem($packet->inventorySlot);
|
||||
$oldBook = $inventory->getItem($packet->inventorySlot);
|
||||
if(!($oldBook instanceof WritableBook)){
|
||||
return false;
|
||||
}
|
||||
|
@ -73,8 +73,10 @@ class LoginPacketHandler extends PacketHandler{
|
||||
try{
|
||||
$skin = $this->session->getTypeConverter()->getSkinAdapter()->fromSkinData(ClientDataToSkinDataHelper::fromClientData($clientData));
|
||||
}catch(\InvalidArgumentException | InvalidSkinException $e){
|
||||
$this->session->getLogger()->debug("Invalid skin: " . $e->getMessage());
|
||||
$this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_invalidSkin());
|
||||
$this->session->disconnectWithError(
|
||||
reason: "Invalid skin: " . $e->getMessage(),
|
||||
disconnectScreenMessage: KnownTranslationFactory::disconnectionScreen_invalidSkin()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -167,6 +169,7 @@ class LoginPacketHandler extends PacketHandler{
|
||||
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bExceptionOnUndefinedProperty = true;
|
||||
$mapper->bStrictObjectTypeChecking = true;
|
||||
try{
|
||||
/** @var AuthenticationData $extraData */
|
||||
$extraData = $mapper->map($claims["extraData"], new AuthenticationData());
|
||||
@ -195,6 +198,7 @@ class LoginPacketHandler extends PacketHandler{
|
||||
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bExceptionOnUndefinedProperty = true;
|
||||
$mapper->bStrictObjectTypeChecking = true;
|
||||
try{
|
||||
$clientData = $mapper->map($clientDataClaims, new ClientData());
|
||||
}catch(\JsonMapper_Exception $e){
|
||||
|
@ -85,8 +85,10 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
private function disconnectWithError(string $error) : void{
|
||||
$this->session->getLogger()->error("Error downloading resource packs: " . $error);
|
||||
$this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_resourcePack());
|
||||
$this->session->disconnectWithError(
|
||||
reason: "Error downloading resource packs: " . $error,
|
||||
disconnectScreenMessage: KnownTranslationFactory::disconnectionScreen_resourcePack()
|
||||
);
|
||||
}
|
||||
|
||||
public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{
|
||||
|
@ -27,7 +27,6 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\NetworkSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\RequestNetworkSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
|
||||
|
||||
final class SessionStartPacketHandler extends PacketHandler{
|
||||
|
||||
@ -50,7 +49,7 @@ final class SessionStartPacketHandler extends PacketHandler{
|
||||
//TODO: we're filling in the defaults to get pre-1.19.30 behaviour back for now, but we should explore the new options in the future
|
||||
$this->session->sendDataPacket(NetworkSettingsPacket::create(
|
||||
NetworkSettingsPacket::COMPRESS_EVERYTHING,
|
||||
CompressionAlgorithm::ZLIB,
|
||||
$this->session->getCompressor()->getNetworkId(),
|
||||
false,
|
||||
0,
|
||||
0
|
||||
|
@ -219,11 +219,14 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
|
||||
$session->handleEncoded($buf);
|
||||
}catch(PacketHandlingException $e){
|
||||
$logger = $session->getLogger();
|
||||
$logger->error("Bad packet: " . $e->getMessage());
|
||||
|
||||
$session->disconnectWithError(
|
||||
reason: "Bad packet: " . $e->getMessage(),
|
||||
disconnectScreenMessage: KnownTranslationFactory::pocketmine_disconnect_error_badPacket()
|
||||
);
|
||||
//intentionally doesn't use logException, we don't want spammy packet error traces to appear in release mode
|
||||
$logger->debug(implode("\n", Utils::printableExceptionInfo($e)));
|
||||
$session->disconnectWithError(KnownTranslationFactory::pocketmine_disconnect_error_badPacket());
|
||||
|
||||
$this->interface->blockAddress($address, 5);
|
||||
}catch(\Throwable $e){
|
||||
//record the name of the player who caused the crash, to make it easier to find the reproducing steps
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\network\mcpe\convert\BlockTranslator;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\world\format\Chunk;
|
||||
@ -43,12 +44,34 @@ final class ChunkSerializer{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the min/max subchunk index expected in the protocol.
|
||||
* This has no relation to the world height supported by PM.
|
||||
*
|
||||
* @phpstan-param DimensionIds::* $dimensionId
|
||||
* @return int[]
|
||||
* @phpstan-return array{int, int}
|
||||
*/
|
||||
public static function getDimensionChunkBounds(int $dimensionId) : array{
|
||||
return match($dimensionId){
|
||||
DimensionIds::OVERWORLD => [-4, 19],
|
||||
DimensionIds::NETHER => [0, 7],
|
||||
DimensionIds::THE_END => [0, 15],
|
||||
default => throw new \InvalidArgumentException("Unknown dimension ID $dimensionId"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of subchunks that will be sent from the given chunk.
|
||||
* Chunks are sent in a stack, so every chunk below the top non-empty one must be sent.
|
||||
*
|
||||
* @phpstan-param DimensionIds::* $dimensionId
|
||||
*/
|
||||
public static function getSubChunkCount(Chunk $chunk) : int{
|
||||
for($y = Chunk::MAX_SUBCHUNK_INDEX, $count = count($chunk->getSubChunks()); $y >= Chunk::MIN_SUBCHUNK_INDEX; --$y, --$count){
|
||||
public static function getSubChunkCount(Chunk $chunk, int $dimensionId) : int{
|
||||
//if the protocol world bounds ever exceed the PM supported bounds again in the future, we might need to
|
||||
//polyfill some stuff here
|
||||
[$minSubChunkIndex, $maxSubChunkIndex] = self::getDimensionChunkBounds($dimensionId);
|
||||
for($y = $maxSubChunkIndex, $count = $maxSubChunkIndex - $minSubChunkIndex + 1; $y >= $minSubChunkIndex; --$y, --$count){
|
||||
if($chunk->getSubChunk($y)->isEmptyFast()){
|
||||
continue;
|
||||
}
|
||||
@ -58,18 +81,23 @@ final class ChunkSerializer{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function serializeFullChunk(Chunk $chunk, BlockTranslator $blockTranslator, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{
|
||||
/**
|
||||
* @phpstan-param DimensionIds::* $dimensionId
|
||||
*/
|
||||
public static function serializeFullChunk(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{
|
||||
$stream = PacketSerializer::encoder($encoderContext);
|
||||
|
||||
$subChunkCount = self::getSubChunkCount($chunk);
|
||||
$subChunkCount = self::getSubChunkCount($chunk, $dimensionId);
|
||||
$writtenCount = 0;
|
||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $writtenCount < $subChunkCount; ++$y, ++$writtenCount){
|
||||
|
||||
[$minSubChunkIndex, $maxSubChunkIndex] = self::getDimensionChunkBounds($dimensionId);
|
||||
for($y = $minSubChunkIndex; $writtenCount < $subChunkCount; ++$y, ++$writtenCount){
|
||||
self::serializeSubChunk($chunk->getSubChunk($y), $blockTranslator, $stream, false);
|
||||
}
|
||||
|
||||
$biomeIdMap = LegacyBiomeIdToStringIdMap::getInstance();
|
||||
//all biomes must always be written :(
|
||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
||||
for($y = $minSubChunkIndex; $y <= $maxSubChunkIndex; ++$y){
|
||||
self::serializeBiomePalette($chunk->getSubChunk($y)->getBiomeArray(), $biomeIdMap, $stream);
|
||||
}
|
||||
|
||||
|
@ -813,13 +813,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->usedChunks[$index] = UsedChunkStatus::REQUESTED_GENERATION;
|
||||
$this->activeChunkGenerationRequests[$index] = true;
|
||||
unset($this->loadQueue[$index]);
|
||||
$this->getWorld()->registerChunkLoader($this->chunkLoader, $X, $Z, true);
|
||||
$this->getWorld()->registerChunkListener($this, $X, $Z);
|
||||
$world->registerChunkLoader($this->chunkLoader, $X, $Z, true);
|
||||
$world->registerChunkListener($this, $X, $Z);
|
||||
if(isset($this->tickingChunks[$index])){
|
||||
$this->getWorld()->registerTickingChunk($this->chunkTicker, $X, $Z);
|
||||
$world->registerTickingChunk($this->chunkTicker, $X, $Z);
|
||||
}
|
||||
|
||||
$this->getWorld()->requestChunkPopulation($X, $Z, $this->chunkLoader)->onCompletion(
|
||||
$world->requestChunkPopulation($X, $Z, $this->chunkLoader)->onCompletion(
|
||||
function() use ($X, $Z, $index, $world) : void{
|
||||
if(!$this->isConnected() || !isset($this->usedChunks[$index]) || $world !== $this->getWorld()){
|
||||
return;
|
||||
|
@ -70,7 +70,7 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
|
||||
$this->configFile = Path::join($this->dataFolder, "config.yml");
|
||||
|
||||
$prefix = $this->getDescription()->getPrefix();
|
||||
$prefix = $this->description->getPrefix();
|
||||
$this->logger = new PluginLogger($server->getLogger(), $prefix !== "" ? $prefix : $this->getName());
|
||||
$this->scheduler = new TaskScheduler($this->getFullName());
|
||||
|
||||
@ -145,9 +145,9 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
private function registerYamlCommands() : void{
|
||||
$pluginCmds = [];
|
||||
|
||||
foreach(Utils::stringifyKeys($this->getDescription()->getCommands()) as $key => $data){
|
||||
foreach(Utils::stringifyKeys($this->description->getCommands()) as $key => $data){
|
||||
if(str_contains($key, ":")){
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_commandError($key, $this->getDescription()->getFullName(), ":")));
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_commandError($key, $this->description->getFullName(), ":")));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
$aliasList = [];
|
||||
foreach($data->getAliases() as $alias){
|
||||
if(str_contains($alias, ":")){
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_aliasError($alias, $this->getDescription()->getFullName(), ":")));
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_aliasError($alias, $this->description->getFullName(), ":")));
|
||||
continue;
|
||||
}
|
||||
$aliasList[] = $alias;
|
||||
@ -181,7 +181,7 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
}
|
||||
|
||||
if(count($pluginCmds) > 0){
|
||||
$this->server->getCommandMap()->registerAll($this->getDescription()->getName(), $pluginCmds);
|
||||
$this->server->getCommandMap()->registerAll($this->description->getName(), $pluginCmds);
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,9 +190,9 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
* @phpstan-return (Command&PluginOwned)|null
|
||||
*/
|
||||
public function getCommand(string $name){
|
||||
$command = $this->getServer()->getPluginCommand($name);
|
||||
$command = $this->server->getPluginCommand($name);
|
||||
if($command === null || $command->getOwningPlugin() !== $this){
|
||||
$command = $this->getServer()->getPluginCommand(strtolower($this->description->getName()) . ":" . $name);
|
||||
$command = $this->server->getPluginCommand(strtolower($this->description->getName()) . ":" . $name);
|
||||
}
|
||||
|
||||
if($command instanceof PluginOwned && $command->getOwningPlugin() === $this){
|
||||
|
@ -108,6 +108,7 @@ class ZippedResourcePack implements ResourcePack{
|
||||
|
||||
$mapper = new \JsonMapper();
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bStrictObjectTypeChecking = true;
|
||||
|
||||
try{
|
||||
/** @var Manifest $manifest */
|
||||
|
@ -35,6 +35,7 @@ use function get_class;
|
||||
use function str_starts_with;
|
||||
|
||||
abstract class Timings{
|
||||
public const GROUP_MINECRAFT = "Minecraft";
|
||||
public const GROUP_BREAKDOWN = "Minecraft - Breakdown";
|
||||
|
||||
private static bool $initialized = false;
|
||||
@ -134,8 +135,8 @@ abstract class Timings{
|
||||
self::$initialized = true;
|
||||
|
||||
self::$fullTick = new TimingsHandler("Full Server Tick");
|
||||
self::$serverTick = new TimingsHandler("Server Tick Update Cycle", self::$fullTick, group: self::GROUP_BREAKDOWN);
|
||||
self::$serverInterrupts = new TimingsHandler("Server Mid-Tick Processing", self::$fullTick, group: self::GROUP_BREAKDOWN);
|
||||
self::$serverTick = new TimingsHandler("Server Tick Update Cycle", self::$fullTick);
|
||||
self::$serverInterrupts = new TimingsHandler("Server Mid-Tick Processing", self::$fullTick);
|
||||
self::$memoryManager = new TimingsHandler("Memory Manager");
|
||||
self::$garbageCollector = new TimingsHandler("Garbage Collector", self::$memoryManager);
|
||||
self::$titleTick = new TimingsHandler("Console Title Tick");
|
||||
@ -143,51 +144,51 @@ abstract class Timings{
|
||||
self::$connection = new TimingsHandler("Connection Handler");
|
||||
|
||||
self::$playerNetworkSend = new TimingsHandler("Player Network Send", self::$connection);
|
||||
self::$playerNetworkSendCompress = new TimingsHandler("Player Network Send - Compression", self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkSendCompressBroadcast = new TimingsHandler("Player Network Send - Compression (Broadcast)", self::$playerNetworkSendCompress, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkSendCompressSessionBuffer = new TimingsHandler("Player Network Send - Compression (Session Buffer)", self::$playerNetworkSendCompress, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkSendEncrypt = new TimingsHandler("Player Network Send - Encryption", self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkSendInventorySync = new TimingsHandler("Player Network Send - Inventory Sync", self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkSendPreSpawnGameData = new TimingsHandler("Player Network Send - Pre-Spawn Game Data", self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkSendCompress = new TimingsHandler("Player Network Send - Compression", self::$playerNetworkSend);
|
||||
self::$playerNetworkSendCompressBroadcast = new TimingsHandler("Player Network Send - Compression (Broadcast)", self::$playerNetworkSendCompress);
|
||||
self::$playerNetworkSendCompressSessionBuffer = new TimingsHandler("Player Network Send - Compression (Session Buffer)", self::$playerNetworkSendCompress);
|
||||
self::$playerNetworkSendEncrypt = new TimingsHandler("Player Network Send - Encryption", self::$playerNetworkSend);
|
||||
self::$playerNetworkSendInventorySync = new TimingsHandler("Player Network Send - Inventory Sync", self::$playerNetworkSend);
|
||||
self::$playerNetworkSendPreSpawnGameData = new TimingsHandler("Player Network Send - Pre-Spawn Game Data", self::$playerNetworkSend);
|
||||
|
||||
self::$playerNetworkReceive = new TimingsHandler("Player Network Receive", self::$connection);
|
||||
self::$playerNetworkReceiveDecompress = new TimingsHandler("Player Network Receive - Decompression", self::$playerNetworkReceive, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkReceiveDecrypt = new TimingsHandler("Player Network Receive - Decryption", self::$playerNetworkReceive, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerNetworkReceiveDecompress = new TimingsHandler("Player Network Receive - Decompression", self::$playerNetworkReceive);
|
||||
self::$playerNetworkReceiveDecrypt = new TimingsHandler("Player Network Receive - Decryption", self::$playerNetworkReceive);
|
||||
|
||||
self::$broadcastPackets = new TimingsHandler("Broadcast Packets", self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$broadcastPackets = new TimingsHandler("Broadcast Packets", self::$playerNetworkSend);
|
||||
|
||||
self::$playerMove = new TimingsHandler("Player Movement");
|
||||
self::$playerChunkOrder = new TimingsHandler("Player Order Chunks");
|
||||
self::$playerChunkSend = new TimingsHandler("Player Network Send - Chunks", self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$playerChunkSend = new TimingsHandler("Player Network Send - Chunks", self::$playerNetworkSend);
|
||||
self::$scheduler = new TimingsHandler("Scheduler");
|
||||
self::$serverCommand = new TimingsHandler("Server Command");
|
||||
self::$permissibleCalculation = new TimingsHandler("Permissible Calculation");
|
||||
self::$permissibleCalculationDiff = new TimingsHandler("Permissible Calculation - Diff", self::$permissibleCalculation, group: self::GROUP_BREAKDOWN);
|
||||
self::$permissibleCalculationCallback = new TimingsHandler("Permissible Calculation - Callbacks", self::$permissibleCalculation, group: self::GROUP_BREAKDOWN);
|
||||
self::$permissibleCalculationDiff = new TimingsHandler("Permissible Calculation - Diff", self::$permissibleCalculation);
|
||||
self::$permissibleCalculationCallback = new TimingsHandler("Permissible Calculation - Callbacks", self::$permissibleCalculation);
|
||||
|
||||
self::$syncPlayerDataLoad = new TimingsHandler("Player Data Load");
|
||||
self::$syncPlayerDataSave = new TimingsHandler("Player Data Save");
|
||||
|
||||
self::$entityMove = new TimingsHandler("Entity Movement", group: self::GROUP_BREAKDOWN);
|
||||
self::$entityMoveCollision = new TimingsHandler("Entity Movement - Collision Checks", self::$entityMove, group: self::GROUP_BREAKDOWN);
|
||||
self::$entityMove = new TimingsHandler("Entity Movement");
|
||||
self::$entityMoveCollision = new TimingsHandler("Entity Movement - Collision Checks", self::$entityMove);
|
||||
|
||||
self::$projectileMove = new TimingsHandler("Projectile Movement", self::$entityMove, group: self::GROUP_BREAKDOWN);
|
||||
self::$projectileMoveRayTrace = new TimingsHandler("Projectile Movement - Ray Tracing", self::$projectileMove, group: self::GROUP_BREAKDOWN);
|
||||
self::$projectileMove = new TimingsHandler("Projectile Movement", self::$entityMove);
|
||||
self::$projectileMoveRayTrace = new TimingsHandler("Projectile Movement - Ray Tracing", self::$projectileMove);
|
||||
|
||||
self::$playerCheckNearEntities = new TimingsHandler("checkNearEntities", group: self::GROUP_BREAKDOWN);
|
||||
self::$entityBaseTick = new TimingsHandler("Entity Base Tick", group: self::GROUP_BREAKDOWN);
|
||||
self::$livingEntityBaseTick = new TimingsHandler("Entity Base Tick - Living", group: self::GROUP_BREAKDOWN);
|
||||
self::$itemEntityBaseTick = new TimingsHandler("Entity Base Tick - ItemEntity", group: self::GROUP_BREAKDOWN);
|
||||
self::$playerCheckNearEntities = new TimingsHandler("checkNearEntities");
|
||||
self::$entityBaseTick = new TimingsHandler("Entity Base Tick");
|
||||
self::$livingEntityBaseTick = new TimingsHandler("Entity Base Tick - Living");
|
||||
self::$itemEntityBaseTick = new TimingsHandler("Entity Base Tick - ItemEntity");
|
||||
|
||||
self::$schedulerSync = new TimingsHandler("Scheduler - Sync Tasks", group: self::GROUP_BREAKDOWN);
|
||||
self::$schedulerSync = new TimingsHandler("Scheduler - Sync Tasks");
|
||||
|
||||
self::$schedulerAsync = new TimingsHandler("Scheduler - Async Tasks", group: self::GROUP_BREAKDOWN);
|
||||
self::$asyncTaskProgressUpdateParent = new TimingsHandler("Async Tasks - Progress Updates", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
|
||||
self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
|
||||
self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
|
||||
self::$schedulerAsync = new TimingsHandler("Scheduler - Async Tasks");
|
||||
self::$asyncTaskProgressUpdateParent = new TimingsHandler("Async Tasks - Progress Updates", self::$schedulerAsync);
|
||||
self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync);
|
||||
self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync);
|
||||
|
||||
self::$playerCommand = new TimingsHandler("Player Command", group: self::GROUP_BREAKDOWN);
|
||||
self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache", group: self::GROUP_BREAKDOWN);
|
||||
self::$playerCommand = new TimingsHandler("Player Command");
|
||||
self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache");
|
||||
|
||||
}
|
||||
|
||||
@ -229,7 +230,7 @@ abstract class Timings{
|
||||
}else{
|
||||
$displayName = self::shortenCoreClassName($entity::class, "pocketmine\\entity\\");
|
||||
}
|
||||
self::$entityTypeTimingMap[$entity::class] = new TimingsHandler("Entity Tick - " . $displayName, group: self::GROUP_BREAKDOWN);
|
||||
self::$entityTypeTimingMap[$entity::class] = new TimingsHandler("Entity Tick - " . $displayName);
|
||||
}
|
||||
|
||||
return self::$entityTypeTimingMap[$entity::class];
|
||||
@ -239,8 +240,7 @@ abstract class Timings{
|
||||
self::init();
|
||||
if(!isset(self::$tileEntityTypeTimingMap[$tile::class])){
|
||||
self::$tileEntityTypeTimingMap[$tile::class] = new TimingsHandler(
|
||||
"Block Entity Tick - " . self::shortenCoreClassName($tile::class, "pocketmine\\block\\tile\\"),
|
||||
group: self::GROUP_BREAKDOWN
|
||||
"Block Entity Tick - " . self::shortenCoreClassName($tile::class, "pocketmine\\block\\tile\\")
|
||||
);
|
||||
}
|
||||
|
||||
@ -250,7 +250,7 @@ abstract class Timings{
|
||||
public static function getReceiveDataPacketTimings(ServerboundPacket $pk) : TimingsHandler{
|
||||
self::init();
|
||||
if(!isset(self::$packetReceiveTimingMap[$pk::class])){
|
||||
self::$packetReceiveTimingMap[$pk::class] = new TimingsHandler("Receive - " . $pk->getName(), self::$playerNetworkReceive, group: self::GROUP_BREAKDOWN);
|
||||
self::$packetReceiveTimingMap[$pk::class] = new TimingsHandler("Receive - " . $pk->getName(), self::$playerNetworkReceive);
|
||||
}
|
||||
|
||||
return self::$packetReceiveTimingMap[$pk::class];
|
||||
@ -259,31 +259,28 @@ abstract class Timings{
|
||||
public static function getDecodeDataPacketTimings(ServerboundPacket $pk) : TimingsHandler{
|
||||
return self::$packetDecodeTimingMap[$pk::class] ??= new TimingsHandler(
|
||||
"Decode - " . $pk->getName(),
|
||||
self::getReceiveDataPacketTimings($pk),
|
||||
group: self::GROUP_BREAKDOWN
|
||||
self::getReceiveDataPacketTimings($pk)
|
||||
);
|
||||
}
|
||||
|
||||
public static function getHandleDataPacketTimings(ServerboundPacket $pk) : TimingsHandler{
|
||||
return self::$packetHandleTimingMap[$pk::class] ??= new TimingsHandler(
|
||||
"Handler - " . $pk->getName(),
|
||||
self::getReceiveDataPacketTimings($pk),
|
||||
group: self::GROUP_BREAKDOWN
|
||||
self::getReceiveDataPacketTimings($pk)
|
||||
);
|
||||
}
|
||||
|
||||
public static function getEncodeDataPacketTimings(ClientboundPacket $pk) : TimingsHandler{
|
||||
return self::$packetEncodeTimingMap[$pk::class] ??= new TimingsHandler(
|
||||
"Encode - " . $pk->getName(),
|
||||
self::getSendDataPacketTimings($pk),
|
||||
group: self::GROUP_BREAKDOWN
|
||||
self::getSendDataPacketTimings($pk)
|
||||
);
|
||||
}
|
||||
|
||||
public static function getSendDataPacketTimings(ClientboundPacket $pk) : TimingsHandler{
|
||||
self::init();
|
||||
if(!isset(self::$packetSendTimingMap[$pk::class])){
|
||||
self::$packetSendTimingMap[$pk::class] = new TimingsHandler("Send - " . $pk->getName(), self::$playerNetworkSend, group: self::GROUP_BREAKDOWN);
|
||||
self::$packetSendTimingMap[$pk::class] = new TimingsHandler("Send - " . $pk->getName(), self::$playerNetworkSend);
|
||||
}
|
||||
|
||||
return self::$packetSendTimingMap[$pk::class];
|
||||
@ -292,7 +289,7 @@ abstract class Timings{
|
||||
public static function getCommandDispatchTimings(string $commandName) : TimingsHandler{
|
||||
self::init();
|
||||
|
||||
return self::$commandTimingMap[$commandName] ??= new TimingsHandler("Command - " . $commandName, group: self::GROUP_BREAKDOWN);
|
||||
return self::$commandTimingMap[$commandName] ??= new TimingsHandler("Command - " . $commandName);
|
||||
}
|
||||
|
||||
public static function getEventTimings(Event $event) : TimingsHandler{
|
||||
@ -316,7 +313,7 @@ abstract class Timings{
|
||||
return self::$eventHandlers[$event][$handlerName];
|
||||
}
|
||||
|
||||
public static function getAsyncTaskProgressUpdateTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
|
||||
public static function getAsyncTaskProgressUpdateTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskProgressUpdate[$taskClass])){
|
||||
self::init();
|
||||
@ -330,7 +327,7 @@ abstract class Timings{
|
||||
return self::$asyncTaskProgressUpdate[$taskClass];
|
||||
}
|
||||
|
||||
public static function getAsyncTaskCompletionTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
|
||||
public static function getAsyncTaskCompletionTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskCompletion[$taskClass])){
|
||||
self::init();
|
||||
@ -344,7 +341,7 @@ abstract class Timings{
|
||||
return self::$asyncTaskCompletion[$taskClass];
|
||||
}
|
||||
|
||||
public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
|
||||
public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
||||
$taskClass = $task::class;
|
||||
if(!isset(self::$asyncTaskError[$taskClass])){
|
||||
self::init();
|
||||
|
@ -120,7 +120,7 @@ class TimingsHandler{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private ?TimingsHandler $parent = null,
|
||||
private string $group = "Minecraft"
|
||||
private string $group = Timings::GROUP_MINECRAFT
|
||||
){}
|
||||
|
||||
public function getName() : string{ return $this->name; }
|
||||
|
@ -55,6 +55,7 @@ class UpdateCheckTask extends AsyncTask{
|
||||
}else{
|
||||
$mapper = new \JsonMapper();
|
||||
$mapper->bExceptionOnMissingData = true;
|
||||
$mapper->bStrictObjectTypeChecking = true;
|
||||
$mapper->bEnforceMapType = false;
|
||||
try{
|
||||
/** @var UpdateInfo $responseObj */
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use LogLevel;
|
||||
use pmmp\thread\Thread as NativeThread;
|
||||
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
||||
use pocketmine\thread\log\ThreadSafeLoggerAttachment;
|
||||
@ -132,28 +131,28 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{
|
||||
|
||||
public function log($level, $message){
|
||||
switch($level){
|
||||
case LogLevel::EMERGENCY:
|
||||
case \LogLevel::EMERGENCY:
|
||||
$this->emergency($message);
|
||||
break;
|
||||
case LogLevel::ALERT:
|
||||
case \LogLevel::ALERT:
|
||||
$this->alert($message);
|
||||
break;
|
||||
case LogLevel::CRITICAL:
|
||||
case \LogLevel::CRITICAL:
|
||||
$this->critical($message);
|
||||
break;
|
||||
case LogLevel::ERROR:
|
||||
case \LogLevel::ERROR:
|
||||
$this->error($message);
|
||||
break;
|
||||
case LogLevel::WARNING:
|
||||
case \LogLevel::WARNING:
|
||||
$this->warning($message);
|
||||
break;
|
||||
case LogLevel::NOTICE:
|
||||
case \LogLevel::NOTICE:
|
||||
$this->notice($message);
|
||||
break;
|
||||
case LogLevel::INFO:
|
||||
case \LogLevel::INFO:
|
||||
$this->info($message);
|
||||
break;
|
||||
case LogLevel::DEBUG:
|
||||
case \LogLevel::DEBUG:
|
||||
$this->debug($message);
|
||||
break;
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use pocketmine\thread\Thread;
|
||||
use function time;
|
||||
use function hrtime;
|
||||
use function intdiv;
|
||||
|
||||
class ServerKiller extends Thread{
|
||||
private bool $stopped = false;
|
||||
@ -34,13 +35,15 @@ class ServerKiller extends Thread{
|
||||
){}
|
||||
|
||||
protected function onRun() : void{
|
||||
$start = time();
|
||||
$this->synchronized(function() : void{
|
||||
if(!$this->stopped){
|
||||
$this->wait($this->time * 1000000);
|
||||
$start = hrtime(true);
|
||||
$remaining = $this->time * 1_000_000;
|
||||
$this->synchronized(function() use (&$remaining, $start) : void{
|
||||
while(!$this->stopped && $remaining > 0){
|
||||
$this->wait($remaining);
|
||||
$remaining -= intdiv(hrtime(true) - $start, 1000);
|
||||
}
|
||||
});
|
||||
if(time() - $start >= $this->time){
|
||||
if($remaining <= 0){
|
||||
echo "\nTook too long to stop, server was killed forcefully!\n";
|
||||
@Process::kill(Process::pid());
|
||||
}
|
||||
|
@ -173,16 +173,17 @@ final class Utils{
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template T of object
|
||||
* @phpstan-template TKey of array-key
|
||||
* @phpstan-template TValue of object
|
||||
*
|
||||
* @param object[] $array
|
||||
* @phpstan-param T[] $array
|
||||
* @phpstan-param array<TKey, TValue> $array
|
||||
*
|
||||
* @return object[]
|
||||
* @phpstan-return T[]
|
||||
* @phpstan-return array<TKey, TValue>
|
||||
*/
|
||||
public static function cloneObjectArray(array $array) : array{
|
||||
/** @phpstan-var \Closure(T) : T $callback */
|
||||
/** @phpstan-var \Closure(TValue) : TValue $callback */
|
||||
$callback = self::cloneCallback();
|
||||
return array_map($callback, $array);
|
||||
}
|
||||
|
@ -1670,16 +1670,19 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest available level of any type of light at the given coordinates, adjusted for the current
|
||||
* weather and time of day.
|
||||
* Returns the highest level of any type of light at the given coordinates, adjusted for the current weather and
|
||||
* time of day.
|
||||
*/
|
||||
public function getFullLight(Vector3 $pos) : int{
|
||||
return $this->getFullLightAt($pos->x, $pos->y, $pos->z);
|
||||
$floorX = $pos->getFloorX();
|
||||
$floorY = $pos->getFloorY();
|
||||
$floorZ = $pos->getFloorZ();
|
||||
return $this->getFullLightAt($floorX, $floorY, $floorZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest available level of any type of light at the given coordinates, adjusted for the current
|
||||
* weather and time of day.
|
||||
* Returns the highest level of any type of light at the given coordinates, adjusted for the current weather and
|
||||
* time of day.
|
||||
*/
|
||||
public function getFullLightAt(int $x, int $y, int $z) : int{
|
||||
$skyLight = $this->getRealBlockSkyLightAt($x, $y, $z);
|
||||
@ -1691,18 +1694,43 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest available level of any type of light at, or adjacent to, the given coordinates, adjusted for
|
||||
* the current weather and time of day.
|
||||
* Returns the highest level of any type of light at, or adjacent to, the given coordinates, adjusted for the
|
||||
* current weather and time of day.
|
||||
*/
|
||||
public function getHighestAdjacentFullLightAt(int $x, int $y, int $z) : int{
|
||||
return $this->getHighestAdjacentLight($x, $y, $z, $this->getFullLightAt(...));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest potential level of any type of light at the target coordinates.
|
||||
* This is not affected by weather or time of day.
|
||||
*/
|
||||
public function getPotentialLight(Vector3 $pos) : int{
|
||||
$floorX = $pos->getFloorX();
|
||||
$floorY = $pos->getFloorY();
|
||||
$floorZ = $pos->getFloorZ();
|
||||
return $this->getPotentialLightAt($floorX, $floorY, $floorZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest potential level of any type of light at the target coordinates.
|
||||
* This is not affected by weather or time of day.
|
||||
*/
|
||||
public function getPotentialLightAt(int $x, int $y, int $z) : int{
|
||||
return max($this->getPotentialBlockSkyLightAt($x, $y, $z), $this->getBlockLightAt($x, $y, $z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest potential level of any type of light at, or adjacent to, the given coordinates.
|
||||
* This is not affected by weather or time of day.
|
||||
*/
|
||||
public function getHighestAdjacentPotentialLightAt(int $x, int $y, int $z) : int{
|
||||
return $this->getHighestAdjacentLight($x, $y, $z, $this->getPotentialLightAt(...));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest potential level of sky light at the target coordinates, regardless of the time of day or
|
||||
* weather conditions.
|
||||
* You usually don't want to use this for vanilla gameplay logic; prefer the real sky light instead.
|
||||
* @see World::getRealBlockSkyLightAt()
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
@ -2599,7 +2627,7 @@ class World implements ChunkManager{
|
||||
|
||||
public function isChunkPopulated(int $x, int $z) : bool{
|
||||
$chunk = $this->loadChunk($x, $z);
|
||||
return $chunk !== null ? $chunk->isPopulated() : false;
|
||||
return $chunk !== null && $chunk->isPopulated();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world;
|
||||
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
|
||||
class WorldTimings{
|
||||
@ -66,7 +65,7 @@ class WorldTimings{
|
||||
private static function newTimer(string $worldName, string $timerName) : TimingsHandler{
|
||||
$aggregator = self::$aggregators[$timerName] ??= new TimingsHandler("Worlds - $timerName"); //displayed in Minecraft primary table
|
||||
|
||||
return new TimingsHandler("$worldName - $timerName", $aggregator, Timings::GROUP_BREAKDOWN);
|
||||
return new TimingsHandler("$worldName - $timerName", $aggregator);
|
||||
}
|
||||
|
||||
public function __construct(World $world){
|
||||
|
@ -51,12 +51,12 @@ use function time;
|
||||
class BedrockWorldData extends BaseNbtWorldData{
|
||||
|
||||
public const CURRENT_STORAGE_VERSION = 10;
|
||||
public const CURRENT_STORAGE_NETWORK_VERSION = 630;
|
||||
public const CURRENT_STORAGE_NETWORK_VERSION = 649;
|
||||
public const CURRENT_CLIENT_VERSION_TARGET = [
|
||||
1, //major
|
||||
20, //minor
|
||||
50, //patch
|
||||
3, //revision
|
||||
60, //patch
|
||||
4, //revision
|
||||
0 //is beta
|
||||
];
|
||||
|
||||
|
@ -425,6 +425,21 @@ parameters:
|
||||
count: 3
|
||||
path: ../../../src/block/tile/Spawnable.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getPotentialLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/utils/CropGrowthHelper.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getPotentialLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/utils/CropGrowthHelper.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getPotentialLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/utils/CropGrowthHelper.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method addParticle\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#"
|
||||
count: 1
|
||||
@ -910,11 +925,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
@ -940,11 +950,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
@ -970,11 +975,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/World.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 1
|
||||
|
@ -30,7 +30,7 @@ require dirname(__DIR__, 3) . '/vendor/autoload.php';
|
||||
|
||||
/* This script needs to be re-run after any intentional blockfactory change (adding or removing a block state). */
|
||||
|
||||
$factory = new \pocketmine\block\RuntimeBlockStateRegistry();
|
||||
$factory = new RuntimeBlockStateRegistry();
|
||||
$remaps = [];
|
||||
$new = [];
|
||||
foreach(RuntimeBlockStateRegistry::getInstance()->getAllKnownStates() as $index => $block){
|
||||
@ -44,7 +44,7 @@ $oldTablePath = __DIR__ . '/block_factory_consistency_check.json';
|
||||
if(file_exists($oldTablePath)){
|
||||
$oldTable = json_decode(file_get_contents($oldTablePath), true);
|
||||
if(!is_array($oldTable)){
|
||||
throw new \pocketmine\utils\AssumptionFailedError("Old table should be array{knownStates: array<string, string>, stateDataBits: int}");
|
||||
throw new AssumptionFailedError("Old table should be array{knownStates: array<string, string>, stateDataBits: int}");
|
||||
}
|
||||
$old = [];
|
||||
/**
|
||||
|
Submodule tests/plugins/DevTools updated: 411fd5bdc0...c6dca357c7
@ -21,10 +21,29 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\tools\convert_world;
|
||||
|
||||
use pocketmine\world\format\io\FormatConverter;
|
||||
use pocketmine\world\format\io\WorldProviderManager;
|
||||
use pocketmine\world\format\io\WorldProviderManagerEntry;
|
||||
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function fwrite;
|
||||
use function getopt;
|
||||
use function implode;
|
||||
use function is_dir;
|
||||
use function is_string;
|
||||
use function is_writable;
|
||||
use function mkdir;
|
||||
use function realpath;
|
||||
use const PHP_EOL;
|
||||
use const STDERR;
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
@ -76,5 +95,5 @@ if(count($oldProviderClasses) > 1){
|
||||
$oldProviderClass = array_shift($oldProviderClasses);
|
||||
$oldProvider = $oldProviderClass->fromPath($inputPath, new \PrefixedLogger(\GlobalLogger::get(), "Old World Provider"));
|
||||
|
||||
$converter = new FormatConverter($oldProvider, $writableFormats[$args["format"]], $backupPath, GlobalLogger::get());
|
||||
$converter = new FormatConverter($oldProvider, $writableFormats[$args["format"]], $backupPath, \GlobalLogger::get());
|
||||
$converter->execute();
|
||||
|
@ -165,7 +165,7 @@ class ParserPacketHandler extends PacketHandler{
|
||||
$data->block_states = self::blockStatePropertiesToString($blockState);
|
||||
}
|
||||
}elseif($itemStack->getBlockRuntimeId() !== ItemTranslator::NO_BLOCK_RUNTIME_ID){
|
||||
throw new PacketHandlingException("Non-blockitems should have a zero block runtime ID");
|
||||
throw new PacketHandlingException("Non-blockitems should have a zero block runtime ID (" . $itemStack->getBlockRuntimeId() . " on " . $itemStringId . ")");
|
||||
}elseif($meta !== 0){
|
||||
$data->meta = $meta;
|
||||
}
|
||||
|
@ -94,9 +94,14 @@ foreach($files as $file){
|
||||
$newDiff = [];
|
||||
|
||||
foreach($target["simple"] as $oldId => $newId){
|
||||
if(($merged["simple"][$oldId] ?? null) !== $newId){
|
||||
$newDiff["renamedIds"][$oldId] = $newId;
|
||||
$previousNewId = $merged["simple"][$oldId] ?? null;
|
||||
if(
|
||||
$previousNewId === $newId || //if previous schemas already accounted for this
|
||||
($previousNewId !== null && isset($target["simple"][$previousNewId])) //or the item's ID has been changed for a second time
|
||||
){
|
||||
continue;
|
||||
}
|
||||
$newDiff["renamedIds"][$oldId] = $newId;
|
||||
}
|
||||
if(isset($newDiff["renamedIds"])){
|
||||
ksort($newDiff["renamedIds"], SORT_STRING);
|
||||
|
Reference in New Issue
Block a user