Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor 2023-10-16 21:29:11 +01:00
commit d4d7d02067
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
51 changed files with 1199 additions and 191 deletions

View File

@ -12,16 +12,16 @@ jobs:
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -53,7 +53,7 @@ jobs:
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
- name: Build image for tag
uses: docker/build-push-action@v4.1.1
uses: docker/build-push-action@v5.0.0
with:
push: true
context: ./pocketmine-mp
@ -66,7 +66,7 @@ jobs:
- name: Build image for major tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v4.1.1
uses: docker/build-push-action@v5.0.0
with:
push: true
context: ./pocketmine-mp
@ -79,7 +79,7 @@ jobs:
- name: Build image for minor tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v4.1.1
uses: docker/build-push-action@v5.0.0
with:
push: true
context: ./pocketmine-mp
@ -92,7 +92,7 @@ jobs:
- name: Build image for latest tag
if: steps.channel.outputs.CHANNEL == 'stable'
uses: docker/build-push-action@v4.1.1
uses: docker/build-push-action@v5.0.0
with:
push: true
context: ./pocketmine-mp

View File

@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.25.5
uses: shivammathur/setup-php@2.26.0
with:
php-version: 8.1

View File

@ -20,7 +20,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.25.5
uses: shivammathur/setup-php@2.26.0
with:
php-version: ${{ matrix.php-version }}

View File

@ -173,7 +173,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.25.5
uses: shivammathur/setup-php@2.26.0
with:
php-version: 8.1
tools: php-cs-fixer:3.17

@ -1 +1 @@
Subproject commit 3331f8c0d50075eee250ac4b3a8517a12186137a
Subproject commit a34e48e7da753b633ffaa4a4f9516eae4bb97baa

View File

@ -52,10 +52,10 @@
"symfony/filesystem": "~6.3.0"
},
"require-dev": {
"phpstan/phpstan": "1.10.35",
"phpstan/phpstan": "1.10.38",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^10.1"
"phpunit/phpunit": "~10.3.0 || ~10.2.0 || ~10.1.0"
},
"autoload": {
"psr-4": {

76
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": "2028bfaf32594237de07fc60f68df728",
"content-hash": "2282bf7835c1ced757460b083813e092",
"packages": [
{
"name": "adhocore/json-comment",
@ -148,16 +148,16 @@
},
{
"name": "pocketmine/bedrock-data",
"version": "2.5.0+bedrock-1.20.30",
"version": "2.5.1+bedrock-1.20.30",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "e920209393a8bf6cb15fb40c3f3149aaf8e1a2b9"
"reference": "8f9d96047a731c37b18b28c2bfcdfa2705bb303f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e920209393a8bf6cb15fb40c3f3149aaf8e1a2b9",
"reference": "e920209393a8bf6cb15fb40c3f3149aaf8e1a2b9",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/8f9d96047a731c37b18b28c2bfcdfa2705bb303f",
"reference": "8f9d96047a731c37b18b28c2bfcdfa2705bb303f",
"shasum": ""
},
"type": "library",
@ -168,9 +168,9 @@
"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.30"
"source": "https://github.com/pmmp/BedrockData/tree/2.5.1+bedrock-1.20.30"
},
"time": "2023-09-20T16:34:21+00:00"
"time": "2023-09-27T11:40:15+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
@ -1378,16 +1378,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.35",
"version": "1.10.38",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3"
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e730e5facb75ffe09dfb229795e8c01a459f26c3",
"reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5302bb402c57f00fb3c2c015bac86e0827e4b691",
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691",
"shasum": ""
},
"require": {
@ -1436,20 +1436,20 @@
"type": "tidelift"
}
],
"time": "2023-09-19T15:27:56+00:00"
"time": "2023-10-06T14:19:14+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.3.14",
"version": "1.3.15",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "614acc10c522e319639bf38b0698a4a566665f04"
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/614acc10c522e319639bf38b0698a4a566665f04",
"reference": "614acc10c522e319639bf38b0698a4a566665f04",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
"reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a",
"shasum": ""
},
"require": {
@ -1486,9 +1486,9 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.14"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15"
},
"time": "2023-08-25T09:46:39+00:00"
"time": "2023-10-09T18:58:39+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
@ -1541,16 +1541,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.6",
"version": "10.1.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "56f33548fe522c8d82da7ff3824b42829d324364"
"reference": "355324ca4980b8916c18b9db29f3ef484078f26e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/56f33548fe522c8d82da7ff3824b42829d324364",
"reference": "56f33548fe522c8d82da7ff3824b42829d324364",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e",
"reference": "355324ca4980b8916c18b9db29f3ef484078f26e",
"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.6"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7"
},
"funding": [
{
@ -1615,7 +1615,7 @@
"type": "github"
}
],
"time": "2023-09-19T04:59:03+00:00"
"time": "2023-10-04T15:34:17+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -2207,16 +2207,16 @@
},
{
"name": "sebastian/complexity",
"version": "3.0.1",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "c70b73893e10757af9c6a48929fa6a333b56a97a"
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c70b73893e10757af9c6a48929fa6a333b56a97a",
"reference": "c70b73893e10757af9c6a48929fa6a333b56a97a",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957",
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957",
"shasum": ""
},
"require": {
@ -2229,7 +2229,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.0-dev"
"dev-main": "3.1-dev"
}
},
"autoload": {
@ -2253,7 +2253,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues",
"security": "https://github.com/sebastianbergmann/complexity/security/policy",
"source": "https://github.com/sebastianbergmann/complexity/tree/3.0.1"
"source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0"
},
"funding": [
{
@ -2261,7 +2261,7 @@
"type": "github"
}
],
"time": "2023-08-31T09:55:53+00:00"
"time": "2023-09-28T11:50:59+00:00"
},
{
"name": "sebastian/diff",
@ -2396,16 +2396,16 @@
},
{
"name": "sebastian/exporter",
"version": "5.1.0",
"version": "5.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344"
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c3fa8483f9539b190f7cd4bfc4a07631dd1df344",
"reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc",
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc",
"shasum": ""
},
"require": {
@ -2419,7 +2419,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "5.0-dev"
"dev-main": "5.1-dev"
}
},
"autoload": {
@ -2462,7 +2462,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.0"
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1"
},
"funding": [
{
@ -2470,7 +2470,7 @@
"type": "github"
}
],
"time": "2023-09-18T07:15:37+00:00"
"time": "2023-09-24T13:22:09+00:00"
},
{
"name": "sebastian/global-state",

View File

@ -0,0 +1,133 @@
<?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;
use pocketmine\block\utils\AmethystTrait;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\block\utils\FortuneDropHelper;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
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\utils\AssumptionFailedError;
use pocketmine\world\BlockTransaction;
final class AmethystCluster extends Transparent{
use AmethystTrait;
use AnyFacingTrait;
public const STAGE_SMALL_BUD = 0;
public const STAGE_MEDIUM_BUD = 1;
public const STAGE_LARGE_BUD = 2;
public const STAGE_CLUSTER = 3;
private int $stage = self::STAGE_CLUSTER;
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
$w->boundedInt(2, self::STAGE_SMALL_BUD, self::STAGE_CLUSTER, $this->stage);
}
public function getStage() : int{ return $this->stage; }
public function setStage(int $stage) : self{
if($stage < self::STAGE_SMALL_BUD || $stage > self::STAGE_CLUSTER){
throw new \InvalidArgumentException("Size must be in range " . self::STAGE_SMALL_BUD . " ... " . self::STAGE_CLUSTER);
}
$this->stage = $stage;
return $this;
}
public function getLightLevel() : int{
return match($this->stage){
self::STAGE_SMALL_BUD => 1,
self::STAGE_MEDIUM_BUD => 2,
self::STAGE_LARGE_BUD => 4,
self::STAGE_CLUSTER => 5,
default => throw new AssumptionFailedError("Invalid stage $this->stage"),
};
}
protected function recalculateCollisionBoxes() : array{
$myAxis = Facing::axis($this->facing);
$box = AxisAlignedBB::one();
foreach([Axis::Y, Axis::Z, Axis::X] as $axis){
if($axis === $myAxis){
continue;
}
$box->squash($axis, $this->stage === self::STAGE_SMALL_BUD ? 4 / 16 : 3 / 16);
}
$box->trim($this->facing, 1 - ($this->stage === self::STAGE_CLUSTER ? 7 / 16 : ($this->stage + 3) / 16));
return [$box];
}
private function canBeSupportedAt(Block $block, int $facing) : bool{
return $block->getAdjacentSupportType($facing) === SupportType::FULL;
}
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{
if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
return false;
}
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function isAffectedBySilkTouch() : bool{
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{
if($this->stage === self::STAGE_CLUSTER){
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 4, maxBase: 4))];
}
return [];
}
public function getDropsForIncompatibleTool(Item $item) : array{
if($this->stage === self::STAGE_CLUSTER){
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 2, maxBase: 2))];
}
return [];
}
}

View File

@ -562,10 +562,10 @@ final class BlockTypeIds{
public const WEIGHTED_PRESSURE_PLATE_HEAVY = 10532;
public const WEIGHTED_PRESSURE_PLATE_LIGHT = 10533;
public const WHEAT = 10534;
public const BUDDING_AMETHYST = 10535;
public const WHITE_TULIP = 10536;
public const WOOL = 10537;
public const AMETHYST_CLUSTER = 10538;
public const GLAZED_TERRACOTTA = 10539;
public const AMETHYST = 10540;
public const ANCIENT_DEBRIS = 10541;
@ -737,8 +737,16 @@ final class BlockTypeIds{
public const BIG_DRIPLEAF_HEAD = 10707;
public const BIG_DRIPLEAF_STEM = 10708;
public const PINK_PETALS = 10709;
public const CRIMSON_ROOTS = 10710;
public const WARPED_ROOTS = 10711;
public const CHISELED_BOOKSHELF = 10712;
public const TORCHFLOWER = 10713;
public const TORCHFLOWER_CROP = 10714;
public const PITCHER_PLANT = 10715;
public const PITCHER_CROP = 10716;
public const DOUBLE_PITCHER_CROP = 10717;
public const FIRST_UNUSED_BLOCK_ID = 10710;
public const FIRST_UNUSED_BLOCK_ID = 10718;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

View File

@ -45,7 +45,7 @@ class BrewingStand extends Transparent{
protected array $slots = [];
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->brewingStandSlots($this->slots);
$w->enumSet($this->slots, BrewingStandSlot::cases());
}
protected function recalculateCollisionBoxes() : array{

View File

@ -0,0 +1,68 @@
<?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;
use pocketmine\block\utils\AmethystTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use function array_rand;
use function mt_rand;
final class BuddingAmethyst extends Opaque{
use AmethystTrait;
public function ticksRandomly() : bool{
return true;
}
public function onRandomTick() : void{
if(mt_rand(1, 5) === 1){
$face = Facing::ALL[array_rand(Facing::ALL)];
$adjacent = $this->getSide($face);
//TODO: amethyst buds can spawn in water - we need waterlogging support for this
$newStage = null;
if($adjacent->getTypeId() === BlockTypeIds::AIR){
$newStage = AmethystCluster::STAGE_SMALL_BUD;
}elseif(
$adjacent->getTypeId() === BlockTypeIds::AMETHYST_CLUSTER &&
$adjacent instanceof AmethystCluster &&
$adjacent->getStage() < AmethystCluster::STAGE_CLUSTER &&
$adjacent->getFacing() === $face
){
$newStage = $adjacent->getStage() + 1;
}
if($newStage !== null){
BlockEventHelper::grow($adjacent, VanillaBlocks::AMETHYST_CLUSTER()->setStage($newStage)->setFacing($face), null);
}
}
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
}

View File

@ -95,10 +95,10 @@ class Cactus extends Transparent{
}
}
$this->age = 0;
$world->setBlock($this->position, $this);
$world->setBlock($this->position, $this, update: false);
}else{
++$this->age;
$world->setBlock($this->position, $this);
$world->setBlock($this->position, $this, update: false);
}
}
}

View File

@ -0,0 +1,134 @@
<?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;
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Book;
use pocketmine\item\EnchantedBook;
use pocketmine\item\Item;
use pocketmine\item\WritableBookBase;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function spl_object_id;
class ChiseledBookshelf extends Opaque{
use HorizontalFacingTrait;
use FacesOppositePlacingPlayerTrait;
/**
* @var ChiseledBookshelfSlot[]
* @phpstan-var array<int, ChiseledBookshelfSlot>
*/
private array $slots = [];
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
}
/**
* Returns whether the given slot is displayed as occupied.
* This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
*/
public function hasSlot(ChiseledBookshelfSlot $slot) : bool{
return isset($this->slots[spl_object_id($slot)]);
}
/**
* Sets whether the given slot is displayed as occupied.
*
* This doesn't modify the bookshelf's inventory, so you can use this to make invisible
* books or display books that aren't actually in the bookshelf.
*
* To modify the contents of the bookshelf inventory, access the tile inventory.
*
* @return $this
*/
public function setSlot(ChiseledBookshelfSlot $slot, bool $occupied) : self{
if($occupied){
$this->slots[spl_object_id($slot)] = $slot;
}else{
unset($this->slots[spl_object_id($slot)]);
}
return $this;
}
/**
* Returns which slots of the bookshelf are displayed as occupied.
* As above, these values do not necessarily reflect the contents of the bookshelf inventory,
* although they usually will unless modified by plugins.
*
* @return ChiseledBookshelfSlot[]
* @phpstan-return array<int, ChiseledBookshelfSlot>
*/
public function getSlots() : array{
return $this->slots;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($face !== $this->facing){
return false;
}
$x = Facing::axis($face) === Axis::X ? $clickVector->getZ() : $clickVector->getX();
$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
Facing::isPositive(Facing::rotateY($face, true)) ? 1 - $x : $x,
$clickVector->y
);
$tile = $this->position->getWorld()->getTile($this->position);
if(!$tile instanceof TileChiseledBookshelf){
return false;
}
$inventory = $tile->getInventory();
if(!$inventory->isSlotEmpty($slot->value)){
$returnedItems[] = $inventory->getItem($slot->value);
$inventory->clear($slot->value);
$this->setSlot($slot, false);
}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
//TODO: type tags like blocks would be better for this
$inventory->setItem($slot->value, $item->pop());
$this->setSlot($slot, true);
}else{
return true;
}
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
public function isAffectedBySilkTouch() : bool{
return true;
}
}

View File

@ -83,8 +83,6 @@ final class ChorusFlower extends Flowable{
$this->position->getWorld()->useBreakOn($this->position);
}
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
/**
* @phpstan-return array{int, bool}
*/
@ -159,6 +157,8 @@ final class ChorusFlower extends Flowable{
return $tx;
}
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
public function onRandomTick() : void{
$world = $this->position->getWorld();

View File

@ -99,7 +99,7 @@ class CocoaBlock extends Transparent{
}
public function ticksRandomly() : bool{
return true;
return $this->age < self::MAX_AGE;
}
public function onRandomTick() : void{

View File

@ -62,7 +62,7 @@ abstract class Crops extends Flowable{
}
public function ticksRandomly() : bool{
return true;
return $this->age < self::MAX_AGE;
}
public function onRandomTick() : void{

View File

@ -0,0 +1,110 @@
<?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;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Fertilizer;
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 function mt_rand;
final class DoublePitcherCrop extends DoublePlant{
use AgeableTrait {
describeBlockOnlyState as describeAge;
}
public const MAX_AGE = 1;
public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
parent::describeBlockOnlyState($w);
$this->describeAge($w);
}
protected function recalculateCollisionBoxes() : array{
if($this->top){
return [];
}
//the pod exists only in the bottom half of the plant
return [
AxisAlignedBB::one()
->trim(Facing::UP, 11 / 16)
->squash(Axis::X, 3 / 16)
->squash(Axis::Z, 3 / 16)
->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
];
}
private function grow(?Player $player) : bool{
if($this->age >= self::MAX_AGE){
return false;
}
$bottom = $this->top ? $this->getSide(Facing::DOWN) : $this;
$top = $this->top ? $this : $this->getSide(Facing::UP);
if($top->getTypeId() !== BlockTypeIds::AIR && !$top->hasSameTypeId($this)){
return false;
}
$newState = (clone $this)->setAge($this->age + 1);
$tx = new BlockTransaction($this->position->getWorld());
$tx->addBlock($bottom->position, (clone $newState)->setTop(false));
$tx->addBlock($top->position, (clone $newState)->setTop(true));
$ev = new StructureGrowEvent($bottom, $tx, $player);
$ev->call();
return !$ev->isCancelled() && $tx->apply();
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer && $this->grow($player)){
$item->pop();
return true;
}
return false;
}
public function ticksRandomly() : bool{
return $this->age < self::MAX_AGE && !$this->top;
}
public function onRandomTick() : void{
//TODO: the growth speed is influenced by farmland and nearby crops
//only the bottom half of the plant can grow randomly
if(mt_rand(0, 2) === 0 && !$this->top){
$this->grow(null);
}
}
}

View File

@ -110,7 +110,7 @@ class Leaves extends Transparent{
}
public function ticksRandomly() : bool{
return true;
return !$this->noDecay && $this->checkDecay;
}
public function onRandomTick() : void{

39
src/block/NetherRoots.php Normal file
View File

@ -0,0 +1,39 @@
<?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;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\math\Facing;
final class NetherRoots extends Flowable{
use StaticSupportTrait;
private function canBeSupportedAt(Block $block) : bool{
//TODO: nylium, moss
$supportBlock = $block->getSide(Facing::DOWN);
return
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
$supportBlock->hasTypeTag(BlockTypeTags::MUD);
}
}

View File

@ -59,10 +59,6 @@ class NetherVines extends Flowable{
return true;
}
public function ticksRandomly() : bool{
return true;
}
public function canClimb() : bool{
return true;
}
@ -98,8 +94,12 @@ class NetherVines extends Flowable{
return false;
}
public function ticksRandomly() : bool{
return $this->age < self::MAX_AGE;
}
public function onRandomTick() : void{
if(mt_rand(1, 10) === 1 && $this->age < self::MAX_AGE){
if($this->age < self::MAX_AGE && mt_rand(1, 10) === 1){
if($this->getSide($this->growthFace)->canBeReplaced()){
$this->grow(null);
}

View File

@ -42,7 +42,7 @@ class NetherWartPlant extends Flowable{
}
public function ticksRandomly() : bool{
return true;
return $this->age < self::MAX_AGE;
}
public function onRandomTick() : void{

105
src/block/PitcherCrop.php Normal file
View File

@ -0,0 +1,105 @@
<?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;
use pocketmine\block\utils\AgeableTrait;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\event\block\StructureGrowEvent;
use pocketmine\item\Fertilizer;
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 function mt_rand;
final class PitcherCrop extends Flowable{
use AgeableTrait;
use StaticSupportTrait;
public const MAX_AGE = 2;
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
}
protected function recalculateCollisionBoxes() : array{
$widthTrim = $this->age === 0 ? 5 : 3;
$heightTrim = $this->age === 0 ? 13 : 11;
return [
AxisAlignedBB::one()
->trim(Facing::UP, $heightTrim / 16)
->squash(Axis::X, $widthTrim / 16)
->squash(Axis::Z, $widthTrim / 16)
->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
];
}
private function grow(?Player $player) : bool{
if($this->age > self::MAX_AGE){
return false;
}
if($this->age === self::MAX_AGE){
$up = $this->getSide(Facing::UP);
if($up->getTypeId() !== BlockTypeIds::AIR){
return false;
}
$tx = new BlockTransaction($this->position->getWorld());
$tx->addBlock($this->position, VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(false));
$tx->addBlock($this->position->up(), VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(true));
$ev = new StructureGrowEvent($this, $tx, $player);
$ev->call();
return !$ev->isCancelled() && $tx->apply();
}
return BlockEventHelper::grow($this, (clone $this)->setAge($this->age + 1), $player);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer && $this->grow($player)){
$item->pop();
return true;
}
return false;
}
public function ticksRandomly() : bool{
return true;
}
public function onRandomTick() : void{
//TODO: the growth speed is influenced by farmland and nearby crops
if(mt_rand(0, 2) === 0){
$this->grow(null);
}
}
}

View File

@ -70,7 +70,7 @@ class RedstoneOre extends Opaque{
}
public function ticksRandomly() : bool{
return true;
return $this->lit;
}
public function onRandomTick() : void{

View File

@ -102,7 +102,7 @@ class SweetBerryBush extends Flowable{
}
public function ticksRandomly() : bool{
return true;
return $this->age < self::STAGE_MATURE;
}
public function onRandomTick() : void{

View File

@ -0,0 +1,90 @@
<?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;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\StaticSupportTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
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 function mt_rand;
final class TorchflowerCrop extends Flowable{
use StaticSupportTrait;
private bool $ready = false;
public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->bool($this->ready);
}
public function isReady() : bool{ return $this->ready; }
public function setReady(bool $ready) : self{
$this->ready = $ready;
return $this;
}
private function canBeSupportedAt(Block $block) : bool{
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
}
private function getNextState() : Block{
if($this->ready){
return VanillaBlocks::TORCHFLOWER();
}else{
return VanillaBlocks::TORCHFLOWER_CROP()->setReady(true);
}
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer){
if(BlockEventHelper::grow($this, $this->getNextState(), $player)){
$item->pop();
}
return true;
}
return false;
}
public function ticksRandomly() : bool{
return true;
}
public function onRandomTick() : void{
if(mt_rand(0, 2) === 1){
BlockEventHelper::grow($this, $this->getNextState(), null);
}
}
public function asItem() : Item{
return VanillaItems::TORCHFLOWER_SEEDS();
}
}

View File

@ -38,6 +38,7 @@ use pocketmine\block\tile\BlastFurnace as TileBlastFurnace;
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
use pocketmine\block\tile\Cauldron as TileCauldron;
use pocketmine\block\tile\Chest as TileChest;
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
use pocketmine\block\tile\Comparator as TileComparator;
use pocketmine\block\tile\DaylightSensor as TileDaylightSensor;
use pocketmine\block\tile\EnchantTable as TileEnchantingTable;
@ -54,19 +55,16 @@ use pocketmine\block\tile\NormalFurnace as TileNormalFurnace;
use pocketmine\block\tile\Note as TileNote;
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
use pocketmine\block\tile\Smoker as TileSmoker;
use pocketmine\block\utils\AmethystTrait;
use pocketmine\block\utils\LeavesType;
use pocketmine\block\utils\SaplingType;
use pocketmine\block\utils\WoodType;
use pocketmine\crafting\FurnaceType;
use pocketmine\entity\projectile\Projectile;
use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags;
use pocketmine\item\Item;
use pocketmine\item\ToolTier;
use pocketmine\math\Facing;
use pocketmine\math\RayTraceResult;
use pocketmine\utils\CloningRegistryTrait;
use pocketmine\world\sound\AmethystBlockChimeSound;
use pocketmine\world\sound\BlockPunchSound;
use function mb_strtolower;
use function strtolower;
@ -96,6 +94,7 @@ use function strtolower;
* @method static Flower ALLIUM()
* @method static MushroomStem ALL_SIDED_MUSHROOM_STEM()
* @method static Opaque AMETHYST()
* @method static AmethystCluster AMETHYST_CLUSTER()
* @method static Opaque ANCIENT_DEBRIS()
* @method static Opaque ANDESITE()
* @method static Slab ANDESITE_SLAB()
@ -149,6 +148,7 @@ use function strtolower;
* @method static Wall BRICK_WALL()
* @method static BrownMushroom BROWN_MUSHROOM()
* @method static BrownMushroomBlock BROWN_MUSHROOM_BLOCK()
* @method static BuddingAmethyst BUDDING_AMETHYST()
* @method static Cactus CACTUS()
* @method static Cake CAKE()
* @method static CakeWithCandle CAKE_WITH_CANDLE()
@ -178,6 +178,7 @@ use function strtolower;
* @method static WallSign CHERRY_WALL_SIGN()
* @method static Wood CHERRY_WOOD()
* @method static Chest CHEST()
* @method static ChiseledBookshelf CHISELED_BOOKSHELF()
* @method static Opaque CHISELED_DEEPSLATE()
* @method static Opaque CHISELED_NETHER_BRICKS()
* @method static Opaque CHISELED_POLISHED_BLACKSTONE()
@ -222,6 +223,7 @@ use function strtolower;
* @method static Wood CRIMSON_HYPHAE()
* @method static Planks CRIMSON_PLANKS()
* @method static WoodenPressurePlate CRIMSON_PRESSURE_PLATE()
* @method static NetherRoots CRIMSON_ROOTS()
* @method static FloorSign CRIMSON_SIGN()
* @method static WoodenSlab CRIMSON_SLAB()
* @method static WoodenStairs CRIMSON_STAIRS()
@ -282,6 +284,7 @@ use function strtolower;
* @method static Stair DIORITE_STAIRS()
* @method static Wall DIORITE_WALL()
* @method static Dirt DIRT()
* @method static DoublePitcherCrop DOUBLE_PITCHER_CROP()
* @method static DoubleTallGrass DOUBLE_TALLGRASS()
* @method static DragonEgg DRAGON_EGG()
* @method static DriedKelp DRIED_KELP()
@ -577,6 +580,8 @@ use function strtolower;
* @method static DoublePlant PEONY()
* @method static PinkPetals PINK_PETALS()
* @method static Flower PINK_TULIP()
* @method static PitcherCrop PITCHER_CROP()
* @method static DoublePlant PITCHER_PLANT()
* @method static Podzol PODZOL()
* @method static Opaque POLISHED_ANDESITE()
* @method static Slab POLISHED_ANDESITE_SLAB()
@ -724,6 +729,8 @@ use function strtolower;
* @method static TintedGlass TINTED_GLASS()
* @method static TNT TNT()
* @method static Torch TORCH()
* @method static Flower TORCHFLOWER()
* @method static TorchflowerCrop TORCHFLOWER_CROP()
* @method static TrappedChest TRAPPED_CHEST()
* @method static Tripwire TRIPWIRE()
* @method static TripwireHook TRIPWIRE_HOOK()
@ -740,6 +747,7 @@ use function strtolower;
* @method static Wood WARPED_HYPHAE()
* @method static Planks WARPED_PLANKS()
* @method static WoodenPressurePlate WARPED_PRESSURE_PLATE()
* @method static NetherRoots WARPED_ROOTS()
* @method static FloorSign WARPED_SIGN()
* @method static WoodenSlab WARPED_SLAB()
* @method static WoodenStairs WARPED_STAIRS()
@ -808,6 +816,7 @@ final class VanillaBlocks{
self::register("blue_ice", new BlueIce(new BID(Ids::BLUE_ICE), "Blue Ice", new Info(BreakInfo::pickaxe(2.8))));
self::register("bone_block", new BoneBlock(new BID(Ids::BONE_BLOCK), "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD))));
self::register("bookshelf", new Bookshelf(new BID(Ids::BOOKSHELF), "Bookshelf", new Info(BreakInfo::axe(1.5))));
self::register("chiseled_bookshelf", new ChiseledBookshelf(new BID(Ids::CHISELED_BOOKSHELF, TileChiseledBookshelf::class), "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5))));
self::register("brewing_stand", new BrewingStand(new BID(Ids::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))));
$bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
@ -846,6 +855,9 @@ final class VanillaBlocks{
self::register("pink_petals", new PinkPetals(new BID(Ids::PINK_PETALS), "Pink Petals", new Info(BreakInfo::instant())));
self::register("double_tallgrass", new DoubleTallGrass(new BID(Ids::DOUBLE_TALLGRASS), "Double Tallgrass", new Info(BreakInfo::instant(ToolType::SHEARS, 1))));
self::register("large_fern", new DoubleTallGrass(new BID(Ids::LARGE_FERN), "Large Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1))));
self::register("pitcher_plant", new DoublePlant(new BID(Ids::PITCHER_PLANT), "Pitcher Plant", new Info(BreakInfo::instant())));
self::register("pitcher_crop", new PitcherCrop(new BID(Ids::PITCHER_CROP), "Pitcher Crop", new Info(BreakInfo::instant())));
self::register("double_pitcher_crop", new DoublePitcherCrop(new BID(Ids::DOUBLE_PITCHER_CROP), "Double Pitcher Crop", new Info(BreakInfo::instant())));
self::register("dragon_egg", new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD))));
self::register("dried_kelp", new DriedKelp(new BID(Ids::DRIED_KELP), "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5))));
self::register("emerald", new Opaque(new BID(Ids::EMERALD), "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0))));
@ -875,6 +887,8 @@ final class VanillaBlocks{
self::register("pink_tulip", new Flower(new BID(Ids::PINK_TULIP), "Pink Tulip", $flowerTypeInfo));
self::register("red_tulip", new Flower(new BID(Ids::RED_TULIP), "Red Tulip", $flowerTypeInfo));
self::register("white_tulip", new Flower(new BID(Ids::WHITE_TULIP), "White Tulip", $flowerTypeInfo));
self::register("torchflower", new Flower(new BID(Ids::TORCHFLOWER), "Torchflower", $flowerTypeInfo));
self::register("torchflower_crop", new TorchflowerCrop(new BID(Ids::TORCHFLOWER_CROP), "Torchflower Crop", new Info(BreakInfo::instant())));
self::register("flower_pot", new FlowerPot(new BID(Ids::FLOWER_POT, TileFlowerPot::class), "Flower Pot", new Info(BreakInfo::instant())));
self::register("frosted_ice", new FrostedIce(new BID(Ids::FROSTED_ICE), "Frosted Ice", new Info(BreakInfo::pickaxe(2.5))));
self::register("furnace", new Furnace(new BID(Ids::FURNACE, TileNormalFurnace::class), "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE));
@ -1535,17 +1549,21 @@ final class VanillaBlocks{
self::register("twisting_vines", new NetherVines(new BID(Ids::TWISTING_VINES), "Twisting Vines", new Info(BreakInfo::instant()), Facing::UP));
self::register("weeping_vines", new NetherVines(new BID(Ids::WEEPING_VINES), "Weeping Vines", new Info(BreakInfo::instant()), Facing::DOWN));
$netherRootsInfo = new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]);
self::register("crimson_roots", new NetherRoots(new BID(Ids::CRIMSON_ROOTS), "Crimson Roots", $netherRootsInfo));
self::register("warped_roots", new NetherRoots(new BID(Ids::WARPED_ROOTS), "Warped Roots", $netherRootsInfo));
self::register("chain", new Chain(new BID(Ids::CHAIN), "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))));
}
private static function registerBlocksR17() : void{
//in java this can be acquired using any tool - seems to be a parity issue in bedrock
self::register("amethyst", new class(new BID(Ids::AMETHYST), "Amethyst", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD))) extends Opaque{
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
$this->position->getWorld()->addSound($this->position, new AmethystBlockChimeSound());
$this->position->getWorld()->addSound($this->position, new BlockPunchSound($this));
}
$amethystInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD));
self::register("amethyst", new class(new BID(Ids::AMETHYST), "Amethyst", $amethystInfo) extends Opaque{
use AmethystTrait;
});
self::register("budding_amethyst", new BuddingAmethyst(new BID(Ids::BUDDING_AMETHYST), "Budding Amethyst", $amethystInfo));
self::register("amethyst_cluster", new AmethystCluster(new BID(Ids::AMETHYST_CLUSTER), "Amethyst Cluster", $amethystInfo));
self::register("calcite", new Opaque(new BID(Ids::CALCITE), "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD))));
self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0))));

View File

@ -0,0 +1,58 @@
<?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\tile;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\inventory\SimpleInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
use function count;
class ChiseledBookshelf extends Tile implements Container{
use ContainerTrait;
private SimpleInventory $inventory;
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases()));
}
public function getInventory() : SimpleInventory{
return $this->inventory;
}
public function getRealInventory() : SimpleInventory{
return $this->inventory;
}
public function readSaveData(CompoundTag $nbt) : void{
$this->loadItems($nbt);
}
public function writeSaveData(CompoundTag $nbt) : void{
$this->saveItems($nbt);
}
}

View File

@ -59,6 +59,7 @@ final class TileFactory{
$this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]);
$this->register(Cauldron::class, ["Cauldron", "minecraft:cauldron"]);
$this->register(Chest::class, ["Chest", "minecraft:chest"]);
$this->register(ChiseledBookshelf::class, ["ChiseledBookshelf", "minecraft:chiseled_bookshelf"]);
$this->register(Comparator::class, ["Comparator", "minecraft:comparator"]);
$this->register(DaylightSensor::class, ["DaylightDetector", "minecraft:daylight_detector"]);
$this->register(EnchantTable::class, ["EnchantTable", "minecraft:enchanting_table"]);

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\data\runtime\RuntimeDataDescriber;
use function ceil;
use function log;
/**
@ -35,7 +34,7 @@ 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);
$w->boundedInt(((int) log(self::MAX_AGE, 2)) + 1, 0, self::MAX_AGE, $this->age);
}
public function getAge() : int{ return $this->age; }

View File

@ -0,0 +1,40 @@
<?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\entity\projectile\Projectile;
use pocketmine\math\RayTraceResult;
use pocketmine\world\sound\AmethystBlockChimeSound;
use pocketmine\world\sound\BlockPunchSound;
trait AmethystTrait{
/**
* @see Block::onProjectileHit()
*/
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
$this->position->getWorld()->addSound($this->position, new AmethystBlockChimeSound());
$this->position->getWorld()->addSound($this->position, new BlockPunchSound($this));
}
}

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;
enum ChiseledBookshelfSlot : int{
case TOP_LEFT = 0;
case TOP_MIDDLE = 1;
case TOP_RIGHT = 2;
case BOTTOM_LEFT = 3;
case BOTTOM_MIDDLE = 4;
case BOTTOM_RIGHT = 5;
private const SLOTS_PER_SHELF = 3;
public static function fromBlockFaceCoordinates(float $x, float $y) : self{
if($x < 0 || $x > 1){
throw new \InvalidArgumentException("X must be between 0 and 1, got $x");
}
if($y < 0 || $y > 1){
throw new \InvalidArgumentException("Y must be between 0 and 1, got $y");
}
$slot = ($y < 0.5 ? self::SLOTS_PER_SHELF : 0) + match(true){
//we can't use simple maths here as the action is aligned to the 16x16 pixel grid :(
$x < 6 / 16 => 0,
$x < 11 / 16 => 1,
default => 2
};
return self::from($slot);
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\ActivatorRail;
use pocketmine\block\AmethystCluster;
use pocketmine\block\Anvil;
use pocketmine\block\Bamboo;
use pocketmine\block\BambooSapling;
@ -50,6 +51,7 @@ use pocketmine\block\CaveVines;
use pocketmine\block\Chain;
use pocketmine\block\ChemistryTable;
use pocketmine\block\Chest;
use pocketmine\block\ChiseledBookshelf;
use pocketmine\block\ChorusFlower;
use pocketmine\block\CocoaBlock;
use pocketmine\block\Concrete;
@ -63,6 +65,7 @@ use pocketmine\block\DaylightSensor;
use pocketmine\block\DetectorRail;
use pocketmine\block\Dirt;
use pocketmine\block\Door;
use pocketmine\block\DoublePitcherCrop;
use pocketmine\block\DoublePlant;
use pocketmine\block\DoubleTallGrass;
use pocketmine\block\DyedCandle;
@ -101,6 +104,7 @@ use pocketmine\block\NetherPortal;
use pocketmine\block\NetherVines;
use pocketmine\block\NetherWartPlant;
use pocketmine\block\PinkPetals;
use pocketmine\block\PitcherCrop;
use pocketmine\block\Potato;
use pocketmine\block\PoweredRail;
use pocketmine\block\PumpkinStem;
@ -134,6 +138,7 @@ use pocketmine\block\Sugarcane;
use pocketmine\block\SweetBerryBush;
use pocketmine\block\TNT;
use pocketmine\block\Torch;
use pocketmine\block\TorchflowerCrop;
use pocketmine\block\Trapdoor;
use pocketmine\block\TrappedChest;
use pocketmine\block\Tripwire;
@ -717,6 +722,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::BOOKSHELF(), Ids::BOOKSHELF);
$this->mapSimple(Blocks::BRICKS(), Ids::BRICK_BLOCK);
$this->mapSimple(Blocks::BROWN_MUSHROOM(), Ids::BROWN_MUSHROOM);
$this->mapSimple(Blocks::BUDDING_AMETHYST(), Ids::BUDDING_AMETHYST);
$this->mapSimple(Blocks::CALCITE(), Ids::CALCITE);
$this->mapSimple(Blocks::CARTOGRAPHY_TABLE(), Ids::CARTOGRAPHY_TABLE);
$this->mapSimple(Blocks::CHEMICAL_HEAT(), Ids::CHEMICAL_HEAT);
@ -736,6 +742,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS);
$this->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS);
$this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE);
$this->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS);
$this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN);
$this->mapSimple(Blocks::DANDELION(), Ids::YELLOW_FLOWER);
$this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH);
@ -950,8 +957,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL);
$this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM);
$this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS);
$this->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER);
$this->mapSimple(Blocks::TUFF(), Ids::TUFF);
$this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK);
$this->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS);
$this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE);
}
@ -965,6 +974,16 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::ALLIUM(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_ALLIUM));
$this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK)
->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM));
$this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create(
match($stage = $block->getStage()){
AmethystCluster::STAGE_SMALL_BUD => Ids::SMALL_AMETHYST_BUD,
AmethystCluster::STAGE_MEDIUM_BUD => Ids::MEDIUM_AMETHYST_BUD,
AmethystCluster::STAGE_LARGE_BUD => Ids::LARGE_AMETHYST_BUD,
AmethystCluster::STAGE_CLUSTER => Ids::AMETHYST_CLUSTER,
default => throw new BlockStateSerializeException("Invalid Amethyst Cluster stage $stage"),
})
->writeBlockFace($block->getFacing())
);
$this->map(Blocks::ANDESITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_ANDESITE));
$this->map(Blocks::ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_ANDESITE));
$this->map(Blocks::ANDESITE_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::ANDESITE_STAIRS)));
@ -1100,6 +1119,15 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::CHEST)
->writeHorizontalFacing($block->getFacing());
});
$this->map(Blocks::CHISELED_BOOKSHELF(), function(ChiseledBookshelf $block) : Writer{
$flags = 0;
foreach($block->getSlots() as $slot){
$flags |= 1 << $slot->value;
}
return Writer::create(Ids::CHISELED_BOOKSHELF)
->writeLegacyHorizontalFacing($block->getFacing())
->writeInt(StateNames::BOOKS_STORED, $flags);
});
$this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz(StringValues::CHISEL_TYPE_CHISELED, $block->getAxis()));
$this->map(Blocks::CHISELED_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));
$this->map(Blocks::CHISELED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));
@ -1430,6 +1458,20 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeInt(StateNames::GROWTH, $block->getCount() - 1);
});
$this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK));
$this->map(Blocks::PITCHER_PLANT(), function(DoublePlant $block) : Writer{
return Writer::create(Ids::PITCHER_PLANT)
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
});
$this->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{
return Writer::create(Ids::PITCHER_CROP)
->writeInt(StateNames::GROWTH, $block->getAge())
->writeBool(StateNames::UPPER_BLOCK_BIT, false);
});
$this->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{
return Writer::create(Ids::PITCHER_CROP)
->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE)
->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
});
$this->map(Blocks::POLISHED_ANDESITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_ANDESITE_SMOOTH));
$this->map(Blocks::POLISHED_ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE));
$this->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS);
@ -1618,6 +1660,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::TORCH)
->writeTorchFacing($block->getFacing());
});
$this->map(Blocks::TORCHFLOWER_CROP(), function(TorchflowerCrop $block){
return Writer::create(Ids::TORCHFLOWER_CROP)
->writeInt(StateNames::GROWTH, $block->isReady() ? 1 : 0);
});
$this->map(Blocks::TRAPPED_CHEST(), function(TrappedChest $block) : Writer{
return Writer::create(Ids::TRAPPED_CHEST)
->writeHorizontalFacing($block->getFacing());

View File

@ -135,6 +135,19 @@ final class BlockStateReader{
]);
}
/** @throws BlockStateDeserializeException */
public function readBlockFace() : int{
return match($raw = $this->readString(BlockStateNames::MC_BLOCK_FACE)){
StringValues::MC_BLOCK_FACE_DOWN => Facing::DOWN,
StringValues::MC_BLOCK_FACE_UP => Facing::UP,
StringValues::MC_BLOCK_FACE_NORTH => Facing::NORTH,
StringValues::MC_BLOCK_FACE_SOUTH => Facing::SOUTH,
StringValues::MC_BLOCK_FACE_WEST => Facing::WEST,
StringValues::MC_BLOCK_FACE_EAST => Facing::EAST,
default => throw $this->badValueException(BlockStateNames::MC_BLOCK_FACE, $raw)
};
}
/**
* @return int[]
* @phpstan-return array<int, int>

View File

@ -23,16 +23,20 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\AmethystCluster;
use pocketmine\block\Bamboo;
use pocketmine\block\Block;
use pocketmine\block\CaveVines;
use pocketmine\block\ChorusFlower;
use pocketmine\block\DoublePitcherCrop;
use pocketmine\block\Light;
use pocketmine\block\PinkPetals;
use pocketmine\block\PitcherCrop;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\SweetBerryBush;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DirtType;
@ -611,6 +615,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::BOOKSHELF, fn() => Blocks::BOOKSHELF());
$this->mapSimple(Ids::BRICK_BLOCK, fn() => Blocks::BRICKS());
$this->mapSimple(Ids::BROWN_MUSHROOM, fn() => Blocks::BROWN_MUSHROOM());
$this->mapSimple(Ids::BUDDING_AMETHYST, fn() => Blocks::BUDDING_AMETHYST());
$this->mapSimple(Ids::CALCITE, fn() => Blocks::CALCITE());
$this->mapSimple(Ids::CARTOGRAPHY_TABLE, fn() => Blocks::CARTOGRAPHY_TABLE());
$this->mapSimple(Ids::CHEMICAL_HEAT, fn() => Blocks::CHEMICAL_HEAT());
@ -629,6 +634,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS());
$this->mapSimple(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS());
$this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE());
$this->mapSimple(Ids::CRIMSON_ROOTS, fn() => Blocks::CRIMSON_ROOTS());
$this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN());
$this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH());
$this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS());
@ -840,9 +846,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::SPORE_BLOSSOM, fn() => Blocks::SPORE_BLOSSOM());
$this->mapSimple(Ids::STONECUTTER, fn() => Blocks::LEGACY_STONECUTTER());
$this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS());
$this->mapSimple(Ids::TORCHFLOWER, fn() => Blocks::TORCHFLOWER());
$this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF());
$this->mapSimple(Ids::UNDYED_SHULKER_BOX, fn() => Blocks::SHULKER_BOX());
$this->mapSimple(Ids::WARPED_WART_BLOCK, fn() => Blocks::WARPED_WART_BLOCK());
$this->mapSimple(Ids::WARPED_ROOTS, fn() => Blocks::WARPED_ROOTS());
$this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD());
$this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB());
$this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE());
@ -855,6 +863,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setPowered($in->readBool(StateNames::RAIL_DATA_BIT))
->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5));
});
$this->map(Ids::AMETHYST_CLUSTER, function(Reader $in) : Block{
return Blocks::AMETHYST_CLUSTER()
->setStage(AmethystCluster::STAGE_CLUSTER)
->setFacing($in->readBlockFace());
});
$this->mapStairs(Ids::ANDESITE_STAIRS, fn() => Blocks::ANDESITE_STAIRS());
$this->map(Ids::ANVIL, function(Reader $in) : Block{
return Blocks::ANVIL()
@ -983,6 +996,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::CHAIN()
->setAxis($in->readPillarAxis());
});
$this->map(Ids::CHISELED_BOOKSHELF, function(Reader $in) : Block{
$block = Blocks::CHISELED_BOOKSHELF()
->setFacing($in->readLegacyHorizontalFacing());
//we don't use API constant for bounds here as the data bounds might be different to what we support internally
$flags = $in->readBoundedInt(StateNames::BOOKS_STORED, 0, (1 << 6) - 1);
foreach(ChiseledBookshelfSlot::cases() as $slot){
$block->setSlot($slot, ($flags & (1 << $slot->value)) !== 0);
}
return $block;
});
$this->map(Ids::CHEMISTRY_TABLE, function(Reader $in) : Block{
return (match($type = $in->readString(StateNames::CHEMISTRY_TABLE_TYPE)){
StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR => Blocks::COMPOUND_CREATOR(),
@ -1161,6 +1186,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::LANTERN()
->setHanging($in->readBool(StateNames::HANGING));
});
$this->map(Ids::LARGE_AMETHYST_BUD, function(Reader $in) : Block{
return Blocks::AMETHYST_CLUSTER()
->setStage(AmethystCluster::STAGE_LARGE_BUD)
->setFacing($in->readBlockFace());
});
$this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in));
$this->map(Ids::LECTERN, function(Reader $in) : Block{
return Blocks::LECTERN()
@ -1223,6 +1253,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::LOOM()
->setFacing($in->readLegacyHorizontalFacing());
});
$this->map(Ids::MEDIUM_AMETHYST_BUD, function(Reader $in) : Block{
return Blocks::AMETHYST_CLUSTER()
->setStage(AmethystCluster::STAGE_MEDIUM_BUD)
->setFacing($in->readBlockFace());
});
$this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in));
$this->map(Ids::MONSTER_EGG, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::MONSTER_EGG_STONE_TYPE)){
@ -1263,6 +1298,22 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setFacing($in->readCardinalHorizontalFacing())
->setCount(min($growth + 1, PinkPetals::MAX_COUNT));
});
$this->map(Ids::PITCHER_CROP, function(Reader $in) : Block{
$growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7);
$top = $in->readBool(StateNames::UPPER_BLOCK_BIT);
if($growth <= PitcherCrop::MAX_AGE){
//top pitcher crop with age 0-2 is an invalid state
//only the bottom half should exist in this case
return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth);
}
return Blocks::DOUBLE_PITCHER_CROP()
->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE))
->setTop($top);
});
$this->map(Ids::PITCHER_PLANT, function(Reader $in) : Block{
return Blocks::PITCHER_PLANT()
->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT));
});
$this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS());
$this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{
return Blocks::POLISHED_BASALT()
@ -1437,6 +1488,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setFacing($in->readCardinalHorizontalFacing())
->setLit(false);
});
$this->map(Ids::SMALL_AMETHYST_BUD, function(Reader $in) : Block{
return Blocks::AMETHYST_CLUSTER()
->setStage(AmethystCluster::STAGE_SMALL_BUD)
->setFacing($in->readBlockFace());
});
$this->map(Ids::SMALL_DRIPLEAF_BLOCK, function(Reader $in) : Block{
return Blocks::SMALL_DRIPLEAF()
->setFacing($in->readCardinalHorizontalFacing())
@ -1528,6 +1584,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::TORCH()
->setFacing($in->readTorchFacing());
});
$this->map(Ids::TORCHFLOWER_CROP, function(Reader $in) : Block{
return Blocks::TORCHFLOWER_CROP()
//this property can have values 0-7, but only 0-1 are valid
->setReady($in->readBoundedInt(StateNames::GROWTH, 0, 7) !== 0);
});
$this->map(Ids::TRAPPED_CHEST, function(Reader $in) : Block{
return Blocks::TRAPPED_CHEST()
->setFacing($in->readHorizontalFacing());

View File

@ -90,6 +90,20 @@ final class BlockStateWriter{
return $this;
}
/** @return $this */
public function writeBlockFace(int $value) : self{
$this->writeString(BlockStateNames::MC_BLOCK_FACE, match($value){
Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN,
Facing::UP => StringValues::MC_BLOCK_FACE_UP,
Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH,
Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH,
Facing::WEST => StringValues::MC_BLOCK_FACE_WEST,
Facing::EAST => StringValues::MC_BLOCK_FACE_EAST,
default => throw new BlockStateSerializeException("Invalid Facing $value")
});
return $this;
}
/**
* @param int[] $faces
* @phpstan-param array<int, int> $faces

View File

@ -147,6 +147,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Block(Ids::JUNGLE_DOOR, Blocks::JUNGLE_DOOR());
$this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR());
$this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART());
$this->map1to1Block(Ids::PITCHER_POD, Blocks::PITCHER_CROP());
$this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER());
$this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR());
$this->map1to1Block(Ids::SUGAR_CANE, Blocks::SUGARCANE());
@ -359,6 +360,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::STRING, Items::STRING());
$this->map1to1Item(Ids::SUGAR, Items::SUGAR());
$this->map1to1Item(Ids::SWEET_BERRIES, Items::SWEET_BERRIES());
$this->map1to1Item(Ids::TORCHFLOWER_SEEDS, Items::TORCHFLOWER_SEEDS());
$this->map1to1Item(Ids::TOTEM_OF_UNDYING, Items::TOTEM());
$this->map1to1Item(Ids::TROPICAL_FISH, Items::CLOWNFISH());
$this->map1to1Item(Ids::TURTLE_HELMET, Items::TURTLE_HELMET());

View File

@ -71,6 +71,8 @@ interface RuntimeDataDescriber{
/**
* @param BrewingStandSlot[] $slots
* @phpstan-param array<int, BrewingStandSlot> $slots
*
* @deprecated Use {@link enumSet()} instead.
*/
public function brewingStandSlots(array &$slots) : void;
@ -83,4 +85,14 @@ interface RuntimeDataDescriber{
* @phpstan-param T $case
*/
public function enum(\UnitEnum &$case) : void;
/**
* @param \UnitEnum[] &$set
* @param \UnitEnum[] $allCases
*
* @phpstan-template T of \UnitEnum
* @phpstan-param array<int, T> &$set
* @phpstan-param array<int, T> $allCases
*/
public function enumSet(array &$set, array $allCases) : void;
}

View File

@ -179,16 +179,11 @@ final class RuntimeDataReader implements RuntimeDataDescriber{
/**
* @param BrewingStandSlot[] $slots
* @phpstan-param array<int, BrewingStandSlot> $slots
*
* @deprecated Use {@link enumSet()} instead.
*/
public function brewingStandSlots(array &$slots) : void{
$result = [];
foreach(BrewingStandSlot::cases() as $member){
if($this->readBool()){
$result[spl_object_id($member)] = $member;
}
}
$slots = $result;
$this->enumSet($slots, BrewingStandSlot::cases());
}
public function railShape(int &$railShape) : void{
@ -220,5 +215,15 @@ final class RuntimeDataReader implements RuntimeDataDescriber{
$case = $result;
}
public function enumSet(array &$set, array $allCases) : void{
$result = [];
foreach($allCases as $case){
if($this->readBool()){
$result[spl_object_id($case)] = $case;
}
}
$set = $result;
}
public function getOffset() : int{ return $this->offset; }
}

View File

@ -98,4 +98,8 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{
$metadata = RuntimeEnumMetadata::from($case);
$this->addBits($metadata->bits);
}
public function enumSet(array &$set, array $allCases) : void{
$this->addBits(count($allCases));
}
}

View File

@ -157,11 +157,11 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{
/**
* @param BrewingStandSlot[] $slots
* @phpstan-param array<int, BrewingStandSlot> $slots
*
* @deprecated Use {@link enumSet()} instead.
*/
public function brewingStandSlots(array &$slots) : void{
foreach(BrewingStandSlot::cases() as $member){
$this->writeBool(isset($slots[spl_object_id($member)]));
}
$this->enumSet($slots, BrewingStandSlot::cases());
}
public function railShape(int &$railShape) : void{
@ -177,6 +177,12 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{
$this->writeInt($metadata->bits, $metadata->enumToInt($case));
}
public function enumSet(array &$set, array $allCases) : void{
foreach($allCases as $case){
$this->writeBool(isset($set[spl_object_id($case)]));
}
}
public function getValue() : int{ return $this->value; }
public function getOffset() : int{ return $this->offset; }

View File

@ -1686,7 +1686,7 @@ abstract class Entity{
*/
public function broadcastSound(Sound $sound, ?array $targets = null) : void{
if(!$this->silent){
NetworkBroadcastUtils::broadcastPackets($targets ?? $this->getViewers(), $sound->encode($this->location));
$this->getWorld()->addSound($this->location->asVector3(), $sound, $targets ?? $this->getViewers());
}
}

View File

@ -304,8 +304,9 @@ final class ItemTypeIds{
public const GLOW_BERRIES = 20265;
public const CHERRY_SIGN = 20266;
public const ENCHANTED_BOOK = 20267;
public const TORCHFLOWER_SEEDS = 20268;
public const FIRST_UNUSED_ITEM_ID = 20268;
public const FIRST_UNUSED_ITEM_ID = 20269;
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\block\AmethystCluster;
use pocketmine\block\Block;
use pocketmine\block\Light;
use pocketmine\block\utils\CopperOxidation;
@ -134,6 +135,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("all_sided_mushroom_stem", fn() => Blocks::ALL_SIDED_MUSHROOM_STEM());
$result->registerBlock("allium", fn() => Blocks::ALLIUM());
$result->registerBlock("amethyst_block", fn() => Blocks::AMETHYST());
$result->registerBlock("amethyst_cluster", fn() => Blocks::AMETHYST_CLUSTER());
$result->registerBlock("ancient_debris", fn() => Blocks::ANCIENT_DEBRIS());
$result->registerBlock("andesite", fn() => Blocks::ANDESITE());
$result->registerBlock("andesite_slab", fn() => Blocks::ANDESITE_SLAB());
@ -196,6 +198,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("bricks_block", fn() => Blocks::BRICKS());
$result->registerBlock("brown_mushroom", fn() => Blocks::BROWN_MUSHROOM());
$result->registerBlock("brown_mushroom_block", fn() => Blocks::BROWN_MUSHROOM_BLOCK());
$result->registerBlock("budding_amethyst", fn() => Blocks::BUDDING_AMETHYST());
$result->registerBlock("burning_furnace", fn() => Blocks::FURNACE());
$result->registerBlock("bush", fn() => Blocks::DEAD_BUSH());
$result->registerBlock("cactus", fn() => Blocks::CACTUS());
@ -227,6 +230,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("chemistry_table", fn() => Blocks::COMPOUND_CREATOR());
$result->registerBlock("chest", fn() => Blocks::CHEST());
$result->registerBlock("chipped_anvil", fn() => Blocks::ANVIL()->setDamage(1));
$result->registerBlock("chiseled_bookshelf", fn() => Blocks::CHISELED_BOOKSHELF());
$result->registerBlock("chiseled_deepslate", fn() => Blocks::CHISELED_DEEPSLATE());
$result->registerBlock("chiseled_nether_bricks", fn() => Blocks::CHISELED_NETHER_BRICKS());
$result->registerBlock("chiseled_polished_blackstone", fn() => Blocks::CHISELED_POLISHED_BLACKSTONE());
@ -287,6 +291,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("crimson_hyphae", fn() => Blocks::CRIMSON_HYPHAE()->setStripped(false));
$result->registerBlock("crimson_planks", fn() => Blocks::CRIMSON_PLANKS());
$result->registerBlock("crimson_pressure_plate", fn() => Blocks::CRIMSON_PRESSURE_PLATE());
$result->registerBlock("crimson_roots", fn() => Blocks::CRIMSON_ROOTS());
$result->registerBlock("crimson_sign", fn() => Blocks::CRIMSON_SIGN());
$result->registerBlock("crimson_slab", fn() => Blocks::CRIMSON_SLAB());
$result->registerBlock("crimson_stairs", fn() => Blocks::CRIMSON_STAIRS());
@ -743,6 +748,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("lapis_lazuli_block", fn() => Blocks::LAPIS_LAZULI());
$result->registerBlock("lapis_lazuli_ore", fn() => Blocks::LAPIS_LAZULI_ORE());
$result->registerBlock("lapis_ore", fn() => Blocks::LAPIS_LAZULI_ORE());
$result->registerBlock("large_amethyst_bud", fn() => Blocks::AMETHYST_CLUSTER()->setStage(AmethystCluster::STAGE_LARGE_BUD));
$result->registerBlock("large_fern", fn() => Blocks::LARGE_FERN());
$result->registerBlock("lava", fn() => Blocks::LAVA());
$result->registerBlock("leave", fn() => Blocks::OAK_LEAVES());
@ -785,6 +791,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("mangrove_trapdoor", fn() => Blocks::MANGROVE_TRAPDOOR());
$result->registerBlock("mangrove_wood", fn() => Blocks::MANGROVE_WOOD()->setStripped(false));
$result->registerBlock("material_reducer", fn() => Blocks::MATERIAL_REDUCER());
$result->registerBlock("medium_amethyst_bud", fn() => Blocks::AMETHYST_CLUSTER()->setStage(AmethystCluster::STAGE_MEDIUM_BUD));
$result->registerBlock("melon_block", fn() => Blocks::MELON());
$result->registerBlock("melon_stem", fn() => Blocks::MELON_STEM());
$result->registerBlock("mob_head", fn() => Blocks::MOB_HEAD());
@ -975,6 +982,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("slabs", fn() => Blocks::SMOOTH_STONE_SLAB());
$result->registerBlock("slime", fn() => Blocks::SLIME());
$result->registerBlock("slime_block", fn() => Blocks::SLIME());
$result->registerBlock("small_amethyst_bud", fn() => Blocks::AMETHYST_CLUSTER()->setStage(AmethystCluster::STAGE_SMALL_BUD));
$result->registerBlock("small_dripleaf", fn() => Blocks::SMALL_DRIPLEAF());
$result->registerBlock("smoker", fn() => Blocks::SMOKER());
$result->registerBlock("smooth_basalt", fn() => Blocks::SMOOTH_BASALT());
@ -1074,6 +1082,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("tinted_glass", fn() => Blocks::TINTED_GLASS());
$result->registerBlock("tnt", fn() => Blocks::TNT());
$result->registerBlock("torch", fn() => Blocks::TORCH());
$result->registerBlock("torchflower", fn() => Blocks::TORCHFLOWER());
$result->registerBlock("trapdoor", fn() => Blocks::OAK_TRAPDOOR());
$result->registerBlock("trapped_chest", fn() => Blocks::TRAPPED_CHEST());
$result->registerBlock("trip_wire", fn() => Blocks::TRIPWIRE());
@ -1104,6 +1113,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("warped_hyphae", fn() => Blocks::WARPED_HYPHAE()->setStripped(false));
$result->registerBlock("warped_planks", fn() => Blocks::WARPED_PLANKS());
$result->registerBlock("warped_pressure_plate", fn() => Blocks::WARPED_PRESSURE_PLATE());
$result->registerBlock("warped_roots", fn() => Blocks::WARPED_ROOTS());
$result->registerBlock("warped_sign", fn() => Blocks::WARPED_SIGN());
$result->registerBlock("warped_slab", fn() => Blocks::WARPED_SLAB());
$result->registerBlock("warped_stairs", fn() => Blocks::WARPED_STAIRS());
@ -1154,6 +1164,13 @@ final class StringToItemParser extends StringToTParser{
$result->register($prefix("suspicious_stew"), fn() => Items::SUSPICIOUS_STEW()->setType($suspiciousStewType));
}
foreach(PotionType::cases() as $potionType){
$prefix = fn(string $name) => strtolower($potionType->name) . "_" . $name;
$result->register($prefix("potion"), fn() => Items::POTION()->setType($potionType));
$result->register($prefix("splash_potion"), fn() => Items::SPLASH_POTION()->setType($potionType));
}
}
private static function registerItems(self $result) : void{
@ -1165,8 +1182,6 @@ final class StringToItemParser extends StringToTParser{
$result->register("apple_enchanted", fn() => Items::ENCHANTED_GOLDEN_APPLE());
$result->register("appleenchanted", fn() => Items::ENCHANTED_GOLDEN_APPLE());
$result->register("arrow", fn() => Items::ARROW());
$result->register("awkward_potion", fn() => Items::POTION()->setType(PotionType::AWKWARD));
$result->register("awkward_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::AWKWARD));
$result->register("baked_potato", fn() => Items::BAKED_POTATO());
$result->register("baked_potatoes", fn() => Items::BAKED_POTATO());
$result->register("beef", fn() => Items::RAW_BEEF());
@ -1287,8 +1302,6 @@ final class StringToItemParser extends StringToTParser{
$result->register("feather", fn() => Items::FEATHER());
$result->register("fermented_spider_eye", fn() => Items::FERMENTED_SPIDER_EYE());
$result->register("fire_charge", fn() => Items::FIRE_CHARGE());
$result->register("fire_resistance_potion", fn() => Items::POTION()->setType(PotionType::FIRE_RESISTANCE));
$result->register("fire_resistance_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::FIRE_RESISTANCE));
$result->register("fish", fn() => Items::RAW_FISH());
$result->register("fishing_rod", fn() => Items::FISHING_ROD());
$result->register("flint", fn() => Items::FLINT());
@ -1324,16 +1337,10 @@ final class StringToItemParser extends StringToTParser{
$result->register("golden_shovel", fn() => Items::GOLDEN_SHOVEL());
$result->register("golden_sword", fn() => Items::GOLDEN_SWORD());
$result->register("gunpowder", fn() => Items::GUNPOWDER());
$result->register("harming_potion", fn() => Items::POTION()->setType(PotionType::HARMING));
$result->register("harming_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::HARMING));
$result->register("healing_potion", fn() => Items::POTION()->setType(PotionType::HEALING));
$result->register("healing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::HEALING));
$result->register("heart_of_the_sea", fn() => Items::HEART_OF_THE_SEA());
$result->register("honey_bottle", fn() => Items::HONEY_BOTTLE());
$result->register("honeycomb", fn() => Items::HONEYCOMB());
$result->register("ink_sac", fn() => Items::INK_SAC());
$result->register("invisibility_potion", fn() => Items::POTION()->setType(PotionType::INVISIBILITY));
$result->register("invisibility_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::INVISIBILITY));
$result->register("iron_axe", fn() => Items::IRON_AXE());
$result->register("iron_boots", fn() => Items::IRON_BOOTS());
$result->register("iron_chestplate", fn() => Items::IRON_CHESTPLATE());
@ -1348,8 +1355,6 @@ final class StringToItemParser extends StringToTParser{
$result->register("jungle_boat", fn() => Items::JUNGLE_BOAT());
$result->register("lapis_lazuli", fn() => Items::LAPIS_LAZULI());
$result->register("lava_bucket", fn() => Items::LAVA_BUCKET());
$result->register("leaping_potion", fn() => Items::POTION()->setType(PotionType::LEAPING));
$result->register("leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LEAPING));
$result->register("leather", fn() => Items::LEATHER());
$result->register("leather_boots", fn() => Items::LEATHER_BOOTS());
$result->register("leather_cap", fn() => Items::LEATHER_CAP());
@ -1358,42 +1363,12 @@ final class StringToItemParser extends StringToTParser{
$result->register("leather_leggings", fn() => Items::LEATHER_PANTS());
$result->register("leather_pants", fn() => Items::LEATHER_PANTS());
$result->register("leather_tunic", fn() => Items::LEATHER_TUNIC());
$result->register("long_fire_resistance_potion", fn() => Items::POTION()->setType(PotionType::LONG_FIRE_RESISTANCE));
$result->register("long_fire_resistance_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_FIRE_RESISTANCE));
$result->register("long_invisibility_potion", fn() => Items::POTION()->setType(PotionType::LONG_INVISIBILITY));
$result->register("long_invisibility_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_INVISIBILITY));
$result->register("long_leaping_potion", fn() => Items::POTION()->setType(PotionType::LONG_LEAPING));
$result->register("long_leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_LEAPING));
$result->register("long_mundane_potion", fn() => Items::POTION()->setType(PotionType::LONG_MUNDANE));
$result->register("long_mundane_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_MUNDANE));
$result->register("long_night_vision_potion", fn() => Items::POTION()->setType(PotionType::LONG_NIGHT_VISION));
$result->register("long_night_vision_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_NIGHT_VISION));
$result->register("long_poison_potion", fn() => Items::POTION()->setType(PotionType::LONG_POISON));
$result->register("long_poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_POISON));
$result->register("long_regeneration_potion", fn() => Items::POTION()->setType(PotionType::LONG_REGENERATION));
$result->register("long_regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_REGENERATION));
$result->register("long_slow_falling_potion", fn() => Items::POTION()->setType(PotionType::LONG_SLOW_FALLING));
$result->register("long_slow_falling_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SLOW_FALLING));
$result->register("long_slowness_potion", fn() => Items::POTION()->setType(PotionType::LONG_SLOWNESS));
$result->register("long_slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SLOWNESS));
$result->register("long_strength_potion", fn() => Items::POTION()->setType(PotionType::LONG_STRENGTH));
$result->register("long_strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_STRENGTH));
$result->register("long_swiftness_potion", fn() => Items::POTION()->setType(PotionType::LONG_SWIFTNESS));
$result->register("long_swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SWIFTNESS));
$result->register("long_turtle_master_potion", fn() => Items::POTION()->setType(PotionType::LONG_TURTLE_MASTER));
$result->register("long_turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_TURTLE_MASTER));
$result->register("long_water_breathing_potion", fn() => Items::POTION()->setType(PotionType::LONG_WATER_BREATHING));
$result->register("long_water_breathing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_WATER_BREATHING));
$result->register("long_weakness_potion", fn() => Items::POTION()->setType(PotionType::LONG_WEAKNESS));
$result->register("long_weakness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_WEAKNESS));
$result->register("magma_cream", fn() => Items::MAGMA_CREAM());
$result->register("melon", fn() => Items::MELON());
$result->register("melon_seeds", fn() => Items::MELON_SEEDS());
$result->register("melon_slice", fn() => Items::MELON());
$result->register("milk_bucket", fn() => Items::MILK_BUCKET());
$result->register("minecart", fn() => Items::MINECART());
$result->register("mundane_potion", fn() => Items::POTION()->setType(PotionType::MUNDANE));
$result->register("mundane_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::MUNDANE));
$result->register("mushroom_stew", fn() => Items::MUSHROOM_STEW());
$result->register("mutton", fn() => Items::RAW_MUTTON());
$result->register("mutton_cooked", fn() => Items::COOKED_MUTTON());
@ -1417,14 +1392,10 @@ final class StringToItemParser extends StringToTParser{
$result->register("netherite_shovel", fn() => Items::NETHERITE_SHOVEL());
$result->register("netherite_sword", fn() => Items::NETHERITE_SWORD());
$result->register("netherstar", fn() => Items::NETHER_STAR());
$result->register("night_vision_potion", fn() => Items::POTION()->setType(PotionType::NIGHT_VISION));
$result->register("night_vision_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::NIGHT_VISION));
$result->register("oak_boat", fn() => Items::OAK_BOAT());
$result->register("painting", fn() => Items::PAINTING());
$result->register("paper", fn() => Items::PAPER());
$result->register("phantom_membrane", fn() => Items::PHANTOM_MEMBRANE());
$result->register("poison_potion", fn() => Items::POTION()->setType(PotionType::POISON));
$result->register("poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::POISON));
$result->register("poisonous_potato", fn() => Items::POISONOUS_POTATO());
$result->register("popped_chorus_fruit", fn() => Items::POPPED_CHORUS_FRUIT());
$result->register("porkchop", fn() => Items::RAW_PORKCHOP());
@ -1469,8 +1440,6 @@ final class StringToItemParser extends StringToTParser{
$result->register("record_ward", fn() => Items::RECORD_WARD());
$result->register("redstone", fn() => Items::REDSTONE_DUST());
$result->register("redstone_dust", fn() => Items::REDSTONE_DUST());
$result->register("regeneration_potion", fn() => Items::POTION()->setType(PotionType::REGENERATION));
$result->register("regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::REGENERATION));
$result->register("rotten_flesh", fn() => Items::ROTTEN_FLESH());
$result->register("salmon", fn() => Items::RAW_SALMON());
$result->register("scute", fn() => Items::SCUTE());
@ -1479,10 +1448,6 @@ final class StringToItemParser extends StringToTParser{
$result->register("shulker_shell", fn() => Items::SHULKER_SHELL());
$result->register("slime_ball", fn() => Items::SLIMEBALL());
$result->register("slimeball", fn() => Items::SLIMEBALL());
$result->register("slow_falling_potion", fn() => Items::POTION()->setType(PotionType::SLOW_FALLING));
$result->register("slow_falling_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SLOW_FALLING));
$result->register("slowness_potion", fn() => Items::POTION()->setType(PotionType::SLOWNESS));
$result->register("slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SLOWNESS));
$result->register("snowball", fn() => Items::SNOWBALL());
$result->register("speckled_melon", fn() => Items::GLISTERING_MELON());
$result->register("spider_eye", fn() => Items::SPIDER_EYE());
@ -1498,52 +1463,19 @@ final class StringToItemParser extends StringToTParser{
$result->register("stone_pickaxe", fn() => Items::STONE_PICKAXE());
$result->register("stone_shovel", fn() => Items::STONE_SHOVEL());
$result->register("stone_sword", fn() => Items::STONE_SWORD());
$result->register("strength_potion", fn() => Items::POTION()->setType(PotionType::STRENGTH));
$result->register("strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRENGTH));
$result->register("string", fn() => Items::STRING());
$result->register("strong_harming_potion", fn() => Items::POTION()->setType(PotionType::STRONG_HARMING));
$result->register("strong_harming_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_HARMING));
$result->register("strong_healing_potion", fn() => Items::POTION()->setType(PotionType::STRONG_HEALING));
$result->register("strong_healing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_HEALING));
$result->register("strong_leaping_potion", fn() => Items::POTION()->setType(PotionType::STRONG_LEAPING));
$result->register("strong_leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_LEAPING));
$result->register("strong_poison_potion", fn() => Items::POTION()->setType(PotionType::STRONG_POISON));
$result->register("strong_poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_POISON));
$result->register("strong_regeneration_potion", fn() => Items::POTION()->setType(PotionType::STRONG_REGENERATION));
$result->register("strong_regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_REGENERATION));
$result->register("strong_slowness_potion", fn() => Items::POTION()->setType(PotionType::STRONG_SLOWNESS));
$result->register("strong_slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_SLOWNESS));
$result->register("strong_strength_potion", fn() => Items::POTION()->setType(PotionType::STRONG_STRENGTH));
$result->register("strong_strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_STRENGTH));
$result->register("strong_swiftness_potion", fn() => Items::POTION()->setType(PotionType::STRONG_SWIFTNESS));
$result->register("strong_swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_SWIFTNESS));
$result->register("strong_turtle_master_potion", fn() => Items::POTION()->setType(PotionType::STRONG_TURTLE_MASTER));
$result->register("strong_turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_TURTLE_MASTER));
$result->register("sugar", fn() => Items::SUGAR());
$result->register("suspicious_stew", fn() => Items::SUSPICIOUS_STEW());
$result->register("sweet_berries", fn() => Items::SWEET_BERRIES());
$result->register("swiftness_potion", fn() => Items::POTION()->setType(PotionType::SWIFTNESS));
$result->register("swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SWIFTNESS));
$result->register("thick_potion", fn() => Items::POTION()->setType(PotionType::THICK));
$result->register("thick_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::THICK));
$result->register("tonic", fn() => Items::MEDICINE()->setType(MedicineType::TONIC));
$result->register("torchflower_seeds", fn() => Items::TORCHFLOWER_SEEDS());
$result->register("totem", fn() => Items::TOTEM());
$result->register("turtle_helmet", fn() => Items::TURTLE_HELMET());
$result->register("turtle_master_potion", fn() => Items::POTION()->setType(PotionType::TURTLE_MASTER));
$result->register("turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::TURTLE_MASTER));
$result->register("turtle_shell_piece", fn() => Items::SCUTE());
$result->register("villager_spawn_egg", fn() => Items::VILLAGER_SPAWN_EGG());
$result->register("water_breathing_potion", fn() => Items::POTION()->setType(PotionType::WATER_BREATHING));
$result->register("water_breathing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WATER_BREATHING));
$result->register("water_bucket", fn() => Items::WATER_BUCKET());
$result->register("water_potion", fn() => Items::POTION()->setType(PotionType::WATER));
$result->register("water_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WATER));
$result->register("weakness_potion", fn() => Items::POTION()->setType(PotionType::WEAKNESS));
$result->register("weakness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WEAKNESS));
$result->register("wheat", fn() => Items::WHEAT());
$result->register("wheat_seeds", fn() => Items::WHEAT_SEEDS());
$result->register("wither_potion", fn() => Items::POTION()->setType(PotionType::WITHER));
$result->register("wither_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WITHER));
$result->register("wooden_axe", fn() => Items::WOODEN_AXE());
$result->register("wooden_hoe", fn() => Items::WOODEN_HOE());
$result->register("wooden_pickaxe", fn() => Items::WOODEN_PICKAXE());

View File

@ -0,0 +1,34 @@
<?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\item;
use pocketmine\block\Block;
use pocketmine\block\VanillaBlocks;
final class TorchflowerSeeds extends Item{
public function getBlock(?int $clickedFace = null) : Block{
return VanillaBlocks::TORCHFLOWER_CROP();
}
}

View File

@ -296,6 +296,7 @@ use function strtolower;
* @method static Item SUGAR()
* @method static SuspiciousStew SUSPICIOUS_STEW()
* @method static SweetBerries SWEET_BERRIES()
* @method static TorchflowerSeeds TORCHFLOWER_SEEDS()
* @method static Totem TOTEM()
* @method static TurtleHelmet TURTLE_HELMET()
* @method static SpawnEgg VILLAGER_SPAWN_EGG()
@ -538,6 +539,7 @@ final class VanillaItems{
self::register("sugar", new Item(new IID(Ids::SUGAR), "Sugar"));
self::register("suspicious_stew", new SuspiciousStew(new IID(Ids::SUSPICIOUS_STEW), "Suspicious Stew"));
self::register("sweet_berries", new SweetBerries(new IID(Ids::SWEET_BERRIES), "Sweet Berries"));
self::register("torchflower_seeds", new TorchflowerSeeds(new IID(Ids::TORCHFLOWER_SEEDS), "Torchflower Seeds"));
self::register("totem", new Totem(new IID(Ids::TOTEM), "Totem of Undying"));
self::register("warped_sign", new ItemBlockWallOrFloor(new IID(Ids::WARPED_SIGN), Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN()));
self::register("water_bucket", new LiquidBucket(new IID(Ids::WATER_BUCKET), "Water Bucket", Blocks::WATER()));

View File

@ -83,6 +83,9 @@ class LoginPacketHandler extends PacketHandler{
throw new PacketHandlingException("Invalid login UUID");
}
$uuid = Uuid::fromString($extraData->identity);
$arrClientData = (array) $clientData;
$arrClientData["TitleID"] = $extraData->titleId;
if($extraData->XUID !== ""){
$playerInfo = new XboxLivePlayerInfo(
$extraData->XUID,
@ -90,7 +93,7 @@ class LoginPacketHandler extends PacketHandler{
$uuid,
$skin,
$clientData->LanguageCode,
(array) $clientData
$arrClientData
);
}else{
$playerInfo = new PlayerInfo(
@ -98,7 +101,7 @@ class LoginPacketHandler extends PacketHandler{
$uuid,
$skin,
$clientData->LanguageCode,
(array) $clientData
$arrClientData
);
}
($this->playerInfoConsumer)($playerInfo);

View File

@ -2873,7 +2873,15 @@ class World implements ChunkManager{
}elseif($this->getTile($tilePosition) !== null){
$logger->error("Cannot add tile at x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z: Another tile is already at that position");
}else{
$this->addTile($tile);
$block = $this->getBlockAt($tilePosition->getFloorX(), $tilePosition->getFloorY(), $tilePosition->getFloorZ());
$expectedClass = $block->getIdInfo()->getTileClass();
if($expectedClass === null){
$logger->error("Cannot add tile at x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z: Block at that position (" . $block->getName() . ") does not expect a tile");
}elseif(!($tile instanceof $expectedClass)){
$logger->error("Cannot add tile at x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z: Tile is of wrong type (expected $expectedClass but have " . get_class($tile) . ")");
}else{
$this->addTile($tile);
}
}
}

View File

@ -28,8 +28,8 @@ final class LightPropagationContext{
/** @phpstan-var \SplQueue<array{int, int, int}> */
public \SplQueue $spreadQueue;
/**
* @var true[]
* @phpstan-var array<int, true>
* @var int[]|true[]
* @phpstan-var array<int, int|true>
*/
public array $spreadVisited = [];

View File

@ -137,6 +137,7 @@ abstract class LightUpdate{
while(!$context->spreadQueue->isEmpty()){
$touched++;
[$x, $y, $z] = $context->spreadQueue->dequeue();
$from = $context->spreadVisited[World::blockHash($x, $y, $z)];
unset($context->spreadVisited[World::blockHash($x, $y, $z)]);
@ -155,7 +156,11 @@ abstract class LightUpdate{
continue;
}
foreach(Facing::OFFSET as [$ox, $oy, $oz]){
foreach(Facing::OFFSET as $side => [$ox, $oy, $oz]){
if($from === $side){
//don't check the side that this node received its initial light from
continue;
}
$cx = $x + $ox;
$cy = $y + $oy;
$cz = $z + $oz;
@ -169,7 +174,7 @@ abstract class LightUpdate{
$lightArray = $this->getCurrentLightArray();
}
assert($subChunk !== null);
$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight, $context, $lightArray, $subChunk);
$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight, $context, $lightArray, $subChunk, $side);
}
}
@ -199,7 +204,7 @@ abstract class LightUpdate{
}
}
protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context, LightArray $lightArray, SubChunk $subChunk) : void{
protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context, LightArray $lightArray, SubChunk $subChunk, int $side) : void{
$lx = $x & SubChunk::COORD_MASK;
$ly = $y & SubChunk::COORD_MASK;
$lz = $z & SubChunk::COORD_MASK;
@ -210,7 +215,11 @@ abstract class LightUpdate{
$lightArray->set($lx, $ly, $lz, $potentialLight);
if(!isset($context->spreadVisited[$index = World::blockHash($x, $y, $z)]) && $potentialLight > 1){
$context->spreadVisited[$index] = true;
//Track where this node was lit from, to avoid checking the source again when we propagate from here
//TODO: In the future it might be worth tracking more than one adjacent source face in case multiple
//nodes try to light the same node. However, this is a rare case since the vast majority of calls are
//basic propagation with only one source anyway.
$context->spreadVisited[$index] = Facing::opposite($side);
$context->spreadQueue->enqueue([$x, $y, $z]);
}
}

File diff suppressed because one or more lines are too long

View File

@ -276,7 +276,7 @@ class ParserPacketHandler extends PacketHandler{
$meta = $descriptor->getMeta();
if($meta !== 32767){
$blockStateId = $this->blockTranslator->getBlockStateDictionary()->lookupStateIdFromIdMeta($data->name, $meta);
if($blockStateId !== null){
if($this->blockItemIdMap->lookupBlockId($data->name) !== null && $blockStateId !== null){
$blockState = $this->blockTranslator->getBlockStateDictionary()->generateDataFromStateId($blockStateId);
if($blockState !== null && count($blockState->getStates()) > 0){
$data->block_states = self::blockStatePropertiesToString($blockState);