Skip to content

Commit

Permalink
StructureBlock: create tile to store editor settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Bqleine committed Sep 11, 2023
1 parent eb2add8 commit 2560827
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 3 deletions.
20 changes: 20 additions & 0 deletions src/block/StructureBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@

use pocketmine\block\utils\StructureBlockType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\inventory\WindowTypes;
use pocketmine\player\Player;

class StructureBlock extends Opaque{
private StructureBlockType $type;
Expand All @@ -34,6 +40,15 @@ public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo
parent::__construct($idInfo, $name, $typeInfo);
}

public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if ($player instanceof Player) {
$pk = ContainerOpenPacket::blockInv(0, WindowTypes::STRUCTURE_EDITOR, BlockPosition::fromVector3($this->getPosition()));
$player->getNetworkSession()->sendDataPacket($pk);
return true;
}
return false;
}

public function describeBlockItemState(RuntimeDataDescriber $w) : void{
$w->enum($this->type);
}
Expand All @@ -45,6 +60,11 @@ public function getType() : StructureBlockType{
/** @return $this */
public function setType(StructureBlockType $type) : self{
$this->type = $type;
if ($this->position->isValid()){
$this->position->getWorld()->setBlock($this->position, $this);
}
return $this;
}

//TODO: The Structure Block has redstone effects, they are not implemented.
}
3 changes: 2 additions & 1 deletion src/block/VanillaBlocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
use pocketmine\block\tile\Note as TileNote;
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
use pocketmine\block\tile\Smoker as TileSmoker;
use pocketmine\block\tile\StructureBlock as TileStructureBlock;
use pocketmine\block\utils\LeavesType;
use pocketmine\block\utils\SaplingType;
use pocketmine\block\utils\WoodType;
Expand Down Expand Up @@ -1095,7 +1096,7 @@ public function isAffectedBySilkTouch() : bool{
self::register("smooth_quartz_slab", new Slab(new BID(Ids::SMOOTH_QUARTZ_SLAB), "Smooth Quartz", $stoneSlabBreakInfo));
self::register("stone_slab", new Slab(new BID(Ids::STONE_SLAB), "Stone", $stoneSlabBreakInfo));

self::register("structure_block", new StructureBlock(new BID(Ids::STRUCTURE_BLOCK), "Structure Block", new Info(BreakInfo::indestructible(3600000))));
self::register("structure_block", new StructureBlock(new BID(Ids::STRUCTURE_BLOCK, TileStructureBlock::class), "Structure Block", new Info(BreakInfo::indestructible(3600000))));
self::register("structure_void", new StructureVoid(new BID(Ids::STRUCTURE_VOID), "Structure Void", new Info(BreakInfo::indestructible(3600000))));

self::register("legacy_stonecutter", new Opaque(new BID(Ids::LEGACY_STONECUTTER), "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD))));
Expand Down
170 changes: 170 additions & 0 deletions src/block/tile/StructureBlock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?php

/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/

declare(strict_types=1);

namespace pocketmine\block\tile;

use pocketmine\block\utils\StructureBlockType;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\StructureBlockUpdatePacket;
use pocketmine\world\World;

class StructureBlock extends Spawnable{
public const TAG_ANIMATION_MODE = "animationMode";
public const TAG_ANIMATION_SECONDS = "animationSeconds";
public const TAG_DATA = "data";
public const TAG_DATA_FIELD = "dataField";
public const TAG_IGNORE_ENTITIES = "ignoreEntities";
public const TAG_INTEGRITY = "integrity";
public const TAG_IS_POWERED = "isPowered";
public const TAG_MIRROR = "mirror";
public const TAG_REDSTONE_SAVE_MODE = "redstoneSaveMode";
public const TAG_REMOVE_BLOCKS = "removeBlocks";
public const TAG_ROTATION = "rotation";
public const TAG_SEED = "seed";
public const TAG_SHOW_BOUNDING_BOX = "showBoundingBox";
public const TAG_STRUCTURE_NAME = "structureName";
public const TAG_X_STRUCTURE_OFFSET = "xStructureOffset";
public const TAG_Y_STRUCTURE_OFFSET = "yStructureOffset";
public const TAG_Z_STRUCTURE_OFFSET = "zStructureOffset";
public const TAG_X_STRUCTURE_SIZE = "xStructureSize";
public const TAG_Y_STRUCTURE_SIZE = "yStructureSize";
public const TAG_Z_STRUCTURE_SIZE = "zStructureSize";

private int $animationMode = 0; // byte
private float $animationSeconds = 0;
private StructureBlockType $type = StructureBlockType::SAVE; // byte
private string $dataField = ""; // unused
private bool $ignoreEntities = false;
private float $integrityValue = 100;
private int $integritySeed = 0; // long, 0 means random
private bool $isPowered = false; // TODO : set by client, should be server side
private int $mirror = 0; // byte
private int $redstoneSaveMode = 0;
private bool $removeBlocks = false;
private int $rotation = 0; // byte
private bool $showBoundingBox = true;
private string $structureName = "";
private Vector3 $structureOffset;
private Vector3 $structureSize;
// not stored in nbt but present in packet:
// bool waterlogged
// bool includePlayers
// string paletteName
// bool allowNonTickingChunks
// int lastTouchedByPlayerId
// Vector3 pivot

public function __construct(World $world, Vector3 $pos){
$this->structureOffset = new Vector3(0, -1, 0);
$this->structureSize = new Vector3(5, 5, 5);
parent::__construct($world, $pos);
}

// TODO : check values
public function readSaveData(CompoundTag $nbt) : void{
$this->animationMode = $nbt->getByte(self::TAG_ANIMATION_MODE, $this->animationMode);
$this->animationSeconds = $nbt->getFloat(self::TAG_ANIMATION_SECONDS, $this->animationSeconds);
$this->type = StructureBlockType::fromInt($nbt->getByte(self::TAG_DATA, StructureBlockType::toInt($this->type)));
$this->dataField = $nbt->getString(self::TAG_DATA_FIELD, $this->dataField);
$this->ignoreEntities = (bool) $nbt->getByte(self::TAG_IGNORE_ENTITIES, (int) $this->ignoreEntities);
$this->integrityValue = $nbt->getFloat(self::TAG_INTEGRITY, $this->integrityValue);
$this->isPowered = (bool) $nbt->getByte(self::TAG_IS_POWERED, (int) $this->isPowered);
$this->mirror = $nbt->getByte(self::TAG_MIRROR, $this->mirror);
$this->redstoneSaveMode = $nbt->getByte(self::TAG_REDSTONE_SAVE_MODE, $this->redstoneSaveMode);
$this->removeBlocks = (bool) $nbt->getByte(self::TAG_REMOVE_BLOCKS, (int) $this->removeBlocks);
$this->rotation = $nbt->getByte(self::TAG_ROTATION, $this->rotation);
$this->integritySeed = $nbt->getLong(self::TAG_SEED, $this->integritySeed);
$this->showBoundingBox = (bool) $nbt->getByte(self::TAG_SHOW_BOUNDING_BOX, (int) $this->showBoundingBox);
$this->structureName = $nbt->getString(self::TAG_STRUCTURE_NAME, $this->structureName);
$this->structureOffset = new Vector3(
$nbt->getInt(self::TAG_X_STRUCTURE_OFFSET, $this->structureOffset->getFloorX()),
$nbt->getInt(self::TAG_Y_STRUCTURE_OFFSET, $this->structureOffset->getFloorY()),
$nbt->getInt(self::TAG_Z_STRUCTURE_OFFSET, $this->structureOffset->getFloorZ()),
);
$this->structureSize = new Vector3(
$nbt->getInt(self::TAG_X_STRUCTURE_SIZE, $this->structureSize->getFloorX()),
$nbt->getInt(self::TAG_Y_STRUCTURE_SIZE, $this->structureSize->getFloorY()),
$nbt->getInt(self::TAG_Z_STRUCTURE_SIZE, $this->structureSize->getFloorZ()),
);
}

protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setByte(self::TAG_ANIMATION_MODE, $this->animationMode);
$nbt->setFloat(self::TAG_ANIMATION_SECONDS, $this->animationSeconds);
$nbt->setInt(self::TAG_DATA, StructureBlockType::toInt($this->type));
$nbt->setString(self::TAG_DATA_FIELD, $this->dataField);
$nbt->setByte(self::TAG_IGNORE_ENTITIES, (int) $this->ignoreEntities);
$nbt->setFloat(self::TAG_INTEGRITY, $this->integrityValue);
$nbt->setByte(self::TAG_IS_POWERED, (int) $this->isPowered);
$nbt->setByte(self::TAG_MIRROR, $this->mirror);
$nbt->setByte(self::TAG_REDSTONE_SAVE_MODE, $this->redstoneSaveMode);
$nbt->setByte(self::TAG_REMOVE_BLOCKS, (int) $this->removeBlocks);
$nbt->setByte(self::TAG_ROTATION, $this->rotation);
$nbt->setLong(self::TAG_SEED, $this->integritySeed);
$nbt->setByte(self::TAG_SHOW_BOUNDING_BOX, (int) $this->showBoundingBox);
$nbt->setString(self::TAG_STRUCTURE_NAME, $this->structureName);
$nbt->setInt(self::TAG_X_STRUCTURE_OFFSET, $this->structureOffset->getFloorX());
$nbt->setInt(self::TAG_Y_STRUCTURE_OFFSET, $this->structureOffset->getFloorY());
$nbt->setInt(self::TAG_Z_STRUCTURE_OFFSET, $this->structureOffset->getFloorZ());
$nbt->setInt(self::TAG_X_STRUCTURE_SIZE, $this->structureSize->getFloorX());
$nbt->setInt(self::TAG_Y_STRUCTURE_SIZE, $this->structureSize->getFloorY());
$nbt->setInt(self::TAG_Z_STRUCTURE_SIZE, $this->structureSize->getFloorZ());
}

protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$this->writeSaveData($nbt);
}

/**
* @throws \UnexpectedValueException
*/
public function updateFromPacket(StructureBlockUpdatePacket $packet) : void{
//waterlogged
$this->isPowered = $packet->isPowered;

$data = $packet->structureEditorData;
$this->structureName = $data->structureName;
$this->dataField = $data->structureDataField;
//includePlayers
$this->showBoundingBox = $data->showBoundingBox;
$this->type = StructureBlockType::fromInt($data->structureBlockType);
$this->redstoneSaveMode = $data->structureRedstoneSaveMove;

$settings = $data->structureSettings;
//paletteName
$this->ignoreEntities = $settings->ignoreEntities;
$this->removeBlocks = $settings->ignoreBlocks;
//allowNonTickingChunks
$this->structureSize = new Vector3($settings->dimensions->getX(), $settings->dimensions->getY(), $settings->dimensions->getZ());
$this->structureOffset = new Vector3($settings->offset->getX(), $settings->offset->getY(), $settings->offset->getZ());
//lastTouchedByPlayerId
$this->rotation = $settings->rotation;
$this->mirror = $settings->mirror;
$this->animationMode = $settings->animationMode;
$this->animationSeconds = $settings->animationSeconds;
$this->integrityValue = $settings->integrityValue;
$this->integritySeed = $settings->integritySeed;
//pivot
}
}
2 changes: 1 addition & 1 deletion src/block/tile/TileFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public function __construct(){
$this->register(SporeBlossom::class, ["SporeBlossom", "minecraft:spore_blossom"]);
$this->register(MobHead::class, ["Skull", "minecraft:skull"]);
$this->register(GlowingItemFrame::class, ["GlowItemFrame"]);
$this->register(StructureBlock::class, ["StructureBlock", "minecraft:structure_block"]);

//TODO: Campfire
//TODO: ChalkboardBlock
Expand All @@ -91,7 +92,6 @@ public function __construct(){
//TODO: MovingBlock
//TODO: NetherReactor
//TODO: PistonArm
//TODO: StructureBlock
}

/**
Expand Down
28 changes: 27 additions & 1 deletion src/block/utils/StructureBlockType.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@ enum StructureBlockType{
case SAVE;
case LOAD;
case CORNER;
case INVALID; // Inventory Structure Block
case INVALID;
case EXPORT;

/**
* @throws \UnexpectedValueException
*/
public static function fromInt(int $type) : StructureBlockType{
return match($type){
0 => self::DATA,
1 => self::SAVE,
2 => self::LOAD,
3 => self::CORNER,
4 => self::INVALID,
5 => self::EXPORT,
default => throw new \UnexpectedValueException("Unknown structure block type " . $type),
};
}

public static function toInt(StructureBlockType $type) : int{
return match($type){
self::DATA => 0,
self::SAVE => 1,
self::LOAD => 2,
self::CORNER => 3,
self::INVALID => 4,
self::EXPORT => 5,
};
}
}
28 changes: 28 additions & 0 deletions src/network/mcpe/handler/InGamePacketHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@
use pocketmine\block\BaseSign;
use pocketmine\block\ItemFrame;
use pocketmine\block\Lectern;
use pocketmine\block\StructureBlock;
use pocketmine\block\tile\Sign;
use pocketmine\block\tile\StructureBlock as TileStructureBlock;
use pocketmine\block\utils\SignText;
use pocketmine\block\utils\StructureBlockType;
use pocketmine\entity\animation\ConsumingItemAnimation;
use pocketmine\entity\Attribute;
use pocketmine\entity\InvalidSkinException;
Expand Down Expand Up @@ -86,6 +89,7 @@
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
use pocketmine\network\mcpe\protocol\StructureBlockUpdatePacket;
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;
Expand Down Expand Up @@ -1050,4 +1054,28 @@ public function handleRequestAbility(RequestAbilityPacket $packet) : bool{

return false;
}

public function handleStructureBlockUpdate(StructureBlockUpdatePacket $packet) : bool{
$pos = $packet->blockPosition;
$chunkX = $pos->getX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $pos->getZ() >> Chunk::COORD_BIT_SIZE;
$world = $this->player->getWorld();
if(!$world->isChunkLoaded($chunkX, $chunkZ) || $world->isChunkLocked($chunkX, $chunkZ)){
return false;
}

$tile = $world->getTileAt($pos->getX(), $pos->getY(), $pos->getZ());
$block = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ());
if($tile instanceof TileStructureBlock && $block instanceof StructureBlock && $this->player->canInteract($block->getPosition(), 15)){
try{
$type = StructureBlockType::fromInt($packet->structureEditorData->structureBlockType);
}catch(\UnexpectedValueException $e){
throw PacketHandlingException::wrap($e);
}
$block->setType($type);
$tile->updateFromPacket($packet);
return true;
}
return false;
}
}

0 comments on commit 2560827

Please sign in to comment.