Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor 2023-08-08 17:48:23 +01:00
commit 5ec0e0f20b
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
41 changed files with 447 additions and 280 deletions

@ -1 +1 @@
Subproject commit 46604f2f6a07e3f68a82e4f4d7efdd45629b101e Subproject commit ed0bc4d2afafd00f9ee92823c6b1bd66789ce4f2

View File

@ -22,7 +22,7 @@
"ext-openssl": "*", "ext-openssl": "*",
"ext-pcre": "*", "ext-pcre": "*",
"ext-phar": "*", "ext-phar": "*",
"ext-pmmpthread": "^6.0.4", "ext-pmmpthread": "^6.0.7",
"ext-reflection": "*", "ext-reflection": "*",
"ext-simplexml": "*", "ext-simplexml": "*",
"ext-sockets": "*", "ext-sockets": "*",
@ -43,7 +43,7 @@
"pocketmine/errorhandler": "^0.6.0", "pocketmine/errorhandler": "^0.6.0",
"pocketmine/locale-data": "~2.19.0", "pocketmine/locale-data": "~2.19.0",
"pocketmine/log": "^0.4.0", "pocketmine/log": "^0.4.0",
"pocketmine/math": "^0.4.0", "pocketmine/math": "~1.0.0",
"pocketmine/nbt": "^0.3.2", "pocketmine/nbt": "^0.3.2",
"pocketmine/raklib": "^0.15.0", "pocketmine/raklib": "^0.15.0",
"pocketmine/raklib-ipc": "^0.2.0", "pocketmine/raklib-ipc": "^0.2.0",

62
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": "ccd20e7656bc05ec2acd8e28aad9fcf2", "content-hash": "2d51c1adf069760587b6d36f9c4a5db3",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -200,16 +200,16 @@
}, },
{ {
"name": "pocketmine/bedrock-protocol", "name": "pocketmine/bedrock-protocol",
"version": "23.0.2+bedrock-1.20.10", "version": "23.0.3+bedrock-1.20.10",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git", "url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd" "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e4157c7af3f91e1b08fe21be171eb73dad7029e9",
"reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -218,8 +218,8 @@
"php": "^8.0", "php": "^8.0",
"pocketmine/binaryutils": "^0.2.0", "pocketmine/binaryutils": "^0.2.0",
"pocketmine/color": "^0.2.0 || ^0.3.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 || ^1.0.0",
"pocketmine/nbt": "^0.3.0", "pocketmine/nbt": "^0.3.0 || ^1.0.0",
"ramsey/uuid": "^4.1" "ramsey/uuid": "^4.1"
}, },
"require-dev": { "require-dev": {
@ -241,9 +241,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/23.0.2+bedrock-1.20.10" "source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.3+bedrock-1.20.10"
}, },
"time": "2023-07-24T15:35:36+00:00" "time": "2023-08-03T15:30:52+00:00"
}, },
{ {
"name": "pocketmine/binaryutils", "name": "pocketmine/binaryutils",
@ -480,16 +480,16 @@
}, },
{ {
"name": "pocketmine/math", "name": "pocketmine/math",
"version": "0.4.3", "version": "1.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/Math.git", "url": "https://github.com/pmmp/Math.git",
"reference": "47a243d320b01c8099d65309967934c188111549" "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/Math/zipball/47a243d320b01c8099d65309967934c188111549", "url": "https://api.github.com/repos/pmmp/Math/zipball/dc132d93595b32e9f210d78b3c8d43c662a5edbf",
"reference": "47a243d320b01c8099d65309967934c188111549", "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -498,7 +498,7 @@
}, },
"require-dev": { "require-dev": {
"phpstan/extension-installer": "^1.0", "phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "1.8.2", "phpstan/phpstan": "~1.10.3",
"phpstan/phpstan-strict-rules": "^1.0", "phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^8.5 || ^9.5" "phpunit/phpunit": "^8.5 || ^9.5"
}, },
@ -515,9 +515,9 @@
"description": "PHP library containing math related code used in PocketMine-MP", "description": "PHP library containing math related code used in PocketMine-MP",
"support": { "support": {
"issues": "https://github.com/pmmp/Math/issues", "issues": "https://github.com/pmmp/Math/issues",
"source": "https://github.com/pmmp/Math/tree/0.4.3" "source": "https://github.com/pmmp/Math/tree/1.0.0"
}, },
"time": "2022-08-25T18:43:37+00:00" "time": "2023-08-03T12:56:33+00:00"
}, },
{ {
"name": "pocketmine/nbt", "name": "pocketmine/nbt",
@ -1541,16 +1541,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "10.1.2", "version": "10.1.3",
"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": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be1fe461fdc917de2a29a452ccf2657d325b443d",
"reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1607,7 +1607,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.3"
}, },
"funding": [ "funding": [
{ {
@ -1615,7 +1615,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-05-22T09:04:27+00:00" "time": "2023-07-26T13:45:28+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -1861,16 +1861,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "10.2.6", "version": "10.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd" "reference": "d442ce7c4104d5683c12e67e4dcb5058159e9804"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c17815c129f133f3019cc18e8d0c8622e6d9bcd", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d442ce7c4104d5683c12e67e4dcb5058159e9804",
"reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd", "reference": "d442ce7c4104d5683c12e67e4dcb5058159e9804",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1895,7 +1895,7 @@
"sebastian/diff": "^5.0", "sebastian/diff": "^5.0",
"sebastian/environment": "^6.0", "sebastian/environment": "^6.0",
"sebastian/exporter": "^5.0", "sebastian/exporter": "^5.0",
"sebastian/global-state": "^6.0", "sebastian/global-state": "^6.0.1",
"sebastian/object-enumerator": "^5.0", "sebastian/object-enumerator": "^5.0",
"sebastian/recursion-context": "^5.0", "sebastian/recursion-context": "^5.0",
"sebastian/type": "^4.0", "sebastian/type": "^4.0",
@ -1910,7 +1910,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "10.2-dev" "dev-main": "10.3-dev"
} }
}, },
"autoload": { "autoload": {
@ -1942,7 +1942,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.6" "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.1"
}, },
"funding": [ "funding": [
{ {
@ -1958,7 +1958,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-07-17T12:08:28+00:00" "time": "2023-08-04T06:48:08+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@ -2945,7 +2945,7 @@
"ext-openssl": "*", "ext-openssl": "*",
"ext-pcre": "*", "ext-pcre": "*",
"ext-phar": "*", "ext-phar": "*",
"ext-pmmpthread": "^6.0.4", "ext-pmmpthread": "^6.0.7",
"ext-reflection": "*", "ext-reflection": "*",
"ext-simplexml": "*", "ext-simplexml": "*",
"ext-sockets": "*", "ext-sockets": "*",

View File

@ -11,6 +11,7 @@ includes:
rules: rules:
- pocketmine\phpstan\rules\DisallowEnumComparisonRule - pocketmine\phpstan\rules\DisallowEnumComparisonRule
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule - pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule # - pocketmine\phpstan\rules\ThreadedSupportedTypesRule

View File

@ -120,8 +120,8 @@ namespace pocketmine {
} }
if(($pmmpthread_version = phpversion("pmmpthread")) !== false){ if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
if(version_compare($pmmpthread_version, "6.0.4") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){ if(version_compare($pmmpthread_version, "6.0.7") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
$messages[] = "pmmpthread ^6.0.4 is required, while you have $pmmpthread_version."; $messages[] = "pmmpthread ^6.0.7 is required, while you have $pmmpthread_version.";
} }
} }

View File

@ -23,10 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\event\block\BlockDeathEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use function mt_rand; use function mt_rand;
@ -46,11 +46,7 @@ abstract class BaseCoral extends Transparent{
public function onScheduledUpdate() : void{ public function onScheduledUpdate() : void{
if(!$this->dead && !$this->isCoveredWithWater()){ if(!$this->dead && !$this->isCoveredWithWater()){
$ev = new BlockDeathEvent($this, $this->setDead(true)); BlockEventHelper::die($this, (clone $this)->setDead(true));
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
} }
} }

View File

@ -23,10 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
@ -111,12 +111,7 @@ class Cactus extends Transparent{
} }
$b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); $b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z);
if($b->getTypeId() === BlockTypeIds::AIR){ if($b->getTypeId() === BlockTypeIds::AIR){
$ev = new BlockGrowEvent($b, VanillaBlocks::CACTUS()); BlockEventHelper::grow($b, VanillaBlocks::CACTUS(), null);
$ev->call();
if($ev->isCancelled()){
break;
}
$world->setBlock($b->position, $ev->getNewState());
}else{ }else{
break; break;
} }

View File

@ -23,10 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
@ -114,16 +114,12 @@ class CaveVines extends Flowable{
return true; return true;
} }
if($item instanceof Fertilizer){ if($item instanceof Fertilizer){
$ev = new BlockGrowEvent($this, (clone $this) $newState = (clone $this)
->setBerries(true) ->setBerries(true)
->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)) ->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this));
); if(BlockEventHelper::grow($this, $newState, $player)){
$ev->call();
if($ev->isCancelled()){
return false;
}
$item->pop(); $item->pop();
$this->position->getWorld()->setBlock($this->position, $ev->getNewState()); }
return true; return true;
} }
return false; return false;
@ -141,16 +137,10 @@ class CaveVines extends Flowable{
if($world->isInWorld($growthPos->getFloorX(), $growthPos->getFloorY(), $growthPos->getFloorZ())){ if($world->isInWorld($growthPos->getFloorX(), $growthPos->getFloorY(), $growthPos->getFloorZ())){
$block = $world->getBlock($growthPos); $block = $world->getBlock($growthPos);
if($block->getTypeId() === BlockTypeIds::AIR){ if($block->getTypeId() === BlockTypeIds::AIR){
$ev = new BlockGrowEvent($block, VanillaBlocks::CAVE_VINES() $newState = VanillaBlocks::CAVE_VINES()
->setAge($this->age + 1) ->setAge($this->age + 1)
->setBerries(mt_rand(1, 9) === 1) ->setBerries(mt_rand(1, 9) === 1);
); BlockEventHelper::grow($block, $newState, null);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($growthPos, $ev->getNewState());
}
} }
} }
} }

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
@ -123,12 +123,7 @@ class CocoaBlock extends Transparent{
if($this->age < self::MAX_AGE){ if($this->age < self::MAX_AGE){
$block = clone $this; $block = clone $this;
$block->age++; $block->age++;
$ev = new BlockGrowEvent($this, $block, $player); return BlockEventHelper::grow($this, $block, $player);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
return true;
}
} }
return false; return false;
} }

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\ColoredTrait;
use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\Fallable; use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\FallableTrait;
use pocketmine\event\block\BlockFormEvent;
use pocketmine\math\Facing; use pocketmine\math\Facing;
class ConcretePowder extends Opaque implements Fallable{ class ConcretePowder extends Opaque implements Fallable{
@ -43,11 +43,7 @@ class ConcretePowder extends Opaque implements Fallable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(($water = $this->getAdjacentWater()) !== null){ if(($water = $this->getAdjacentWater()) !== null){
$ev = new BlockFormEvent($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water); BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
}else{ }else{
$this->startFalling(); $this->startFalling();
} }

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\CoralTypeTrait;
use pocketmine\event\block\BlockDeathEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use function mt_rand; use function mt_rand;
@ -55,11 +55,7 @@ final class CoralBlock extends Opaque{
} }
} }
if(!$hasWater){ if(!$hasWater){
$ev = new BlockDeathEvent($this, $this->setDead(true)); BlockEventHelper::die($this, (clone $this)->setDead(true));
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $ev->getNewState());
}
} }
} }
} }

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Facing; use pocketmine\math\Facing;
@ -68,11 +68,7 @@ abstract class Crops extends Flowable{
if($block->age > self::MAX_AGE){ if($block->age > self::MAX_AGE){
$block->age = self::MAX_AGE; $block->age = self::MAX_AGE;
} }
if(BlockEventHelper::grow($this, $block, $player)){
$ev = new BlockGrowEvent($this, $block, $player);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
$item->pop(); $item->pop();
} }
@ -96,11 +92,7 @@ abstract class Crops extends Flowable{
if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){ if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){
$block = clone $this; $block = clone $this;
++$block->age; ++$block->age;
$ev = new BlockGrowEvent($this, $block); BlockEventHelper::grow($this, $block, null);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
} }
} }
} }

View File

@ -23,10 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockBurnEvent; use pocketmine\event\block\BlockBurnEvent;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use pocketmine\world\format\Chunk; use pocketmine\world\format\Chunk;
use pocketmine\world\World; use pocketmine\world\World;
@ -225,13 +225,6 @@ class Fire extends BaseFire{
} }
private function spreadBlock(Block $block, Block $newState) : bool{ private function spreadBlock(Block $block, Block $newState) : bool{
$ev = new BlockSpreadEvent($block, $this, $newState); return BlockEventHelper::spread($block, $newState, $this);
$ev->call();
if(!$ev->isCancelled()){
$block->position->getWorld()->setBlock($block->position, $ev->getNewState());
return true;
}
return false;
} }
} }

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockMeltEvent;
use function mt_rand; use function mt_rand;
class FrostedIce extends Ice{ class FrostedIce extends Ice{
@ -97,11 +97,7 @@ class FrostedIce extends Ice{
private function tryMelt() : bool{ private function tryMelt() : bool{
$world = $this->position->getWorld(); $world = $this->position->getWorld();
if($this->age >= self::MAX_AGE){ if($this->age >= self::MAX_AGE){
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); BlockEventHelper::melt($this, VanillaBlocks::WATER());
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $ev->getNewState());
}
return true; return true;
} }

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
@ -166,14 +166,7 @@ class GlowLichen extends Transparent{
return false; return false;
} }
$ev = new BlockSpreadEvent($replacedBlock, $this, $replacementBlock); return BlockEventHelper::spread($replacedBlock, $replacementBlock, $this);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($replacedBlock->getPosition(), $ev->getNewState());
return true;
}
return false;
} }
/** /**

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\DirtType; use pocketmine\block\utils\DirtType;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Hoe; use pocketmine\item\Hoe;
use pocketmine\item\Item; use pocketmine\item\Item;
@ -58,11 +58,7 @@ class Grass extends Opaque{
$lightAbove = $world->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z); $lightAbove = $world->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z);
if($lightAbove < 4 && $world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){ if($lightAbove < 4 && $world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){
//grass dies //grass dies
$ev = new BlockSpreadEvent($this, $this, VanillaBlocks::DIRT()); BlockEventHelper::spread($this, VanillaBlocks::DIRT(), $this);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $ev->getNewState(), false);
}
}elseif($lightAbove >= 9){ }elseif($lightAbove >= 9){
//try grass spread //try grass spread
for($i = 0; $i < 4; ++$i){ for($i = 0; $i < 4; ++$i){
@ -80,11 +76,7 @@ class Grass extends Opaque{
continue; continue;
} }
$ev = new BlockSpreadEvent($b, $this, VanillaBlocks::GRASS()); BlockEventHelper::spread($b, VanillaBlocks::GRASS(), $this);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($b->position, $ev->getNewState(), false);
}
} }
} }
} }

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\event\block\BlockMeltEvent; use pocketmine\block\utils\BlockEventHelper;
use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\player\Player; use pocketmine\player\Player;
@ -53,11 +53,7 @@ class Ice extends Transparent{
public function onRandomTick() : void{ public function onRandomTick() : void{
$world = $this->position->getWorld(); $world = $this->position->getWorld();
if($world->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){ if($world->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); BlockEventHelper::melt($this, VanillaBlocks::WATER());
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $ev->getNewState());
}
} }
} }

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\MinimumCostFlowCalculator; use pocketmine\block\utils\MinimumCostFlowCalculator;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\block\BlockFormEvent;
use pocketmine\event\block\BlockSpreadEvent; use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
@ -363,12 +363,8 @@ abstract class Liquid extends Transparent{
} }
protected function liquidCollide(Block $cause, Block $result) : bool{ protected function liquidCollide(Block $cause, Block $result) : bool{
$ev = new BlockFormEvent($this, $result, $cause); if(BlockEventHelper::form($this, $result, $cause)){
$ev->call(); $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8));
if(!$ev->isCancelled()){
$world = $this->position->getWorld();
$world->setBlock($this->position, $ev->getNewState());
$world->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8));
} }
return true; return true;
} }

View File

@ -113,14 +113,15 @@ class MobHead extends Flowable{
* @return AxisAlignedBB[] * @return AxisAlignedBB[]
*/ */
protected function recalculateCollisionBoxes() : array{ protected function recalculateCollisionBoxes() : array{
$collisionBox = AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5); $collisionBox = AxisAlignedBB::one()
return match($this->facing){ ->contract(0.25, 0, 0.25)
Facing::NORTH => [$collisionBox->offset(0, 0.25, 0.25)], ->trim(Facing::UP, 0.5);
Facing::SOUTH => [$collisionBox->offset(0, 0.25, -0.25)], if($this->facing !== Facing::UP){
Facing::WEST => [$collisionBox->offset(0.25, 0.25, 0)], $collisionBox = $collisionBox
Facing::EAST => [$collisionBox->offset(-0.25, 0.25, 0)], ->offsetTowards(Facing::opposite($this->facing), 0.25)
default => [$collisionBox] ->offsetTowards(Facing::UP, 0.25);
}; }
return [$collisionBox];
} }
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{

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\DirtType; use pocketmine\block\utils\DirtType;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use function mt_rand; use function mt_rand;
@ -54,11 +54,7 @@ class Mycelium extends Opaque{
$block = $world->getBlockAt($x, $y, $z); $block = $world->getBlockAt($x, $y, $z);
if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){ if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){
if($block->getSide(Facing::UP) instanceof Transparent){ if($block->getSide(Facing::UP) instanceof Transparent){
$ev = new BlockSpreadEvent($block, $this, VanillaBlocks::MYCELIUM()); BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($block->position, $ev->getNewState());
}
} }
} }
} }

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\FortuneDropHelper; use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
@ -76,11 +76,7 @@ class NetherWartPlant extends Flowable{
if($this->age < self::MAX_AGE && mt_rand(0, 10) === 0){ //Still growing if($this->age < self::MAX_AGE && mt_rand(0, 10) === 0){ //Still growing
$block = clone $this; $block = clone $this;
$block->age++; $block->age++;
$ev = new BlockGrowEvent($this, $block); BlockEventHelper::grow($this, $block, null);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
} }
} }

View File

@ -27,6 +27,10 @@ use pocketmine\item\Item;
class Podzol extends Opaque{ class Podzol extends Opaque{
public function isAffectedBySilkTouch() : bool{
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{ public function getDropsForCompatibleTool(Item $item) : array{
return [ return [
VanillaBlocks::DIRT()->asItem() VanillaBlocks::DIRT()->asItem()

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\Fallable; use pocketmine\block\utils\Fallable;
use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\FallableTrait;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockMeltEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\VanillaItems; use pocketmine\item\VanillaItems;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
@ -105,11 +105,7 @@ class SnowLayer extends Flowable implements Fallable{
public function onRandomTick() : void{ public function onRandomTick() : void{
$world = $this->position->getWorld(); $world = $this->position->getWorld();
if($world->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){ if($world->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){
$ev = new BlockMeltEvent($this, VanillaBlocks::AIR()); BlockEventHelper::melt($this, VanillaBlocks::AIR());
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $ev->getNewState());
}
} }
} }

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use function array_rand; use function array_rand;
@ -64,11 +64,7 @@ abstract class Stem extends Crops{
if($this->age < self::MAX_AGE){ if($this->age < self::MAX_AGE){
$block = clone $this; $block = clone $this;
++$block->age; ++$block->age;
$ev = new BlockGrowEvent($this, $block); BlockEventHelper::grow($this, $block, null);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $ev->getNewState());
}
}else{ }else{
$grow = $this->getPlant(); $grow = $this->getPlant();
foreach(Facing::HORIZONTAL as $side){ foreach(Facing::HORIZONTAL as $side){
@ -80,12 +76,7 @@ abstract class Stem extends Crops{
$facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]; $facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)];
$side = $this->getSide($facing); $side = $this->getSide($facing);
if($side->getTypeId() === BlockTypeIds::AIR && $side->getSide(Facing::DOWN)->hasTypeTag(BlockTypeTags::DIRT)){ if($side->getTypeId() === BlockTypeIds::AIR && $side->getSide(Facing::DOWN)->hasTypeTag(BlockTypeTags::DIRT)){
$ev = new BlockGrowEvent($side, $grow); BlockEventHelper::grow($side, $grow, null);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $this->setFacing($facing));
$world->setBlock($side->position, $ev->getNewState());
}
} }
} }
} }

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Facing; use pocketmine\math\Facing;
@ -60,13 +60,11 @@ class Sugarcane extends Flowable{
} }
$b = $world->getBlockAt($pos->x, $pos->y + $y, $pos->z); $b = $world->getBlockAt($pos->x, $pos->y + $y, $pos->z);
if($b->getTypeId() === BlockTypeIds::AIR){ if($b->getTypeId() === BlockTypeIds::AIR){
$ev = new BlockGrowEvent($b, VanillaBlocks::SUGARCANE(), $player); if(BlockEventHelper::grow($b, VanillaBlocks::SUGARCANE(), $player)){
$ev->call(); $grew = true;
if($ev->isCancelled()){ }else{
break; break;
} }
$world->setBlock($b->position, $ev->getNewState());
$grew = true;
}elseif(!$b->hasSameTypeId($this)){ }elseif(!$b->hasSameTypeId($this)){
break; break;
} }

View File

@ -23,11 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\FortuneDropHelper; use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\entity\Living; use pocketmine\entity\Living;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\item\Fertilizer; use pocketmine\item\Fertilizer;
use pocketmine\item\Item; use pocketmine\item\Item;
@ -87,15 +87,9 @@ class SweetBerryBush extends Flowable{
if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){ if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){
$block = clone $this; $block = clone $this;
$block->age++; $block->age++;
if(BlockEventHelper::grow($this, $block, $player)){
$ev = new BlockGrowEvent($this, $block, $player);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($this->position, $ev->getNewState());
$item->pop(); $item->pop();
} }
}elseif(($dropAmount = $this->getBerryDropAmount()) > 0){ }elseif(($dropAmount = $this->getBerryDropAmount()) > 0){
$world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES)); $world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES));
$world->dropItem($this->position, $this->asItem()->setCount($dropAmount)); $world->dropItem($this->position, $this->asItem()->setCount($dropAmount));
@ -133,11 +127,7 @@ class SweetBerryBush extends Flowable{
if($this->age < self::STAGE_MATURE && mt_rand(0, 2) === 1){ if($this->age < self::STAGE_MATURE && mt_rand(0, 2) === 1){
$block = clone $this; $block = clone $this;
++$block->age; ++$block->age;
$ev = new BlockGrowEvent($this, $block); BlockEventHelper::grow($this, $block, null);
$ev->call();
if(!$ev->isCancelled()){
$this->position->getWorld()->setBlock($this->position, $ev->getNewState());
}
} }
} }

View File

@ -0,0 +1,115 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\Block;
use pocketmine\event\block\BlockDeathEvent;
use pocketmine\event\block\BlockFormEvent;
use pocketmine\event\block\BlockGrowEvent;
use pocketmine\event\block\BlockMeltEvent;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\player\Player;
/**
* Helper class to call block changing events and apply the results to the world.
* TODO: try to further reduce the amount of code duplication here - while this is much better than before, it's still
* very repetitive.
*/
final class BlockEventHelper{
public static function grow(Block $oldState, Block $newState, ?Player $causingPlayer) : bool{
if(BlockGrowEvent::hasHandlers()){
$ev = new BlockGrowEvent($oldState, $newState, $causingPlayer);
$ev->call();
if($ev->isCancelled()){
return false;
}
$newState = $ev->getNewState();
}
$position = $oldState->getPosition();
$position->getWorld()->setBlock($position, $newState);
return true;
}
public static function spread(Block $oldState, Block $newState, Block $source) : bool{
if(BlockSpreadEvent::hasHandlers()){
$ev = new BlockSpreadEvent($oldState, $source, $newState);
$ev->call();
if($ev->isCancelled()){
return false;
}
$newState = $ev->getNewState();
}
$position = $oldState->getPosition();
$position->getWorld()->setBlock($position, $newState);
return true;
}
public static function form(Block $oldState, Block $newState, Block $cause) : bool{
if(BlockFormEvent::hasHandlers()){
$ev = new BlockFormEvent($oldState, $newState, $cause);
$ev->call();
if($ev->isCancelled()){
return false;
}
$newState = $ev->getNewState();
}
$position = $oldState->getPosition();
$position->getWorld()->setBlock($position, $newState);
return true;
}
public static function melt(Block $oldState, Block $newState) : bool{
if(BlockMeltEvent::hasHandlers()){
$ev = new BlockMeltEvent($oldState, $newState);
$ev->call();
if($ev->isCancelled()){
return false;
}
$newState = $ev->getNewState();
}
$position = $oldState->getPosition();
$position->getWorld()->setBlock($position, $newState);
return true;
}
public static function die(Block $oldState, Block $newState) : bool{
if(BlockDeathEvent::hasHandlers()){
$ev = new BlockDeathEvent($oldState, $newState);
$ev->call();
if($ev->isCancelled()){
return false;
}
$newState = $ev->getNewState();
}
$position = $oldState->getPosition();
$position->getWorld()->setBlock($position, $newState);
return true;
}
}

View File

@ -27,6 +27,7 @@ declare(strict_types=1);
namespace pocketmine\event; namespace pocketmine\event;
use pocketmine\timings\Timings; use pocketmine\timings\Timings;
use function count;
use function get_class; use function get_class;
abstract class Event{ abstract class Event{
@ -54,11 +55,11 @@ abstract class Event{
$timings = Timings::getEventTimings($this); $timings = Timings::getEventTimings($this);
$timings->startTiming(); $timings->startTiming();
$handlerList = HandlerListManager::global()->getListFor(get_class($this)); $handlers = HandlerListManager::global()->getHandlersFor(static::class);
++self::$eventCallDepth; ++self::$eventCallDepth;
try{ try{
foreach($handlerList->getListenerList() as $registration){ foreach($handlers as $registration){
$registration->callEvent($this); $registration->callEvent($this);
} }
}finally{ }finally{
@ -66,4 +67,14 @@ abstract class Event{
$timings->stopTiming(); $timings->stopTiming();
} }
} }
/**
* Returns whether the current class context has any registered global handlers.
* This can be used in hot code paths to avoid unnecessary event object creation.
*
* Usage: SomeEventClass::hasHandlers()
*/
public static function hasHandlers() : bool{
return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0;
}
} }

View File

@ -33,8 +33,6 @@ class HandlerList{
/** @var RegisteredListener[][] */ /** @var RegisteredListener[][] */
private array $handlerSlots = []; private array $handlerSlots = [];
private RegisteredListenerCache $handlerCache;
/** @var RegisteredListenerCache[] */ /** @var RegisteredListenerCache[] */
private array $affectedHandlerCaches = []; private array $affectedHandlerCaches = [];
@ -44,9 +42,9 @@ class HandlerList{
*/ */
public function __construct( public function __construct(
private string $class, private string $class,
private ?HandlerList $parentList private ?HandlerList $parentList,
private RegisteredListenerCache $handlerCache = new RegisteredListenerCache()
){ ){
$this->handlerCache = new RegisteredListenerCache();
for($list = $this; $list !== null; $list = $list->parentList){ for($list = $this; $list !== null; $list = $list->parentList){
$list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache; $list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache;
} }

View File

@ -36,6 +36,11 @@ class HandlerListManager{
/** @var HandlerList[] classname => HandlerList */ /** @var HandlerList[] classname => HandlerList */
private array $allLists = []; private array $allLists = [];
/**
* @var RegisteredListenerCache[] event class name => cache
* @phpstan-var array<class-string<Event>, RegisteredListenerCache>
*/
private array $handlerCaches = [];
/** /**
* Unregisters all the listeners * Unregisters all the listeners
@ -98,7 +103,25 @@ class HandlerListManager{
} }
$parent = self::resolveNearestHandleableParent($class); $parent = self::resolveNearestHandleableParent($class);
return $this->allLists[$event] = new HandlerList($event, $parent !== null ? $this->getListFor($parent->getName()) : null); $cache = new RegisteredListenerCache();
$this->handlerCaches[$event] = $cache;
return $this->allLists[$event] = new HandlerList(
$event,
parentList: $parent !== null ? $this->getListFor($parent->getName()) : null,
handlerCache: $cache
);
}
/**
* @phpstan-template TEvent of Event
* @phpstan-param class-string<TEvent> $event
*
* @return RegisteredListener[]
*/
public function getHandlersFor(string $event) : array{
$cache = $this->handlerCaches[$event] ?? null;
//getListFor() will populate the cache for the next call
return $cache?->list ?? $this->getListFor($event)->getListenerList();
} }
/** /**

View File

@ -406,11 +406,13 @@ class NetworkSession{
$timings->startTiming(); $timings->startTiming();
try{ try{
if(DataPacketDecodeEvent::hasHandlers()){
$ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer);
$ev->call(); $ev->call();
if($ev->isCancelled()){ if($ev->isCancelled()){
return; return;
} }
}
$decodeTimings = Timings::getDecodeDataPacketTimings($packet); $decodeTimings = Timings::getDecodeDataPacketTimings($packet);
$decodeTimings->startTiming(); $decodeTimings->startTiming();
@ -429,9 +431,13 @@ class NetworkSession{
$decodeTimings->stopTiming(); $decodeTimings->stopTiming();
} }
if(DataPacketReceiveEvent::hasHandlers()){
$ev = new DataPacketReceiveEvent($this, $packet); $ev = new DataPacketReceiveEvent($this, $packet);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if($ev->isCancelled()){
return;
}
}
$handlerTimings = Timings::getHandleDataPacketTimings($packet); $handlerTimings = Timings::getHandleDataPacketTimings($packet);
$handlerTimings->startTiming(); $handlerTimings->startTiming();
try{ try{
@ -441,7 +447,6 @@ class NetworkSession{
}finally{ }finally{
$handlerTimings->stopTiming(); $handlerTimings->stopTiming();
} }
}
}finally{ }finally{
$timings->stopTiming(); $timings->stopTiming();
} }
@ -459,12 +464,16 @@ class NetworkSession{
$timings = Timings::getSendDataPacketTimings($packet); $timings = Timings::getSendDataPacketTimings($packet);
$timings->startTiming(); $timings->startTiming();
try{ try{
if(DataPacketSendEvent::hasHandlers()){
$ev = new DataPacketSendEvent([$this], [$packet]); $ev = new DataPacketSendEvent([$this], [$packet]);
$ev->call(); $ev->call();
if($ev->isCancelled()){ if($ev->isCancelled()){
return false; return false;
} }
$packets = $ev->getPackets(); $packets = $ev->getPackets();
}else{
$packets = [$packet];
}
foreach($packets as $evPacket){ foreach($packets as $evPacket){
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket)); $this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket));

View File

@ -44,12 +44,14 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
public function broadcastPackets(array $recipients, array $packets) : void{ public function broadcastPackets(array $recipients, array $packets) : void{
//TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative //TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative
//implementation that doesn't fire events //implementation that doesn't fire events
if(DataPacketSendEvent::hasHandlers()){
$ev = new DataPacketSendEvent($recipients, $packets); $ev = new DataPacketSendEvent($recipients, $packets);
$ev->call(); $ev->call();
if($ev->isCancelled()){ if($ev->isCancelled()){
return; return;
} }
$packets = $ev->getPackets(); $packets = $ev->getPackets();
}
$compressors = []; $compressors = [];

View File

@ -85,6 +85,7 @@ class RakLibServer extends Thread{
gc_enable(); gc_enable();
ini_set("display_errors", '1'); ini_set("display_errors", '1');
ini_set("display_startup_errors", '1'); ini_set("display_startup_errors", '1');
\GlobalLogger::set($this->logger);
$socket = new ServerSocket($this->address->deserialize()); $socket = new ServerSocket($this->address->deserialize());
$manager = new Server( $manager = new Server(
@ -107,11 +108,6 @@ class RakLibServer extends Thread{
$manager->waitShutdown(); $manager->waitShutdown();
} }
protected function onUncaughtException(\Throwable $e) : void{
parent::onUncaughtException($e);
$this->logger->logException($e);
}
public function getThreadName() : string{ public function getThreadName() : string{
return "RakLib"; return "RakLib";
} }

View File

@ -81,9 +81,9 @@ class PermissionManager{
} }
public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{ public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{
foreach($this->permSubs as $permission => &$subs){ foreach($this->permSubs as $permission => $subs){
unset($subs[spl_object_id($permissible)]); unset($this->permSubs[$permission][spl_object_id($permissible)]);
if(count($subs) === 0){ if(count($this->permSubs[$permission]) === 0){
unset($this->permSubs[$permission]); unset($this->permSubs[$permission]);
} }
} }

View File

@ -154,6 +154,13 @@ class ResourcePackManager{
return $this->serverForceResources; return $this->serverForceResources;
} }
/**
* Sets whether players must accept resource packs in order to join.
*/
public function setResourcePacksRequired(bool $value) : void{
$this->serverForceResources = $value;
}
/** /**
* Returns an array of resource packs in use, sorted in order of priority. * Returns an array of resource packs in use, sorted in order of priority.
* @return ResourcePack[] * @return ResourcePack[]

View File

@ -69,11 +69,6 @@ class AsyncWorker extends Worker{
$this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier()); $this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier());
} }
protected function onUncaughtException(\Throwable $e) : void{
parent::onUncaughtException($e);
$this->logger->logException($e);
}
public function getLogger() : ThreadSafeLogger{ public function getLogger() : ThreadSafeLogger{
return $this->logger; return $this->logger;
} }

View File

@ -28,6 +28,7 @@ use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\Server; use pocketmine\Server;
use function error_get_last; use function error_get_last;
use function error_reporting; use function error_reporting;
use function implode;
use function register_shutdown_function; use function register_shutdown_function;
use function set_exception_handler; use function set_exception_handler;
@ -115,6 +116,7 @@ trait CommonThreadPartsTrait{
protected function onUncaughtException(\Throwable $e) : void{ protected function onUncaughtException(\Throwable $e) : void{
$this->synchronized(function() use ($e) : void{ $this->synchronized(function() use ($e) : void{
$this->crashInfo = ThreadCrashInfo::fromThrowable($e, $this->getThreadName()); $this->crashInfo = ThreadCrashInfo::fromThrowable($e, $this->getThreadName());
\GlobalLogger::get()->logException($e);
}); });
} }
@ -124,15 +126,26 @@ trait CommonThreadPartsTrait{
*/ */
protected function onShutdown() : void{ protected function onShutdown() : void{
$this->synchronized(function() : void{ $this->synchronized(function() : void{
if(!$this->isKilled && $this->crashInfo === null){ if($this->isTerminated() && $this->crashInfo === null){
$last = error_get_last(); $last = error_get_last();
if($last !== null){ if($last !== null){
//fatal error //fatal error
$this->crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName()); $crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName());
}else{ }else{
//probably misused exit() //probably misused exit()
$this->crashInfo = ThreadCrashInfo::fromThrowable(new \RuntimeException("Thread crashed without an error - perhaps exit() was called?"), $this->getThreadName()); $crashInfo = ThreadCrashInfo::fromThrowable(new \RuntimeException("Thread crashed without an error - perhaps exit() was called?"), $this->getThreadName());
} }
$this->crashInfo = $crashInfo;
$lines = [];
//mimic exception printed format
$lines[] = "Fatal error: " . $crashInfo->makePrettyMessage();
$lines[] = "--- Stack trace ---";
foreach($crashInfo->getTrace() as $frame){
$lines[] = " " . $frame->getPrintableFrame();
}
$lines[] = "--- End of fatal error information ---";
\GlobalLogger::get()->critical(implode("\n", $lines));
} }
}); });
} }

View File

@ -200,6 +200,11 @@ class World implements ChunkManager{
* @phpstan-var array<ChunkPosHash, array<ChunkBlockPosHash, Block>> * @phpstan-var array<ChunkPosHash, array<ChunkBlockPosHash, Block>>
*/ */
private array $blockCache = []; private array $blockCache = [];
/**
* @var AxisAlignedBB[][][] chunkHash => [relativeBlockHash => AxisAlignedBB[]]
* @phpstan-var array<ChunkPosHash, array<ChunkBlockPosHash, list<AxisAlignedBB>>>
*/
private array $blockCollisionBoxCache = [];
private int $sendTimeTicker = 0; private int $sendTimeTicker = 0;
@ -646,6 +651,7 @@ class World implements ChunkManager{
$this->provider->close(); $this->provider->close();
$this->blockCache = []; $this->blockCache = [];
$this->blockCollisionBoxCache = [];
$this->unloaded = true; $this->unloaded = true;
} }
@ -1117,6 +1123,7 @@ class World implements ChunkManager{
public function clearCache(bool $force = false) : void{ public function clearCache(bool $force = false) : void{
if($force){ if($force){
$this->blockCache = []; $this->blockCache = [];
$this->blockCollisionBoxCache = [];
}else{ }else{
$count = 0; $count = 0;
foreach($this->blockCache as $list){ foreach($this->blockCache as $list){
@ -1126,6 +1133,16 @@ class World implements ChunkManager{
break; break;
} }
} }
$count = 0;
foreach($this->blockCollisionBoxCache as $list){
$count += count($list);
if($count > 2048){
//TODO: Is this really the best logic?
$this->blockCollisionBoxCache = [];
break;
}
}
} }
} }
@ -1491,25 +1508,53 @@ class World implements ChunkManager{
return $collides; return $collides;
} }
/**
* Returns a list of all block AABBs which overlap the full block area at the given coordinates.
* This checks a padding of 1 block around the coordinates to account for oversized AABBs of blocks like fences.
* Larger AABBs (>= 2 blocks on any axis) are not accounted for.
*
* @return AxisAlignedBB[]
*/
private function getCollisionBoxesForCell(int $x, int $y, int $z) : array{
$block = $this->getBlockAt($x, $y, $z);
$boxes = $block->getCollisionBoxes();
$cellBB = AxisAlignedBB::one()->offset($x, $y, $z);
foreach(Facing::ALL as $facing){
$extraBoxes = $block->getSide($facing)->getCollisionBoxes();
foreach($extraBoxes as $extraBox){
if($extraBox->intersectsWith($cellBB)){
$boxes[] = $extraBox;
}
}
}
return $boxes;
}
/** /**
* @return AxisAlignedBB[] * @return AxisAlignedBB[]
* @phpstan-return list<AxisAlignedBB> * @phpstan-return list<AxisAlignedBB>
*/ */
public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{ public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{
$minX = (int) floor($bb->minX - 1); $minX = (int) floor($bb->minX);
$minY = (int) floor($bb->minY - 1); $minY = (int) floor($bb->minY);
$minZ = (int) floor($bb->minZ - 1); $minZ = (int) floor($bb->minZ);
$maxX = (int) floor($bb->maxX + 1); $maxX = (int) floor($bb->maxX);
$maxY = (int) floor($bb->maxY + 1); $maxY = (int) floor($bb->maxY);
$maxZ = (int) floor($bb->maxZ + 1); $maxZ = (int) floor($bb->maxZ);
$collides = []; $collides = [];
for($z = $minZ; $z <= $maxZ; ++$z){ for($z = $minZ; $z <= $maxZ; ++$z){
for($x = $minX; $x <= $maxX; ++$x){ for($x = $minX; $x <= $maxX; ++$x){
$chunkPosHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE);
for($y = $minY; $y <= $maxY; ++$y){ for($y = $minY; $y <= $maxY; ++$y){
$block = $this->getBlockAt($x, $y, $z); $relativeBlockHash = World::chunkBlockHash($x, $y, $z);
foreach($block->getCollisionBoxes() as $blockBB){
$boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getCollisionBoxesForCell($x, $y, $z);
foreach($boxes as $blockBB){
if($blockBB->intersectsWith($bb)){ if($blockBB->intersectsWith($bb)){
$collides[] = $blockBB; $collides[] = $blockBB;
} }
@ -1682,14 +1727,10 @@ class World implements ChunkManager{
*/ */
private function getHighestAdjacentLight(int $x, int $y, int $z, \Closure $lightGetter) : int{ private function getHighestAdjacentLight(int $x, int $y, int $z, \Closure $lightGetter) : int{
$max = 0; $max = 0;
foreach([ foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){
[$x + 1, $y, $z], $x1 = $x + $offsetX;
[$x - 1, $y, $z], $y1 = $y + $offsetY;
[$x, $y + 1, $z], $z1 = $z + $offsetZ;
[$x, $y - 1, $z],
[$x, $y, $z + 1],
[$x, $y, $z - 1]
] as [$x1, $y1, $z1]){
if( if(
!$this->isInWorld($x1, $y1, $z1) || !$this->isInWorld($x1, $y1, $z1) ||
($chunk = $this->getChunk($x1 >> Chunk::COORD_BIT_SIZE, $z1 >> Chunk::COORD_BIT_SIZE)) === null || ($chunk = $this->getChunk($x1 >> Chunk::COORD_BIT_SIZE, $z1 >> Chunk::COORD_BIT_SIZE)) === null ||
@ -1858,6 +1899,14 @@ class World implements ChunkManager{
$relativeBlockHash = World::chunkBlockHash($x, $y, $z); $relativeBlockHash = World::chunkBlockHash($x, $y, $z);
unset($this->blockCache[$chunkHash][$relativeBlockHash]); unset($this->blockCache[$chunkHash][$relativeBlockHash]);
unset($this->blockCollisionBoxCache[$chunkHash][$relativeBlockHash]);
//blocks like fences have collision boxes that reach into neighbouring blocks, so we need to invalidate the
//caches for those blocks as well
foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){
$sideChunkPosHash = World::chunkHash(($x + $offsetX) >> Chunk::COORD_BIT_SIZE, ($z + $offsetZ) >> Chunk::COORD_BIT_SIZE);
$sideChunkBlockHash = World::chunkBlockHash($x + $offsetX, $y + $offsetY, $z + $offsetZ);
unset($this->blockCollisionBoxCache[$sideChunkPosHash][$sideChunkBlockHash]);
}
if(!isset($this->changedBlocks[$chunkHash])){ if(!isset($this->changedBlocks[$chunkHash])){
$this->changedBlocks[$chunkHash] = []; $this->changedBlocks[$chunkHash] = [];
@ -2454,6 +2503,7 @@ class World implements ChunkManager{
$this->chunks[$chunkHash] = $chunk; $this->chunks[$chunkHash] = $chunk;
unset($this->blockCache[$chunkHash]); unset($this->blockCache[$chunkHash]);
unset($this->blockCollisionBoxCache[$chunkHash]);
unset($this->changedBlocks[$chunkHash]); unset($this->changedBlocks[$chunkHash]);
$chunk->setTerrainDirty(); $chunk->setTerrainDirty();
$this->markTickingChunkForRecheck($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking $this->markTickingChunkForRecheck($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking
@ -2733,6 +2783,7 @@ class World implements ChunkManager{
} }
$this->chunks[$chunkHash] = $chunk; $this->chunks[$chunkHash] = $chunk;
unset($this->blockCache[$chunkHash]); unset($this->blockCache[$chunkHash]);
unset($this->blockCollisionBoxCache[$chunkHash]);
$this->initChunk($x, $z, $chunkData); $this->initChunk($x, $z, $chunkData);
@ -2887,6 +2938,7 @@ class World implements ChunkManager{
unset($this->chunks[$chunkHash]); unset($this->chunks[$chunkHash]);
unset($this->blockCache[$chunkHash]); unset($this->blockCache[$chunkHash]);
unset($this->blockCollisionBoxCache[$chunkHash]);
unset($this->changedBlocks[$chunkHash]); unset($this->changedBlocks[$chunkHash]);
unset($this->registeredTickingChunks[$chunkHash]); unset($this->registeredTickingChunks[$chunkHash]);
$this->markTickingChunkForRecheck($x, $z); $this->markTickingChunkForRecheck($x, $z);
@ -2951,7 +3003,7 @@ class World implements ChunkManager{
* @throws WorldException if the terrain is not generated * @throws WorldException if the terrain is not generated
*/ */
public function getSafeSpawn(?Vector3 $spawn = null) : Position{ public function getSafeSpawn(?Vector3 $spawn = null) : Position{
if(!($spawn instanceof Vector3) || $spawn->y < 1){ if(!($spawn instanceof Vector3) || $spawn->y <= $this->minY){
$spawn = $this->getSpawnLocation(); $spawn = $this->getSpawnLocation();
} }

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\world\light; namespace pocketmine\world\light;
use pocketmine\math\Facing;
use pocketmine\world\format\LightArray; use pocketmine\world\format\LightArray;
use pocketmine\world\format\SubChunk; use pocketmine\world\format\SubChunk;
use pocketmine\world\utils\SubChunkExplorer; use pocketmine\world\utils\SubChunkExplorer;
@ -33,15 +34,6 @@ use function max;
//TODO: make light updates asynchronous //TODO: make light updates asynchronous
abstract class LightUpdate{ abstract class LightUpdate{
private const ADJACENTS = [
[ 1, 0, 0],
[-1, 0, 0],
[ 0, 1, 0],
[ 0, -1, 0],
[ 0, 0, 1],
[ 0, 0, -1]
];
public const BASE_LIGHT_FILTER = 1; public const BASE_LIGHT_FILTER = 1;
/** /**
@ -78,7 +70,7 @@ abstract class LightUpdate{
protected function getHighestAdjacentLight(int $x, int $y, int $z) : int{ protected function getHighestAdjacentLight(int $x, int $y, int $z) : int{
$adjacent = 0; $adjacent = 0;
foreach(self::ADJACENTS as [$ox, $oy, $oz]){ foreach(Facing::OFFSET as [$ox, $oy, $oz]){
if(($adjacent = max($adjacent, $this->getEffectiveLight($x + $ox, $y + $oy, $z + $oz))) === 15){ if(($adjacent = max($adjacent, $this->getEffectiveLight($x + $ox, $y + $oy, $z + $oz))) === 15){
break; break;
} }
@ -123,7 +115,7 @@ abstract class LightUpdate{
$touched++; $touched++;
[$x, $y, $z, $oldAdjacentLight] = $context->removalQueue->dequeue(); [$x, $y, $z, $oldAdjacentLight] = $context->removalQueue->dequeue();
foreach(self::ADJACENTS as [$ox, $oy, $oz]){ foreach(Facing::OFFSET as [$ox, $oy, $oz]){
$cx = $x + $ox; $cx = $x + $ox;
$cy = $y + $oy; $cy = $y + $oy;
$cz = $z + $oz; $cz = $z + $oz;
@ -163,7 +155,7 @@ abstract class LightUpdate{
continue; continue;
} }
foreach(self::ADJACENTS as [$ox, $oy, $oz]){ foreach(Facing::OFFSET as [$ox, $oy, $oz]){
$cx = $x + $ox; $cx = $x + $ox;
$cy = $y + $oy; $cy = $y + $oy;
$cz = $z + $oz; $cz = $z + $oz;

View File

@ -0,0 +1,53 @@
<?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\phpstan\rules;
use PhpParser\Node;
use PhpParser\Node\Stmt\Foreach_;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
/**
* @phpstan-implements Rule<Foreach_>
*/
final class DisallowForeachByReferenceRule implements Rule{
public function getNodeType() : string{
return Foreach_::class;
}
public function processNode(Node $node, Scope $scope) : array{
/** @var Foreach_ $node */
if($node->byRef){
return [
RuleErrorBuilder::message("Foreach by-reference is not allowed, because it has surprising behaviour.")
->tip("If the value variable is used outside of the foreach construct (e.g. in a second foreach), the iterable's contents will be unexpectedly altered.")
->build()
];
}
return [];
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\RuntimeBlockStateRegistry;
use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils;
require dirname(__DIR__, 3) . '/vendor/autoload.php'; require dirname(__DIR__, 3) . '/vendor/autoload.php';
@ -91,8 +92,9 @@ foreach($new as $stateId => $name){
$newTable[$name][] = $stateId; $newTable[$name][] = $stateId;
} }
ksort($newTable, SORT_STRING); ksort($newTable, SORT_STRING);
foreach($newTable as &$stateIds){ foreach(Utils::stringifyKeys($newTable) as $name => $stateIds){
sort($stateIds, SORT_NUMERIC); sort($stateIds, SORT_NUMERIC);
$newTable[$name] = $stateIds;
} }
file_put_contents(__DIR__ . '/block_factory_consistency_check.json', json_encode( file_put_contents(__DIR__ . '/block_factory_consistency_check.json', json_encode(