Merge branch 'next-minor' into next-major

This commit is contained in:
Dylan K. Taylor 2023-01-12 22:08:25 +00:00
commit ecd8f151f1
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
23 changed files with 257 additions and 163 deletions

View File

@ -50,3 +50,23 @@ Released 6th January 2023.
## Fixes ## Fixes
- Removed a workaround for an old client bug in custom form responses. The code contained a denial-of-service vulnerability. - Removed a workaround for an old client bug in custom form responses. The code contained a denial-of-service vulnerability.
# 4.12.6
Released 7th January 2023.
## Changes
- Added a new security measure to `NetworkSession` to detect and ban players who flood the server with packets.
# 4.12.7
Released 8th January 2023.
## Fixes
- Fixed players getting kicked when the server lags for too long.
- Fixed players getting kicked when a debugging session is active and a breakpoint is hit.
# 4.12.8
Released 9th January 2023.
## Fixes
- Fixed players getting kicked during PvP.
- Fixed players randomly getting kicked on Windows (improper rate limit handling wrt. 15ms timer resolution).

View File

@ -37,13 +37,13 @@
"pocketmine/bedrock-block-upgrade-schema": "dev-master@dev", "pocketmine/bedrock-block-upgrade-schema": "dev-master@dev",
"pocketmine/bedrock-data": "dev-modern-world-support@dev", "pocketmine/bedrock-data": "dev-modern-world-support@dev",
"pocketmine/bedrock-item-upgrade-schema": "dev-master", "pocketmine/bedrock-item-upgrade-schema": "dev-master",
"pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50", "pocketmine/bedrock-protocol": "~18.0.0+bedrock-1.19.50",
"pocketmine/binaryutils": "^0.2.1", "pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2", "pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0", "pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0", "pocketmine/color": "^0.3.0",
"pocketmine/errorhandler": "^0.6.0", "pocketmine/errorhandler": "^0.6.0",
"pocketmine/locale-data": "~2.15.0", "pocketmine/locale-data": "~2.16.0",
"pocketmine/log": "^0.4.0", "pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0", "pocketmine/log-pthreads": "^0.4.0",
"pocketmine/math": "^0.4.0", "pocketmine/math": "^0.4.0",
@ -55,7 +55,7 @@
"symfony/filesystem": "^5.4" "symfony/filesystem": "^5.4"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.9.7", "phpstan/phpstan": "1.9.11",
"phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0", "phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2" "phpunit/phpunit": "^9.2"

173
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "bf576fbfb1454aecaa4313129d2deadc", "content-hash": "ecbdb7bda8ed93cb0499bfe486a68ff6",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -330,16 +330,16 @@
}, },
{ {
"name": "pocketmine/bedrock-protocol", "name": "pocketmine/bedrock-protocol",
"version": "17.1.0+bedrock-1.19.50", "version": "18.0.0+bedrock-1.19.50",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git", "url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "c572706cf5e3202718dd35a35dd30fe08cd671de" "reference": "b558ec883bd967dd3339f513cba62d2fbcd63349"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c572706cf5e3202718dd35a35dd30fe08cd671de", "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/b558ec883bd967dd3339f513cba62d2fbcd63349",
"reference": "c572706cf5e3202718dd35a35dd30fe08cd671de", "reference": "b558ec883bd967dd3339f513cba62d2fbcd63349",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -347,13 +347,13 @@
"netresearch/jsonmapper": "^4.0", "netresearch/jsonmapper": "^4.0",
"php": "^8.0", "php": "^8.0",
"pocketmine/binaryutils": "^0.2.0", "pocketmine/binaryutils": "^0.2.0",
"pocketmine/color": "^0.2.0", "pocketmine/color": "^0.2.0 || ^0.3.0",
"pocketmine/math": "^0.3.0 || ^0.4.0", "pocketmine/math": "^0.3.0 || ^0.4.0",
"pocketmine/nbt": "^0.3.0", "pocketmine/nbt": "^0.3.0",
"ramsey/uuid": "^4.1" "ramsey/uuid": "^4.1"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.9.3", "phpstan/phpstan": "1.9.4",
"phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.5"
@ -371,9 +371,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": { "support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues", "issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/17.1.0+bedrock-1.19.50" "source": "https://github.com/pmmp/BedrockProtocol/tree/18.0.0+bedrock-1.19.50"
}, },
"time": "2022-12-15T20:34:49+00:00" "time": "2023-01-06T21:47:35+00:00"
}, },
{ {
"name": "pocketmine/binaryutils", "name": "pocketmine/binaryutils",
@ -514,24 +514,24 @@
}, },
{ {
"name": "pocketmine/color", "name": "pocketmine/color",
"version": "0.2.0", "version": "0.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/Color.git", "url": "https://github.com/pmmp/Color.git",
"reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2" "reference": "8cb346d0a21ad3287cc8d7175e4b643416607249"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/Color/zipball/09be6ea6d76f2e33d6813c39d29c22c46c17e1d2", "url": "https://api.github.com/repos/pmmp/Color/zipball/8cb346d0a21ad3287cc8d7175e4b643416607249",
"reference": "09be6ea6d76f2e33d6813c39d29c22c46c17e1d2", "reference": "8cb346d0a21ad3287cc8d7175e4b643416607249",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0" "php": "^8.0"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "0.12.59", "phpstan/phpstan": "1.9.4",
"phpstan/phpstan-strict-rules": "^0.12.2" "phpstan/phpstan-strict-rules": "^1.2.0"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -546,9 +546,9 @@
"description": "Color handling library used by PocketMine-MP and related projects", "description": "Color handling library used by PocketMine-MP and related projects",
"support": { "support": {
"issues": "https://github.com/pmmp/Color/issues", "issues": "https://github.com/pmmp/Color/issues",
"source": "https://github.com/pmmp/Color/tree/0.2.0" "source": "https://github.com/pmmp/Color/tree/0.3.0"
}, },
"time": "2020-12-11T01:24:32+00:00" "time": "2022-12-18T19:49:21+00:00"
}, },
{ {
"name": "pocketmine/errorhandler", "name": "pocketmine/errorhandler",
@ -591,16 +591,16 @@
}, },
{ {
"name": "pocketmine/locale-data", "name": "pocketmine/locale-data",
"version": "2.15.0", "version": "2.16.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/Language.git", "url": "https://github.com/pmmp/Language.git",
"reference": "87feaefdd8364730a2350e58fa274b1b493a9d3f" "reference": "b3bf9029c112414fdb7cd9de778df191565d3038"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/87feaefdd8364730a2350e58fa274b1b493a9d3f", "url": "https://api.github.com/repos/pmmp/Language/zipball/b3bf9029c112414fdb7cd9de778df191565d3038",
"reference": "87feaefdd8364730a2350e58fa274b1b493a9d3f", "reference": "b3bf9029c112414fdb7cd9de778df191565d3038",
"shasum": "" "shasum": ""
}, },
"type": "library", "type": "library",
@ -608,9 +608,9 @@
"description": "Language resources used by PocketMine-MP", "description": "Language resources used by PocketMine-MP",
"support": { "support": {
"issues": "https://github.com/pmmp/Language/issues", "issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.15.0" "source": "https://github.com/pmmp/Language/tree/2.16.0"
}, },
"time": "2022-12-27T19:59:39+00:00" "time": "2023-01-04T20:27:52+00:00"
}, },
{ {
"name": "pocketmine/log", "name": "pocketmine/log",
@ -906,42 +906,53 @@
}, },
{ {
"name": "ramsey/collection", "name": "ramsey/collection",
"version": "1.2.2", "version": "1.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/collection.git", "url": "https://github.com/ramsey/collection.git",
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", "url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4",
"reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.3 || ^8", "php": "^7.4 || ^8.0",
"symfony/polyfill-php81": "^1.23" "symfony/polyfill-php81": "^1.23"
}, },
"require-dev": { "require-dev": {
"captainhook/captainhook": "^5.3", "captainhook/plugin-composer": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "ergebnis/composer-normalize": "^2.28.3",
"ergebnis/composer-normalize": "^2.6", "fakerphp/faker": "^1.21",
"fakerphp/faker": "^1.5", "hamcrest/hamcrest-php": "^2.0",
"hamcrest/hamcrest-php": "^2", "jangregor/phpstan-prophecy": "^1.0",
"jangregor/phpstan-prophecy": "^0.8", "mockery/mockery": "^1.5",
"mockery/mockery": "^1.3", "php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0", "phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1", "phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^0.12.32", "phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^0.12.5", "phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^0.12.11", "phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^8.5 || ^9", "phpunit/phpunit": "^9.5",
"psy/psysh": "^0.10.4", "psalm/plugin-mockery": "^1.1",
"slevomat/coding-standard": "^6.3", "psalm/plugin-phpunit": "^0.18.4",
"squizlabs/php_codesniffer": "^3.5", "ramsey/coding-standard": "^2.0.3",
"vimeo/psalm": "^4.4" "ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
}, },
"type": "library", "type": "library",
"extra": {
"captainhook": {
"force-install": true
},
"ramsey/conventional-commits": {
"configFile": "conventional-commits.json"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Ramsey\\Collection\\": "src/" "Ramsey\\Collection\\": "src/"
@ -969,7 +980,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/ramsey/collection/issues", "issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/1.2.2" "source": "https://github.com/ramsey/collection/tree/1.3.0"
}, },
"funding": [ "funding": [
{ {
@ -981,27 +992,27 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-10-10T03:01:02+00:00" "time": "2022-12-27T19:12:24+00:00"
}, },
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",
"version": "4.6.0", "version": "4.7.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/uuid.git", "url": "https://github.com/ramsey/uuid.git",
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f" "reference": "433b2014e3979047db08a17a205f410ba3869cf2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/ad63bc700e7d021039e30ce464eba384c4a1d40f", "url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2",
"reference": "ad63bc700e7d021039e30ce464eba384c4a1d40f", "reference": "433b2014e3979047db08a17a205f410ba3869cf2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10", "brick/math": "^0.8.8 || ^0.9 || ^0.10",
"ext-json": "*", "ext-json": "*",
"php": "^8.0", "php": "^8.0",
"ramsey/collection": "^1.0" "ramsey/collection": "^1.2 || ^2.0"
}, },
"replace": { "replace": {
"rhumsaa/uuid": "self.version" "rhumsaa/uuid": "self.version"
@ -1061,7 +1072,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/ramsey/uuid/issues", "issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.6.0" "source": "https://github.com/ramsey/uuid/tree/4.7.3"
}, },
"funding": [ "funding": [
{ {
@ -1073,7 +1084,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-05T23:03:38+00:00" "time": "2023-01-12T18:13:24+00:00"
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
@ -1470,30 +1481,30 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",
"version": "1.4.1", "version": "1.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/instantiator.git", "url": "https://github.com/doctrine/instantiator.git",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1 || ^8.0" "php": "^7.1 || ^8.0"
}, },
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^9", "doctrine/coding-standard": "^9 || ^11",
"ext-pdo": "*", "ext-pdo": "*",
"ext-phar": "*", "ext-phar": "*",
"phpbench/phpbench": "^0.16 || ^1", "phpbench/phpbench": "^0.16 || ^1",
"phpstan/phpstan": "^1.4", "phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.22" "vimeo/psalm": "^4.30 || ^5.4"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -1520,7 +1531,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/doctrine/instantiator/issues", "issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.4.1" "source": "https://github.com/doctrine/instantiator/tree/1.5.0"
}, },
"funding": [ "funding": [
{ {
@ -1536,7 +1547,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-03-03T08:28:38+00:00" "time": "2022-12-30T00:15:36+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@ -1766,16 +1777,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.9.7", "version": "1.9.11",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "0501435cd342eac7664bd62155b1ef907fc60b6f" "reference": "60f3d68481eef216199eae7a2603cd5fe124d464"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0501435cd342eac7664bd62155b1ef907fc60b6f", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/60f3d68481eef216199eae7a2603cd5fe124d464",
"reference": "0501435cd342eac7664bd62155b1ef907fc60b6f", "reference": "60f3d68481eef216199eae7a2603cd5fe124d464",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1805,7 +1816,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan/issues", "issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.9.7" "source": "https://github.com/phpstan/phpstan/tree/1.9.11"
}, },
"funding": [ "funding": [
{ {
@ -1821,7 +1832,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-01-04T21:59:57+00:00" "time": "2023-01-12T14:04:13+00:00"
}, },
{ {
"name": "phpstan/phpstan-phpunit", "name": "phpstan/phpstan-phpunit",
@ -1877,21 +1888,21 @@
}, },
{ {
"name": "phpstan/phpstan-strict-rules", "name": "phpstan/phpstan-strict-rules",
"version": "1.4.4", "version": "1.4.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git", "url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6" "reference": "361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d",
"reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", "reference": "361f75b06066f3fdaba87c1f57bdb1ffc28d6f1d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.8.6" "phpstan/phpstan": "^1.9.7"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^4.13.0", "nikic/php-parser": "^4.13.0",
@ -1919,22 +1930,22 @@
"description": "Extra strict and opinionated rules for PHPStan", "description": "Extra strict and opinionated rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.4" "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.5"
}, },
"time": "2022-09-21T11:38:17+00:00" "time": "2023-01-11T14:16:29+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "9.2.22", "version": "9.2.23",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "e4bf60d2220b4baaa0572986b5d69870226b06df" "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e4bf60d2220b4baaa0572986b5d69870226b06df", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c",
"reference": "e4bf60d2220b4baaa0572986b5d69870226b06df", "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1990,7 +2001,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.22" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23"
}, },
"funding": [ "funding": [
{ {
@ -1998,7 +2009,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2022-12-18T16:40:55+00:00" "time": "2022-12-28T12:41:10+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",

View File

@ -114,7 +114,14 @@ abstract class BaseBanner extends Transparent{
return SupportType::NONE(); return SupportType::NONE();
} }
private function canBeSupportedBy(Block $block) : bool{
return $block->isSolid();
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
return false;
}
if($item instanceof ItemBanner){ if($item instanceof ItemBanner){
$this->color = $item->getColor(); $this->color = $item->getColor();
$this->setPatterns($item->getPatterns()); $this->setPatterns($item->getPatterns());
@ -126,7 +133,7 @@ abstract class BaseBanner extends Transparent{
abstract protected function getSupportingFace() : int; abstract protected function getSupportingFace() : int;
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){ if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
$this->position->getWorld()->useBreakOn($this->position); $this->position->getWorld()->useBreakOn($this->position);
} }
} }

View File

@ -88,14 +88,13 @@ final class Bell extends Transparent{
return $this; return $this;
} }
private function canBeSupportedBy(Block $block) : bool{ private function canBeSupportedBy(Block $block, int $face) : bool{
//TODO: this isn't the actual logic, but it's the closest approximation we can support for now return !$block->getSupportType($face)->equals(SupportType::NONE());
return $block->isSolid();
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face === Facing::UP){ if($face === Facing::UP){
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()))){ if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->down()), Facing::UP)){
return false; return false;
} }
if($player !== null){ if($player !== null){
@ -103,18 +102,18 @@ final class Bell extends Transparent{
} }
$this->setAttachmentType(BellAttachmentType::FLOOR()); $this->setAttachmentType(BellAttachmentType::FLOOR());
}elseif($face === Facing::DOWN){ }elseif($face === Facing::DOWN){
if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()))){ if(!$this->canBeSupportedBy($tx->fetchBlock($this->position->up()), Facing::DOWN)){
return false; return false;
} }
$this->setAttachmentType(BellAttachmentType::CEILING()); $this->setAttachmentType(BellAttachmentType::CEILING());
}else{ }else{
$this->setFacing($face); $this->setFacing($face);
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))))){ if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide(Facing::opposite($face))), $face)){
$this->setAttachmentType(BellAttachmentType::ONE_WALL()); $this->setAttachmentType(BellAttachmentType::ONE_WALL());
}else{ }else{
return false; return false;
} }
if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)))){ if($this->canBeSupportedBy($tx->fetchBlock($this->position->getSide($face)), Facing::opposite($face))){
$this->setAttachmentType(BellAttachmentType::TWO_WALLS()); $this->setAttachmentType(BellAttachmentType::TWO_WALLS());
} }
} }
@ -123,10 +122,10 @@ final class Bell extends Transparent{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if( if(
($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP))) || ($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP), Facing::DOWN)) ||
($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN))) || ($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN), Facing::UP)) ||
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)))) || ($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)) ||
($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing))))) ($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing), Facing::opposite($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)))
){ ){
$this->position->getWorld()->useBreakOn($this->position); $this->position->getWorld()->useBreakOn($this->position);
} }
@ -135,21 +134,20 @@ final class Bell extends Transparent{
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player !== null){ if($player !== null){
$faceHit = Facing::opposite($player->getHorizontalFacing()); $faceHit = Facing::opposite($player->getHorizontalFacing());
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
$this->ring($faceHit);
}
if($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)){
$this->ring($faceHit);
}
if( if(
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) && $this->attachmentType->equals(BellAttachmentType::CEILING()) ||
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true)) ($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)) ||
(
($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) &&
($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true))
)
){ ){
$this->ring($faceHit); $this->ring($faceHit);
return true;
} }
} }
return true; return false;
} }
public function ring(int $faceHit) : void{ public function ring(int $faceHit) : void{

View File

@ -55,7 +55,7 @@ abstract class Button extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($blockClicked, $face)){ if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
$this->facing = $face; $this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
} }

View File

@ -70,7 +70,7 @@ class Ladder extends Transparent{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($blockClicked, $face) && Facing::axis($face) !== Axis::Y){ if($this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face) && Facing::axis($face) !== Axis::Y){
$this->facing = $face; $this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
} }

View File

@ -69,7 +69,7 @@ class Lever extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockClicked, $face)){ if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
return false; return false;
} }

View File

@ -45,7 +45,7 @@ abstract class PressurePlate extends Transparent{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($blockClicked)){ if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
} }
return false; return false;
@ -55,5 +55,11 @@ abstract class PressurePlate extends Transparent{
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE()); return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
} }
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
//TODO //TODO
} }

View File

@ -41,7 +41,7 @@ class Thin extends Transparent{
foreach(Facing::HORIZONTAL as $facing){ foreach(Facing::HORIZONTAL as $facing){
$side = $this->getSide($facing); $side = $this->getSide($facing);
if($side instanceof Thin || $side->isFullCube()){ if($side instanceof Thin || $side instanceof Wall || $side->isFullCube()){
$this->connections[$facing] = true; $this->connections[$facing] = true;
}else{ }else{
unset($this->connections[$facing]); unset($this->connections[$facing]);

View File

@ -59,7 +59,6 @@ class Torch extends Flowable{
} }
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
$below = $this->getSide(Facing::DOWN);
$face = Facing::opposite($this->facing); $face = Facing::opposite($this->facing);
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){ if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
@ -68,10 +67,7 @@ class Torch extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockClicked->canBeReplaced() && $this->canBeSupportedBy($blockClicked->getSide(Facing::DOWN), Facing::UP)){ if($face !== Facing::DOWN && $this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
$this->facing = Facing::UP;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($face !== Facing::DOWN && $this->canBeSupportedBy($blockClicked, $face)){
$this->facing = $face; $this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}else{ }else{

View File

@ -105,7 +105,7 @@ class Vine extends Flowable{
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockClicked->isFullCube() || Facing::axis($face) === Axis::Y){ if(!$blockReplace->getSide(Facing::opposite($face))->isFullCube() || Facing::axis($face) === Axis::Y){
return false; return false;
} }

View File

@ -104,7 +104,7 @@ class Wall extends Transparent{
foreach(Facing::HORIZONTAL as $facing){ foreach(Facing::HORIZONTAL as $facing){
$block = $this->getSide($facing); $block = $this->getSide($facing);
if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){ if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || ($block->isSolid() && !$block->isTransparent())){
if(!isset($this->connections[$facing])){ if(!isset($this->connections[$facing])){
$this->connections[$facing] = WallConnectionType::SHORT(); $this->connections[$facing] = WallConnectionType::SHORT();
$changed++; $changed++;

View File

@ -45,7 +45,7 @@ final class WallCoralFan extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$axis = Facing::axis($face); $axis = Facing::axis($face);
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockClicked, $face)){ if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
return false; return false;
} }
$this->facing = $face; $this->facing = $face;

View File

@ -39,19 +39,23 @@ class WaterLily extends Flowable{
return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)]; return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)];
} }
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
if($blockClicked instanceof Water){ return !$blockReplace instanceof Water && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
$up = $blockClicked->getSide(Facing::UP); }
if($up->canBeReplaced()){
return parent::place($tx, $item, $up, $blockClicked, $face, $clickVector, $player);
}
}
return false; private function canBeSupportedBy(Block $block) : bool{
return $block instanceof Water;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
} }
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(!($this->getSide(Facing::DOWN) instanceof Water)){ if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position); $this->position->getWorld()->useBreakOn($this->position);
} }
} }

View File

@ -236,11 +236,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$locatedSerializer = $this->serializers[$typeId][get_class($blockState)] ?? null; $locatedSerializer = $this->serializers[$typeId][get_class($blockState)] ?? null;
if($locatedSerializer === null){ if($locatedSerializer === null){
$parents = class_parents($blockState); foreach(class_parents($blockState) as $parent){
if($parents === false){
throw new AssumptionFailedError("A block class should always have at least one parent");
}
foreach($parents as $parent){
if(isset($this->serializers[$typeId][$parent])){ if(isset($this->serializers[$typeId][$parent])){
$locatedSerializer = $this->serializers[$typeId][$parent]; $locatedSerializer = $this->serializers[$typeId][$parent];
break; break;

View File

@ -103,13 +103,10 @@ final class ItemSerializer{
$locatedSerializer = $this->itemSerializers[$index][get_class($item)] ?? null; $locatedSerializer = $this->itemSerializers[$index][get_class($item)] ?? null;
if($locatedSerializer === null){ if($locatedSerializer === null){
$parents = class_parents($item); foreach(class_parents($item) as $parent){
if($parents !== false){ if(isset($this->itemSerializers[$index][$parent])){
foreach($parents as $parent){ $locatedSerializer = $this->itemSerializers[$index][$parent];
if(isset($this->itemSerializers[$index][$parent])){ break;
$locatedSerializer = $this->itemSerializers[$index][$parent];
break;
}
} }
} }
} }
@ -162,13 +159,10 @@ final class ItemSerializer{
$locatedSerializer = $this->blockItemSerializers[$index][get_class($block)] ?? null; $locatedSerializer = $this->blockItemSerializers[$index][get_class($block)] ?? null;
if($locatedSerializer === null){ if($locatedSerializer === null){
$parents = class_parents($block); foreach(class_parents($block) as $parent){
if($parents !== false){ if(isset($this->blockItemSerializers[$index][$parent])){
foreach($parents as $parent){ $locatedSerializer = $this->blockItemSerializers[$index][$parent];
if(isset($this->blockItemSerializers[$index][$parent])){ break;
$locatedSerializer = $this->blockItemSerializers[$index][$parent];
break;
}
} }
} }
} }

View File

@ -1176,6 +1176,10 @@ final class KnownTranslationFactory{
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION, []); return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION, []);
} }
public static function pocketmine_command_dumpmemory_description() : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_DUMPMEMORY_DESCRIPTION, []);
}
public static function pocketmine_command_effect_description() : Translatable{ public static function pocketmine_command_effect_description() : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_EFFECT_DESCRIPTION, []); return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_EFFECT_DESCRIPTION, []);
} }

View File

@ -260,6 +260,7 @@ final class KnownTranslationKeys{
public const POCKETMINE_COMMAND_DEFAULTGAMEMODE_DESCRIPTION = "pocketmine.command.defaultgamemode.description"; public const POCKETMINE_COMMAND_DEFAULTGAMEMODE_DESCRIPTION = "pocketmine.command.defaultgamemode.description";
public const POCKETMINE_COMMAND_DEOP_DESCRIPTION = "pocketmine.command.deop.description"; public const POCKETMINE_COMMAND_DEOP_DESCRIPTION = "pocketmine.command.deop.description";
public const POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION = "pocketmine.command.difficulty.description"; public const POCKETMINE_COMMAND_DIFFICULTY_DESCRIPTION = "pocketmine.command.difficulty.description";
public const POCKETMINE_COMMAND_DUMPMEMORY_DESCRIPTION = "pocketmine.command.dumpmemory.description";
public const POCKETMINE_COMMAND_EFFECT_DESCRIPTION = "pocketmine.command.effect.description"; public const POCKETMINE_COMMAND_EFFECT_DESCRIPTION = "pocketmine.command.effect.description";
public const POCKETMINE_COMMAND_ENCHANT_DESCRIPTION = "pocketmine.command.enchant.description"; public const POCKETMINE_COMMAND_ENCHANT_DESCRIPTION = "pocketmine.command.enchant.description";
public const POCKETMINE_COMMAND_ERROR_PERMISSION = "pocketmine.command.error.permission"; public const POCKETMINE_COMMAND_ERROR_PERMISSION = "pocketmine.command.error.permission";

View File

@ -142,8 +142,10 @@ class Language{
* @param (float|int|string|Translatable)[] $params * @param (float|int|string|Translatable)[] $params
*/ */
public function translateString(string $str, array $params = [], ?string $onlyPrefix = null) : string{ public function translateString(string $str, array $params = [], ?string $onlyPrefix = null) : string{
$baseText = $this->get($str); $baseText = ($onlyPrefix === null || str_starts_with($str, $onlyPrefix)) ? $this->internalGet($str) : null;
$baseText = $this->parseTranslation(($onlyPrefix === null || str_starts_with($str, $onlyPrefix)) ? $baseText : $str, $onlyPrefix); if($baseText === null){ //key not found, embedded inside format string, or doesn't match prefix
$baseText = $this->parseTranslation($str, $onlyPrefix);
}
foreach($params as $i => $p){ foreach($params as $i => $p){
$replacement = $p instanceof Translatable ? $this->translate($p) : (string) $p; $replacement = $p instanceof Translatable ? $this->translate($p) : (string) $p;
@ -155,7 +157,9 @@ class Language{
public function translate(Translatable $c) : string{ public function translate(Translatable $c) : string{
$baseText = $this->internalGet($c->getText()); $baseText = $this->internalGet($c->getText());
$baseText = $this->parseTranslation($baseText ?? $c->getText()); if($baseText === null){ //key not found or embedded inside format string
$baseText = $this->parseTranslation($c->getText());
}
foreach($c->getParameters() as $i => $p){ foreach($c->getParameters() as $i => $p){
$replacement = $p instanceof Translatable ? $this->translate($p) : $p; $replacement = $p instanceof Translatable ? $this->translate($p) : $p;
@ -181,6 +185,19 @@ class Language{
return $this->lang; return $this->lang;
} }
/**
* Replaces translation keys embedded inside a string with their raw values.
* Embedded translation keys must be prefixed by a "%" character.
*
* This is used to allow the "text" field of a Translatable to contain formatting (e.g. colour codes) and
* multiple embedded translation keys.
*
* Normal translations whose "text" is just a single translation key don't need to use this method, and can be
* processed via get() directly.
*
* @param string|null $onlyPrefix If non-null, only translation keys with this prefix will be replaced. This is
* used to allow a client to do its own translating of vanilla strings.
*/
protected function parseTranslation(string $text, ?string $onlyPrefix = null) : string{ protected function parseTranslation(string $text, ?string $onlyPrefix = null) : string{
$newString = ""; $newString = "";

View File

@ -128,10 +128,14 @@ use function array_values;
use function base64_encode; use function base64_encode;
use function bin2hex; use function bin2hex;
use function count; use function count;
use function function_exists;
use function get_class; use function get_class;
use function hrtime;
use function in_array; use function in_array;
use function intdiv;
use function json_encode; use function json_encode;
use function ksort; use function ksort;
use function min;
use function random_bytes; use function random_bytes;
use function strcasecmp; use function strcasecmp;
use function strlen; use function strlen;
@ -139,10 +143,25 @@ use function strtolower;
use function substr; use function substr;
use function time; use function time;
use function ucfirst; use function ucfirst;
use function xdebug_is_debugger_active;
use const JSON_THROW_ON_ERROR; use const JSON_THROW_ON_ERROR;
use const SORT_NUMERIC; use const SORT_NUMERIC;
class NetworkSession{ class NetworkSession{
private const INCOMING_PACKET_BATCH_PER_TICK = 2; //usually max 1 per tick, but transactions may arrive separately
private const INCOMING_PACKET_BATCH_MAX_BUDGET = 100; //enough to account for a 5-second lag spike
/**
* At most this many more packets can be received. If this reaches zero, any additional packets received will cause
* the player to be kicked from the server.
* This number is increased every tick up to a maximum limit.
*
* @see self::INCOMING_PACKET_BATCH_PER_TICK
* @see self::INCOMING_PACKET_BATCH_MAX_BUDGET
*/
private int $incomingPacketBatchBudget = self::INCOMING_PACKET_BATCH_MAX_BUDGET;
private int $lastPacketBudgetUpdateTimeNs;
private \PrefixedLogger $logger; private \PrefixedLogger $logger;
private ?Player $player = null; private ?Player $player = null;
private ?PlayerInfo $info = null; private ?PlayerInfo $info = null;
@ -200,6 +219,7 @@ class NetworkSession{
$this->disposeHooks = new ObjectSet(); $this->disposeHooks = new ObjectSet();
$this->connectTime = time(); $this->connectTime = time();
$this->lastPacketBudgetUpdateTimeNs = hrtime(true);
$this->setHandler(new SessionStartPacketHandler( $this->setHandler(new SessionStartPacketHandler(
$this, $this,
@ -341,6 +361,17 @@ class NetworkSession{
return; return;
} }
if($this->incomingPacketBatchBudget <= 0){
if(!function_exists('xdebug_is_debugger_active') || !xdebug_is_debugger_active()){
throw new PacketHandlingException("Receiving packets too fast");
}else{
//when a debugging session is active, the server may halt at any point for an indefinite length of time,
//in which time the client will continue to send packets
$this->incomingPacketBatchBudget = self::INCOMING_PACKET_BATCH_MAX_BUDGET;
}
}
$this->incomingPacketBatchBudget--;
if($this->cipher !== null){ if($this->cipher !== null){
Timings::$playerNetworkReceiveDecrypt->startTiming(); Timings::$playerNetworkReceiveDecrypt->startTiming();
try{ try{
@ -962,16 +993,13 @@ class NetworkSession{
public function onChatMessage(Translatable|string $message) : void{ public function onChatMessage(Translatable|string $message) : void{
if($message instanceof Translatable){ if($message instanceof Translatable){
//we can't send nested translations to the client, so make sure they are always pre-translated by the server
$language = $this->player->getLanguage(); $language = $this->player->getLanguage();
$parameters = array_map(fn(string|Translatable $p) => $p instanceof Translatable ? $language->translate($p) : $p, $message->getParameters());
if(!$this->server->isLanguageForced()){ if(!$this->server->isLanguageForced()){
foreach($parameters as $i => $p){ //we can't send nested translations to the client, so make sure they are always pre-translated by the server
$parameters[$i] = $language->translateString($p, [], "pocketmine."); $parameters = array_map(fn(string|Translatable $p) => $p instanceof Translatable ? $language->translate($p) : $p, $message->getParameters());
}
$this->sendDataPacket(TextPacket::translation($language->translateString($message->getText(), $parameters, "pocketmine."), $parameters)); $this->sendDataPacket(TextPacket::translation($language->translateString($message->getText(), $parameters, "pocketmine."), $parameters));
}else{ }else{
$this->sendDataPacket(TextPacket::raw($language->translateString($message->getText(), $parameters))); $this->sendDataPacket(TextPacket::raw($language->translate($message)));
} }
}else{ }else{
$this->sendDataPacket(TextPacket::raw($message)); $this->sendDataPacket(TextPacket::raw($message));
@ -1170,5 +1198,16 @@ class NetworkSession{
} }
$this->flushSendBuffer(); $this->flushSendBuffer();
$nowNs = hrtime(true);
$timeSinceLastUpdateNs = $nowNs - $this->lastPacketBudgetUpdateTimeNs;
if($timeSinceLastUpdateNs > 50_000_000){
$ticksSinceLastUpdate = intdiv($timeSinceLastUpdateNs, 50_000_000);
$this->incomingPacketBatchBudget = min(
$this->incomingPacketBatchBudget + (self::INCOMING_PACKET_BATCH_PER_TICK * 2 * $ticksSinceLastUpdate),
self::INCOMING_PACKET_BATCH_MAX_BUDGET
);
$this->lastPacketBudgetUpdateTimeNs = $nowNs;
}
} }
} }

View File

@ -52,17 +52,13 @@ class DeathPacketHandler extends PacketHandler{
/** @var string[] $parameters */ /** @var string[] $parameters */
$parameters = []; $parameters = [];
if($this->deathMessage instanceof Translatable){ if($this->deathMessage instanceof Translatable){
//we can't send nested translations to the client, so make sure they are always pre-translated by the server
$language = $this->player->getLanguage(); $language = $this->player->getLanguage();
$parameters = array_map(fn(string|Translatable $p) => $p instanceof Translatable ? $language->translate($p) : $p, $this->deathMessage->getParameters());
if(!$this->player->getServer()->isLanguageForced()){ if(!$this->player->getServer()->isLanguageForced()){
foreach($parameters as $i => $p){ //we can't send nested translations to the client, so make sure they are always pre-translated by the server
$parameters[$i] = $language->translateString($p, [], "pocketmine."); $parameters = array_map(fn(string|Translatable $p) => $p instanceof Translatable ? $language->translate($p) : $p, $this->deathMessage->getParameters());
}
$message = $language->translateString($this->deathMessage->getText(), $parameters, "pocketmine."); $message = $language->translateString($this->deathMessage->getText(), $parameters, "pocketmine.");
}else{ }else{
$message = $language->translateString($this->deathMessage->getText(), $parameters); $message = $language->translate($this->deathMessage);
$parameters = [];
} }
}else{ }else{
$message = $this->deathMessage; $message = $this->deathMessage;

View File

@ -61,6 +61,7 @@ use pocketmine\item\StringToItemParser;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\KnownTranslationFactory;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
@ -2026,6 +2027,10 @@ class World implements ChunkManager{
if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){
$blockReplace = $blockClicked; $blockReplace = $blockClicked;
//TODO: while this mimics the vanilla behaviour with replaceable blocks, we should really pass some other
//value like NULL and let place() deal with it. This will look like a bug to anyone who doesn't know about
//the vanilla behaviour.
$face = Facing::UP;
$hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z);
}elseif(!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)){ }elseif(!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)){
return false; return false;