diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml
index a7a251c77..a1ef20982 100644
--- a/.github/workflows/discord-release-notify.yml
+++ b/.github/workflows/discord-release-notify.yml
@@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
- uses: shivammathur/setup-php@2.21.2
+ uses: shivammathur/setup-php@2.22.0
with:
php-version: 8.0
diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml
index 9c26c3ff1..c8cc68bf7 100644
--- a/.github/workflows/draft-release.yml
+++ b/.github/workflows/draft-release.yml
@@ -18,7 +18,7 @@ jobs:
submodules: true
- name: Setup PHP
- uses: shivammathur/setup-php@2.21.2
+ uses: shivammathur/setup-php@2.22.0
with:
php-version: 8.0
@@ -69,7 +69,7 @@ jobs:
${{ github.workspace }}/build_info.json
- name: Create draft release
- uses: ncipollo/release-action@v1.11.1
+ uses: ncipollo/release-action@v1.12.0
with:
artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json
commit: ${{ github.sha }}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 44a672b73..653a56496 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -195,10 +195,12 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
- uses: shivammathur/setup-php@2.21.2
+ uses: shivammathur/setup-php@2.22.0
with:
php-version: 8.0
tools: php-cs-fixer:3.11
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run PHP-CS-Fixer
run: php-cs-fixer fix --dry-run --diff --ansi
diff --git a/.github/workflows/support.yml b/.github/workflows/support.yml
index 44487684b..fe726dfef 100644
--- a/.github/workflows/support.yml
+++ b/.github/workflows/support.yml
@@ -8,7 +8,7 @@ jobs:
support:
runs-on: ubuntu-latest
steps:
- - uses: dessant/support-requests@v2
+ - uses: dessant/support-requests@v3
with:
github-token: ${{ github.token }}
support-label: "Support request"
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 1df291e8e..c3fac9558 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -62,6 +62,11 @@
+
+
+
+
+
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 70801c065..32af1ef48 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -66,6 +66,9 @@ BODY,
],
'indentation_type' => true,
'logical_operators' => true,
+ 'native_constant_invocation' => [
+ 'scope' => 'namespaced'
+ ],
'native_function_invocation' => [
'scope' => 'namespaced',
'include' => ['@all'],
@@ -92,6 +95,12 @@ BODY,
],
'sort_algorithm' => 'alpha'
],
+ 'phpdoc_align' => [
+ 'align' => 'vertical',
+ 'tags' => [
+ 'param',
+ ]
+ ],
'phpdoc_line_span' => [
'property' => 'single',
'method' => null,
diff --git a/build/generate-registry-annotations.php b/build/generate-registry-annotations.php
index 1198db207..f17e39170 100644
--- a/build/generate-registry-annotations.php
+++ b/build/generate-registry-annotations.php
@@ -39,6 +39,7 @@ use function sprintf;
use function str_replace;
use function substr;
use const SORT_STRING;
+use const STDERR;
if(count($argv) !== 2){
fwrite(STDERR, "Provide a path to process\n");
diff --git a/build/php b/build/php
index 14ed8eaad..6b605ed7c 160000
--- a/build/php
+++ b/build/php
@@ -1 +1 @@
-Subproject commit 14ed8eaadd921407c87be4964a8726b22427e80e
+Subproject commit 6b605ed7c458fc8f95080ffe41a7f248a7160107
diff --git a/build/server-phar.php b/build/server-phar.php
index f3d26a2c4..0665c89d5 100644
--- a/build/server-phar.php
+++ b/build/server-phar.php
@@ -40,12 +40,13 @@ use function rtrim;
use function sprintf;
use function str_replace;
use function unlink;
+use const DIRECTORY_SEPARATOR;
use const PHP_EOL;
require dirname(__DIR__) . '/vendor/autoload.php';
/**
- * @param string[] $strings
+ * @param string[] $strings
*
* @return string[]
*/
diff --git a/changelogs/4.10.md b/changelogs/4.10.md
new file mode 100644
index 000000000..c8ff6a045
--- /dev/null
+++ b/changelogs/4.10.md
@@ -0,0 +1,53 @@
+**For Minecraft: Bedrock Edition 1.19.40**
+
+### Note about API versions
+Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
+Plugin developers should **only** update their required API to this version if you need the changes in this build.
+
+**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
+
+# 4.10.0
+Released 26th October 2022.
+
+## General
+- Added support for Minecraft: Bedrock Edition 1.19.40.
+- Removed support for older versions.
+
+## Fixes
+- Fixed incorrect command descriptions showing in `/help` when multiple commands use the same name. Previously, the most recently registered command would show, even though it wouldn't actually be invoked.
+- Fixed splash potions affecting players in spectator mode.
+- Fixed `World->addParticle()` sending particles to players who couldn't possibly see them when a list of targets was used.
+- Fixed `World->addSound()` sending sounds to players who couldn't possibly hear them when a list of targets was used.
+
+## Documentation
+- Improved type information available for various API methods in `World`.
+
+# 4.10.1
+Released 7th November 2022.
+
+## Fixes
+- Fixed spawning in the void if spawn terrain in a world is solid at the default spawn position.
+- Fixed totems of undying activating when the player has 1 HP remaining.
+- Fixed durable items such as tools becoming unbreakable when in stacks larger than 1. Now, the durability correctly resets when the tool breaks.
+- TPS below 12 now correctly shows as red in `/status`. Previously, it showed as orange due to a condition ordering bug.
+- Improved handling of missing arguments in user-defined `pocketmine.yml` command aliases. Previously, missing arguments would be filled with an empty string, which caused a variety of unexpected behaviour.
+
+## Internals
+- Added validation for the array given to `BaseInventory->setContents()` to ensure that it contains only `Item` instances.
+- Silenced `PlayerAuthInputPacket` spam when the session is in the "spawn response" state.
+- Updated to PHPStan 1.9.
+
+# 4.10.2
+Released 25th November 2022.
+
+## Fixes
+- Fixed crashes on macOS and Linux when using console colours without the `TERM` environment variable set.
+- Fixed crashdumps not being generated when error messages contained invalid UTF-8 characters.
+
+## Documentation
+- Clarified documentation of caching behaviour for `Internet::getIP()`.
+- Added and improved documentation for many `Inventory` methods.
+- Rewritten documentation for `PlayerCreationEvent` with warnings and more detail.
+
+## Internals
+- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions.
diff --git a/changelogs/4.11-beta.md b/changelogs/4.11-beta.md
new file mode 100644
index 000000000..0d8bc257c
--- /dev/null
+++ b/changelogs/4.11-beta.md
@@ -0,0 +1,92 @@
+**For Minecraft: Bedrock Edition 1.19.40**
+
+This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
+
+### Note about API versions
+Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
+Plugin developers should **only** update their required API to this version if you need the changes in this build.
+
+**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
+
+# 4.11.0-BETA1
+Released 7th November 2022.
+
+## General
+- Packet receive timings have now been split into two subcategories - Decode and Handle.
+- Console command entry can now be disabled via the `console.enable-input` setting in `pocketmine.yml`.
+ - Best suited for headless servers (e.g. in a Docker container) where the console will never be used anyway.
+ - Disabling the console reader slightly reduces memory usage, because console reading currently requires an additional subprocess.
+- Console command output now appears on the terminal only, and is not written to the log file.
+- The output from console commands now appears with a `Command output |` prefix, instead of as a log message.
+- Introduced validation for the `--data` and `--plugins` command line options.
+- Encrypted resource packs are now supported, by means of adding a `.key` file alongside the pack in the `resource_packs` folder.
+ - e.g. `MyEncryptedPack.zip` -> `MyEncryptedPack.zip.key`
+
+## Gameplay
+- Fixed supporting blocks of dead bush to be in line with vanilla.
+- Sugarcane can now be grown using bonemeal on any part of the sugarcane. Previously, it only worked when used on the bottom block.
+- Fixed modifier values for Instant Damage and Regeneration effects.
+
+## API
+### General
+- Plugins are now always disabled before their dependencies, to ensure that they are able to shutdown properly (e.g. a core plugin depending on a database plugin may want to save data to a DB during `onDisable()`).
+- [`webmozart/path-util`](https://packagist.org/packages/webmozart/path-util) has been deprecated, and will be dropped in favour of [`symfony/filesystem`](https://packagist.org/packages/symfony/filesystem) in PM5.
+ - To prepare for this change, simply replace any usage of `Webmozart\PathUtil\Path` with `Symfony\Component\Filesystem\Path`, which is available as a dependency in this release.
+
+### `pocketmine`
+- The following API methods are now deprecated:
+ - `Server->getPlayerByPrefix()`
+
+### `pocketmine\entity`
+- `EntitySpawnEvent` and `ItemSpawnEvent` are now fired on the first tick after the entity is added to the world. Previously, these events were called directly from the entity constructor, making it impossible to get properties like velocity which are often set after the entity is created.
+- The following API methods are now deprecated:
+ - `Living->hasLineOfSight()`
+
+### `pocketmine\item`
+- The following new API methods have been added:
+ - `public Armor->clearCustomColor() : $this`
+
+### `pocketmine\inventory\transaction`
+- Introduced a `TransactionBuilder` class. This makes it less of a hassle to build an `InventoryTransaction` server-side, since the regular `Inventory` API methods can be used, rather than having to manually create `SlotChangeAction`s.
+
+### `pocketmine\player`
+- The following new API methods have been added:
+ - `public Player->sendToastNotification(string $title, string $body) : void` - makes a grey box appear at the top of the player's screen containing the specified message
+
+### `pocketmine\utils`
+- The following new API methods have been added:
+ - `public static TextFormat::addBase(string $baseFormat, string $string) : string` - used for coloured log messages, changes the base formatting of a string by inserting the given formatting codes after every RESET code
+
+## Internals
+- Improved performance of `ContainerTrait` dropping items on block destroy. (24e72ec109c1442b09558df89b6833cf2f2e0ec7)
+- Avoid repeated calls to `Position->getWorld()` (use local variables). (2940547026db40ce76deb46e992870de3ead79ad)
+- Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. (e90abecf38d9c57635fa0497514bba7e546a2469)
+- Console polling is now done on the main thread (no longer a performance concern).
+- Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie.
+- `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged.
+- `DelegateInventory` now uses `WeakReference` to track its inventory listener. This allows the delegate to be reused.
+
+# 4.11.0-BETA2
+Released 13th November 2022.
+
+## Configuration
+- The `chunk-ticking.per-tick` setting is now deprecated, and will be removed in a future release.
+ - The functionality of this setting has been removed, since it caused more problems than it solved.
+ - Setting it to zero will still disable chunk ticking (for now), but this should now be done by setting `chunk-ticking.tick-radius` to `0` instead.
+
+## Gameplay
+- Improved chunk random ticking:
+ - Removed the limit on chunks ticked per tick, and its associated config option is no longer respected.
+ - This change significantly improves crop and plant growth with large numbers of players, but may cause higher CPU usage.
+ - This limit was causing a linear decrease in chunk ticking speed with larger numbers of players, leading to worsened gameplay experience.
+ - Every chunk within the configured tick radius of a player will be ticked. Previously, chunks were randomly selected from the radius.
+- Implemented Darkness effect.
+
+## API
+### `pocketmine\world`
+- The following new API methods have been added:
+ - `public World->getChunkTickRadius() : int` - returns the world's simulation radius
+ - `public World->setChunkTickRadius(int $radius) : void` - sets the world's simulation radius
+
+## Internals
+- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions.
diff --git a/changelogs/4.11.md b/changelogs/4.11.md
new file mode 100644
index 000000000..96ab00a47
--- /dev/null
+++ b/changelogs/4.11.md
@@ -0,0 +1,106 @@
+**For Minecraft: Bedrock Edition 1.19.40**
+
+This is a minor feature release for PocketMine-MP, introducing some new features and improvements.
+
+### Note about API versions
+Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
+Plugin developers should **only** update their required API to this version if you need the changes in this build.
+
+**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
+
+# 4.11.0
+Released 25th November 2022.
+
+## General
+- Packet receive timings have now been split into two subcategories - Decode and Handle.
+- Console command entry can now be disabled via the `console.enable-input` setting in `pocketmine.yml`.
+ - Best suited for headless servers (e.g. in a Docker container) where the console will never be used anyway.
+ - Disabling the console reader slightly reduces memory usage, because console reading currently requires an additional subprocess.
+- Console command output now appears on the terminal only, and is not written to the log file.
+- The output from console commands now appears with a `Command output |` prefix, instead of as a log message.
+- User-defined `pocketmine.yml` custom commands now use a generic description which makes clear the command is config-defined.
+- Introduced validation for the `--data` and `--plugins` command line options.
+- Encrypted resource packs are now supported, by means of adding a `.key` file alongside the pack in the `resource_packs` folder.
+ - e.g. `MyEncryptedPack.zip` -> `MyEncryptedPack.zip.key`
+ - The file must contain the raw key bytes, and must not end with a newline.
+
+## Configuration
+- The `chunk-ticking.per-tick` setting is now deprecated, and will be removed in a future release.
+ - The functionality of this setting has been removed, since it caused more problems than it solved.
+ - Setting it to zero will still disable chunk ticking (for now), but this should now be done by setting `chunk-ticking.tick-radius` to `0` instead.
+
+## Gameplay
+- Fixed supporting blocks of dead bush to be in line with vanilla.
+- Sugarcane can now be grown using bonemeal on any part of the sugarcane. Previously, it only worked when used on the bottom block.
+- Fixed missing sounds when adding, rotating, or removing items in item frames.
+- Fixed modifier values for Instant Damage and Regeneration effects.
+- Implemented Darkness effect.
+- Improved chunk random ticking:
+ - Removed the limit on chunks ticked per tick, and its associated config option is no longer respected.
+ - This change significantly improves crop and plant growth with large numbers of players.
+ - This limit was causing a linear decrease in chunk ticking speed with larger numbers of players, leading to worsened gameplay experience.
+ - **Warning: This change will result in increased CPU usage if players are spread over a very large area.**
+ - Every chunk within the configured tick radius of a player will be ticked. Previously, chunks were randomly selected from the radius.
+
+## API
+### General
+- Plugins are now always disabled before their dependencies, to ensure that they are able to shutdown properly (e.g. a core plugin depending on a database plugin may want to save data to a DB during `onDisable()`).
+- [`webmozart/path-util`](https://packagist.org/packages/webmozart/path-util) has been deprecated, and will be dropped in favour of [`symfony/filesystem`](https://packagist.org/packages/symfony/filesystem) in PM5.
+ - To prepare for this change, simply replace any usage of `Webmozart\PathUtil\Path` with `Symfony\Component\Filesystem\Path`, which is available as a dependency in this release.
+
+### `pocketmine`
+- The following API methods are now deprecated:
+ - `Server->getPlayerByPrefix()`
+
+### `pocketmine\entity`
+- `EntitySpawnEvent` and `ItemSpawnEvent` are now fired on the first tick after the entity is added to the world. Previously, these events were called directly from the entity constructor, making it impossible to get properties like velocity which are often set after the entity is created.
+- The following API methods are now deprecated:
+ - `Living->hasLineOfSight()`
+
+### `pocketmine\event\block`
+- The following new classes have been added:
+ - `BlockDeathEvent` - event called when coral or coral blocks die due to lack of water
+
+### `pocketmine\item`
+- The following new API methods have been added:
+ - `public Armor->clearCustomColor() : $this`
+
+### `pocketmine\inventory\transaction`
+- Introduced a `TransactionBuilder` class. This makes it less of a hassle to build an `InventoryTransaction` server-side, since the regular `Inventory` API methods can be used, rather than having to manually create `SlotChangeAction`s.
+
+### `pocketmine\lang`
+- The following new API methods have been added:
+ - `public Language->getAll() : array`
+
+### `pocketmine\player`
+- The following new API methods have been added:
+ - `public Player->sendToastNotification(string $title, string $body) : void` - makes a grey box appear at the top of the player's screen containing the specified message
+
+### `pocketmine\utils`
+- The following new API methods have been added:
+ - `public static TextFormat::addBase(string $baseFormat, string $string) : string` - used for coloured log messages, changes the base formatting of a string by inserting the given formatting codes after every RESET code
+
+### `pocketmine\world`
+- The following new API methods have been added:
+ - `public World->getChunkTickRadius() : int` - returns the world's simulation radius
+ - `public World->setChunkTickRadius(int $radius) : void` - sets the world's simulation radius
+
+### `pocketmine\world\sound`
+- The following new classes have been added:
+ - `ItemFrameAddItemSound`
+ - `ItemFrameRemoveItemSound`
+ - `ItemFrameRotateItemSound`
+
+## Internals
+- Improved performance of `ContainerTrait` dropping items on block destroy. ([link](https://github.com/pmmp/PocketMine-MP/commits/24e72ec109c1442b09558df89b6833cf2f2e0ec7))
+- Avoid repeated calls to `Position->getWorld()` (use local variables). ([link](https://github.com/pmmp/PocketMine-MP/commit/2940547026db40ce76deb46e992870de3ead79ad))
+- Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. ([link](https://github.com/pmmp/PocketMine-MP/commit/e90abecf38d9c57635fa0497514bba7e546a2469))
+ - Inventories are now mapped on a per-slot basis. This means that more than one inventory can be mapped to the same window ID, which is necessary for correctly handling "UI" inventories like crafting tables.
+ - `InventoryManager->getWindow(int $windowId) : ?Inventory` is replaced by `locateWindowAndSlot` (see below).
+ - Added `InventoryManager->locateWindowAndSlot(int $windowId, int $netSlotId) : array{Inventory, int}` - accepts a window ID and absolute slot ID, and returns the associated inventory and the slot relative to the inventory's own start (for use with `getItem()` etc.).
+ - Slot offset mapping for "UI" inventories is now handled in `InventoryManager->createComplexSlotMapping()` instead of in `TypeConverter`.
+- Console polling is now done on the main thread (no longer a performance concern). ([link](https://github.com/pmmp/PocketMine-MP/commit/b3f03d7ae645de67a54b7300c09b94eeca16298e))
+- Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. ([link](https://github.com/pmmp/PocketMine-MP/commit/2585160ca2c4df5758b8b980331307402ff9f0fb))
+- `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. ([link](https://github.com/pmmp/PocketMine-MP/commit/83e5b0adb6fa0dddec377182bb1c7945ac8f7820))
+- `DelegateInventory` now uses `WeakReference` to track its inventory listener. This allows the delegate to be reused. ([link](https://github.com/pmmp/PocketMine-MP/commit/3feaa18f6c10c3a99c0deca75f57ec2d74b92ab4))
+- Non-arrow projectile damage is now unscaled. Scaling according to velocity is only applied to arrows. This currently doesn't cause any observable change in behaviour, but is required for future additions.
\ No newline at end of file
diff --git a/changelogs/4.12.md b/changelogs/4.12.md
new file mode 100644
index 000000000..066a5ec0b
--- /dev/null
+++ b/changelogs/4.12.md
@@ -0,0 +1,46 @@
+**For Minecraft: Bedrock Edition 1.19.50**
+
+### Note about API versions
+Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
+Plugin developers should **only** update their required API to this version if you need the changes in this build.
+
+**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
+
+# 4.12.0
+Released 30th November 2022.
+
+## General
+- Added support for Minecraft: Bedrock Edition 1.19.50.
+- Removed support for older versions.
+
+# 4.12.1
+Released 4th December 2022.
+
+## Fixes
+- Fixed items glitching when dragging a stack of items across the crafting grid (desync issues).
+
+# 4.12.2
+Released 15th December 2022.
+
+## Fixes
+- Folder used for plugins (optionally specified by `--plugins`) is no longer required to be writable.
+- Fixed broken writable check for server data folder (`is_writable()` broken on NFS and similar filesystems).
+- `Filesystem::createLockFile()` exceptions now include more information about why the lock file could not be created.
+- Fixed client-side item predictions not being rolled back when cancelling events such as `PlayerItemUseEvent`.
+
+## Dependencies
+- Updated BedrockProtocol to [17.1.0](https://github.com/pmmp/BedrockProtocol/releases/tag/17.1.0+bedrock-1.19.50). This adds some missing `LevelSoundEvent` constants and fixes the values for `ContainerUIIds`.
+
+# 4.12.3
+Released 28th December 2022.
+
+## Fixes
+- Fixed unauthenticated connections taking up player count slots, preventing players from joining.
+- Fixed a possible crash in `World->tickChunk()` when plugins unload chunks during some events.
+- `/gamemode` will now report a failure to change game mode if the player is already in the requested game mode.
+
+# 4.12.4
+Released 3rd January 2023.
+
+## Fixes
+- Added workarounds for an active exploit being used to deny service to servers.
\ No newline at end of file
diff --git a/composer.json b/composer.json
index ba334459b..90c838f18 100644
--- a/composer.json
+++ b/composer.json
@@ -34,14 +34,14 @@
"adhocore/json-comment": "^1.1",
"fgrosse/phpasn1": "^2.3",
"netresearch/jsonmapper": "^4.0",
- "pocketmine/bedrock-data": "~1.11.0+bedrock-1.19.30",
- "pocketmine/bedrock-protocol": "~13.0.0+bedrock-1.19.30",
+ "pocketmine/bedrock-data": "~1.13.0+bedrock-1.19.50",
+ "pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0",
"pocketmine/errorhandler": "^0.6.0",
- "pocketmine/locale-data": "~2.8.0 <2.8.9",
+ "pocketmine/locale-data": "~2.11.0",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",
"pocketmine/math": "^0.4.0",
@@ -54,7 +54,7 @@
"webmozart/path-util": "^2.3"
},
"require-dev": {
- "phpstan/phpstan": "1.8.9",
+ "phpstan/phpstan": "1.9.4",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"
diff --git a/composer.lock b/composer.lock
index ede3e8afe..fd1325531 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "f652fc7867f7fd3d183df26e44658cd0",
+ "content-hash": "393c7921d03d080d3ef3b836f90b4415",
"packages": [
{
"name": "adhocore/json-comment",
@@ -123,24 +123,24 @@
},
{
"name": "fgrosse/phpasn1",
- "version": "v2.4.0",
+ "version": "v2.5.0",
"source": {
"type": "git",
"url": "https://github.com/fgrosse/PHPASN1.git",
- "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296"
+ "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/eef488991d53e58e60c9554b09b1201ca5ba9296",
- "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296",
+ "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/42060ed45344789fb9f21f9f1864fc47b9e3507b",
+ "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b",
"shasum": ""
},
"require": {
- "php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0"
+ "php": "^7.1 || ^8.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "~2.0",
- "phpunit/phpunit": "^6.3 || ^7.0 || ^8.0"
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
},
"suggest": {
"ext-bcmath": "BCmath is the fallback extension for big integer calculations",
@@ -192,22 +192,23 @@
],
"support": {
"issues": "https://github.com/fgrosse/PHPASN1/issues",
- "source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0"
+ "source": "https://github.com/fgrosse/PHPASN1/tree/v2.5.0"
},
- "time": "2021-12-11T12:41:06+00:00"
+ "abandoned": true,
+ "time": "2022-12-19T11:08:26+00:00"
},
{
"name": "netresearch/jsonmapper",
- "version": "v4.0.0",
+ "version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
- "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d"
+ "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
- "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
+ "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"shasum": ""
},
"require": {
@@ -243,22 +244,22 @@
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
- "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0"
+ "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0"
},
- "time": "2020-12-01T19:48:11+00:00"
+ "time": "2022-12-08T20:46:14+00:00"
},
{
"name": "pocketmine/bedrock-data",
- "version": "1.11.1+bedrock-1.19.30",
+ "version": "1.13.0+bedrock-1.19.50",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
- "reference": "9ec9a9645ba19f04dd4e39d6d9bd30b562dfe90c"
+ "reference": "57337ddc9433a0e245a1ce48c51af05f0573d58d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/9ec9a9645ba19f04dd4e39d6d9bd30b562dfe90c",
- "reference": "9ec9a9645ba19f04dd4e39d6d9bd30b562dfe90c",
+ "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/57337ddc9433a0e245a1ce48c51af05f0573d58d",
+ "reference": "57337ddc9433a0e245a1ce48c51af05f0573d58d",
"shasum": ""
},
"type": "library",
@@ -269,22 +270,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/1.11.1+bedrock-1.19.30"
+ "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.19.50"
},
- "time": "2022-09-27T22:00:01+00:00"
+ "time": "2022-11-30T16:19:59+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
- "version": "13.0.0+bedrock-1.19.30",
+ "version": "17.1.0+bedrock-1.19.50",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
- "reference": "94de2221676ca717587e1ff4e45445c24ada1749"
+ "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/94de2221676ca717587e1ff4e45445c24ada1749",
- "reference": "94de2221676ca717587e1ff4e45445c24ada1749",
+ "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c572706cf5e3202718dd35a35dd30fe08cd671de",
+ "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de",
"shasum": ""
},
"require": {
@@ -298,7 +299,7 @@
"ramsey/uuid": "^4.1"
},
"require-dev": {
- "phpstan/phpstan": "1.8.0",
+ "phpstan/phpstan": "1.9.3",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5"
@@ -316,9 +317,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
- "source": "https://github.com/pmmp/BedrockProtocol/tree/bedrock-1.19.30"
+ "source": "https://github.com/pmmp/BedrockProtocol/tree/17.1.0+bedrock-1.19.50"
},
- "time": "2022-09-20T18:35:00+00:00"
+ "time": "2022-12-15T20:34:49+00:00"
},
{
"name": "pocketmine/binaryutils",
@@ -536,16 +537,16 @@
},
{
"name": "pocketmine/locale-data",
- "version": "2.8.7",
+ "version": "2.11.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
- "reference": "e115d3d64a508065f1cedad1be55528906308456"
+ "reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pmmp/Language/zipball/e115d3d64a508065f1cedad1be55528906308456",
- "reference": "e115d3d64a508065f1cedad1be55528906308456",
+ "url": "https://api.github.com/repos/pmmp/Language/zipball/4b33d8fa53eda53d9662a7478806ebae2e4a5c53",
+ "reference": "4b33d8fa53eda53d9662a7478806ebae2e4a5c53",
"shasum": ""
},
"type": "library",
@@ -553,9 +554,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
- "source": "https://github.com/pmmp/Language/tree/2.8.7"
+ "source": "https://github.com/pmmp/Language/tree/2.11.0"
},
- "time": "2022-08-21T20:37:16+00:00"
+ "time": "2022-11-25T14:24:34+00:00"
},
{
"name": "pocketmine/log",
@@ -930,21 +931,20 @@
},
{
"name": "ramsey/uuid",
- "version": "4.5.1",
+ "version": "4.6.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
- "reference": "a161a26d917604dc6d3aa25100fddf2556e9f35d"
+ "reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ramsey/uuid/zipball/a161a26d917604dc6d3aa25100fddf2556e9f35d",
- "reference": "a161a26d917604dc6d3aa25100fddf2556e9f35d",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/ad63bc700e7d021039e30ce464eba384c4a1d40f",
+ "reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f",
"shasum": ""
},
"require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10",
- "ext-ctype": "*",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.0"
@@ -976,7 +976,6 @@
},
"suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
- "ext-ctype": "Enables faster processing of character classification using ctype functions.",
"ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
"ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
"paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
@@ -1008,7 +1007,7 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
- "source": "https://github.com/ramsey/uuid/tree/4.5.1"
+ "source": "https://github.com/ramsey/uuid/tree/4.6.0"
},
"funding": [
{
@@ -1020,7 +1019,7 @@
"type": "tidelift"
}
],
- "time": "2022-09-16T03:22:46+00:00"
+ "time": "2022-11-05T23:03:38+00:00"
},
{
"name": "symfony/filesystem",
@@ -1088,16 +1087,16 @@
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.26.0",
+ "version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
+ "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
- "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
+ "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": ""
},
"require": {
@@ -1112,7 +1111,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.26-dev"
+ "dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1150,7 +1149,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
},
"funding": [
{
@@ -1166,20 +1165,20 @@
"type": "tidelift"
}
],
- "time": "2022-05-24T11:49:31+00:00"
+ "time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.26.0",
+ "version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
- "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": ""
},
"require": {
@@ -1194,7 +1193,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.26-dev"
+ "dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1233,7 +1232,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
},
"funding": [
{
@@ -1249,20 +1248,20 @@
"type": "tidelift"
}
],
- "time": "2022-05-24T11:49:31+00:00"
+ "time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.26.0",
+ "version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
+ "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
- "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+ "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"shasum": ""
},
"require": {
@@ -1271,7 +1270,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.26-dev"
+ "dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1316,7 +1315,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
},
"funding": [
{
@@ -1332,20 +1331,20 @@
"type": "tidelift"
}
],
- "time": "2022-05-10T07:21:04+00:00"
+ "time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-php81",
- "version": "v1.26.0",
+ "version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
- "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
+ "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
- "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
+ "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
"shasum": ""
},
"require": {
@@ -1354,7 +1353,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.26-dev"
+ "dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1395,7 +1394,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
},
"funding": [
{
@@ -1411,7 +1410,7 @@
"type": "tidelift"
}
],
- "time": "2022-05-24T11:49:31+00:00"
+ "time": "2022-11-03T14:55:06+00:00"
},
{
"name": "webmozart/assert",
@@ -1655,16 +1654,16 @@
},
{
"name": "nikic/php-parser",
- "version": "v4.15.1",
+ "version": "v4.15.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900"
+ "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
- "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
+ "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
"shasum": ""
},
"require": {
@@ -1705,9 +1704,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
},
- "time": "2022-09-04T07:30:47+00:00"
+ "time": "2022-11-12T15:38:23+00:00"
},
{
"name": "phar-io/manifest",
@@ -1822,16 +1821,16 @@
},
{
"name": "phpstan/phpstan",
- "version": "1.8.9",
+ "version": "1.9.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "3a72d9d9f2528fbd50c2d8fcf155fd9f74ade3f2"
+ "reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3a72d9d9f2528fbd50c2d8fcf155fd9f74ade3f2",
- "reference": "3a72d9d9f2528fbd50c2d8fcf155fd9f74ade3f2",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03bccee595e2146b7c9d174486b84f4dc61b0f2",
+ "reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2",
"shasum": ""
},
"require": {
@@ -1861,7 +1860,7 @@
],
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
- "source": "https://github.com/phpstan/phpstan/tree/1.8.9"
+ "source": "https://github.com/phpstan/phpstan/tree/1.9.4"
},
"funding": [
{
@@ -1877,25 +1876,25 @@
"type": "tidelift"
}
],
- "time": "2022-10-13T13:40:18+00:00"
+ "time": "2022-12-17T13:33:52+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
- "version": "1.1.1",
+ "version": "1.3.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
- "reference": "4a3c437c09075736285d1cabb5c75bf27ed0bc84"
+ "reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/4a3c437c09075736285d1cabb5c75bf27ed0bc84",
- "reference": "4a3c437c09075736285d1cabb5c75bf27ed0bc84",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/54a24bd23e9e80ee918cdc24f909d376c2e273f7",
+ "reference": "54a24bd23e9e80ee918cdc24f909d376c2e273f7",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
- "phpstan/phpstan": "^1.5.0"
+ "phpstan/phpstan": "^1.9.3"
},
"conflict": {
"phpunit/phpunit": "<7.0"
@@ -1927,9 +1926,9 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
- "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.1.1"
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.3"
},
- "time": "2022-04-20T15:24:25+00:00"
+ "time": "2022-12-21T15:25:00+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
@@ -1981,16 +1980,16 @@
},
{
"name": "phpunit/php-code-coverage",
- "version": "9.2.17",
+ "version": "9.2.22",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8"
+ "reference": "e4bf60d2220b4baaa0572986b5d69870226b06df"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8",
- "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e4bf60d2220b4baaa0572986b5d69870226b06df",
+ "reference": "e4bf60d2220b4baaa0572986b5d69870226b06df",
"shasum": ""
},
"require": {
@@ -2046,7 +2045,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.22"
},
"funding": [
{
@@ -2054,7 +2053,7 @@
"type": "github"
}
],
- "time": "2022-08-30T12:24:04+00:00"
+ "time": "2022-12-18T16:40:55+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -2299,16 +2298,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.5.25",
+ "version": "9.5.27",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d"
+ "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
- "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2bc7ffdca99f92d959b3f2270529334030bba38",
+ "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38",
"shasum": ""
},
"require": {
@@ -2381,7 +2380,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.25"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.27"
},
"funding": [
{
@@ -2397,7 +2396,7 @@
"type": "tidelift"
}
],
- "time": "2022-09-25T03:44:45+00:00"
+ "time": "2022-12-09T07:31:23+00:00"
},
{
"name": "sebastian/cli-parser",
diff --git a/resources/pocketmine.yml b/resources/pocketmine.yml
index 4ad8ea9c0..ac60afe53 100644
--- a/resources/pocketmine.yml
+++ b/resources/pocketmine.yml
@@ -119,8 +119,6 @@ chunk-sending:
spawn-radius: 4
chunk-ticking:
- #Max amount of chunks processed each tick
- per-tick: 40
#Radius of chunks around a player to tick
tick-radius: 3
#Number of blocks inside ticking areas' subchunks that get ticked every tick. Higher values will accelerate events
diff --git a/src/MemoryManager.php b/src/MemoryManager.php
index cf7bc745d..9eecd370e 100644
--- a/src/MemoryManager.php
+++ b/src/MemoryManager.php
@@ -286,7 +286,7 @@ class MemoryManager{
/**
* Static memory dumper accessible from any thread.
*
- * @param mixed $startingObject
+ * @param mixed $startingObject
*/
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{
$hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist");
@@ -398,7 +398,7 @@ class MemoryManager{
do{
$continue = false;
- foreach($objects as $hash => $object){
+ foreach(Utils::stringifyKeys($objects) as $hash => $object){
if(!is_object($object)){
continue;
}
@@ -480,9 +480,14 @@ class MemoryManager{
/**
* @param mixed $from
- * @param object[] $objects reference parameter
+ * @param object[] $objects reference parameter
* @param int[] $refCounts reference parameter
*
+ * @phpstan-param array $objects
+ * @phpstan-param array $refCounts
+ * @phpstan-param-out array $objects
+ * @phpstan-param-out array $refCounts
+ *
* @return mixed
*/
private static function continueDump($from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize){
diff --git a/src/PocketMine.php b/src/PocketMine.php
index faccb27e1..f49115826 100644
--- a/src/PocketMine.php
+++ b/src/PocketMine.php
@@ -39,11 +39,14 @@ namespace pocketmine {
use function extension_loaded;
use function function_exists;
use function getcwd;
+ use function is_dir;
+ use function mkdir;
use function phpversion;
use function preg_match;
use function preg_quote;
use function realpath;
use function version_compare;
+ use const DIRECTORY_SEPARATOR;
require_once __DIR__ . '/VersionInfo.php';
@@ -201,6 +204,22 @@ JIT_WARNING
ini_set('assert.exception', '1');
}
+ function getopt_string(string $opt) : ?string{
+ $opts = getopt("", ["$opt:"]);
+ if(isset($opts[$opt])){
+ if(is_string($opts[$opt])){
+ return $opts[$opt];
+ }
+ if(is_array($opts[$opt])){
+ critical_error("Cannot specify --$opt multiple times");
+ }else{
+ critical_error("Missing value for --$opt");
+ }
+ exit(1);
+ }
+ return null;
+ }
+
/**
* @return void
*/
@@ -252,27 +271,42 @@ JIT_WARNING
ErrorToExceptionHandler::set();
- $opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-ansi", "disable-ansi"]);
-
$cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd())));
- $dataPath = isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR;
- $pluginPath = isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : $cwd . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR;
+ $dataPath = getopt_string("data") ?? $cwd;
+ $pluginPath = getopt_string("plugins") ?? $cwd . DIRECTORY_SEPARATOR . "plugins";
Filesystem::addCleanedPath($pluginPath, Filesystem::CLEAN_PATH_PLUGINS_PREFIX);
- if(!file_exists($dataPath)){
- mkdir($dataPath, 0777, true);
+ if(!@mkdir($dataPath, 0777, true) && !is_dir($dataPath)){
+ critical_error("Unable to create/access data directory at $dataPath. Check that the target location is accessible by the current user.");
+ exit(1);
}
+ //this has to be done after we're sure the data path exists
+ $dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR;
$lockFilePath = Path::join($dataPath, 'server.lock');
- if(($pid = Filesystem::createLockFile($lockFilePath)) !== null){
+ try{
+ $pid = Filesystem::createLockFile($lockFilePath);
+ }catch(\InvalidArgumentException $e){
+ critical_error($e->getMessage());
+ critical_error("Please ensure that there is enough space on the disk and that the current user has read/write permissions to the selected data directory $dataPath.");
+ exit(1);
+ }
+ if($pid !== null){
critical_error("Another " . VersionInfo::NAME . " instance (PID $pid) is already using this folder (" . realpath($dataPath) . ").");
critical_error("Please stop the other server first before running a new one.");
exit(1);
}
+ if(!@mkdir($pluginPath, 0777, true) && !is_dir($pluginPath)){
+ critical_error("Unable to create plugin directory at $pluginPath. Check that the target location is accessible by the current user.");
+ exit(1);
+ }
+ $pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR;
+
//Logger has a dependency on timezone
Timezone::init();
+ $opts = getopt("", ["no-wizard", "enable-ansi", "disable-ansi"]);
if(isset($opts["enable-ansi"])){
Terminal::init(true);
}elseif(isset($opts["disable-ansi"])){
diff --git a/src/Server.php b/src/Server.php
index 147e5a00b..a29cb29fc 100644
--- a/src/Server.php
+++ b/src/Server.php
@@ -1268,7 +1268,7 @@ class Server{
}
/**
- * @param CommandSender[]|null $recipients
+ * @param CommandSender[]|null $recipients
*/
public function broadcastMessage(Translatable|string $message, ?array $recipients = null) : int{
$recipients = $recipients ?? $this->getBroadcastChannelSubscribers(self::BROADCAST_CHANNEL_USERS);
@@ -1321,9 +1321,9 @@ class Server{
}
/**
- * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
- * @param int $stay Duration in ticks to stay on screen for
- * @param int $fadeOut Duration in ticks for fade-out.
+ * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
+ * @param int $stay Duration in ticks to stay on screen for
+ * @param int $fadeOut Duration in ticks for fade-out.
* @param Player[]|null $recipients
*/
public function broadcastTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1, ?array $recipients = null) : int{
diff --git a/src/ServerConfigGroup.php b/src/ServerConfigGroup.php
index 0a3b09c87..bf82f534a 100644
--- a/src/ServerConfigGroup.php
+++ b/src/ServerConfigGroup.php
@@ -44,7 +44,7 @@ final class ServerConfigGroup{
){}
/**
- * @param mixed $defaultValue
+ * @param mixed $defaultValue
*
* @return mixed
*/
diff --git a/src/VersionInfo.php b/src/VersionInfo.php
index 36d399fc8..95668c9d2 100644
--- a/src/VersionInfo.php
+++ b/src/VersionInfo.php
@@ -31,9 +31,9 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
- public const BASE_VERSION = "4.9.2";
+ public const BASE_VERSION = "4.12.5";
public const IS_DEVELOPMENT_BUILD = true;
- public const BUILD_CHANNEL = "beta";
+ public const BUILD_CHANNEL = "stable";
private function __construct(){
//NOOP
diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php
index 2eeb42d74..5b3f9acce 100644
--- a/src/block/BaseBanner.php
+++ b/src/block/BaseBanner.php
@@ -87,7 +87,7 @@ abstract class BaseBanner extends Transparent{
}
/**
- * @param BannerPatternLayer[] $patterns
+ * @param BannerPatternLayer[] $patterns
*
* @phpstan-param list $patterns
* @return $this
diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php
index f3bb9255d..0912a85ba 100644
--- a/src/block/BaseCoral.php
+++ b/src/block/BaseCoral.php
@@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\SupportType;
+use pocketmine\event\block\BlockDeathEvent;
use pocketmine\item\Item;
abstract class BaseCoral extends Transparent{
@@ -50,7 +51,11 @@ abstract class BaseCoral extends Transparent{
//TODO: check water inside the block itself (not supported on the API yet)
if(!$hasWater){
- $world->setBlock($this->position, $this->setDead(true));
+ $ev = new BlockDeathEvent($this, $this->setDead(true));
+ $ev->call();
+ if(!$ev->isCancelled()){
+ $world->setBlock($this->position, $ev->getNewState());
+ }
}
}
}
diff --git a/src/block/Block.php b/src/block/Block.php
index 6f2f1b27e..2e6dcb4ef 100644
--- a/src/block/Block.php
+++ b/src/block/Block.php
@@ -62,7 +62,7 @@ class Block{
protected ?array $collisionBoxes = null;
/**
- * @param string $name English name of the block type (TODO: implement translations)
+ * @param string $name English name of the block type (TODO: implement translations)
*/
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
if(($idInfo->getVariant() & $this->getStateBitmask()) !== 0){
diff --git a/src/block/BlockFactory.php b/src/block/BlockFactory.php
index 8aabeb9ca..ec97df002 100644
--- a/src/block/BlockFactory.php
+++ b/src/block/BlockFactory.php
@@ -968,7 +968,7 @@ class BlockFactory{
* NOTE: If you are registering a new block type, you will need to add it to the creative inventory yourself - it
* will not automatically appear there.
*
- * @param bool $override Whether to override existing registrations
+ * @param bool $override Whether to override existing registrations
*
* @throws \RuntimeException if something attempted to override an already-registered block without specifying the
* $override parameter.
diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php
index e29591b3d..0718112eb 100644
--- a/src/block/CoralBlock.php
+++ b/src/block/CoralBlock.php
@@ -27,6 +27,7 @@ use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\data\bedrock\CoralTypeIdMap;
+use pocketmine\event\block\BlockDeathEvent;
use pocketmine\item\Item;
use function mt_rand;
@@ -77,7 +78,11 @@ final class CoralBlock extends Opaque{
}
}
if(!$hasWater){
- $world->setBlock($this->position, $this->setDead(true));
+ $ev = new BlockDeathEvent($this, $this->setDead(true));
+ $ev->call();
+ if(!$ev->isCancelled()){
+ $world->setBlock($this->position, $ev->getNewState());
+ }
}
}
}
diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php
index d71621943..8df62c72f 100644
--- a/src/block/ItemFrame.php
+++ b/src/block/ItemFrame.php
@@ -31,6 +31,9 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
+use pocketmine\world\sound\ItemFrameAddItemSound;
+use pocketmine\world\sound\ItemFrameRemoveItemSound;
+use pocketmine\world\sound\ItemFrameRotateItemSound;
use function is_infinite;
use function is_nan;
use function lcg_value;
@@ -136,8 +139,12 @@ class ItemFrame extends Flowable{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->framedItem !== null){
$this->itemRotation = ($this->itemRotation + 1) % self::ROTATIONS;
+
+ $this->position->getWorld()->addSound($this->position, new ItemFrameRotateItemSound());
}elseif(!$item->isNull()){
$this->framedItem = $item->pop();
+
+ $this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
}else{
return true;
}
@@ -154,6 +161,7 @@ class ItemFrame extends Flowable{
$world = $this->position->getWorld();
if(lcg_value() <= $this->itemDropChance){
$world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem);
+ $world->addSound($this->position, new ItemFrameRemoveItemSound());
}
$this->setFramedItem(null);
$world->setBlock($this->position, $this);
diff --git a/src/block/Leaves.php b/src/block/Leaves.php
index c5d2a5c2a..c845176e3 100644
--- a/src/block/Leaves.php
+++ b/src/block/Leaves.php
@@ -84,6 +84,7 @@ class Leaves extends Transparent{
/**
* @param true[] $visited reference parameter
* @phpstan-param array $visited
+ * @phpstan-param-out array $visited
*/
protected function findLog(Vector3 $pos, array &$visited = [], int $distance = 0) : bool{
$index = World::blockHash($pos->x, $pos->y, $pos->z);
diff --git a/src/block/tile/Banner.php b/src/block/tile/Banner.php
index 0af4b2e79..191d4c8a5 100644
--- a/src/block/tile/Banner.php
+++ b/src/block/tile/Banner.php
@@ -135,7 +135,7 @@ class Banner extends Spawnable{
}
/**
- * @param BannerPatternLayer[] $patterns
+ * @param BannerPatternLayer[] $patterns
*
* @phpstan-param list $patterns
*/
diff --git a/src/command/CommandExecutor.php b/src/command/CommandExecutor.php
index 138c4f22a..1356b7581 100644
--- a/src/command/CommandExecutor.php
+++ b/src/command/CommandExecutor.php
@@ -26,7 +26,7 @@ namespace pocketmine\command;
interface CommandExecutor{
/**
- * @param string[] $args
+ * @param string[] $args
*/
public function onCommand(CommandSender $sender, Command $command, string $label, array $args) : bool;
diff --git a/src/command/FormattedCommandAlias.php b/src/command/FormattedCommandAlias.php
index 21d2c0e70..df2b8f4fe 100644
--- a/src/command/FormattedCommandAlias.php
+++ b/src/command/FormattedCommandAlias.php
@@ -28,9 +28,9 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
-use function array_map;
use function array_shift;
use function count;
+use function implode;
use function preg_match;
use function strlen;
use function strpos;
@@ -52,7 +52,7 @@ class FormattedCommandAlias extends Command{
string $alias,
private array $formatStrings
){
- parent::__construct($alias);
+ parent::__construct($alias, KnownTranslationFactory::pocketmine_command_userDefined_description());
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
@@ -62,7 +62,20 @@ class FormattedCommandAlias extends Command{
foreach($this->formatStrings as $formatString){
try{
$formatArgs = CommandStringHelper::parseQuoteAware($formatString);
- $commands[] = array_map(fn(string $formatArg) => $this->buildCommand($formatArg, $args), $formatArgs);
+ $unresolved = [];
+ $processedArgs = [];
+ foreach($formatArgs as $formatArg){
+ $processedArg = $this->buildCommand($formatArg, $args);
+ if($processedArg === null){
+ $unresolved[] = $formatArg;
+ }elseif(count($unresolved) !== 0){
+ //unresolved args are OK only if they are at the end of the string - we can't have holes in the args list
+ throw new \InvalidArgumentException("Unable to resolve format arguments (" . implode(", ", $unresolved) . ") in command string \"$formatString\" due to missing arguments");
+ }else{
+ $processedArgs[] = $processedArg;
+ }
+ }
+ $commands[] = $processedArgs;
}catch(\InvalidArgumentException $e){
$sender->sendMessage(TextFormat::RED . $e->getMessage());
return false;
@@ -107,7 +120,7 @@ class FormattedCommandAlias extends Command{
/**
* @param string[] $args
*/
- private function buildCommand(string $formatString, array $args) : string{
+ private function buildCommand(string $formatString, array $args) : ?string{
$index = 0;
while(($index = strpos($formatString, '$', $index)) !== false){
$start = $index;
@@ -129,6 +142,9 @@ class FormattedCommandAlias extends Command{
}
$replacement = self::buildReplacement($args, $position, $rest);
+ if($replacement === null){
+ return null;
+ }
$end = $index + strlen($fullPlaceholder);
$formatString = substr($formatString, 0, $start) . $replacement . substr($formatString, $end);
@@ -143,9 +159,9 @@ class FormattedCommandAlias extends Command{
* @param string[] $args
* @phpstan-param list $args
*/
- private static function buildReplacement(array $args, int $position, bool $rest) : string{
- $replacement = "";
+ private static function buildReplacement(array $args, int $position, bool $rest) : ?string{
if($rest && $position < count($args)){
+ $replacement = "";
for($i = $position, $c = count($args); $i < $c; ++$i){
if($i !== $position){
$replacement .= " ";
@@ -153,11 +169,13 @@ class FormattedCommandAlias extends Command{
$replacement .= $args[$i];
}
+
+ return $replacement;
}elseif($position < count($args)){
- $replacement .= $args[$position];
+ return $args[$position];
}
- return $replacement;
+ return null;
}
/**
diff --git a/src/command/SimpleCommandMap.php b/src/command/SimpleCommandMap.php
index 07afecf6d..a618dd1c0 100644
--- a/src/command/SimpleCommandMap.php
+++ b/src/command/SimpleCommandMap.php
@@ -139,7 +139,7 @@ class SimpleCommandMap implements CommandMap{
public function register(string $fallbackPrefix, Command $command, ?string $label = null) : bool{
if($label === null){
- $label = $command->getName();
+ $label = $command->getLabel();
}
$label = trim($label);
$fallbackPrefix = strtolower(trim($fallbackPrefix));
diff --git a/src/command/defaults/GamemodeCommand.php b/src/command/defaults/GamemodeCommand.php
index 1363d34dc..81d4fd3f8 100644
--- a/src/command/defaults/GamemodeCommand.php
+++ b/src/command/defaults/GamemodeCommand.php
@@ -72,6 +72,11 @@ class GamemodeCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException();
}
+ if($target->getGamemode()->equals($gameMode)){
+ $sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
+ return true;
+ }
+
$target->setGamemode($gameMode);
if(!$gameMode->equals($target->getGamemode())){
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
diff --git a/src/command/defaults/HelpCommand.php b/src/command/defaults/HelpCommand.php
index 4ed5d4275..9eec9bc68 100644
--- a/src/command/defaults/HelpCommand.php
+++ b/src/command/defaults/HelpCommand.php
@@ -76,11 +76,10 @@ class HelpCommand extends VanillaCommand{
$pageHeight = $sender->getScreenLineHeight();
if($commandName === ""){
- /** @var Command[][] $commands */
$commands = [];
foreach($sender->getServer()->getCommandMap()->getCommands() as $command){
if($command->testPermissionSilent($sender)){
- $commands[$command->getName()] = $command;
+ $commands[$command->getLabel()] = $command;
}
}
ksort($commands, SORT_NATURAL | SORT_FLAG_CASE);
@@ -95,7 +94,7 @@ class HelpCommand extends VanillaCommand{
foreach($commands[$pageNumber - 1] as $command){
$description = $command->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
- $sender->sendMessage(TextFormat::DARK_GREEN . "/" . $command->getName() . ": " . TextFormat::RESET . $descriptionString);
+ $sender->sendMessage(TextFormat::DARK_GREEN . "/" . $command->getLabel() . ": " . TextFormat::RESET . $descriptionString);
}
}
@@ -106,7 +105,7 @@ class HelpCommand extends VanillaCommand{
$lang = $sender->getLanguage();
$description = $cmd->getDescription();
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
- $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($cmd->getName())
+ $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($cmd->getLabel())
->format(TextFormat::YELLOW . "--------- " . TextFormat::RESET, TextFormat::YELLOW . " ---------"));
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::RESET . $descriptionString)
->prefix(TextFormat::GOLD));
diff --git a/src/command/defaults/StatusCommand.php b/src/command/defaults/StatusCommand.php
index e97e55f95..7c2884993 100644
--- a/src/command/defaults/StatusCommand.php
+++ b/src/command/defaults/StatusCommand.php
@@ -82,10 +82,10 @@ class StatusCommand extends VanillaCommand{
$sender->sendMessage(TextFormat::GOLD . "Uptime: " . TextFormat::RED . $uptime);
$tpsColor = TextFormat::GREEN;
- if($server->getTicksPerSecond() < 17){
- $tpsColor = TextFormat::GOLD;
- }elseif($server->getTicksPerSecond() < 12){
+ if($server->getTicksPerSecond() < 12){
$tpsColor = TextFormat::RED;
+ }elseif($server->getTicksPerSecond() < 17){
+ $tpsColor = TextFormat::GOLD;
}
$sender->sendMessage(TextFormat::GOLD . "Current TPS: {$tpsColor}{$server->getTicksPerSecond()} ({$server->getTickUsage()}%)");
diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php
index 9eddb17a0..0b37080fa 100644
--- a/src/crafting/CraftingManager.php
+++ b/src/crafting/CraftingManager.php
@@ -201,7 +201,7 @@ class CraftingManager{
}
/**
- * @param Item[] $outputs
+ * @param Item[] $outputs
*/
public function matchRecipe(CraftingGrid $grid, array $outputs) : ?CraftingRecipe{
//TODO: try to match special recipes before anything else (first they need to be implemented!)
diff --git a/src/crafting/ShapedRecipe.php b/src/crafting/ShapedRecipe.php
index aa15eb4c5..371fe9dae 100644
--- a/src/crafting/ShapedRecipe.php
+++ b/src/crafting/ShapedRecipe.php
@@ -46,15 +46,15 @@ class ShapedRecipe implements CraftingRecipe{
/**
* Constructs a ShapedRecipe instance.
*
- * @param string[] $shape
- * Array of 1, 2, or 3 strings representing the rows of the recipe.
- * This accepts an array of 1, 2 or 3 strings. Each string should be of the same length and must be at most 3
- * characters long. Each character represents a unique type of ingredient. Spaces are interpreted as air.
+ * @param string[] $shape
+ * Array of 1, 2, or 3 strings representing the rows of the recipe.
+ * This accepts an array of 1, 2 or 3 strings. Each string should be of the same length and must be at most 3
+ * characters long. Each character represents a unique type of ingredient. Spaces are interpreted as air.
* @param Item[] $ingredients
- * Char => Item map of items to be set into the shape.
- * This accepts an array of Items, indexed by character. Every unique character (except space) in the shape
- * array MUST have a corresponding item in this list. Space character is automatically treated as air.
- * @param Item[] $results List of items that this recipe produces when crafted.
+ * Char => Item map of items to be set into the shape.
+ * This accepts an array of Items, indexed by character. Every unique character (except space) in the shape
+ * array MUST have a corresponding item in this list. Space character is automatically treated as air.
+ * @param Item[] $results List of items that this recipe produces when crafted.
*
* Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns.
*/
diff --git a/src/crafting/ShapelessRecipe.php b/src/crafting/ShapelessRecipe.php
index 2c399fe04..2b77e58b8 100644
--- a/src/crafting/ShapelessRecipe.php
+++ b/src/crafting/ShapelessRecipe.php
@@ -36,7 +36,8 @@ class ShapelessRecipe implements CraftingRecipe{
/**
* @param Item[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
- * @param Item[] $results List of result items created by this recipe.
+ * @param Item[] $results List of result items created by this recipe.
+ *
* TODO: we'll want to make the type parameter mandatory in PM5
*/
public function __construct(array $ingredients, array $results, ?ShapelessRecipeType $type = null){
diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php
index e743a6704..e6d2b52a0 100644
--- a/src/crash/CrashDump.php
+++ b/src/crash/CrashDump.php
@@ -43,6 +43,7 @@ use function get_loaded_extensions;
use function json_encode;
use function ksort;
use function max;
+use function mb_scrub;
use function mb_strtoupper;
use function microtime;
use function ob_end_clean;
@@ -196,12 +197,14 @@ class CrashDump{
$error["message"] = substr($error["message"], 0, $pos);
}
}
+ $error["message"] = mb_scrub($error["message"], 'UTF-8');
if(isset($lastError)){
if(isset($lastError["trace"])){
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
}
$this->data->lastError = $lastError;
+ $this->data->lastError["message"] = mb_scrub($this->data->lastError["message"], 'UTF-8');
}
$this->data->error = $error;
diff --git a/src/data/bedrock/EffectIdMap.php b/src/data/bedrock/EffectIdMap.php
index 23985fa12..6dce86d9b 100644
--- a/src/data/bedrock/EffectIdMap.php
+++ b/src/data/bedrock/EffectIdMap.php
@@ -74,6 +74,7 @@ final class EffectIdMap{
//TODO: SLOW_FALLING
//TODO: BAD_OMEN
//TODO: VILLAGE_HERO
+ $this->register(EffectIds::DARKNESS, VanillaEffects::DARKNESS());
}
//TODO: not a big fan of the code duplication here :(
diff --git a/src/data/bedrock/EffectIds.php b/src/data/bedrock/EffectIds.php
index 3acf56569..a2ada01d9 100644
--- a/src/data/bedrock/EffectIds.php
+++ b/src/data/bedrock/EffectIds.php
@@ -58,4 +58,5 @@ final class EffectIds{
public const SLOW_FALLING = 27;
public const BAD_OMEN = 28;
public const VILLAGE_HERO = 29;
+ public const DARKNESS = 30;
}
diff --git a/src/data/bedrock/PotionTypeIdMap.php b/src/data/bedrock/PotionTypeIdMap.php
index eebab87e2..2e6fef3cc 100644
--- a/src/data/bedrock/PotionTypeIdMap.php
+++ b/src/data/bedrock/PotionTypeIdMap.php
@@ -33,13 +33,13 @@ final class PotionTypeIdMap{
* @var PotionType[]
* @phpstan-var array
*/
- private array $idToEnum;
+ private array $idToEnum = [];
/**
* @var int[]
* @phpstan-var array
*/
- private array $enumToId;
+ private array $enumToId = [];
private function __construct(){
$this->register(PotionTypeIds::WATER, PotionType::WATER());
diff --git a/src/entity/Entity.php b/src/entity/Entity.php
index 3e34d5c26..e58910997 100644
--- a/src/entity/Entity.php
+++ b/src/entity/Entity.php
@@ -52,6 +52,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
+use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData;
use pocketmine\player\Player;
use pocketmine\Server;
use pocketmine\timings\Timings;
@@ -997,9 +998,7 @@ abstract class Entity{
$this->timings->stopTiming();
- //if($this->isStatic())
return ($hasUpdate || $this->hasMovementUpdate());
- //return !($this instanceof Player);
}
final public function scheduleUpdate() : void{
@@ -1479,6 +1478,7 @@ abstract class Entity{
return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []);
}, $this->attributeMap->getAll()),
$this->getAllNetworkData(),
+ new PropertySyncData([], []),
[] //TODO: entity links
));
}
@@ -1598,7 +1598,7 @@ abstract class Entity{
/**
* @param Player[]|null $targets
- * @param MetadataProperty[] $data Properly formatted entity data, defaults to everything
+ * @param MetadataProperty[] $data Properly formatted entity data, defaults to everything
*
* @phpstan-param array $data
*/
diff --git a/src/entity/Human.php b/src/entity/Human.php
index ae323f0f8..ade87f981 100644
--- a/src/entity/Human.php
+++ b/src/entity/Human.php
@@ -54,6 +54,7 @@ use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DeviceOS;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
+use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData;
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
@@ -355,7 +356,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
&& ($this->inventory->getItemInHand() instanceof Totem || $this->offHandInventory->getItem(0) instanceof Totem)){
$compensation = $this->getHealth() - $source->getFinalDamage() - 1;
- if($compensation < 0){
+ if($compensation <= -1){
$source->setModifier($compensation, EntityDamageEvent::MODIFIER_TOTEM);
}
}
@@ -485,6 +486,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())),
GameMode::SURVIVAL,
$this->getAllNetworkData(),
+ new PropertySyncData([], []),
UpdateAbilitiesPacket::create(CommandPermissions::NORMAL, PlayerPermissions::VISITOR, $this->getId() /* TODO: this should be unique ID */, [
new UpdateAbilitiesPacketLayer(
UpdateAbilitiesPacketLayer::LAYER_BASE,
diff --git a/src/entity/effect/Effect.php b/src/entity/effect/Effect.php
index cce009733..6373a2281 100644
--- a/src/entity/effect/Effect.php
+++ b/src/entity/effect/Effect.php
@@ -35,9 +35,9 @@ class Effect{
use NotSerializable;
/**
- * @param Translatable|string $name Translation key used for effect name
- * @param Color $color Color of bubbles given by this effect
- * @param bool $bad Whether the effect is harmful
+ * @param Translatable|string $name Translation key used for effect name
+ * @param Color $color Color of bubbles given by this effect
+ * @param bool $bad Whether the effect is harmful
* @param bool $hasBubbles Whether the effect has potion bubbles. Some do not (e.g. Instant Damage has its own particles instead of bubbles)
*/
public function __construct(
diff --git a/src/entity/effect/EffectInstance.php b/src/entity/effect/EffectInstance.php
index f948b1f39..50d92a67b 100644
--- a/src/entity/effect/EffectInstance.php
+++ b/src/entity/effect/EffectInstance.php
@@ -36,7 +36,7 @@ class EffectInstance{
private Color $color;
/**
- * @param int|null $duration Passing null will use the effect type's default duration
+ * @param int|null $duration Passing null will use the effect type's default duration
*/
public function __construct(Effect $effectType, ?int $duration = null, int $amplifier = 0, bool $visible = true, bool $ambient = false, ?Color $overrideColor = null){
$this->effectType = $effectType;
diff --git a/src/entity/effect/StringToEffectParser.php b/src/entity/effect/StringToEffectParser.php
index d336af33b..23bd29bd0 100644
--- a/src/entity/effect/StringToEffectParser.php
+++ b/src/entity/effect/StringToEffectParser.php
@@ -40,6 +40,7 @@ final class StringToEffectParser extends StringToTParser{
$result->register("absorption", fn() => VanillaEffects::ABSORPTION());
$result->register("blindness", fn() => VanillaEffects::BLINDNESS());
$result->register("conduit_power", fn() => VanillaEffects::CONDUIT_POWER());
+ $result->register("darkness", fn() => VanillaEffects::DARKNESS());
$result->register("fatal_poison", fn() => VanillaEffects::FATAL_POISON());
$result->register("fire_resistance", fn() => VanillaEffects::FIRE_RESISTANCE());
$result->register("haste", fn() => VanillaEffects::HASTE());
diff --git a/src/entity/effect/VanillaEffects.php b/src/entity/effect/VanillaEffects.php
index 04f7985da..50544054a 100644
--- a/src/entity/effect/VanillaEffects.php
+++ b/src/entity/effect/VanillaEffects.php
@@ -36,6 +36,7 @@ use pocketmine\utils\RegistryTrait;
* @method static AbsorptionEffect ABSORPTION()
* @method static Effect BLINDNESS()
* @method static Effect CONDUIT_POWER()
+ * @method static Effect DARKNESS()
* @method static PoisonEffect FATAL_POISON()
* @method static Effect FIRE_RESISTANCE()
* @method static Effect HASTE()
@@ -68,6 +69,7 @@ final class VanillaEffects{
//TODO: bad_omen
self::register("blindness", new Effect(KnownTranslationFactory::potion_blindness(), new Color(0x1f, 0x1f, 0x23), true));
self::register("conduit_power", new Effect(KnownTranslationFactory::potion_conduitPower(), new Color(0x1d, 0xc2, 0xd1)));
+ self::register("darkness", new Effect(KnownTranslationFactory::effect_darkness(), new Color(0x29, 0x27, 0x21), true, 600, false));
self::register("fatal_poison", new PoisonEffect(KnownTranslationFactory::potion_poison(), new Color(0x4e, 0x93, 0x31), true, 600, true, true));
self::register("fire_resistance", new Effect(KnownTranslationFactory::potion_fireResistance(), new Color(0xe4, 0x9a, 0x3a)));
self::register("haste", new Effect(KnownTranslationFactory::potion_digSpeed(), new Color(0xd9, 0xc0, 0x43)));
diff --git a/src/entity/projectile/Arrow.php b/src/entity/projectile/Arrow.php
index 2ec412545..df3237d62 100644
--- a/src/entity/projectile/Arrow.php
+++ b/src/entity/projectile/Arrow.php
@@ -38,6 +38,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\player\Player;
use pocketmine\world\sound\ArrowHitSound;
+use function ceil;
use function mt_rand;
use function sqrt;
@@ -103,7 +104,7 @@ class Arrow extends Projectile{
}
public function getResultDamage() : int{
- $base = parent::getResultDamage();
+ $base = (int) ceil($this->motion->length() * parent::getResultDamage());
if($this->isCritical()){
return ($base + mt_rand(0, (int) ($base / 2) + 1));
}else{
diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php
index 0d6fe2a35..be2082948 100644
--- a/src/entity/projectile/Projectile.php
+++ b/src/entity/projectile/Projectile.php
@@ -128,7 +128,7 @@ abstract class Projectile extends Entity{
* Returns the amount of damage this projectile will deal to the entity it hits.
*/
public function getResultDamage() : int{
- return (int) ceil($this->motion->length() * $this->damage);
+ return (int) ceil($this->damage);
}
public function saveNBT() : CompoundTag{
diff --git a/src/event/HandlerList.php b/src/event/HandlerList.php
index 7d93c2ebe..0e16555b3 100644
--- a/src/event/HandlerList.php
+++ b/src/event/HandlerList.php
@@ -31,6 +31,10 @@ class HandlerList{
/** @var RegisteredListener[][] */
private array $handlerSlots = [];
+ /**
+ * @phpstan-template TEvent of Event
+ * @phpstan-param class-string $class
+ */
public function __construct(
private string $class,
private ?HandlerList $parentList
diff --git a/src/event/block/BlockDeathEvent.php b/src/event/block/BlockDeathEvent.php
new file mode 100644
index 000000000..c07e3ad6e
--- /dev/null
+++ b/src/event/block/BlockDeathEvent.php
@@ -0,0 +1,32 @@
+entity = $entity;
diff --git a/src/event/inventory/CraftItemEvent.php b/src/event/inventory/CraftItemEvent.php
index 2ca324d77..64f285f15 100644
--- a/src/event/inventory/CraftItemEvent.php
+++ b/src/event/inventory/CraftItemEvent.php
@@ -35,8 +35,8 @@ class CraftItemEvent extends Event implements Cancellable{
use CancellableTrait;
/**
- * @param Item[] $inputs
- * @param Item[] $outputs
+ * @param Item[] $inputs
+ * @param Item[] $outputs
*/
public function __construct(
private CraftingTransaction $transaction,
diff --git a/src/event/player/PlayerChatEvent.php b/src/event/player/PlayerChatEvent.php
index 3b9195b27..8062cece7 100644
--- a/src/event/player/PlayerChatEvent.php
+++ b/src/event/player/PlayerChatEvent.php
@@ -26,6 +26,7 @@ namespace pocketmine\event\player;
use pocketmine\command\CommandSender;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
+use pocketmine\lang\KnownTranslationKeys;
use pocketmine\player\Player;
use pocketmine\utils\Utils;
@@ -47,7 +48,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
/**
* @param CommandSender[] $recipients
*/
- public function __construct(Player $player, string $message, array $recipients, string $format = "chat.type.text"){
+ public function __construct(Player $player, string $message, array $recipients, string $format = KnownTranslationKeys::CHAT_TYPE_TEXT){
$this->player = $player;
$this->message = $message;
diff --git a/src/event/player/PlayerCreationEvent.php b/src/event/player/PlayerCreationEvent.php
index 7e7e2cfaa..64f449d1a 100644
--- a/src/event/player/PlayerCreationEvent.php
+++ b/src/event/player/PlayerCreationEvent.php
@@ -30,7 +30,20 @@ use pocketmine\utils\Utils;
use function is_a;
/**
- * Allows the creation of players overriding the base Player class
+ * Allows the use of custom Player classes. This enables overriding built-in Player methods to change behaviour that is
+ * not possible to alter any other way.
+ *
+ * You probably don't need this event, and found your way here because you looked at some code in an old plugin that
+ * abused it (very common). Instead of using custom player classes, you should consider making session classes instead.
+ *
+ * @see https://github.com/pmmp/SessionsDemo
+ *
+ * This event is a power-user feature, and multiple plugins using it at the same time will conflict and break unless
+ * they've been designed to work together. This means that it's only usually useful in private plugins.
+ *
+ * WARNING: This should NOT be used for adding extra functions or properties. This is intended for **overriding existing
+ * core behaviour**, and should only be used if you know EXACTLY what you're doing.
+ * Custom player classes may break in any update without warning. This event isn't much more than glorified reflection.
*/
class PlayerCreationEvent extends Event{
@@ -54,6 +67,8 @@ class PlayerCreationEvent extends Event{
}
/**
+ * Returns the base class that the final player class must extend.
+ *
* @return string
* @phpstan-return class-string
*/
@@ -62,6 +77,10 @@ class PlayerCreationEvent extends Event{
}
/**
+ * Sets the class that the final player class must extend.
+ * The new base class must be a subclass of the current base class.
+ * This can (perhaps) be used to limit the options for custom player classes provided by other plugins.
+ *
* @param string $class
* @phpstan-param class-string $class
*/
@@ -74,6 +93,8 @@ class PlayerCreationEvent extends Event{
}
/**
+ * Returns the class that will be instantiated to create the player after the event.
+ *
* @return string
* @phpstan-return class-string
*/
@@ -82,6 +103,9 @@ class PlayerCreationEvent extends Event{
}
/**
+ * Sets the class that will be instantiated to create the player after the event. The class must not be abstract,
+ * and must be an instance of the base class.
+ *
* @param string $class
* @phpstan-param class-string $class
*/
diff --git a/src/form/Form.php b/src/form/Form.php
index f17da3e36..97f49df07 100644
--- a/src/form/Form.php
+++ b/src/form/Form.php
@@ -34,7 +34,7 @@ interface Form extends \JsonSerializable{
/**
* Handles a form response from a player.
*
- * @param mixed $data
+ * @param mixed $data
*
* @throws FormValidationException if the data could not be processed
*/
diff --git a/src/inventory/BaseInventory.php b/src/inventory/BaseInventory.php
index 3edc52043..06b3b9800 100644
--- a/src/inventory/BaseInventory.php
+++ b/src/inventory/BaseInventory.php
@@ -27,6 +27,7 @@ use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\player\Player;
use pocketmine\utils\ObjectSet;
+use pocketmine\utils\Utils;
use function array_slice;
use function count;
use function max;
@@ -85,6 +86,7 @@ abstract class BaseInventory implements Inventory{
* @phpstan-param array $items
*/
public function setContents(array $items) : void{
+ Utils::validateArrayValueType($items, function(Item $item) : void{});
if(count($items) > $this->getSize()){
$items = array_slice($items, 0, $this->getSize(), true);
}
diff --git a/src/inventory/Inventory.php b/src/inventory/Inventory.php
index 230f6aa49..5c81d7d9c 100644
--- a/src/inventory/Inventory.php
+++ b/src/inventory/Inventory.php
@@ -33,12 +33,25 @@ use pocketmine\utils\ObjectSet;
interface Inventory{
public const MAX_STACK = 64;
+ /**
+ * Returns the number of slots in the inventory.
+ */
public function getSize() : int;
+ /**
+ * Returns the maximum stack size for items in this inventory. Individual item types (such as armor or tools) may
+ * have a smaller maximum stack size.
+ */
public function getMaxStackSize() : int;
+ /**
+ * Sets the maximum stack size for items in this inventory.
+ */
public function setMaxStackSize(int $size) : void;
+ /**
+ * Returns the item in the specified slot.
+ */
public function getItem(int $index) : Item;
/**
@@ -65,10 +78,11 @@ interface Inventory{
public function setContents(array $items) : void;
/**
- * Stores the given Items in the inventory. This will try to fill
- * existing stacks and empty slots as well as it can.
+ * Stores the given Items in the inventory.
+ * This will add to any non-full existing stacks first, and then put the remaining items in empty slots if there are
+ * any available.
*
- * Returns the Items that did not fit.
+ * Returns an array of items which could not fit in the inventory.
*
* @return Item[]
*/
@@ -85,15 +99,20 @@ interface Inventory{
public function getAddableItemQuantity(Item $item) : int;
/**
- * Checks if the inventory contains any Item with the same material data.
- * It will check id, amount, and metadata (if not null)
+ * Returns whether the total amount of matching items is at least the stack size of the given item. Multiple stacks
+ * of the same item are added together.
+ *
+ * If the input item has specific NBT, only items with the same type and NBT will match. Otherwise, only the item
+ * type is checked.
*/
public function contains(Item $item) : bool;
/**
- * Will return all the Items that has the same id and metadata (if not null).
- * Won't check amount
- * The returned array is indexed by slot number.
+ * Returns all matching items in the inventory, irrespective of stack size. The returned array is indexed by slot
+ * number.
+ *
+ * If the input item has specific NBT, only items with the same type and NBT will match. Otherwise, only the item
+ * type is checked.
*
* @return Item[]
* @phpstan-return array
@@ -101,10 +120,10 @@ interface Inventory{
public function all(Item $item) : array;
/**
- * Returns the first slot number containing an item with the same ID, damage (if not any-damage), NBT (if not empty)
- * and count >= to the count of the specified item stack.
+ * Returns the first slot number containing a matching item with a stack size greater than or equal to the input item.
*
- * If $exact is true, only items with equal ID, damage, NBT and count will match.
+ * If the input item has specific NBT, or if $exact is true, only items with the same type and NBT will match.
+ * Otherwise, only the item type is checked.
*/
public function first(Item $item, bool $exact = false) : int;
@@ -119,13 +138,19 @@ interface Inventory{
public function isSlotEmpty(int $index) : bool;
/**
- * Will remove all the Items that has the same id and metadata (if not null)
+ * Clears all slots containing items equivalent to the given item.
+ *
+ * If the input item has specific NBT, only items with the same type and NBT will match. Otherwise, only the item
+ * type is checked.
*/
public function remove(Item $item) : void;
/**
- * Removes the given Item from the inventory.
- * It will return the Items that couldn't be removed.
+ * Removes items from the inventory in the amounts specified by the given itemstacks.
+ * Returns an array of items that couldn't be removed.
+ *
+ * If the input item has specific NBT, only items with the same type and NBT will match. Otherwise, only the item
+ * type is checked.
*
* @return Item[]
*/
diff --git a/src/inventory/transaction/InventoryTransaction.php b/src/inventory/transaction/InventoryTransaction.php
index 27bd34e8d..46dacb8ec 100644
--- a/src/inventory/transaction/InventoryTransaction.php
+++ b/src/inventory/transaction/InventoryTransaction.php
@@ -134,6 +134,8 @@ class InventoryTransaction{
/**
* @param Item[] $needItems
* @param Item[] $haveItems
+ * @phpstan-param-out Item[] $needItems
+ * @phpstan-param-out Item[] $haveItems
*
* @throws TransactionValidationException
*/
diff --git a/src/item/Banner.php b/src/item/Banner.php
index 343fe8e24..92bf7729d 100644
--- a/src/item/Banner.php
+++ b/src/item/Banner.php
@@ -75,7 +75,7 @@ class Banner extends ItemBlockWallOrFloor{
}
/**
- * @param BannerPatternLayer[] $patterns
+ * @param BannerPatternLayer[] $patterns
*
* @phpstan-param list $patterns
*
diff --git a/src/item/Durable.php b/src/item/Durable.php
index 3b04ef381..887fbb4fd 100644
--- a/src/item/Durable.php
+++ b/src/item/Durable.php
@@ -109,6 +109,7 @@ abstract class Durable extends Item{
*/
protected function onBroken() : void{
$this->pop();
+ $this->setDamage(0); //the stack size may be greater than 1 if overstacked by a plugin
}
/**
@@ -120,7 +121,7 @@ abstract class Durable extends Item{
* Returns whether the item is broken.
*/
public function isBroken() : bool{
- return $this->damage >= $this->getMaxDurability();
+ return $this->damage >= $this->getMaxDurability() || $this->isNull();
}
protected function deserializeCompoundTag(CompoundTag $tag) : void{
diff --git a/src/item/Item.php b/src/item/Item.php
index 2a4312539..93f1e1817 100644
--- a/src/item/Item.php
+++ b/src/item/Item.php
@@ -564,7 +564,7 @@ class Item implements \JsonSerializable{
/**
* Compares an Item to this Item and check if they match.
*
- * @param bool $checkDamage Whether to verify that the damage values match.
+ * @param bool $checkDamage Whether to verify that the damage values match.
* @param bool $checkCompound Whether to verify that the items' NBT match.
*/
final public function equals(Item $item, bool $checkDamage = true, bool $checkCompound = true) : bool{
diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php
index c7f4a4242..f1db1c6d1 100644
--- a/src/lang/KnownTranslationFactory.php
+++ b/src/lang/KnownTranslationFactory.php
@@ -43,6 +43,34 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ACCEPT_LICENSE, []);
}
+ public static function action_interact_armorstand_equip() : Translatable{
+ return new Translatable(KnownTranslationKeys::ACTION_INTERACT_ARMORSTAND_EQUIP, []);
+ }
+
+ public static function action_interact_armorstand_pose() : Translatable{
+ return new Translatable(KnownTranslationKeys::ACTION_INTERACT_ARMORSTAND_POSE, []);
+ }
+
+ public static function action_interact_exit_boat() : Translatable{
+ return new Translatable(KnownTranslationKeys::ACTION_INTERACT_EXIT_BOAT, []);
+ }
+
+ public static function action_interact_fishing() : Translatable{
+ return new Translatable(KnownTranslationKeys::ACTION_INTERACT_FISHING, []);
+ }
+
+ public static function action_interact_name() : Translatable{
+ return new Translatable(KnownTranslationKeys::ACTION_INTERACT_NAME, []);
+ }
+
+ public static function action_interact_ride_boat() : Translatable{
+ return new Translatable(KnownTranslationKeys::ACTION_INTERACT_RIDE_BOAT, []);
+ }
+
+ public static function action_interact_ride_minecart() : Translatable{
+ return new Translatable(KnownTranslationKeys::ACTION_INTERACT_RIDE_MINECART, []);
+ }
+
public static function chat_type_achievement(Translatable|string $param0, Translatable|string $param1) : Translatable{
return new Translatable(KnownTranslationKeys::CHAT_TYPE_ACHIEVEMENT, [
0 => $param0,
@@ -627,6 +655,12 @@ final class KnownTranslationFactory{
]);
}
+ public static function death_attack_fallingBlock(Translatable|string $param0) : Translatable{
+ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_FALLINGBLOCK, [
+ 0 => $param0,
+ ]);
+ }
+
public static function death_attack_generic(Translatable|string $param0) : Translatable{
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [
0 => $param0,
@@ -691,6 +725,13 @@ final class KnownTranslationFactory{
]);
}
+ public static function death_attack_trident(Translatable|string $param0, Translatable|string $param1) : Translatable{
+ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_TRIDENT, [
+ 0 => $param0,
+ 1 => $param1,
+ ]);
+ }
+
public static function death_attack_wither(Translatable|string $param0) : Translatable{
return new Translatable(KnownTranslationKeys::DEATH_ATTACK_WITHER, [
0 => $param0,
@@ -743,6 +784,10 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::DISCONNECTIONSCREEN_SERVERFULL, []);
}
+ public static function effect_darkness() : Translatable{
+ return new Translatable(KnownTranslationKeys::EFFECT_DARKNESS, []);
+ }
+
public static function enchantment_arrowDamage() : Translatable{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_ARROWDAMAGE, []);
}
@@ -859,6 +904,10 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_SOUL_SPEED, []);
}
+ public static function enchantment_swift_sneak() : Translatable{
+ return new Translatable(KnownTranslationKeys::ENCHANTMENT_SWIFT_SNEAK, []);
+ }
+
public static function enchantment_thorns() : Translatable{
return new Translatable(KnownTranslationKeys::ENCHANTMENT_THORNS, []);
}
@@ -950,6 +999,10 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_13_DESC, []);
}
+ public static function item_record_5_desc() : Translatable{
+ return new Translatable(KnownTranslationKeys::ITEM_RECORD_5_DESC, []);
+ }
+
public static function item_record_blocks_desc() : Translatable{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_BLOCKS_DESC, []);
}
@@ -974,6 +1027,10 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_MELLOHI_DESC, []);
}
+ public static function item_record_otherside_desc() : Translatable{
+ return new Translatable(KnownTranslationKeys::ITEM_RECORD_OTHERSIDE_DESC, []);
+ }
+
public static function item_record_pigstep_desc() : Translatable{
return new Translatable(KnownTranslationKeys::ITEM_RECORD_PIGSTEP_DESC, []);
}
@@ -1393,6 +1450,10 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_UNBAN_PLAYER_DESCRIPTION, []);
}
+ public static function pocketmine_command_userDefined_description() : Translatable{
+ return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_USERDEFINED_DESCRIPTION, []);
+ }
+
public static function pocketmine_command_version_description() : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_VERSION_DESCRIPTION, []);
}
diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php
index 9a9e91d39..bd98496cb 100644
--- a/src/lang/KnownTranslationKeys.php
+++ b/src/lang/KnownTranslationKeys.php
@@ -33,6 +33,13 @@ final class KnownTranslationKeys{
public const ABILITY_FLIGHT = "ability.flight";
public const ABILITY_NOCLIP = "ability.noclip";
public const ACCEPT_LICENSE = "accept_license";
+ public const ACTION_INTERACT_ARMORSTAND_EQUIP = "action.interact.armorstand.equip";
+ public const ACTION_INTERACT_ARMORSTAND_POSE = "action.interact.armorstand.pose";
+ public const ACTION_INTERACT_EXIT_BOAT = "action.interact.exit.boat";
+ public const ACTION_INTERACT_FISHING = "action.interact.fishing";
+ public const ACTION_INTERACT_NAME = "action.interact.name";
+ public const ACTION_INTERACT_RIDE_BOAT = "action.interact.ride.boat";
+ public const ACTION_INTERACT_RIDE_MINECART = "action.interact.ride.minecart";
public const CHAT_TYPE_ACHIEVEMENT = "chat.type.achievement";
public const CHAT_TYPE_ADMIN = "chat.type.admin";
public const CHAT_TYPE_ANNOUNCEMENT = "chat.type.announcement";
@@ -138,6 +145,7 @@ final class KnownTranslationKeys{
public const DEATH_ATTACK_EXPLOSION = "death.attack.explosion";
public const DEATH_ATTACK_EXPLOSION_PLAYER = "death.attack.explosion.player";
public const DEATH_ATTACK_FALL = "death.attack.fall";
+ public const DEATH_ATTACK_FALLINGBLOCK = "death.attack.fallingBlock";
public const DEATH_ATTACK_GENERIC = "death.attack.generic";
public const DEATH_ATTACK_INFIRE = "death.attack.inFire";
public const DEATH_ATTACK_INWALL = "death.attack.inWall";
@@ -148,6 +156,7 @@ final class KnownTranslationKeys{
public const DEATH_ATTACK_OUTOFWORLD = "death.attack.outOfWorld";
public const DEATH_ATTACK_PLAYER = "death.attack.player";
public const DEATH_ATTACK_PLAYER_ITEM = "death.attack.player.item";
+ public const DEATH_ATTACK_TRIDENT = "death.attack.trident";
public const DEATH_ATTACK_WITHER = "death.attack.wither";
public const DEATH_FELL_ACCIDENT_GENERIC = "death.fell.accident.generic";
public const DEFAULT_GAMEMODE = "default_gamemode";
@@ -160,6 +169,7 @@ final class KnownTranslationKeys{
public const DISCONNECTIONSCREEN_OUTDATEDSERVER = "disconnectionScreen.outdatedServer";
public const DISCONNECTIONSCREEN_RESOURCEPACK = "disconnectionScreen.resourcePack";
public const DISCONNECTIONSCREEN_SERVERFULL = "disconnectionScreen.serverFull";
+ public const EFFECT_DARKNESS = "effect.darkness";
public const ENCHANTMENT_ARROWDAMAGE = "enchantment.arrowDamage";
public const ENCHANTMENT_ARROWFIRE = "enchantment.arrowFire";
public const ENCHANTMENT_ARROWINFINITE = "enchantment.arrowInfinite";
@@ -189,6 +199,7 @@ final class KnownTranslationKeys{
public const ENCHANTMENT_PROTECT_FIRE = "enchantment.protect.fire";
public const ENCHANTMENT_PROTECT_PROJECTILE = "enchantment.protect.projectile";
public const ENCHANTMENT_SOUL_SPEED = "enchantment.soul_speed";
+ public const ENCHANTMENT_SWIFT_SNEAK = "enchantment.swift_sneak";
public const ENCHANTMENT_THORNS = "enchantment.thorns";
public const ENCHANTMENT_TRIDENTCHANNELING = "enchantment.tridentChanneling";
public const ENCHANTMENT_TRIDENTIMPALING = "enchantment.tridentImpaling";
@@ -210,12 +221,14 @@ final class KnownTranslationKeys{
public const IP_WARNING = "ip_warning";
public const ITEM_RECORD_11_DESC = "item.record_11.desc";
public const ITEM_RECORD_13_DESC = "item.record_13.desc";
+ public const ITEM_RECORD_5_DESC = "item.record_5.desc";
public const ITEM_RECORD_BLOCKS_DESC = "item.record_blocks.desc";
public const ITEM_RECORD_CAT_DESC = "item.record_cat.desc";
public const ITEM_RECORD_CHIRP_DESC = "item.record_chirp.desc";
public const ITEM_RECORD_FAR_DESC = "item.record_far.desc";
public const ITEM_RECORD_MALL_DESC = "item.record_mall.desc";
public const ITEM_RECORD_MELLOHI_DESC = "item.record_mellohi.desc";
+ public const ITEM_RECORD_OTHERSIDE_DESC = "item.record_otherside.desc";
public const ITEM_RECORD_PIGSTEP_DESC = "item.record_pigstep.desc";
public const ITEM_RECORD_STAL_DESC = "item.record_stal.desc";
public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc";
@@ -306,6 +319,7 @@ final class KnownTranslationKeys{
public const POCKETMINE_COMMAND_TRANSFERSERVER_USAGE = "pocketmine.command.transferserver.usage";
public const POCKETMINE_COMMAND_UNBAN_IP_DESCRIPTION = "pocketmine.command.unban.ip.description";
public const POCKETMINE_COMMAND_UNBAN_PLAYER_DESCRIPTION = "pocketmine.command.unban.player.description";
+ public const POCKETMINE_COMMAND_USERDEFINED_DESCRIPTION = "pocketmine.command.userDefined.description";
public const POCKETMINE_COMMAND_VERSION_DESCRIPTION = "pocketmine.command.version.description";
public const POCKETMINE_COMMAND_VERSION_MINECRAFTVERSION = "pocketmine.command.version.minecraftVersion";
public const POCKETMINE_COMMAND_VERSION_NOSUCHPLUGIN = "pocketmine.command.version.noSuchPlugin";
diff --git a/src/lang/Language.php b/src/lang/Language.php
index 3c61a0cdd..57c5c78e1 100644
--- a/src/lang/Language.php
+++ b/src/lang/Language.php
@@ -173,6 +173,14 @@ class Language{
return $this->internalGet($id) ?? $id;
}
+ /**
+ * @return string[]
+ * @phpstan-return array
+ */
+ public function getAll() : array{
+ return $this->lang;
+ }
+
protected function parseTranslation(string $text, ?string $onlyPrefix = null) : string{
$newString = "";
diff --git a/src/network/AdvancedNetworkInterface.php b/src/network/AdvancedNetworkInterface.php
index 1fac1311d..da1af4b28 100644
--- a/src/network/AdvancedNetworkInterface.php
+++ b/src/network/AdvancedNetworkInterface.php
@@ -35,7 +35,7 @@ interface AdvancedNetworkInterface extends NetworkInterface{
/**
* Prevents packets received from the IP address getting processed for the given timeout.
*
- * @param int $timeout Seconds
+ * @param int $timeout Seconds
*/
public function blockAddress(string $address, int $timeout = 300) : void;
diff --git a/src/network/Network.php b/src/network/Network.php
index 6bb1f9230..8e60d416c 100644
--- a/src/network/Network.php
+++ b/src/network/Network.php
@@ -54,7 +54,7 @@ class Network{
private BidirectionalBandwidthStatsTracker $bandwidthTracker;
private string $name;
- private NetworkSessionManager$sessionManager;
+ private NetworkSessionManager $sessionManager;
public function __construct(
private \Logger $logger
@@ -80,6 +80,10 @@ class Network{
return $this->sessionManager->getSessionCount();
}
+ public function getValidConnectionCount() : int{
+ return $this->sessionManager->getValidSessionCount();
+ }
+
public function tick() : void{
foreach($this->interfaces as $interface){
$interface->tick();
diff --git a/src/network/NetworkSessionManager.php b/src/network/NetworkSessionManager.php
index de1b7bfe3..1336b53cc 100644
--- a/src/network/NetworkSessionManager.php
+++ b/src/network/NetworkSessionManager.php
@@ -32,12 +32,25 @@ class NetworkSessionManager{
/** @var NetworkSession[] */
private array $sessions = [];
+ /** @var NetworkSession[] */
+ private array $pendingLoginSessions = [];
+
/**
* Adds a network session to the manager. This should only be called on session creation.
*/
public function add(NetworkSession $session) : void{
$idx = spl_object_id($session);
$this->sessions[$idx] = $session;
+ $this->pendingLoginSessions[$idx] = $session;
+ }
+
+ /**
+ * Marks the session as having sent a login request. After this point, they are counted towards the total player
+ * count.
+ */
+ public function markLoginReceived(NetworkSession $session) : void{
+ $idx = spl_object_id($session);
+ unset($this->pendingLoginSessions[$idx]);
}
/**
@@ -47,15 +60,24 @@ class NetworkSessionManager{
public function remove(NetworkSession $session) : void{
$idx = spl_object_id($session);
unset($this->sessions[$idx]);
+ unset($this->pendingLoginSessions[$idx]);
}
/**
- * Returns the number of known connected sessions.
+ * Returns the number of known connected sessions, including sessions which have not yet sent a login request.
*/
public function getSessionCount() : int{
return count($this->sessions);
}
+ /**
+ * Returns the number of connected sessions which have either sent a login request, or have already completed the
+ * login sequence.
+ */
+ public function getValidSessionCount() : int{
+ return count($this->sessions) - count($this->pendingLoginSessions);
+ }
+
/** @return NetworkSession[] */
public function getSessions() : array{ return $this->sessions; }
diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php
index 3b868e0f5..188b2fcae 100644
--- a/src/network/mcpe/NetworkSession.php
+++ b/src/network/mcpe/NetworkSession.php
@@ -98,6 +98,7 @@ use pocketmine\network\mcpe\protocol\types\command\CommandPermissions;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
+use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
@@ -228,6 +229,7 @@ class NetworkSession{
$this->info = $info;
$this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET);
$this->logger->setPrefix($this->getLogPrefix());
+ $this->manager->markLoginReceived($this);
},
function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{
$this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey);
@@ -364,7 +366,7 @@ class NetworkSession{
}
try{
- foreach((new PacketBatch($decompressed))->getPackets($this->packetPool, $this->packetSerializerContext, 500) as [$packet, $buffer]){
+ foreach((new PacketBatch($decompressed))->getPackets($this->packetPool, $this->packetSerializerContext, 1300) as [$packet, $buffer]){
if($packet === null){
$this->logger->debug("Unknown packet: " . base64_encode($buffer));
throw new PacketHandlingException("Unknown packet received");
@@ -877,7 +879,7 @@ class NetworkSession{
//TODO: HACK! as of 1.18.10, the client responds differently to the same data ordered in different orders - for
//example, sending HEIGHT in the list before FLAGS when unsetting the SWIMMING flag results in a hitbox glitch
ksort($properties, SORT_NUMERIC);
- $this->sendDataPacket(SetActorDataPacket::create($entity->getId(), $properties, 0));
+ $this->sendDataPacket(SetActorDataPacket::create($entity->getId(), $properties, new PropertySyncData([], []), 0));
}
public function onEntityEffectAdded(Living $entity, EffectInstance $effect, bool $replacesOldEffect) : void{
@@ -896,11 +898,11 @@ class NetworkSession{
public function syncAvailableCommands() : void{
$commandData = [];
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
- if(isset($commandData[$command->getName()]) || $command->getName() === "help" || !$command->testPermissionSilent($this->player)){
+ if(isset($commandData[$command->getLabel()]) || $command->getLabel() === "help" || !$command->testPermissionSilent($this->player)){
continue;
}
- $lname = strtolower($command->getName());
+ $lname = strtolower($command->getLabel());
$aliases = $command->getAliases();
$aliasObj = null;
if(count($aliases) > 0){
@@ -908,7 +910,7 @@ class NetworkSession{
//work around a client bug which makes the original name not show when aliases are used
$aliases[] = $lname;
}
- $aliasObj = new CommandEnum(ucfirst($command->getName()) . "Aliases", array_values($aliases));
+ $aliasObj = new CommandEnum(ucfirst($command->getLabel()) . "Aliases", array_values($aliases));
}
$description = $command->getDescription();
@@ -923,7 +925,7 @@ class NetworkSession{
]
);
- $commandData[$command->getName()] = $data;
+ $commandData[$command->getLabel()] = $data;
}
$this->sendDataPacket(AvailableCommandsPacket::create($commandData, [], [], []));
diff --git a/src/network/mcpe/PacketBroadcaster.php b/src/network/mcpe/PacketBroadcaster.php
index a31393981..2a231b7f3 100644
--- a/src/network/mcpe/PacketBroadcaster.php
+++ b/src/network/mcpe/PacketBroadcaster.php
@@ -28,7 +28,7 @@ use pocketmine\network\mcpe\protocol\ClientboundPacket;
interface PacketBroadcaster{
/**
- * @param NetworkSession[] $recipients
+ * @param NetworkSession[] $recipients
* @param ClientboundPacket[] $packets
*/
public function broadcastPackets(array $recipients, array $packets) : void;
diff --git a/src/network/mcpe/convert/ItemTranslator.php b/src/network/mcpe/convert/ItemTranslator.php
index 30e3d6b92..ba13fa784 100644
--- a/src/network/mcpe/convert/ItemTranslator.php
+++ b/src/network/mcpe/convert/ItemTranslator.php
@@ -118,7 +118,7 @@ final class ItemTranslator{
}
/**
- * @param int[] $simpleMappings
+ * @param int[] $simpleMappings
* @param int[][] $complexMappings
* @phpstan-param array $simpleMappings
* @phpstan-param array> $complexMappings
@@ -169,6 +169,7 @@ final class ItemTranslator{
}
/**
+ * @phpstan-param-out bool $isComplexMapping
* @return int[]
* @phpstan-return array{int, int}
* @throws TypeConversionException
diff --git a/src/network/mcpe/encryption/EncryptionUtils.php b/src/network/mcpe/encryption/EncryptionUtils.php
index cb56562db..920c54a7e 100644
--- a/src/network/mcpe/encryption/EncryptionUtils.php
+++ b/src/network/mcpe/encryption/EncryptionUtils.php
@@ -34,6 +34,7 @@ use function openssl_digest;
use function openssl_error_string;
use function openssl_pkey_derive;
use function str_pad;
+use const STR_PAD_LEFT;
final class EncryptionUtils{
diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php
index ef8159bee..c617d1598 100644
--- a/src/network/mcpe/handler/InGamePacketHandler.php
+++ b/src/network/mcpe/handler/InGamePacketHandler.php
@@ -255,6 +255,10 @@ class InGamePacketHandler extends PacketHandler{
$useItemTransaction = $packet->getItemInteractionData();
if($useItemTransaction !== null){
+ if(count($useItemTransaction->getTransactionData()->getActions()) > 100){
+ throw new PacketHandlingException("Too many actions in item use transaction");
+ }
+ $this->inventoryManager->addPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions());
if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
$packetHandled = false;
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
@@ -273,6 +277,9 @@ class InGamePacketHandler extends PacketHandler{
$blockActions = $packet->getBlockActions();
if($blockActions !== null){
+ if(count($blockActions) > 100){
+ throw new PacketHandlingException("Too many block actions in PlayerAuthInputPacket");
+ }
foreach($blockActions as $k => $blockAction){
$actionHandled = false;
if($blockAction instanceof PlayerBlockActionStopBreak){
@@ -319,6 +326,12 @@ class InGamePacketHandler extends PacketHandler{
public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{
$result = true;
+ if(count($packet->trData->getActions()) > 100){
+ throw new PacketHandlingException("Too many actions in inventory transaction");
+ }
+
+ $this->inventoryManager->addRawPredictedSlotChanges($packet->trData->getActions());
+
if($packet->trData instanceof NormalTransactionData){
$result = $this->handleNormalTransaction($packet->trData);
}elseif($packet->trData instanceof MismatchTransactionData){
@@ -333,9 +346,7 @@ class InGamePacketHandler extends PacketHandler{
$result = $this->handleReleaseItemTransaction($packet->trData);
}
- if(!$result){
- $this->inventoryManager->syncAll();
- }else{
+ if($this->craftingTransaction === null){ //don't sync if we're waiting to complete a crafting transaction
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
}
if($packet->requestId !== 0){
@@ -438,7 +449,6 @@ class InGamePacketHandler extends PacketHandler{
private function handleUseItemTransaction(UseItemTransactionData $data) : bool{
$this->player->selectHotbarSlot($data->getHotbarSlot());
- $this->inventoryManager->addRawPredictedSlotChanges($data->getActions());
switch($data->getActionType()){
case UseItemTransactionData::ACTION_CLICK_BLOCK:
@@ -524,9 +534,7 @@ class InGamePacketHandler extends PacketHandler{
}
$this->player->selectHotbarSlot($data->getHotbarSlot());
- $this->inventoryManager->addRawPredictedSlotChanges($data->getActions());
- //TODO: use transactiondata for rollbacks here
switch($data->getActionType()){
case UseItemOnEntityTransactionData::ACTION_INTERACT:
$this->player->interactEntity($target, $data->getClickPosition());
@@ -541,15 +549,10 @@ class InGamePacketHandler extends PacketHandler{
private function handleReleaseItemTransaction(ReleaseItemTransactionData $data) : bool{
$this->player->selectHotbarSlot($data->getHotbarSlot());
- $this->inventoryManager->addRawPredictedSlotChanges($data->getActions());
- //TODO: use transactiondata for rollbacks here (resending entire inventory is very wasteful)
- switch($data->getActionType()){
- case ReleaseItemTransactionData::ACTION_RELEASE:
- if(!$this->player->releaseHeldItem()){
- $this->inventoryManager->syncContents($this->player->getInventory());
- }
- return true;
+ if($data->getActionType() == ReleaseItemTransactionData::ACTION_RELEASE){
+ $this->player->releaseHeldItem();
+ return true;
}
return false;
diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php
index 11e5b4af4..80f162697 100644
--- a/src/network/mcpe/handler/LoginPacketHandler.php
+++ b/src/network/mcpe/handler/LoginPacketHandler.php
@@ -123,7 +123,7 @@ class LoginPacketHandler extends PacketHandler{
$this->session->getPort(),
$this->server->requiresAuthentication()
);
- if($this->server->getNetwork()->getConnectionCount() > $this->server->getMaxPlayers()){
+ if($this->server->getNetwork()->getValidConnectionCount() > $this->server->getMaxPlayers()){
$ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_SERVER_FULL, KnownTranslationKeys::DISCONNECTIONSCREEN_SERVERFULL);
}
if(!$this->server->isWhitelisted($playerInfo->getUsername())){
diff --git a/src/network/mcpe/handler/SpawnResponsePacketHandler.php b/src/network/mcpe/handler/SpawnResponsePacketHandler.php
index fae3597b8..554322af5 100644
--- a/src/network/mcpe/handler/SpawnResponsePacketHandler.php
+++ b/src/network/mcpe/handler/SpawnResponsePacketHandler.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\handler;
+use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket;
final class SpawnResponsePacketHandler extends PacketHandler{
@@ -35,4 +36,10 @@ final class SpawnResponsePacketHandler extends PacketHandler{
($this->responseCallback)();
return true;
}
+
+ public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
+ //the client will send this every tick once we start sending chunks, but we don't handle it in this stage
+ //this is very spammy so we filter it out
+ return true;
+ }
}
diff --git a/src/network/mcpe/raklib/RakLibInterface.php b/src/network/mcpe/raklib/RakLibInterface.php
index 33a50ac56..bf1f75c26 100644
--- a/src/network/mcpe/raklib/RakLibInterface.php
+++ b/src/network/mcpe/raklib/RakLibInterface.php
@@ -52,6 +52,7 @@ use function mt_rand;
use function random_bytes;
use function rtrim;
use function substr;
+use const PHP_INT_MAX;
class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
/**
diff --git a/src/network/query/DedicatedQueryNetworkInterface.php b/src/network/query/DedicatedQueryNetworkInterface.php
index c2d27029c..2917ba0ee 100644
--- a/src/network/query/DedicatedQueryNetworkInterface.php
+++ b/src/network/query/DedicatedQueryNetworkInterface.php
@@ -40,6 +40,7 @@ use function strlen;
use function time;
use function trim;
use const AF_INET;
+use const AF_INET6;
use const IPPROTO_IPV6;
use const IPV6_V6ONLY;
use const PHP_INT_MAX;
diff --git a/src/network/upnp/UPnP.php b/src/network/upnp/UPnP.php
index 0d482ab3d..2d48a2db8 100644
--- a/src/network/upnp/UPnP.php
+++ b/src/network/upnp/UPnP.php
@@ -73,6 +73,12 @@ use function sprintf;
use function strlen;
use function trim;
use const AF_INET;
+use const PREG_BACKTRACK_LIMIT_ERROR;
+use const PREG_BAD_UTF8_ERROR;
+use const PREG_BAD_UTF8_OFFSET_ERROR;
+use const PREG_INTERNAL_ERROR;
+use const PREG_JIT_STACKLIMIT_ERROR;
+use const PREG_RECURSION_LIMIT_ERROR;
use const SO_RCVTIMEO;
use const SOCK_DGRAM;
use const SOCKET_ETIMEDOUT;
@@ -150,6 +156,7 @@ class UPnP{
throw new UPnPException("Failed to recognize the port number from the router's url: {$location}");
}
$urlPort = $url['port'];
+ $err = "";
$response = Internet::getURL($location, 3, [], $err);
if($response === null){
throw new UPnPException("Unable to access XML: {$err}");
diff --git a/src/player/Player.php b/src/player/Player.php
index 157038beb..4ba1c81ae 100644
--- a/src/player/Player.php
+++ b/src/player/Player.php
@@ -1390,7 +1390,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
/**
* Returns whether the player can interact with the specified position. This checks distance and direction.
*
- * @param float $maxDiff defaults to half of the 3D diagonal width of a block
+ * @param float $maxDiff defaults to half of the 3D diagonal width of a block
*/
public function canInteract(Vector3 $pos, float $maxDistance, float $maxDiff = M_SQRT3 / 2) : bool{
$eyePos = $this->getEyePos();
@@ -1916,9 +1916,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
/**
* Adds a title text to the user's screen, with an optional subtitle.
*
- * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
- * @param int $stay Duration in ticks to stay on screen for
- * @param int $fadeOut Duration in ticks for fade-out.
+ * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used.
+ * @param int $stay Duration in ticks to stay on screen for
+ * @param int $fadeOut Duration in ticks for fade-out.
*/
public function sendTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1) : void{
$this->setTitleDuration($fadeIn, $stay, $fadeOut);
@@ -1959,8 +1959,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
/**
* Sets the title duration.
*
- * @param int $fadeIn Title fade-in time in ticks.
- * @param int $stay Title stay time in ticks.
+ * @param int $fadeIn Title fade-in time in ticks.
+ * @param int $stay Title stay time in ticks.
* @param int $fadeOut Title fade-out time in ticks.
*/
public function setTitleDuration(int $fadeIn, int $stay, int $fadeOut) : void{
@@ -2061,7 +2061,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
* Transfers a player to another server.
*
* @param string $address The IP address or hostname of the destination server
- * @param int $port The destination port, defaults to 19132
+ * @param int $port The destination port, defaults to 19132
* @param string $message Message to show in the console when closing the player
*
* @return bool if transfer was successful.
@@ -2105,7 +2105,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
*
* Note for internals developers: Do not call this from network sessions. It will cause a feedback loop.
*
- * @param string $reason Shown to the player, usually this will appear on their disconnect screen.
+ * @param string $reason Shown to the player, usually this will appear on their disconnect screen.
* @param Translatable|string|null $quitMessage Message to broadcast to online players (null will use default)
*/
public function disconnect(string $reason, Translatable|string|null $quitMessage = null) : void{
@@ -2121,7 +2121,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
* @internal
* This method executes post-disconnect actions and cleanups.
*
- * @param string $reason Shown to the player, usually this will appear on their disconnect screen.
+ * @param string $reason Shown to the player, usually this will appear on their disconnect screen.
* @param Translatable|string|null $quitMessage Message to broadcast to online players (null will use default)
*/
public function onPostDisconnect(string $reason, Translatable|string|null $quitMessage) : void{
@@ -2416,9 +2416,6 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->ySize = 0;
}
- /**
- * {@inheritdoc}
- */
public function teleport(Vector3 $pos, ?float $yaw = null, ?float $pitch = null) : bool{
if(parent::teleport($pos, $yaw, $pitch)){
diff --git a/src/plugin/PluginBase.php b/src/plugin/PluginBase.php
index 9b0ac2399..a4c0f6ba4 100644
--- a/src/plugin/PluginBase.php
+++ b/src/plugin/PluginBase.php
@@ -45,6 +45,7 @@ use function stream_copy_to_stream;
use function strpos;
use function strtolower;
use function trim;
+use const DIRECTORY_SEPARATOR;
abstract class PluginBase implements Plugin, CommandExecutor{
private bool $isEnabled = false;
@@ -201,7 +202,7 @@ abstract class PluginBase implements Plugin, CommandExecutor{
}
/**
- * @param string[] $args
+ * @param string[] $args
*/
public function onCommand(CommandSender $sender, Command $command, string $label, array $args) : bool{
return false;
diff --git a/src/plugin/PluginGraylist.php b/src/plugin/PluginGraylist.php
index ef719b72b..84aa3f792 100644
--- a/src/plugin/PluginGraylist.php
+++ b/src/plugin/PluginGraylist.php
@@ -70,8 +70,7 @@ class PluginGraylist{
}
$isWhitelist = match($array["mode"]){
"whitelist" => true,
- "blacklist" => false,
- default => throw new \InvalidArgumentException("\"mode\" must be either \"whitelist\" or \"blacklist\"")
+ "blacklist" => false
};
$plugins = [];
if(isset($array["plugins"])){
diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php
index fc16fa7ea..07a099f2f 100644
--- a/src/plugin/PluginManager.php
+++ b/src/plugin/PluginManager.php
@@ -319,6 +319,9 @@ class PluginManager{
/**
* @param string[][] $dependencyLists
* @param Plugin[] $loadedPlugins
+ *
+ * @phpstan-param array> $dependencyLists
+ * @phpstan-param-out array> $dependencyLists
*/
private function checkDepsForTriage(string $pluginName, string $dependencyType, array &$dependencyLists, array $loadedPlugins, PluginLoadTriage $triage) : void{
if(isset($dependencyLists[$pluginName])){
diff --git a/src/resourcepacks/ResourcePack.php b/src/resourcepacks/ResourcePack.php
index 93f446965..04feeeb3d 100644
--- a/src/resourcepacks/ResourcePack.php
+++ b/src/resourcepacks/ResourcePack.php
@@ -57,7 +57,7 @@ interface ResourcePack{
* Note that resource packs must **always** be in zip archive format for sending.
* A folder resource loader may need to perform on-the-fly compression for this purpose.
*
- * @param int $start Offset to start reading the chunk from
+ * @param int $start Offset to start reading the chunk from
* @param int $length Maximum length of data to return.
*
* @return string byte-array
diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php
index d1482d8b6..c8b2cb389 100644
--- a/src/resourcepacks/ResourcePackManager.php
+++ b/src/resourcepacks/ResourcePackManager.php
@@ -58,7 +58,7 @@ class ResourcePackManager{
private array $encryptionKeys = [];
/**
- * @param string $path Path to resource-packs directory.
+ * @param string $path Path to resource-packs directory.
*/
public function __construct(string $path, \Logger $logger){
$this->path = $path;
diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php
index c65552bf3..831753f4d 100644
--- a/src/scheduler/AsyncTask.php
+++ b/src/scheduler/AsyncTask.php
@@ -187,7 +187,7 @@ abstract class AsyncTask extends \Threaded{
* {@link AsyncTask::onCompletion} is called.
*
* @param mixed $progress The parameter passed to {@link AsyncTask#publishProgress}. It is serialize()'ed
- * and then unserialize()'ed, as if it has been cloned.
+ * and then unserialize()'ed, as if it has been cloned.
*/
public function onProgressUpdate($progress) : void{
@@ -215,7 +215,7 @@ abstract class AsyncTask extends \Threaded{
* Objects stored in this storage can be retrieved using fetchLocal() on the same thread that this method was called
* from.
*
- * @param mixed $complexData the data to store
+ * @param mixed $complexData the data to store
*/
protected function storeLocal(string $key, $complexData) : void{
if(self::$threadLocalStorage === null){
diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php
index af1ec2ad6..7262f9597 100644
--- a/src/scheduler/AsyncWorker.php
+++ b/src/scheduler/AsyncWorker.php
@@ -77,7 +77,7 @@ class AsyncWorker extends Worker{
* Saves mixed data into the worker's thread-local object store. This can be used to store objects which you
* want to use on this worker thread from multiple AsyncTasks.
*
- * @param mixed $value
+ * @param mixed $value
*/
public function saveToThreadStore(string $identifier, $value) : void{
if(\Thread::getCurrentThread() !== $this){
diff --git a/src/scheduler/BulkCurlTaskOperation.php b/src/scheduler/BulkCurlTaskOperation.php
index cd6b478fd..3291d8718 100644
--- a/src/scheduler/BulkCurlTaskOperation.php
+++ b/src/scheduler/BulkCurlTaskOperation.php
@@ -26,7 +26,7 @@ namespace pocketmine\scheduler;
final class BulkCurlTaskOperation{
/**
* @param string[] $extraHeaders
- * @param mixed[] $extraOpts
+ * @param mixed[] $extraOpts
* @phpstan-param list $extraHeaders
* @phpstan-param array $extraOpts
*/
diff --git a/src/utils/BroadcastLoggerForwarder.php b/src/utils/BroadcastLoggerForwarder.php
index a015615b5..1900421fc 100644
--- a/src/utils/BroadcastLoggerForwarder.php
+++ b/src/utils/BroadcastLoggerForwarder.php
@@ -29,6 +29,7 @@ use pocketmine\lang\Translatable;
use pocketmine\permission\PermissibleBase;
use pocketmine\permission\PermissibleDelegateTrait;
use pocketmine\Server;
+use const PHP_INT_MAX;
/**
* Forwards any messages it receives via sendMessage() to the given logger. Used for forwarding chat messages and
diff --git a/src/utils/CloningRegistryTrait.php b/src/utils/CloningRegistryTrait.php
index c69b38d7d..d160b1f45 100644
--- a/src/utils/CloningRegistryTrait.php
+++ b/src/utils/CloningRegistryTrait.php
@@ -23,6 +23,10 @@ declare(strict_types=1);
namespace pocketmine\utils;
+/**
+ * This trait offers the same functionality as RegistryTrait, but also clones any returned objects to prevent outside
+ * modification.
+ */
trait CloningRegistryTrait{
use RegistryTrait;
diff --git a/src/utils/Config.php b/src/utils/Config.php
index 306f9eb80..617feeacf 100644
--- a/src/utils/Config.php
+++ b/src/utils/Config.php
@@ -55,6 +55,7 @@ use const CASE_LOWER;
use const JSON_BIGINT_AS_STRING;
use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR;
+use const YAML_UTF8_ENCODING;
/**
* Config Class for simple config manipulation of multiple formats.
@@ -108,8 +109,8 @@ class Config{
];
/**
- * @param string $file Path of the file to be loaded
- * @param int $type Config type to load, -1 by default (detect)
+ * @param string $file Path of the file to be loaded
+ * @param int $type Config type to load, -1 by default (detect)
* @param mixed[] $default Array with the default values that will be written to the file if it did not exist
* @phpstan-param array $default
*/
@@ -493,9 +494,10 @@ class Config{
/**
* @param mixed[] $default
- * @param mixed[] $data reference parameter
+ * @param mixed[] $data reference parameter
* @phpstan-param array $default
* @phpstan-param array $data
+ * @phpstan-param-out array $data
*/
private function fillDefaults(array $default, &$data) : int{
$changed = 0;
diff --git a/src/utils/EnumTrait.php b/src/utils/EnumTrait.php
index c9d24e0fc..ba0a4fcf2 100644
--- a/src/utils/EnumTrait.php
+++ b/src/utils/EnumTrait.php
@@ -23,6 +23,13 @@ declare(strict_types=1);
namespace pocketmine\utils;
+/**
+ * This trait allows a class to simulate a Java-style enum. Members are exposed as static methods and handled via
+ * __callStatic().
+ *
+ * Classes using this trait need to include \@method tags in their class docblock for every enum member.
+ * Alternatively, just put \@generate-registry-docblock in the docblock and run tools/generate-registry-annotations.php
+ */
trait EnumTrait{
use RegistryTrait;
use NotCloneable;
diff --git a/src/utils/Filesystem.php b/src/utils/Filesystem.php
index a4c1ff236..0a183d4cd 100644
--- a/src/utils/Filesystem.php
+++ b/src/utils/Filesystem.php
@@ -187,9 +187,10 @@ final class Filesystem{
* @throws \InvalidArgumentException if the lock file path is invalid (e.g. parent directory doesn't exist, permission denied)
*/
public static function createLockFile(string $lockFilePath) : ?int{
- $resource = fopen($lockFilePath, "a+b");
- if($resource === false){
- throw new \InvalidArgumentException("Invalid lock file path or read/write permissions denied");
+ try{
+ $resource = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($lockFilePath, "a+b"));
+ }catch(\ErrorException $e){
+ throw new \InvalidArgumentException("Failed to open lock file: " . $e->getMessage(), 0, $e);
}
if(!flock($resource, LOCK_EX | LOCK_NB)){
//wait for a shared lock to avoid race conditions if two servers started at the same time - this makes sure the
diff --git a/src/utils/Git.php b/src/utils/Git.php
index 9d5744f06..041d795a1 100644
--- a/src/utils/Git.php
+++ b/src/utils/Git.php
@@ -36,7 +36,7 @@ final class Git{
/**
* Returns the git hash of the currently checked out head of the given repository, or null on failure.
*
- * @param bool $dirty reference parameter, set to whether the repo has local changes
+ * @param bool $dirty reference parameter, set to whether the repo has local changes
*/
public static function getRepositoryState(string $dir, bool &$dirty) : ?string{
if(Process::execute("git -C \"$dir\" rev-parse HEAD", $out) === 0 && $out !== false && strlen($out = trim($out)) === 40){
diff --git a/src/utils/Internet.php b/src/utils/Internet.php
index a679320bb..4aa39cb36 100644
--- a/src/utils/Internet.php
+++ b/src/utils/Internet.php
@@ -70,7 +70,7 @@ class Internet{
public static $online = true;
/**
- * Gets the External IP using an external service, it is cached
+ * Lazily gets the External IP using an external service and caches the result
*
* @param bool $force default false, force IP check even when cached
*
@@ -139,10 +139,14 @@ class Internet{
* GETs an URL using cURL
* NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread.
*
- * @param int $timeout default 10
+ * @phpstan-template TErrorVar of mixed
+ *
+ * @param int $timeout default 10
* @param string[] $extraHeaders
- * @param string|null $err reference parameter, will be set to the output of curl_error(). Use this to retrieve errors that occured during the operation.
+ * @param string|null $err reference parameter, will be set to the output of curl_error(). Use this to retrieve errors that occured during the operation.
* @phpstan-param list $extraHeaders
+ * @phpstan-param TErrorVar $err
+ * @phpstan-param-out TErrorVar|string $err
*/
public static function getURL(string $page, int $timeout = 10, array $extraHeaders = [], &$err = null) : ?InternetRequestResult{
try{
@@ -157,11 +161,15 @@ class Internet{
* POSTs data to an URL
* NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread.
*
+ * @phpstan-template TErrorVar of mixed
+ *
* @param string[]|string $args
* @param string[] $extraHeaders
- * @param string|null $err reference parameter, will be set to the output of curl_error(). Use this to retrieve errors that occurred during the operation.
+ * @param string|null $err reference parameter, will be set to the output of curl_error(). Use this to retrieve errors that occurred during the operation.
* @phpstan-param string|array $args
* @phpstan-param list $extraHeaders
+ * @phpstan-param TErrorVar $err
+ * @phpstan-param-out TErrorVar|string $err
*/
public static function postURL(string $page, $args, int $timeout = 10, array $extraHeaders = [], &$err = null) : ?InternetRequestResult{
try{
@@ -179,7 +187,7 @@ class Internet{
* General cURL shorthand function.
* NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread.
*
- * @param float|int $timeout The maximum connect timeout and timeout in seconds, correct to ms.
+ * @param float|int $timeout The maximum connect timeout and timeout in seconds, correct to ms.
* @param string[] $extraHeaders extra headers to send as a plain string array
* @param array $extraOpts extra CURLOPT_* to set as an [opt => value] map
* @param \Closure|null $onSuccess function to be called if there is no error. Accepts a resource argument as the cURL handle.
diff --git a/src/utils/Process.php b/src/utils/Process.php
index d66dd80bd..47e6239df 100644
--- a/src/utils/Process.php
+++ b/src/utils/Process.php
@@ -150,8 +150,10 @@ final class Process{
/**
* @param string $command Command to execute
- * @param string|null $stdout Reference parameter to write stdout to
- * @param string|null $stderr Reference parameter to write stderr to
+ * @param string|null $stdout Reference parameter to write stdout to
+ * @param string|null $stderr Reference parameter to write stderr to
+ * @phpstan-param-out string $stdout
+ * @phpstan-param-out string $stderr
*
* @return int process exit code
*/
diff --git a/src/utils/Random.php b/src/utils/Random.php
index 3d2963c30..e0318c50a 100644
--- a/src/utils/Random.php
+++ b/src/utils/Random.php
@@ -115,7 +115,7 @@ class Random{
* Returns a random integer between $start and $end
*
* @param int $start default 0
- * @param int $end default 0x7fffffff
+ * @param int $end default 0x7fffffff
*/
public function nextRange(int $start = 0, int $end = 0x7fffffff) : int{
return $start + ($this->nextInt() % ($end + 1 - $start));
diff --git a/src/utils/RegistryTrait.php b/src/utils/RegistryTrait.php
index 776980599..f8ffc1143 100644
--- a/src/utils/RegistryTrait.php
+++ b/src/utils/RegistryTrait.php
@@ -28,6 +28,13 @@ use function count;
use function mb_strtoupper;
use function preg_match;
+/**
+ * This trait allows a class to simulate object class constants, since PHP doesn't currently support this.
+ * These faux constants are exposed in static class methods, which are handled using __callStatic().
+ *
+ * Classes using this trait need to include \@method tags in their class docblock for every faux constant.
+ * Alternatively, just put \@generate-registry-docblock in the docblock and run tools/generate-registry-annotations.php
+ */
trait RegistryTrait{
/**
* @var object[]
diff --git a/src/utils/Terminal.php b/src/utils/Terminal.php
index 0ad729658..7144b8a9b 100644
--- a/src/utils/Terminal.php
+++ b/src/utils/Terminal.php
@@ -166,8 +166,10 @@ abstract class Terminal{
case Utils::OS_LINUX:
case Utils::OS_MACOS:
case Utils::OS_BSD:
- self::getEscapeCodes();
- return;
+ if(getenv('TERM') !== false){
+ self::getEscapeCodes();
+ return;
+ }
case Utils::OS_WINDOWS:
case Utils::OS_ANDROID:
diff --git a/src/utils/Timezone.php b/src/utils/Timezone.php
index 02110c0eb..3cf3a19df 100644
--- a/src/utils/Timezone.php
+++ b/src/utils/Timezone.php
@@ -208,7 +208,7 @@ abstract class Timezone{
//That's been a bug in PHP since 2008!
foreach(timezone_abbreviations_list() as $zones){
foreach($zones as $timezone){
- if($timezone['offset'] == $offset){
+ if($timezone['timezone_id'] !== null && $timezone['offset'] == $offset){
return $timezone['timezone_id'];
}
}
diff --git a/src/utils/Utils.php b/src/utils/Utils.php
index 35d6595ca..a77a874d9 100644
--- a/src/utils/Utils.php
+++ b/src/utils/Utils.php
@@ -313,7 +313,7 @@ final class Utils{
}
}elseif(($cpuPresent = @file_get_contents("/sys/devices/system/cpu/present")) !== false){
if(preg_match("/^([0-9]+)\\-([0-9]+)$/", trim($cpuPresent), $matches) > 0){
- $processors = (int) ($matches[2] - $matches[1]);
+ $processors = ((int) $matches[2]) - ((int) $matches[1]);
}
}
break;
@@ -539,7 +539,7 @@ final class Utils{
* incompatible.
*
* @param callable|CallbackType $signature Dummy callable with the required parameters and return type
- * @param callable $subject Callable to check the signature of
+ * @param callable $subject Callable to check the signature of
* @phpstan-param anyCallable|CallbackType $signature
* @phpstan-param anyCallable $subject
*
diff --git a/src/world/World.php b/src/world/World.php
index 0071f221f..54fd7863b 100644
--- a/src/world/World.php
+++ b/src/world/World.php
@@ -66,6 +66,7 @@ use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
+use pocketmine\player\ChunkSelector;
use pocketmine\player\Player;
use pocketmine\promise\Promise;
use pocketmine\promise\PromiseResolver;
@@ -317,7 +318,6 @@ class World implements ChunkManager{
private int $sleepTicks = 0;
private int $chunkTickRadius;
- private int $chunksPerTick;
private int $tickedBlocksPerSubchunkPerTick = self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK;
/**
* @var true[]
@@ -396,6 +396,9 @@ class World implements ChunkManager{
/**
* @phpstan-param BlockPosHash $hash
+ * @phpstan-param-out int $x
+ * @phpstan-param-out int $y
+ * @phpstan-param-out int $z
*/
public static function getBlockXYZ(int $hash, ?int &$x, ?int &$y, ?int &$z) : void{
[$baseX, $baseY, $baseZ] = morton3d_decode($hash);
@@ -410,6 +413,8 @@ class World implements ChunkManager{
/**
* @phpstan-param ChunkPosHash $hash
+ * @phpstan-param-out int $x
+ * @phpstan-param-out int $z
*/
public static function getXZ(int $hash, ?int &$x, ?int &$z) : void{
[$x, $z] = morton2d_decode($hash);
@@ -488,7 +493,11 @@ class World implements ChunkManager{
$cfg = $this->server->getConfigGroup();
$this->chunkTickRadius = min($this->server->getViewDistance(), max(1, $cfg->getPropertyInt("chunk-ticking.tick-radius", 4)));
- $this->chunksPerTick = $cfg->getPropertyInt("chunk-ticking.per-tick", 40);
+ if($cfg->getPropertyInt("chunk-ticking.per-tick", 40) <= 0){
+ //TODO: this needs l10n
+ $this->logger->warning("\"chunk-ticking.per-tick\" setting is deprecated, but you've used it to disable chunk ticking. Set \"chunk-ticking.tick-radius\" to 0 in \"pocketmine.yml\" instead.");
+ $this->chunkTickRadius = 0;
+ }
$this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt("chunk-ticking.blocks-per-subchunk-per-tick", self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK);
$this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt("chunk-generation.population-queue-size", 2);
@@ -634,7 +643,7 @@ class World implements ChunkManager{
* Returns a list of players who are in the given filter and also using the chunk containing the target position.
* Used for broadcasting sounds and particles with specific targets.
*
- * @param Player[] $allowed
+ * @param Player[] $allowed
* @phpstan-param list $allowed
*
* @return array
@@ -1095,8 +1104,23 @@ class World implements ChunkManager{
unset($this->randomTickBlocks[$block->getFullId()]);
}
+ /**
+ * Returns the radius of chunks to be ticked around each ticking chunk loader (usually players). This is referred to
+ * as "simulation distance" in the Minecraft: Bedrock world options screen.
+ */
+ public function getChunkTickRadius() : int{
+ return $this->chunkTickRadius;
+ }
+
+ /**
+ * Sets the radius of chunks ticked around each ticking chunk loader (usually players).
+ */
+ public function setChunkTickRadius(int $radius) : void{
+ $this->chunkTickRadius = $radius;
+ }
+
private function tickChunks() : void{
- if($this->chunksPerTick <= 0 || count($this->tickingLoaders) === 0){
+ if($this->chunkTickRadius <= 0 || count($this->tickingLoaders) === 0){
return;
}
@@ -1105,19 +1129,26 @@ class World implements ChunkManager{
/** @var bool[] $chunkTickList chunkhash => dummy */
$chunkTickList = [];
- $chunksPerLoader = min(200, max(1, (int) ((($this->chunksPerTick - count($this->tickingLoaders)) / count($this->tickingLoaders)) + 0.5)));
- $randRange = 3 + $chunksPerLoader / 30;
- $randRange = (int) ($randRange > $this->chunkTickRadius ? $this->chunkTickRadius : $randRange);
+ $centerChunks = [];
+ $selector = new ChunkSelector();
foreach($this->tickingLoaders as $loader){
- $chunkX = (int) floor($loader->getX()) >> Chunk::COORD_BIT_SIZE;
- $chunkZ = (int) floor($loader->getZ()) >> Chunk::COORD_BIT_SIZE;
+ $centerChunkX = (int) floor($loader->getX()) >> Chunk::COORD_BIT_SIZE;
+ $centerChunkZ = (int) floor($loader->getZ()) >> Chunk::COORD_BIT_SIZE;
+ $centerChunkPosHash = World::chunkHash($centerChunkX, $centerChunkZ);
+ if(isset($centerChunks[$centerChunkPosHash])){
+ //we already queued chunks in this radius because of a previous loader on the same chunk
+ continue;
+ }
+ $centerChunks[$centerChunkPosHash] = true;
- for($chunk = 0; $chunk < $chunksPerLoader; ++$chunk){
- $dx = mt_rand(-$randRange, $randRange);
- $dz = mt_rand(-$randRange, $randRange);
- $hash = World::chunkHash($dx + $chunkX, $dz + $chunkZ);
- if(!isset($chunkTickList[$hash]) && isset($this->chunks[$hash]) && $this->isChunkTickable($dx + $chunkX, $dz + $chunkZ)){
+ foreach($selector->selectChunks(
+ $this->chunkTickRadius,
+ $centerChunkX,
+ $centerChunkZ
+ ) as $hash){
+ World::getXZ($hash, $chunkX, $chunkZ);
+ if(!isset($chunkTickList[$hash]) && isset($this->chunks[$hash]) && $this->isChunkTickable($chunkX, $chunkZ)){
$chunkTickList[$hash] = true;
}
}
@@ -1191,7 +1222,8 @@ class World implements ChunkManager{
private function tickChunk(int $chunkX, int $chunkZ) : void{
$chunk = $this->getChunk($chunkX, $chunkZ);
if($chunk === null){
- throw new \InvalidArgumentException("Chunk is not loaded");
+ //the chunk may have been unloaded during a previous chunk's update (e.g. during BlockGrowEvent)
+ return;
}
foreach($this->getChunkEntities($chunkX, $chunkZ) as $entity){
$entity->onRandomUpdate();
@@ -1603,8 +1635,8 @@ class World implements ChunkManager{
* Note: If you're using this for performance-sensitive code, and you're guaranteed to be supplying ints in the
* specified vector, consider using {@link getBlockAt} instead for better performance.
*
- * @param bool $cached Whether to use the block cache for getting the block (faster, but may be inaccurate)
- * @param bool $addToCache Whether to cache the block object created by this method call.
+ * @param bool $cached Whether to use the block cache for getting the block (faster, but may be inaccurate)
+ * @param bool $addToCache Whether to cache the block object created by this method call.
*/
public function getBlock(Vector3 $pos, bool $cached = true, bool $addToCache = true) : Block{
return $this->getBlockAt((int) floor($pos->x), (int) floor($pos->y), (int) floor($pos->z), $cached, $addToCache);
@@ -1616,7 +1648,7 @@ class World implements ChunkManager{
* Note for plugin developers: If you are using this method a lot (thousands of times for many positions for
* example), you may want to set addToCache to false to avoid using excessive amounts of memory.
*
- * @param bool $cached Whether to use the block cache for getting the block (faster, but may be inaccurate)
+ * @param bool $cached Whether to use the block cache for getting the block (faster, but may be inaccurate)
* @param bool $addToCache Whether to cache the block object created by this method call.
*/
public function getBlockAt(int $x, int $y, int $z, bool $cached = true, bool $addToCache = true) : Block{
@@ -1763,7 +1795,8 @@ class World implements ChunkManager{
* Tries to break a block using a item, including Player time checks if available
* It'll try to lower the durability if Item is a tool, and set it to Air if broken.
*
- * @param Item $item reference parameter (if null, can break anything)
+ * @param Item $item reference parameter (if null, can break anything)
+ * @phpstan-param-out Item $item
*/
public function useBreakOn(Vector3 $vector, Item &$item = null, ?Player $player = null, bool $createParticles = false) : bool{
$vector = $vector->floor();
@@ -1864,8 +1897,8 @@ class World implements ChunkManager{
/**
* Uses a item on a position and face, placing it or activating the block
*
- * @param Player|null $player default null
- * @param bool $playSound Whether to play a block-place sound if the block was placed successfully.
+ * @param Player|null $player default null
+ * @param bool $playSound Whether to play a block-place sound if the block was placed successfully.
*/
public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $clickVector = null, ?Player $player = null, bool $playSound = false) : bool{
$blockClicked = $this->getBlock($vector);
@@ -2060,8 +2093,8 @@ class World implements ChunkManager{
/**
* Returns the closest Entity to the specified position, within the given radius.
*
- * @param string $entityType Class of entity to use for instanceof
- * @param bool $includeDead Whether to include entitites which are dead
+ * @param string $entityType Class of entity to use for instanceof
+ * @param bool $includeDead Whether to include entitites which are dead
* @phpstan-template TEntity of Entity
* @phpstan-param class-string $entityType
*
@@ -2778,8 +2811,8 @@ class World implements ChunkManager{
if($this->getBlockAt($x, $y, $z)->isFullCube()){
if($wasAir){
$y++;
- break;
}
+ break;
}else{
$wasAir = true;
}
diff --git a/src/world/WorldManager.php b/src/world/WorldManager.php
index 1c26d494b..80561298d 100644
--- a/src/world/WorldManager.php
+++ b/src/world/WorldManager.php
@@ -166,7 +166,7 @@ class WorldManager{
/**
* Loads a world from the data directory
*
- * @param bool $autoUpgrade Converts worlds to the default format if the world's format is not writable / deprecated
+ * @param bool $autoUpgrade Converts worlds to the default format if the world's format is not writable / deprecated
*
* @throws WorldException
*/
diff --git a/src/world/format/Chunk.php b/src/world/format/Chunk.php
index 865cc9a51..afcbf259e 100644
--- a/src/world/format/Chunk.php
+++ b/src/world/format/Chunk.php
@@ -164,8 +164,8 @@ class Chunk{
/**
* Sets the biome ID at the specified X/Z chunk block coordinates
*
- * @param int $x 0-15
- * @param int $z 0-15
+ * @param int $x 0-15
+ * @param int $z 0-15
* @param int $biomeId 0-255
*/
public function setBiomeId(int $x, int $z, int $biomeId) : void{
diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php
index c2204582a..05f4dd8f1 100644
--- a/src/world/format/io/leveldb/LevelDB.php
+++ b/src/world/format/io/leveldb/LevelDB.php
@@ -191,6 +191,11 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
return PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
}
+ /**
+ * @phpstan-param-out int $x
+ * @phpstan-param-out int $y
+ * @phpstan-param-out int $z
+ */
protected static function deserializeExtraDataKey(int $chunkVersion, int $key, ?int &$x, ?int &$y, ?int &$z) : void{
if($chunkVersion >= ChunkVersion::v1_0_0){
$x = ($key >> 12) & 0xf;
@@ -499,7 +504,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
/**
- * @param CompoundTag[] $targets
+ * @param CompoundTag[] $targets
*/
private function writeTags(array $targets, string $index, \LevelDBWriteBatch $write) : void{
if(count($targets) > 0){
diff --git a/src/world/format/io/region/RegionLoader.php b/src/world/format/io/region/RegionLoader.php
index 92848c6b5..cdba29f68 100644
--- a/src/world/format/io/region/RegionLoader.php
+++ b/src/world/format/io/region/RegionLoader.php
@@ -276,6 +276,8 @@ class RegionLoader{
/**
* @param int $x reference parameter
* @param int $z reference parameter
+ * @phpstan-param-out int $x
+ * @phpstan-param-out int $z
*/
protected static function getChunkCoords(int $offset, ?int &$x, ?int &$z) : void{
$x = $offset & 0x1f;
diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php
index f02b1d790..0efd50c4c 100644
--- a/src/world/format/io/region/RegionWorldProvider.php
+++ b/src/world/format/io/region/RegionWorldProvider.php
@@ -92,6 +92,8 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
/**
* @param int $regionX reference parameter
* @param int $regionZ reference parameter
+ * @phpstan-param-out int $regionX
+ * @phpstan-param-out int $regionZ
*/
public static function getRegionIndex(int $chunkX, int $chunkZ, &$regionX, &$regionZ) : void{
$regionX = $chunkX >> 5;
diff --git a/src/world/generator/FlatGeneratorOptions.php b/src/world/generator/FlatGeneratorOptions.php
index b5b39a4b7..a47c97990 100644
--- a/src/world/generator/FlatGeneratorOptions.php
+++ b/src/world/generator/FlatGeneratorOptions.php
@@ -38,7 +38,7 @@ use function preg_match_all;
final class FlatGeneratorOptions{
/**
- * @param int[] $structure
+ * @param int[] $structure
* @param mixed[] $extraOptions
* @phpstan-param array $structure
* @phpstan-param array|true> $extraOptions
diff --git a/src/world/generator/GeneratorManager.php b/src/world/generator/GeneratorManager.php
index e94d2336a..a00edd559 100644
--- a/src/world/generator/GeneratorManager.php
+++ b/src/world/generator/GeneratorManager.php
@@ -58,10 +58,10 @@ final class GeneratorManager{
}
/**
- * @param string $class Fully qualified name of class that extends \pocketmine\world\generator\Generator
- * @param string $name Alias for this generator type that can be written in configs
- * @param \Closure $presetValidator Callback to validate generator options for new worlds
- * @param bool $overwrite Whether to force overwriting any existing registered generator with the same name
+ * @param string $class Fully qualified name of class that extends \pocketmine\world\generator\Generator
+ * @param string $name Alias for this generator type that can be written in configs
+ * @param \Closure $presetValidator Callback to validate generator options for new worlds
+ * @param bool $overwrite Whether to force overwriting any existing registered generator with the same name
*
* @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator
*
diff --git a/src/world/light/SkyLightUpdate.php b/src/world/light/SkyLightUpdate.php
index d3416832c..bc0e1603c 100644
--- a/src/world/light/SkyLightUpdate.php
+++ b/src/world/light/SkyLightUpdate.php
@@ -208,8 +208,8 @@ class SkyLightUpdate extends LightUpdate{
/**
* Recalculates the heightmap for the block column at the specified X/Z chunk coordinates
*
- * @param int $x 0-15
- * @param int $z 0-15
+ * @param int $x 0-15
+ * @param int $z 0-15
* @param \SplFixedArray|bool[] $directSkyLightBlockers
* @phpstan-param \SplFixedArray $directSkyLightBlockers
*
diff --git a/src/world/particle/FloatingTextParticle.php b/src/world/particle/FloatingTextParticle.php
index 313e40d48..8797a7d5c 100644
--- a/src/world/particle/FloatingTextParticle.php
+++ b/src/world/particle/FloatingTextParticle.php
@@ -36,6 +36,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\IntMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty;
+use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData;
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
class FloatingTextParticle implements Particle{
@@ -115,6 +116,7 @@ class FloatingTextParticle implements Particle{
0,
[],
$actorMetadata,
+ new PropertySyncData([], []),
[]
);
}
diff --git a/src/world/sound/ItemFrameAddItemSound.php b/src/world/sound/ItemFrameAddItemSound.php
new file mode 100644
index 000000000..22e35aead
--- /dev/null
+++ b/src/world/sound/ItemFrameAddItemSound.php
@@ -0,0 +1,35 @@
+\\|string\\|false and '/'\\|'\\\\\\\\' results in an error\\.$#"
- count: 2
- path: ../../../src/PocketMine.php
-
-
message: "#^Do\\-while loop condition is always false\\.$#"
count: 1
path: ../../../src/PocketMine.php
- -
- message: "#^Cannot cast mixed to string\\.$#"
- count: 1
- path: ../../../src/Server.php
-
-
message: "#^Parameter \\#1 \\$array of static method pocketmine\\\\plugin\\\\PluginGraylist\\:\\:fromArray\\(\\) expects array, mixed given\\.$#"
count: 1
diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon
index e50532cfd..3ee56e8be 100644
--- a/tests/phpstan/configs/phpstan-bugs.neon
+++ b/tests/phpstan/configs/phpstan-bugs.neon
@@ -46,7 +46,7 @@ parameters:
path: ../../../src/plugin/PluginManager.php
-
- message: "#^Offset \\(int\\|string\\) on non\\-empty\\-array\\ in isset\\(\\) always exists and is not nullable\\.$#"
+ message: "#^Offset \\(int\\|string\\) on array\\ in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: ../../../src/plugin/PluginManager.php
@@ -65,11 +65,6 @@ parameters:
count: 1
path: ../../../src/thread/Worker.php
- -
- message: "#^Offset \\(int\\|string\\) on non\\-empty\\-array\\ in isset\\(\\) always exists and is not nullable\\.$#"
- count: 1
- path: ../../../src/world/WorldManager.php
-
-
message: "#^Call to function is_resource\\(\\) with resource will always evaluate to true\\.$#"
count: 2
diff --git a/tests/phpunit/network/mcpe/handler/StupidJsonDecodeTest.php b/tests/phpunit/network/mcpe/handler/StupidJsonDecodeTest.php
index 99890b402..6a48bd1ba 100644
--- a/tests/phpunit/network/mcpe/handler/StupidJsonDecodeTest.php
+++ b/tests/phpunit/network/mcpe/handler/StupidJsonDecodeTest.php
@@ -57,7 +57,7 @@ class StupidJsonDecodeTest extends TestCase{
/**
* @dataProvider stupidJsonDecodeProvider
*
- * @param mixed $expect
+ * @param mixed $expect
*
* @throws \ReflectionException
*/
diff --git a/tools/compact-regions.php b/tools/compact-regions.php
index beb764060..6959c82fe 100644
--- a/tools/compact-regions.php
+++ b/tools/compact-regions.php
@@ -43,6 +43,10 @@ use function rename;
use function round;
use function scandir;
use function unlink;
+use const PATHINFO_EXTENSION;
+use const PHP_BINARY;
+use const SCANDIR_SORT_NONE;
+use const SORT_NUMERIC;
require dirname(__DIR__) . '/vendor/autoload.php';
diff --git a/tools/decode-crashdump.php b/tools/decode-crashdump.php
new file mode 100644
index 000000000..26a5d144f
--- /dev/null
+++ b/tools/decode-crashdump.php
@@ -0,0 +1,89 @@
+ $line){
+ if(trim($line) === "===BEGIN CRASH DUMP==="){
+ $start = $num + 1;
+ break;
+ }
+}
+
+if($start === -1){
+ fwrite(STDERR, "Crashdump encoded data not found in target file" . PHP_EOL);
+ exit(1);
+}
+
+$data = array_slice($lines, $start);
+array_pop($data);
+
+$zlibData = base64_decode(implode("", $data), true);
+if($zlibData === false){
+ fwrite(STDERR, "Invalid encoded data in crashdump" . PHP_EOL);
+ exit(1);
+}
+$decoded = zlib_decode($zlibData);
+if($decoded === false){
+ fwrite(STDERR, "Invalid compressed data in crashdump" . PHP_EOL);
+ exit(1);
+}
+
+file_put_contents($output, json_encode(json_decode($decoded), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+echo "Wrote decoded crashdump to " . realpath($output) . PHP_EOL;
diff --git a/tools/ping-server.php b/tools/ping-server.php
index 83a997bbd..1784d0988 100644
--- a/tools/ping-server.php
+++ b/tools/ping-server.php
@@ -49,9 +49,13 @@ use function socket_strerror;
use function strlen;
use function time;
use function trim;
+use const AF_INET;
use const MSG_DONTROUTE;
use const PHP_BINARY;
use const PHP_INT_MAX;
+use const SOCK_DGRAM;
+use const SOL_UDP;
+use const STDIN;
require_once 'vendor/autoload.php';