Merge branch 'stable' into item-stack-request

This commit is contained in:
Dylan K. Taylor 2023-01-03 19:53:25 +00:00
commit f51717323b
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
121 changed files with 1211 additions and 321 deletions

View File

@ -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

View File

@ -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 }}

View File

@ -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

View File

@ -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"

View File

@ -62,6 +62,11 @@
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Shell Script">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="neon">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />

View File

@ -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,

View File

@ -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");

@ -1 +1 @@
Subproject commit 14ed8eaadd921407c87be4964a8726b22427e80e
Subproject commit 6b605ed7c458fc8f95080ffe41a7f248a7160107

View File

@ -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[]
*/

53
changelogs/4.10.md Normal file
View File

@ -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.

92
changelogs/4.11-beta.md Normal file
View File

@ -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.

106
changelogs/4.11.md Normal file
View File

@ -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<string, string>`
### `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.

46
changelogs/4.12.md Normal file
View File

@ -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.

View File

@ -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"

201
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "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",

View File

@ -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

View File

@ -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<string, object> $objects
* @phpstan-param array<string, int> $refCounts
* @phpstan-param-out array<string, object> $objects
* @phpstan-param-out array<string, int> $refCounts
*
* @return mixed
*/
private static function continueDump($from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize){

View File

@ -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"])){

View File

@ -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{

View File

@ -44,7 +44,7 @@ final class ServerConfigGroup{
){}
/**
* @param mixed $defaultValue
* @param mixed $defaultValue
*
* @return mixed
*/

View File

@ -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

View File

@ -87,7 +87,7 @@ abstract class BaseBanner extends Transparent{
}
/**
* @param BannerPatternLayer[] $patterns
* @param BannerPatternLayer[] $patterns
*
* @phpstan-param list<BannerPatternLayer> $patterns
* @return $this

View File

@ -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());
}
}
}
}

View File

@ -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){

View File

@ -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.

View File

@ -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());
}
}
}
}

View File

@ -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);

View File

@ -84,6 +84,7 @@ class Leaves extends Transparent{
/**
* @param true[] $visited reference parameter
* @phpstan-param array<int, true> $visited
* @phpstan-param-out array<int, true> $visited
*/
protected function findLog(Vector3 $pos, array &$visited = [], int $distance = 0) : bool{
$index = World::blockHash($pos->x, $pos->y, $pos->z);

View File

@ -135,7 +135,7 @@ class Banner extends Spawnable{
}
/**
* @param BannerPatternLayer[] $patterns
* @param BannerPatternLayer[] $patterns
*
* @phpstan-param list<BannerPatternLayer> $patterns
*/

View File

@ -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;

View File

@ -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<string> $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;
}
/**

View File

@ -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));

View File

@ -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()));

View File

@ -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));

View File

@ -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()}%)");

View File

@ -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!)

View File

@ -46,15 +46,15 @@ class ShapedRecipe implements CraftingRecipe{
/**
* Constructs a ShapedRecipe instance.
*
* @param string[] $shape <br>
* 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 <br>
* 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 <br>
* 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.
*/

View File

@ -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){

View File

@ -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;

View File

@ -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 :(

View File

@ -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;
}

View File

@ -33,13 +33,13 @@ final class PotionTypeIdMap{
* @var PotionType[]
* @phpstan-var array<int, PotionType>
*/
private array $idToEnum;
private array $idToEnum = [];
/**
* @var int[]
* @phpstan-var array<int, int>
*/
private array $enumToId;
private array $enumToId = [];
private function __construct(){
$this->register(PotionTypeIds::WATER, PotionType::WATER());

View File

@ -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<int, MetadataProperty> $data
*/

View File

@ -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,

View File

@ -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(

View File

@ -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;

View File

@ -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());

View File

@ -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)));

View File

@ -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{

View File

@ -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{

View File

@ -31,6 +31,10 @@ class HandlerList{
/** @var RegisteredListener[][] */
private array $handlerSlots = [];
/**
* @phpstan-template TEvent of Event
* @phpstan-param class-string<TEvent> $class
*/
public function __construct(
private string $class,
private ?HandlerList $parentList

View File

@ -0,0 +1,32 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event\block;
/**
* Called when a block dies.
* This could be things like corals dying due to no water being nearby.
*/
class BlockDeathEvent extends BaseBlockChangeEvent{
}

View File

@ -23,11 +23,9 @@ declare(strict_types=1);
namespace pocketmine\event\block;
use pocketmine\event\Cancellable;
/**
* Called when plants or crops grow.
*/
class BlockGrowEvent extends BaseBlockChangeEvent implements Cancellable{
class BlockGrowEvent extends BaseBlockChangeEvent{
}

View File

@ -52,7 +52,7 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{
/**
* @param Block[] $blocks
* @param float $yield 0-100
* @param float $yield 0-100
*/
public function __construct(Entity $entity, Position $position, array $blocks, float $yield){
$this->entity = $entity;

View File

@ -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,

View File

@ -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;

View File

@ -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<Player>
*/
@ -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<Player> $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<Player>
*/
@ -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<Player> $class
*/

View File

@ -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
*/

View File

@ -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<int, Item> $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);
}

View File

@ -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<int, Item>
@ -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[]
*/

View File

@ -134,6 +134,8 @@ class InventoryTransaction{
/**
* @param Item[] $needItems
* @param Item[] $haveItems
* @phpstan-param-out Item[] $needItems
* @phpstan-param-out Item[] $haveItems
*
* @throws TransactionValidationException
*/

View File

@ -75,7 +75,7 @@ class Banner extends ItemBlockWallOrFloor{
}
/**
* @param BannerPatternLayer[] $patterns
* @param BannerPatternLayer[] $patterns
*
* @phpstan-param list<BannerPatternLayer> $patterns
*

View File

@ -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{

View File

@ -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{

View File

@ -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, []);
}

View File

@ -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";

View File

@ -173,6 +173,14 @@ class Language{
return $this->internalGet($id) ?? $id;
}
/**
* @return string[]
* @phpstan-return array<string, string>
*/
public function getAll() : array{
return $this->lang;
}
protected function parseTranslation(string $text, ?string $onlyPrefix = null) : string{
$newString = "";

View File

@ -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;

View File

@ -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();

View File

@ -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; }

View File

@ -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, [], [], []));

View File

@ -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;

View File

@ -118,7 +118,7 @@ final class ItemTranslator{
}
/**
* @param int[] $simpleMappings
* @param int[] $simpleMappings
* @param int[][] $complexMappings
* @phpstan-param array<string, int> $simpleMappings
* @phpstan-param array<string, array<int, int>> $complexMappings
@ -169,6 +169,7 @@ final class ItemTranslator{
}
/**
* @phpstan-param-out bool $isComplexMapping
* @return int[]
* @phpstan-return array{int, int}
* @throws TypeConversionException

View File

@ -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{

View File

@ -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;

View File

@ -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())){

View File

@ -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;
}
}

View File

@ -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{
/**

View File

@ -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;

View File

@ -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}");

View File

@ -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)){

View File

@ -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;

View File

@ -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"])){

View File

@ -319,6 +319,9 @@ class PluginManager{
/**
* @param string[][] $dependencyLists
* @param Plugin[] $loadedPlugins
*
* @phpstan-param array<string, list<string>> $dependencyLists
* @phpstan-param-out array<string, list<string>> $dependencyLists
*/
private function checkDepsForTriage(string $pluginName, string $dependencyType, array &$dependencyLists, array $loadedPlugins, PluginLoadTriage $triage) : void{
if(isset($dependencyLists[$pluginName])){

View File

@ -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

View File

@ -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;

View File

@ -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){

View File

@ -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){

View File

@ -26,7 +26,7 @@ namespace pocketmine\scheduler;
final class BulkCurlTaskOperation{
/**
* @param string[] $extraHeaders
* @param mixed[] $extraOpts
* @param mixed[] $extraOpts
* @phpstan-param list<string> $extraHeaders
* @phpstan-param array<int, mixed> $extraOpts
*/

View File

@ -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

View File

@ -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;

View File

@ -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<string, mixed> $default
*/
@ -493,9 +494,10 @@ class Config{
/**
* @param mixed[] $default
* @param mixed[] $data reference parameter
* @param mixed[] $data reference parameter
* @phpstan-param array<string, mixed> $default
* @phpstan-param array<string, mixed> $data
* @phpstan-param-out array<string, mixed> $data
*/
private function fillDefaults(array $default, &$data) : int{
$changed = 0;

View File

@ -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;

View File

@ -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

View File

@ -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){

View File

@ -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<string> $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<string, string> $args
* @phpstan-param list<string> $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.

View File

@ -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
*/

View File

@ -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));

View File

@ -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[]

View File

@ -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:

Some files were not shown because too many files have changed in this diff Show More