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

View File

@ -114,7 +114,14 @@ abstract class BaseBanner extends Transparent{
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{
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
return false;
}
if($item instanceof ItemBanner){
$this->color = $item->getColor();
$this->setPatterns($item->getPatterns());
@ -126,7 +133,7 @@ abstract class BaseBanner extends Transparent{
abstract protected function getSupportingFace() : int;
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);
}
}

View File

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

View File

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

View File

@ -59,7 +59,6 @@ class Torch extends Flowable{
}
public function onNearbyBlockChange() : void{
$below = $this->getSide(Facing::DOWN);
$face = Facing::opposite($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{
if($blockClicked->canBeReplaced() && $this->canBeSupportedBy($blockClicked->getSide(Facing::DOWN), Facing::UP)){
$this->facing = Facing::UP;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($face !== Facing::DOWN && $this->canBeSupportedBy($blockClicked, $face)){
if($face !== Facing::DOWN && $this->canBeSupportedBy($blockReplace->getSide(Facing::opposite($face)), $face)){
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}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{
if(!$blockClicked->isFullCube() || Facing::axis($face) === Axis::Y){
if(!$blockReplace->getSide(Facing::opposite($face))->isFullCube() || Facing::axis($face) === Axis::Y){
return false;
}

View File

@ -104,7 +104,7 @@ class Wall extends Transparent{
foreach(Facing::HORIZONTAL as $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])){
$this->connections[$facing] = WallConnectionType::SHORT();
$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{
$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;
}
$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)];
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockClicked instanceof Water){
$up = $blockClicked->getSide(Facing::UP);
if($up->canBeReplaced()){
return parent::place($tx, $item, $up, $blockClicked, $face, $clickVector, $player);
}
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return !$blockReplace instanceof Water && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
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{
if(!($this->getSide(Facing::DOWN) instanceof Water)){
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$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;
if($locatedSerializer === null){
$parents = class_parents($blockState);
if($parents === false){
throw new AssumptionFailedError("A block class should always have at least one parent");
}
foreach($parents as $parent){
foreach(class_parents($blockState) as $parent){
if(isset($this->serializers[$typeId][$parent])){
$locatedSerializer = $this->serializers[$typeId][$parent];
break;

View File

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

View File

@ -1176,6 +1176,10 @@ final class KnownTranslationFactory{
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{
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_DEOP_DESCRIPTION = "pocketmine.command.deop.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_ENCHANT_DESCRIPTION = "pocketmine.command.enchant.description";
public const POCKETMINE_COMMAND_ERROR_PERMISSION = "pocketmine.command.error.permission";

View File

@ -142,8 +142,10 @@ class Language{
* @param (float|int|string|Translatable)[] $params
*/
public function translateString(string $str, array $params = [], ?string $onlyPrefix = null) : string{
$baseText = $this->get($str);
$baseText = $this->parseTranslation(($onlyPrefix === null || str_starts_with($str, $onlyPrefix)) ? $baseText : $str, $onlyPrefix);
$baseText = ($onlyPrefix === null || str_starts_with($str, $onlyPrefix)) ? $this->internalGet($str) : null;
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){
$replacement = $p instanceof Translatable ? $this->translate($p) : (string) $p;
@ -155,7 +157,9 @@ class Language{
public function translate(Translatable $c) : string{
$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){
$replacement = $p instanceof Translatable ? $this->translate($p) : $p;
@ -181,6 +185,19 @@ class Language{
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{
$newString = "";

View File

@ -128,10 +128,14 @@ use function array_values;
use function base64_encode;
use function bin2hex;
use function count;
use function function_exists;
use function get_class;
use function hrtime;
use function in_array;
use function intdiv;
use function json_encode;
use function ksort;
use function min;
use function random_bytes;
use function strcasecmp;
use function strlen;
@ -139,10 +143,25 @@ use function strtolower;
use function substr;
use function time;
use function ucfirst;
use function xdebug_is_debugger_active;
use const JSON_THROW_ON_ERROR;
use const SORT_NUMERIC;
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 ?Player $player = null;
private ?PlayerInfo $info = null;
@ -200,6 +219,7 @@ class NetworkSession{
$this->disposeHooks = new ObjectSet();
$this->connectTime = time();
$this->lastPacketBudgetUpdateTimeNs = hrtime(true);
$this->setHandler(new SessionStartPacketHandler(
$this,
@ -341,6 +361,17 @@ class NetworkSession{
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){
Timings::$playerNetworkReceiveDecrypt->startTiming();
try{
@ -962,16 +993,13 @@ class NetworkSession{
public function onChatMessage(Translatable|string $message) : void{
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();
$parameters = array_map(fn(string|Translatable $p) => $p instanceof Translatable ? $language->translate($p) : $p, $message->getParameters());
if(!$this->server->isLanguageForced()){
foreach($parameters as $i => $p){
$parameters[$i] = $language->translateString($p, [], "pocketmine.");
}
//we can't send nested translations to the client, so make sure they are always pre-translated by the server
$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));
}else{
$this->sendDataPacket(TextPacket::raw($language->translateString($message->getText(), $parameters)));
$this->sendDataPacket(TextPacket::raw($language->translate($message)));
}
}else{
$this->sendDataPacket(TextPacket::raw($message));
@ -1170,5 +1198,16 @@ class NetworkSession{
}
$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 */
$parameters = [];
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();
$parameters = array_map(fn(string|Translatable $p) => $p instanceof Translatable ? $language->translate($p) : $p, $this->deathMessage->getParameters());
if(!$this->player->getServer()->isLanguageForced()){
foreach($parameters as $i => $p){
$parameters[$i] = $language->translateString($p, [], "pocketmine.");
}
//we can't send nested translations to the client, so make sure they are always pre-translated by the server
$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.");
}else{
$message = $language->translateString($this->deathMessage->getText(), $parameters);
$parameters = [];
$message = $language->translate($this->deathMessage);
}
}else{
$message = $this->deathMessage;

View File

@ -61,6 +61,7 @@ use pocketmine\item\StringToItemParser;
use pocketmine\item\VanillaItems;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
@ -2026,6 +2027,10 @@ class World implements ChunkManager{
if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){
$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);
}elseif(!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)){
return false;