Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor 2023-09-20 19:15:08 +01:00
commit 05981d2669
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
55 changed files with 727 additions and 863 deletions

17
changelogs/4.24.md Normal file
View File

@ -0,0 +1,17 @@
# 4.24.0
Released 20th September 2023.
**For Minecraft: Bedrock Edition 1.20.30**
This is a support release for Minecraft: Bedrock Edition 1.20.30.
**Plugin compatibility:** Plugins for previous 4.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.30.
- Removed support for older versions.
- Updated 4.x obsoletion message.

20
changelogs/5.6.md Normal file
View File

@ -0,0 +1,20 @@
# 5.6.0
Released 20th September 2023.
**For Minecraft: Bedrock Edition 1.20.30**
This is a support release for Minecraft: Bedrock Edition 1.20.30.
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
Do not update plugin minimum API versions unless you need new features added in this release.
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
## General
- Added support for Minecraft: Bedrock Edition 1.20.30.
- Removed support for older versions.
## Fixes
- Fixed support conditions for hanging roots, cave vines and dead bushes.
- Fixed connection conditions for fences, glass panes, iron bars, and walls.

View File

@ -33,10 +33,10 @@
"composer-runtime-api": "^2.0",
"adhocore/json-comment": "~1.2.0",
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
"pocketmine/bedrock-block-upgrade-schema": "~3.1.0+bedrock-1.20.10",
"pocketmine/bedrock-data": "~2.4.0+bedrock-1.20.10",
"pocketmine/bedrock-item-upgrade-schema": "~1.4.0+bedrock-1.20.10",
"pocketmine/bedrock-protocol": "~23.0.2+bedrock-1.20.10",
"pocketmine/bedrock-block-upgrade-schema": "~3.2.0+bedrock-1.20.30",
"pocketmine/bedrock-data": "~2.5.0+bedrock-1.20.30",
"pocketmine/bedrock-item-upgrade-schema": "~1.5.0+bedrock-1.20.30",
"pocketmine/bedrock-protocol": "~24.0.0+bedrock-1.20.30",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/color": "^0.3.0",

95
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": "9237955fd97ba7c1697d80314fa9ad6f",
"content-hash": "5249e8bab61f885ce205fda0fc7e9aa9",
"packages": [
{
"name": "adhocore/json-comment",
@ -122,16 +122,16 @@
},
{
"name": "pocketmine/bedrock-block-upgrade-schema",
"version": "3.1.0",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "6d4ae416043337946a22fc31e8065ca2c21f472d"
"reference": "874e1c0c9b7b620744d08b59c78354fe9f028dfa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/6d4ae416043337946a22fc31e8065ca2c21f472d",
"reference": "6d4ae416043337946a22fc31e8065ca2c21f472d",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/874e1c0c9b7b620744d08b59c78354fe9f028dfa",
"reference": "874e1c0c9b7b620744d08b59c78354fe9f028dfa",
"shasum": ""
},
"type": "library",
@ -142,22 +142,22 @@
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.1.0"
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.2.0"
},
"time": "2023-07-12T12:05:36+00:00"
"time": "2023-09-20T17:03:43+00:00"
},
{
"name": "pocketmine/bedrock-data",
"version": "2.4.0+bedrock-1.20.10",
"version": "2.5.0+bedrock-1.20.30",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "f98bd1cae46d2920058acf3b23c0bedeac79f4ab"
"reference": "e920209393a8bf6cb15fb40c3f3149aaf8e1a2b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/f98bd1cae46d2920058acf3b23c0bedeac79f4ab",
"reference": "f98bd1cae46d2920058acf3b23c0bedeac79f4ab",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e920209393a8bf6cb15fb40c3f3149aaf8e1a2b9",
"reference": "e920209393a8bf6cb15fb40c3f3149aaf8e1a2b9",
"shasum": ""
},
"type": "library",
@ -168,22 +168,22 @@
"description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.10"
"source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.20.30"
},
"time": "2023-07-12T11:51:54+00:00"
"time": "2023-09-20T16:34:21+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
"version": "1.4.0",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git",
"reference": "60d199afe5e371fd189b21d685ec1fed6ba54230"
"reference": "3edc9ebbad9a4f2d9c8f53b3a5ba44d4a792ad93"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/60d199afe5e371fd189b21d685ec1fed6ba54230",
"reference": "60d199afe5e371fd189b21d685ec1fed6ba54230",
"url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/3edc9ebbad9a4f2d9c8f53b3a5ba44d4a792ad93",
"reference": "3edc9ebbad9a4f2d9c8f53b3a5ba44d4a792ad93",
"shasum": ""
},
"type": "library",
@ -194,32 +194,32 @@
"description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves",
"support": {
"issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.4.0"
"source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.5.0"
},
"time": "2023-07-12T12:08:37+00:00"
"time": "2023-09-01T19:58:57+00:00"
},
{
"name": "pocketmine/bedrock-protocol",
"version": "23.0.4+bedrock-1.20.10",
"version": "24.0.0+bedrock-1.20.30",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "ae0d8f4d49506674b7ff622f7c81ce241dc49adb"
"reference": "38a516274aa6641b0da38011af35a5587fc87895"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/ae0d8f4d49506674b7ff622f7c81ce241dc49adb",
"reference": "ae0d8f4d49506674b7ff622f7c81ce241dc49adb",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/38a516274aa6641b0da38011af35a5587fc87895",
"reference": "38a516274aa6641b0da38011af35a5587fc87895",
"shasum": ""
},
"require": {
"ext-json": "*",
"netresearch/jsonmapper": "^4.0",
"php": "^8.0",
"php": "^8.1",
"pocketmine/binaryutils": "^0.2.0",
"pocketmine/color": "^0.2.0 || ^0.3.0",
"pocketmine/math": "^0.3.0 || ^0.4.0 || ^1.0.0",
"pocketmine/nbt": "^0.3.0 || ^1.0.0",
"pocketmine/nbt": "^1.0.0",
"ramsey/uuid": "^4.1"
},
"require-dev": {
@ -241,9 +241,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/23.0.4+bedrock-1.20.10"
"source": "https://github.com/pmmp/BedrockProtocol/tree/24.0.0+bedrock-1.20.30"
},
"time": "2023-09-06T07:36:44+00:00"
"time": "2023-09-20T16:57:53+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -1541,16 +1541,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.4",
"version": "10.1.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "cd59bb34756a16ca8253ce9b2909039c227fff71"
"reference": "56f33548fe522c8d82da7ff3824b42829d324364"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cd59bb34756a16ca8253ce9b2909039c227fff71",
"reference": "cd59bb34756a16ca8253ce9b2909039c227fff71",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/56f33548fe522c8d82da7ff3824b42829d324364",
"reference": "56f33548fe522c8d82da7ff3824b42829d324364",
"shasum": ""
},
"require": {
@ -1607,7 +1607,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.4"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.6"
},
"funding": [
{
@ -1615,7 +1615,7 @@
"type": "github"
}
],
"time": "2023-08-31T14:04:38+00:00"
"time": "2023-09-19T04:59:03+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1862,16 +1862,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.3.3",
"version": "10.3.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4"
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503",
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503",
"shasum": ""
},
"require": {
@ -1885,7 +1885,7 @@
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"php": ">=8.1",
"phpunit/php-code-coverage": "^10.1.1",
"phpunit/php-code-coverage": "^10.1.5",
"phpunit/php-file-iterator": "^4.0",
"phpunit/php-invoker": "^4.0",
"phpunit/php-text-template": "^3.0",
@ -1895,7 +1895,7 @@
"sebastian/comparator": "^5.0",
"sebastian/diff": "^5.0",
"sebastian/environment": "^6.0",
"sebastian/exporter": "^5.0",
"sebastian/exporter": "^5.1",
"sebastian/global-state": "^6.0.1",
"sebastian/object-enumerator": "^5.0",
"sebastian/recursion-context": "^5.0",
@ -1943,7 +1943,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.3"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5"
},
"funding": [
{
@ -1959,7 +1959,7 @@
"type": "tidelift"
}
],
"time": "2023-09-05T04:34:51+00:00"
"time": "2023-09-19T05:42:37+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2396,16 +2396,16 @@
},
{
"name": "sebastian/exporter",
"version": "5.0.0",
"version": "5.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0"
"reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c3fa8483f9539b190f7cd4bfc4a07631dd1df344",
"reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344",
"shasum": ""
},
"require": {
@ -2461,7 +2461,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0"
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.0"
},
"funding": [
{
@ -2469,7 +2470,7 @@
"type": "github"
}
],
"time": "2023-02-03T07:06:49+00:00"
"time": "2023-09-18T07:15:37+00:00"
},
{
"name": "sebastian/global-state",

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.5.1";
public const BASE_VERSION = "5.6.1";
public const IS_DEVELOPMENT_BUILD = true;
public const BUILD_CHANNEL = "stable";

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\StructureGrowEvent;
@ -46,6 +47,7 @@ use function mt_rand;
use const PHP_INT_MAX;
class Bamboo extends Transparent{
use StaticSupportTrait;
public const NO_LEAVES = 0;
public const SMALL_LEAVES = 1;
@ -120,12 +122,14 @@ class Bamboo extends Transparent{
return new Vector3($retX, 0, $retZ);
}
private function canBeSupportedBy(Block $block) : bool{
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return
$block->getTypeId() === BlockTypeIds::GRAVEL ||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
$block->hasTypeTag(BlockTypeTags::MUD) ||
$block->hasTypeTag(BlockTypeTags::SAND);
$supportBlock->hasSameTypeId($this) ||
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
}
private function seekToTop() : Bamboo{
@ -153,14 +157,6 @@ class Bamboo extends Transparent{
return false;
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
$below = $world->getBlock($this->position->down());
if(!$this->canBeSupportedBy($below) && !$below->hasSameTypeId($this)){
$world->useBreakOn($this->position);
}
}
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->up())->canBeReplaced()){

View File

@ -23,17 +23,21 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Bamboo as ItemBamboo;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class BambooSapling extends Flowable{
use StaticSupportTrait;
private bool $ready = false;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@ -48,19 +52,13 @@ final class BambooSapling extends Flowable{
return $this;
}
private function canBeSupportedBy(Block $block) : bool{
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return
$block->getTypeId() === BlockTypeIds::GRAVEL ||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
$block->hasTypeTag(BlockTypeTags::MUD) ||
$block->hasTypeTag(BlockTypeTags::SAND);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->position->getWorld()->getBlock($blockReplace->position->down()))){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@ -73,13 +71,6 @@ final class BambooSapling extends Flowable{
return false;
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
$world->useBreakOn($this->position);
}
}
private function grow(?Player $player) : bool{
$world = $this->position->getWorld();
if(!$world->getBlock($this->position->up())->canBeReplaced()){

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\FoodSource;
@ -31,27 +32,16 @@ use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
abstract class BaseCake extends Transparent implements FoodSource{
use StaticSupportTrait;
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->getTypeId() !== BlockTypeIds::AIR){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){ //Replace with common break method
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{

View File

@ -23,39 +23,22 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\event\entity\EntityDamageByBlockEvent;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Cactus extends Transparent{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 15;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function hasEntityCollision() : bool{
return true;
}
@ -78,23 +61,18 @@ class Cactus extends Transparent{
return true;
}
private function canBeSupportedBy(Block $block) : bool{
return $block->hasSameTypeId($this) || $block->hasTypeTag(BlockTypeTags::SAND);
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$world->useBreakOn($this->position);
}else{
foreach(Facing::HORIZONTAL as $side){
$b = $this->getSide($side);
if($b->isSolid()){
$world->useBreakOn($this->position);
break;
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
if(!$supportBlock->hasSameTypeId($this) && !$supportBlock->hasTypeTag(BlockTypeTags::SAND)){
return false;
}
foreach(Facing::HORIZONTAL as $side){
if($block->getSide($side)->isSolid()){
return false;
}
}
return true;
}
public function ticksRandomly() : bool{
@ -124,18 +102,4 @@ class Cactus extends Transparent{
}
}
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
foreach(Facing::HORIZONTAL as $side){
if($this->getSide($side)->isSolid()){
return false;
}
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
}

View File

@ -24,15 +24,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\ColoredTrait;
use pocketmine\item\Item;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Carpet extends Flowable{
use ColoredTrait;
use StaticSupportTrait;
public function isSolid() : bool{
return true;
@ -45,19 +43,8 @@ class Carpet extends Flowable{
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->getTypeId() !== BlockTypeIds::AIR){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
}
public function getFlameEncouragement() : int{

View File

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
@ -38,9 +40,11 @@ use pocketmine\world\sound\GlowBerriesPickSound;
use function mt_rand;
class CaveVines extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 25;
protected int $age = 0;
protected bool $berries = false;
protected bool $head = false;
@ -66,19 +70,6 @@ class CaveVines extends Flowable{
return $this;
}
public function getAge() : int{
return $this->age;
}
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function canClimb() : bool{
return true;
}
@ -88,19 +79,11 @@ class CaveVines extends Flowable{
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::UP) === SupportType::FULL || $block->hasSameTypeId($this);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
$supportBlock = $block->getSide(Facing::UP);
return $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || $supportBlock->hasSameTypeId($this);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
$this->age = mt_rand(0, self::MAX_AGE);
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -23,52 +23,38 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\entity\projectile\Projectile;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
use pocketmine\world\sound\ChorusFlowerDieSound;
use pocketmine\world\sound\ChorusFlowerGrowSound;
use pocketmine\world\World;
use function array_rand;
use function min;
use function mt_rand;
final class ChorusFlower extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MIN_AGE = 0;
public const MAX_AGE = 5;
private const MAX_STEM_HEIGHT = 5;
private int $age = self::MIN_AGE;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, self::MIN_AGE, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < self::MIN_AGE || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in the range " . self::MIN_AGE . " ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()];
}
private function canBeSupportedAt(Position $position) : bool{
private function canBeSupportedAt(Block $block) : bool{
$position = $block->getPosition();
$world = $position->getWorld();
$down = $world->getBlock($position->down());
@ -93,19 +79,6 @@ final class ChorusFlower extends Flowable{
return $plantAdjacent;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace->getPosition())){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this->position)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
$this->position->getWorld()->useBreakOn($this->position);
}
@ -181,7 +154,7 @@ final class ChorusFlower extends Flowable{
if($tx === null){
$tx = new BlockTransaction($this->position->getWorld());
}
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge($this->getAge() + $ageChange));
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->getAge() + $ageChange)));
return $tx;
}

View File

@ -23,18 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
use function mt_rand;
final class ChorusPlant extends Flowable{
use StaticSupportTrait;
protected function recalculateCollisionBoxes() : array{
$bb = AxisAlignedBB::one();
@ -52,7 +50,8 @@ final class ChorusPlant extends Flowable{
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
}
private function canStay(Position $position) : bool{
private function canBeSupportedAt(Block $block) : bool{
$position = $block->getPosition();
$world = $position->getWorld();
$down = $world->getBlock($position->down());
@ -72,24 +71,7 @@ final class ChorusPlant extends Flowable{
}
}
if($this->canBeSupportedBy($down)){
return true;
}
return false;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canStay($blockReplace->getPosition())){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canStay($this->position)){
$this->position->getWorld()->useBreakOn($this->position);
}
return $this->canBeSupportedBy($down);
}
public function getDropsForCompatibleTool(Item $item) : array{

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\SupportType;
@ -41,27 +42,15 @@ use function mt_rand;
class CocoaBlock extends Transparent{
use HorizontalFacingTrait;
use AgeableTrait;
public const MAX_AGE = 2;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
/**
* @return AxisAlignedBB[]
*/

View File

@ -23,29 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class Coral extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedAt($this)){
$world->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
use StaticSupportTrait;
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();

View File

@ -23,51 +23,34 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
abstract class Crops extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 7;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($blockReplace->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
$block = clone $this;
$block->age += mt_rand(2, 5);
if($block->age > self::MAX_AGE){
$block->age = self::MAX_AGE;
$tempAge = $block->age + mt_rand(2, 5);
if($tempAge > self::MAX_AGE){
$tempAge = self::MAX_AGE;
}
$block->age = $tempAge;
if(BlockEventHelper::grow($this, $block, $player)){
$item->pop();
}
@ -78,12 +61,6 @@ abstract class Crops extends Flowable{
return false;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::FARMLAND){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function ticksRandomly() : bool{
return true;
}

View File

@ -23,29 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
class DeadBush extends Flowable{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
use StaticSupportTrait;
public function getDropsForIncompatibleTool(Item $item) : array{
return [
@ -65,14 +50,21 @@ class DeadBush extends Flowable{
return 100;
}
private function canBeSupportedBy(Block $block) : bool{
$blockId = $block->getTypeId();
return $blockId === BlockTypeIds::SAND
|| $blockId === BlockTypeIds::RED_SAND
|| $blockId === BlockTypeIds::PODZOL
|| $blockId === BlockTypeIds::MYCELIUM
|| $blockId === BlockTypeIds::DIRT
|| $blockId === BlockTypeIds::HARDENED_CLAY
|| $blockId === BlockTypeIds::STAINED_CLAY;
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return
$supportBlock->hasTypeTag(BlockTypeTags::SAND) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
match($supportBlock->getTypeId()){
//can't use DIRT tag here because it includes farmland
BlockTypeIds::PODZOL,
BlockTypeIds::MYCELIUM,
BlockTypeIds::DIRT,
BlockTypeIds::GRASS,
BlockTypeIds::HARDENED_CLAY,
BlockTypeIds::STAINED_CLAY => true,
//TODO: moss block
default => false,
};
}
}

View File

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockBurnEvent;
use pocketmine\math\Facing;
use pocketmine\world\format\Chunk;
@ -36,25 +36,10 @@ use function min;
use function mt_rand;
class Fire extends BaseFire{
use AgeableTrait;
public const MAX_AGE = 15;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
protected function getFireDamage() : int{
return 1;
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
@ -35,6 +36,8 @@ use function atan2;
use function rad2deg;
final class FloorCoralFan extends BaseCoral{
use StaticSupportTrait;
private int $axis = Axis::X;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@ -53,9 +56,6 @@ final class FloorCoralFan extends BaseCoral{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
if($player !== null){
$playerBlockPos = $player->getPosition()->floor();
$directionVector = $blockReplace->getPosition()->subtractVector($playerBlockPos)->normalize();
@ -73,15 +73,6 @@ final class FloorCoralFan extends BaseCoral{
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$world = $this->position->getWorld();
if(!$this->canBeSupportedAt($this)){
$world->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
}

View File

@ -23,28 +23,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class Flower extends Flowable{
use StaticSupportTrait;
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
$down = $this->getSide(Facing::DOWN);
if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
}
public function getFlameEncouragement() : int{

View File

@ -24,15 +24,16 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function assert;
class FlowerPot extends Flowable{
use StaticSupportTrait;
protected ?Block $plant = null;
@ -89,20 +90,6 @@ class FlowerPot extends Flowable{
return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
}

View File

@ -23,30 +23,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use function mt_rand;
class FrostedIce extends Ice{
use AgeableTrait;
public const MAX_AGE = 3;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function onNearbyBlockChange() : void{
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40));
}

View File

@ -23,30 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class HangingRoots extends Flowable{
use StaticSupportTrait;
private function canBeSupportedBy(Block $block) : bool{
return $block->isSolid(); //TODO: not sure if this is the correct logic
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$blockReplace->getSide(Facing::UP)->isSolid()){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::UP))){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::UP)->hasCenterSupport(); //weird I know, but they can be placed on the bottom of fences
}
public function getDropsForIncompatibleTool(Item $item) : array{

View File

@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Fertilizer;
@ -41,36 +42,19 @@ use function mt_rand;
* This class is used for Weeping & Twisting vines, because they have same behaviour
*/
class NetherVines extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 25;
/** Direction the vine grows towards. */
private int $growthFace;
protected int $age = 0;
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $growthFace){
$this->growthFace = $growthFace;
parent::__construct($idInfo, $name, $typeInfo);
}
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{
return $this->age;
}
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function isAffectedBySilkTouch() : bool{
return true;
}
@ -88,12 +72,6 @@ class NetherVines extends Flowable{
return $supportBlock->getSupportType($this->growthFace)->hasCenterSupport() || $supportBlock->hasSameTypeId($this);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
/**
* Returns the block at the end of the vine structure furthest from the supporting block.
*/
@ -106,9 +84,6 @@ class NetherVines extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
$this->age = mt_rand(0, self::MAX_AGE - 1);
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}

View File

@ -23,49 +23,22 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
class NetherWartPlant extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 3;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ..." . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->getTypeId() === BlockTypeIds::SOUL_SAND){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::SOUL_SAND){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::SOUL_SAND;
}
public function ticksRandomly() : bool{

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
@ -35,6 +36,9 @@ use pocketmine\world\BlockTransaction;
class PinkPetals extends Flowable{
use HorizontalFacingTrait;
use StaticSupportTrait {
canBePlacedAt as supportedWhenPlacedAt;
}
public const MIN_COUNT = 1;
public const MAX_COUNT = 4;
@ -65,20 +69,11 @@ class PinkPetals extends Flowable{
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($this)){
return false;
}
if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){
$this->count = $blockReplace->getCount() + 1;
$this->facing = $blockReplace->getFacing();

View File

@ -23,21 +23,19 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
use pocketmine\event\block\PressurePlateUpdateEvent;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\PressurePlateActivateSound;
use pocketmine\world\sound\PressurePlateDeactivateSound;
use function count;
abstract class PressurePlate extends Transparent{
use StaticSupportTrait;
private readonly int $deactivationDelayTicks;
@ -63,23 +61,10 @@ abstract class PressurePlate extends Transparent{
return SupportType::NONE;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedAt($blockReplace)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function hasEntityCollision() : bool{
return true;
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\tile\Comparator;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@ -41,6 +42,7 @@ class RedstoneComparator extends Flowable{
use HorizontalFacingTrait;
use AnalogRedstoneSignalEmitterTrait;
use PoweredByRedstoneTrait;
use StaticSupportTrait;
protected bool $isSubtractMode = false;
@ -85,14 +87,10 @@ class RedstoneComparator extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedAt($blockReplace)){
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
return false;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@ -101,12 +99,6 @@ class RedstoneComparator extends Flowable{
return true;
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
@ -37,6 +38,7 @@ use pocketmine\world\BlockTransaction;
class RedstoneRepeater extends Flowable{
use HorizontalFacingTrait;
use PoweredByRedstoneTrait;
use StaticSupportTrait;
public const MIN_DELAY = 1;
public const MAX_DELAY = 4;
@ -68,15 +70,11 @@ class RedstoneRepeater extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedAt($blockReplace)){
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
if($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
return false;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@ -87,12 +85,6 @@ class RedstoneRepeater extends Flowable{
return true;
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
}

View File

@ -24,22 +24,14 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class RedstoneWire extends Flowable{
use AnalogRedstoneSignalEmitterTrait;
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedAt($blockReplace)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
use StaticSupportTrait;
public function readStateFromWorld() : Block{
parent::readStateFromWorld();
@ -48,12 +40,6 @@ class RedstoneWire extends Flowable{
return $this;
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SaplingType;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Fertilizer;
@ -32,11 +33,12 @@ use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\utils\Random;
use pocketmine\world\BlockTransaction;
use pocketmine\world\generator\object\TreeFactory;
use function mt_rand;
class Sapling extends Flowable{
use StaticSupportTrait;
protected bool $ready = false;
private SaplingType $saplingType;
@ -58,13 +60,9 @@ class Sapling extends Flowable{
return $this;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@ -77,13 +75,6 @@ class Sapling extends Flowable{
return false;
}
public function onNearbyBlockChange() : void{
$down = $this->getSide(Facing::DOWN);
if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function ticksRandomly() : bool{
return true;
}

View File

@ -23,30 +23,14 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class SporeBlossom extends Flowable{
use StaticSupportTrait;
private function canBeSupportedAt(Block $block) : bool{
return $block->getAdjacentSupportType(Facing::UP) === SupportType::FULL;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($blockReplace)){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
}

View File

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\math\Facing;
@ -34,14 +35,11 @@ use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
class Sugarcane extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 15;
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
}
private function seekToBottom() : Position{
$world = $this->position->getWorld();
$bottom = $this->position;
@ -74,17 +72,6 @@ class Sugarcane extends Flowable{
return $grew;
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer){
if($this->grow($this->seekToBottom(), $player)){
@ -97,18 +84,12 @@ class Sugarcane extends Flowable{
return false;
}
private function canBeSupportedBy(Block $block) : bool{
return
$block->hasTypeTag(BlockTypeTags::MUD) ||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
$block->hasTypeTag(BlockTypeTags::SAND);
}
public function onNearbyBlockChange() : void{
$down = $this->getSide(Facing::DOWN);
if(!$down->hasSameTypeId($this) && !$this->canBeSupportedBy($down)){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return $supportBlock->hasSameTypeId($this) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
}
public function ticksRandomly() : bool{
@ -127,15 +108,16 @@ class Sugarcane extends Flowable{
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$down = $this->getSide(Facing::DOWN);
$down = $blockReplace->getSide(Facing::DOWN);
if($down->hasSameTypeId($this)){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($this->canBeSupportedBy($down)){
foreach(Facing::HORIZONTAL as $side){
$sideBlock = $down->getSide($side);
if($sideBlock instanceof Water || $sideBlock instanceof FrostedIce){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}
//support criteria are checked by FixedSupportTrait, but this part applies to placement only
foreach(Facing::HORIZONTAL as $side){
$sideBlock = $down->getSide($side);
if($sideBlock instanceof Water || $sideBlock instanceof FrostedIce){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}

View File

@ -23,9 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use pocketmine\event\entity\EntityDamageByBlockEvent;
@ -35,31 +36,17 @@ use pocketmine\item\VanillaItems;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function mt_rand;
class SweetBerryBush extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const STAGE_SAPLING = 0;
public const STAGE_BUSH_NO_BERRIES = 1;
public const STAGE_BUSH_SOME_BERRIES = 2;
public const STAGE_MATURE = 3;
protected int $age = self::STAGE_SAPLING;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt(3, self::STAGE_SAPLING, self::STAGE_MATURE, $this->age);
}
public function getAge() : int{ return $this->age; }
/** @return $this */
public function setAge(int $age) : self{
if($age < self::STAGE_SAPLING || $age > self::STAGE_MATURE){
throw new \InvalidArgumentException("Age must be in range 0-3");
}
$this->age = $age;
return $this;
}
public const MAX_AGE = self::STAGE_MATURE;
public function getBerryDropAmount() : int{
if($this->age === self::STAGE_MATURE){
@ -70,16 +57,17 @@ class SweetBerryBush extends Flowable{
return 0;
}
/**
* @deprecated
*/
protected function canBeSupportedBy(Block $block) : bool{
return $block->getTypeId() !== BlockTypeIds::FARMLAND && //bedrock-specific thing (bug?)
($block->hasTypeTag(BlockTypeTags::DIRT) || $block->hasTypeTag(BlockTypeTags::MUD));
}
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);
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return $this->canBeSupportedBy($supportBlock);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@ -113,12 +101,6 @@ class SweetBerryBush extends Flowable{
];
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function ticksRandomly() : bool{
return true;
}

View File

@ -23,31 +23,16 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\block\utils\TallGrassTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class TallGrass extends Flowable{
use TallGrassTrait;
use StaticSupportTrait;
private function canBeSupportedBy(Block $block) : bool{
return $block->hasTypeTag(BlockTypeTags::DIRT) || $block->hasTypeTag(BlockTypeTags::MUD);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
return false;
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){ //Replace with common break method
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
}
}

View File

@ -23,14 +23,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\item\Item;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class WaterLily extends Flowable{
use StaticSupportTrait {
canBePlacedAt as supportedWhenPlacedAt;
}
/**
* @return AxisAlignedBB[]
@ -40,23 +41,10 @@ class WaterLily extends Flowable{
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return !$blockReplace instanceof Water && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
return !$blockReplace instanceof Water && $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
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->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN) instanceof Water;
}
}

View File

@ -23,23 +23,22 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\VanillaEffects;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class WitherRose extends Flowable{
use StaticSupportTrait;
private function canBeSupportedBy(Block $block) : bool{
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
return
$block->hasTypeTag(BlockTypeTags::DIRT) ||
$block->hasTypeTag(BlockTypeTags::MUD) ||
match($block->getTypeId()){
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
match($supportBlock->getTypeId()){
BlockTypeIds::NETHERRACK,
BlockTypeIds::SOUL_SAND,
BlockTypeIds::SOUL_SOIL => true,
@ -47,19 +46,6 @@ class WitherRose extends Flowable{
};
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
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 hasEntityCollision() : bool{ return true; }
public function onEntityInside(Entity $entity) : bool{

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\block\utils;
use pocketmine\data\runtime\RuntimeDataDescriber;
use function ceil;
use function log;
/**
* This trait is used for blocks that have an age property.
* Need to add to the block the constant MAX_AGE.
*/
trait AgeableTrait{
protected int $age = 0;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->boundedInt((int) ceil(log(self::MAX_AGE, 2)), 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }
/**
* @return $this
*/
public function setAge(int $age) : self{
if($age < 0 || $age > self::MAX_AGE){
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
}
$this->age = $age;
return $this;
}
}

View File

@ -0,0 +1,57 @@
<?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\math\Vector3;
/**
* Used by blocks which always have the same support requirements no matter what state they are in.
* Prevents placement if support isn't available, and automatically destroys itself if support is removed.
*/
trait StaticSupportTrait{
/**
* Implement this to define the block's support requirements.
*/
abstract private function canBeSupportedAt(Block $block) : bool;
/**
* @see Block::canBePlacedAt()
*/
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return $this->canBeSupportedAt($blockReplace) && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
/**
* @see Block::onNearbyBlockChange()
*/
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}else{
parent::onNearbyBlockChange();
}
}
}

View File

@ -42,8 +42,8 @@ final class BlockStateData{
public const CURRENT_VERSION =
(1 << 24) | //major
(20 << 16) | //minor
(10 << 8) | //patch
(32); //revision
(30 << 8) | //patch
(50); //revision
public const TAG_NAME = "name";
public const TAG_STATES = "states";

View File

@ -98,8 +98,10 @@ final class BlockStateNames{
public const LEVER_DIRECTION = "lever_direction";
public const LIQUID_DEPTH = "liquid_depth";
public const LIT = "lit";
public const MC_BLOCK_FACE = "minecraft:block_face";
public const MC_CARDINAL_DIRECTION = "minecraft:cardinal_direction";
public const MC_FACING_DIRECTION = "minecraft:facing_direction";
public const MC_VERTICAL_HALF = "minecraft:vertical_half";
public const MOISTURIZED_AMOUNT = "moisturized_amount";
public const MONSTER_EGG_STONE_TYPE = "monster_egg_stone_type";
public const MULTI_FACE_DIRECTION_BITS = "multi_face_direction_bits";
@ -141,7 +143,6 @@ final class BlockStateNames{
public const SUSPENDED_BIT = "suspended_bit";
public const TALL_GRASS_TYPE = "tall_grass_type";
public const TOGGLE_BIT = "toggle_bit";
public const TOP_SLOT_BIT = "top_slot_bit";
public const TORCH_FACING_DIRECTION = "torch_facing_direction";
public const TRIGGERED_BIT = "triggered_bit";
public const TURTLE_EGG_COUNT = "turtle_egg_count";

View File

@ -131,6 +131,13 @@ final class BlockStateStringValues{
public const LEVER_DIRECTION_UP_NORTH_SOUTH = "up_north_south";
public const LEVER_DIRECTION_WEST = "west";
public const MC_BLOCK_FACE_DOWN = "down";
public const MC_BLOCK_FACE_EAST = "east";
public const MC_BLOCK_FACE_NORTH = "north";
public const MC_BLOCK_FACE_SOUTH = "south";
public const MC_BLOCK_FACE_UP = "up";
public const MC_BLOCK_FACE_WEST = "west";
public const MC_CARDINAL_DIRECTION_EAST = "east";
public const MC_CARDINAL_DIRECTION_NORTH = "north";
public const MC_CARDINAL_DIRECTION_SOUTH = "south";
@ -143,6 +150,9 @@ final class BlockStateStringValues{
public const MC_FACING_DIRECTION_UP = "up";
public const MC_FACING_DIRECTION_WEST = "west";
public const MC_VERTICAL_HALF_BOTTOM = "bottom";
public const MC_VERTICAL_HALF_TOP = "top";
public const MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK = "chiseled_stone_brick";
public const MONSTER_EGG_STONE_TYPE_COBBLESTONE = "cobblestone";
public const MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK = "cracked_stone_brick";

View File

@ -99,8 +99,12 @@ final class BlockTypeNames{
public const BLACK_CANDLE_CAKE = "minecraft:black_candle_cake";
public const BLACK_CARPET = "minecraft:black_carpet";
public const BLACK_CONCRETE = "minecraft:black_concrete";
public const BLACK_CONCRETE_POWDER = "minecraft:black_concrete_powder";
public const BLACK_GLAZED_TERRACOTTA = "minecraft:black_glazed_terracotta";
public const BLACK_SHULKER_BOX = "minecraft:black_shulker_box";
public const BLACK_STAINED_GLASS = "minecraft:black_stained_glass";
public const BLACK_STAINED_GLASS_PANE = "minecraft:black_stained_glass_pane";
public const BLACK_TERRACOTTA = "minecraft:black_terracotta";
public const BLACK_WOOL = "minecraft:black_wool";
public const BLACKSTONE = "minecraft:blackstone";
public const BLACKSTONE_DOUBLE_SLAB = "minecraft:blackstone_double_slab";
@ -112,9 +116,13 @@ final class BlockTypeNames{
public const BLUE_CANDLE_CAKE = "minecraft:blue_candle_cake";
public const BLUE_CARPET = "minecraft:blue_carpet";
public const BLUE_CONCRETE = "minecraft:blue_concrete";
public const BLUE_CONCRETE_POWDER = "minecraft:blue_concrete_powder";
public const BLUE_GLAZED_TERRACOTTA = "minecraft:blue_glazed_terracotta";
public const BLUE_ICE = "minecraft:blue_ice";
public const BLUE_SHULKER_BOX = "minecraft:blue_shulker_box";
public const BLUE_STAINED_GLASS = "minecraft:blue_stained_glass";
public const BLUE_STAINED_GLASS_PANE = "minecraft:blue_stained_glass_pane";
public const BLUE_TERRACOTTA = "minecraft:blue_terracotta";
public const BLUE_WOOL = "minecraft:blue_wool";
public const BONE_BLOCK = "minecraft:bone_block";
public const BOOKSHELF = "minecraft:bookshelf";
@ -127,10 +135,14 @@ final class BlockTypeNames{
public const BROWN_CANDLE_CAKE = "minecraft:brown_candle_cake";
public const BROWN_CARPET = "minecraft:brown_carpet";
public const BROWN_CONCRETE = "minecraft:brown_concrete";
public const BROWN_CONCRETE_POWDER = "minecraft:brown_concrete_powder";
public const BROWN_GLAZED_TERRACOTTA = "minecraft:brown_glazed_terracotta";
public const BROWN_MUSHROOM = "minecraft:brown_mushroom";
public const BROWN_MUSHROOM_BLOCK = "minecraft:brown_mushroom_block";
public const BROWN_SHULKER_BOX = "minecraft:brown_shulker_box";
public const BROWN_STAINED_GLASS = "minecraft:brown_stained_glass";
public const BROWN_STAINED_GLASS_PANE = "minecraft:brown_stained_glass_pane";
public const BROWN_TERRACOTTA = "minecraft:brown_terracotta";
public const BROWN_WOOL = "minecraft:brown_wool";
public const BUBBLE_COLUMN = "minecraft:bubble_column";
public const BUBBLE_CORAL = "minecraft:bubble_coral";
@ -194,7 +206,6 @@ final class BlockTypeNames{
public const COLORED_TORCH_RG = "minecraft:colored_torch_rg";
public const COMMAND_BLOCK = "minecraft:command_block";
public const COMPOSTER = "minecraft:composter";
public const CONCRETE_POWDER = "minecraft:concrete_powder";
public const CONDUIT = "minecraft:conduit";
public const COPPER_BLOCK = "minecraft:copper_block";
public const COPPER_ORE = "minecraft:copper_ore";
@ -235,8 +246,12 @@ final class BlockTypeNames{
public const CYAN_CANDLE_CAKE = "minecraft:cyan_candle_cake";
public const CYAN_CARPET = "minecraft:cyan_carpet";
public const CYAN_CONCRETE = "minecraft:cyan_concrete";
public const CYAN_CONCRETE_POWDER = "minecraft:cyan_concrete_powder";
public const CYAN_GLAZED_TERRACOTTA = "minecraft:cyan_glazed_terracotta";
public const CYAN_SHULKER_BOX = "minecraft:cyan_shulker_box";
public const CYAN_STAINED_GLASS = "minecraft:cyan_stained_glass";
public const CYAN_STAINED_GLASS_PANE = "minecraft:cyan_stained_glass_pane";
public const CYAN_TERRACOTTA = "minecraft:cyan_terracotta";
public const CYAN_WOOL = "minecraft:cyan_wool";
public const DARK_OAK_BUTTON = "minecraft:dark_oak_button";
public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
@ -463,15 +478,23 @@ final class BlockTypeNames{
public const GRAY_CANDLE_CAKE = "minecraft:gray_candle_cake";
public const GRAY_CARPET = "minecraft:gray_carpet";
public const GRAY_CONCRETE = "minecraft:gray_concrete";
public const GRAY_CONCRETE_POWDER = "minecraft:gray_concrete_powder";
public const GRAY_GLAZED_TERRACOTTA = "minecraft:gray_glazed_terracotta";
public const GRAY_SHULKER_BOX = "minecraft:gray_shulker_box";
public const GRAY_STAINED_GLASS = "minecraft:gray_stained_glass";
public const GRAY_STAINED_GLASS_PANE = "minecraft:gray_stained_glass_pane";
public const GRAY_TERRACOTTA = "minecraft:gray_terracotta";
public const GRAY_WOOL = "minecraft:gray_wool";
public const GREEN_CANDLE = "minecraft:green_candle";
public const GREEN_CANDLE_CAKE = "minecraft:green_candle_cake";
public const GREEN_CARPET = "minecraft:green_carpet";
public const GREEN_CONCRETE = "minecraft:green_concrete";
public const GREEN_CONCRETE_POWDER = "minecraft:green_concrete_powder";
public const GREEN_GLAZED_TERRACOTTA = "minecraft:green_glazed_terracotta";
public const GREEN_SHULKER_BOX = "minecraft:green_shulker_box";
public const GREEN_STAINED_GLASS = "minecraft:green_stained_glass";
public const GREEN_STAINED_GLASS_PANE = "minecraft:green_stained_glass_pane";
public const GREEN_TERRACOTTA = "minecraft:green_terracotta";
public const GREEN_WOOL = "minecraft:green_wool";
public const GRINDSTONE = "minecraft:grindstone";
public const HANGING_ROOTS = "minecraft:hanging_roots";
@ -525,14 +548,22 @@ final class BlockTypeNames{
public const LIGHT_BLUE_CANDLE_CAKE = "minecraft:light_blue_candle_cake";
public const LIGHT_BLUE_CARPET = "minecraft:light_blue_carpet";
public const LIGHT_BLUE_CONCRETE = "minecraft:light_blue_concrete";
public const LIGHT_BLUE_CONCRETE_POWDER = "minecraft:light_blue_concrete_powder";
public const LIGHT_BLUE_GLAZED_TERRACOTTA = "minecraft:light_blue_glazed_terracotta";
public const LIGHT_BLUE_SHULKER_BOX = "minecraft:light_blue_shulker_box";
public const LIGHT_BLUE_STAINED_GLASS = "minecraft:light_blue_stained_glass";
public const LIGHT_BLUE_STAINED_GLASS_PANE = "minecraft:light_blue_stained_glass_pane";
public const LIGHT_BLUE_TERRACOTTA = "minecraft:light_blue_terracotta";
public const LIGHT_BLUE_WOOL = "minecraft:light_blue_wool";
public const LIGHT_GRAY_CANDLE = "minecraft:light_gray_candle";
public const LIGHT_GRAY_CANDLE_CAKE = "minecraft:light_gray_candle_cake";
public const LIGHT_GRAY_CARPET = "minecraft:light_gray_carpet";
public const LIGHT_GRAY_CONCRETE = "minecraft:light_gray_concrete";
public const LIGHT_GRAY_CONCRETE_POWDER = "minecraft:light_gray_concrete_powder";
public const LIGHT_GRAY_SHULKER_BOX = "minecraft:light_gray_shulker_box";
public const LIGHT_GRAY_STAINED_GLASS = "minecraft:light_gray_stained_glass";
public const LIGHT_GRAY_STAINED_GLASS_PANE = "minecraft:light_gray_stained_glass_pane";
public const LIGHT_GRAY_TERRACOTTA = "minecraft:light_gray_terracotta";
public const LIGHT_GRAY_WOOL = "minecraft:light_gray_wool";
public const LIGHT_WEIGHTED_PRESSURE_PLATE = "minecraft:light_weighted_pressure_plate";
public const LIGHTNING_ROD = "minecraft:lightning_rod";
@ -540,8 +571,12 @@ final class BlockTypeNames{
public const LIME_CANDLE_CAKE = "minecraft:lime_candle_cake";
public const LIME_CARPET = "minecraft:lime_carpet";
public const LIME_CONCRETE = "minecraft:lime_concrete";
public const LIME_CONCRETE_POWDER = "minecraft:lime_concrete_powder";
public const LIME_GLAZED_TERRACOTTA = "minecraft:lime_glazed_terracotta";
public const LIME_SHULKER_BOX = "minecraft:lime_shulker_box";
public const LIME_STAINED_GLASS = "minecraft:lime_stained_glass";
public const LIME_STAINED_GLASS_PANE = "minecraft:lime_stained_glass_pane";
public const LIME_TERRACOTTA = "minecraft:lime_terracotta";
public const LIME_WOOL = "minecraft:lime_wool";
public const LIT_BLAST_FURNACE = "minecraft:lit_blast_furnace";
public const LIT_DEEPSLATE_REDSTONE_ORE = "minecraft:lit_deepslate_redstone_ore";
@ -556,8 +591,12 @@ final class BlockTypeNames{
public const MAGENTA_CANDLE_CAKE = "minecraft:magenta_candle_cake";
public const MAGENTA_CARPET = "minecraft:magenta_carpet";
public const MAGENTA_CONCRETE = "minecraft:magenta_concrete";
public const MAGENTA_CONCRETE_POWDER = "minecraft:magenta_concrete_powder";
public const MAGENTA_GLAZED_TERRACOTTA = "minecraft:magenta_glazed_terracotta";
public const MAGENTA_SHULKER_BOX = "minecraft:magenta_shulker_box";
public const MAGENTA_STAINED_GLASS = "minecraft:magenta_stained_glass";
public const MAGENTA_STAINED_GLASS_PANE = "minecraft:magenta_stained_glass_pane";
public const MAGENTA_TERRACOTTA = "minecraft:magenta_terracotta";
public const MAGENTA_WOOL = "minecraft:magenta_wool";
public const MAGMA = "minecraft:magma";
public const MANGROVE_BUTTON = "minecraft:mangrove_button";
@ -620,8 +659,12 @@ final class BlockTypeNames{
public const ORANGE_CANDLE_CAKE = "minecraft:orange_candle_cake";
public const ORANGE_CARPET = "minecraft:orange_carpet";
public const ORANGE_CONCRETE = "minecraft:orange_concrete";
public const ORANGE_CONCRETE_POWDER = "minecraft:orange_concrete_powder";
public const ORANGE_GLAZED_TERRACOTTA = "minecraft:orange_glazed_terracotta";
public const ORANGE_SHULKER_BOX = "minecraft:orange_shulker_box";
public const ORANGE_STAINED_GLASS = "minecraft:orange_stained_glass";
public const ORANGE_STAINED_GLASS_PANE = "minecraft:orange_stained_glass_pane";
public const ORANGE_TERRACOTTA = "minecraft:orange_terracotta";
public const ORANGE_WOOL = "minecraft:orange_wool";
public const OXIDIZED_COPPER = "minecraft:oxidized_copper";
public const OXIDIZED_CUT_COPPER = "minecraft:oxidized_cut_copper";
@ -635,9 +678,13 @@ final class BlockTypeNames{
public const PINK_CANDLE_CAKE = "minecraft:pink_candle_cake";
public const PINK_CARPET = "minecraft:pink_carpet";
public const PINK_CONCRETE = "minecraft:pink_concrete";
public const PINK_CONCRETE_POWDER = "minecraft:pink_concrete_powder";
public const PINK_GLAZED_TERRACOTTA = "minecraft:pink_glazed_terracotta";
public const PINK_PETALS = "minecraft:pink_petals";
public const PINK_SHULKER_BOX = "minecraft:pink_shulker_box";
public const PINK_STAINED_GLASS = "minecraft:pink_stained_glass";
public const PINK_STAINED_GLASS_PANE = "minecraft:pink_stained_glass_pane";
public const PINK_TERRACOTTA = "minecraft:pink_terracotta";
public const PINK_WOOL = "minecraft:pink_wool";
public const PISTON = "minecraft:piston";
public const PISTON_ARM_COLLISION = "minecraft:piston_arm_collision";
@ -681,8 +728,12 @@ final class BlockTypeNames{
public const PURPLE_CANDLE_CAKE = "minecraft:purple_candle_cake";
public const PURPLE_CARPET = "minecraft:purple_carpet";
public const PURPLE_CONCRETE = "minecraft:purple_concrete";
public const PURPLE_CONCRETE_POWDER = "minecraft:purple_concrete_powder";
public const PURPLE_GLAZED_TERRACOTTA = "minecraft:purple_glazed_terracotta";
public const PURPLE_SHULKER_BOX = "minecraft:purple_shulker_box";
public const PURPLE_STAINED_GLASS = "minecraft:purple_stained_glass";
public const PURPLE_STAINED_GLASS_PANE = "minecraft:purple_stained_glass_pane";
public const PURPLE_TERRACOTTA = "minecraft:purple_terracotta";
public const PURPLE_WOOL = "minecraft:purple_wool";
public const PURPUR_BLOCK = "minecraft:purpur_block";
public const PURPUR_STAIRS = "minecraft:purpur_stairs";
@ -698,6 +749,7 @@ final class BlockTypeNames{
public const RED_CANDLE_CAKE = "minecraft:red_candle_cake";
public const RED_CARPET = "minecraft:red_carpet";
public const RED_CONCRETE = "minecraft:red_concrete";
public const RED_CONCRETE_POWDER = "minecraft:red_concrete_powder";
public const RED_FLOWER = "minecraft:red_flower";
public const RED_GLAZED_TERRACOTTA = "minecraft:red_glazed_terracotta";
public const RED_MUSHROOM = "minecraft:red_mushroom";
@ -707,6 +759,9 @@ final class BlockTypeNames{
public const RED_SANDSTONE = "minecraft:red_sandstone";
public const RED_SANDSTONE_STAIRS = "minecraft:red_sandstone_stairs";
public const RED_SHULKER_BOX = "minecraft:red_shulker_box";
public const RED_STAINED_GLASS = "minecraft:red_stained_glass";
public const RED_STAINED_GLASS_PANE = "minecraft:red_stained_glass_pane";
public const RED_TERRACOTTA = "minecraft:red_terracotta";
public const RED_WOOL = "minecraft:red_wool";
public const REDSTONE_BLOCK = "minecraft:redstone_block";
public const REDSTONE_LAMP = "minecraft:redstone_lamp";
@ -766,9 +821,6 @@ final class BlockTypeNames{
public const SPRUCE_STANDING_SIGN = "minecraft:spruce_standing_sign";
public const SPRUCE_TRAPDOOR = "minecraft:spruce_trapdoor";
public const SPRUCE_WALL_SIGN = "minecraft:spruce_wall_sign";
public const STAINED_GLASS = "minecraft:stained_glass";
public const STAINED_GLASS_PANE = "minecraft:stained_glass_pane";
public const STAINED_HARDENED_CLAY = "minecraft:stained_hardened_clay";
public const STANDING_BANNER = "minecraft:standing_banner";
public const STANDING_SIGN = "minecraft:standing_sign";
public const STICKY_PISTON = "minecraft:sticky_piston";
@ -883,8 +935,12 @@ final class BlockTypeNames{
public const WHITE_CANDLE_CAKE = "minecraft:white_candle_cake";
public const WHITE_CARPET = "minecraft:white_carpet";
public const WHITE_CONCRETE = "minecraft:white_concrete";
public const WHITE_CONCRETE_POWDER = "minecraft:white_concrete_powder";
public const WHITE_GLAZED_TERRACOTTA = "minecraft:white_glazed_terracotta";
public const WHITE_SHULKER_BOX = "minecraft:white_shulker_box";
public const WHITE_STAINED_GLASS = "minecraft:white_stained_glass";
public const WHITE_STAINED_GLASS_PANE = "minecraft:white_stained_glass_pane";
public const WHITE_TERRACOTTA = "minecraft:white_terracotta";
public const WHITE_WOOL = "minecraft:white_wool";
public const WITHER_ROSE = "minecraft:wither_rose";
public const WOOD = "minecraft:wood";
@ -896,8 +952,12 @@ final class BlockTypeNames{
public const YELLOW_CANDLE_CAKE = "minecraft:yellow_candle_cake";
public const YELLOW_CARPET = "minecraft:yellow_carpet";
public const YELLOW_CONCRETE = "minecraft:yellow_concrete";
public const YELLOW_CONCRETE_POWDER = "minecraft:yellow_concrete_powder";
public const YELLOW_FLOWER = "minecraft:yellow_flower";
public const YELLOW_GLAZED_TERRACOTTA = "minecraft:yellow_glazed_terracotta";
public const YELLOW_SHULKER_BOX = "minecraft:yellow_shulker_box";
public const YELLOW_STAINED_GLASS = "minecraft:yellow_stained_glass";
public const YELLOW_STAINED_GLASS_PANE = "minecraft:yellow_stained_glass_pane";
public const YELLOW_TERRACOTTA = "minecraft:yellow_terracotta";
public const YELLOW_WOOL = "minecraft:yellow_wool";
}

View File

@ -406,6 +406,82 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
DyeColor::WHITE => Ids::WHITE_CONCRETE,
DyeColor::YELLOW => Ids::YELLOW_CONCRETE,
}));
$this->map(Blocks::CONCRETE_POWDER(), fn(ConcretePowder $block) => Writer::create(match($block->getColor()){
DyeColor::BLACK => Ids::BLACK_CONCRETE_POWDER,
DyeColor::BLUE => Ids::BLUE_CONCRETE_POWDER,
DyeColor::BROWN => Ids::BROWN_CONCRETE_POWDER,
DyeColor::CYAN => Ids::CYAN_CONCRETE_POWDER,
DyeColor::GRAY => Ids::GRAY_CONCRETE_POWDER,
DyeColor::GREEN => Ids::GREEN_CONCRETE_POWDER,
DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CONCRETE_POWDER,
DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CONCRETE_POWDER,
DyeColor::LIME => Ids::LIME_CONCRETE_POWDER,
DyeColor::MAGENTA => Ids::MAGENTA_CONCRETE_POWDER,
DyeColor::ORANGE => Ids::ORANGE_CONCRETE_POWDER,
DyeColor::PINK => Ids::PINK_CONCRETE_POWDER,
DyeColor::PURPLE => Ids::PURPLE_CONCRETE_POWDER,
DyeColor::RED => Ids::RED_CONCRETE_POWDER,
DyeColor::WHITE => Ids::WHITE_CONCRETE_POWDER,
DyeColor::YELLOW => Ids::YELLOW_CONCRETE_POWDER,
}));
$this->map(Blocks::STAINED_CLAY(), fn(StainedHardenedClay $block) => Writer::create(match($block->getColor()){
DyeColor::BLACK => Ids::BLACK_TERRACOTTA,
DyeColor::BLUE => Ids::BLUE_TERRACOTTA,
DyeColor::BROWN => Ids::BROWN_TERRACOTTA,
DyeColor::CYAN => Ids::CYAN_TERRACOTTA,
DyeColor::GRAY => Ids::GRAY_TERRACOTTA,
DyeColor::GREEN => Ids::GREEN_TERRACOTTA,
DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_TERRACOTTA,
DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_TERRACOTTA,
DyeColor::LIME => Ids::LIME_TERRACOTTA,
DyeColor::MAGENTA => Ids::MAGENTA_TERRACOTTA,
DyeColor::ORANGE => Ids::ORANGE_TERRACOTTA,
DyeColor::PINK => Ids::PINK_TERRACOTTA,
DyeColor::PURPLE => Ids::PURPLE_TERRACOTTA,
DyeColor::RED => Ids::RED_TERRACOTTA,
DyeColor::WHITE => Ids::WHITE_TERRACOTTA,
DyeColor::YELLOW => Ids::YELLOW_TERRACOTTA,
}));
$this->map(Blocks::STAINED_GLASS(), fn(StainedGlass $block) => Writer::create(match($block->getColor()){
DyeColor::BLACK => Ids::BLACK_STAINED_GLASS,
DyeColor::BLUE => Ids::BLUE_STAINED_GLASS,
DyeColor::BROWN => Ids::BROWN_STAINED_GLASS,
DyeColor::CYAN => Ids::CYAN_STAINED_GLASS,
DyeColor::GRAY => Ids::GRAY_STAINED_GLASS,
DyeColor::GREEN => Ids::GREEN_STAINED_GLASS,
DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_STAINED_GLASS,
DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_STAINED_GLASS,
DyeColor::LIME => Ids::LIME_STAINED_GLASS,
DyeColor::MAGENTA => Ids::MAGENTA_STAINED_GLASS,
DyeColor::ORANGE => Ids::ORANGE_STAINED_GLASS,
DyeColor::PINK => Ids::PINK_STAINED_GLASS,
DyeColor::PURPLE => Ids::PURPLE_STAINED_GLASS,
DyeColor::RED => Ids::RED_STAINED_GLASS,
DyeColor::WHITE => Ids::WHITE_STAINED_GLASS,
DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS,
}));
$this->map(Blocks::STAINED_GLASS_PANE(), fn(StainedGlassPane $block) => Writer::create(match($block->getColor()){
DyeColor::BLACK => Ids::BLACK_STAINED_GLASS_PANE,
DyeColor::BLUE => Ids::BLUE_STAINED_GLASS_PANE,
DyeColor::BROWN => Ids::BROWN_STAINED_GLASS_PANE,
DyeColor::CYAN => Ids::CYAN_STAINED_GLASS_PANE,
DyeColor::GRAY => Ids::GRAY_STAINED_GLASS_PANE,
DyeColor::GREEN => Ids::GREEN_STAINED_GLASS_PANE,
DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_STAINED_GLASS_PANE,
DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_STAINED_GLASS_PANE,
DyeColor::LIME => Ids::LIME_STAINED_GLASS_PANE,
DyeColor::MAGENTA => Ids::MAGENTA_STAINED_GLASS_PANE,
DyeColor::ORANGE => Ids::ORANGE_STAINED_GLASS_PANE,
DyeColor::PINK => Ids::PINK_STAINED_GLASS_PANE,
DyeColor::PURPLE => Ids::PURPLE_STAINED_GLASS_PANE,
DyeColor::RED => Ids::RED_STAINED_GLASS_PANE,
DyeColor::WHITE => Ids::WHITE_STAINED_GLASS_PANE,
DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS_PANE,
}));
}
private function registerFlatCoralSerializers() : void{
@ -895,7 +971,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_ANDESITE));
$this->map(Blocks::ANVIL(), function(Anvil $block) : Writer{
return Writer::create(Ids::ANVIL)
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeString(StateNames::DAMAGE, match($damage = $block->getDamage()){
0 => StringValues::DAMAGE_UNDAMAGED,
1 => StringValues::DAMAGE_SLIGHTLY_DAMAGED,
@ -955,7 +1031,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
});
$this->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{
return Writer::create(Ids::BIG_DRIPLEAF)
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeString(StateNames::BIG_DRIPLEAF_TILT, match($block->getLeafState()){
DripleafState::STABLE => StringValues::BIG_DRIPLEAF_TILT_NONE,
DripleafState::UNSTABLE => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE,
@ -966,7 +1042,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
});
$this->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{
return Writer::create(Ids::BIG_DRIPLEAF)
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE)
->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false);
});
@ -1118,10 +1194,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing()));
});
$this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR, new Writer(Ids::CHEMISTRY_TABLE)));
$this->map(Blocks::CONCRETE_POWDER(), function(ConcretePowder $block) : Writer{
return Writer::create(Ids::CONCRETE_POWDER)
->writeColor($block->getColor());
});
$this->map(Blocks::CORAL_BLOCK(), function(CoralBlock $block) : Writer{
return Writer::create(Ids::CORAL_BLOCK)
->writeBool(StateNames::DEAD_BIT, $block->isDead())
@ -1192,7 +1264,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::END_PORTAL_FRAME(), function(EndPortalFrame $block) : Writer{
return Writer::create(Ids::END_PORTAL_FRAME)
->writeBool(StateNames::END_PORTAL_EYE_BIT, $block->hasEye())
->writeLegacyHorizontalFacing($block->getFacing());
->writeCardinalHorizontalFacing($block->getFacing());
});
$this->map(Blocks::END_ROD(), function(EndRod $block) : Writer{
return Writer::create(Ids::END_ROD)
@ -1279,7 +1351,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::LECTERN(), function(Lectern $block) : Writer{
return Writer::create(Ids::LECTERN)
->writeBool(StateNames::POWERED_BIT, $block->isProducingSignal())
->writeLegacyHorizontalFacing($block->getFacing());
->writeCardinalHorizontalFacing($block->getFacing());
});
$this->map(Blocks::LEVER(), function(Lever $block) : Writer{
return Writer::create(Ids::LEVER)
@ -1354,7 +1426,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{
return Writer::create(Ids::PINK_PETALS)
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeInt(StateNames::GROWTH, $block->getCount() - 1);
});
$this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK));
@ -1428,13 +1500,13 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create($block->isPowered() ? Ids::POWERED_COMPARATOR : Ids::UNPOWERED_COMPARATOR)
->writeBool(StateNames::OUTPUT_LIT_BIT, $block->isPowered())
->writeBool(StateNames::OUTPUT_SUBTRACT_BIT, $block->isSubtractMode())
->writeLegacyHorizontalFacing($block->getFacing());
->writeCardinalHorizontalFacing($block->getFacing());
});
$this->map(Blocks::REDSTONE_LAMP(), fn(RedstoneLamp $block) => new Writer($block->isPowered() ? Ids::LIT_REDSTONE_LAMP : Ids::REDSTONE_LAMP));
$this->map(Blocks::REDSTONE_ORE(), fn(RedstoneOre $block) => new Writer($block->isLit() ? Ids::LIT_REDSTONE_ORE : Ids::REDSTONE_ORE));
$this->map(Blocks::REDSTONE_REPEATER(), function(RedstoneRepeater $block) : Writer{
return Writer::create($block->isPowered() ? Ids::POWERED_REPEATER : Ids::UNPOWERED_REPEATER)
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeInt(StateNames::REPEATER_DELAY, $block->getDelay() - 1);
});
$this->map(Blocks::REDSTONE_TORCH(), function(RedstoneTorch $block) : Writer{
@ -1471,7 +1543,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
});
$this->map(Blocks::SMALL_DRIPLEAF(), function(SmallDripleaf $block) : Writer{
return Writer::create(Ids::SMALL_DRIPLEAF_BLOCK)
->writeLegacyHorizontalFacing($block->getFacing())
->writeCardinalHorizontalFacing($block->getFacing())
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
});
$this->map(Blocks::SMOKER(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::SMOKER, Ids::LIT_SMOKER));
@ -1507,18 +1579,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY);
});
$this->map(Blocks::SPRUCE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_SPRUCE));
$this->map(Blocks::STAINED_CLAY(), function(StainedHardenedClay $block) : Writer{
return Writer::create(Ids::STAINED_HARDENED_CLAY)
->writeColor($block->getColor());
});
$this->map(Blocks::STAINED_GLASS(), function(StainedGlass $block) : Writer{
return Writer::create(Ids::STAINED_GLASS)
->writeColor($block->getColor());
});
$this->map(Blocks::STAINED_GLASS_PANE(), function(StainedGlassPane $block) : Writer{
return Writer::create(Ids::STAINED_GLASS_PANE)
->writeColor($block->getColor());
});
$this->map(Blocks::STAINED_HARDENED_GLASS(), function(StainedHardenedGlass $block) : Writer{
return Writer::create(Ids::HARD_STAINED_GLASS)
->writeColor($block->getColor());

View File

@ -93,7 +93,7 @@ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeComparator(RedstoneComparator $block, BlockStateReader $in) : RedstoneComparator{
return $block
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setPowered($in->readBool(BlockStateNames::OUTPUT_LIT_BIT))
->setSubtractMode($in->readBool(BlockStateNames::OUTPUT_SUBTRACT_BIT));
}
@ -216,7 +216,7 @@ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeRepeater(RedstoneRepeater $block, BlockStateReader $in) : RedstoneRepeater{
return $block
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1);
}

View File

@ -293,7 +293,11 @@ final class BlockStateReader{
/** @throws BlockStateDeserializeException */
public function readSlabPosition() : SlabType{
return $this->readBool(BlockStateNames::TOP_SLOT_BIT) ? SlabType::TOP : SlabType::BOTTOM;
return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){
StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM,
StringValues::MC_VERTICAL_HALF_TOP => SlabType::TOP,
default => throw $this->badValueException(BlockStateNames::MC_VERTICAL_HALF, $rawValue, "Invalid slab position"),
};
}
/**

View File

@ -135,7 +135,7 @@ final class BlockStateSerializerHelper{
public static function encodeFurnace(Furnace $block, string $unlitId, string $litId) : BlockStateWriter{
return BlockStateWriter::create($block->isLit() ? $litId : $unlitId)
->writeHorizontalFacing($block->getFacing());
->writeCardinalHorizontalFacing($block->getFacing());
}
public static function encodeItemFrame(ItemFrame $block, string $id) : BlockStateWriter{
@ -209,9 +209,8 @@ final class BlockStateSerializerHelper{
public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : BlockStateWriter{
$slabType = $block->getSlabType();
return BlockStateWriter::create($slabType === SlabType::DOUBLE ? $doubleId : $singleId)
//this is (intentionally) also written for double slabs (as zero) to maintain bug parity with MCPE
->writeBool(BlockStateNames::TOP_SLOT_BIT, $slabType === SlabType::TOP);
->writeSlabPosition($slabType === SlabType::DOUBLE ? SlabType::BOTTOM : $slabType);
}
public static function encodeStairs(Stair $block, BlockStateWriter $out) : BlockStateWriter{

View File

@ -113,7 +113,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock) : void{
$this->map($singleId, fn(Reader $in) : Slab => $getBlock($in)->setSlabType($in->readSlabPosition()));
$this->map($doubleId, function(Reader $in) use ($getBlock) : Slab{
$in->ignored(StateNames::TOP_SLOT_BIT);
$in->ignored(StateNames::MC_VERTICAL_HALF);
return $getBlock($in)->setSlabType(SlabType::DOUBLE);
});
}
@ -288,6 +288,90 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
] as $id => $color){
$this->mapSimple($id, fn() => Blocks::CONCRETE()->setColor($color));
}
foreach([
Ids::BLACK_CONCRETE_POWDER => DyeColor::BLACK,
Ids::BLUE_CONCRETE_POWDER => DyeColor::BLUE,
Ids::BROWN_CONCRETE_POWDER => DyeColor::BROWN,
Ids::CYAN_CONCRETE_POWDER => DyeColor::CYAN,
Ids::GRAY_CONCRETE_POWDER => DyeColor::GRAY,
Ids::GREEN_CONCRETE_POWDER => DyeColor::GREEN,
Ids::LIGHT_BLUE_CONCRETE_POWDER => DyeColor::LIGHT_BLUE,
Ids::LIGHT_GRAY_CONCRETE_POWDER => DyeColor::LIGHT_GRAY,
Ids::LIME_CONCRETE_POWDER => DyeColor::LIME,
Ids::MAGENTA_CONCRETE_POWDER => DyeColor::MAGENTA,
Ids::ORANGE_CONCRETE_POWDER => DyeColor::ORANGE,
Ids::PINK_CONCRETE_POWDER => DyeColor::PINK,
Ids::PURPLE_CONCRETE_POWDER => DyeColor::PURPLE,
Ids::RED_CONCRETE_POWDER => DyeColor::RED,
Ids::WHITE_CONCRETE_POWDER => DyeColor::WHITE,
Ids::YELLOW_CONCRETE_POWDER => DyeColor::YELLOW,
] as $id => $color){
$this->mapSimple($id, fn() => Blocks::CONCRETE_POWDER()->setColor($color));
}
foreach([
Ids::BLACK_TERRACOTTA => DyeColor::BLACK,
Ids::BLUE_TERRACOTTA => DyeColor::BLUE,
Ids::BROWN_TERRACOTTA => DyeColor::BROWN,
Ids::CYAN_TERRACOTTA => DyeColor::CYAN,
Ids::GRAY_TERRACOTTA => DyeColor::GRAY,
Ids::GREEN_TERRACOTTA => DyeColor::GREEN,
Ids::LIGHT_BLUE_TERRACOTTA => DyeColor::LIGHT_BLUE,
Ids::LIGHT_GRAY_TERRACOTTA => DyeColor::LIGHT_GRAY,
Ids::LIME_TERRACOTTA => DyeColor::LIME,
Ids::MAGENTA_TERRACOTTA => DyeColor::MAGENTA,
Ids::ORANGE_TERRACOTTA => DyeColor::ORANGE,
Ids::PINK_TERRACOTTA => DyeColor::PINK,
Ids::PURPLE_TERRACOTTA => DyeColor::PURPLE,
Ids::RED_TERRACOTTA => DyeColor::RED,
Ids::WHITE_TERRACOTTA => DyeColor::WHITE,
Ids::YELLOW_TERRACOTTA => DyeColor::YELLOW,
] as $id => $color){
$this->mapSimple($id, fn() => Blocks::STAINED_CLAY()->setColor($color));
}
foreach([
Ids::BLACK_STAINED_GLASS => DyeColor::BLACK,
Ids::BLUE_STAINED_GLASS => DyeColor::BLUE,
Ids::BROWN_STAINED_GLASS => DyeColor::BROWN,
Ids::CYAN_STAINED_GLASS => DyeColor::CYAN,
Ids::GRAY_STAINED_GLASS => DyeColor::GRAY,
Ids::GREEN_STAINED_GLASS => DyeColor::GREEN,
Ids::LIGHT_BLUE_STAINED_GLASS => DyeColor::LIGHT_BLUE,
Ids::LIGHT_GRAY_STAINED_GLASS => DyeColor::LIGHT_GRAY,
Ids::LIME_STAINED_GLASS => DyeColor::LIME,
Ids::MAGENTA_STAINED_GLASS => DyeColor::MAGENTA,
Ids::ORANGE_STAINED_GLASS => DyeColor::ORANGE,
Ids::PINK_STAINED_GLASS => DyeColor::PINK,
Ids::PURPLE_STAINED_GLASS => DyeColor::PURPLE,
Ids::RED_STAINED_GLASS => DyeColor::RED,
Ids::WHITE_STAINED_GLASS => DyeColor::WHITE,
Ids::YELLOW_STAINED_GLASS => DyeColor::YELLOW,
] as $id => $color){
$this->mapSimple($id, fn() => Blocks::STAINED_GLASS()->setColor($color));
}
foreach([
Ids::BLACK_STAINED_GLASS_PANE => DyeColor::BLACK,
Ids::BLUE_STAINED_GLASS_PANE => DyeColor::BLUE,
Ids::BROWN_STAINED_GLASS_PANE => DyeColor::BROWN,
Ids::CYAN_STAINED_GLASS_PANE => DyeColor::CYAN,
Ids::GRAY_STAINED_GLASS_PANE => DyeColor::GRAY,
Ids::GREEN_STAINED_GLASS_PANE => DyeColor::GREEN,
Ids::LIGHT_BLUE_STAINED_GLASS_PANE => DyeColor::LIGHT_BLUE,
Ids::LIGHT_GRAY_STAINED_GLASS_PANE => DyeColor::LIGHT_GRAY,
Ids::LIME_STAINED_GLASS_PANE => DyeColor::LIME,
Ids::MAGENTA_STAINED_GLASS_PANE => DyeColor::MAGENTA,
Ids::ORANGE_STAINED_GLASS_PANE => DyeColor::ORANGE,
Ids::PINK_STAINED_GLASS_PANE => DyeColor::PINK,
Ids::PURPLE_STAINED_GLASS_PANE => DyeColor::PURPLE,
Ids::RED_STAINED_GLASS_PANE => DyeColor::RED,
Ids::WHITE_STAINED_GLASS_PANE => DyeColor::WHITE,
Ids::YELLOW_STAINED_GLASS_PANE => DyeColor::YELLOW,
] as $id => $color){
$this->mapSimple($id, fn() => Blocks::STAINED_GLASS_PANE()->setColor($color));
}
}
private function registerFlatCoralDeserializers() : void{
@ -781,7 +865,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
StringValues::DAMAGE_BROKEN => 0,
default => throw $in->badValueException(StateNames::DAMAGE, $value),
})
->setFacing($in->readLegacyHorizontalFacing());
->setFacing($in->readCardinalHorizontalFacing());
});
$this->map(Ids::BAMBOO, function(Reader $in) : Block{
return Blocks::BAMBOO()
@ -831,7 +915,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{
if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){
return Blocks::BIG_DRIPLEAF_HEAD()
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setLeafState(match($type = $in->readString(StateNames::BIG_DRIPLEAF_TILT)){
StringValues::BIG_DRIPLEAF_TILT_NONE => DripleafState::STABLE,
StringValues::BIG_DRIPLEAF_TILT_UNSTABLE => DripleafState::UNSTABLE,
@ -841,7 +925,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
}else{
$in->ignored(StateNames::BIG_DRIPLEAF_TILT);
return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readLegacyHorizontalFacing());
return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readCardinalHorizontalFacing());
}
});
$this->mapSlab(Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::BLACKSTONE_SLAB());
@ -849,7 +933,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BLACKSTONE_WALL(), $in));
$this->map(Ids::BLAST_FURNACE, function(Reader $in) : Block{
return Blocks::BLAST_FURNACE()
->setFacing($in->readHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setLit(false);
});
$this->map(Ids::BONE_BLOCK, function(Reader $in) : Block{
@ -935,10 +1019,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing()) :
Blocks::RED_TORCH()->setFacing($in->readTorchFacing());
});
$this->map(Ids::CONCRETE_POWDER, function(Reader $in) : Block{
return Blocks::CONCRETE_POWDER()
->setColor($in->readColor());
});
$this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE));
$this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
$this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
@ -1007,7 +1087,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::END_PORTAL_FRAME, function(Reader $in) : Block{
return Blocks::END_PORTAL_FRAME()
->setEye($in->readBool(StateNames::END_PORTAL_EYE_BIT))
->setFacing($in->readLegacyHorizontalFacing());
->setFacing($in->readCardinalHorizontalFacing());
});
$this->map(Ids::END_ROD, function(Reader $in) : Block{
return Blocks::END_ROD()
@ -1042,7 +1122,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
$this->map(Ids::FURNACE, function(Reader $in) : Block{
return Blocks::FURNACE()
->setFacing($in->readHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setLit(false);
});
$this->map(Ids::GLOW_LICHEN, fn(Reader $in) => Blocks::GLOW_LICHEN()->setFaces($in->readFacingFlags()));
@ -1084,7 +1164,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in));
$this->map(Ids::LECTERN, function(Reader $in) : Block{
return Blocks::LECTERN()
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setProducingSignal($in->readBool(StateNames::POWERED_BIT));
});
$this->map(Ids::LEVER, function(Reader $in) : Block{
@ -1113,13 +1193,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::LIGHT_WEIGHTED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeWeightedPressurePlate(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), $in));
$this->map(Ids::LIT_BLAST_FURNACE, function(Reader $in) : Block{
return Blocks::BLAST_FURNACE()
->setFacing($in->readHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setLit(true);
});
$this->map(Ids::LIT_DEEPSLATE_REDSTONE_ORE, fn() => Blocks::DEEPSLATE_REDSTONE_ORE()->setLit(true));
$this->map(Ids::LIT_FURNACE, function(Reader $in) : Block{
return Blocks::FURNACE()
->setFacing($in->readHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setLit(true);
});
$this->map(Ids::LIT_PUMPKIN, function(Reader $in) : Block{
@ -1136,7 +1216,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
$this->map(Ids::LIT_SMOKER, function(Reader $in) : Block{
return Blocks::SMOKER()
->setFacing($in->readHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setLit(true);
});
$this->map(Ids::LOOM, function(Reader $in) : Block{
@ -1180,7 +1260,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
//Pink petals only uses 0-3, but GROWTH state can go up to 7
$growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7);
return Blocks::PINK_PETALS()
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setCount(min($growth + 1, PinkPetals::MAX_COUNT));
});
$this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS());
@ -1354,12 +1434,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
});
$this->map(Ids::SMOKER, function(Reader $in) : Block{
return Blocks::SMOKER()
->setFacing($in->readHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setLit(false);
});
$this->map(Ids::SMALL_DRIPLEAF_BLOCK, function(Reader $in) : Block{
return Blocks::SMALL_DRIPLEAF()
->setFacing($in->readLegacyHorizontalFacing())
->setFacing($in->readCardinalHorizontalFacing())
->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT));
});
$this->mapStairs(Ids::SMOOTH_QUARTZ_STAIRS, fn() => Blocks::SMOOTH_QUARTZ_STAIRS());
@ -1388,18 +1468,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
default => throw $in->badValueException(StateNames::SPONGE_TYPE, $type),
});
});
$this->map(Ids::STAINED_GLASS, function(Reader $in) : Block{
return Blocks::STAINED_GLASS()
->setColor($in->readColor());
});
$this->map(Ids::STAINED_GLASS_PANE, function(Reader $in) : Block{
return Blocks::STAINED_GLASS_PANE()
->setColor($in->readColor());
});
$this->map(Ids::STAINED_HARDENED_CLAY, function(Reader $in) : Block{
return Blocks::STAINED_CLAY()
->setColor($in->readColor());
});
$this->map(Ids::STANDING_BANNER, function(Reader $in) : Block{
return Blocks::BANNER()
->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15));

View File

@ -245,9 +245,9 @@ final class BlockStateWriter{
/** @return $this */
public function writeSlabPosition(SlabType $slabType) : self{
$this->writeBool(BlockStateNames::TOP_SLOT_BIT, match($slabType){
SlabType::TOP => true,
SlabType::BOTTOM => false,
$this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){
SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP,
SlabType::BOTTOM => StringValues::MC_VERTICAL_HALF_BOTTOM,
default => throw new BlockStateSerializeException("Invalid slab type " . $slabType->name)
});
return $this;

View File

@ -125,6 +125,7 @@ final class ItemTypeNames{
public const COMPASS = "minecraft:compass";
public const COMPOUND = "minecraft:compound";
public const CONCRETE = "minecraft:concrete";
public const CONCRETE_POWDER = "minecraft:concrete_powder";
public const COOKED_BEEF = "minecraft:cooked_beef";
public const COOKED_CHICKEN = "minecraft:cooked_chicken";
public const COOKED_COD = "minecraft:cooked_cod";
@ -437,6 +438,9 @@ final class ItemTypeNames{
public const SPRUCE_SIGN = "minecraft:spruce_sign";
public const SPYGLASS = "minecraft:spyglass";
public const SQUID_SPAWN_EGG = "minecraft:squid_spawn_egg";
public const STAINED_GLASS = "minecraft:stained_glass";
public const STAINED_GLASS_PANE = "minecraft:stained_glass_pane";
public const STAINED_HARDENED_CLAY = "minecraft:stained_hardened_clay";
public const STICK = "minecraft:stick";
public const STONE_AXE = "minecraft:stone_axe";
public const STONE_HOE = "minecraft:stone_hoe";

View File

@ -79,7 +79,6 @@ use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\RequestAbilityPacket;
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket;
use pocketmine\network\mcpe\protocol\SetActorMotionPacket;
@ -116,7 +115,6 @@ use function fmod;
use function get_debug_type;
use function implode;
use function in_array;
use function is_bool;
use function is_infinite;
use function is_nan;
use function json_decode;
@ -224,11 +222,13 @@ class InGamePacketHandler extends PacketHandler{
$sprinting = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING);
$swimming = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING);
$gliding = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING);
$flying = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_FLYING, PlayerAuthInputFlags::STOP_FLYING);
$mismatch =
($sneaking !== null && !$this->player->toggleSneak($sneaking)) |
($sprinting !== null && !$this->player->toggleSprint($sprinting)) |
($swimming !== null && !$this->player->toggleSwim($swimming)) |
($gliding !== null && !$this->player->toggleGlide($gliding));
($gliding !== null && !$this->player->toggleGlide($gliding)) |
($flying !== null && !$this->player->toggleFlight($flying));
if((bool) $mismatch){
$this->player->sendData([$this->player]);
}
@ -1032,22 +1032,4 @@ class InGamePacketHandler extends PacketHandler{
$this->player->emote($packet->getEmoteId());
return true;
}
public function handleRequestAbility(RequestAbilityPacket $packet) : bool{
if($packet->getAbilityId() === RequestAbilityPacket::ABILITY_FLYING){
$isFlying = $packet->getAbilityValue();
if(!is_bool($isFlying)){
throw new PacketHandlingException("Flying ability should always have a bool value");
}
if($isFlying !== $this->player->isFlying()){
if(!$this->player->toggleFlight($isFlying)){
$this->session->syncAbilities($this->player);
}
}
return true;
}
return false;
}
}

View File

@ -80,7 +80,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
);
}, $this->resourcePackManager->getResourceStack());
//TODO: support forcing server packs
$this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false));
$this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false, []));
$this->session->getLogger()->debug("Waiting for client to accept resource packs");
}

View File

@ -37,8 +37,8 @@ use function preg_match;
*/
trait RegistryTrait{
/**
* @var object[]
* @phpstan-var array<string, object>
* @var object[]|null
* @phpstan-var array<string, object>|null
*/
private static $members = null;
@ -54,6 +54,9 @@ trait RegistryTrait{
* @throws \InvalidArgumentException
*/
private static function _registryRegister(string $name, object $member) : void{
if(self::$members === null){
throw new AssumptionFailedError("Cannot register members outside of " . self::class . "::setup()");
}
self::verifyName($name);
$upperName = mb_strtoupper($name);
if(isset(self::$members[$upperName])){
@ -86,6 +89,9 @@ trait RegistryTrait{
*/
private static function _registryFromString(string $name) : object{
self::checkInit();
if(self::$members === null){
throw new AssumptionFailedError(self::class . "::checkInit() did not initialize self::\$members correctly");
}
$upperName = mb_strtoupper($name);
if(!isset(self::$members[$upperName])){
throw new \InvalidArgumentException("No such registry member: " . self::class . "::" . $upperName);
@ -118,6 +124,6 @@ trait RegistryTrait{
*/
private static function _registryGetAll() : array{
self::checkInit();
return array_map(self::preprocessMember(...), self::$members);
return array_map(self::preprocessMember(...), self::$members ?? throw new AssumptionFailedError(self::class . "::checkInit() did not initialize self::\$members correctly"));
}
}

View File

@ -27,13 +27,13 @@ declare(strict_types=1);
*/
namespace pocketmine\wizard;
use pocketmine\data\java\GameModeIdMap;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\lang\Language;
use pocketmine\lang\LanguageNotFoundException;
use pocketmine\lang\Translatable;
use pocketmine\player\GameMode;
use pocketmine\Server;
use pocketmine\ServerProperties;
use pocketmine\utils\Config;
use pocketmine\utils\Internet;
use pocketmine\utils\InternetException;
@ -48,8 +48,11 @@ use const PHP_EOL;
use const STDIN;
class SetupWizard{
/** @deprecated */
public const DEFAULT_NAME = Server::DEFAULT_SERVER_NAME;
/** @deprecated */
public const DEFAULT_PORT = Server::DEFAULT_PORT_IPV4;
/** @deprecated */
public const DEFAULT_PLAYERS = Server::DEFAULT_MAX_PLAYERS;
private Language $lang;
@ -91,7 +94,7 @@ class SetupWizard{
//this has to happen here to prevent user avoiding agreeing to license
$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
$config->set("language", $lang);
$config->set(ServerProperties::LANGUAGE, $lang);
$config->save();
if(strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::skip_installer()), "n", "y/N")) === "y"){
@ -101,10 +104,12 @@ class SetupWizard{
$this->writeLine();
$this->welcome();
$this->generateBaseConfig();
$this->generateUserFiles();
$this->networkFunctions();
$this->generateBaseConfig($config);
$this->generateUserFiles($config);
$this->networkFunctions($config);
$config->save();
$this->printIpDetails();
$this->endWizard();
@ -151,34 +156,33 @@ LICENSE;
}
}
private function generateBaseConfig() : void{
$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
$config->set("motd", ($name = $this->getInput($this->lang->translate(KnownTranslationFactory::name_your_server()), self::DEFAULT_NAME)));
$config->set("server-name", $name);
private function generateBaseConfig(Config $config) : void{
$config->set(ServerProperties::MOTD, ($name = $this->getInput($this->lang->translate(KnownTranslationFactory::name_your_server()), Server::DEFAULT_SERVER_NAME)));
$this->message($this->lang->translate(KnownTranslationFactory::port_warning()));
$config->set("server-port", $this->askPort(KnownTranslationFactory::server_port_v4(), Server::DEFAULT_PORT_IPV4));
$config->set("server-portv6", $this->askPort(KnownTranslationFactory::server_port_v6(), Server::DEFAULT_PORT_IPV6));
$config->set(ServerProperties::SERVER_PORT_IPV4, $this->askPort(KnownTranslationFactory::server_port_v4(), Server::DEFAULT_PORT_IPV4));
$config->set(ServerProperties::SERVER_PORT_IPV6, $this->askPort(KnownTranslationFactory::server_port_v6(), Server::DEFAULT_PORT_IPV6));
$this->message($this->lang->translate(KnownTranslationFactory::gamemode_info()));
do{
//TODO: drop the usage of internal Mojang IDs for this - we really just need a set of options to choose from
$gamemode = GameModeIdMap::getInstance()->fromId((int) $this->getInput($this->lang->translate(KnownTranslationFactory::default_gamemode()), (string) GameModeIdMap::getInstance()->toId(GameMode::SURVIVAL)));
$input = (int) $this->getInput($this->lang->translate(KnownTranslationFactory::default_gamemode()), "0");
$gamemode = match($input){
0 => GameMode::SURVIVAL,
1 => GameMode::CREATIVE,
default => null
};
}while($gamemode === null);
//TODO: this probably shouldn't use the enum name directly
$config->set("gamemode", $gamemode->name);
$config->set(ServerProperties::GAME_MODE, $gamemode->name);
$config->set("max-players", (int) $this->getInput($this->lang->translate(KnownTranslationFactory::max_players()), (string) self::DEFAULT_PLAYERS));
$config->set(ServerProperties::MAX_PLAYERS, (int) $this->getInput($this->lang->translate(KnownTranslationFactory::max_players()), (string) Server::DEFAULT_MAX_PLAYERS));
$config->set("view-distance", (int) $this->getInput($this->lang->translate(KnownTranslationFactory::view_distance()), (string) Server::DEFAULT_MAX_VIEW_DISTANCE));
$config->save();
$config->set(ServerProperties::VIEW_DISTANCE, (int) $this->getInput($this->lang->translate(KnownTranslationFactory::view_distance()), (string) Server::DEFAULT_MAX_VIEW_DISTANCE));
}
private function generateUserFiles() : void{
private function generateUserFiles(Config $config) : void{
$this->message($this->lang->translate(KnownTranslationFactory::op_info()));
$op = strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::op_who()), ""));
@ -192,27 +196,22 @@ LICENSE;
$this->message($this->lang->translate(KnownTranslationFactory::whitelist_info()));
$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
if(strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::whitelist_enable()), "n", "y/N")) === "y"){
$this->error($this->lang->translate(KnownTranslationFactory::whitelist_warning()));
$config->set("white-list", true);
$config->set(ServerProperties::WHITELIST, true);
}else{
$config->set("white-list", false);
$config->set(ServerProperties::WHITELIST, false);
}
$config->save();
}
private function networkFunctions() : void{
$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
private function networkFunctions(Config $config) : void{
$this->error($this->lang->translate(KnownTranslationFactory::query_warning1()));
$this->error($this->lang->translate(KnownTranslationFactory::query_warning2()));
if(strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::query_disable()), "n", "y/N")) === "y"){
$config->set("enable-query", false);
$config->set(ServerProperties::ENABLE_QUERY, false);
}else{
$config->set("enable-query", true);
$config->set(ServerProperties::ENABLE_QUERY, true);
}
$config->save();
}
private function printIpDetails() : void{

View File

@ -51,12 +51,12 @@ use function time;
class BedrockWorldData extends BaseNbtWorldData{
public const CURRENT_STORAGE_VERSION = 10;
public const CURRENT_STORAGE_NETWORK_VERSION = 594;
public const CURRENT_STORAGE_NETWORK_VERSION = 618;
public const CURRENT_CLIENT_VERSION_TARGET = [
1, //major
20, //minor
10, //patch
1, //revision
30, //patch
2, //revision
0 //is beta
];