mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-19 15:35:52 +00:00
commit
734ca1cc6b
103
changelogs/5.21.md
Normal file
103
changelogs/5.21.md
Normal file
@ -0,0 +1,103 @@
|
||||
# 5.21.0
|
||||
Released 3rd November 2024.
|
||||
|
||||
This is a minor feature release, including gameplay features and minor internals improvements.
|
||||
|
||||
**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace.
|
||||
Do not update plugin minimum API versions unless you need new features added in this release.
|
||||
|
||||
**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.**
|
||||
Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly.
|
||||
|
||||
## Gameplay
|
||||
- Added the following new blocks:
|
||||
- Campfire
|
||||
- Chiseled Copper
|
||||
- Chiseled Tuff
|
||||
- Chiseled Tuff Bricks
|
||||
- Copper Bulb
|
||||
- Copper Door
|
||||
- Copper Grate
|
||||
- Copper Trapdoor
|
||||
- Polished Tuff, Slabs, Stairs and Walls
|
||||
- Soul Campfire
|
||||
- Tuff Bricks, Slabs, Stairs and Walls
|
||||
- Tuff Slab, Stairs and Walls
|
||||
- Added the following new types of painting:
|
||||
- backyard
|
||||
- baroque
|
||||
- bouquet
|
||||
- cavebird
|
||||
- changing
|
||||
- cotan
|
||||
- endboss
|
||||
- fern
|
||||
- finding
|
||||
- humble
|
||||
- lowmist
|
||||
- meditative
|
||||
- orb
|
||||
- owlemons
|
||||
- passage
|
||||
- pond
|
||||
- prairie_ride
|
||||
- sunflowers
|
||||
- tides
|
||||
- unpacked
|
||||
- Armor slots are now properly restricted (on the server side) to only contain the appropriate type of armor or headwear.
|
||||
- Implemented Aqua Affinity enchantment. Since the server doesn't currently enforce any movement restrictions in water, this enchantment works based on client-side behaviour only.
|
||||
|
||||
## API
|
||||
### `pocketmine\block`
|
||||
- The following new API methods have been added:
|
||||
- `public ChiseledBookshelf->getLastInteractedSlot() : ?ChiseledBookshelfSlot`
|
||||
- `public ChiseledBookshelf->setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : $this`
|
||||
- The following new classes have been added:
|
||||
- `utils\CopperMaterial` - interface implemented by all copper-like blocks with oxidation and waxed properties
|
||||
- `CopperBulb`
|
||||
- `CopperDoor`
|
||||
- `CopperGrate`
|
||||
- `CopperTrapdoor`
|
||||
- `SoulCampfire`
|
||||
- `Campfire`
|
||||
- The following enums have new cases:
|
||||
- `utils\BannerPatternType` has new cases `FLOW` and `GUSTER`
|
||||
|
||||
### `pocketmine\crafting`
|
||||
- The following enums have new cases:
|
||||
- `FurnaceType` has new cases `CAMPFIRE` and `SOUL_CAMPFIRE`
|
||||
|
||||
### `pocketmine\event`
|
||||
- The following new classes have been added:
|
||||
- `block\CampfireCookEvent` - called when a campfire finishes cooking an item
|
||||
|
||||
### `pocketmine\inventory`
|
||||
- Added support for slot validators, which permit restricting the types of items a player can put into an inventory slot.
|
||||
- The following new classes have been added:
|
||||
- `transaction\action\SlotValidator` - interface
|
||||
- `transaction\action\CallbackSlotValidator` - class allowing a closure to be used for slot content validation
|
||||
- `SlotValidatedInventory` - implemented by inventories which support the use of slot validators
|
||||
|
||||
### `pocketmine\item`
|
||||
- The following new API methods have been added:
|
||||
- `public Item->getCooldownTag() : ?string` - returns the cooldown group this item belongs to, used for ensuring that, for example, different types of goat horns all respect a general horn cooldown
|
||||
- The following new classes have been added:
|
||||
- `ItemCooldownTags` - list of cooldown group tags used by PocketMine-MP
|
||||
|
||||
### `pocketmine\world\sound`
|
||||
- The following new classes have been added
|
||||
- `CampfireSound` - sound made by campfires while lit
|
||||
|
||||
## Tools
|
||||
- `tools/blockstate-upgrade-schema-utils.php` (formerly `generate-blockstate-upgrade-schema.php`) has several improvements:
|
||||
- Support for generating `flattenedProperties` rules as per [BedrockBlockUpgradeSchema 5.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/5.0.0)
|
||||
- Improved criteria for flattened property selection to minimize the amount of rules required
|
||||
- Several subcommands are now available:
|
||||
- `generate` - generates a schema from provided data
|
||||
- `update` - regenerates an existing schema in a newer format
|
||||
- `update-all` - regenerates a folder of existing schemas in a newer format (useful for updating `BedrockBlockUpgradeSchema` en masse)
|
||||
- `test` - verifies that a schema produces the results expected by provided data
|
||||
|
||||
## Internals
|
||||
- Fixed incorrect visibility of `createEntity` in spawn eggs.
|
||||
- Added support for newer `BedrockBlockUpgradeSchema` in `BlockStateUpgrader`.
|
@ -33,7 +33,7 @@
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.4.999",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~4.5.0+bedrock-1.21.40",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40",
|
||||
"pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40",
|
||||
"pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40",
|
||||
|
14
composer.lock
generated
14
composer.lock
generated
@ -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": "5c5882370131d2ae3a043819c05e6f9c",
|
||||
"content-hash": "b2fbf6e7a9d650341dc71fa4dd124681",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -127,16 +127,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "4.5.0",
|
||||
"version": "5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
|
||||
"reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1"
|
||||
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/7943b894e050d68dd21b5c7fa609827a4e2e30f1",
|
||||
"reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
|
||||
"reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -147,9 +147,9 @@
|
||||
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.5.0"
|
||||
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0"
|
||||
},
|
||||
"time": "2024-10-23T16:15:24+00:00"
|
||||
"time": "2024-11-03T14:13:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-data",
|
||||
|
@ -31,8 +31,8 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.20.2";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BASE_VERSION = "5.21.0";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
/**
|
||||
|
@ -745,8 +745,28 @@ final class BlockTypeIds{
|
||||
public const PITCHER_PLANT = 10715;
|
||||
public const PITCHER_CROP = 10716;
|
||||
public const DOUBLE_PITCHER_CROP = 10717;
|
||||
public const CAMPFIRE = 10718;
|
||||
public const SOUL_CAMPFIRE = 10719;
|
||||
public const TUFF_SLAB = 10720;
|
||||
public const TUFF_STAIRS = 10721;
|
||||
public const TUFF_WALL = 10722;
|
||||
public const CHISELED_TUFF = 10723;
|
||||
public const TUFF_BRICKS = 10724;
|
||||
public const TUFF_BRICK_SLAB = 10725;
|
||||
public const TUFF_BRICK_STAIRS = 10726;
|
||||
public const TUFF_BRICK_WALL = 10727;
|
||||
public const CHISELED_TUFF_BRICKS = 10728;
|
||||
public const POLISHED_TUFF = 10729;
|
||||
public const POLISHED_TUFF_SLAB = 10730;
|
||||
public const POLISHED_TUFF_STAIRS = 10731;
|
||||
public const POLISHED_TUFF_WALL = 10732;
|
||||
public const COPPER_BULB = 10733;
|
||||
public const COPPER_DOOR = 10734;
|
||||
public const COPPER_TRAPDOOR = 10735;
|
||||
public const CHISELED_COPPER = 10736;
|
||||
public const COPPER_GRATE = 10737;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10718;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10738;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
|
277
src/block/Campfire.php
Normal file
277
src/block/Campfire.php
Normal file
@ -0,0 +1,277 @@
|
||||
<?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\inventory\CampfireInventory;
|
||||
use pocketmine\block\tile\Campfire as TileCampfire;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\crafting\FurnaceRecipe;
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\entity\projectile\SplashPotion;
|
||||
use pocketmine\event\block\CampfireCookEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\item\PotionType;
|
||||
use pocketmine\item\Shovel;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\BlazeShootSound;
|
||||
use pocketmine\world\sound\FireExtinguishSound;
|
||||
use pocketmine\world\sound\FlintSteelSound;
|
||||
use pocketmine\world\sound\ItemFrameAddItemSound;
|
||||
use function count;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
|
||||
class Campfire extends Transparent{
|
||||
use HorizontalFacingTrait{
|
||||
HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState;
|
||||
}
|
||||
use LightableTrait{
|
||||
LightableTrait::describeBlockOnlyState as encodeLitState;
|
||||
}
|
||||
|
||||
private const UPDATE_INTERVAL_TICKS = 10;
|
||||
|
||||
protected CampfireInventory $inventory;
|
||||
|
||||
/**
|
||||
* @var int[] slot => ticks
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
protected array $cookingTimes = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$this->encodeFacingState($w);
|
||||
$this->encodeLitState($w);
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileCampfire){
|
||||
$this->inventory = $tile->getInventory();
|
||||
$this->cookingTimes = $tile->getCookingTimes();
|
||||
}else{
|
||||
$this->inventory = new CampfireInventory($this->position);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileCampfire){
|
||||
$tile->setCookingTimes($this->cookingTimes);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return $this->lit ? 15 : 0;
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::CHARCOAL()->setCount(2)
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)];
|
||||
}
|
||||
|
||||
public function getInventory() : CampfireInventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
protected function getFurnaceType() : FurnaceType{
|
||||
return FurnaceType::CAMPFIRE;
|
||||
}
|
||||
|
||||
protected function getEntityCollisionDamage() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of ticks during the item in the given slot has been cooked.
|
||||
*/
|
||||
public function setCookingTime(int $slot, int $time) : void{
|
||||
if($slot < 0 || $slot > 3){
|
||||
throw new \InvalidArgumentException("Slot must be in range 0-3");
|
||||
}
|
||||
if($time < 0 || $time > $this->getFurnaceType()->getCookDurationTicks()){
|
||||
throw new \InvalidArgumentException("CookingTime must be in range 0-" . $this->getFurnaceType()->getCookDurationTicks());
|
||||
}
|
||||
$this->cookingTimes[$slot] = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of ticks during the item in the given slot has been cooked.
|
||||
*/
|
||||
public function getCookingTime(int $slot) : int{
|
||||
return $this->cookingTimes[$slot] ?? 0;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->getSide(Facing::DOWN) instanceof Campfire){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
$this->facing = $player->getHorizontalFacing();
|
||||
}
|
||||
$this->lit = true;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if(!$this->lit){
|
||||
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){
|
||||
$item->pop();
|
||||
$this->ignite();
|
||||
$this->position->getWorld()->addSound($this->position, new BlazeShootSound());
|
||||
return true;
|
||||
}elseif($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
|
||||
if($item instanceof Durable){
|
||||
$item->applyDamage(1);
|
||||
}
|
||||
$this->ignite();
|
||||
return true;
|
||||
}
|
||||
}elseif($item instanceof Shovel){
|
||||
$item->applyDamage(1);
|
||||
$this->extinguish();
|
||||
return true;
|
||||
}
|
||||
|
||||
if($this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($this->getFurnaceType())->match($item) !== null){
|
||||
$ingredient = clone $item;
|
||||
$ingredient->setCount(1);
|
||||
if(count($this->inventory->addItem($ingredient)) === 0){
|
||||
$item->pop();
|
||||
$this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->lit && $this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){
|
||||
$this->extinguish();
|
||||
//TODO: Waterlogging
|
||||
}
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if(!$this->lit){
|
||||
if($entity->isOnFire()){
|
||||
$this->ignite();
|
||||
return false;
|
||||
}
|
||||
}elseif($entity instanceof Living){
|
||||
$entity->attack(new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, $this->getEntityCollisionDamage()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||
if($this->lit && $projectile instanceof SplashPotion && $projectile->getPotionType() === PotionType::WATER){
|
||||
$this->extinguish();
|
||||
}
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
if($this->lit){
|
||||
$items = $this->inventory->getContents();
|
||||
$furnaceType = $this->getFurnaceType();
|
||||
$maxCookDuration = $furnaceType->getCookDurationTicks();
|
||||
foreach($items as $slot => $item){
|
||||
$this->setCookingTime($slot, min($maxCookDuration, $this->getCookingTime($slot) + self::UPDATE_INTERVAL_TICKS));
|
||||
if($this->getCookingTime($slot) >= $maxCookDuration){
|
||||
$result =
|
||||
($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($item)) instanceof FurnaceRecipe ?
|
||||
$recipe->getResult() :
|
||||
VanillaItems::AIR();
|
||||
|
||||
$ev = new CampfireCookEvent($this, $slot, $item, $result);
|
||||
$ev->call();
|
||||
|
||||
if ($ev->isCancelled()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->inventory->setItem($slot, VanillaItems::AIR());
|
||||
$this->setCookingTime($slot, 0);
|
||||
$this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $ev->getResult());
|
||||
}
|
||||
}
|
||||
if(count($items) > 0){
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
}
|
||||
if(mt_rand(1, 6) === 1){
|
||||
$this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound());
|
||||
}
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
|
||||
}
|
||||
}
|
||||
|
||||
private function extinguish() : void{
|
||||
$this->position->getWorld()->addSound($this->position, new FireExtinguishSound());
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLit(false));
|
||||
}
|
||||
|
||||
private function ignite() : void{
|
||||
$this->position->getWorld()->addSound($this->position, new FlintSteelSound());
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLit(true));
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
|
||||
}
|
||||
}
|
@ -48,11 +48,32 @@ class ChiseledBookshelf extends Opaque{
|
||||
*/
|
||||
private array $slots = [];
|
||||
|
||||
private ?ChiseledBookshelfSlot $lastInteractedSlot = null;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileChiseledBookshelf){
|
||||
$this->lastInteractedSlot = $tile->getLastInteractedSlot();
|
||||
}else{
|
||||
$this->lastInteractedSlot = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileChiseledBookshelf){
|
||||
$tile->setLastInteractedSlot($this->lastInteractedSlot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -92,6 +113,23 @@ class ChiseledBookshelf extends Opaque{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last slot interacted by a player or null if no slot has been interacted with yet.
|
||||
*/
|
||||
public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{
|
||||
return $this->lastInteractedSlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last slot interacted by a player.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : self{
|
||||
$this->lastInteractedSlot = $lastInteractedSlot;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($face !== $this->facing){
|
||||
return false;
|
||||
@ -112,10 +150,12 @@ class ChiseledBookshelf extends Opaque{
|
||||
$returnedItems[] = $inventory->getItem($slot->value);
|
||||
$inventory->clear($slot->value);
|
||||
$this->setSlot($slot, false);
|
||||
$this->lastInteractedSlot = $slot;
|
||||
}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);
|
||||
$this->lastInteractedSlot = $slot;
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class Copper extends Opaque{
|
||||
class Copper extends Opaque implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
}
|
||||
|
69
src/block/CopperBulb.php
Normal file
69
src/block/CopperBulb.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?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\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
use pocketmine\block\utils\LightableTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
|
||||
class CopperBulb extends Opaque implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
use PoweredByRedstoneTrait;
|
||||
use LightableTrait{
|
||||
describeBlockOnlyState as encodeLitState;
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$this->encodeLitState($w);
|
||||
$w->bool($this->powered);
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function togglePowered(bool $powered) : self{
|
||||
if($powered === $this->powered){
|
||||
return $this;
|
||||
}
|
||||
if ($powered) {
|
||||
$this->setLit(!$this->lit);
|
||||
}
|
||||
$this->setPowered($powered);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
if ($this->lit) {
|
||||
return match($this->oxidation){
|
||||
CopperOxidation::NONE => 15,
|
||||
CopperOxidation::EXPOSED => 12,
|
||||
CopperOxidation::WEATHERED => 8,
|
||||
CopperOxidation::OXIDIZED => 4,
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
53
src/block/CopperDoor.php
Normal file
53
src/block/CopperDoor.php
Normal 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;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class CopperDoor extends Door implements CopperMaterial{
|
||||
use CopperTrait{
|
||||
onInteract as onInteractCopper;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) {
|
||||
//copy copper properties to other half
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
$world = $this->position->getWorld();
|
||||
if ($other instanceof CopperDoor) {
|
||||
$other->setOxidation($this->oxidation);
|
||||
$other->setWaxed($this->waxed);
|
||||
$world->setBlock($other->position, $other);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
|
||||
}
|
||||
}
|
33
src/block/CopperGrate.php
Normal file
33
src/block/CopperGrate.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?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\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperGrate extends Transparent implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
|
||||
//TODO: waterlogging!
|
||||
}
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperSlab extends Slab{
|
||||
class CopperSlab extends Slab implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
}
|
||||
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperStairs extends Stair{
|
||||
class CopperStairs extends Stair implements CopperMaterial{
|
||||
use CopperTrait;
|
||||
}
|
||||
|
44
src/block/CopperTrapdoor.php
Normal file
44
src/block/CopperTrapdoor.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?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\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class CopperTrapdoor extends Trapdoor implements CopperMaterial{
|
||||
use CopperTrait{
|
||||
onInteract as onInteractCopper;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if ($player !== null && $player->isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems);
|
||||
}
|
||||
}
|
48
src/block/SoulCampfire.php
Normal file
48
src/block/SoulCampfire.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?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\crafting\FurnaceType;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class SoulCampfire extends Campfire{
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return $this->lit ? 10 : 0;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaBlocks::SOUL_SOIL()->asItem()
|
||||
];
|
||||
}
|
||||
|
||||
protected function getEntityCollisionDamage() : int{
|
||||
return 2;
|
||||
}
|
||||
|
||||
protected function getFurnaceType() : FurnaceType{
|
||||
return FurnaceType::SOUL_CAMPFIRE;
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ use pocketmine\block\tile\Bed as TileBed;
|
||||
use pocketmine\block\tile\Bell as TileBell;
|
||||
use pocketmine\block\tile\BlastFurnace as TileBlastFurnace;
|
||||
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
|
||||
use pocketmine\block\tile\Campfire as TileCampfire;
|
||||
use pocketmine\block\tile\Cauldron as TileCauldron;
|
||||
use pocketmine\block\tile\Chest as TileChest;
|
||||
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
|
||||
@ -154,6 +155,7 @@ use function strtolower;
|
||||
* @method static CakeWithCandle CAKE_WITH_CANDLE()
|
||||
* @method static CakeWithDyedCandle CAKE_WITH_DYED_CANDLE()
|
||||
* @method static Opaque CALCITE()
|
||||
* @method static Campfire CAMPFIRE()
|
||||
* @method static Candle CANDLE()
|
||||
* @method static Carpet CARPET()
|
||||
* @method static Carrot CARROTS()
|
||||
@ -179,6 +181,7 @@ use function strtolower;
|
||||
* @method static Wood CHERRY_WOOD()
|
||||
* @method static Chest CHEST()
|
||||
* @method static ChiseledBookshelf CHISELED_BOOKSHELF()
|
||||
* @method static Copper CHISELED_COPPER()
|
||||
* @method static Opaque CHISELED_DEEPSLATE()
|
||||
* @method static Opaque CHISELED_NETHER_BRICKS()
|
||||
* @method static Opaque CHISELED_POLISHED_BLACKSTONE()
|
||||
@ -186,6 +189,8 @@ use function strtolower;
|
||||
* @method static Opaque CHISELED_RED_SANDSTONE()
|
||||
* @method static Opaque CHISELED_SANDSTONE()
|
||||
* @method static Opaque CHISELED_STONE_BRICKS()
|
||||
* @method static Opaque CHISELED_TUFF()
|
||||
* @method static Opaque CHISELED_TUFF_BRICKS()
|
||||
* @method static ChorusFlower CHORUS_FLOWER()
|
||||
* @method static ChorusPlant CHORUS_PLANT()
|
||||
* @method static Clay CLAY()
|
||||
@ -205,7 +210,11 @@ use function strtolower;
|
||||
* @method static Concrete CONCRETE()
|
||||
* @method static ConcretePowder CONCRETE_POWDER()
|
||||
* @method static Copper COPPER()
|
||||
* @method static CopperBulb COPPER_BULB()
|
||||
* @method static CopperDoor COPPER_DOOR()
|
||||
* @method static CopperGrate COPPER_GRATE()
|
||||
* @method static CopperOre COPPER_ORE()
|
||||
* @method static CopperTrapdoor COPPER_TRAPDOOR()
|
||||
* @method static Coral CORAL()
|
||||
* @method static CoralBlock CORAL_BLOCK()
|
||||
* @method static FloorCoralFan CORAL_FAN()
|
||||
@ -607,6 +616,10 @@ use function strtolower;
|
||||
* @method static Opaque POLISHED_GRANITE()
|
||||
* @method static Slab POLISHED_GRANITE_SLAB()
|
||||
* @method static Stair POLISHED_GRANITE_STAIRS()
|
||||
* @method static Opaque POLISHED_TUFF()
|
||||
* @method static Slab POLISHED_TUFF_SLAB()
|
||||
* @method static Stair POLISHED_TUFF_STAIRS()
|
||||
* @method static Wall POLISHED_TUFF_WALL()
|
||||
* @method static Flower POPPY()
|
||||
* @method static Potato POTATOES()
|
||||
* @method static PotionCauldron POTION_CAULDRON()
|
||||
@ -685,6 +698,7 @@ use function strtolower;
|
||||
* @method static Slab SMOOTH_STONE_SLAB()
|
||||
* @method static Snow SNOW()
|
||||
* @method static SnowLayer SNOW_LAYER()
|
||||
* @method static SoulCampfire SOUL_CAMPFIRE()
|
||||
* @method static SoulFire SOUL_FIRE()
|
||||
* @method static Lantern SOUL_LANTERN()
|
||||
* @method static SoulSand SOUL_SAND()
|
||||
@ -735,6 +749,13 @@ use function strtolower;
|
||||
* @method static Tripwire TRIPWIRE()
|
||||
* @method static TripwireHook TRIPWIRE_HOOK()
|
||||
* @method static Opaque TUFF()
|
||||
* @method static Opaque TUFF_BRICKS()
|
||||
* @method static Slab TUFF_BRICK_SLAB()
|
||||
* @method static Stair TUFF_BRICK_STAIRS()
|
||||
* @method static Wall TUFF_BRICK_WALL()
|
||||
* @method static Slab TUFF_SLAB()
|
||||
* @method static Stair TUFF_STAIRS()
|
||||
* @method static Wall TUFF_WALL()
|
||||
* @method static NetherVines TWISTING_VINES()
|
||||
* @method static UnderwaterTorch UNDERWATER_TORCH()
|
||||
* @method static Vine VINES()
|
||||
@ -826,6 +847,11 @@ final class VanillaBlocks{
|
||||
self::register("brown_mushroom", new BrownMushroom(new BID(Ids::BROWN_MUSHROOM), "Brown Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS])));
|
||||
self::register("cactus", new Cactus(new BID(Ids::CACTUS), "Cactus", new Info(new BreakInfo(0.4), [Tags::POTTABLE_PLANTS])));
|
||||
self::register("cake", new Cake(new BID(Ids::CAKE), "Cake", new Info(new BreakInfo(0.5))));
|
||||
|
||||
$campfireBreakInfo = new Info(BreakInfo::axe(2.0));
|
||||
self::register("campfire", new Campfire(new BID(Ids::CAMPFIRE, TileCampfire::class), "Campfire", $campfireBreakInfo));
|
||||
self::register("soul_campfire", new SoulCampfire(new BID(Ids::SOUL_CAMPFIRE, TileCampfire::class), "Soul Campfire", $campfireBreakInfo));
|
||||
|
||||
self::register("carrots", new Carrot(new BID(Ids::CARROTS), "Carrot Block", new Info(BreakInfo::instant())));
|
||||
|
||||
$chestBreakInfo = new Info(BreakInfo::axe(2.5));
|
||||
@ -1261,6 +1287,7 @@ final class VanillaBlocks{
|
||||
self::registerBlocksR17();
|
||||
self::registerBlocksR18();
|
||||
self::registerMudBlocks();
|
||||
self::registerTuffBlocks();
|
||||
|
||||
self::registerCraftingTables();
|
||||
self::registerChorusBlocks();
|
||||
@ -1568,7 +1595,6 @@ final class VanillaBlocks{
|
||||
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))));
|
||||
|
||||
self::register("raw_copper", new Opaque(new BID(Ids::RAW_COPPER), "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0))));
|
||||
self::register("raw_gold", new Opaque(new BID(Ids::RAW_GOLD), "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0))));
|
||||
@ -1621,9 +1647,16 @@ final class VanillaBlocks{
|
||||
self::register("lightning_rod", new LightningRod(new BID(Ids::LIGHTNING_ROD), "Lightning Rod", $copperBreakInfo));
|
||||
|
||||
self::register("copper", new Copper(new BID(Ids::COPPER), "Copper Block", $copperBreakInfo));
|
||||
self::register("chiseled_copper", new Copper(new BID(Ids::CHISELED_COPPER), "Chiseled Copper", $copperBreakInfo));
|
||||
self::register("copper_grate", new CopperGrate(new BID(Ids::COPPER_GRATE), "Copper Grate", $copperBreakInfo));
|
||||
self::register("cut_copper", new Copper(new BID(Ids::CUT_COPPER), "Cut Copper Block", $copperBreakInfo));
|
||||
self::register("cut_copper_slab", new CopperSlab(new BID(Ids::CUT_COPPER_SLAB), "Cut Copper Slab", $copperBreakInfo));
|
||||
self::register("cut_copper_stairs", new CopperStairs(new BID(Ids::CUT_COPPER_STAIRS), "Cut Copper Stairs", $copperBreakInfo));
|
||||
self::register("copper_bulb", new CopperBulb(new BID(Ids::COPPER_BULB), "Copper Bulb", $copperBreakInfo));
|
||||
|
||||
$copperDoorBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0));
|
||||
self::register("copper_door", new CopperDoor(new BID(Ids::COPPER_DOOR), "Copper Door", $copperDoorBreakInfo));
|
||||
self::register("copper_trapdoor", new CopperTrapdoor(new BID(Ids::COPPER_TRAPDOOR), "Copper Trapdoor", $copperDoorBreakInfo));
|
||||
|
||||
$candleBreakInfo = new Info(new BreakInfo(0.1));
|
||||
self::register("candle", new Candle(new BID(Ids::CANDLE), "Candle", $candleBreakInfo));
|
||||
@ -1659,6 +1692,27 @@ final class VanillaBlocks{
|
||||
self::register("mud_brick_wall", new Wall(new BID(Ids::MUD_BRICK_WALL), "Mud Brick Wall", $mudBricksBreakInfo));
|
||||
}
|
||||
|
||||
private static function registerTuffBlocks() : void{
|
||||
$tuffBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0));
|
||||
|
||||
self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", $tuffBreakInfo));
|
||||
self::register("tuff_slab", new Slab(new BID(Ids::TUFF_SLAB), "Tuff", $tuffBreakInfo));
|
||||
self::register("tuff_stairs", new Stair(new BID(Ids::TUFF_STAIRS), "Tuff Stairs", $tuffBreakInfo));
|
||||
self::register("tuff_wall", new Wall(new BID(Ids::TUFF_WALL), "Tuff Wall", $tuffBreakInfo));
|
||||
self::register("chiseled_tuff", new Opaque(new BID(Ids::CHISELED_TUFF), "Chiseled Tuff", $tuffBreakInfo));
|
||||
|
||||
self::register("tuff_bricks", new Opaque(new BID(Ids::TUFF_BRICKS), "Tuff Bricks", $tuffBreakInfo));
|
||||
self::register("tuff_brick_slab", new Slab(new BID(Ids::TUFF_BRICK_SLAB), "Tuff Brick", $tuffBreakInfo));
|
||||
self::register("tuff_brick_stairs", new Stair(new BID(Ids::TUFF_BRICK_STAIRS), "Tuff Brick Stairs", $tuffBreakInfo));
|
||||
self::register("tuff_brick_wall", new Wall(new BID(Ids::TUFF_BRICK_WALL), "Tuff Brick Wall", $tuffBreakInfo));
|
||||
self::register("chiseled_tuff_bricks", new Opaque(new BID(Ids::CHISELED_TUFF_BRICKS), "Chiseled Tuff Bricks", $tuffBreakInfo));
|
||||
|
||||
self::register("polished_tuff", new Opaque(new BID(Ids::POLISHED_TUFF), "Polished Tuff", $tuffBreakInfo));
|
||||
self::register("polished_tuff_slab", new Slab(new BID(Ids::POLISHED_TUFF_SLAB), "Polished Tuff", $tuffBreakInfo));
|
||||
self::register("polished_tuff_stairs", new Stair(new BID(Ids::POLISHED_TUFF_STAIRS), "Polished Tuff Stairs", $tuffBreakInfo));
|
||||
self::register("polished_tuff_wall", new Wall(new BID(Ids::POLISHED_TUFF_WALL), "Polished Tuff Wall", $tuffBreakInfo));
|
||||
}
|
||||
|
||||
private static function registerCauldronBlocks() : void{
|
||||
$cauldronBreakInfo = new Info(BreakInfo::pickaxe(2, ToolTier::WOOD));
|
||||
|
||||
|
40
src/block/inventory/CampfireInventory.php
Normal file
40
src/block/inventory/CampfireInventory.php
Normal 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\inventory;
|
||||
|
||||
use pocketmine\inventory\SimpleInventory;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
class CampfireInventory extends SimpleInventory implements BlockInventory{
|
||||
use BlockInventoryTrait;
|
||||
|
||||
public function __construct(Position $holder){
|
||||
$this->holder = $holder;
|
||||
parent::__construct(4);
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return 1;
|
||||
}
|
||||
}
|
143
src/block/tile/Campfire.php
Normal file
143
src/block/tile/Campfire.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?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\Campfire as BlockCampfire;
|
||||
use pocketmine\block\inventory\CampfireInventory;
|
||||
use pocketmine\inventory\CallbackInventoryListener;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\world\World;
|
||||
|
||||
class Campfire extends Spawnable implements Container{
|
||||
use ContainerTrait;
|
||||
|
||||
private const TAG_FIRST_INPUT_ITEM = "Item1"; //TAG_Compound
|
||||
private const TAG_SECOND_INPUT_ITEM = "Item2"; //TAG_Compound
|
||||
private const TAG_THIRD_INPUT_ITEM = "Item3"; //TAG_Compound
|
||||
private const TAG_FOURTH_INPUT_ITEM = "Item4"; //TAG_Compound
|
||||
|
||||
private const TAG_FIRST_COOKING_TIME = "ItemTime1"; //TAG_Int
|
||||
private const TAG_SECOND_COOKING_TIME = "ItemTime2"; //TAG_Int
|
||||
private const TAG_THIRD_COOKING_TIME = "ItemTime3"; //TAG_Int
|
||||
private const TAG_FOURTH_COOKING_TIME = "ItemTime4"; //TAG_Int
|
||||
|
||||
protected CampfireInventory $inventory;
|
||||
/** @var array<int, int> */
|
||||
private array $cookingTimes = [];
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
$this->inventory = new CampfireInventory($this->position);
|
||||
$this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange(
|
||||
static function(Inventory $unused) use ($world, $pos) : void{
|
||||
$block = $world->getBlock($pos);
|
||||
if($block instanceof BlockCampfire){
|
||||
$world->setBlock($pos, $block);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public function getInventory() : CampfireInventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function getRealInventory() : CampfireInventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
* @phpstan-return array<int, int>
|
||||
*/
|
||||
public function getCookingTimes() : array{
|
||||
return $this->cookingTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $cookingTimes
|
||||
* @phpstan-param array<int, int> $cookingTimes
|
||||
*/
|
||||
public function setCookingTimes(array $cookingTimes) : void{
|
||||
$this->cookingTimes = $cookingTimes;
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$items = [];
|
||||
$listeners = $this->inventory->getListeners()->toArray();
|
||||
$this->inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
|
||||
|
||||
foreach([
|
||||
[0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME],
|
||||
[1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME],
|
||||
[2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME],
|
||||
[3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME],
|
||||
] as [$slot, $itemTag, $cookingTimeTag]){
|
||||
if(($tag = $nbt->getTag($itemTag)) instanceof CompoundTag){
|
||||
$items[$slot] = Item::nbtDeserialize($tag);
|
||||
}
|
||||
if(($tag = $nbt->getTag($cookingTimeTag)) instanceof IntTag){
|
||||
$this->cookingTimes[$slot] = $tag->getValue();
|
||||
}
|
||||
}
|
||||
$this->inventory->setContents($items);
|
||||
$this->inventory->getListeners()->add(...$listeners);
|
||||
}
|
||||
|
||||
protected function writeSaveData(CompoundTag $nbt) : void{
|
||||
foreach([
|
||||
[0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME],
|
||||
[1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME],
|
||||
[2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME],
|
||||
[3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME],
|
||||
] as [$slot, $itemTag, $cookingTimeTag]){
|
||||
$item = $this->inventory->getItem($slot);
|
||||
if(!$item->isNull()){
|
||||
$nbt->setTag($itemTag, $item->nbtSerialize());
|
||||
if(isset($this->cookingTimes[$slot])){
|
||||
$nbt->setInt($cookingTimeTag, $this->cookingTimes[$slot]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
foreach([
|
||||
0 => self::TAG_FIRST_INPUT_ITEM,
|
||||
1 => self::TAG_SECOND_INPUT_ITEM,
|
||||
2 => self::TAG_THIRD_INPUT_ITEM,
|
||||
3 => self::TAG_FOURTH_INPUT_ITEM
|
||||
] as $slot => $tag){
|
||||
$item = $this->inventory->getItem($slot);
|
||||
if(!$item->isNull()){
|
||||
$nbt->setTag($tag, TypeConverter::getInstance()->getItemTranslator()->toNetworkNbt($item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -40,8 +40,12 @@ use function count;
|
||||
class ChiseledBookshelf extends Tile implements Container{
|
||||
use ContainerTrait;
|
||||
|
||||
private const TAG_LAST_INTERACTED_SLOT = "LastInteractedSlot"; //TAG_Int
|
||||
|
||||
private SimpleInventory $inventory;
|
||||
|
||||
private ?ChiseledBookshelfSlot $lastInteractedSlot = null;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
$this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases()));
|
||||
@ -55,12 +59,30 @@ class ChiseledBookshelf extends Tile implements Container{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$this->loadItems($nbt);
|
||||
public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{
|
||||
return $this->lastInteractedSlot;
|
||||
}
|
||||
|
||||
public function writeSaveData(CompoundTag $nbt) : void{
|
||||
public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : void{
|
||||
$this->lastInteractedSlot = $lastInteractedSlot;
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$this->loadItems($nbt);
|
||||
|
||||
$lastInteractedSlot = $nbt->getInt(self::TAG_LAST_INTERACTED_SLOT, 0);
|
||||
if($lastInteractedSlot !== 0){
|
||||
$this->lastInteractedSlot = ChiseledBookshelfSlot::tryFrom($lastInteractedSlot - 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected function writeSaveData(CompoundTag $nbt) : void{
|
||||
$this->saveItems($nbt);
|
||||
|
||||
$nbt->setInt(self::TAG_LAST_INTERACTED_SLOT, $this->lastInteractedSlot !== null ?
|
||||
$this->lastInteractedSlot->value + 1 :
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadItems(CompoundTag $tag) : void{
|
||||
|
@ -57,6 +57,7 @@ final class TileFactory{
|
||||
$this->register(Bell::class, ["Bell", "minecraft:bell"]);
|
||||
$this->register(BlastFurnace::class, ["BlastFurnace", "minecraft:blast_furnace"]);
|
||||
$this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]);
|
||||
$this->register(Campfire::class, ["Campfire", "minecraft:campfire"]);
|
||||
$this->register(Cauldron::class, ["Cauldron", "minecraft:cauldron"]);
|
||||
$this->register(Chest::class, ["Chest", "minecraft:chest"]);
|
||||
$this->register(ChiseledBookshelf::class, ["ChiseledBookshelf", "minecraft:chiseled_bookshelf"]);
|
||||
@ -79,7 +80,6 @@ final class TileFactory{
|
||||
$this->register(MobHead::class, ["Skull", "minecraft:skull"]);
|
||||
$this->register(GlowingItemFrame::class, ["GlowItemFrame"]);
|
||||
|
||||
//TODO: Campfire
|
||||
//TODO: ChalkboardBlock
|
||||
//TODO: ChemistryTable
|
||||
//TODO: CommandBlock
|
||||
|
@ -81,10 +81,12 @@ enum BannerPatternType{
|
||||
case DIAGONAL_RIGHT;
|
||||
case DIAGONAL_UP_LEFT;
|
||||
case DIAGONAL_UP_RIGHT;
|
||||
case FLOW;
|
||||
case FLOWER;
|
||||
case GLOBE;
|
||||
case GRADIENT;
|
||||
case GRADIENT_UP;
|
||||
case GUSTER;
|
||||
case HALF_HORIZONTAL;
|
||||
case HALF_HORIZONTAL_BOTTOM;
|
||||
case HALF_VERTICAL;
|
||||
|
38
src/block/utils/CopperMaterial.php
Normal file
38
src/block/utils/CopperMaterial.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Represents copper blocks that have oxidized and waxed variations.
|
||||
*/
|
||||
interface CopperMaterial{
|
||||
|
||||
public function getOxidation() : CopperOxidation;
|
||||
|
||||
public function setOxidation(CopperOxidation $oxidation) : CopperMaterial;
|
||||
|
||||
public function isWaxed() : bool;
|
||||
|
||||
public function setWaxed(bool $waxed) : CopperMaterial;
|
||||
}
|
@ -275,7 +275,8 @@ final class CraftingManagerFromDataHelper{
|
||||
"furnace" => FurnaceType::FURNACE,
|
||||
"blast_furnace" => FurnaceType::BLAST_FURNACE,
|
||||
"smoker" => FurnaceType::SMOKER,
|
||||
//TODO: campfire
|
||||
"campfire" => FurnaceType::CAMPFIRE,
|
||||
"soul_campfire" => FurnaceType::SOUL_CAMPFIRE,
|
||||
default => null
|
||||
};
|
||||
if($furnaceType === null){
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\utils\LegacyEnumShimTrait;
|
||||
use pocketmine\world\sound\BlastFurnaceSound;
|
||||
use pocketmine\world\sound\CampfireSound;
|
||||
use pocketmine\world\sound\FurnaceSound;
|
||||
use pocketmine\world\sound\SmokerSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
@ -35,8 +36,10 @@ use function spl_object_id;
|
||||
* These are retained for backwards compatibility only.
|
||||
*
|
||||
* @method static FurnaceType BLAST_FURNACE()
|
||||
* @method static FurnaceType CAMPFIRE()
|
||||
* @method static FurnaceType FURNACE()
|
||||
* @method static FurnaceType SMOKER()
|
||||
* @method static FurnaceType SOUL_CAMPFIRE()
|
||||
*
|
||||
* @phpstan-type TMetadata array{0: int, 1: Sound}
|
||||
*/
|
||||
@ -46,6 +49,8 @@ enum FurnaceType{
|
||||
case FURNACE;
|
||||
case BLAST_FURNACE;
|
||||
case SMOKER;
|
||||
case CAMPFIRE;
|
||||
case SOUL_CAMPFIRE;
|
||||
|
||||
/**
|
||||
* @phpstan-return TMetadata
|
||||
@ -58,6 +63,7 @@ enum FurnaceType{
|
||||
self::FURNACE => [200, new FurnaceSound()],
|
||||
self::BLAST_FURNACE => [100, new BlastFurnaceSound()],
|
||||
self::SMOKER => [100, new SmokerSound()],
|
||||
self::CAMPFIRE, self::SOUL_CAMPFIRE => [600, new CampfireSound()]
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -56,9 +56,11 @@ final class BannerPatternTypeIdMap{
|
||||
BannerPatternType::DIAGONAL_UP_LEFT => "ld",
|
||||
BannerPatternType::DIAGONAL_UP_RIGHT => "rud",
|
||||
BannerPatternType::FLOWER => "flo",
|
||||
BannerPatternType::FLOW => "flw",
|
||||
BannerPatternType::GLOBE => "glb",
|
||||
BannerPatternType::GRADIENT => "gra",
|
||||
BannerPatternType::GRADIENT_UP => "gru",
|
||||
BannerPatternType::GUSTER => "gus",
|
||||
BannerPatternType::HALF_HORIZONTAL => "hh",
|
||||
BannerPatternType::HALF_HORIZONTAL_BOTTOM => "hhb",
|
||||
BannerPatternType::HALF_VERTICAL => "vh",
|
||||
|
@ -43,6 +43,7 @@ final class EnchantmentIdMap{
|
||||
$this->register(EnchantmentIds::PROJECTILE_PROTECTION, VanillaEnchantments::PROJECTILE_PROTECTION());
|
||||
$this->register(EnchantmentIds::THORNS, VanillaEnchantments::THORNS());
|
||||
$this->register(EnchantmentIds::RESPIRATION, VanillaEnchantments::RESPIRATION());
|
||||
$this->register(EnchantmentIds::AQUA_AFFINITY, VanillaEnchantments::AQUA_AFFINITY());
|
||||
|
||||
$this->register(EnchantmentIds::SHARPNESS, VanillaEnchantments::SHARPNESS());
|
||||
//TODO: smite, bane of arthropods (these don't make sense now because their applicable mobs don't exist yet)
|
||||
|
@ -43,6 +43,7 @@ use pocketmine\block\Cactus;
|
||||
use pocketmine\block\Cake;
|
||||
use pocketmine\block\CakeWithCandle;
|
||||
use pocketmine\block\CakeWithDyedCandle;
|
||||
use pocketmine\block\Campfire;
|
||||
use pocketmine\block\Candle;
|
||||
use pocketmine\block\Carpet;
|
||||
use pocketmine\block\Carrot;
|
||||
@ -57,8 +58,12 @@ use pocketmine\block\CocoaBlock;
|
||||
use pocketmine\block\Concrete;
|
||||
use pocketmine\block\ConcretePowder;
|
||||
use pocketmine\block\Copper;
|
||||
use pocketmine\block\CopperBulb;
|
||||
use pocketmine\block\CopperDoor;
|
||||
use pocketmine\block\CopperGrate;
|
||||
use pocketmine\block\CopperSlab;
|
||||
use pocketmine\block\CopperStairs;
|
||||
use pocketmine\block\CopperTrapdoor;
|
||||
use pocketmine\block\Coral;
|
||||
use pocketmine\block\CoralBlock;
|
||||
use pocketmine\block\DaylightSensor;
|
||||
@ -124,6 +129,7 @@ use pocketmine\block\SimplePressurePlate;
|
||||
use pocketmine\block\Slab;
|
||||
use pocketmine\block\SmallDripleaf;
|
||||
use pocketmine\block\SnowLayer;
|
||||
use pocketmine\block\SoulCampfire;
|
||||
use pocketmine\block\Sponge;
|
||||
use pocketmine\block\StainedGlass;
|
||||
use pocketmine\block\StainedGlassPane;
|
||||
@ -791,6 +797,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE);
|
||||
$this->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE);
|
||||
$this->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS);
|
||||
$this->mapSimple(Blocks::CHISELED_TUFF(), Ids::CHISELED_TUFF);
|
||||
$this->mapSimple(Blocks::CHISELED_TUFF_BRICKS(), Ids::CHISELED_TUFF_BRICKS);
|
||||
$this->mapSimple(Blocks::CHORUS_PLANT(), Ids::CHORUS_PLANT);
|
||||
$this->mapSimple(Blocks::CLAY(), Ids::CLAY);
|
||||
$this->mapSimple(Blocks::COAL(), Ids::COAL_BLOCK);
|
||||
@ -1014,6 +1022,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::POLISHED_DEEPSLATE(), Ids::POLISHED_DEEPSLATE);
|
||||
$this->mapSimple(Blocks::POLISHED_DIORITE(), Ids::POLISHED_DIORITE);
|
||||
$this->mapSimple(Blocks::POLISHED_GRANITE(), Ids::POLISHED_GRANITE);
|
||||
$this->mapSimple(Blocks::POLISHED_TUFF(), Ids::POLISHED_TUFF);
|
||||
$this->mapSimple(Blocks::PRISMARINE(), Ids::PRISMARINE);
|
||||
$this->mapSimple(Blocks::PRISMARINE_BRICKS(), Ids::PRISMARINE_BRICKS);
|
||||
$this->mapSimple(Blocks::QUARTZ_BRICKS(), Ids::QUARTZ_BRICKS);
|
||||
@ -1049,6 +1058,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS);
|
||||
$this->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER);
|
||||
$this->mapSimple(Blocks::TUFF(), Ids::TUFF);
|
||||
$this->mapSimple(Blocks::TUFF_BRICKS(), Ids::TUFF_BRICKS);
|
||||
$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);
|
||||
@ -1187,6 +1197,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
return Writer::create(Ids::CAKE)
|
||||
->writeInt(StateNames::BITE_COUNTER, $block->getBites());
|
||||
});
|
||||
$this->map(Blocks::CAMPFIRE(), function(Campfire $block) : Writer{
|
||||
return Writer::create(Ids::CAMPFIRE)
|
||||
->writeCardinalHorizontalFacing($block->getFacing())
|
||||
->writeBool(StateNames::EXTINGUISHED, !$block->isLit());
|
||||
});
|
||||
$this->map(Blocks::CARROTS(), fn(Carrot $block) => Helper::encodeCrops($block, new Writer(Ids::CARROTS)));
|
||||
$this->map(Blocks::CARVED_PUMPKIN(), function(CarvedPumpkin $block) : Writer{
|
||||
return Writer::create(Ids::CARVED_PUMPKIN)
|
||||
@ -1238,6 +1253,40 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER)
|
||||
);
|
||||
});
|
||||
$this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{
|
||||
$oxidation = $block->getOxidation();
|
||||
return new Writer($block->isWaxed() ?
|
||||
Helper::selectCopperId($oxidation,
|
||||
Ids::WAXED_CHISELED_COPPER,
|
||||
Ids::WAXED_EXPOSED_CHISELED_COPPER,
|
||||
Ids::WAXED_WEATHERED_CHISELED_COPPER,
|
||||
Ids::WAXED_OXIDIZED_CHISELED_COPPER
|
||||
) :
|
||||
Helper::selectCopperId($oxidation,
|
||||
Ids::CHISELED_COPPER,
|
||||
Ids::EXPOSED_CHISELED_COPPER,
|
||||
Ids::WEATHERED_CHISELED_COPPER,
|
||||
Ids::OXIDIZED_CHISELED_COPPER
|
||||
)
|
||||
);
|
||||
});
|
||||
$this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{
|
||||
$oxidation = $block->getOxidation();
|
||||
return new Writer($block->isWaxed() ?
|
||||
Helper::selectCopperId($oxidation,
|
||||
Ids::WAXED_COPPER_GRATE,
|
||||
Ids::WAXED_EXPOSED_COPPER_GRATE,
|
||||
Ids::WAXED_WEATHERED_COPPER_GRATE,
|
||||
Ids::WAXED_OXIDIZED_COPPER_GRATE
|
||||
) :
|
||||
Helper::selectCopperId($oxidation,
|
||||
Ids::COPPER_GRATE,
|
||||
Ids::EXPOSED_COPPER_GRATE,
|
||||
Ids::WEATHERED_COPPER_GRATE,
|
||||
Ids::OXIDIZED_COPPER_GRATE
|
||||
)
|
||||
);
|
||||
});
|
||||
$this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{
|
||||
$oxidation = $block->getOxidation();
|
||||
return new Writer($block->isWaxed() ?
|
||||
@ -1305,6 +1354,67 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
)
|
||||
);
|
||||
});
|
||||
$this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{
|
||||
$oxidation = $block->getOxidation();
|
||||
return Writer::create($block->isWaxed() ?
|
||||
Helper::selectCopperId($oxidation,
|
||||
Ids::WAXED_COPPER_BULB,
|
||||
Ids::WAXED_EXPOSED_COPPER_BULB,
|
||||
Ids::WAXED_WEATHERED_COPPER_BULB,
|
||||
Ids::WAXED_OXIDIZED_COPPER_BULB) :
|
||||
Helper::selectCopperId($oxidation,
|
||||
Ids::COPPER_BULB,
|
||||
Ids::EXPOSED_COPPER_BULB,
|
||||
Ids::WEATHERED_COPPER_BULB,
|
||||
Ids::OXIDIZED_COPPER_BULB
|
||||
))
|
||||
->writeBool(StateNames::LIT, $block->isLit())
|
||||
->writeBool(StateNames::POWERED_BIT, $block->isPowered());
|
||||
});
|
||||
$this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{
|
||||
$oxidation = $block->getOxidation();
|
||||
return Helper::encodeDoor(
|
||||
$block,
|
||||
new Writer($block->isWaxed() ?
|
||||
Helper::selectCopperId(
|
||||
$oxidation,
|
||||
Ids::WAXED_COPPER_DOOR,
|
||||
Ids::WAXED_EXPOSED_COPPER_DOOR,
|
||||
Ids::WAXED_WEATHERED_COPPER_DOOR,
|
||||
Ids::WAXED_OXIDIZED_COPPER_DOOR
|
||||
) :
|
||||
Helper::selectCopperId(
|
||||
$oxidation,
|
||||
Ids::COPPER_DOOR,
|
||||
Ids::EXPOSED_COPPER_DOOR,
|
||||
Ids::WEATHERED_COPPER_DOOR,
|
||||
Ids::OXIDIZED_COPPER_DOOR
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
$this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{
|
||||
$oxidation = $block->getOxidation();
|
||||
return Helper::encodeTrapdoor(
|
||||
$block,
|
||||
new Writer($block->isWaxed() ?
|
||||
Helper::selectCopperId(
|
||||
$oxidation,
|
||||
Ids::WAXED_COPPER_TRAPDOOR,
|
||||
Ids::WAXED_EXPOSED_COPPER_TRAPDOOR,
|
||||
Ids::WAXED_WEATHERED_COPPER_TRAPDOOR,
|
||||
Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR
|
||||
) :
|
||||
Helper::selectCopperId(
|
||||
$oxidation,
|
||||
Ids::COPPER_TRAPDOOR,
|
||||
Ids::EXPOSED_COPPER_TRAPDOOR,
|
||||
Ids::WEATHERED_COPPER_TRAPDOOR,
|
||||
Ids::OXIDIZED_COPPER_TRAPDOOR
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
$this->map(Blocks::COCOA_POD(), function(CocoaBlock $block) : Writer{
|
||||
return Writer::create(Ids::COCOA)
|
||||
->writeInt(StateNames::AGE, $block->getAge())
|
||||
@ -1546,6 +1656,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
$this->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS);
|
||||
$this->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS);
|
||||
$this->mapSlab(Blocks::POLISHED_TUFF_SLAB(), Ids::POLISHED_TUFF_SLAB, Ids::POLISHED_TUFF_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::POLISHED_TUFF_STAIRS(), Ids::POLISHED_TUFF_STAIRS);
|
||||
$this->map(Blocks::POLISHED_TUFF_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_TUFF_WALL)));
|
||||
$this->map(Blocks::POTATOES(), fn(Potato $block) => Helper::encodeCrops($block, new Writer(Ids::POTATOES)));
|
||||
$this->map(Blocks::POWERED_RAIL(), function(PoweredRail $block) : Writer{
|
||||
return Writer::create(Ids::GOLDEN_RAIL)
|
||||
@ -1635,6 +1748,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeBool(StateNames::COVERED_BIT, false)
|
||||
->writeInt(StateNames::HEIGHT, $block->getLayers() - 1);
|
||||
});
|
||||
$this->map(Blocks::SOUL_CAMPFIRE(), function(SoulCampfire $block) : Writer{
|
||||
return Writer::create(Ids::SOUL_CAMPFIRE)
|
||||
->writeCardinalHorizontalFacing($block->getFacing())
|
||||
->writeBool(StateNames::EXTINGUISHED, !$block->isLit());
|
||||
});
|
||||
$this->map(Blocks::SOUL_FIRE(), function() : Writer{
|
||||
return Writer::create(Ids::SOUL_FIRE)
|
||||
->writeInt(StateNames::AGE, 0); //useless for soul fire, we don't track it
|
||||
@ -1694,6 +1812,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
|
||||
->writeBool(StateNames::POWERED_BIT, $block->isPowered())
|
||||
->writeLegacyHorizontalFacing($block->getFacing());
|
||||
});
|
||||
$this->mapSlab(Blocks::TUFF_BRICK_SLAB(), Ids::TUFF_BRICK_SLAB, Ids::TUFF_BRICK_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::TUFF_BRICK_STAIRS(), Ids::TUFF_BRICK_STAIRS);
|
||||
$this->map(Blocks::TUFF_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::TUFF_BRICK_WALL)));
|
||||
$this->mapSlab(Blocks::TUFF_SLAB(), Ids::TUFF_SLAB, Ids::TUFF_DOUBLE_SLAB);
|
||||
$this->mapStairs(Blocks::TUFF_STAIRS(), Ids::TUFF_STAIRS);
|
||||
$this->map(Blocks::TUFF_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::TUFF_WALL)));
|
||||
$this->map(Blocks::TWISTING_VINES(), function(NetherVines $block) : Writer{
|
||||
return Writer::create(Ids::TWISTING_VINES)
|
||||
->writeInt(StateNames::TWISTING_VINES_AGE, $block->getAge());
|
||||
|
@ -26,9 +26,6 @@ namespace pocketmine\data\bedrock\block\convert;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Button;
|
||||
use pocketmine\block\Candle;
|
||||
use pocketmine\block\Copper;
|
||||
use pocketmine\block\CopperSlab;
|
||||
use pocketmine\block\CopperStairs;
|
||||
use pocketmine\block\Crops;
|
||||
use pocketmine\block\DaylightSensor;
|
||||
use pocketmine\block\Door;
|
||||
@ -48,6 +45,7 @@ use pocketmine\block\Slab;
|
||||
use pocketmine\block\Stair;
|
||||
use pocketmine\block\Stem;
|
||||
use pocketmine\block\Trapdoor;
|
||||
use pocketmine\block\utils\CopperMaterial;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\Wall;
|
||||
@ -99,24 +97,24 @@ final class BlockStateDeserializerHelper{
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TBlock of Copper|CopperSlab|CopperStairs
|
||||
* @phpstan-template TBlock of CopperMaterial
|
||||
*
|
||||
* @phpstan-param TBlock $block
|
||||
* @phpstan-return TBlock
|
||||
*/
|
||||
public static function decodeCopper(Copper|CopperSlab|CopperStairs $block, CopperOxidation $oxidation) : Copper|CopperSlab|CopperStairs{
|
||||
public static function decodeCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{
|
||||
$block->setOxidation($oxidation);
|
||||
$block->setWaxed(false);
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TBlock of Copper|CopperSlab|CopperStairs
|
||||
* @phpstan-template TBlock of CopperMaterial
|
||||
*
|
||||
* @phpstan-param TBlock $block
|
||||
* @phpstan-return TBlock
|
||||
*/
|
||||
public static function decodeWaxedCopper(Copper|CopperSlab|CopperStairs $block, CopperOxidation $oxidation) : Copper|CopperSlab|CopperStairs{
|
||||
public static function decodeWaxedCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{
|
||||
$block->setOxidation($oxidation);
|
||||
$block->setWaxed(true);
|
||||
return $block;
|
||||
|
@ -722,6 +722,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::CHISELED_RED_SANDSTONE, fn() => Blocks::CHISELED_RED_SANDSTONE());
|
||||
$this->mapSimple(Ids::CHISELED_SANDSTONE, fn() => Blocks::CHISELED_SANDSTONE());
|
||||
$this->mapSimple(Ids::CHISELED_STONE_BRICKS, fn() => Blocks::CHISELED_STONE_BRICKS());
|
||||
$this->mapSimple(Ids::CHISELED_TUFF, fn() => Blocks::CHISELED_TUFF());
|
||||
$this->mapSimple(Ids::CHISELED_TUFF_BRICKS, fn() => Blocks::CHISELED_TUFF_BRICKS());
|
||||
$this->mapSimple(Ids::CHORUS_PLANT, fn() => Blocks::CHORUS_PLANT());
|
||||
$this->mapSimple(Ids::CLAY, fn() => Blocks::CLAY());
|
||||
$this->mapSimple(Ids::COAL_BLOCK, fn() => Blocks::COAL());
|
||||
@ -940,6 +942,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapSimple(Ids::POLISHED_DEEPSLATE, fn() => Blocks::POLISHED_DEEPSLATE());
|
||||
$this->mapSimple(Ids::POLISHED_DIORITE, fn() => Blocks::POLISHED_DIORITE());
|
||||
$this->mapSimple(Ids::POLISHED_GRANITE, fn() => Blocks::POLISHED_GRANITE());
|
||||
$this->mapSimple(Ids::POLISHED_TUFF, fn() => Blocks::POLISHED_TUFF());
|
||||
$this->mapSimple(Ids::PRISMARINE, fn() => Blocks::PRISMARINE());
|
||||
$this->mapSimple(Ids::PRISMARINE_BRICKS, fn() => Blocks::PRISMARINE_BRICKS());
|
||||
$this->mapSimple(Ids::QUARTZ_BRICKS, fn() => Blocks::QUARTZ_BRICKS());
|
||||
@ -977,6 +980,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$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::TUFF_BRICKS, fn() => Blocks::TUFF_BRICKS());
|
||||
$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());
|
||||
@ -1123,6 +1127,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
return Blocks::CAKE()
|
||||
->setBites($in->readBoundedInt(StateNames::BITE_COUNTER, 0, 6));
|
||||
});
|
||||
$this->map(Ids::CAMPFIRE, function(Reader $in) : Block{
|
||||
return Blocks::CAMPFIRE()
|
||||
->setFacing($in->readCardinalHorizontalFacing())
|
||||
->setLit(!$in->readBool(StateNames::EXTINGUISHED));
|
||||
});
|
||||
$this->map(Ids::CARROTS, fn(Reader $in) => Helper::decodeCrops(Blocks::CARROTS(), $in));
|
||||
$this->map(Ids::CARVED_PUMPKIN, function(Reader $in) : Block{
|
||||
return Blocks::CARVED_PUMPKIN()
|
||||
@ -1162,6 +1171,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
|
||||
return $block;
|
||||
});
|
||||
$this->map(Ids::CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE));
|
||||
$this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{
|
||||
return Blocks::CHISELED_QUARTZ()
|
||||
->setAxis($in->readPillarAxis());
|
||||
@ -1193,6 +1203,14 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing(Facing::opposite($in->readLegacyHorizontalFacing()))
|
||||
);
|
||||
$this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE));
|
||||
$this->map(Ids::COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
|
||||
$this->map(Ids::COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
|
||||
$this->map(Ids::COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
|
||||
$this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
|
||||
$this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
|
||||
$this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
|
||||
@ -1251,9 +1269,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing($in->readCardinalHorizontalFacing());
|
||||
});
|
||||
$this->map(Ids::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::EXPOSED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::EXPOSED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
|
||||
$this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
|
||||
$this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::EXPOSED_COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
|
||||
$this->map(Ids::EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
|
||||
$this->map(Ids::FARMLAND, function(Reader $in) : Block{
|
||||
return Blocks::FARMLAND()
|
||||
->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7));
|
||||
@ -1407,9 +1434,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS());
|
||||
$this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis()));
|
||||
$this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::OXIDIZED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
|
||||
$this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
|
||||
$this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
|
||||
$this->map(Ids::OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
|
||||
$this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis()));
|
||||
$this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB());
|
||||
$this->map(Ids::PINK_PETALS, function(Reader $in) : Block{
|
||||
@ -1456,6 +1492,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$this->mapStairs(Ids::POLISHED_DIORITE_STAIRS, fn() => Blocks::POLISHED_DIORITE_STAIRS());
|
||||
$this->mapSlab(Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_GRANITE_SLAB());
|
||||
$this->mapStairs(Ids::POLISHED_GRANITE_STAIRS, fn() => Blocks::POLISHED_GRANITE_STAIRS());
|
||||
$this->mapSlab(Ids::POLISHED_TUFF_SLAB, Ids::POLISHED_TUFF_DOUBLE_SLAB, fn() => Blocks::POLISHED_TUFF_SLAB());
|
||||
$this->mapStairs(Ids::POLISHED_TUFF_STAIRS, fn() => Blocks::POLISHED_TUFF_STAIRS());
|
||||
$this->map(Ids::POLISHED_TUFF_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_TUFF_WALL(), $in));
|
||||
$this->map(Ids::PORTAL, function(Reader $in) : Block{
|
||||
return Blocks::NETHER_PORTAL()
|
||||
->setAxis(match($value = $in->readString(StateNames::PORTAL_AXIS)){
|
||||
@ -1566,6 +1605,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
$in->ignored(StateNames::COVERED_BIT); //seems to be useless
|
||||
return Blocks::SNOW_LAYER()->setLayers($in->readBoundedInt(StateNames::HEIGHT, 0, 7) + 1);
|
||||
});
|
||||
$this->map(Ids::SOUL_CAMPFIRE, function(Reader $in) : Block{
|
||||
return Blocks::SOUL_CAMPFIRE()
|
||||
->setFacing($in->readCardinalHorizontalFacing())
|
||||
->setLit(!$in->readBool(StateNames::EXTINGUISHED));
|
||||
});
|
||||
$this->map(Ids::SOUL_FIRE, function(Reader $in) : Block{
|
||||
$in->ignored(StateNames::AGE); //this is useless for soul fire, since it doesn't have the logic associated
|
||||
return Blocks::SOUL_FIRE();
|
||||
@ -1629,6 +1673,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
->setFacing($in->readLegacyHorizontalFacing())
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->mapSlab(Ids::TUFF_BRICK_SLAB, Ids::TUFF_BRICK_DOUBLE_SLAB, fn() => Blocks::TUFF_BRICK_SLAB());
|
||||
$this->mapStairs(Ids::TUFF_BRICK_STAIRS, fn() => Blocks::TUFF_BRICK_STAIRS());
|
||||
$this->map(Ids::TUFF_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::TUFF_BRICK_WALL(), $in));
|
||||
$this->mapSlab(Ids::TUFF_SLAB, Ids::TUFF_DOUBLE_SLAB, fn() => Blocks::TUFF_SLAB());
|
||||
$this->mapStairs(Ids::TUFF_STAIRS, fn() => Blocks::TUFF_STAIRS());
|
||||
$this->map(Ids::TUFF_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::TUFF_WALL(), $in));
|
||||
$this->map(Ids::TWISTING_VINES, function(Reader $in) : Block{
|
||||
return Blocks::TWISTING_VINES()
|
||||
->setAge($in->readBoundedInt(StateNames::TWISTING_VINES_AGE, 0, 25));
|
||||
@ -1665,25 +1715,70 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
});
|
||||
$this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in));
|
||||
$this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE));
|
||||
$this->map(Ids::WAXED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE));
|
||||
$this->map(Ids::WAXED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE));
|
||||
$this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
|
||||
$this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
|
||||
$this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
|
||||
$this->map(Ids::WAXED_COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::WAXED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in));
|
||||
$this->map(Ids::WAXED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in));
|
||||
$this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::WAXED_EXPOSED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::WAXED_EXPOSED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
|
||||
$this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
|
||||
$this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
|
||||
$this->map(Ids::WAXED_EXPOSED_COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::WAXED_EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in));
|
||||
$this->map(Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in));
|
||||
$this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::WAXED_OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::WAXED_OXIDIZED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
|
||||
$this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
|
||||
$this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
|
||||
$this->map(Ids::WAXED_OXIDIZED_COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::WAXED_OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in));
|
||||
$this->map(Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in));
|
||||
$this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WAXED_WEATHERED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WAXED_WEATHERED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
|
||||
$this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
|
||||
$this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WAXED_WEATHERED_COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::WAXED_WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
|
||||
$this->map(Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
|
||||
$this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WEATHERED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WEATHERED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
|
||||
$this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
|
||||
$this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
|
||||
$this->map(Ids::WEATHERED_COPPER_BULB, function(Reader $in) : Block{
|
||||
return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED)
|
||||
->setLit($in->readBool(StateNames::LIT))
|
||||
->setPowered($in->readBool(StateNames::POWERED_BIT));
|
||||
});
|
||||
$this->map(Ids::WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in));
|
||||
$this->map(Ids::WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in));
|
||||
$this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{
|
||||
return Blocks::WEEPING_VINES()
|
||||
->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25));
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenInfo as FlattenInfo;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap as ValueRemap;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use function count;
|
||||
@ -58,6 +59,12 @@ final class BlockStateUpgradeSchema{
|
||||
*/
|
||||
public array $remappedPropertyValues = [];
|
||||
|
||||
/**
|
||||
* @var FlattenInfo[]
|
||||
* @phpstan-var array<string, FlattenInfo>
|
||||
*/
|
||||
public array $flattenedProperties = [];
|
||||
|
||||
/**
|
||||
* @var BlockStateUpgradeSchemaBlockRemap[][]
|
||||
* @phpstan-var array<string, list<BlockStateUpgradeSchemaBlockRemap>>
|
||||
@ -93,6 +100,7 @@ final class BlockStateUpgradeSchema{
|
||||
$this->removedProperties,
|
||||
$this->renamedProperties,
|
||||
$this->remappedPropertyValues,
|
||||
$this->flattenedProperties,
|
||||
$this->remappedStates,
|
||||
] as $list){
|
||||
if(count($list) !== 0){
|
||||
|
@ -40,7 +40,7 @@ final class BlockStateUpgradeSchemaBlockRemap{
|
||||
*/
|
||||
public function __construct(
|
||||
public array $oldState,
|
||||
public string|BlockStateUpgradeSchemaFlattenedName $newName,
|
||||
public string|BlockStateUpgradeSchemaFlattenInfo $newName,
|
||||
public array $newState,
|
||||
public array $copiedState
|
||||
){}
|
||||
@ -48,8 +48,8 @@ final class BlockStateUpgradeSchemaBlockRemap{
|
||||
public function equals(self $that) : bool{
|
||||
$sameName = $this->newName === $that->newName ||
|
||||
(
|
||||
$this->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
|
||||
$that->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
|
||||
$this->newName instanceof BlockStateUpgradeSchemaFlattenInfo &&
|
||||
$that->newName instanceof BlockStateUpgradeSchemaFlattenInfo &&
|
||||
$this->newName->equals($that->newName)
|
||||
);
|
||||
if(!$sameName){
|
||||
|
@ -23,20 +23,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use function ksort;
|
||||
use const SORT_STRING;
|
||||
|
||||
final class BlockStateUpgradeSchemaFlattenedName{
|
||||
final class BlockStateUpgradeSchemaFlattenInfo{
|
||||
|
||||
/**
|
||||
* @param string[] $flattenedValueRemaps
|
||||
* @phpstan-param array<string, string> $flattenedValueRemaps
|
||||
* @phpstan-param ?class-string<ByteTag|IntTag|StringTag> $flattenedPropertyType
|
||||
*/
|
||||
public function __construct(
|
||||
public string $prefix,
|
||||
public string $flattenedProperty,
|
||||
public string $suffix,
|
||||
public array $flattenedValueRemaps
|
||||
public array $flattenedValueRemaps,
|
||||
public ?string $flattenedPropertyType = null
|
||||
){
|
||||
ksort($this->flattenedValueRemaps, SORT_STRING);
|
||||
}
|
||||
@ -45,6 +50,7 @@ final class BlockStateUpgradeSchemaFlattenedName{
|
||||
return $this->prefix === $that->prefix &&
|
||||
$this->flattenedProperty === $that->flattenedProperty &&
|
||||
$this->suffix === $that->suffix &&
|
||||
$this->flattenedValueRemaps === $that->flattenedValueRemaps;
|
||||
$this->flattenedValueRemaps === $that->flattenedValueRemaps &&
|
||||
$this->flattenedPropertyType === $that->flattenedPropertyType;
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModel;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenedName;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenInfo;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelTag;
|
||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
@ -155,20 +155,24 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($model->flattenedProperties ?? []) as $blockName => $flattenRule){
|
||||
$result->flattenedProperties[$blockName] = self::jsonModelToFlattenRule($flattenRule);
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){
|
||||
foreach($remaps as $remap){
|
||||
if(isset($remap->newName) === isset($remap->newFlattenedName)){
|
||||
if(isset($remap->newName)){
|
||||
$remapName = $remap->newName;
|
||||
}elseif(isset($remap->newFlattenedName)){
|
||||
$flattenRule = $remap->newFlattenedName;
|
||||
$remapName = self::jsonModelToFlattenRule($flattenRule);
|
||||
}else{
|
||||
throw new \UnexpectedValueException("Expected exactly one of 'newName' or 'newFlattenedName' properties to be set");
|
||||
}
|
||||
|
||||
$result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []),
|
||||
$remap->newName ?? new BlockStateUpgradeSchemaFlattenedName(
|
||||
$remap->newFlattenedName->prefix,
|
||||
$remap->newFlattenedName->flattenedProperty,
|
||||
$remap->newFlattenedName->suffix,
|
||||
$remap->newFlattenedName->flattenedValueRemaps ?? [],
|
||||
),
|
||||
$remapName,
|
||||
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
|
||||
$remap->copiedState ?? []
|
||||
);
|
||||
@ -254,6 +258,36 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
$model->remappedPropertyValues = $modelDedupMapping;
|
||||
}
|
||||
|
||||
private static function flattenRuleToJsonModel(BlockStateUpgradeSchemaFlattenInfo $flattenRule) : BlockStateUpgradeSchemaModelFlattenInfo{
|
||||
return new BlockStateUpgradeSchemaModelFlattenInfo(
|
||||
$flattenRule->prefix,
|
||||
$flattenRule->flattenedProperty,
|
||||
$flattenRule->suffix,
|
||||
$flattenRule->flattenedValueRemaps,
|
||||
match($flattenRule->flattenedPropertyType){
|
||||
StringTag::class => null, //omit for TAG_String, as this is the common case
|
||||
ByteTag::class => "byte",
|
||||
IntTag::class => "int",
|
||||
default => throw new \LogicException("Unexpected tag type " . $flattenRule->flattenedPropertyType . " in flattened property type")
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static function jsonModelToFlattenRule(BlockStateUpgradeSchemaModelFlattenInfo $flattenRule) : BlockStateUpgradeSchemaFlattenInfo{
|
||||
return new BlockStateUpgradeSchemaFlattenInfo(
|
||||
$flattenRule->prefix,
|
||||
$flattenRule->flattenedProperty,
|
||||
$flattenRule->suffix,
|
||||
$flattenRule->flattenedValueRemaps ?? [],
|
||||
match ($flattenRule->flattenedPropertyType) {
|
||||
"string", null => StringTag::class,
|
||||
"int" => IntTag::class,
|
||||
"byte" => ByteTag::class,
|
||||
default => throw new \UnexpectedValueException("Unexpected flattened property type $flattenRule->flattenedPropertyType, expected 'string', 'int' or 'byte'")
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static function toJsonModel(BlockStateUpgradeSchema $schema) : BlockStateUpgradeSchemaModel{
|
||||
$result = new BlockStateUpgradeSchemaModel();
|
||||
$result->maxVersionMajor = $schema->maxVersionMajor;
|
||||
@ -292,19 +326,19 @@ final class BlockStateUpgradeSchemaUtils{
|
||||
|
||||
self::buildRemappedValuesIndex($schema, $result);
|
||||
|
||||
foreach(Utils::stringifyKeys($schema->flattenedProperties) as $blockName => $flattenRule){
|
||||
$result->flattenedProperties[$blockName] = self::flattenRuleToJsonModel($flattenRule);
|
||||
}
|
||||
if(isset($result->flattenedProperties)){
|
||||
ksort($result->flattenedProperties);
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($schema->remappedStates) as $oldBlockName => $remaps){
|
||||
$keyedRemaps = [];
|
||||
foreach($remaps as $remap){
|
||||
$modelRemap = new BlockStateUpgradeSchemaModelBlockRemap(
|
||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
|
||||
is_string($remap->newName) ?
|
||||
$remap->newName :
|
||||
new BlockStateUpgradeSchemaModelFlattenedName(
|
||||
$remap->newName->prefix,
|
||||
$remap->newName->flattenedProperty,
|
||||
$remap->newName->suffix,
|
||||
$remap->newName->flattenedValueRemaps
|
||||
),
|
||||
is_string($remap->newName) ? $remap->newName : self::flattenRuleToJsonModel($remap->newName),
|
||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
||||
$remap->copiedState
|
||||
);
|
||||
|
@ -24,10 +24,14 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function is_string;
|
||||
use function ksort;
|
||||
use function max;
|
||||
@ -79,6 +83,8 @@ final class BlockStateUpgrader{
|
||||
* version doesn't tell us which of the schemas have already been applied.
|
||||
* If there's only one schema for a version (the norm), we can safely assume it's already been applied if
|
||||
* the version is the same, and skip over it.
|
||||
* TODO: this causes issues when testing isolated schemas since there will only be one schema for a version.
|
||||
* The second check should be disabled for that case.
|
||||
*/
|
||||
if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){
|
||||
continue;
|
||||
@ -104,10 +110,21 @@ final class BlockStateUpgrader{
|
||||
}
|
||||
|
||||
$oldName = $blockStateData->getName();
|
||||
$newName = $schema->renamedIds[$oldName] ?? null;
|
||||
$states = $blockStateData->getStates();
|
||||
|
||||
if(isset($schema->renamedIds[$oldName]) && isset($schema->flattenedProperties[$oldName])){
|
||||
//TODO: this probably ought to be validated when the schema is constructed
|
||||
throw new AssumptionFailedError("Both renamedIds and flattenedProperties are set for the same block ID \"$oldName\" - don't know what to do");
|
||||
}
|
||||
if(isset($schema->renamedIds[$oldName])){
|
||||
$newName = $schema->renamedIds[$oldName] ?? null;
|
||||
}elseif(isset($schema->flattenedProperties[$oldName])){
|
||||
[$newName, $states] = $this->applyPropertyFlattened($schema->flattenedProperties[$oldName], $oldName, $states);
|
||||
}else{
|
||||
$newName = null;
|
||||
}
|
||||
|
||||
$stateChanges = 0;
|
||||
$states = $blockStateData->getStates();
|
||||
|
||||
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
|
||||
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
|
||||
@ -140,15 +157,8 @@ final class BlockStateUpgrader{
|
||||
if(is_string($remap->newName)){
|
||||
$newName = $remap->newName;
|
||||
}else{
|
||||
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
|
||||
if($flattenedValue instanceof StringTag){
|
||||
$embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue();
|
||||
$newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix);
|
||||
unset($oldState[$remap->newName->flattenedProperty]);
|
||||
}else{
|
||||
//flattened property is not a TAG_String, so this transformation is not applicable
|
||||
continue;
|
||||
}
|
||||
//discard flatten modifications to state - the remap newState and copiedState will take care of it
|
||||
[$newName, ] = $this->applyPropertyFlattened($remap->newName, $oldName, $oldState);
|
||||
}
|
||||
|
||||
$newState = $remap->newState;
|
||||
@ -266,4 +276,32 @@ final class BlockStateUpgrader{
|
||||
|
||||
return $states;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag[] $states
|
||||
* @phpstan-param array<string, Tag> $states
|
||||
*
|
||||
* @return (string|Tag[])[]
|
||||
* @phpstan-return array{0: string, 1: array<string, Tag>}
|
||||
*/
|
||||
private function applyPropertyFlattened(BlockStateUpgradeSchemaFlattenInfo $flattenInfo, string $oldName, array $states) : array{
|
||||
$flattenedValue = $states[$flattenInfo->flattenedProperty] ?? null;
|
||||
$expectedType = $flattenInfo->flattenedPropertyType;
|
||||
if(!$flattenedValue instanceof $expectedType){
|
||||
//flattened property is not of the expected type, so this transformation is not applicable
|
||||
return [$oldName, $states];
|
||||
}
|
||||
$embedKey = match(get_class($flattenedValue)){
|
||||
StringTag::class => $flattenedValue->getValue(),
|
||||
ByteTag::class => (string) $flattenedValue->getValue(),
|
||||
IntTag::class => (string) $flattenedValue->getValue(),
|
||||
//flattenedPropertyType is always one of these three types, but PHPStan doesn't know that
|
||||
default => throw new AssumptionFailedError("flattenedPropertyType should be one of these three types, but have " . get_class($flattenedValue)),
|
||||
};
|
||||
$embedValue = $flattenInfo->flattenedValueRemaps[$embedKey] ?? $embedKey;
|
||||
$newName = sprintf("%s%s%s", $flattenInfo->prefix, $embedValue, $flattenInfo->suffix);
|
||||
unset($states[$flattenInfo->flattenedProperty]);
|
||||
|
||||
return [$newName, $states];
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,12 @@ final class BlockStateUpgradeSchemaModel implements \JsonSerializable{
|
||||
*/
|
||||
public array $remappedPropertyValuesIndex;
|
||||
|
||||
/**
|
||||
* @var BlockStateUpgradeSchemaModelFlattenInfo[]
|
||||
* @phpstan-var array<string, BlockStateUpgradeSchemaModelFlattenInfo>
|
||||
*/
|
||||
public array $flattenedProperties;
|
||||
|
||||
/**
|
||||
* @var BlockStateUpgradeSchemaModelBlockRemap[][]
|
||||
* @phpstan-var array<string, list<BlockStateUpgradeSchemaModelBlockRemap>>
|
||||
|
@ -43,7 +43,7 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
|
||||
* Either this or newName must be present
|
||||
* Due to technical limitations of jsonmapper, we can't use a union type here
|
||||
*/
|
||||
public BlockStateUpgradeSchemaModelFlattenedName $newFlattenedName;
|
||||
public BlockStateUpgradeSchemaModelFlattenInfo $newFlattenedName;
|
||||
|
||||
/**
|
||||
* @var BlockStateUpgradeSchemaModelTag[]|null
|
||||
@ -67,9 +67,9 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
|
||||
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
|
||||
* @phpstan-param list<string> $copiedState
|
||||
*/
|
||||
public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenedName $newNameRule, array $newState, array $copiedState){
|
||||
public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenInfo $newNameRule, array $newState, array $copiedState){
|
||||
$this->oldState = count($oldState) === 0 ? null : $oldState;
|
||||
if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenedName){
|
||||
if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenInfo){
|
||||
$this->newFlattenedName = $newNameRule;
|
||||
}else{
|
||||
$this->newName = $newNameRule;
|
||||
|
@ -25,12 +25,13 @@ namespace pocketmine\data\bedrock\block\upgrade\model;
|
||||
|
||||
use function count;
|
||||
|
||||
final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializable{
|
||||
final class BlockStateUpgradeSchemaModelFlattenInfo implements \JsonSerializable{
|
||||
|
||||
/** @required */
|
||||
public string $prefix;
|
||||
/** @required */
|
||||
public string $flattenedProperty;
|
||||
public ?string $flattenedPropertyType = null;
|
||||
/** @required */
|
||||
public string $suffix;
|
||||
/**
|
||||
@ -43,11 +44,12 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab
|
||||
* @param string[] $flattenedValueRemaps
|
||||
* @phpstan-param array<string, string> $flattenedValueRemaps
|
||||
*/
|
||||
public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps){
|
||||
public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps, ?string $flattenedPropertyType = null){
|
||||
$this->prefix = $prefix;
|
||||
$this->flattenedProperty = $flattenedProperty;
|
||||
$this->suffix = $suffix;
|
||||
$this->flattenedValueRemaps = $flattenedValueRemaps;
|
||||
$this->flattenedPropertyType = $flattenedPropertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,6 +60,9 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab
|
||||
if(count($this->flattenedValueRemaps) === 0){
|
||||
unset($result["flattenedValueRemaps"]);
|
||||
}
|
||||
if($this->flattenedPropertyType === null){
|
||||
unset($result["flattenedPropertyType"]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@ namespace pocketmine\data\bedrock\item;
|
||||
|
||||
use pocketmine\block\Bed;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\CopperDoor;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\VanillaBlocks as Blocks;
|
||||
use pocketmine\data\bedrock\CompoundTypeIds;
|
||||
@ -54,6 +56,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->register1to1BlockWithMetaMappings();
|
||||
$this->register1to1ItemWithMetaMappings();
|
||||
$this->register1ToNItemMappings();
|
||||
$this->registerMiscBlockMappings();
|
||||
$this->registerMiscItemMappings();
|
||||
}
|
||||
|
||||
@ -131,6 +134,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Block(Ids::BIRCH_DOOR, Blocks::BIRCH_DOOR());
|
||||
$this->map1to1Block(Ids::BREWING_STAND, Blocks::BREWING_STAND());
|
||||
$this->map1to1Block(Ids::CAKE, Blocks::CAKE());
|
||||
$this->map1to1Block(Ids::CAMPFIRE, Blocks::CAMPFIRE());
|
||||
$this->map1to1Block(Ids::CAULDRON, Blocks::CAULDRON());
|
||||
$this->map1to1Block(Ids::CHAIN, Blocks::CHAIN());
|
||||
$this->map1to1Block(Ids::CHERRY_DOOR, Blocks::CHERRY_DOOR());
|
||||
@ -146,6 +150,7 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
$this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR());
|
||||
$this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART());
|
||||
$this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER());
|
||||
$this->map1to1Block(Ids::SOUL_CAMPFIRE, Blocks::SOUL_CAMPFIRE());
|
||||
$this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR());
|
||||
$this->map1to1Block(Ids::SUGAR_CANE, Blocks::SUGARCANE());
|
||||
$this->map1to1Block(Ids::WARPED_DOOR, Blocks::WARPED_DOOR());
|
||||
@ -526,4 +531,29 @@ final class ItemSerializerDeserializerRegistrar{
|
||||
}
|
||||
$this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers serializers and deserializers for PocketMine-MP blockitems that don't fit any other pattern.
|
||||
* Ideally we want to get rid of this completely, if possible.
|
||||
*
|
||||
* Most of these are single PocketMine-MP blocks which map to multiple IDs depending on their properties, which is
|
||||
* complex to implement in a generic way.
|
||||
*/
|
||||
private function registerMiscBlockMappings() : void{
|
||||
$copperDoorStateIdMap = [];
|
||||
foreach ([
|
||||
[Ids::COPPER_DOOR, CopperOxidation::NONE, false],
|
||||
[Ids::EXPOSED_COPPER_DOOR, CopperOxidation::EXPOSED, false],
|
||||
[Ids::WEATHERED_COPPER_DOOR, CopperOxidation::WEATHERED, false],
|
||||
[Ids::OXIDIZED_COPPER_DOOR, CopperOxidation::OXIDIZED, false],
|
||||
[Ids::WAXED_COPPER_DOOR, CopperOxidation::NONE, true],
|
||||
[Ids::WAXED_EXPOSED_COPPER_DOOR, CopperOxidation::EXPOSED, true],
|
||||
[Ids::WAXED_WEATHERED_COPPER_DOOR, CopperOxidation::WEATHERED, true],
|
||||
[Ids::WAXED_OXIDIZED_COPPER_DOOR, CopperOxidation::OXIDIZED, true]
|
||||
] as [$id, $oxidation, $waxed]) {
|
||||
$copperDoorStateIdMap[$oxidation->value][$waxed ? 1 : 0] = $id;
|
||||
$this->deserializer?->mapBlock($id, fn() => Blocks::COPPER_DOOR()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
}
|
||||
$this->serializer?->mapBlock(Blocks::COPPER_DOOR(), fn(CopperDoor $block) => new Data($copperDoorStateIdMap[$block->getOxidation()->value][$block->isWaxed() ? 1 : 0]));
|
||||
}
|
||||
}
|
||||
|
@ -37,9 +37,11 @@ class PaintingMotive{
|
||||
new PaintingMotive(1, 1, "Aztec2"),
|
||||
new PaintingMotive(1, 1, "Bomb"),
|
||||
new PaintingMotive(1, 1, "Kebab"),
|
||||
new PaintingMotive(1, 1, "meditative"),
|
||||
new PaintingMotive(1, 1, "Plant"),
|
||||
new PaintingMotive(1, 1, "Wasteland"),
|
||||
new PaintingMotive(1, 2, "Graham"),
|
||||
new PaintingMotive(1, 2, "prairie_ride"),
|
||||
new PaintingMotive(1, 2, "Wanderer"),
|
||||
new PaintingMotive(2, 1, "Courbet"),
|
||||
new PaintingMotive(2, 1, "Creebet"),
|
||||
@ -47,8 +49,10 @@ class PaintingMotive{
|
||||
new PaintingMotive(2, 1, "Sea"),
|
||||
new PaintingMotive(2, 1, "Sunset"),
|
||||
new PaintingMotive(2, 2, "Bust"),
|
||||
new PaintingMotive(2, 2, "baroque"),
|
||||
new PaintingMotive(2, 2, "Earth"),
|
||||
new PaintingMotive(2, 2, "Fire"),
|
||||
new PaintingMotive(2, 2, "humble"),
|
||||
new PaintingMotive(2, 2, "Match"),
|
||||
new PaintingMotive(2, 2, "SkullAndRoses"),
|
||||
new PaintingMotive(2, 2, "Stage"),
|
||||
@ -56,12 +60,28 @@ class PaintingMotive{
|
||||
new PaintingMotive(2, 2, "Water"),
|
||||
new PaintingMotive(2, 2, "Wind"),
|
||||
new PaintingMotive(2, 2, "Wither"),
|
||||
new PaintingMotive(3, 3, "bouquet"),
|
||||
new PaintingMotive(3, 3, "cavebird"),
|
||||
new PaintingMotive(3, 3, "cotan"),
|
||||
new PaintingMotive(3, 3, "endboss"),
|
||||
new PaintingMotive(3, 3, "fern"),
|
||||
new PaintingMotive(3, 3, "owlemons"),
|
||||
new PaintingMotive(3, 3, "sunflowers"),
|
||||
new PaintingMotive(3, 3, "tides"),
|
||||
new PaintingMotive(3, 4, "backyard"),
|
||||
new PaintingMotive(3, 4, "pond"),
|
||||
new PaintingMotive(4, 2, "changing"),
|
||||
new PaintingMotive(4, 2, "Fighters"),
|
||||
new PaintingMotive(4, 2, "finding"),
|
||||
new PaintingMotive(4, 2, "lowmist"),
|
||||
new PaintingMotive(4, 2, "passage"),
|
||||
new PaintingMotive(4, 3, "DonkeyKong"),
|
||||
new PaintingMotive(4, 3, "Skeleton"),
|
||||
new PaintingMotive(4, 4, "BurningSkull"),
|
||||
new PaintingMotive(4, 4, "orb"),
|
||||
new PaintingMotive(4, 4, "Pigscene"),
|
||||
new PaintingMotive(4, 4, "Pointer")
|
||||
new PaintingMotive(4, 4, "Pointer"),
|
||||
new PaintingMotive(4, 4, "unpacked")
|
||||
] as $motive){
|
||||
self::registerMotive($motive);
|
||||
}
|
||||
|
63
src/event/block/CampfireCookEvent.php
Normal file
63
src/event/block/CampfireCookEvent.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?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\event\block;
|
||||
|
||||
use pocketmine\block\Campfire;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class CampfireCookEvent extends BlockEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
|
||||
public function __construct(
|
||||
private Campfire $campfire,
|
||||
private int $slot,
|
||||
private Item $input,
|
||||
private Item $result
|
||||
){
|
||||
parent::__construct($campfire);
|
||||
$this->input = clone $input;
|
||||
}
|
||||
|
||||
public function getCampfire() : Campfire{
|
||||
return $this->campfire;
|
||||
}
|
||||
|
||||
public function getSlot() : int{
|
||||
return $this->slot;
|
||||
}
|
||||
|
||||
public function getInput() : Item{
|
||||
return $this->input;
|
||||
}
|
||||
|
||||
public function getResult() : Item{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function setResult(Item $result) : void{
|
||||
$this->result = $result;
|
||||
}
|
||||
}
|
@ -23,8 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\block\BlockTypeIds;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\inventory\transaction\action\validator\CallbackSlotValidator;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Armor;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemBlock;
|
||||
|
||||
class ArmorInventory extends SimpleInventory{
|
||||
public const SLOT_HEAD = 0;
|
||||
@ -36,6 +41,8 @@ class ArmorInventory extends SimpleInventory{
|
||||
protected Living $holder
|
||||
){
|
||||
parent::__construct(4);
|
||||
|
||||
$this->validators->add(new CallbackSlotValidator($this->validate(...)));
|
||||
}
|
||||
|
||||
public function getHolder() : Living{
|
||||
@ -73,4 +80,20 @@ class ArmorInventory extends SimpleInventory{
|
||||
public function setBoots(Item $boots) : void{
|
||||
$this->setItem(self::SLOT_FEET, $boots);
|
||||
}
|
||||
|
||||
private function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{
|
||||
if($item instanceof Armor){
|
||||
if($item->getArmorSlot() !== $slot){
|
||||
return new TransactionValidationException("Armor item is in wrong slot");
|
||||
}
|
||||
}else{
|
||||
if(!($slot === ArmorInventory::SLOT_HEAD && $item instanceof ItemBlock && (
|
||||
$item->getBlock()->getTypeId() === BlockTypeIds::CARVED_PUMPKIN ||
|
||||
$item->getBlock()->getTypeId() === BlockTypeIds::MOB_HEAD
|
||||
))){
|
||||
return new TransactionValidationException("Item is not accepted in an armor slot");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,10 @@ use function spl_object_id;
|
||||
|
||||
/**
|
||||
* This class provides everything needed to implement an inventory, minus the underlying storage system.
|
||||
*
|
||||
* @phpstan-import-type SlotValidators from SlotValidatedInventory
|
||||
*/
|
||||
abstract class BaseInventory implements Inventory{
|
||||
abstract class BaseInventory implements Inventory, SlotValidatedInventory{
|
||||
protected int $maxStackSize = Inventory::MAX_STACK;
|
||||
/** @var Player[] */
|
||||
protected array $viewers = [];
|
||||
@ -46,9 +48,12 @@ abstract class BaseInventory implements Inventory{
|
||||
* @phpstan-var ObjectSet<InventoryListener>
|
||||
*/
|
||||
protected ObjectSet $listeners;
|
||||
/** @phpstan-var SlotValidators */
|
||||
protected ObjectSet $validators;
|
||||
|
||||
public function __construct(){
|
||||
$this->listeners = new ObjectSet();
|
||||
$this->validators = new ObjectSet();
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
@ -398,4 +403,8 @@ abstract class BaseInventory implements Inventory{
|
||||
public function getListeners() : ObjectSet{
|
||||
return $this->listeners;
|
||||
}
|
||||
|
||||
public function getSlotValidators() : ObjectSet{
|
||||
return $this->validators;
|
||||
}
|
||||
}
|
||||
|
46
src/inventory/SlotValidatedInventory.php
Normal file
46
src/inventory/SlotValidatedInventory.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\inventory\transaction\action\validator\SlotValidator;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
|
||||
/**
|
||||
* A "slot validated inventory" has validators which may restrict items
|
||||
* from being placed in particular slots of the inventory when transactions are executed.
|
||||
*
|
||||
* @phpstan-type SlotValidators ObjectSet<SlotValidator>
|
||||
*/
|
||||
interface SlotValidatedInventory{
|
||||
/**
|
||||
* Returns a set of validators that will be used to determine whether an item can be placed in a particular slot.
|
||||
* All validators need to return null for the transaction to be allowed.
|
||||
* If one of the validators returns an exception, the transaction will be cancelled.
|
||||
*
|
||||
* There is no guarantee that the validators will be called in any particular order.
|
||||
*
|
||||
* @phpstan-return SlotValidators
|
||||
*/
|
||||
public function getSlotValidators() : ObjectSet;
|
||||
}
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\inventory\transaction\action;
|
||||
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\SlotValidatedInventory;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Item;
|
||||
@ -74,6 +75,14 @@ class SlotChangeAction extends InventoryAction{
|
||||
if($this->targetItem->getCount() > $this->inventory->getMaxStackSize()){
|
||||
throw new TransactionValidationException("Target item exceeds inventory max stack size");
|
||||
}
|
||||
if($this->inventory instanceof SlotValidatedInventory && !$this->targetItem->isNull()){
|
||||
foreach($this->inventory->getSlotValidators() as $validator){
|
||||
$ret = $validator->validate($this->inventory, $this->targetItem, $this->inventorySlot);
|
||||
if($ret !== null){
|
||||
throw new TransactionValidationException("Target item is not accepted by the inventory at slot #" . $this->inventorySlot . ": " . $ret->getMessage(), 0, $ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,44 @@
|
||||
<?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\inventory\transaction\action\validator;
|
||||
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class CallbackSlotValidator implements SlotValidator{
|
||||
/**
|
||||
* @phpstan-param \Closure(Inventory, Item, int) : ?TransactionValidationException $validate
|
||||
*/
|
||||
public function __construct(
|
||||
private \Closure $validate
|
||||
){
|
||||
Utils::validateCallableSignature(function(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{ return null; }, $validate);
|
||||
}
|
||||
|
||||
public function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{
|
||||
return ($this->validate)($inventory, $item, $slot);
|
||||
}
|
||||
}
|
38
src/inventory/transaction/action/validator/SlotValidator.php
Normal file
38
src/inventory/transaction/action/validator/SlotValidator.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\inventory\transaction\action\validator;
|
||||
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
/**
|
||||
* Validates a slot placement in an inventory.
|
||||
*/
|
||||
interface SlotValidator{
|
||||
/**
|
||||
* Returns null if the slot placement is valid, or a TransactionValidationException if it is not.
|
||||
*/
|
||||
public function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException;
|
||||
}
|
@ -88,4 +88,8 @@ class ChorusFruit extends Food{
|
||||
public function getCooldownTicks() : int{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public function getCooldownTag() : ?string{
|
||||
return ItemCooldownTags::CHORUS_FRUIT;
|
||||
}
|
||||
}
|
||||
|
@ -45,4 +45,8 @@ class EnderPearl extends ProjectileItem{
|
||||
public function getCooldownTicks() : int{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public function getCooldownTag() : ?string{
|
||||
return ItemCooldownTags::ENDER_PEARL;
|
||||
}
|
||||
}
|
||||
|
@ -654,6 +654,20 @@ class Item implements \JsonSerializable{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a tag that identifies a group of items that should have cooldown at the same time
|
||||
* regardless of their state or type.
|
||||
* When cooldown starts, any other items with the same cooldown tag can't be used until the cooldown expires.
|
||||
* Such behaviour can be seen in goat horns and shields.
|
||||
*
|
||||
* If tag is null, item state id will be used to store cooldown.
|
||||
*
|
||||
* @see ItemCooldownTags
|
||||
*/
|
||||
public function getCooldownTag() : ?string{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares an Item to this Item and check if they match.
|
||||
*
|
||||
|
45
src/item/ItemCooldownTags.php
Normal file
45
src/item/ItemCooldownTags.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Tags used by items to determine their cooldown group.
|
||||
*
|
||||
* These tag values are not related to Minecraft internal IDs.
|
||||
* They only share a visual similarity because these are the most obvious values to use.
|
||||
* Any arbitrary string can be used.
|
||||
*
|
||||
* @see Item::getCooldownTag()
|
||||
*/
|
||||
final class ItemCooldownTags{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public const CHORUS_FRUIT = "chorus_fruit";
|
||||
public const ENDER_PEARL = "ender_pearl";
|
||||
public const SHIELD = "shield";
|
||||
public const GOAT_HORN = "goat_horn";
|
||||
}
|
@ -98,9 +98,14 @@ final class StringToItemParser extends StringToTParser{
|
||||
foreach(["" => false, "waxed_" => true] as $waxedPrefix => $waxed){
|
||||
$register = fn(string $name, \Closure $callback) => $result->registerBlock($waxedPrefix . $oxPrefix . $name, $callback);
|
||||
$register("copper_block", fn() => Blocks::COPPER()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("chiseled_copper", fn() => Blocks::CHISELED_COPPER()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("copper_grate", fn() => Blocks::COPPER_GRATE()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("cut_copper_block", fn() => Blocks::CUT_COPPER()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("cut_copper_stairs", fn() => Blocks::CUT_COPPER_STAIRS()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("cut_copper_slab", fn() => Blocks::CUT_COPPER_SLAB()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("copper_bulb", fn() => Blocks::COPPER_BULB()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("copper_door", fn() => Blocks::COPPER_DOOR()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
$register("copper_trapdoor", fn() => Blocks::COPPER_TRAPDOOR()->setOxidation($oxidation)->setWaxed($waxed));
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,6 +210,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("cake", fn() => Blocks::CAKE());
|
||||
$result->registerBlock("cake_block", fn() => Blocks::CAKE());
|
||||
$result->registerBlock("calcite", fn() => Blocks::CALCITE());
|
||||
$result->registerBlock("campfire", fn() => Blocks::CAMPFIRE());
|
||||
$result->registerBlock("candle", fn() => Blocks::CANDLE());
|
||||
$result->registerBlock("carpet", fn() => Blocks::CARPET());
|
||||
$result->registerBlock("carrot_block", fn() => Blocks::CARROTS());
|
||||
@ -239,6 +245,8 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("chiseled_red_sandstone", fn() => Blocks::CHISELED_RED_SANDSTONE());
|
||||
$result->registerBlock("chiseled_sandstone", fn() => Blocks::CHISELED_SANDSTONE());
|
||||
$result->registerBlock("chiseled_stone_bricks", fn() => Blocks::CHISELED_STONE_BRICKS());
|
||||
$result->registerBlock("chiseled_tuff", fn() => Blocks::CHISELED_TUFF());
|
||||
$result->registerBlock("chiseled_tuff_bricks", fn() => Blocks::CHISELED_TUFF_BRICKS());
|
||||
$result->registerBlock("chorus_flower", fn() => Blocks::CHORUS_FLOWER());
|
||||
$result->registerBlock("chorus_plant", fn() => Blocks::CHORUS_PLANT());
|
||||
$result->registerBlock("clay_block", fn() => Blocks::CLAY());
|
||||
@ -897,6 +905,10 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("polished_granite", fn() => Blocks::POLISHED_GRANITE());
|
||||
$result->registerBlock("polished_granite_slab", fn() => Blocks::POLISHED_GRANITE_SLAB());
|
||||
$result->registerBlock("polished_granite_stairs", fn() => Blocks::POLISHED_GRANITE_STAIRS());
|
||||
$result->registerBlock("polished_tuff", fn() => Blocks::POLISHED_TUFF());
|
||||
$result->registerBlock("polished_tuff_slab", fn() => Blocks::POLISHED_TUFF_SLAB());
|
||||
$result->registerBlock("polished_tuff_stairs", fn() => Blocks::POLISHED_TUFF_STAIRS());
|
||||
$result->registerBlock("polished_tuff_wall", fn() => Blocks::POLISHED_TUFF_WALL());
|
||||
$result->registerBlock("poppy", fn() => Blocks::POPPY());
|
||||
$result->registerBlock("portal", fn() => Blocks::NETHER_PORTAL());
|
||||
$result->registerBlock("portal_block", fn() => Blocks::NETHER_PORTAL());
|
||||
@ -1003,6 +1015,7 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("snow", fn() => Blocks::SNOW());
|
||||
$result->registerBlock("snow_block", fn() => Blocks::SNOW());
|
||||
$result->registerBlock("snow_layer", fn() => Blocks::SNOW_LAYER());
|
||||
$result->registerBlock("soul_campfire", fn() => Blocks::SOUL_CAMPFIRE());
|
||||
$result->registerBlock("soul_lantern", fn() => Blocks::SOUL_LANTERN());
|
||||
$result->registerBlock("soul_sand", fn() => Blocks::SOUL_SAND());
|
||||
$result->registerBlock("soul_soil", fn() => Blocks::SOUL_SOIL());
|
||||
@ -1096,6 +1109,13 @@ final class StringToItemParser extends StringToTParser{
|
||||
$result->registerBlock("trunk", fn() => Blocks::OAK_PLANKS());
|
||||
$result->registerBlock("trunk2", fn() => Blocks::ACACIA_LOG()->setStripped(false));
|
||||
$result->registerBlock("tuff", fn() => Blocks::TUFF());
|
||||
$result->registerBlock("tuff_bricks", fn() => Blocks::TUFF_BRICKS());
|
||||
$result->registerBlock("tuff_brick_slab", fn() => Blocks::TUFF_BRICK_SLAB());
|
||||
$result->registerBlock("tuff_brick_stairs", fn() => Blocks::TUFF_BRICK_STAIRS());
|
||||
$result->registerBlock("tuff_brick_wall", fn() => Blocks::TUFF_BRICK_WALL());
|
||||
$result->registerBlock("tuff_slab", fn() => Blocks::TUFF_SLAB());
|
||||
$result->registerBlock("tuff_stairs", fn() => Blocks::TUFF_STAIRS());
|
||||
$result->registerBlock("tuff_wall", fn() => Blocks::TUFF_WALL());
|
||||
$result->registerBlock("twisting_vines", fn() => Blocks::TWISTING_VINES());
|
||||
$result->registerBlock("underwater_tnt", fn() => Blocks::TNT()->setWorksUnderwater(true));
|
||||
$result->registerBlock("underwater_torch", fn() => Blocks::UNDERWATER_TORCH());
|
||||
|
@ -591,12 +591,12 @@ final class VanillaItems{
|
||||
}
|
||||
});
|
||||
self::register("squid_spawn_egg", new class(new IID(Ids::SQUID_SPAWN_EGG), "Squid Spawn Egg") extends SpawnEgg{
|
||||
public function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
|
||||
protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
|
||||
return new Squid(Location::fromObject($pos, $world, $yaw, $pitch));
|
||||
}
|
||||
});
|
||||
self::register("villager_spawn_egg", new class(new IID(Ids::VILLAGER_SPAWN_EGG), "Villager Spawn Egg") extends SpawnEgg{
|
||||
public function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
|
||||
protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
|
||||
return new Villager(Location::fromObject($pos, $world, $yaw, $pitch));
|
||||
}
|
||||
});
|
||||
|
@ -56,6 +56,7 @@ final class AvailableEnchantmentRegistry{
|
||||
$this->register(Enchantments::PROJECTILE_PROTECTION(), [Tags::ARMOR], []);
|
||||
$this->register(Enchantments::THORNS(), [Tags::CHESTPLATE], [Tags::HELMET, Tags::LEGGINGS, Tags::BOOTS]);
|
||||
$this->register(Enchantments::RESPIRATION(), [Tags::HELMET], []);
|
||||
$this->register(Enchantments::AQUA_AFFINITY(), [Tags::HELMET], []);
|
||||
$this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []);
|
||||
$this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []);
|
||||
$this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []);
|
||||
|
@ -52,6 +52,7 @@ final class StringToEnchantmentParser extends StringToTParser{
|
||||
$result->register("protection", fn() => VanillaEnchantments::PROTECTION());
|
||||
$result->register("punch", fn() => VanillaEnchantments::PUNCH());
|
||||
$result->register("respiration", fn() => VanillaEnchantments::RESPIRATION());
|
||||
$result->register("aqua_affinity", fn() => VanillaEnchantments::AQUA_AFFINITY());
|
||||
$result->register("sharpness", fn() => VanillaEnchantments::SHARPNESS());
|
||||
$result->register("silk_touch", fn() => VanillaEnchantments::SILK_TOUCH());
|
||||
$result->register("swift_sneak", fn() => VanillaEnchantments::SWIFT_SNEAK());
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\utils\RegistryTrait;
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static Enchantment AQUA_AFFINITY()
|
||||
* @method static ProtectionEnchantment BLAST_PROTECTION()
|
||||
* @method static Enchantment EFFICIENCY()
|
||||
* @method static ProtectionEnchantment FEATHER_FALLING()
|
||||
@ -144,6 +145,15 @@ final class VanillaEnchantments{
|
||||
fn(int $level) : int => 10 * $level,
|
||||
30
|
||||
));
|
||||
self::register("AQUA_AFFINITY", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_waterWorker(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
null,
|
||||
40
|
||||
));
|
||||
|
||||
self::register("SHARPNESS", new SharpnessEnchantment(
|
||||
KnownTranslationFactory::enchantment_damage_all(),
|
||||
|
@ -364,6 +364,7 @@ class InventoryManager{
|
||||
FurnaceType::FURNACE => WindowTypes::FURNACE,
|
||||
FurnaceType::BLAST_FURNACE => WindowTypes::BLAST_FURNACE,
|
||||
FurnaceType::SMOKER => WindowTypes::SMOKER,
|
||||
FurnaceType::CAMPFIRE, FurnaceType::SOUL_CAMPFIRE => throw new \LogicException("Campfire inventory cannot be displayed to a player")
|
||||
},
|
||||
$inv instanceof EnchantInventory => WindowTypes::ENCHANTMENT,
|
||||
$inv instanceof BrewingStandInventory => WindowTypes::BREWING_STAND,
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\event\server\DataPacketDecodeEvent;
|
||||
use pocketmine\event\server\DataPacketReceiveEvent;
|
||||
use pocketmine\event\server\DataPacketSendEvent;
|
||||
use pocketmine\form\Form;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -65,6 +66,7 @@ use pocketmine\network\mcpe\protocol\Packet;
|
||||
use pocketmine\network\mcpe\protocol\PacketDecodeException;
|
||||
use pocketmine\network\mcpe\protocol\PacketPool;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerStartItemCooldownPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\serializer\PacketBatch;
|
||||
@ -111,6 +113,7 @@ use pocketmine\utils\BinaryDataException;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\world\format\io\GlobalItemDataHandlers;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\YmlServerProperties;
|
||||
use function array_map;
|
||||
@ -1289,6 +1292,13 @@ class NetworkSession{
|
||||
$this->sendDataPacket(OpenSignPacket::create(BlockPosition::fromVector3($signPosition), $frontSide));
|
||||
}
|
||||
|
||||
public function onItemCooldownChanged(Item $item, int $ticks) : void{
|
||||
$this->sendDataPacket(PlayerStartItemCooldownPacket::create(
|
||||
GlobalItemDataHandlers::getSerializer()->serializeType($item)->getName(),
|
||||
$ticks
|
||||
));
|
||||
}
|
||||
|
||||
public function tick() : void{
|
||||
if(!$this->isConnected()){
|
||||
$this->dispose();
|
||||
|
2
src/network/mcpe/cache/CraftingDataCache.php
vendored
2
src/network/mcpe/cache/CraftingDataCache.php
vendored
@ -130,6 +130,8 @@ final class CraftingDataCache{
|
||||
FurnaceType::FURNACE => FurnaceRecipeBlockName::FURNACE,
|
||||
FurnaceType::BLAST_FURNACE => FurnaceRecipeBlockName::BLAST_FURNACE,
|
||||
FurnaceType::SMOKER => FurnaceRecipeBlockName::SMOKER,
|
||||
FurnaceType::CAMPFIRE => FurnaceRecipeBlockName::CAMPFIRE,
|
||||
FurnaceType::SOUL_CAMPFIRE => FurnaceRecipeBlockName::SOUL_CAMPFIRE
|
||||
};
|
||||
foreach($manager->getFurnaceRecipeManager($furnaceType)->getAll() as $recipe){
|
||||
$input = $converter->coreRecipeIngredientToNet($recipe->getInput())->getDescriptor();
|
||||
|
@ -283,7 +283,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
protected string $locale = "en_US";
|
||||
|
||||
protected int $startAction = -1;
|
||||
/** @var int[] ID => ticks map */
|
||||
|
||||
/**
|
||||
* @phpstan-var array<int|string, int>
|
||||
* @var int[] stateId|cooldownTag => ticks map
|
||||
*/
|
||||
protected array $usedItemsCooldown = [];
|
||||
|
||||
private int $lastEmoteTick = 0;
|
||||
@ -697,7 +701,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
*/
|
||||
public function getItemCooldownExpiry(Item $item) : int{
|
||||
$this->checkItemCooldowns();
|
||||
return $this->usedItemsCooldown[$item->getStateId()] ?? 0;
|
||||
return $this->usedItemsCooldown[$item->getCooldownTag() ?? $item->getStateId()] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -705,7 +709,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
*/
|
||||
public function hasItemCooldown(Item $item) : bool{
|
||||
$this->checkItemCooldowns();
|
||||
return isset($this->usedItemsCooldown[$item->getStateId()]);
|
||||
return isset($this->usedItemsCooldown[$item->getCooldownTag() ?? $item->getStateId()]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -714,7 +718,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
public function resetItemCooldown(Item $item, ?int $ticks = null) : void{
|
||||
$ticks = $ticks ?? $item->getCooldownTicks();
|
||||
if($ticks > 0){
|
||||
$this->usedItemsCooldown[$item->getStateId()] = $this->server->getTick() + $ticks;
|
||||
$this->usedItemsCooldown[$item->getCooldownTag() ?? $item->getStateId()] = $this->server->getTick() + $ticks;
|
||||
$this->getNetworkSession()->onItemCooldownChanged($item, $ticks);
|
||||
}
|
||||
}
|
||||
|
||||
|
35
src/world/sound/CampfireSound.php
Normal file
35
src/world/sound/CampfireSound.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?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\world\sound;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
final class CampfireSound implements Sound{
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::BLOCK_CAMPFIRE_CRACKLE, $pos, false)];
|
||||
}
|
||||
}
|
@ -785,11 +785,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$length of function fread expects int\\<0, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/resourcepacks/ZippedResourcePack.php
|
||||
|
||||
-
|
||||
message: "#^Property pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:\\$fileResource \\(resource\\) does not accept resource\\|false\\.$#"
|
||||
count: 1
|
||||
@ -1035,11 +1030,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/world/format/io/region/RegionLoader.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$length of function fread expects int\\<0, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/world/format/io/region/RegionLoader.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$size of function ftruncate expects int\\<0, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
@ -1190,18 +1180,3 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/world/light/SkyLightUpdate.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/block/BlockTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/block/regenerate_consistency_check.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$logFile of class pocketmine\\\\utils\\\\MainLogger constructor expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
path: ../../phpunit/scheduler/AsyncPoolTest.php
|
||||
|
||||
|
@ -5,6 +5,16 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/block/CakeWithCandle.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\block\\\\CopperDoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/CopperDoor.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\block\\\\CopperTrapdoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/block/CopperTrapdoor.php
|
||||
|
||||
-
|
||||
message: "#^Method pocketmine\\\\block\\\\DoubleTallGrass\\:\\:traitGetDropsForIncompatibleTool\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
|
@ -79,6 +79,7 @@
|
||||
"CAKE_WITH_CANDLE": 2,
|
||||
"CAKE_WITH_DYED_CANDLE": 32,
|
||||
"CALCITE": 1,
|
||||
"CAMPFIRE": 8,
|
||||
"CANDLE": 8,
|
||||
"CARPET": 16,
|
||||
"CARROTS": 8,
|
||||
@ -104,6 +105,7 @@
|
||||
"CHERRY_WOOD": 6,
|
||||
"CHEST": 4,
|
||||
"CHISELED_BOOKSHELF": 256,
|
||||
"CHISELED_COPPER": 8,
|
||||
"CHISELED_DEEPSLATE": 1,
|
||||
"CHISELED_NETHER_BRICKS": 1,
|
||||
"CHISELED_POLISHED_BLACKSTONE": 1,
|
||||
@ -111,6 +113,8 @@
|
||||
"CHISELED_RED_SANDSTONE": 1,
|
||||
"CHISELED_SANDSTONE": 1,
|
||||
"CHISELED_STONE_BRICKS": 1,
|
||||
"CHISELED_TUFF": 1,
|
||||
"CHISELED_TUFF_BRICKS": 1,
|
||||
"CHORUS_FLOWER": 6,
|
||||
"CHORUS_PLANT": 1,
|
||||
"CLAY": 1,
|
||||
@ -130,7 +134,11 @@
|
||||
"CONCRETE": 16,
|
||||
"CONCRETE_POWDER": 16,
|
||||
"COPPER": 8,
|
||||
"COPPER_BULB": 32,
|
||||
"COPPER_DOOR": 256,
|
||||
"COPPER_GRATE": 8,
|
||||
"COPPER_ORE": 1,
|
||||
"COPPER_TRAPDOOR": 128,
|
||||
"CORAL": 10,
|
||||
"CORAL_BLOCK": 10,
|
||||
"CORAL_FAN": 20,
|
||||
@ -532,6 +540,10 @@
|
||||
"POLISHED_GRANITE": 1,
|
||||
"POLISHED_GRANITE_SLAB": 3,
|
||||
"POLISHED_GRANITE_STAIRS": 8,
|
||||
"POLISHED_TUFF": 1,
|
||||
"POLISHED_TUFF_SLAB": 3,
|
||||
"POLISHED_TUFF_STAIRS": 8,
|
||||
"POLISHED_TUFF_WALL": 162,
|
||||
"POPPY": 1,
|
||||
"POTATOES": 8,
|
||||
"POTION_CAULDRON": 6,
|
||||
@ -610,6 +622,7 @@
|
||||
"SMOOTH_STONE_SLAB": 3,
|
||||
"SNOW": 1,
|
||||
"SNOW_LAYER": 8,
|
||||
"SOUL_CAMPFIRE": 8,
|
||||
"SOUL_FIRE": 1,
|
||||
"SOUL_LANTERN": 2,
|
||||
"SOUL_SAND": 1,
|
||||
@ -660,6 +673,13 @@
|
||||
"TRIPWIRE": 16,
|
||||
"TRIPWIRE_HOOK": 16,
|
||||
"TUFF": 1,
|
||||
"TUFF_BRICKS": 1,
|
||||
"TUFF_BRICK_SLAB": 3,
|
||||
"TUFF_BRICK_STAIRS": 8,
|
||||
"TUFF_BRICK_WALL": 162,
|
||||
"TUFF_SLAB": 3,
|
||||
"TUFF_STAIRS": 8,
|
||||
"TUFF_WALL": 162,
|
||||
"TWISTING_VINES": 26,
|
||||
"UNDERWATER_TORCH": 5,
|
||||
"VINES": 16,
|
||||
|
@ -24,8 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\data\bedrock\block\upgrade;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
class BlockStateUpgraderTest extends TestCase{
|
||||
@ -210,6 +212,23 @@ class BlockStateUpgraderTest extends TestCase{
|
||||
self::assertSame($upgradedStateData->getState(self::TEST_PROPERTY_2)?->getValue(), $valueAfter);
|
||||
}
|
||||
|
||||
public function testFlattenProperty() : void{
|
||||
$schema = $this->getNewSchema();
|
||||
$schema->flattenedProperties[self::TEST_BLOCK] = new BlockStateUpgradeSchemaFlattenInfo(
|
||||
"minecraft:",
|
||||
"test",
|
||||
"_suffix",
|
||||
[],
|
||||
StringTag::class
|
||||
);
|
||||
|
||||
$stateData = new BlockStateData(self::TEST_BLOCK, ["test" => new StringTag("value1")], 0);
|
||||
$upgradedStateData = $this->upgrade($stateData, fn() => $stateData);
|
||||
|
||||
self::assertSame("minecraft:value1_suffix", $upgradedStateData->getName());
|
||||
self::assertEmpty($upgradedStateData->getStates());
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return \Generator<int, array{int, int, bool, int}, void, void>
|
||||
*/
|
||||
|
@ -21,36 +21,49 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\tools\generate_blockstate_upgrade_schema;
|
||||
namespace pocketmine\tools\blockstate_upgrade_schema_utils;
|
||||
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenedName;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenInfo;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\convert\BlockStateDictionary;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_key_first;
|
||||
use function array_key_last;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
use function array_unique;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function get_class;
|
||||
use function get_debug_type;
|
||||
use function implode;
|
||||
use function is_dir;
|
||||
use function is_numeric;
|
||||
use function json_encode;
|
||||
use function ksort;
|
||||
use function min;
|
||||
use function preg_match;
|
||||
use function scandir;
|
||||
use function sort;
|
||||
use function strlen;
|
||||
use function strrev;
|
||||
@ -83,18 +96,18 @@ function encodeProperty(Tag $tag) : string{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TreeRoot[] $oldNewStateList
|
||||
* @phpstan-param list<TreeRoot> $oldNewStateList
|
||||
*
|
||||
* @return BlockStateMapping[][]
|
||||
* @phpstan-return array<string, array<string, BlockStateMapping>>
|
||||
*/
|
||||
function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
$contents = Filesystem::fileGetContents($file);
|
||||
$data = (new NetworkNbtSerializer())->readMultiple($contents);
|
||||
|
||||
function buildUpgradeTableFromData(array $oldNewStateList, bool $reverse) : array{
|
||||
$result = [];
|
||||
|
||||
for($i = 0; isset($data[$i]); $i += 2){
|
||||
$oldTag = $data[$i]->mustGetCompoundTag();
|
||||
$newTag = $data[$i + 1]->mustGetCompoundTag();
|
||||
for($i = 0; isset($oldNewStateList[$i]); $i += 2){
|
||||
$oldTag = $oldNewStateList[$i]->mustGetCompoundTag();
|
||||
$newTag = $oldNewStateList[$i + 1]->mustGetCompoundTag();
|
||||
$old = BlockStateData::fromNbt($reverse ? $newTag : $oldTag);
|
||||
$new = BlockStateData::fromNbt($reverse ? $oldTag : $newTag);
|
||||
|
||||
@ -107,6 +120,17 @@ function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BlockStateMapping[][]
|
||||
* @phpstan-return array<string, array<string, BlockStateMapping>>
|
||||
*/
|
||||
function loadUpgradeTableFromFile(string $file, bool $reverse) : array{
|
||||
$contents = Filesystem::fileGetContents($file);
|
||||
$data = (new NetworkNbtSerializer())->readMultiple($contents);
|
||||
|
||||
return buildUpgradeTableFromData($data, $reverse);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockStateData[] $states
|
||||
* @phpstan-param array<string, BlockStateData> $states
|
||||
@ -159,6 +183,11 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra
|
||||
$removedProperties = [];
|
||||
$renamedProperties = [];
|
||||
|
||||
$uniqueNewIds = [];
|
||||
foreach($upgradeTable as $pair){
|
||||
$uniqueNewIds[$pair->new->getName()] = $pair->new->getName();
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($newProperties) as $newPropertyName => $newPropertyValues){
|
||||
if(count($newPropertyValues) === 1){
|
||||
$newPropertyValue = $newPropertyValues[array_key_first($newPropertyValues)];
|
||||
@ -254,6 +283,45 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra
|
||||
}
|
||||
}
|
||||
|
||||
if(count($uniqueNewIds) > 1){
|
||||
//detect possible flattening
|
||||
$flattenedProperty = null;
|
||||
$flattenedPropertyType = null;
|
||||
$flattenedPropertyMap = [];
|
||||
foreach($removedProperties as $removedProperty){
|
||||
$valueMap = [];
|
||||
foreach($upgradeTable as $pair){
|
||||
$oldValue = $pair->old->getState($removedProperty);
|
||||
if($oldValue === null){
|
||||
throw new AssumptionFailedError("We already checked that all states had consistent old properties");
|
||||
}
|
||||
if(!checkFlattenPropertySuitability($oldValue, $flattenedPropertyType, $pair->new->getName(), $valueMap)){
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
if($flattenedProperty !== null){
|
||||
//found multiple candidates for flattening - fallback to remappedStates
|
||||
return false;
|
||||
}
|
||||
//we found a suitable candidate
|
||||
$flattenedProperty = $removedProperty;
|
||||
$flattenedPropertyMap = $valueMap;
|
||||
break;
|
||||
}
|
||||
|
||||
if($flattenedProperty === null){
|
||||
//can't figure out how the new IDs are related to the old states - fallback to remappedStates
|
||||
return false;
|
||||
}
|
||||
if($flattenedPropertyType === null){
|
||||
throw new AssumptionFailedError("This should never happen at this point");
|
||||
}
|
||||
|
||||
$result->flattenedProperties[$oldName] = buildFlattenPropertyRule($flattenedPropertyMap, $flattenedProperty, $flattenedPropertyType);
|
||||
unset($removedProperties[$flattenedProperty]);
|
||||
}
|
||||
|
||||
//finally, write the results to the schema
|
||||
|
||||
if(count($remappedPropertyValues) !== 0){
|
||||
@ -308,43 +376,100 @@ function findCommonSuffix(array $strings) : string{
|
||||
return strrev(findCommonPrefix($reversed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $valueToIdMap
|
||||
* @phpstan-param ?class-string<ByteTag|IntTag|StringTag> $expectedType
|
||||
* @phpstan-param-out class-string<ByteTag|IntTag|StringTag> $expectedType
|
||||
* @phpstan-param array<string, string> $valueToIdMap
|
||||
* @phpstan-param-out array<string, string> $valueToIdMap
|
||||
*/
|
||||
function checkFlattenPropertySuitability(Tag $oldValue, ?string &$expectedType, string $actualNewId, array &$valueToIdMap) : bool{
|
||||
//TODO: lots of similar logic to the remappedStates builder below
|
||||
if(!$oldValue instanceof ByteTag && !$oldValue instanceof IntTag && !$oldValue instanceof StringTag){
|
||||
//unknown property type - bad candidate for flattening
|
||||
return false;
|
||||
}
|
||||
if($expectedType === null){
|
||||
$expectedType = get_class($oldValue);
|
||||
}elseif(!$oldValue instanceof $expectedType){
|
||||
//property type mismatch - bad candidate for flattening
|
||||
return false;
|
||||
}
|
||||
|
||||
$rawValue = (string) $oldValue->getValue();
|
||||
$existingNewId = $valueToIdMap[$rawValue] ?? null;
|
||||
if($existingNewId !== null && $existingNewId !== $actualNewId){
|
||||
//this property value is associated with multiple new IDs - bad candidate for flattening
|
||||
return false;
|
||||
}
|
||||
$valueToIdMap[$rawValue] = $actualNewId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $valueToId
|
||||
* @phpstan-param array<string, string> $valueToId
|
||||
* @phpstan-param class-string<ByteTag|IntTag|StringTag> $propertyType
|
||||
*/
|
||||
function buildFlattenPropertyRule(array $valueToId, string $propertyName, string $propertyType) : BlockStateUpgradeSchemaFlattenInfo{
|
||||
$ids = array_values($valueToId);
|
||||
|
||||
//TODO: this is a bit too enthusiastic. For example, when flattening the old "stone", it will see that
|
||||
//"granite", "andesite", "stone" etc all have "e" as a common suffix, which works, but looks a bit daft.
|
||||
//This also causes more remaps to be generated than necessary, since some of the values are already
|
||||
//contained in the new ID.
|
||||
$idPrefix = findCommonPrefix($ids);
|
||||
$idSuffix = findCommonSuffix($ids);
|
||||
if(strlen($idSuffix) < 2){
|
||||
$idSuffix = "";
|
||||
}
|
||||
|
||||
$valueMap = [];
|
||||
foreach(Utils::stringifyKeys($valueToId) as $value => $newId){
|
||||
$newValue = substr($newId, strlen($idPrefix), $idSuffix !== "" ? -strlen($idSuffix) : null);
|
||||
if($newValue !== $value){
|
||||
$valueMap[$value] = $newValue;
|
||||
}
|
||||
}
|
||||
|
||||
$allNumeric = true;
|
||||
if(count($valueMap) > 0){
|
||||
foreach(Utils::stringifyKeys($valueMap) as $value => $newValue){
|
||||
if(!is_numeric($value)){
|
||||
$allNumeric = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($allNumeric){
|
||||
//add a dummy key to force the JSON to be an object and not a list
|
||||
$valueMap["dummy"] = "map_not_list";
|
||||
}
|
||||
}
|
||||
|
||||
return new BlockStateUpgradeSchemaFlattenInfo(
|
||||
$idPrefix,
|
||||
$propertyName,
|
||||
$idSuffix,
|
||||
$valueMap,
|
||||
$propertyType,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[][][] $candidateFlattenedValues
|
||||
* @phpstan-param array<string, array<string, array<string, string>>> $candidateFlattenedValues
|
||||
* @param string[] $candidateFlattenPropertyTypes
|
||||
* @phpstan-param array<string, class-string<ByteTag|IntTag|StringTag>> $candidateFlattenPropertyTypes
|
||||
*
|
||||
* @return BlockStateUpgradeSchemaFlattenedName[][]
|
||||
* @phpstan-return array<string, array<string, BlockStateUpgradeSchemaFlattenedName>>
|
||||
* @return BlockStateUpgradeSchemaFlattenInfo[][]
|
||||
* @phpstan-return array<string, array<string, BlockStateUpgradeSchemaFlattenInfo>>
|
||||
*/
|
||||
function buildFlattenPropertyRules(array $candidateFlattenedValues) : array{
|
||||
function buildFlattenPropertyRules(array $candidateFlattenedValues, array $candidateFlattenPropertyTypes) : array{
|
||||
$flattenPropertyRules = [];
|
||||
foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){
|
||||
foreach(Utils::stringifyKeys($filters) as $filter => $valueToId){
|
||||
$ids = array_values($valueToId);
|
||||
|
||||
//TODO: this is a bit too enthusiastic. For example, when flattening the old "stone", it will see that
|
||||
//"granite", "andesite", "stone" etc all have "e" as a common suffix, which works, but looks a bit daft.
|
||||
//This also causes more remaps to be generated than necessary, since some of the values are already
|
||||
//contained in the new ID.
|
||||
$idPrefix = findCommonPrefix($ids);
|
||||
$idSuffix = findCommonSuffix($ids);
|
||||
if(strlen($idSuffix) < 2){
|
||||
$idSuffix = "";
|
||||
}
|
||||
|
||||
$valueMap = [];
|
||||
foreach(Utils::stringifyKeys($valueToId) as $value => $newId){
|
||||
$newValue = substr($newId, strlen($idPrefix), $idSuffix !== "" ? -strlen($idSuffix) : null);
|
||||
if($newValue !== $value){
|
||||
$valueMap[$value] = $newValue;
|
||||
}
|
||||
}
|
||||
|
||||
$flattenPropertyRules[$propertyName][$filter] = new BlockStateUpgradeSchemaFlattenedName(
|
||||
$idPrefix,
|
||||
$propertyName,
|
||||
$idSuffix,
|
||||
$valueMap
|
||||
);
|
||||
$flattenPropertyRules[$propertyName][$filter] = buildFlattenPropertyRule($valueToId, $propertyName, $candidateFlattenPropertyTypes[$propertyName]);
|
||||
}
|
||||
}
|
||||
ksort($flattenPropertyRules, SORT_STRING);
|
||||
@ -406,56 +531,54 @@ function processRemappedStates(array $upgradeTable) : array{
|
||||
$notFlattenedProperties = [];
|
||||
|
||||
$candidateFlattenedValues = [];
|
||||
$candidateFlattenedPropertyTypes = [];
|
||||
foreach($upgradeTable as $pair){
|
||||
foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){
|
||||
if(isset($notFlattenedProperties[$propertyName])){
|
||||
continue;
|
||||
}
|
||||
if(!$propertyValue instanceof StringTag){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
$rawValue = $propertyValue->getValue();
|
||||
if($rawValue === ""){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$filter = $pair->old->getStates();
|
||||
foreach($unchangedStatesByNewName[$pair->new->getName()] as $unchangedPropertyName){
|
||||
if($unchangedPropertyName === $propertyName){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue 2;
|
||||
}
|
||||
unset($filter[$unchangedPropertyName]);
|
||||
}
|
||||
unset($filter[$propertyName]);
|
||||
|
||||
$rawFilter = encodeOrderedProperties($filter);
|
||||
if(isset($candidateFlattenedValues[$propertyName][$rawFilter])){
|
||||
$valuesToIds = $candidateFlattenedValues[$propertyName][$rawFilter];
|
||||
$existingNewId = $valuesToIds[$rawValue] ?? null;
|
||||
if($existingNewId !== null && $existingNewId !== $pair->new->getName()){
|
||||
//this old value is associated with multiple new IDs - bad candidate for flattening
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
foreach(Utils::stringifyKeys($valuesToIds) as $otherRawValue => $otherNewId){
|
||||
if($otherRawValue === $rawValue){
|
||||
continue;
|
||||
}
|
||||
if($otherNewId === $pair->new->getName()){
|
||||
//this old value maps to the same new ID as another old value - bad candidate for flattening
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$candidateFlattenedValues[$propertyName][$rawFilter] ??= [];
|
||||
$expectedType = $candidateFlattenedPropertyTypes[$propertyName] ?? null;
|
||||
if(!checkFlattenPropertySuitability($propertyValue, $expectedType, $pair->new->getName(), $candidateFlattenedValues[$propertyName][$rawFilter])){
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue;
|
||||
}
|
||||
$candidateFlattenedPropertyTypes[$propertyName] = $expectedType;
|
||||
}
|
||||
}
|
||||
foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){
|
||||
foreach($filters as $valuesToIds){
|
||||
if(count(array_unique($valuesToIds)) === 1){
|
||||
//this property doesn't influence the new ID
|
||||
$notFlattenedProperties[$propertyName] = true;
|
||||
continue 2;
|
||||
}
|
||||
$candidateFlattenedValues[$propertyName][$rawFilter][$rawValue] = $pair->new->getName();
|
||||
}
|
||||
}
|
||||
foreach(Utils::stringifyKeys($notFlattenedProperties) as $propertyName => $_){
|
||||
unset($candidateFlattenedValues[$propertyName]);
|
||||
}
|
||||
|
||||
$flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues);
|
||||
$flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues, $candidateFlattenedPropertyTypes);
|
||||
$flattenProperty = array_key_first($flattenedProperties);
|
||||
//Properties with fewer rules take up less space for the same result
|
||||
foreach(Utils::stringifyKeys($flattenedProperties) as $propertyName => $rules){
|
||||
if(count($rules) < count($flattenedProperties[$flattenProperty])){
|
||||
$flattenProperty = $propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
@ -475,8 +598,8 @@ function processRemappedStates(array $upgradeTable) : array{
|
||||
ksort($cleanedNewState);
|
||||
if($flattenProperty !== null){
|
||||
$flattenedValue = $cleanedOldState[$flattenProperty] ?? null;
|
||||
if(!$flattenedValue instanceof StringTag){
|
||||
throw new AssumptionFailedError("This should always be a TAG_String ($newName $flattenProperty)");
|
||||
if(!$flattenedValue instanceof StringTag && !$flattenedValue instanceof IntTag && !$flattenedValue instanceof ByteTag){
|
||||
throw new AssumptionFailedError("Non-flattenable type of tag ($newName $flattenProperty) but have " . get_debug_type($flattenedValue));
|
||||
}
|
||||
unset($cleanedOldState[$flattenProperty]);
|
||||
}
|
||||
@ -583,10 +706,15 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
||||
throw new \RuntimeException("States with the same ID should be fully consistent");
|
||||
}
|
||||
}else{
|
||||
//block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap
|
||||
//even if some of the states stay under the same ID, the compression techniques used by this function
|
||||
//implicitly rely on knowing the full set of old states and their new transformations
|
||||
$result->remappedStates[$oldName] = processRemappedStates($blockStateMappings);
|
||||
//try processing this as a regular state group first
|
||||
//if a property was flattened into the ID, the remaining states will normally be consistent
|
||||
//if not we fall back to remap states and state filters
|
||||
if(!processStateGroup($oldName, $blockStateMappings, $result)){
|
||||
//block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap
|
||||
//even if some of the states stay under the same ID, the compression techniques used by this function
|
||||
//implicitly rely on knowing the full set of old states and their new transformations
|
||||
$result->remappedStates[$oldName] = processRemappedStates($blockStateMappings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,18 +722,42 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $argv
|
||||
* @param BlockStateMapping[][] $upgradeTable
|
||||
* @phpstan-param array<string, array<string, BlockStateMapping>> $upgradeTable
|
||||
*/
|
||||
function main(array $argv) : int{
|
||||
if(count($argv) !== 3){
|
||||
fwrite(STDERR, "Required arguments: input file path, output file path\n");
|
||||
return 1;
|
||||
function testBlockStateUpgradeSchema(array $upgradeTable, BlockStateUpgradeSchema $schema) : bool{
|
||||
//TODO: HACK!
|
||||
//the upgrader won't apply the schema if it's the same version and there's only one schema with a matching version
|
||||
//ID (for performance reasons), which is a problem for testing isolated schemas
|
||||
//add a dummy schema to bypass this optimization
|
||||
$dummySchema = new BlockStateUpgradeSchema($schema->maxVersionMajor, $schema->maxVersionMinor, $schema->maxVersionPatch, $schema->maxVersionRevision, $schema->getSchemaId() + 1);
|
||||
$upgrader = new BlockStateUpgrader([$schema, $dummySchema]);
|
||||
|
||||
foreach($upgradeTable as $mappingsByOldName){
|
||||
foreach($mappingsByOldName as $mapping){
|
||||
$expectedNewState = $mapping->new;
|
||||
|
||||
$actualNewState = $upgrader->upgrade($mapping->old);
|
||||
|
||||
if(!$expectedNewState->equals($actualNewState)){
|
||||
\GlobalLogger::get()->error("Expected: " . $expectedNewState->toNbt());
|
||||
\GlobalLogger::get()->error("Actual: " . $actualNewState->toNbt());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$input = $argv[1];
|
||||
$output = $argv[2];
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = loadUpgradeTable($input, false);
|
||||
/**
|
||||
* @param string[] $argv
|
||||
*/
|
||||
function cmdGenerate(array $argv) : int{
|
||||
$upgradeTableFile = $argv[2];
|
||||
$schemaFile = $argv[3];
|
||||
|
||||
$table = loadUpgradeTableFromFile($upgradeTableFile, false);
|
||||
|
||||
ksort($table, SORT_STRING);
|
||||
|
||||
@ -614,13 +766,148 @@ function main(array $argv) : int{
|
||||
\GlobalLogger::get()->warning("All states appear to be the same! No schema generated.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!testBlockStateUpgradeSchema($table, $diff)){
|
||||
\GlobalLogger::get()->error("Generated schema does not produce the results expected by $upgradeTableFile");
|
||||
\GlobalLogger::get()->error("This is probably a bug in the schema generation code. Please report this to the developers.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$output,
|
||||
$schemaFile,
|
||||
json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($diff), JSON_PRETTY_PRINT) . "\n"
|
||||
);
|
||||
\GlobalLogger::get()->info("Schema file $output generated successfully.");
|
||||
\GlobalLogger::get()->info("Schema file $schemaFile generated successfully.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $argv
|
||||
*/
|
||||
function cmdTest(array $argv) : int{
|
||||
$upgradeTableFile = $argv[2];
|
||||
$schemaFile = $argv[3];
|
||||
|
||||
$table = loadUpgradeTableFromFile($upgradeTableFile, false);
|
||||
|
||||
ksort($table, SORT_STRING);
|
||||
|
||||
$schema = BlockStateUpgradeSchemaUtils::loadSchemaFromString(Filesystem::fileGetContents($schemaFile), 0);
|
||||
if(!testBlockStateUpgradeSchema($table, $schema)){
|
||||
\GlobalLogger::get()->error("Schema $schemaFile does not produce the results predicted by $upgradeTableFile");
|
||||
return 1;
|
||||
}
|
||||
\GlobalLogger::get()->info("Schema $schemaFile is valid according to $upgradeTableFile");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $argv
|
||||
*/
|
||||
function cmdUpdate(array $argv) : int{
|
||||
[, , $oldSchemaFile, $oldPaletteFile, $newSchemaFile] = $argv;
|
||||
|
||||
$palette = BlockStateDictionary::loadPaletteFromString(Filesystem::fileGetContents($oldPaletteFile));
|
||||
$schema = BlockStateUpgradeSchemaUtils::loadSchemaFromString(Filesystem::fileGetContents($oldSchemaFile), 0);
|
||||
//TODO: HACK!
|
||||
//the upgrader won't apply the schema if it's the same version and there's only one schema with a matching version
|
||||
//ID (for performance reasons), which is a problem for testing isolated schemas
|
||||
//add a dummy schema to bypass this optimization
|
||||
$dummySchema = new BlockStateUpgradeSchema($schema->maxVersionMajor, $schema->maxVersionMinor, $schema->maxVersionPatch, $schema->maxVersionRevision, $schema->getSchemaId() + 1);
|
||||
$upgrader = new BlockStateUpgrader([$schema, $dummySchema]);
|
||||
|
||||
$tags = [];
|
||||
foreach($palette as $stateData){
|
||||
$tags[] = new TreeRoot($stateData->toNbt());
|
||||
$tags[] = new TreeRoot($upgrader->upgrade($stateData)->toNbt());
|
||||
}
|
||||
|
||||
$upgradeTable = buildUpgradeTableFromData($tags, false);
|
||||
$newSchema = generateBlockStateUpgradeSchema($upgradeTable);
|
||||
|
||||
if(!testBlockStateUpgradeSchema($upgradeTable, $newSchema)){
|
||||
\GlobalLogger::get()->error("Updated schema does not produce the expected results!");
|
||||
\GlobalLogger::get()->error("This is probably a bug in the schema generation code. Please report this to the developers.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$newSchemaFile,
|
||||
json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($newSchema), JSON_PRETTY_PRINT) . "\n"
|
||||
);
|
||||
\GlobalLogger::get()->info("Schema file $newSchemaFile updated to new format (from $oldSchemaFile) successfully.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $argv
|
||||
*/
|
||||
function cmdUpdateAll(array $argv) : int{
|
||||
$oldPaletteFilenames = [
|
||||
'1.9.0' => '1.09.0',
|
||||
'1.19.50' => '1.19.50.23_beta',
|
||||
'1.19.60' => '1.19.60.26_beta',
|
||||
'1.19.70' => '1.19.70.26_beta',
|
||||
'1.19.80' => '1.19.80.24_beta',
|
||||
];
|
||||
$schemaDir = $argv[2];
|
||||
$paletteArchiveDir = $argv[3];
|
||||
|
||||
$schemaFileNames = scandir($schemaDir);
|
||||
if($schemaFileNames === false){
|
||||
\GlobalLogger::get()->error("Failed to read schema directory $schemaDir");
|
||||
return 1;
|
||||
}
|
||||
foreach($schemaFileNames as $file){
|
||||
$schemaFile = Path::join($schemaDir, $file);
|
||||
if(!file_exists($schemaFile) || is_dir($schemaFile)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(preg_match('/^\d{4}_(.+?)_to_(.+?).json/', $file, $matches) !== 1){
|
||||
continue;
|
||||
}
|
||||
$oldPaletteFile = Path::join($paletteArchiveDir, ($oldPaletteFilenames[$matches[1]] ?? $matches[1]) . '.nbt');
|
||||
|
||||
//a bit clunky but it avoids having to make yet another function
|
||||
//TODO: perhaps in the future we should write the result to a tmpfile until all schemas are updated,
|
||||
//and then copy the results into place at the end
|
||||
if(cmdUpdate([$argv[0], "update", $schemaFile, $oldPaletteFile, $schemaFile]) !== 0){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
\GlobalLogger::get()->info("All schemas updated successfully.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $argv
|
||||
*/
|
||||
function main(array $argv) : int{
|
||||
$options = [
|
||||
"generate" => [["palette upgrade table file", "schema output file"], cmdGenerate(...)],
|
||||
"test" => [["palette upgrade table file", "schema output file"], cmdTest(...)],
|
||||
"update" => [["schema input file", "old palette file", "updated schema output file"], cmdUpdate(...)],
|
||||
"update-all" => [["schema folder", "path to BlockPaletteArchive"], cmdUpdateAll(...)]
|
||||
];
|
||||
|
||||
$selected = $argv[1] ?? null;
|
||||
if($selected === null || !isset($options[$selected])){
|
||||
fwrite(STDERR, "Available commands:\n");
|
||||
foreach($options as $command => [$args, $callback]){
|
||||
fwrite(STDERR, " - $command " . implode(" ", array_map(fn(string $a) => "<$a>", $args)) . "\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
$callback = $options[$selected][1];
|
||||
if(count($argv) !== count($options[$selected][0]) + 2){
|
||||
fwrite(STDERR, "Usage: {$argv[0]} $selected " . implode(" ", array_map(fn(string $a) => "<$a>", $options[$selected][0])) . "\n");
|
||||
return 1;
|
||||
}
|
||||
return $callback($argv);
|
||||
}
|
||||
|
||||
exit(main($argv));
|
Loading…
x
Reference in New Issue
Block a user