diff --git a/.poggit.yml b/.poggit.yml index dc74bb13..b3d168d8 100644 --- a/.poggit.yml +++ b/.poggit.yml @@ -25,10 +25,10 @@ projects: version: ^4.3.4 branch: "4.0" - src: thebigsmilexd/libstructure/libstructure - version: ^0.1.7 - branch: libblockstate + version: ^0.3.0 + branch: rewrite - src: thebigsmilexd/libblockstate/libblockstate - version: ^0.0.1 + version: ^0.1.2 branch: master - src: Ifera/scorefactory/ScoreFactory version: ^3.1.0 diff --git a/plugin.yml b/plugin.yml index 9a6df1a4..5b7dc632 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,7 +1,7 @@ --- name: MagicWE2 main: xenialdan\MagicWE2\Loader -version: 10.2.2 +version: 10.3.0 api: ["4.0.0"] php: [ "8.0" ] softdepend: [ "DEVirion" ] diff --git a/resources/lang/eng.ini b/resources/lang/eng.ini index a79e2ee3..7073031d 100644 --- a/resources/lang/eng.ini +++ b/resources/lang/eng.ini @@ -57,6 +57,7 @@ session.language.set = "Successfully set language to {%0}" session.language.notfound = "Language {%0} not found, resetting to default" ; task task.copy.success = "Async Copy succeed, took {%0}, copied {%1} blocks out of {%2}." +task.paste.success = "Async Paste succeed, took {%0}, pasted {%1} blocks out of {%2}." task.count.success = "Async analysing succeed, took {%0}" task.count.result = "{%0} blocks found in a total of {%1} blocks" task.fill.success = "Async Fill succeed, took {%0}, {%1} blocks out of {%2} changed." @@ -78,12 +79,13 @@ tool.wand.lore.3 = "Use //togglewand to toggle it's functionality" tool.wand.disabled = "The wand tool is disabled. Use //togglewand to re-enable it" tool.wand.setenabled = "The wand tool is now {%0}!" ; debug tool -tool.debug = "Debug Tool" -tool.debug.lore.1 = "Left click a block to get information" -tool.debug.lore.2 = "like the name and damage values of a block" -tool.debug.lore.3 = "Use //toggledebug to toggle it's functionality" -tool.debug.disabled = "The debug tool is disabled. Use //toggledebug to re-enable it" -tool.debug.setenabled = "The debug tool is now {%0}!" +tool.debug = "Debug Stick" +tool.debug.lore.1 = "Left click a block to select a blockstate" +tool.debug.lore.2 = "Right click a block to advance the blockstate's value" +tool.debug.lore.3 = "Sneak to reverse the order" +tool.debug.lore.4 = "Use //toggledebug to toggle it's functionality" +tool.debug.disabled = "The debug stick is disabled. Use //toggledebug to re-enable it" +tool.debug.setenabled = "The debug stick is now {%0}!" ; WAILA tool (What am i looking at) tool.waila = "Waila" tool.waila.setenabled = "The Waila utility is now {%0}!" diff --git a/src/xenialdan/MagicWE2/API.php b/src/xenialdan/MagicWE2/API.php index d40b129c..b36c7820 100644 --- a/src/xenialdan/MagicWE2/API.php +++ b/src/xenialdan/MagicWE2/API.php @@ -7,23 +7,33 @@ use BlockHorizons\libschematic\Schematic; use Exception; use InvalidArgumentException; +use JsonSchema\Exception\ResourceNotFoundException; use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\math\Vector3; +use pocketmine\nbt\NoSuchTagException; use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\UnexpectedTagTypeException; +use pocketmine\network\mcpe\protocol\types\BlockPosition; use pocketmine\player\Player; use pocketmine\Server; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; +use pocketmine\utils\Utils; use pocketmine\world\Position; use RuntimeException; use xenialdan\libblockstate\BlockEntry; +use xenialdan\libblockstate\BlockState; +use xenialdan\libblockstate\BlockStatesParser; +use xenialdan\libblockstate\exception\BlockQueryParsingFailedException; use xenialdan\libstructure\format\MCStructure; +use xenialdan\libstructure\format\MCStructureData; use xenialdan\MagicWE2\clipboard\Clipboard; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\BlockQueryAlreadyParsedException; use xenialdan\MagicWE2\exception\CalculationException; use xenialdan\MagicWE2\exception\LimitExceededException; +use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\selection\shape\Cuboid; @@ -31,17 +41,20 @@ use xenialdan\MagicWE2\session\data\Asset; use xenialdan\MagicWE2\session\Session; use xenialdan\MagicWE2\session\UserSession; +use xenialdan\MagicWE2\task\action\CountAction; use xenialdan\MagicWE2\task\action\RotateAction; use xenialdan\MagicWE2\task\action\SetBiomeAction; use xenialdan\MagicWE2\task\action\TaskAction; use xenialdan\MagicWE2\task\AsyncActionTask; use xenialdan\MagicWE2\task\AsyncCopyTask; -use xenialdan\MagicWE2\task\AsyncCountTask; use xenialdan\MagicWE2\task\AsyncFillTask; use xenialdan\MagicWE2\task\AsyncPasteAssetTask; use xenialdan\MagicWE2\task\AsyncPasteTask; use xenialdan\MagicWE2\task\AsyncReplaceTask; use xenialdan\MagicWE2\tool\Brush; +use function array_keys; +use function str_replace; +use function var_dump; class API { @@ -75,10 +88,12 @@ class API public const TAG_MAGIC_WE_BRUSH = "MagicWEBrush"; public const TAG_MAGIC_WE_ASSET = "MagicWEAsset"; public const TAG_MAGIC_WE_PALETTE = "MagicWEPalette"; + public const TAG_MAGIC_WE_DEBUG = "MagicWEDebug"; //TODO Split into separate Class (SchematicStorage?) /** @var Clipboard[] */ private static array $schematics = [];//TODO + public static array $rotationData = []; /** * @param Selection $selection @@ -190,18 +205,18 @@ public static function pasteAsync(SingleClipboard $clipboard, Session $session, } #$c = $clipboard->getCenter(); #$clipboard->setCenter($target->asVector3());//TODO check - if ($session instanceof UserSession) { + if($session instanceof UserSession){ $player = $session->getPlayer(); /** @var Player $player */ $session->getBossBar()->showTo([$player]); } // $start = clone $target->asVector3()->floor()->addVector($clipboard->position)->floor();//start pos of paste//TODO if using rotate, this fails // $end = $start->addVector($clipboard->selection->getShape()->getMaxVec3()->subtractVector($clipboard->selection->getShape()->getMinVec3()));//add size - $shape = $clipboard->selection->getShape(); - $shape->offset($shape->getPasteVector()->subtractVector($target->asVector3())); - $shape->setPasteVector($target->asVector3()->floor());//needed - $clipboard->selection->setShape($shape);//TODO probably need to update selection - Server::getInstance()->getAsyncPool()->submitTask(new AsyncPasteTask($session->getUUID(), $clipboard)); +// $shape = $clipboard->selection->getShape(); +// $shape->offset($shape->getPasteVector()->subtractVector($target->asVector3())); +// $shape->setPasteVector($target->asVector3()->floor());//needed +// $clipboard->selection->setShape($shape);//TODO probably need to update selection + Server::getInstance()->getAsyncPool()->submitTask(new AsyncPasteTask($session->getUUID(), $clipboard, $target->floor())); } catch (Exception $e) { $session->sendMessage($e->getMessage()); Loader::getInstance()->getLogger()->logException($e); @@ -218,12 +233,20 @@ public static function pasteAsync(SingleClipboard $clipboard, Session $session, * @return bool */ public static function countAsync(Selection $selection, Session $session, BlockPalette $filterBlocks, int $flags = self::FLAG_BASE): bool{ - try { + try{ $limit = Loader::getInstance()->getConfig()->get("limit", -1); - if ($limit !== -1 && $selection->getShape()->getTotalCount() > $limit) { + if($limit !== -1 && $selection->getShape()->getTotalCount() > $limit){ throw new LimitExceededException("You are trying to count too many blocks at once. Reduce the selection or raise the limit"); } - Server::getInstance()->getAsyncPool()->submitTask(new AsyncCountTask($session->getUUID(), $selection, $filterBlocks)); + Server::getInstance()->getAsyncPool()->submitTask( + new AsyncActionTask( + $session->getUUID(), + $selection, + new CountAction(), + BlockPalette::CREATE(), + $filterBlocks + ) + ); } catch (Exception $e) { $session->sendMessage($e->getMessage()); Loader::getInstance()->getLogger()->logException($e); @@ -482,9 +505,9 @@ public static function evalAsMath(string $str): float|int{ * @param CompoundTag $compoundTag * @return array */ - public static function compoundToArray(CompoundTag $compoundTag): array{ + public static function compoundToArray(CompoundTag $compoundTag) : array{//TODO add recursive $a = []; - foreach ($compoundTag->getValue() as $key => $value) { + foreach($compoundTag->getValue() as $key => $value){ $a[$key] = $value; } return $a; @@ -520,23 +543,24 @@ public static function positiveModulo(int $i, int $n) : int{ } /** - * @throws InvalidArgumentException + * @throws InvalidArgumentException|SelectionException */ - public static function rotate(Schematic|MCStructure|SingleClipboard $structure, int $rotation) : Schematic|MCStructure|SingleClipboard{ + public static function rotate(Schematic|MCStructure|SingleClipboard $structure, int $rotation, array &$errors = []) : Schematic|MCStructure|SingleClipboard{ $rotation = self::positiveModulo($rotation, 360); if($rotation % 90 !== 0){ throw new InvalidArgumentException("Rotation must be divisible by 90"); } - if($structure instanceof Schematic) return self::rotateSchematic($structure, $rotation); - elseif($structure instanceof MCStructure) return self::rotateStructure($structure, $rotation);//TODO add support for creating new structures to libstructures - elseif($structure instanceof SingleClipboard) return self::rotateClipboard($structure, $rotation); + $errors = []; + if($structure instanceof Schematic) return self::rotateSchematic($structure, $rotation, $errors); + elseif($structure instanceof MCStructure) return self::rotateStructure($structure, $rotation, $errors);//TODO add support for creating new structures to libstructures + elseif($structure instanceof SingleClipboard) return self::rotateClipboard($structure, $rotation, $errors); throw new InvalidArgumentException("Invalid structure"); } /** * @throws InvalidArgumentException */ - private static function rotateSchematic(Schematic $structure, int $rotation) : Schematic{ + private static function rotateSchematic(Schematic $structure, int $rotation, array &$errors = []) : Schematic{ if($rotation % 90 !== 0){ throw new InvalidArgumentException("Rotation must be divisible by 90"); } @@ -549,30 +573,77 @@ private static function rotateSchematic(Schematic $structure, int $rotation) : S /** @var Block $block */ foreach($structure->blocks() as $block){ //TODO set block to rotated blockstate - - $blocks[] = match ($rotation) { - //TODO check if the new positions are calculated correctly - RotateAction::ROTATE_90 => self::setComponents($block, $block->getPosition()->getFloorZ(), $block->getPosition()->getFloorY(), $structure->getWidth() - $block->getPosition()->getFloorX() - 1), - RotateAction::ROTATE_180 => self::setComponents($block, $structure->getWidth() - $block->getPosition()->getFloorX() - 1, $block->getPosition()->getFloorY(), $structure->getLength() - $block->getPosition()->getFloorZ() - 1), - RotateAction::ROTATE_270 => self::setComponents($block, $structure->getLength() - $block->getPosition()->getFloorZ() - 1, $block->getPosition()->getFloorY(), $block->getPosition()->getFloorX()), - default => $block - }; - //TODO move origin of structure + $state = self::entryToState(BlockEntry::fromBlock($block)); + try{ + $stateRotated = self::rotateBlockState($state, $rotation); + $blocks[] = match ($rotation) { + //TODO check if the new positions are calculated correctly + RotateAction::ROTATE_90 => self::setComponents($stateRotated->getBlock(), $structure->getLength() - $block->getPosition()->getFloorZ() - 1, $block->getPosition()->getFloorY(), $block->getPosition()->getFloorX()), + RotateAction::ROTATE_180 => self::setComponents($stateRotated->getBlock(), $structure->getWidth() - $block->getPosition()->getFloorX() - 1, $block->getPosition()->getFloorY(), $structure->getLength() - $block->getPosition()->getFloorZ() - 1), + RotateAction::ROTATE_270 => self::setComponents($stateRotated->getBlock(), $block->getPosition()->getFloorZ(), $block->getPosition()->getFloorY(), $structure->getWidth() - $block->getPosition()->getFloorX() - 1), + default => $stateRotated->getBlock() + }; + }catch(BlockQueryParsingFailedException | NoSuchTagException | UnexpectedTagTypeException $e){ + $errors[] = $e->getMessage();//TODO implement error printing, for now silently continue + continue; + } } + //TODO move origin of structure $newSchematic = new Schematic(); $newSchematic->setBlockArray($blocks); return $newSchematic; } - private static function rotateStructure(MCStructure $structure, int $rotation) : MCStructure{ - //TODO this is not yet implemented due to lack of support for creating new MCStructures in libstructure - return $structure; + private static function rotateStructure(MCStructure $structure, int $rotation, array &$errors = []) : MCStructure{ + if($rotation % 90 !== 0){ + throw new InvalidArgumentException("Rotation must be divisible by 90"); + } + $rotation = self::positiveModulo($rotation, 360); + if($rotation === 0) return $structure; + + $shape = new Cuboid(Vector3::zero(), $structure->getSize()->getX(), $structure->getSize()->getY(), $structure->getSize()->getZ()); + $shape = $shape->rotate($rotation); + var_dump("TILES BEFORE ROTATE", array_keys($structure->getBlockEntitiesRaw())); + $new = new MCStructure(MCStructureData::fromStructure($structure), new BlockPosition($shape->width, $shape->height, $shape->depth), $structure->origin); + var_dump("TILES AFTER ROTATE", array_keys($new->getBlockEntitiesRaw())); + + + //$x = $y = $z = null; + foreach($structure->blocks() as $entry){ + if($entry === null) continue; + //TODO set block to rotated blockstate + $state = BlockStatesParser::getInstance()->getFromBlock($entry); + try{ + $stateRotated = self::rotateBlockState($state, $rotation); + [$x, $y, $z] = [$entry->getPosition()->getX(), $entry->getPosition()->getY(), $entry->getPosition()->getZ()]; + + $newV3 = match ($rotation)//TODO figure out how to avoid new Vector3 objects + { + RotateAction::ROTATE_90 => new Vector3($structure->getSize()->getZ() - $z - 1, $y, $x), + RotateAction::ROTATE_180 => new Vector3($structure->getSize()->getX() - $x - 1, $y, $structure->getSize()->getZ() - $z - 1),//TODO is this flip instead of rotate? + RotateAction::ROTATE_270 => new Vector3($z, $y, $structure->getSize()->getX() - $x - 1), + default => new Vector3($x, $y, $z) + }; + //TODO move origin of structure + if(($tile = $structure->translateBlockEntity($entry->getPosition(), new Vector3($structure->origin->getX(), $structure->origin->getY(), $structure->origin->getZ())) !== null)){ + var_dump($tile); + } + $new->set($newV3->getX(), $newV3->getY(), $newV3->getZ(), $stateRotated); + }catch(BlockQueryParsingFailedException | NoSuchTagException | UnexpectedTagTypeException $e){ + $errors[] = $e->getMessage();//TODO implement error printing, for now silently continue + continue; + } + } + $new->check(); + return $new->parse(); } /** - * @throws InvalidArgumentException + * @param array $errors Exceptions thrown whilst rotating the blockstates + * + * @throws InvalidArgumentException|SelectionException */ - private static function rotateClipboard(SingleClipboard $structure, int $rotation) : SingleClipboard{ + private static function rotateClipboard(SingleClipboard $structure, int $rotation, array &$errors = []) : SingleClipboard{ if($rotation % 90 !== 0){ throw new InvalidArgumentException("Rotation must be divisible by 90"); } @@ -580,23 +651,84 @@ private static function rotateClipboard(SingleClipboard $structure, int $rotatio if($rotation === 0) return $structure; //width is x axis, length is z axis - $newClipboard = new SingleClipboard(Vector3::zero()); + $newClipboard = new SingleClipboard($structure->position); + $newClipboard->selection = $structure->selection; +// $newClipboard->selection->free();//TODO check if this is necessary + $newClipboard->selection->shape = $structure->selection->getShape()->rotate($rotation); + //$x = $y = $z = null; /** @var BlockEntry $entry */ foreach($structure->iterateEntries($x, $y, $z) as $entry){ //TODO set entry to rotated blockstate - - $newV3 = match ($rotation)//TODO figure out how to avoid new Vector3 objects - { - RotateAction::ROTATE_90 => new Vector3($z, $y, $structure->selection->getSizeX() - $x - 1), - RotateAction::ROTATE_180 => new Vector3($structure->selection->getSizeX() - $x - 1, $y, $structure->selection->getSizeZ() - $z - 1),//TODO is this flip instead of rotate? - RotateAction::ROTATE_270 => new Vector3($structure->selection->getSizeZ() - $z - 1, $y, $x), - default => new Vector3($x, $y, $z) - }; - $newClipboard->addEntry($newV3->getFloorX(), $newV3->getFloorY(), $newV3->getFloorZ(), $entry); - //TODO move origin of structure + $state = self::entryToState($entry); + try{ + $stateRotated = self::rotateBlockState($state, $rotation); + + $newV3 = match ($rotation)//TODO figure out how to avoid new Vector3 objects + { + RotateAction::ROTATE_90 => new Vector3($structure->selection->getSizeZ() - $z - 1, $y, $x), + RotateAction::ROTATE_180 => new Vector3($structure->selection->getSizeX() - $x - 1, $y, $structure->selection->getSizeZ() - $z - 1),//TODO is this flip instead of rotate? + RotateAction::ROTATE_270 => new Vector3($z, $y, $structure->selection->getSizeX() - $x - 1), + default => new Vector3($x, $y, $z) + }; + //TODO move origin of structure + $newClipboard->addEntry($newV3->getFloorX(), $newV3->getFloorY(), $newV3->getFloorZ(), new BlockEntry($stateRotated->getFullId())); + }catch(BlockQueryParsingFailedException | NoSuchTagException | UnexpectedTagTypeException $e){ + $errors[] = $e->getMessage();//TODO implement error printing, for now silently continue + continue; + } } return $newClipboard; } + public static function decodeJsonResource(string $filename) : array{ + $resource = Loader::getInstance()->getResource($filename); + if($resource === null) throw new ResourceNotFoundException("Resource not found: $filename"); + $array = json_decode(Utils::assumeNotFalse(stream_get_contents($resource), "Invalid json file: $filename"), true); + fclose($resource); + if($array === null) throw new AssumptionFailedError("Invalid json file: $filename"); + return $array; + } + + public static function entryToState(BlockEntry $entry) : BlockState{ + /** @var BlockStatesParser $parser */ + $parser = BlockStatesParser::getInstance(); + return $parser->getFullId($entry->fullId); + } + + public static function stateToEntry(BlockState $state) : BlockEntry{ + return new BlockEntry($state->getFullId()); + } + + /** + * @throws InvalidArgumentException When rotation is invalid (not divisible by 90) + * @throws UnexpectedTagTypeException|NoSuchTagException + * @throws BlockQueryParsingFailedException When no such blockstate could be found + */ + private static function rotateBlockState(BlockState $state, int $rotation) : BlockState{ + $data = self::getRotationData($state, $rotation); + if(empty($data)) return $state; + + #$newState = clone $state;//TODO check if needed + $newState = $state; + + return $newState->replaceBlockStateValues($data); + } + + /** @throws InvalidArgumentException */ + public static function getRotationData(BlockState $blockState, int $rotation) : array{ + if($rotation !== RotateAction::ROTATE_90 && $rotation !== RotateAction::ROTATE_180 && $rotation !== RotateAction::ROTATE_270) throw new InvalidArgumentException("Invalid rotation $rotation given"); + + $id = str_replace("minecraft:", "", $blockState->state->getId()); + $meta = $blockState->state->getMeta(); + + if(!isset(API::$rotationData[$id . ":" . $meta])) return []; + + return API::$rotationData[$id . ":" . $meta][(string) $rotation] ?? []; + } + + public static function setRotationData(array $json) : void{ + self::$rotationData = $json; + } + } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/EventListener.php b/src/xenialdan/MagicWE2/EventListener.php index c41d7f27..2440cb6d 100644 --- a/src/xenialdan/MagicWE2/EventListener.php +++ b/src/xenialdan/MagicWE2/EventListener.php @@ -19,12 +19,12 @@ use pocketmine\event\player\PlayerJoinEvent; use pocketmine\event\player\PlayerQuitEvent; use pocketmine\item\ItemIds; +use pocketmine\item\Stick; use pocketmine\nbt\UnexpectedTagTypeException; use pocketmine\player\Player; use pocketmine\plugin\Plugin; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\world\Position; use RuntimeException; use xenialdan\libstructure\tile\StructureBlockTile; use xenialdan\MagicWE2\event\MWESelectionChangeEvent; @@ -34,42 +34,41 @@ use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\tool\Brush; +use xenialdan\MagicWE2\tool\Debug; +use function is_null; use function var_dump; -class EventListener implements Listener -{ +class EventListener implements Listener{ /** @var Plugin */ public Plugin $owner; - public function __construct(Plugin $plugin) - { + public function __construct(Plugin $plugin){ $this->owner = $plugin; } /** * @param PlayerJoinEvent $event + * * @throws InvalidSkinException * @throws JsonException * @throws RuntimeException * @throws SessionException */ - public function onLogin(PlayerJoinEvent $event): void - { - if ($event->getPlayer()->hasPermission("we.session")) { - if (SessionHelper::hasSession($event->getPlayer()) && ($session = SessionHelper::getUserSession($event->getPlayer())) instanceof UserSession) { + public function onLogin(PlayerJoinEvent $event) : void{ + if($event->getPlayer()->hasPermission("we.session")){ + if(SessionHelper::hasSession($event->getPlayer()) && ($session = SessionHelper::getUserSession($event->getPlayer())) instanceof UserSession){ Loader::getInstance()->getLogger()->debug("Restored cached session for player {$session->getPlayer()->getName()}"); - } else if (($session = SessionHelper::loadUserSession($event->getPlayer())) instanceof UserSession) { + }else if(($session = SessionHelper::loadUserSession($event->getPlayer())) instanceof UserSession){ Loader::getInstance()->getLogger()->debug("Restored session from file for player {$session->getPlayer()->getName()}"); - } else (SessionHelper::createUserSession($event->getPlayer())); + }else (SessionHelper::createUserSession($event->getPlayer())); } } - public function onSessionLoad(MWESessionLoadEvent $event): void - { + public function onSessionLoad(MWESessionLoadEvent $event) : void{ Loader::getInstance()->wailaBossBar->addPlayer($event->getPlayer()); - if (Loader::hasScoreboard()) { + if(Loader::hasScoreboard()){ $session = $event->getSession(); - if ($session instanceof UserSession && $session->isSidebarEnabled()) + if($session instanceof UserSession && $session->isSidebarEnabled()) /** @var UserSession $session */ $session->sidebar->handleScoreboard($session); } @@ -77,12 +76,12 @@ public function onSessionLoad(MWESessionLoadEvent $event): void /** * @param PlayerQuitEvent $event + * * @throws JsonException * @throws SessionException */ - public function onLogout(PlayerQuitEvent $event): void - { - if (($session = SessionHelper::getUserSession($event->getPlayer())) instanceof UserSession) { + public function onLogout(PlayerQuitEvent $event) : void{ + if(($session = SessionHelper::getUserSession($event->getPlayer())) instanceof UserSession){ SessionHelper::destroySession($session); unset($session); } @@ -90,13 +89,13 @@ public function onLogout(PlayerQuitEvent $event): void /** * @param PlayerInteractEvent $event + * * @throws AssumptionFailedError * @throws Error */ - public function onInteract(PlayerInteractEvent $event): void - { - try { - switch ($event->getAction()) { + public function onInteract(PlayerInteractEvent $event) : void{ + try{ + switch($event->getAction()){ case PlayerInteractEvent::RIGHT_CLICK_BLOCK: { $this->onRightClickBlock($event); @@ -108,7 +107,7 @@ public function onInteract(PlayerInteractEvent $event): void break; } } - } catch (Exception $error) { + }catch(Exception $error){ $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . "Interaction failed!"); $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); } @@ -116,13 +115,13 @@ public function onInteract(PlayerInteractEvent $event): void /** * @param PlayerItemUseEvent $event + * * @throws AssumptionFailedError */ - public function onItemRightClick(PlayerItemUseEvent $event): void - { - try { + public function onItemRightClick(PlayerItemUseEvent $event) : void{ + try{ $this->onRightClickAir($event); - } catch (Exception $error) { + }catch(Exception $error){ $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . "Interaction failed!"); $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); } @@ -130,19 +129,20 @@ public function onItemRightClick(PlayerItemUseEvent $event): void /** * @param BlockBreakEvent $event + * * @throws AssumptionFailedError * @throws Error * @throws UnexpectedTagTypeException */ - public function onBreak(BlockBreakEvent $event): void - { - if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE)) || !is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))) { + public function onBreak(BlockBreakEvent $event) : void{ + if(!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE)) || !is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))){ $event->cancel(); - try { + try{ $this->onBreakBlock($event); - } catch (Exception $error) { + }catch(Exception $error){ $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . "Interaction failed!"); $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + Loader::getInstance()->getLogger()->logException($error); } } } @@ -167,37 +167,43 @@ public function onBreak(BlockBreakEvent $event): void /** * TODO use tool classes + * * @param BlockBreakEvent $event + * * @throws AssumptionFailedError * @throws Error - * @throws InvalidArgumentException * @throws SessionException */ - private function onBreakBlock(BlockBreakEvent $event): void - { + private function onBreakBlock(BlockBreakEvent $event) : void{ $session = SessionHelper::getUserSession($event->getPlayer()); - if (!$session instanceof UserSession) return; - switch ($event->getItem()->getId()) { + if(!$session instanceof UserSession) return; + $item = $event->getItem(); + switch($item->getId()){ case ItemIds::WOODEN_AXE: { - if (!$session->isWandEnabled()) { + if(!$session->isWandEnabled()){ $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.wand.disabled")); break; } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $event->getBlock()->getPosition()->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $event->getBlock()->getPosition()->getWorld()))); // TODO check if the selection inside of the session updates } - $selection->setPos1(new Position($event->getBlock()->getPosition()->x, $event->getBlock()->getPosition()->y, $event->getBlock()->getPosition()->z, $event->getBlock()->getPosition()->getWorld())); + $selection->setPos1($event->getBlock()->getPosition()); break; } case ItemIds::STICK: { - if (!$session->isDebugToolEnabled()) { + if(!$session->isDebugToolEnabled()){ $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.debug.disabled")); break; } - $event->getPlayer()->sendMessage($event->getBlock()->__toString() . ', variant: ' . $event->getBlock()->getIdInfo()->getVariant()); + if($item instanceof Stick){ + $event->cancel(); + $session = SessionHelper::getUserSession($event->getPlayer()); + if(!$session instanceof UserSession) return; + $session->debug ??= Debug::fromItem($item); + $session->debug->usePrimary($session, $event->getBlock()); + } break; } } @@ -205,7 +211,9 @@ private function onBreakBlock(BlockBreakEvent $event): void /** * TODO use tool classes + * * @param PlayerInteractEvent $event + * * @throws AssumptionFailedError * @throws Error * @throws InvalidArgumentException @@ -213,52 +221,60 @@ private function onBreakBlock(BlockBreakEvent $event): void * @throws UnexpectedTagTypeException * @throws LogicException */ - private function onRightClickBlock(PlayerInteractEvent $event): void - { - if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE)) || !is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET))) { + private function onRightClickBlock(PlayerInteractEvent $event) : void{ + //TODO cleanup this method + $item = $event->getItem(); + if(!is_null($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE)) || !is_null($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET)) || !is_null($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_DEBUG))){ $event->cancel(); $session = SessionHelper::getUserSession($event->getPlayer()); - if (!$session instanceof UserSession) return; - switch ($event->getItem()->getId()) { + if(!$session instanceof UserSession) return; + switch($item->getId()){ case ItemIds::WOODEN_AXE: { - if (!$session->isWandEnabled()) { + if(!$session->isWandEnabled()){ $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.wand.disabled")); break; } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $event->getBlock()->getPosition()->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $event->getBlock()->getPosition()->getWorld()))); // TODO check if the selection inside of the session updates } - $selection->setPos2(new Position($event->getBlock()->getPosition()->x, $event->getBlock()->getPosition()->y, $event->getBlock()->getPosition()->z, $event->getBlock()->getPosition()->getWorld())); + $selection->setPos2($event->getBlock()->getPosition()); break; } case ItemIds::STICK: { - if (!$session->isDebugToolEnabled()) { + if(!$session->isDebugToolEnabled()){ $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.debug.disabled")); break; } - $event->getPlayer()->sendMessage($event->getBlock()->__toString() . ', variant: ' . $event->getBlock()->getIdInfo()->getVariant()); + if($item instanceof Stick){ + $event->cancel(); + $session = SessionHelper::getUserSession($event->getPlayer()); + + if(!$session instanceof UserSession) return; + $session->debug ??= Debug::fromItem($item); + $session->debug->useSecondary($session, $event->getBlock()); + #var_dump($session->debug->toItem($session->getLanguage())->getNamedTag()); + } break; } case ItemIds::BUCKET: { #if (){// && has perms - API::floodArea($event->getBlock()->getSide($event->getFace()), $event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE), $session); + API::floodArea($event->getBlock()->getSide($event->getFace()), $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE), $session); #} break; } case ItemIds::SCAFFOLDING: { - $tag = $event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET); - if ($tag !== null) { + $tag = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET); + if($tag !== null){ $filename = $tag->getString('filename'); $asset = Loader::$assetCollection->assets[$filename];//TODO allow private assets again $target = $event->getBlock()->getSide($event->getFace())->getPosition(); - if (API::placeAsset($target, $asset, $tag, $session)) { + if(API::placeAsset($target, $asset, $tag, $session)){ $event->getPlayer()->sendMessage("Asset placed!"); - } else { + }else{ $event->getPlayer()->sendMessage("Asset not placed!"); } } @@ -266,7 +282,7 @@ private function onRightClickBlock(PlayerInteractEvent $event): void } default: { - var_dump($event->getItem()); + var_dump($item); $event->cancel(); break; } @@ -276,6 +292,7 @@ private function onRightClickBlock(PlayerInteractEvent $event): void /** * @param PlayerInteractEvent $event + * * @throws AssumptionFailedError * @throws Error * @throws InvalidArgumentException @@ -283,29 +300,27 @@ private function onRightClickBlock(PlayerInteractEvent $event): void * @throws UnexpectedTagTypeException * @throws LogicException */ - private function onLeftClickBlock(PlayerInteractEvent $event): void - { - if (!is_null($event->getItem()->getNamedTag()->getTag(API::TAG_MAGIC_WE))) { + private function onLeftClickBlock(PlayerInteractEvent $event) : void{ + if(!is_null($event->getItem()->getNamedTag()->getTag(API::TAG_MAGIC_WE))){ $event->cancel(); $session = SessionHelper::getUserSession($event->getPlayer()); - if (!$session instanceof UserSession) return; - switch ($event->getItem()->getId()) { + if(!$session instanceof UserSession) return; + switch($event->getItem()->getId()){ case ItemIds::WOODEN_AXE: { - if (!$session->isWandEnabled()) { + if(!$session->isWandEnabled()){ $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.wand.disabled")); break; } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $event->getBlock()->getPosition()->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $event->getBlock()->getPosition()->getWorld()))); // TODO check if the selection inside of the session updates } - $selection->setPos1(new Position($event->getBlock()->getPosition()->x, $event->getBlock()->getPosition()->y, $event->getBlock()->getPosition()->z, $event->getBlock()->getPosition()->getWorld())); + $selection->setPos1($event->getBlock()->getPosition()); break; } case ItemIds::STICK: { - if (!$session->isDebugToolEnabled()) { + if(!$session->isDebugToolEnabled()){ $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.debug.disabled")); break; } @@ -325,6 +340,7 @@ private function onLeftClickBlock(PlayerInteractEvent $event): void /** * @param PlayerItemUseEvent $event + * * @throws AssumptionFailedError * @throws InvalidArgumentException * @throws SessionException @@ -332,16 +348,15 @@ private function onLeftClickBlock(PlayerInteractEvent $event): void * @throws RuntimeException * @throws Exception */ - private function onRightClickAir(PlayerItemUseEvent $event): void - { - if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))) { + private function onRightClickAir(PlayerItemUseEvent $event) : void{ + if(!is_null(($item = $event->getItem())->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))){ $event->cancel(); $session = SessionHelper::getUserSession($event->getPlayer()); - if (!$session instanceof UserSession) return; + if(!$session instanceof UserSession) return; $target = $event->getPlayer()->getTargetBlock(Loader::getInstance()->getToolDistance()); $brush = $session->getBrushes()->getBrushFromItem($event->getItem()); var_dump(json_encode($brush, JSON_THROW_ON_ERROR)); - if ($brush instanceof Brush && !is_null($target)) {// && has perms + if($brush instanceof Brush && !is_null($target)){// && has perms API::createBrush($target, $brush, $session); } } @@ -350,16 +365,15 @@ private function onRightClickAir(PlayerItemUseEvent $event): void /** * @param PlayerDropItemEvent $event */ - public function onDropItem(PlayerDropItemEvent $event): void - { - try { - if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))) { + public function onDropItem(PlayerDropItemEvent $event) : void{ + try{ + if(!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))){ $event->cancel(); $session = SessionHelper::getUserSession($event->getPlayer()); - if (!$session instanceof UserSession) return; + if(!$session instanceof UserSession) return; $brush = $session->getBrushes()->getBrushFromItem($event->getItem()); - if ($brush instanceof Brush) { - $form = (new ModalForm(function (Player $player, $data) use ($session, $brush) { + if($brush instanceof Brush){ + $form = (new ModalForm(function(Player $player, $data) use ($session, $brush){ $session->getBrushes()->removeBrush($brush, $data); })) ->setTitle(TF::BOLD . $brush->getName()) @@ -370,31 +384,31 @@ public function onDropItem(PlayerDropItemEvent $event): void ->setButton2(TF::BOLD . TF::DARK_GREEN . "Remove"); $event->getPlayer()->sendForm($form); } - } else if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE))) { + }else if(!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE))){ $event->cancel(); $event->getPlayer()->getInventory()->remove($event->getItem()); } - } catch (Exception) { + }catch(Exception){ } } - public function onSelectionChange(MWESelectionChangeEvent $event): void - { + public function onSelectionChange(MWESelectionChangeEvent $event) : void{ #Loader::getInstance()->getLogger()->debug("Called " . $event->getEventName()); $session = $event->getSession(); - if ($session instanceof UserSession && $event->getPlayer() !== null) { + if($session instanceof UserSession && $event->getPlayer() !== null){ /** @var UserSession $session */ - if ($session->isOutlineEnabled()) $session->createOrUpdateOutline($event->getSelection()); + $session->setOutlineEnabled($session->isOutlineEnabled()); + //if ($session->isOutlineEnabled()) $session->createOrUpdateOutline($event->getSelection()); $session->sidebar->handleScoreboard($session); } } /** * TODO use tool classes + * * @param PlayerItemHeldEvent $event */ - public function onChangeSlot(PlayerItemHeldEvent $event): void - { + public function onChangeSlot(PlayerItemHeldEvent $event) : void{ /*var_dump($event->getSlot()); $player = $event->getPlayer(); #$item = $player->getInventory()->getItemInHand(); @@ -428,14 +442,13 @@ public function onChangeSlot(PlayerItemHeldEvent $event): void }*/ } - public function onStructureBlockClick(PlayerInteractEvent $event): void - { + public function onStructureBlockClick(PlayerInteractEvent $event) : void{ //$player = $event->getPlayer(); $blockTouched = $event->getBlock(); - if ($blockTouched->getId() === BlockLegacyIds::STRUCTURE_BLOCK) { - var_dump("Clicked Structure Block", (string)$blockTouched); + if($blockTouched->getId() === BlockLegacyIds::STRUCTURE_BLOCK){ + var_dump("Clicked Structure Block", (string) $blockTouched); $tile = $blockTouched->getPosition()->getWorld()->getTile($blockTouched->getPosition()->asVector3()); - if ($tile instanceof StructureBlockTile) { + if($tile instanceof StructureBlockTile){ var_dump("Is Structure Block Tile", $tile->getSpawnCompound()->toString()); // $item = $player->getInventory()->getItemInHand(); // if (!is_null(($tag = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET)))) { diff --git a/src/xenialdan/MagicWE2/Loader.php b/src/xenialdan/MagicWE2/Loader.php index bfee7240..ad5e5fcc 100644 --- a/src/xenialdan/MagicWE2/Loader.php +++ b/src/xenialdan/MagicWE2/Loader.php @@ -8,10 +8,10 @@ use jackmd\scorefactory\ScoreFactory; use jojoe77777\FormAPI\FormAPI; use JsonException; +use JsonSchema\Exception\ResourceNotFoundException; use muqsit\invmenu\InvMenuHandler; use pocketmine\block\Block; use pocketmine\data\bedrock\EnchantmentIdMap; -use pocketmine\entity\InvalidSkinException; use pocketmine\item\enchantment\Enchantment; use pocketmine\item\enchantment\ItemFlags; use pocketmine\lang\Language; @@ -23,10 +23,6 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; use RuntimeException; -use wfcore\lib\exception\GeometryNotFoundException; -use wfcore\lib\exception\GeometryParsingException; -use wfcore\lib\exception\SplitIDException; -use wfcore\lib\exception\TextureNotFoundException; use xenialdan\apibossbar\DiverseBossBar; use xenialdan\libblockstate\BlockStatesParser; use xenialdan\libstructure\PacketListener; @@ -39,6 +35,7 @@ use xenialdan\MagicWE2\commands\clipboard\Cut2Command; use xenialdan\MagicWE2\commands\clipboard\CutCommand; use xenialdan\MagicWE2\commands\clipboard\PasteCommand; +use xenialdan\MagicWE2\commands\clipboard\RotateCommand; use xenialdan\MagicWE2\commands\debug\GenerateCommandsMDCommand; use xenialdan\MagicWE2\commands\debug\PlaceAllBlockstatesCommand; use xenialdan\MagicWE2\commands\debug\TestAPICommand; @@ -83,45 +80,32 @@ use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\task\action\ActionRegistry; -class Loader extends PluginBase -{ +class Loader extends PluginBase{ public const FAKE_ENCH_ID = 201; public const PREFIX = TF::RESET . TF::BOLD . TF::GOLD . "[MagicWE2]" . TF::RESET . " "; public const PREFIX_ASSETS = TF::RESET . TF::BOLD . TF::GOLD . "[Asset]" . TF::RESET . " "; public const PREFIX_BRUSH = TF::RESET . TF::BOLD . TF::GOLD . "[Brush]" . TF::RESET . " "; public const PREFIX_PALETTE = TF::RESET . TF::BOLD . TF::GOLD . "[Palette]" . TF::RESET . " "; public const PREFIX_FORM = TF::RESET . TF::BOLD . TF::DARK_PURPLE . "[MWE2]" . TF::RESET . " "; - /** @var Loader|null */ private static ?Loader $instance; - /** @var null|ShapeRegistry */ public static ?ShapeRegistry $shapeRegistry; - /** @var null|ActionRegistry */ public static ?ActionRegistry $actionRegistry; - /** @var Enchantment */ public static Enchantment $ench; - /** @var Language */ private Language $baseLang; /** @var string[] Donator names */ public array $donators = []; - /** @var string */ public string $donatorData = ""; - /** @var string */ - private static string $rotPath; - /** @var string */ - private static string $doorRotPath; - /** @var DiverseBossBar */#BossBar + #BossBar public DiverseBossBar $wailaBossBar; - /** @var null|string */ public static ?string $scoreboardAPI; - /** @var AssetCollection */ public static AssetCollection $assetCollection; + private array $possibleBlockstates = []; /** * Returns an instance of the plugin * @return Loader */ - public static function getInstance(): Loader - { + public static function getInstance() : Loader{ return self::$instance; } @@ -130,48 +114,26 @@ public static function getInstance(): Loader * @return ShapeRegistry * @throws ShapeRegistryException */ - public static function getShapeRegistry(): ShapeRegistry - { - if (self::$shapeRegistry) return self::$shapeRegistry; + public static function getShapeRegistry() : ShapeRegistry{ + if(self::$shapeRegistry) return self::$shapeRegistry; throw new ShapeRegistryException("Shape registry is not initialized"); } - public static function getRotFlipPath(): string - { - return self::$rotPath; - #return self::getInstance()->getFile() . "resources" . DIRECTORY_SEPARATOR . "rotation_flip_data.json"; - } - - public static function getDoorRotFlipPath(): string - { - return self::$doorRotPath; - #return self::getInstance()->getFile() . "resources" . DIRECTORY_SEPARATOR . "door_data.json"; - } - - /** - * @return bool - */ - public static function hasScoreboard(): bool - { + public static function hasScoreboard() : bool{ return self::$scoreboardAPI !== null; } /** * @throws AssumptionFailedError - * @throws InvalidArgumentException * @throws JsonException * @throws PluginException * @throws RuntimeException - * @throws InvalidSkinException - * @throws GeometryNotFoundException - * @throws GeometryParsingException - * @throws SplitIDException - * @throws TextureNotFoundException + * @throws ResourceNotFoundException */ - public function onLoad(): void - { + public function onLoad() : void{ self::$instance = $this; self::$ench = new Enchantment("", 0, ItemFlags::AXE, ItemFlags::NONE, 1); + /** @var EnchantmentIdMap $enchantmapinstance */ $enchantmapinstance = EnchantmentIdMap::getInstance(); $enchantmapinstance->register(self::FAKE_ENCH_ID, self::$ench); self::$shapeRegistry = new ShapeRegistry(); @@ -180,20 +142,20 @@ public function onLoad(): void #$this->saveResource("rotation_flip_data.json", true); $this->saveResource("blockstate_alias_map.json", true); - self::$rotPath = $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "rotation_flip_data.json"; - self::$doorRotPath = $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "door_data.json"; + API::setRotationData(API::decodeJsonResource("rotation_flip_data.json")); + #$this->doorRotationData = API::decodeJsonResource("door_data.json"); /** @var BlockStatesParser $blockstateparserInstance */ $blockstateparserInstance = BlockStatesParser::getInstance(); - #$blockstateparserInstance::$rotPath = $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "rotation_flip_data.json"; - #$blockstateparserInstance::$doorRotPath = $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "door_data.json"; $fileGetContents = file_get_contents($this->getDataFolder() . "blockstate_alias_map.json"); - if ($fileGetContents === false) { + if($fileGetContents === false){ throw new PluginException("blockstate_alias_map.json could not be loaded! Blockstate support has been disabled!"); } $blockstateparserInstance->setAliasMap(json_decode($fileGetContents, true, 512, JSON_THROW_ON_ERROR)); - if ($this->getConfig()->get("developer-extended-debug", false)) $blockstateparserInstance->runTest(); + $this->possibleBlockstates = API::decodeJsonResource("possible_blockstates.json"); + + if($this->getConfig()->get("developer-extended-debug", false)) $blockstateparserInstance->runTest(); self::$assetCollection = new AssetCollection(new PluginSession($this)); } @@ -203,9 +165,8 @@ public function onLoad(): void * @return ActionRegistry * @throws ActionRegistryException */ - public static function getActionRegistry(): ActionRegistry - { - if (self::$actionRegistry) return self::$actionRegistry; + public static function getActionRegistry() : ActionRegistry{ + if(self::$actionRegistry) return self::$actionRegistry; throw new ActionRegistryException("Action registry is not initialized"); } @@ -215,13 +176,12 @@ public static function getActionRegistry(): ActionRegistry * @throws LanguageNotFoundException * @throws RuntimeException */ - public function onEnable(): void - { + public function onEnable() : void{ $lang = $this->getConfig()->get("language", Language::FALLBACK_LANGUAGE); - $this->baseLang = new Language((string)$lang, $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR); + $this->baseLang = new Language((string) $lang, $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR); $registerDeveloperCommands = $this->getConfig()->get("developer-commands", false); - if (!InvMenuHandler::isRegistered()) InvMenuHandler::register($this); - if (!PacketListener::isRegistered()) PacketListener::register($this); + if(!InvMenuHandler::isRegistered()) InvMenuHandler::register($this); + if(!PacketListener::isRegistered()) PacketListener::register($this); //PacketListener::register($this);//TODO currently this just doubles the debug spam $this->getServer()->getPluginManager()->registerEvents(new EventListener($this), $this); $this->getServer()->getCommandMap()->registerAll("MagicWE2", [ @@ -276,7 +236,7 @@ public function onEnable(): void new ClearClipboardCommand($this, "/clearclipboard", "Clear your clipboard"), //TODO re-add flip/rotate in libblockstate #new FlipCommand($this, "/flip", "Flip the contents of the clipboard across the origin", ["/mirror"]), - #new RotateCommand($this, "/rotate", "Rotate the contents of the clipboard around the origin"), + new RotateCommand($this, "/rotate", "Rotate the contents of the clipboard around the origin"), /* -- history -- */ new UndoCommand($this, "/undo", "Rolls back the last action"), new RedoCommand($this, "/redo", "Applies the last undo action again"), @@ -318,13 +278,13 @@ public function onEnable(): void new ToggleSidebarCommand($this, "/togglesidebar", "Toggle the sidebar", ["/sidebar"]), new ToggleOutlineCommand($this, "/toggleoutline", "Toggle the selection outline", ["/outline", "/showbounds"]), ]); - if ($registerDeveloperCommands) $this->getServer()->getCommandMap()->registerAll("MagicWE2", [ + if($registerDeveloperCommands) $this->getServer()->getCommandMap()->registerAll("MagicWE2", [ /* -- developer commands -- */ new PlaceAllBlockstatesCommand($this, "/placeallblockstates", "Place all blockstates similar to Java debug worlds"), new TestAPICommand($this, "/testapi", "Internal command for testing API methods"), new GenerateCommandsMDCommand($this, "/generatecommandsmd", "Generates the commands.md file"), ]); - if (class_exists(FormAPI::class)) { + if(class_exists(FormAPI::class)){ $this->getLogger()->notice("FormAPI found, can use ui-based commands"); $this->getServer()->getCommandMap()->registerAll("MagicWE2", [ /* -- assets -- */ @@ -336,13 +296,13 @@ public function onEnable(): void /* -- tool -- */ new FloodCommand($this, "/flood", "Opens the flood fill tool menu", ["/floodfill"]), ]); - } else { + }else{ $this->getLogger()->notice(TF::RED . "FormAPI NOT found, can NOT use ui-based commands"); } - if (class_exists(ScoreFactory::class)) { + if(class_exists(ScoreFactory::class)){ $this->getLogger()->notice("Scoreboard API found, can use scoreboards"); self::$scoreboardAPI = ScoreFactory::class; - } else { + }else{ $this->getLogger()->notice(TF::RED . "Scoreboard API NOT found, can NOT use scoreboards"); } @@ -350,38 +310,37 @@ public function onEnable(): void $this->wailaBossBar = (new DiverseBossBar())->setPercentage(1.0)/*->setColor(BarColor::RED)*/ ; //WAILA updater - $this->getScheduler()->scheduleDelayedRepeatingTask(new ClosureTask(function (): void { + $this->getScheduler()->scheduleDelayedRepeatingTask(new ClosureTask(function() : void{ /** @var BlockStatesParser $blockStatesParser */ $blockStatesParser = BlockStatesParser::getInstance(); $players = Loader::getInstance()->wailaBossBar->getPlayers(); - foreach ($players as $player) { - if (!$player->isOnline() || !SessionHelper::hasSession($player) || (($session = SessionHelper::getUserSession($player)) instanceof UserSession && !$session->isWailaEnabled())) { + foreach($players as $player){ + if(!$player->isOnline() || !SessionHelper::hasSession($player) || (($session = SessionHelper::getUserSession($player)) instanceof UserSession && !$session->isWailaEnabled())){ Loader::getInstance()->wailaBossBar->hideFrom([$player]); continue; } - if (($block = $player->getTargetBlock(10)) instanceof Block && $block->getId() !== 0) { + if(($block = $player->getTargetBlock(10)) instanceof Block && $block->getId() !== 0){ $stateEntry = $blockStatesParser->get($block->getId(), $block->getMeta()); - $title = (string)$block; + $title = (string) $block; $sub = implode("," . TF::EOL, explode(",", $blockStatesParser->prettyPrintStates($stateEntry, false))); $distancePercentage = round(floor($block->getPosition()->distance($player->getEyePos())) / 10, 1); Loader::getInstance()->wailaBossBar->showTo([$player]); Loader::getInstance()->wailaBossBar->setTitleFor([$player], $title)->setSubTitleFor([$player], $sub)->setPercentage($distancePercentage); - } else + }else Loader::getInstance()->wailaBossBar->hideFrom([$player]); } }), 60, 1); } - public function onDisable(): void - { - try { - foreach (SessionHelper::getPluginSessions() as $session) { + public function onDisable() : void{ + try{ + foreach(SessionHelper::getPluginSessions() as $session){ SessionHelper::destroySession($session, false); } - foreach (SessionHelper::getUserSessions() as $session) { + foreach(SessionHelper::getUserSessions() as $session){ SessionHelper::destroySession($session); } - } catch (JsonException $e) { + }catch(JsonException $e){ $this->getLogger()->logException($e); } } @@ -390,27 +349,23 @@ public function onDisable(): void * @return Language * @api */ - public function getLanguage(): Language - { + public function getLanguage() : Language{ return $this->baseLang; } - public function getToolDistance(): int - { - return (int)$this->getConfig()->get("tool-range", 100); + public function getToolDistance() : int{ + return (int) $this->getConfig()->get("tool-range", 100); } - public function getEditLimit(): int - { - return (int)$this->getConfig()->get("limit", -1); + public function getEditLimit() : int{ + return (int) $this->getConfig()->get("limit", -1); } /** * @return array * @throws RuntimeException */ - public static function getInfo(): array - { + public static function getInfo() : array{ return [ "| " . TF::GREEN . self::getInstance()->getFullName() . TF::RESET . " | Information |", "| --- | --- |", @@ -433,8 +388,7 @@ public static function getInfo(): array * * @return string */ - public function getLanguageFolder(): string - { + public function getLanguageFolder() : string{ return $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR; } @@ -444,8 +398,12 @@ public function getLanguageFolder(): string * @phpstan-return array code=>name * @throws LanguageNotFoundException */ - public function getLanguageList(): array - { + public function getLanguageList() : array{ return Language::getLanguageList($this->getLanguageFolder()); } + + //TODO move to API? + public function getPossibleBlockstates(string $state) : array{ + return $this->possibleBlockstates[$state] ?? []; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/clipboard/Clipboard.php b/src/xenialdan/MagicWE2/clipboard/Clipboard.php index a2a77a35..54b351c9 100644 --- a/src/xenialdan/MagicWE2/clipboard/Clipboard.php +++ b/src/xenialdan/MagicWE2/clipboard/Clipboard.php @@ -6,11 +6,9 @@ use Exception; use pocketmine\Server; -use pocketmine\world\format\Chunk; use pocketmine\world\World; use Serializable; use xenialdan\MagicWE2\exception\SelectionException; -use xenialdan\MagicWE2\helper\AsyncChunkManager; abstract class Clipboard implements Serializable { @@ -29,21 +27,6 @@ abstract class Clipboard implements Serializable public ?int $worldId = null; public string $customName = ""; - /** - * Creates a chunk manager used for async editing - * @param Chunk[] $chunks - * @return AsyncChunkManager - */ - public static function getChunkManager(array $chunks): AsyncChunkManager - { - $manager = new AsyncChunkManager(World::Y_MIN, World::Y_MAX); - foreach ($chunks as $hash => $chunk) { - World::getXZ($hash, $x, $z); - $manager->setChunk($x, $z, $chunk); - } - return $manager; - } - /** * @return World * @throws Exception diff --git a/src/xenialdan/MagicWE2/commands/HelpCommand.php b/src/xenialdan/MagicWE2/commands/HelpCommand.php index bba24068..34fd0998 100644 --- a/src/xenialdan/MagicWE2/commands/HelpCommand.php +++ b/src/xenialdan/MagicWE2/commands/HelpCommand.php @@ -67,7 +67,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo } $message .= TF::RESET . TF::LIGHT_PURPLE . " [" . implode(",", $aliases) . "]"; } - $message .= TF::RESET . TF::WHITE . " " . ($command->getDescription() instanceof Translatable ? $lang->translate($command->getDescription()) : $command->getDescription()) . TF::EOL . " ยป " . ($command->getDescription() instanceof Translatable ? $lang->translate($command->getDescription()) : $command->getDescription()); + $message .= TF::RESET . TF::WHITE . " " . ($command->getDescription() instanceof Translatable ? $lang->translate($command->getDescription()) : $command->getDescription()) . TF::EOL . " - " . ($command->getUsage() instanceof Translatable ? $lang->translate($command->getUsage()) : $command->getUsage()); $sender->sendMessage($message); } } catch (Exception $error) { diff --git a/src/xenialdan/MagicWE2/commands/args/BlocksArgument.php b/src/xenialdan/MagicWE2/commands/args/BlocksArgument.php index 8a014e6e..1594b578 100644 --- a/src/xenialdan/MagicWE2/commands/args/BlocksArgument.php +++ b/src/xenialdan/MagicWE2/commands/args/BlocksArgument.php @@ -34,19 +34,21 @@ public function canParse(string $testString, CommandSender $sender) : bool{ } /** - * @param string $argument + * @param string $argument * @param CommandSender $sender * * @return BlockPalette * @throws SessionException */ - public function parse(string $argument, CommandSender $sender): BlockPalette - { - try { + public function parse(string $argument, CommandSender $sender) : BlockPalette{ + try{ return BlockPalette::fromString($argument); - } catch (BlockQueryAlreadyParsedException | InvalidArgumentExceptionAlias $error) { - if ($sender instanceof Player) - SessionHelper::getUserSession($sender)->sendMessage('error.command-error'); + }catch(BlockQueryAlreadyParsedException | InvalidArgumentExceptionAlias $error){ + if($sender instanceof Player){ + SessionHelper::getUserSession($sender)?->sendMessage('error.command-error'); + }else{ + $sender->sendMessage(Loader::getInstance()->getLanguage()->translateString('error.command-error')); + } $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); } return BlockPalette::CREATE(); diff --git a/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php index ac07d92e..d02379d2 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php @@ -66,9 +66,10 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo throw new SessionException($lang->translateString('error.noclipboard')); } $action = new RotateAction($angle/*, $aroundOrigin*/);//TODO reenable origin support if error fixed: does not rotate. Let's see if PHPStan find it for me! + #$action->rotationData = API::$rotationData; #$offset = $selection->getShape()->getMinVec3()->subtract($session->getPlayer()->asVector3()->floor())->floor(); #$action->setClipboardVector($offset); - var_dump($action); + #var_dump($action); Server::getInstance()->getAsyncPool()->submitTask( new AsyncClipboardActionTask( $session->getUUID(), diff --git a/src/xenialdan/MagicWE2/commands/debug/TestAPICommand.php b/src/xenialdan/MagicWE2/commands/debug/TestAPICommand.php index 72556429..21f1aff4 100644 --- a/src/xenialdan/MagicWE2/commands/debug/TestAPICommand.php +++ b/src/xenialdan/MagicWE2/commands/debug/TestAPICommand.php @@ -8,6 +8,7 @@ use Exception; use InvalidArgumentException; use pocketmine\command\CommandSender; +use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\Server; use pocketmine\utils\TextFormat as TF; @@ -46,7 +47,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo try { //TODO REMOVE DEBUG $pluginSession = SessionHelper::createPluginSession(Loader::getInstance()); - $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 0, 0, 0); + $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), new Vector3(0, 0, 0), new Vector3(0, 0, 0)); $pluginSession->addSelection($selection); Server::getInstance()->getAsyncPool()->submitTask( new AsyncActionTask( @@ -57,7 +58,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo BlockPalette::fromString("minecraft:tnt") ) ); - $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 1, 1, 1); + $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), new Vector3(0, 0, 0), new Vector3(1, 1, 1)); Server::getInstance()->getAsyncPool()->submitTask( new AsyncActionTask( $pluginSession->getUUID(), @@ -67,7 +68,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo BlockPalette::fromString("minecraft:tnt") ) ); - $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 2, 2, 2); + $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), new Vector3(0, 0, 0), new Vector3(2, 2, 2)); Server::getInstance()->getAsyncPool()->submitTask( new AsyncActionTask( $pluginSession->getUUID(), @@ -77,7 +78,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo BlockPalette::fromString("minecraft:tnt") ) ); - $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 1, 2, 3); + $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), new Vector3(0, 0, 0), new Vector3(1, 2, 3)); Server::getInstance()->getAsyncPool()->submitTask( new AsyncActionTask( $pluginSession->getUUID(), diff --git a/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php b/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php index e1071384..bd427886 100644 --- a/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php +++ b/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php @@ -19,16 +19,15 @@ use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; +use function is_null; -class ChunkCommand extends BaseCommand -{ +class ChunkCommand extends BaseCommand{ /** * This is where all the arguments, permissions, sub-commands, etc would be registered * @throws InvalidArgumentException */ - protected function prepare(): void - { + protected function prepare() : void{ $this->setPermission("we.command.selection.chunk"); } @@ -49,22 +48,21 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo return; } /** @var Player $sender */ - try { + try{ $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + if(!$session instanceof UserSession){ throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $sender->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $sender->getWorld()))); // TODO check if the selection inside of the session updates } $chunk = $sender->getWorld()->getOrLoadChunkAtPosition($sender->getPosition()); - if (is_null($chunk)) { + if(is_null($chunk)){ throw new Error("Could not find a chunk at your position"); } $x = $sender->getPosition()->x >> 4; $z = $sender->getPosition()->x >> 4; - $selection->setPos1(Position::fromObject(new Vector3($x * 16, 0, $z * 16), $sender->getWorld())); + $selection->setPos1(Position::fromObject(new Vector3($x * 16, World::Y_MIN, $z * 16), $sender->getWorld())); $selection->setPos2(Position::fromObject(new Vector3($x * 16 + 15, World::Y_MAX, $z * 16 + 15), $sender->getWorld())); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); diff --git a/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php b/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php index 2b66e41b..d3faa61e 100644 --- a/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php @@ -17,15 +17,13 @@ use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; -class HPos1Command extends BaseCommand -{ +class HPos1Command extends BaseCommand{ /** * This is where all the arguments, permissions, sub-commands, etc would be registered * @throws InvalidArgumentException */ - protected function prepare(): void - { + protected function prepare() : void{ $this->setPermission("we.command.selection.hpos"); } @@ -46,17 +44,16 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo return; } /** @var Player $sender */ - try { + try{ $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + if(!$session instanceof UserSession){ throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $sender->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $sender->getWorld()))); // TODO check if the selection inside of the session updates } $target = $sender->getTargetBlock(Loader::getInstance()->getToolDistance()); - if ($target === null) { + if($target === null){ $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.notarget')); return; } diff --git a/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php b/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php index 6bf1da45..9a243c79 100644 --- a/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php @@ -17,15 +17,13 @@ use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; -class HPos2Command extends BaseCommand -{ +class HPos2Command extends BaseCommand{ /** * This is where all the arguments, permissions, sub-commands, etc would be registered * @throws InvalidArgumentException */ - protected function prepare(): void - { + protected function prepare() : void{ $this->setPermission("we.command.selection.hpos"); } @@ -46,17 +44,16 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo return; } /** @var Player $sender */ - try { + try{ $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + if(!$session instanceof UserSession){ throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $sender->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $sender->getWorld()))); // TODO check if the selection inside of the session updates } $target = $sender->getTargetBlock(Loader::getInstance()->getToolDistance()); - if ($target === null) { + if($target === null){ $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.notarget')); return; } diff --git a/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php b/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php index 61d5d8e6..926c0f02 100644 --- a/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php @@ -17,15 +17,13 @@ use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; -class Pos1Command extends BaseCommand -{ +class Pos1Command extends BaseCommand{ /** * This is where all the arguments, permissions, sub-commands, etc would be registered * @throws InvalidArgumentException */ - protected function prepare(): void - { + protected function prepare() : void{ $this->setPermission("we.command.selection.pos"); } @@ -46,17 +44,17 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo return; } /** @var Player $sender */ - try { + try{ $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + if(!$session instanceof UserSession){ throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $sender->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $sender->getWorld()))); // TODO check if the selection inside of the session updates } $selection->setPos1($sender->getPosition()); } catch (Exception $error) { + Loader::getInstance()->getLogger()->logException($error); $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); $sender->sendMessage($this->getUsage()); diff --git a/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php b/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php index 5288892f..de700476 100644 --- a/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php @@ -17,15 +17,13 @@ use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; -class Pos2Command extends BaseCommand -{ +class Pos2Command extends BaseCommand{ /** * This is where all the arguments, permissions, sub-commands, etc would be registered * @throws InvalidArgumentException */ - protected function prepare(): void - { + protected function prepare() : void{ $this->setPermission("we.command.selection.pos"); } @@ -46,17 +44,17 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo return; } /** @var Player $sender */ - try { + try{ $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + if(!$session instanceof UserSession){ throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $sender->getWorld())); // TODO check if the selection inside of the session updates - if (is_null($selection)) { - throw new Error("No selection created - Check the console for errors"); + if(($selection = $session->getLatestSelection()) === null){ + $session->addSelection(($selection = new Selection($session->getUUID(), $sender->getWorld()))); // TODO check if the selection inside of the session updates } $selection->setPos2($sender->getPosition()); } catch (Exception $error) { + Loader::getInstance()->getLogger()->logException($error); $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); $sender->sendMessage($this->getUsage()); diff --git a/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php b/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php index c7286e16..30723cf3 100644 --- a/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php +++ b/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php @@ -9,61 +9,48 @@ use Exception; use InvalidArgumentException; use pocketmine\command\CommandSender; -use pocketmine\item\enchantment\EnchantmentInstance; -use pocketmine\item\VanillaItems; -use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; -use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; +use xenialdan\MagicWE2\tool\Debug; -class DebugCommand extends BaseCommand -{ +class DebugCommand extends BaseCommand{ /** * This is where all the arguments, permissions, sub-commands, etc would be registered * @throws InvalidArgumentException */ - protected function prepare(): void - { + protected function prepare() : void{ $this->setPermission("we.command.tool.debug"); } /** * @inheritDoc */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { + public function onRun(CommandSender $sender, string $aliasUsed, array $args) : void{ $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { + if($sender instanceof Player && SessionHelper::hasSession($sender)){ + try{ $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException) { + }catch(SessionException){ } } - if (!$sender instanceof Player) { + if(!$sender instanceof Player){ $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); return; } /** @var Player $sender */ - try { - $item = VanillaItems::STICK() - ->addEnchantment(new EnchantmentInstance(Loader::$ench)) - ->setCustomName(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . $lang->translateString('tool.debug')) - ->setLore([ - TF::RESET . $lang->translateString('tool.debug.lore.1'), - TF::RESET . $lang->translateString('tool.debug.lore.2'), - TF::RESET . $lang->translateString('tool.debug.lore.3') - ]); - $item->getNamedTag()->setTag(API::TAG_MAGIC_WE, CompoundTag::create()); + try{ + $session = SessionHelper::getUserSession($sender); + $item = ($session->debug ?? (new Debug()))->toItem($lang); $sender->getInventory()->addItem($item); - } catch (Exception $error) { + }catch(Exception $error){ $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); $sender->sendMessage($this->getUsage()); - } catch (Error $error) { + }catch(Error $error){ Loader::getInstance()->getLogger()->logException($error); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); } diff --git a/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php b/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php index 3ee6b11f..1dbca6f1 100644 --- a/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php +++ b/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php @@ -34,33 +34,21 @@ public function __construct(Selection $selection, int $type) } } - /** - * @return Selection - */ public function getSelection(): Selection { return $this->selection; } - /** - * @param Selection $selection - */ public function setSelection(Selection $selection): void { $this->selection = $selection; } - /** - * @return null|Session - */ public function getSession(): ?Session { return $this->session; } - /** - * @return null|Player - */ public function getPlayer(): ?Player { if (($session = $this->getSession()) instanceof UserSession) @@ -69,9 +57,6 @@ public function getPlayer(): ?Player return null; } - /** - * @return int - */ public function getType(): int { return $this->type; diff --git a/src/xenialdan/MagicWE2/helper/ArrayUtils.php b/src/xenialdan/MagicWE2/helper/ArrayUtils.php new file mode 100644 index 00000000..92035cbd --- /dev/null +++ b/src/xenialdan/MagicWE2/helper/ArrayUtils.php @@ -0,0 +1,45 @@ +getBlockAt($x, $y, $z)->getFullId(); - } - - /** - * @return Chunk[] - */ - public function getChunks(): array - { - return $this->chunks; - } -} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/AsyncWorld.php b/src/xenialdan/MagicWE2/helper/AsyncWorld.php index 1cb44d88..55b16b69 100644 --- a/src/xenialdan/MagicWE2/helper/AsyncWorld.php +++ b/src/xenialdan/MagicWE2/helper/AsyncWorld.php @@ -5,45 +5,60 @@ namespace xenialdan\MagicWE2\helper; use pocketmine\world\format\Chunk; +use pocketmine\world\format\io\FastChunkSerializer; use pocketmine\world\SimpleChunkManager; use pocketmine\world\World; +use RuntimeException; +use Serializable; +use xenialdan\MagicWE2\clipboard\RevertClipboard; +use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\selection\Selection; +use function igbinary_serialize; +use function igbinary_unserialize; -class AsyncWorld extends SimpleChunkManager -{ +class AsyncWorld extends SimpleChunkManager implements Serializable{ // /** @var CompoundTag[] *///TODO maybe CacheableNbt // protected array $tiles = []; - public function __construct(Selection $selection) - { + public function __construct(){ parent::__construct(World::Y_MIN, World::Y_MAX); - $this->copyChunks($selection); + } + + public static function fromRevertClipboard(RevertClipboard $clipboard) : self{ + $world = new self(); + foreach($clipboard->chunks as $hash => $chunk){ + World::getXZ($hash, $x, $z); + $world->setChunk($x, $z, $clipboard->getWorld()->getChunk($x, $z)); + } + return $world; } /** * @return Chunk[] */ - public function getChunks(): array - { + public function getChunks() : array{ return $this->chunks; } - public function copyChunks(Selection $selection): void - { - if (!$selection->isValid()) return; + /** + * May not be called from async task + * @throws SelectionException|RuntimeException + */ + public function copyChunks(Selection $selection) : void{ +// if(!$selection->isValid()) return; $this->cleanChunks(); $shape = $selection->getShape(); $aabb = $shape->getAABB(); $world = $selection->getWorld(); - $maxX = $aabb->maxX >> 4; - $minX = $aabb->minX >> 4; - $maxZ = $aabb->maxZ >> 4; - $minZ = $aabb->minZ >> 4; - for ($x = $minX; $x <= $maxX; $x++) { - for ($z = $minZ; $z <= $maxZ; $z++) { + $maxX = $aabb->maxX >> Chunk::COORD_BIT_SIZE; + $minX = $aabb->minX >> Chunk::COORD_BIT_SIZE; + $maxZ = $aabb->maxZ >> Chunk::COORD_BIT_SIZE; + $minZ = $aabb->minZ >> Chunk::COORD_BIT_SIZE; + for($x = $minX; $x <= $maxX; $x++){ + for($z = $minZ; $z <= $maxZ; $z++){ $chunk = $world->getChunk($x, $z); - if ($chunk === null) { + if($chunk === null){ continue; } $this->setChunk($x, $z, $chunk); @@ -52,11 +67,28 @@ public function copyChunks(Selection $selection): void } } - public function getBlockFullIdAt(int $x, int $y, int $z): int - { - if ($this->isInWorld($x, $y, $z) && ($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null) { + public function getBlockFullIdAt(int $x, int $y, int $z) : int{ + if($this->isInWorld($x, $y, $z) && ($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null){ return $chunk->getFullBlock($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK); } return 0;//TODO idk } + + public function serialize(){ + $chunks = []; + foreach($this->getChunks() as $hash => $chunk){ + $chunks[$hash] = FastChunkSerializer::serializeTerrain($chunk); + } + return igbinary_serialize([$this->getMinY(), $this->getMaxY(), $chunks]); + } + + public function unserialize(string $data){ + [$minY, $maxY, $chunks] = igbinary_unserialize($data); + parent::__construct($minY, $maxY);//TODO test + foreach($chunks as $hash => $chunk){ + World::getXZ($hash, $x, $z); + $chunk = FastChunkSerializer::deserializeTerrain($chunk); + $this->setChunk($x, $z, $chunk); + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/Scoreboard.php b/src/xenialdan/MagicWE2/helper/Scoreboard.php index 1f18bea5..1285d19b 100644 --- a/src/xenialdan/MagicWE2/helper/Scoreboard.php +++ b/src/xenialdan/MagicWE2/helper/Scoreboard.php @@ -11,8 +11,10 @@ use pocketmine\utils\TextFormat as TF; use ReflectionClass; use ReflectionException; +use RuntimeException; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; @@ -24,17 +26,20 @@ public function handleScoreboard(UserSession $session) : void{ try{ ScoreFactory::setObjective($player, Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . "Sidebar", SetDisplayObjectivePacket::SORT_ORDER_ASCENDING, SetDisplayObjectivePacket::DISPLAY_SLOT_SIDEBAR, "MWE_sidebar_default"); ScoreFactory::sendObjective($player); - if($session->getLatestSelection() !== null){ + if(($selection = $session->getLatestSelection()) !== null){ $line = 0; - $selection = $session->getLatestSelection(); ScoreFactory::setScoreLine($player, ++$line, TF::GOLD . $session->getLanguage()->translateString("spacer", ["Selection"])); - ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Position: " . TF::RESET . API::vecToString($selection->getPos1()->asVector3()) . " ยป " . API::vecToString($selection->getPos2()->asVector3())); + try{ + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Position: " . TF::RESET . API::vecToString($selection->getPos1()->asVector3()) . " ยป " . API::vecToString($selection->getPos2()->asVector3())); + }catch(SelectionException | RuntimeException | ScoreFactoryException){ + } ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " World: " . TF::RESET . $selection->getWorld()->getFolderName()); if($selection->shape === null) ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Shape: " . TF::RESET . 'N/A'); - else + else{ ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Shape: " . TF::RESET . (new ReflectionClass($selection->shape))->getShortName()); - ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Size: " . TF::RESET . API::vecToString(new Vector3($selection->getSizeX(), $selection->getSizeY(), $selection->getSizeZ())) . " ({$selection->getShape()->getTotalCount()})"); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Size: " . TF::RESET . API::vecToString(new Vector3($selection->getSizeX(), $selection->getSizeY(), $selection->getSizeZ())) . " ({$selection->shape->getTotalCount()})"); + } ScoreFactory::setScoreLine($player, ++$line, TF::GOLD . $session->getLanguage()->translateString("spacer", ["Settings"])); ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Tool Range: " . TF::RESET . Loader::getInstance()->getToolDistance()); diff --git a/src/xenialdan/MagicWE2/helper/SessionHelper.php b/src/xenialdan/MagicWE2/helper/SessionHelper.php index 63674f93..e0ed4221 100644 --- a/src/xenialdan/MagicWE2/helper/SessionHelper.php +++ b/src/xenialdan/MagicWE2/helper/SessionHelper.php @@ -9,6 +9,8 @@ use JsonException; use pocketmine\entity\InvalidSkinException; use pocketmine\entity\Skin; +use pocketmine\item\Item; +use pocketmine\item\Stick; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\plugin\Plugin; @@ -27,20 +29,19 @@ use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\tool\Brush; use xenialdan\MagicWE2\tool\BrushProperties; +use xenialdan\MagicWE2\tool\Debug; use function array_filter; use function array_values; use function count; -class SessionHelper -{ +class SessionHelper{ /** @var array */ private static array $userSessions = []; /** @var array */ private static array $pluginSessions = []; - public static function init(): void - { - if (!@mkdir($concurrentDirectory = Loader::getInstance()->getDataFolder() . "sessions") && !is_dir($concurrentDirectory)) { + public static function init() : void{ + if(!@mkdir($concurrentDirectory = Loader::getInstance()->getDataFolder() . "sessions") && !is_dir($concurrentDirectory)){ throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); } } @@ -51,32 +52,32 @@ public static function init(): void * @throws InvalidSkinException * @throws JsonException */ - public static function addSession(Session $session): void - { - if ($session instanceof UserSession) { + public static function addSession(Session $session) : void{ + if($session instanceof UserSession){ self::$userSessions[$session->getUUID()->toString()] = $session; - if (!empty(Loader::getInstance()->donatorData) && (($player = $session->getPlayer())->hasPermission("we.donator") || in_array($player->getName(), Loader::getInstance()->donators))) { + if(!empty(Loader::getInstance()->donatorData) && (($player = $session->getPlayer())->hasPermission("we.donator") || in_array($player->getName(), Loader::getInstance()->donators))){ $oldSkin = $player->getSkin(); $newSkin = new Skin($oldSkin->getSkinId(), $oldSkin->getSkinData(), Loader::getInstance()->donatorData, $oldSkin->getGeometryName(), $oldSkin->getGeometryData()); $player->setSkin($newSkin); $player->sendSkin(); } - } else if ($session instanceof PluginSession) self::$pluginSessions[$session->getUUID()->toString()] = $session; + }else if($session instanceof PluginSession) self::$pluginSessions[$session->getUUID()->toString()] = $session; } /** * Destroys a session and removes it from cache. Saves to file if $save is true + * * @param Session $session - * @param bool $save + * @param bool $save + * * @throws JsonException */ - public static function destroySession(Session $session, bool $save = true): void - { - if ($session instanceof UserSession) { + public static function destroySession(Session $session, bool $save = true) : void{ + if($session instanceof UserSession){ $session->cleanupInventory(); unset(self::$userSessions[$session->getUUID()->toString()]); - } else if ($session instanceof PluginSession) unset(self::$pluginSessions[($session->getUUID()->toString())]); - if ($save && $session instanceof UserSession) { + }else if($session instanceof PluginSession) unset(self::$pluginSessions[($session->getUUID()->toString())]); + if($save && $session instanceof UserSession){ $session->save(); } } @@ -93,11 +94,10 @@ public static function destroySession(Session $session, bool $save = true): void * @throws SessionException*@throws JsonException * @throws JsonException */ - public static function createUserSession(Player $player, bool $add = true): UserSession - { - if (!$player->hasPermission("we.session")) throw new SessionException(TF::RED . "You do not have the permission \"magicwe.session\""); + public static function createUserSession(Player $player, bool $add = true) : UserSession{ + if(!$player->hasPermission("we.session")) throw new SessionException(TF::RED . "You do not have the permission \"magicwe.session\""); $session = new UserSession($player); - if ($add) { + if($add){ self::addSession($session); (new MWESessionLoadEvent(Loader::getInstance(), $session))->call(); } @@ -114,142 +114,149 @@ public static function createUserSession(Player $player, bool $add = true): User * @throws InvalidSkinException*@throws JsonException * @throws JsonException */ - public static function createPluginSession(Plugin $plugin, bool $add = true): PluginSession - { + public static function createPluginSession(Plugin $plugin, bool $add = true) : PluginSession{ $session = new PluginSession($plugin); - if ($add) self::addSession($session); + if($add) self::addSession($session); return $session; } /** * @param Player $player + * * @return bool */ - public static function hasSession(Player $player): bool - { - try { + public static function hasSession(Player $player) : bool{ + try{ return self::getUserSession($player) instanceof UserSession; - } catch (SessionException) { + }catch(SessionException){ return false; } } /** * @param Player $player + * * @return null|UserSession * @throws SessionException */ - public static function getUserSession(Player $player): ?UserSession - { - if (count(self::$userSessions) === 0) return null; - $filtered = array_filter(self::$userSessions, function (Session $session) use ($player) { + public static function getUserSession(Player $player) : ?UserSession{ + if(count(self::$userSessions) === 0) return null; + $filtered = array_filter(self::$userSessions, function(Session $session) use ($player){ return $session instanceof UserSession && $session->getPlayer() === $player; }); - if (count($filtered) === 0) return null; - if (count($filtered) > 1) throw new SessionException("Multiple sessions found for player {$player->getName()}. This should never happen!"); + if(count($filtered) === 0) return null; + if(count($filtered) > 1) throw new SessionException("Multiple sessions found for player {$player->getName()}. This should never happen!"); return array_values($filtered)[0]; } /** * TODO cleanup or optimize + * * @param UuidInterface $uuid + * * @return null|Session * @throws SessionException */ - public static function getSessionByUUID(UuidInterface $uuid): ?Session - { + public static function getSessionByUUID(UuidInterface $uuid) : ?Session{ $v = self::$userSessions[$uuid->toString()] ?? self::$pluginSessions[$uuid->toString()] ?? null; - if (!$v instanceof Session) throw new SessionException("Session with uuid {$uuid->toString()} not found"); + if(!$v instanceof Session) throw new SessionException("Session with uuid {$uuid->toString()} not found"); return $v; } /** * @return array */ - public static function getUserSessions(): array - { + public static function getUserSessions() : array{ return self::$userSessions; } /** * @return array */ - public static function getPluginSessions(): array - { + public static function getPluginSessions() : array{ return self::$pluginSessions; } /** * @param Player $player + * * @return UserSession|null * @throws InvalidSkinException * @throws JsonException * @throws RuntimeException + * + * TODO use libmarshal */ - public static function loadUserSession(Player $player): ?UserSession - { - $path = Loader::getInstance()->getDataFolder() . "sessions" . DIRECTORY_SEPARATOR . - $player->getName() . ".json"; - if (!file_exists($path)) return null; + public static function loadUserSession(Player $player) : ?UserSession{ + $path = Loader::getInstance()->getDataFolder() . "sessions" . DIRECTORY_SEPARATOR . $player->getName() . ".json"; + if(!file_exists($path)) return null; $contents = file_get_contents($path); - if ($contents === false) return null; + if($contents === false) return null; $data = json_decode($contents, true, 512, JSON_THROW_ON_ERROR); - if (is_null($data) || json_last_error() !== JSON_ERROR_NONE) { + if(is_null($data) || json_last_error() !== JSON_ERROR_NONE){ Loader::getInstance()->getLogger()->error("Could not load user session from json file $path: " . json_last_error_msg()); #unlink($path);//TODO make safe return null; } $session = new UserSession($player); - try { + try{ + //TODO use libmarshal to load data $session->setUUID(UuidV4::fromString($data["uuid"])); $session->setWandEnabled($data["wandEnabled"]); $session->setDebugToolEnabled($data["debugToolEnabled"]); $session->setWailaEnabled($data["wailaEnabled"]); $session->setSidebarEnabled($data["sidebarEnabled"]); $session->setLanguage($data["language"]); - foreach ($data["brushes"] as $brushJson) { - try { + foreach($data["brushes"] as $brushJson){ + try{ $properties = BrushProperties::fromJson($brushJson["properties"]); $brush = new Brush($properties); $session->getBrushes()->addBrush($brush); - } catch (InvalidArgumentException) { + }catch(InvalidArgumentException){ continue; } } - if (!is_null(($latestSelection = $data["latestSelection"] ?? null))) { - try { + if(!is_null(($latestSelection = $data["latestSelection"] ?? null))){ + try{ $world = Server::getInstance()->getWorldManager()->getWorld($latestSelection["worldId"]); - if (is_null($world)) { + if(is_null($world)){ $session->sendMessage(TF::RED . "The world of the saved sessions selection is not loaded, the last selection was not restored.");//TODO translate better - } else { + }else{ $shapeClass = $latestSelection["shapeClass"] ?? Cuboid::class; - $pasteVector = $latestSelection["shape"]["pasteVector"]; + $pasteVector = $latestSelection["shape"]["pasteVector"] ?? null; unset($latestSelection["shape"]["pasteVector"]); - if (!is_null($pasteVector)) { + if(!is_null($pasteVector)){ $pasteV = new Vector3(...array_values($pasteVector)); - $shape = new $shapeClass($pasteV, ...array_values($latestSelection["shape"])); + $shape = new $shapeClass($pasteV, ...array_values($latestSelection["shape"]));//TODO why is this done this way } $selection = new Selection( $session->getUUID(), - Server::getInstance()->getWorldManager()->getWorld($latestSelection["worldId"]), - $latestSelection["pos1"]["x"], - $latestSelection["pos1"]["y"], - $latestSelection["pos1"]["z"], - $latestSelection["pos2"]["x"], - $latestSelection["pos2"]["y"], - $latestSelection["pos2"]["z"], + $world, + isset($latestSelection["pos1"]) ? new Vector3($latestSelection["pos1"]["x"], $latestSelection["pos1"]["y"], $latestSelection["pos1"]["z"]) : null, + isset($latestSelection["pos2"]) ? new Vector3($latestSelection["pos2"]["x"], $latestSelection["pos2"]["y"], $latestSelection["pos2"]["z"]) : null, $shape ?? null ); - if ($selection->isValid()) { + if($selection->isValid()){ $session->addSelection($selection); } } - } catch (RuntimeException) { + }catch(RuntimeException $e){ + Loader::getInstance()->getLogger()->logException($e); } } $session->setOutlineEnabled($data["outlineEnabled"]); + $debugData = $data["debug"] ?? null; + #var_dump($debugData); + if(!is_null($debugData)){ + $debugStick = Item::jsonDeserialize($debugData); + #var_dump($debugStick); + if(!$debugStick instanceof Stick) Loader::getInstance()->getLogger()->info("Debug stick data could not be loaded, ignoring"); + else $session->debug = Debug::fromItem($debugStick); + #var_dump($session->debug->toItem($session->getLanguage())->getNamedTag()); + } //TODO clipboard - } catch (Exception) { + }catch(Exception $e){ + Loader::getInstance()->getLogger()->logException($e); return null; } self::addSession($session); diff --git a/src/xenialdan/MagicWE2/helper/StructureStore.php b/src/xenialdan/MagicWE2/helper/StructureStore.php index 8bd081e7..30263336 100644 --- a/src/xenialdan/MagicWE2/helper/StructureStore.php +++ b/src/xenialdan/MagicWE2/helper/StructureStore.php @@ -53,8 +53,7 @@ public function loadStructure(string $filename, bool $override = true): MCStruct $id = pathinfo($filename, PATHINFO_FILENAME); if (!$override && array_key_exists($id, $this->structures)) throw new InvalidArgumentException("Can not override $id"); $path = Loader::getInstance()->getDataFolder() . 'assets' . DIRECTORY_SEPARATOR . $id . '.mcstructure'; - $structure = new MCStructure(); - $structure->parse($path); + $structure = MCStructure::read($path)->parse(); $this->structures[$id] = $structure; return $this->structures[$id]; } diff --git a/src/xenialdan/MagicWE2/helper/SubChunkIterator.php b/src/xenialdan/MagicWE2/helper/SubChunkIterator.php index 2e4376a4..8dd4b74a 100644 --- a/src/xenialdan/MagicWE2/helper/SubChunkIterator.php +++ b/src/xenialdan/MagicWE2/helper/SubChunkIterator.php @@ -63,7 +63,7 @@ public function getBlock(Vector3 $vector): int */ public function getBlockAt(int $x, int $y, int $z): int { - $y = (int)min(World::Y_MAX - 1, max(0, $y)); + $y = (int) min(World::Y_MAX - 1, max(World::Y_MIN, $y));//TODO check if this should be 255 or World::Y_MAX - 1 $this->moveTo($x, $y, $z); return $this->getCurrentSubChunk()->getFullBlock($x & 0x0f, $y & 0x0f, $z & 0x0f); } @@ -87,9 +87,8 @@ public function setBlock(Vector3 $vector, int $block): void * @throws BadMethodCallException * @throws BadMethodCallException */ - public function setBlockAt(int $x, int $y, int $z, int $block): void - { - $y = (int)min(World::Y_MAX - 1, max(0, $y)); + public function setBlockAt(int $x, int $y, int $z, int $block): void{ + $y = (int) min(World::Y_MAX - 1, max(World::Y_MIN, $y));//TODO check if this should be 255 or World::Y_MAX - 1 $this->moveTo($x, $y, $z); $this->getCurrentSubChunk()->setFullBlock($x & 0x0f, $y & 0x0f, $z & 0x0f, $block); } diff --git a/src/xenialdan/MagicWE2/selection/Selection.php b/src/xenialdan/MagicWE2/selection/Selection.php index a2c2ab72..d91d40a8 100644 --- a/src/xenialdan/MagicWE2/selection/Selection.php +++ b/src/xenialdan/MagicWE2/selection/Selection.php @@ -22,6 +22,7 @@ use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\helper\SubChunkIterator; +use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\shape\Cuboid; use xenialdan\MagicWE2\selection\shape\Shape; use xenialdan\MagicWE2\session\Session; @@ -30,98 +31,75 @@ * Class Selection * @package xenialdan\MagicWE2 */ -class Selection implements Serializable, JsonSerializable -{ - /** @var int|null */ +class Selection implements Serializable, JsonSerializable{ public ?int $worldId = null; - /** @var Vector3|null */ public ?Vector3 $pos1 = null; - /** @var Vector3|null */ public ?Vector3 $pos2 = null; - /** @var UuidInterface */ public UuidInterface $uuid; - /** @var UuidInterface */ public UuidInterface $sessionUUID; - /** @var Shape|null */ public ?Shape $shape = null; private SubChunkIterator $iterator; - /** - * Selection constructor. - * @param UuidInterface $sessionUUID - * @param World $world - * @param ?int $minX - * @param ?int $minY - * @param ?int $minZ - * @param ?int $maxX - * @param ?int $maxY - * @param ?int $maxZ - * @param ?Shape $shape - */ - public function __construct(UuidInterface $sessionUUID, World $world, ?int $minX = null, ?int $minY = null, ?int $minZ = null, ?int $maxX = null, ?int $maxY = null, ?int $maxZ = null, ?Shape $shape = null) - { + public function __construct(UuidInterface $sessionUUID, World $world, ?Vector3 $minPos = null, ?Vector3 $maxPos = null, ?Shape $shape = null){ $this->sessionUUID = $sessionUUID; $this->worldId = $world->getId(); - if (isset($minX, $minY, $minZ)) { - $this->pos1 = (new Vector3($minX, $minY, $minZ))->floor(); - } - if (isset($maxX, $maxY, $maxZ)) { - $this->pos2 = (new Vector3($maxX, $maxY, $maxZ))->floor(); - } - if ($shape !== null) $this->shape = $shape; + if($minPos !== null) $minPos = $minPos->floor(); + if($maxPos !== null) $maxPos = $maxPos->floor(); + $this->pos1 = $minPos; + $this->pos2 = $maxPos; + $this->shape = $shape; $this->setUUID(Uuid::uuid4()); - try { + try{ (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_CREATE))->call(); - } catch (RuntimeException) { + }catch(RuntimeException $e){ + Loader::getInstance()->getLogger()->logException($e); } - - $this->iterator = new SubChunkIterator(new AsyncWorld($this)); } - public function free(): void - { + public function free() : void{ $this->iterator->invalidate(); $manager = $this->iterator->getManager(); - if ($manager instanceof AsyncWorld) $manager->cleanChunks(); + if($manager instanceof AsyncWorld) $manager->cleanChunks(); } /** * @return World - * @throws Exception + * @throws SelectionException|RuntimeException */ - public function getWorld(): World - { - if (is_null($this->worldId)) { + public function getWorld() : World{ + if(is_null($this->worldId)){ throw new SelectionException("World is not set!"); } $world = Server::getInstance()->getWorldManager()->getWorld($this->worldId); - if (is_null($world)) { + if(is_null($world)){ throw new SelectionException("World is not found!"); } return $world; } - public function setWorld(World $world): void - { + public function setWorld(World $world) : void{ + if($this->worldId === $world->getId()){ + return; + } $this->worldId = $world->getId(); - try { + try{ (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_WORLD))->call(); - } catch (RuntimeException) { + }catch(RuntimeException $e){ + Loader::getInstance()->getLogger()->logException($e); } - $this->free(); - $manager = $this->getIterator()->getManager(); - if ($manager instanceof AsyncWorld) $manager->copyChunks($this); +// $this->free(); +// $manager = $this->getIterator()->getManager(); +// if($manager instanceof AsyncWorld) $manager->copyChunks($this); } /** * @return Position - * @throws Exception + * @throws SelectionException|RuntimeException */ - public function getPos1(): Position - { - if (is_null($this->pos1)) { + public function getPos1() : Position{ + if(is_null($this->pos1)){ throw new SelectionException("Position 1 is not set!"); } return Position::fromObject($this->pos1, $this->getWorld()); @@ -129,43 +107,45 @@ public function getPos1(): Position /** * @param Position $position + * * @throws AssumptionFailedError */ - public function setPos1(Position $position): void - { + public function setPos1(Position $position) : void{ $this->pos1 = $position->asVector3()->floor(); - if ($this->pos1->y >= World::Y_MAX) $this->pos1->y = World::Y_MAX; - if ($this->pos1->y < 0) $this->pos1->y = 0; - if ($this->worldId !== $position->getWorld()->getId()) {//reset other position if in different world + if($this->pos1->y > World::Y_MAX) $this->pos1->y = World::Y_MAX;//TODO check if this should be 255 or World::Y_MAX + if($this->pos1->y < World::Y_MIN) $this->pos1->y = World::Y_MIN; + if($this->worldId !== $position->getWorld()->getId()){//reset other position if in different world $this->pos2 = null; } $this->setWorld($position->getWorld()); - if (($this->shape instanceof Cuboid || $this->shape === null) && $this->isValid())//TODO test change + if(($this->shape === null || $this->shape instanceof Cuboid) && $this->isValid()) $this->setShape(Cuboid::constructFromPositions($this->pos1, $this->pos2)); - try { + try{ $session = SessionHelper::getSessionByUUID($this->sessionUUID); - if ($session instanceof Session) { + if($session instanceof Session){ $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('selection.pos1.set', [$this->pos1->getX(), $this->pos1->getY(), $this->pos1->getZ()])); - try { + try{ (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_POS1))->call(); - } catch (RuntimeException) { + }catch(RuntimeException $e){ + Loader::getInstance()->getLogger()->logException($e); } - $this->free(); - $manager = $this->getIterator()->getManager(); - if ($manager instanceof AsyncWorld) $manager->copyChunks($this); +// $this->free(); +// $manager = $this->getIterator()->getManager(); +// if($manager instanceof AsyncWorld) $manager->copyChunks($this); } - } catch (SessionException) { + }catch(SessionException){ //TODO log? kick? + }catch(SelectionException | RuntimeException $e){ + Loader::getInstance()->getLogger()->logException($e); } } /** * @return Position - * @throws Exception + * @throws SelectionException|RuntimeException */ - public function getPos2(): Position - { - if (is_null($this->pos2)) { + public function getPos2() : Position{ + if(is_null($this->pos2)){ throw new SelectionException("Position 2 is not set!"); } return Position::fromObject($this->pos2, $this->getWorld()); @@ -173,56 +153,55 @@ public function getPos2(): Position /** * @param Position $position - * @throws AssumptionFailedError */ - public function setPos2(Position $position): void - { + public function setPos2(Position $position) : void{ $this->pos2 = $position->asVector3()->floor(); - if ($this->pos2->y >= World::Y_MAX) $this->pos2->y = World::Y_MAX; - if ($this->pos2->y < 0) $this->pos2->y = 0; - if ($this->worldId !== $position->getWorld()->getId()) { + if($this->pos2->y > World::Y_MAX) $this->pos2->y = World::Y_MAX; + if($this->pos2->y < World::Y_MIN) $this->pos2->y = World::Y_MIN; + if($this->worldId !== $position->getWorld()->getId()){ $this->pos1 = null; } $this->setWorld($position->getWorld()); - if (($this->shape instanceof Cuboid || $this->shape === null) && $this->isValid()) + if(($this->shape === null || $this->shape instanceof Cuboid) && $this->isValid()) $this->setShape(Cuboid::constructFromPositions($this->pos1, $this->pos2)); - try { + try{ $session = SessionHelper::getSessionByUUID($this->sessionUUID); - if ($session instanceof Session) { + if($session instanceof Session){ $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('selection.pos2.set', [$this->pos2->getX(), $this->pos2->getY(), $this->pos2->getZ()])); - try { + try{ (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_POS2))->call(); - } catch (RuntimeException) { + }catch(RuntimeException $e){ + Loader::getInstance()->getLogger()->logException($e); } - $this->free(); - $manager = $this->getIterator()->getManager(); - if ($manager instanceof AsyncWorld) $manager->copyChunks($this); +// $this->free(); +// $manager = $this->getIterator()->getManager(); +// if($manager instanceof AsyncWorld) $manager->copyChunks($this); } - } catch (SessionException) { - //TODO log? kick? + }catch(SessionException | SelectionException | RuntimeException $e){//TODO log? kick? + Loader::getInstance()->getLogger()->logException($e); } } /** * @return Shape - * @throws Exception + * @throws SelectionException */ - public function getShape(): Shape - { - if (!$this->shape instanceof Shape) throw new SelectionException("Shape is not valid"); + public function getShape() : Shape{ + if(!$this->shape instanceof Shape) throw new SelectionException("Shape is not valid"); return $this->shape; } - public function setShape(Shape $shape): void - { + public function setShape(Shape $shape) : void{ +// var_dump($shape); $this->shape = $shape; - try { - (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_SHAPE))->call(); - } catch (RuntimeException) { - }//might cause duplicated call - $this->free(); - $manager = $this->getIterator()->getManager(); - if ($manager instanceof AsyncWorld) $manager->copyChunks($this); + try{ + (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_SHAPE))->call();//might cause duplicated call +// $this->free(); +// $manager = $this->getIterator()->getManager(); +// if($manager instanceof AsyncWorld) $manager->copyChunks($this); + }catch(RuntimeException | SelectionException $e){ + Loader::getInstance()->getLogger()->debug($e->getMessage()); + } } /** @@ -233,47 +212,44 @@ public function setShape(Shape $shape): void * - The positions are not in the same world * @return bool */ - public function isValid(): bool - { - try { - #$this->getShape(); + public function isValid() : bool{ + try{ +// var_dump("World: " . $this->getWorld()->getId() . " Pos1: " . $this->pos1 . " Pos2: " . $this->pos2 . " Shape: " . $this->shape?->serialize()); + //$this->getShape(); $this->getWorld(); $this->getPos1(); $this->getPos2(); - } catch (Exception) { + }catch(Exception $e){ + Loader::getInstance()->getLogger()->debug($e->getMessage()); return false; } return true; } - public function getSizeX(): int - { - return (int)(abs($this->pos1->x - $this->pos2->x) + 1); + public function getSizeX() : int{ + return (int) (abs($this->pos1->x - $this->pos2->x) + 1); } - public function getSizeY(): int - { - return (int)(abs($this->pos1->y - $this->pos2->y) + 1); + public function getSizeY() : int{ + return (int) (abs($this->pos1->y - $this->pos2->y) + 1); } - public function getSizeZ(): int - { - return (int)(abs($this->pos1->z - $this->pos2->z) + 1); + public function getSizeZ() : int{ + return (int) (abs($this->pos1->z - $this->pos2->z) + 1); } - public function setUUID(UuidInterface $uuid): void - { + public function setUUID(UuidInterface $uuid) : void{ $this->uuid = $uuid; } - public function getUUID(): UuidInterface - { + public function getUUID() : UuidInterface{ return $this->uuid; } - public function getIterator(): SubChunkIterator - { - return $this->iterator; + public function getIterator(bool $copyChunks = true) : SubChunkIterator{ + $manager = new AsyncWorld(); + if($copyChunks) $manager->copyChunks($this); + return new SubChunkIterator($manager); } /** @@ -282,8 +258,7 @@ public function getIterator(): SubChunkIterator * @return string the string representation of the object or null * @since 5.1.0 */ - public function serialize(): string - { + public function serialize() : string{ return serialize([ $this->worldId, $this->pos1, @@ -291,7 +266,7 @@ public function serialize(): string $this->uuid, $this->sessionUUID, $this->shape, - $this->iterator, +// $this->iterator, ]); } @@ -314,7 +289,7 @@ public function unserialize(string $data){ $this->uuid, $this->sessionUUID, $this->shape, - $this->iterator +// $this->iterator ] = unserialize($data/*, ['allowed_classes' => [__CLASS__, Vector3::class,UuidInterface::class,Shape::class]]*/);//TODO test pm4 } @@ -325,10 +300,9 @@ public function unserialize(string $data){ * which is a value of any type other than a resource. * @since 5.4.0 */ - public function jsonSerialize(): array - { - $arr = (array)$this; - if ($this->shape !== null) + public function jsonSerialize() : array{ + $arr = (array) $this; + if($this->shape !== null) $arr["shapeClass"] = get_class($this->shape); return $arr; } diff --git a/src/xenialdan/MagicWE2/selection/shape/Cone.php b/src/xenialdan/MagicWE2/selection/shape/Cone.php index d3aa6e80..cbf1d8bb 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cone.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cone.php @@ -36,28 +36,33 @@ public function __construct(Vector3 $pasteVector, int $height, int $diameter, bo $this->flipped = $flipped; } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + return clone $this; + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator { ##$reducePerLayer = ($this->diameter / $this->height); - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); - for ($x = (int)floor($centerVec2->x - $this->diameter / 2 - 1); $x <= floor($centerVec2->x + $this->diameter / 2 + 1); $x++) { - for ($y = (int)floor($this->getPasteVector()->y), $ry = 0; $y < floor($this->getPasteVector()->y + $this->height); $y++, $ry++) { - for ($z = (int)floor($centerVec2->y - $this->diameter / 2 - 1); $z <= floor($centerVec2->y + $this->diameter / 2 + 1); $z++) { + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); + for ($x = (int)floor($centerVec2->x - $this->diameter / 2 - 1); $x <= floor($centerVec2->x + $this->diameter / 2 + 1); $x++){ + for($y = (int) floor($this->pasteVector->y), $ry = 0; $y < floor($this->pasteVector->y + $this->height); $y++, $ry++){ + for($z = (int) floor($centerVec2->y - $this->diameter / 2 - 1); $z <= floor($centerVec2->y + $this->diameter / 2 + 1); $z++){ // $vec2 = new Vector2($x, $z); $vec3 = new Vector3($x, $y, $z); /*if ($this->flipped) @@ -66,16 +71,16 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene $radiusLayer = ($this->diameter - $reducePerLayer * $ry) / 2;*/ // if ($vec2->distanceSquared($centerVec2) > ($radiusLayer ** 2) || (API::hasFlag($flags, API::FLAG_HOLLOW_CLOSED) && ($ry !== 0 && $ry !== $this->height - 1) && $vec2->distanceSquared($centerVec2) <= ((($this->diameter / 2) - 1) ** 2)) || ((API::hasFlag($flags, API::FLAG_HOLLOW) && $vec2->distanceSquared($centerVec2) <= ((($this->diameter / 2) - 1) ** 2)))) // continue; - $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int)$vec3->x, (int)$vec3->y, (int)$vec3->z); + $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int) $vec3->x, (int) $vec3->y, (int) $vec3->z); // if (API::hasFlag($flags, API::FLAG_KEEP_BLOCKS) && $block->getId() !== BlockLegacyIds::AIR) continue; // if (API::hasFlag($flags, API::FLAG_KEEP_AIR) && $block->getId() === BlockLegacyIds::AIR) continue; - if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fuufufufuuu EDIT: And.. fufufu is what? - if ($filterblocks->empty()) yield $block; - else { - foreach ($filterblocks->palette() as $filterblock) { + if($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < World::Y_MIN) continue;//TODO check if this should be 255 or World::Y_MAX + if($filterblocks->empty()) yield $block; + else{ + foreach($filterblocks->palette() as $filterblock){ // if (($block->getId() === $filterblock->getId()) && ((API::hasFlag($flags, API::FLAG_VARIANT) && $block->getIdInfo()->getVariant() === $filterblock->getIdInfo()->getVariant()) || (!API::hasFlag($flags, API::FLAG_VARIANT) && ($block->getMeta() === $filterblock->getMeta() || API::hasFlag($flags, API::FLAG_KEEP_META))))) - if ($block->getFullId() === $filterblock->getFullId()) + if($block->getFullId() === $filterblock->getFullId()) yield $block; } } @@ -86,13 +91,14 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ - public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator - { - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); + public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator{ + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); for ($x = (int)floor($centerVec2->x - $this->diameter / 2 - 1); $x <= floor($centerVec2->x + $this->diameter / 2 + 1); $x++) { for ($z = (int)floor($centerVec2->y - $this->diameter / 2 - 1); $z <= floor($centerVec2->y + $this->diameter / 2 + 1); $z++) { $vec2 = new Vector2($x, $z); diff --git a/src/xenialdan/MagicWE2/selection/shape/Cube.php b/src/xenialdan/MagicWE2/selection/shape/Cube.php index 184f9437..fcfcd63c 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cube.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cube.php @@ -23,22 +23,27 @@ public function __construct(Vector3 $pasteVector, int $width) $this->width = $width; } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + return clone $this; + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator { for ($x = (int)floor($this->getMinVec3()->x), $rx = 0; $x <= floor($this->getMaxVec3()->x); $x++, $rx++) { for ($y = (int)floor($this->getMinVec3()->y), $ry = 0; $y <= floor($this->getMaxVec3()->y); $y++, $ry++) { @@ -47,13 +52,13 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene // if (API::hasFlag($flags, API::FLAG_KEEP_BLOCKS) && $block->getId() !== BlockLegacyIds::AIR) continue; // if (API::hasFlag($flags, API::FLAG_KEEP_AIR) && $block->getId() === BlockLegacyIds::AIR) continue; - if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO check for removal because relative might be at other y + if($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < World::Y_MIN) continue;//TODO check if this should be 255 or World::Y_MAX //TODO check for removal because relative might be at other y // if (API::hasFlag($flags, API::FLAG_HOLLOW) && ($block->getPosition()->x > $this->getMinVec3()->getX() && $block->getPosition()->x < $this->getMaxVec3()->getX()) && ($block->getPosition()->y > $this->getMinVec3()->getY() && $block->getPosition()->y < $this->getMaxVec3()->getY()) && ($block->getPosition()->z > $this->getMinVec3()->getZ() && $block->getPosition()->z < $this->getMaxVec3()->getZ())) continue; - if ($filterblocks->empty()) yield $block; - else { - foreach ($filterblocks->palette() as $filterblock) { + if($filterblocks->empty()) yield $block; + else{ + foreach($filterblocks->palette() as $filterblock){ // if (($block->getId() === $filterblock->getId()) && ((API::hasFlag($flags, API::FLAG_VARIANT) && $block->getIdInfo()->getVariant() === $filterblock->getIdInfo()->getVariant()) || (!API::hasFlag($flags, API::FLAG_VARIANT) && ($block->getMeta() === $filterblock->getMeta() || API::hasFlag($flags, API::FLAG_KEEP_META))))) - if ($block->getFullId() === $filterblock->getFullId()) + if($block->getFullId() === $filterblock->getFullId()) yield $block; } } @@ -64,8 +69,10 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator diff --git a/src/xenialdan/MagicWE2/selection/shape/Cuboid.php b/src/xenialdan/MagicWE2/selection/shape/Cuboid.php index 87983791..d91c8bcf 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cuboid.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cuboid.php @@ -3,6 +3,7 @@ namespace xenialdan\MagicWE2\selection\shape; use Generator; +use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -11,14 +12,12 @@ use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; +use function array_keys; +use function var_dump; -class Cuboid extends Shape -{ - /** @var int */ +class Cuboid extends Shape{ public int $width = 5; - /** @var int */ public int $height = 5; - /** @var int */ public int $depth = 5; /** @@ -36,31 +35,44 @@ public function __construct(Vector3 $pasteVector, int $width, int $height, int $ $this->depth = $depth; } - public static function constructFromPositions(Vector3 $pos1, Vector3 $pos2): self - { - $width = (int)abs($pos1->getX() - $pos2->getX()) + 1; - $height = (int)abs($pos1->getY() - $pos2->getY()) + 1; - $depth = (int)abs($pos1->getZ() - $pos2->getZ()) + 1; + public static function constructFromPositions(Vector3 $p1, Vector3 $p2) : self{ + $pos1 = Vector3::minComponents($p1, $p2); + $pos2 = Vector3::maxComponents($p1, $p2); + $width = (int) abs($pos1->getX() - $pos2->getX()) + 1; + $height = (int) abs($pos1->getY() - $pos2->getY()) + 1; + $depth = (int) abs($pos1->getZ() - $pos2->getZ()) + 1; return new Cuboid((new Vector3(($pos1->x + $pos2->x) / 2, min($pos1->y, $pos2->y), ($pos1->z + $pos2->z) / 2)), $width, $height, $depth); } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + if($rotation % 90 !== 0){ + throw new InvalidArgumentException("Rotation must be divisible by 90"); + } + return match ($rotation % 180 === 0) { + true => clone $this, + false => new self($this->pasteVector, $this->depth, $this->height, $this->width) + }; + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator { + var_dump(array_keys($manager->getChunks())); for ($x = (int)floor($this->getMinVec3()->x); $x <= floor($this->getMaxVec3()->x); $x++) { for ($y = (int)floor($this->getMinVec3()->y); $y <= floor($this->getMaxVec3()->y); $y++) { for ($z = (int)floor($this->getMinVec3()->z); $z <= floor($this->getMaxVec3()->z); $z++) { @@ -69,13 +81,13 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene // if (API::hasFlag($flags, API::FLAG_KEEP_BLOCKS) && $block->getId() !== BlockLegacyIds::AIR) continue; // if (API::hasFlag($flags, API::FLAG_KEEP_AIR) && $block->getId() === BlockLegacyIds::AIR) continue; - if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO check for removal because relative might be at other y + if($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < World::Y_MIN) continue;//TODO check if this should be 255 or World::Y_MAX //TODO check for removal because relative might be at other y // if (API::hasFlag($flags, API::FLAG_HOLLOW) && ($block->getPosition()->x > $this->getMinVec3()->getX() && $block->getPosition()->x < $this->getMaxVec3()->getX()) && ($block->getPosition()->y > $this->getMinVec3()->getY() && $block->getPosition()->y < $this->getMaxVec3()->getY()) && ($block->getPosition()->z > $this->getMinVec3()->getZ() && $block->getPosition()->z < $this->getMaxVec3()->getZ())) continue; - if ($filterblocks->empty()) yield $block; - else { - foreach ($filterblocks->palette() as $filterblock) { + if($filterblocks->empty()) yield $block; + else{ + foreach($filterblocks->palette() as $filterblock){ // if (($block->getId() === $filterblock->getId()) && ((API::hasFlag($flags, API::FLAG_VARIANT) && $block->getIdInfo()->getVariant() === $filterblock->getIdInfo()->getVariant()) || (!API::hasFlag($flags, API::FLAG_VARIANT) && ($block->getMeta() === $filterblock->getMeta() || API::hasFlag($flags, API::FLAG_KEEP_META))))) - if ($block->getFullId() === $filterblock->getFullId()) + if($block->getFullId() === $filterblock->getFullId()) yield $block; } } @@ -86,8 +98,10 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator @@ -111,9 +125,6 @@ public function getAABB(): AxisAlignedBB ); } - /** - * @return int - */ public function getTotalCount(): int { return $this->width * $this->height * $this->depth; diff --git a/src/xenialdan/MagicWE2/selection/shape/Custom.php b/src/xenialdan/MagicWE2/selection/shape/Custom.php index 30501842..d938a9cd 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Custom.php +++ b/src/xenialdan/MagicWE2/selection/shape/Custom.php @@ -3,6 +3,7 @@ namespace xenialdan\MagicWE2\selection\shape; use Generator; +use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -28,25 +29,30 @@ public function __construct(Vector3 $pasteVector, array $positions) $this->positions = $positions; } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; $pos = $this->positions; $this->positions = []; - foreach ($pos as $vector3)$this->positions[]=$vector3->addVector($offset); - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + foreach($pos as $vector3) $this->positions[] = $vector3->addVector($offset); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + throw new InvalidArgumentException("Rotation is not supported"); + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator { foreach ($this->positions as $position) { //TODO filterblocks @@ -56,8 +62,10 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator diff --git a/src/xenialdan/MagicWE2/selection/shape/Cylinder.php b/src/xenialdan/MagicWE2/selection/shape/Cylinder.php index 3fafd595..106a4008 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cylinder.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cylinder.php @@ -32,41 +32,45 @@ public function __construct(Vector3 $pasteVector, int $height, int $diameter) $this->diameter = $diameter; } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + return clone $this; + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator - { - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); - for ($x = (int)floor($centerVec2->x - $this->diameter / 2 - 1); $x <= floor($centerVec2->x + $this->diameter / 2 + 1); $x++) { - for ($y = (int)floor($this->getPasteVector()->y), $ry = 0; $y < floor($this->getPasteVector()->y + $this->height); $y++, $ry++) { - for ($z = (int)floor($centerVec2->y - $this->diameter / 2 - 1); $z <= floor($centerVec2->y + $this->diameter / 2 + 1); $z++) { + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator{ + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); + for ($x = (int)floor($centerVec2->x - $this->diameter / 2 - 1); $x <= floor($centerVec2->x + $this->diameter / 2 + 1); $x++){ + for($y = (int) floor($this->pasteVector->y), $ry = 0; $y < floor($this->pasteVector->y + $this->height); $y++, $ry++){ + for($z = (int) floor($centerVec2->y - $this->diameter / 2 - 1); $z <= floor($centerVec2->y + $this->diameter / 2 + 1); $z++){ // $vec2 = new Vector2($x, $z); $vec3 = new Vector3($x, $y, $z); // if ($vec2->distanceSquared($centerVec2) > (($this->diameter / 2) ** 2) || (API::hasFlag($flags, API::FLAG_HOLLOW_CLOSED) && ($ry !== 0 && $ry !== $this->height - 1) && $vec2->distanceSquared($centerVec2) <= ((($this->diameter / 2) - 1) ** 2)) || ((API::hasFlag($flags, API::FLAG_HOLLOW) && $vec2->distanceSquared($centerVec2) <= ((($this->diameter / 2) - 1) ** 2)))) // continue; - $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int)$vec3->x, (int)$vec3->y, (int)$vec3->z); + $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int) $vec3->x, (int) $vec3->y, (int) $vec3->z); // if (API::hasFlag($flags, API::FLAG_KEEP_BLOCKS) && $block->getId() !== BlockLegacyIds::AIR) continue; // if (API::hasFlag($flags, API::FLAG_KEEP_AIR) && $block->getId() === BlockLegacyIds::AIR) continue; - if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fuufufufuuu - if ($filterblocks->empty()) yield $block; - else { - foreach ($filterblocks->palette() as $filterblock) { + if($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < World::Y_MIN) continue;//TODO check if this should be 255 or World::Y_MAX + if($filterblocks->empty()) yield $block; + else{ + foreach($filterblocks->palette() as $filterblock){ // if (($block->getId() === $filterblock->getId()) && ((API::hasFlag($flags, API::FLAG_VARIANT) && $block->getIdInfo()->getVariant() === $filterblock->getIdInfo()->getVariant()) || (!API::hasFlag($flags, API::FLAG_VARIANT) && ($block->getMeta() === $filterblock->getMeta() || API::hasFlag($flags, API::FLAG_KEEP_META))))) - if ($block->getFullId() === $filterblock->getFullId()) + if($block->getFullId() === $filterblock->getFullId()) yield $block; } } @@ -77,13 +81,14 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ - public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator - { - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); + public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator{ + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); for ($x = (int)floor($centerVec2->x - $this->diameter / 2 - 1); $x <= floor($centerVec2->x + $this->diameter / 2 + 1); $x++) { for ($z = (int)floor($centerVec2->y - $this->diameter / 2 - 1); $z <= floor($centerVec2->y + $this->diameter / 2 + 1); $z++) { $vec2 = new Vector2($x, $z); diff --git a/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php b/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php index 2becaea2..92d04d11 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php +++ b/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php @@ -3,6 +3,7 @@ namespace xenialdan\MagicWE2\selection\shape; use Generator; +use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -36,25 +37,35 @@ public function __construct(Vector3 $pasteVector, int $width, int $height, int $ $this->depth = $depth; } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + if($rotation % 90 !== 0){ + throw new InvalidArgumentException("Rotation must be divisible by 90"); + } + return match ($rotation % 180 === 0) { + true => clone $this, + false => new self($this->pasteVector, $this->depth, $this->height, $this->width) + }; + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator - { - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); - $this->pasteVector = $this->getPasteVector()->add(0, -0.5, 0); + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator{ + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); + $this->pasteVector = $this->pasteVector->add(0, -0.5, 0); $xrad = $this->width / 2; $yrad = $this->height / 2; @@ -68,24 +79,24 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene for ($x = (int)floor($centerVec2->x - $this->width / 2 /*- 1*/); $x <= floor($centerVec2->x + $this->width / 2 /*+ 1*/); $x++) { $xSquared = ($targetX - $x) ** 2; - for ($y = (int)floor($this->getPasteVector()->y) + 1, $ry = 0; $y <= floor($this->getPasteVector()->y + $this->height); $y++, $ry++) { + for($y = (int) floor($this->pasteVector->y) + 1, $ry = 0; $y <= floor($this->pasteVector->y + $this->height); $y++, $ry++){ $ySquared = ($targetY - $y + $yrad) ** 2; - for ($z = (int)floor($centerVec2->y - $this->depth / 2 /*- 1*/); $z <= floor($centerVec2->y + $this->depth / 2 /*+ 1*/); $z++) { + for($z = (int) floor($centerVec2->y - $this->depth / 2 /*- 1*/); $z <= floor($centerVec2->y + $this->depth / 2 /*+ 1*/); $z++){ $zSquared = ($targetZ - $z) ** 2; $vec3 = new Vector3($x, $y, $z); //TODO hollow - if ($xSquared / $xradSquared + $ySquared / $yradSquared + $zSquared / $zradSquared >= 1) continue; - $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int)$vec3->x, (int)$vec3->y, (int)$vec3->z); + if($xSquared / $xradSquared + $ySquared / $yradSquared + $zSquared / $zradSquared >= 1) continue; + $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int) $vec3->x, (int) $vec3->y, (int) $vec3->z); // if (API::hasFlag($flags, API::FLAG_KEEP_BLOCKS) && $block->getId() !== BlockLegacyIds::AIR) continue; // if (API::hasFlag($flags, API::FLAG_KEEP_AIR) && $block->getId() === BlockLegacyIds::AIR) continue; - if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fuufufufuuu - if ($filterblocks->empty()) yield $block; - else { - foreach ($filterblocks->palette() as $filterblock) { + if($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < World::Y_MIN) continue;//TODO check if this should be 255 or World::Y_MAX + if($filterblocks->empty()) yield $block; + else{ + foreach($filterblocks->palette() as $filterblock){ // if (($block->getId() === $filterblock->getId()) && ((API::hasFlag($flags, API::FLAG_VARIANT) && $block->getIdInfo()->getVariant() === $filterblock->getIdInfo()->getVariant()) || (!API::hasFlag($flags, API::FLAG_VARIANT) && ($block->getMeta() === $filterblock->getMeta() || API::hasFlag($flags, API::FLAG_KEEP_META))))) - if ($block->getFullId() === $filterblock->getFullId()) + if($block->getFullId() === $filterblock->getFullId()) yield $block; } } @@ -96,13 +107,14 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ - public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator - { - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); + public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator{ + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); $xrad = $this->width / 2; $zrad = $this->depth / 2; diff --git a/src/xenialdan/MagicWE2/selection/shape/Pyramid.php b/src/xenialdan/MagicWE2/selection/shape/Pyramid.php index ea87ae48..29836c21 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Pyramid.php +++ b/src/xenialdan/MagicWE2/selection/shape/Pyramid.php @@ -3,6 +3,7 @@ namespace xenialdan\MagicWE2\selection\shape; use Generator; +use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -12,8 +13,7 @@ use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; -class Pyramid extends Shape -{ +class Pyramid extends Shape{ /** @var int */ public int $width = 5; /** @var int */ @@ -25,14 +25,14 @@ class Pyramid extends Shape /** * Pyramid constructor. + * * @param Vector3 $pasteVector - * @param int $width - * @param int $height - * @param int $depth - * @param bool $flipped + * @param int $width + * @param int $height + * @param int $depth + * @param bool $flipped */ - public function __construct(Vector3 $pasteVector, int $width, int $height, int $depth, bool $flipped = false) - { + public function __construct(Vector3 $pasteVector, int $width, int $height, int $depth, bool $flipped = false){ $this->pasteVector = $pasteVector; $this->width = $width; $this->height = $height; @@ -40,51 +40,61 @@ public function __construct(Vector3 $pasteVector, int $width, int $height, int $ $this->flipped = $flipped; } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + if($rotation % 90 !== 0){ + throw new InvalidArgumentException("Rotation must be divisible by 90"); + } + return match ($rotation % 180 === 0) { + true => clone $this, + false => new self($this->pasteVector, $this->depth, $this->height, $this->width, $this->flipped) + }; + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator - { + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator{ $reduceXPerLayer = -($this->width / $this->height); $reduceZPerLayer = -($this->depth / $this->height); - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); - for ($x = (int)floor($centerVec2->x - $this->width / 2 - 1); $x <= floor($centerVec2->x + $this->width / 2 + 1); $x++) { - for ($y = (int)floor($this->getPasteVector()->y), $ry = 0; $y < floor($this->getPasteVector()->y + $this->height); $y++, $ry++) { - for ($z = (int)floor($centerVec2->y - $this->depth / 2 - 1); $z <= floor($centerVec2->y + $this->depth / 2 + 1); $z++) { + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); + for($x = (int) floor($centerVec2->x - $this->width / 2 - 1); $x <= floor($centerVec2->x + $this->width / 2 + 1); $x++){ + for($y = (int) floor($this->pasteVector->y), $ry = 0; $y < floor($this->pasteVector->y + $this->height); $y++, $ry++){ + for($z = (int) floor($centerVec2->y - $this->depth / 2 - 1); $z <= floor($centerVec2->y + $this->depth / 2 + 1); $z++){ $vec2 = new Vector2($x, $z); $vec3 = new Vector3($x, $y, $z); - if ($this->flipped) { + if($this->flipped){ $radiusLayerX = ($this->width + $reduceXPerLayer * ($this->height - $ry)) / 2; $radiusLayerZ = ($this->depth + $reduceZPerLayer * ($this->height - $ry)) / 2; - } else { + }else{ $radiusLayerX = ($this->width + $reduceXPerLayer * $ry) / 2; $radiusLayerZ = ($this->depth + $reduceZPerLayer * $ry) / 2; } //TODO hollow - if (floor(abs($centerVec2->x - $vec2->x)) >= $radiusLayerX || floor(abs($centerVec2->y - $vec2->y)) >= $radiusLayerZ) + if(floor(abs($centerVec2->x - $vec2->x)) >= $radiusLayerX || floor(abs($centerVec2->y - $vec2->y)) >= $radiusLayerZ) continue; - $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int)$vec3->x, (int)$vec3->y, (int)$vec3->z); + $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int) $vec3->x, (int) $vec3->y, (int) $vec3->z); // if (API::hasFlag($flags, API::FLAG_KEEP_BLOCKS) && $block->getId() !== BlockLegacyIds::AIR) continue; // if (API::hasFlag($flags, API::FLAG_KEEP_AIR) && $block->getId() === BlockLegacyIds::AIR) continue; - if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fuufufufuuu - if ($filterblocks->empty()) yield $block; - else { - foreach ($filterblocks->palette() as $filterblock) { + if($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < World::Y_MIN) continue;//TODO check if this should be 255 or World::Y_MAX + if($filterblocks->empty()) yield $block; + else{ + foreach($filterblocks->palette() as $filterblock){ // if (($block->getId() === $filterblock->getId()) && ((API::hasFlag($flags, API::FLAG_VARIANT) && $block->getIdInfo()->getVariant() === $filterblock->getIdInfo()->getVariant()) || (!API::hasFlag($flags, API::FLAG_VARIANT) && ($block->getMeta() === $filterblock->getMeta() || API::hasFlag($flags, API::FLAG_KEEP_META))))) - if ($block->getFullId() === $filterblock->getFullId()) + if($block->getFullId() === $filterblock->getFullId()) yield $block; } } @@ -95,23 +105,23 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ - public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator - { - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); - for ($x = (int)floor($centerVec2->x - $this->width / 2 - 1); $x <= floor($centerVec2->x + $this->width / 2 + 1); $x++) { - for ($z = (int)floor($centerVec2->y - $this->depth / 2 - 1); $z <= floor($centerVec2->y + $this->depth / 2 + 1); $z++) { + public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE) : Generator{ + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); + for($x = (int) floor($centerVec2->x - $this->width / 2 - 1); $x <= floor($centerVec2->x + $this->width / 2 + 1); $x++){ + for($z = (int) floor($centerVec2->y - $this->depth / 2 - 1); $z <= floor($centerVec2->y + $this->depth / 2 + 1); $z++){ //TODO hollow yield new Vector2($x, $z); } } } - public function getAABB(): AxisAlignedBB - { + public function getAABB() : AxisAlignedBB{ return new AxisAlignedBB( floor($this->pasteVector->x - $this->width / 2), $this->pasteVector->y, @@ -122,13 +132,11 @@ public function getAABB(): AxisAlignedBB ); } - public function getTotalCount(): int - { - return (int)ceil((1 / 3) * ($this->width * $this->depth) * $this->height); + public function getTotalCount() : int{ + return (int) ceil((1 / 3) * ($this->width * $this->depth) * $this->height); } - public static function getName(): string - { + public static function getName() : string{ return "Pyramid"; } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/selection/shape/Shape.php b/src/xenialdan/MagicWE2/selection/shape/Shape.php index 994cfde7..4e4adfe8 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Shape.php +++ b/src/xenialdan/MagicWE2/selection/shape/Shape.php @@ -69,22 +69,23 @@ public function getMaxVec3(): Vector3 return new Vector3($this->getAABB()->maxX, $this->getAABB()->maxY, $this->getAABB()->maxZ); } - abstract public static function getName(): string; + abstract public static function getName() : string; - public function getShapeProperties(): array - { + public function getShapeProperties() : array{ return array_diff(get_object_vars($this), get_class_vars(__CLASS__)); } + //TODO add Shape::flip() and Shape::rotate() + abstract public function rotate(int $rotation) : self; + /** * String representation of object * @link http://php.net/manual/en/serializable.serialize.php * @return string the string representation of the object or null * @since 5.1.0 */ - public function serialize(): string - { - return serialize((array)$this); + public function serialize() : string{ + return serialize((array) $this); } /** diff --git a/src/xenialdan/MagicWE2/selection/shape/Sphere.php b/src/xenialdan/MagicWE2/selection/shape/Sphere.php index c6f7b020..12e715b3 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Sphere.php +++ b/src/xenialdan/MagicWE2/selection/shape/Sphere.php @@ -28,39 +28,44 @@ public function __construct(Vector3 $pasteVector, int $diameter) $this->diameter = $diameter; } - public function offset(Vector3 $offset): Shape - { + public function offset(Vector3 $offset) : Shape{ $shape = clone $this; - $shape->setPasteVector($this->getPasteVector()->addVector($offset)); + $shape->setPasteVector($this->pasteVector->addVector($offset)); return $shape; } + public function rotate(int $rotation) : self{ + return clone $this; + } + /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Block[]|Generator * @phpstan-return Generator * @noinspection PhpDocSignatureInspection */ - public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Generator + public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks) : Generator { for ($x = (int)floor($this->pasteVector->x - $this->diameter / 2 - 1); $x <= floor($this->pasteVector->x + $this->diameter / 2 + 1); $x++) { for ($y = (int)floor($this->pasteVector->y - $this->diameter / 2 - 1); $y <= floor($this->pasteVector->y + $this->diameter / 2 + 1); $y++) { for ($z = (int)floor($this->pasteVector->z - $this->diameter / 2 - 1); $z <= floor($this->pasteVector->z + $this->diameter / 2 + 1); $z++) { $vec3 = new Vector3($x, $y, $z); -// if ($vec3->distanceSquared($this->getPasteVector()) > (($this->diameter / 2) ** 2) || (API::hasFlag(API::FLAG_BASE, API::FLAG_HOLLOW) && $vec3->distanceSquared($this->getPasteVector()) <= ((($this->diameter / 2) - 1) ** 2))) +// if ($vec3->distanceSquared($this->pasteVector) > (($this->diameter / 2) ** 2) || (API::hasFlag(API::FLAG_BASE, API::FLAG_HOLLOW) && $vec3->distanceSquared($this->pasteVector) <= ((($this->diameter / 2) - 1) ** 2))) // continue; - $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int)$vec3->x, (int)$vec3->y, (int)$vec3->z); + $block = API::setComponents($manager->getBlockAt($vec3->getFloorX(), $vec3->getFloorY(), $vec3->getFloorZ()), (int) $vec3->x, (int) $vec3->y, (int) $vec3->z); // if (API::hasFlag(API::FLAG_BASE, API::FLAG_KEEP_BLOCKS) && $block->getId() !== BlockLegacyIds::AIR) continue; // if (API::hasFlag(API::FLAG_BASE, API::FLAG_KEEP_AIR) && $block->getId() === BlockLegacyIds::AIR) continue; - if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fufufufuuu - if ($filterblocks->empty()) yield $block; - else { - foreach ($filterblocks->palette() as $filterblock) { + if($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < World::Y_MIN) continue;//TODO check if this should be 255 or World::Y_MAX + if($filterblocks->empty()) yield $block; + else{ + foreach($filterblocks->palette() as $filterblock){ // if (($block->getId() === $filterblock->getId()) && ((API::hasFlag($flags, API::FLAG_VARIANT) && $block->getIdInfo()->getVariant() === $filterblock->getIdInfo()->getVariant()) || (!API::hasFlag($flags, API::FLAG_VARIANT) && ($block->getMeta() === $filterblock->getMeta() || API::hasFlag($flags, API::FLAG_KEEP_META))))) - if ($block->getFullId() === $filterblock->getFullId()) + if($block->getFullId() === $filterblock->getFullId()) yield $block; } } @@ -71,13 +76,14 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager - * @param int $flags + * + * @param AsyncWorld $manager The world or AsyncWorld + * @param int $flags + * * @return Generator */ - public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator - { - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); + public function getLayer(AsyncWorld $manager, int $flags = API::FLAG_BASE): Generator{ + $centerVec2 = new Vector2($this->pasteVector->getX(), $this->pasteVector->getZ()); for ($x = (int)floor($centerVec2->x - $this->diameter / 2 - 1); $x <= floor($centerVec2->x + $this->diameter / 2 + 1); $x++) { for ($z = (int)floor($centerVec2->y - $this->diameter / 2 - 1); $z <= floor($centerVec2->y + $this->diameter / 2 + 1); $z++) { $vec2 = new Vector2($x, $z); diff --git a/src/xenialdan/MagicWE2/session/Session.php b/src/xenialdan/MagicWE2/session/Session.php index 4f50881d..c8cd989f 100644 --- a/src/xenialdan/MagicWE2/session/Session.php +++ b/src/xenialdan/MagicWE2/session/Session.php @@ -9,7 +9,6 @@ use pocketmine\lang\Language; use pocketmine\Server; use pocketmine\utils\TextFormat as TF; -use pocketmine\world\World; use Ramsey\Uuid\UuidInterface; use RuntimeException; use SplDoublyLinkedList; @@ -56,15 +55,10 @@ public function setUUID(UuidInterface $uuid): void $this->uuid = $uuid; } - /** - * @param Selection $selection - * @return null|Selection - */ - public function &addSelection(Selection $selection): ?Selection + public function addSelection(Selection $selection) : void//TODO why do i return the same thing? { $this->selections[$selection->getUUID()->toString()] = $selection; $this->setLatestSelectionUUID($selection->getUUID()); - return $this->getLatestSelection(); } /** @@ -224,17 +218,17 @@ public function addRevert(RevertClipboard $revertClipboard): void */ public function undo(): void { - if ($this->undoHistory->count() === 0) { + if($this->undoHistory->count() === 0){ $this->sendMessage(TF::RED . $this->getLanguage()->translateString('session.undo.none')); return; } /** @var RevertClipboard $revertClipboard */ $revertClipboard = $this->undoHistory->pop(); - $world = $revertClipboard->getWorld(); - foreach ($revertClipboard->chunks as $hash => $chunk) { - World::getXZ($hash, $x, $z); - $revertClipboard->chunks[$hash] = $world->getChunk($x, $z); - } +// $world = $revertClipboard->getWorld(); +// foreach ($revertClipboard->chunks as $hash => $chunk) { +// World::getXZ($hash, $x, $z); +// $revertClipboard->chunks[$hash] = $world->getChunk($x, $z); +// } Server::getInstance()->getAsyncPool()->submitTask(new AsyncRevertTask($this->getUUID(), $revertClipboard, AsyncRevertTask::TYPE_UNDO)); $this->sendMessage(TF::GREEN . $this->getLanguage()->translateString('session.undo.left', [count($this->undoHistory)])); } diff --git a/src/xenialdan/MagicWE2/session/UserSession.php b/src/xenialdan/MagicWE2/session/UserSession.php index e51c74dc..0daa8ac9 100644 --- a/src/xenialdan/MagicWE2/session/UserSession.php +++ b/src/xenialdan/MagicWE2/session/UserSession.php @@ -21,6 +21,7 @@ use xenialdan\MagicWE2\session\data\BrushCollection; use xenialdan\MagicWE2\session\data\Outline; use xenialdan\MagicWE2\session\data\PaletteCollection; +use xenialdan\MagicWE2\tool\Debug; use function mkdir; class UserSession extends Session implements JsonSerializable //TODO use JsonMapper @@ -38,6 +39,7 @@ class UserSession extends Session implements JsonSerializable //TODO use JsonMap private AssetCollection $assets; private PaletteCollection $palettes; private ?Language $lang = null; + public ?Debug $debug = null; public function __construct(Player $player) { @@ -62,14 +64,14 @@ public function __construct(Player $player) Loader::getInstance()->getLogger()->debug("Created new session for player {$player->getName()}"); } - /** - * @throws ScoreFactoryException - */ public function __destruct(){ Loader::getInstance()->getLogger()->debug("Destructing session {$this->getUUID()} for user " . $this->getPlayer()->getName()); $this->bossBar->removeAllPlayers(); if(Loader::hasScoreboard() && $this->sidebar !== null){ - ScoreFactory::removeObjective($this->getPlayer(), true); + try{ + ScoreFactory::removeObjective($this->getPlayer(), true); + }catch(ScoreFactoryException){ + } } } @@ -238,11 +240,12 @@ public function cleanupInventory(): void public function __toString() { + //TODO translations return __CLASS__ . " UUID: " . $this->getUUID()->__toString() . " Player: " . $this->getPlayer()->getName() . " Wand tool enabled: " . ($this->isWandEnabled() ? "enabled" : "disabled") . - " Debug tool enabled: " . ($this->isDebugToolEnabled() ? "enabled" : "disabled") . + " Debug stick enabled: " . ($this->isDebugToolEnabled() ? "enabled" : "disabled") . " WAILA enabled: " . ($this->isWailaEnabled() ? "enabled" : "disabled") . " Sidebar enabled: " . ($this->sidebarEnabled ? "enabled" : "disabled") . " Outline enabled: " . ($this->outlineEnabled ? "enabled" : "disabled") . @@ -263,9 +266,10 @@ public function sendMessage(string $message): void $this->player->sendMessage(Loader::PREFIX . $message); } + //TODO use libmarshal to serialize this public function jsonSerialize(): array { - return [ + $return = [ "uuid" => $this->getUUID()->toString(), "wandEnabled" => $this->wandEnabled, "debugToolEnabled" => $this->debugToolEnabled, @@ -276,10 +280,14 @@ public function jsonSerialize(): array //todo assets, palettes "latestSelection" => $this->getLatestSelection(), "currentClipboard" => $this->getCurrentClipboard(), - "language" => $this->getLanguage()->getLang() + "language" => $this->getLanguage()->getLang(), ]; + if($this->debug !== null) $return["debug"] = $this->debug->jsonSerialize(); + #var_dump($return); + return $return; } + //TODO use libmarshal to serialize this public function save(): void { @mkdir(Loader::getInstance()->getDataFolder() . "sessions", 0777, true); diff --git a/src/xenialdan/MagicWE2/session/data/Asset.php b/src/xenialdan/MagicWE2/session/data/Asset.php index 6e3c3307..3a20edce 100644 --- a/src/xenialdan/MagicWE2/session/data/Asset.php +++ b/src/xenialdan/MagicWE2/session/data/Asset.php @@ -66,8 +66,8 @@ public function __construct(string $filename, Schematic|SingleClipboard|MCStruct public function getSize(): Vector3 { - if ($this->structure instanceof Schematic) return new Vector3($this->structure->getWidth(), $this->structure->getHeight(), $this->structure->getLength()); - if ($this->structure instanceof MCStructure) return $this->structure->getSize(); + if($this->structure instanceof Schematic) return new Vector3($this->structure->getWidth(), $this->structure->getHeight(), $this->structure->getLength()); + if($this->structure instanceof MCStructure) return new Vector3($this->structure->getSize()->getX(), $this->structure->getSize()->getY(), $this->structure->getSize()->getZ()); else return new Vector3($this->structure->selection->getSizeX(), $this->structure->selection->getSizeY(), $this->structure->selection->getSizeZ()); //throw new Exception("Unknown structure type"); } @@ -81,8 +81,8 @@ public function getTotalCount(): int public function getOrigin(): Vector3 { - if ($this->structure instanceof Schematic) return new Vector3(0, 0, 0); - if ($this->structure instanceof MCStructure) return $this->structure->getStructureWorldOrigin(); + if($this->structure instanceof Schematic) return new Vector3(0, 0, 0); + if($this->structure instanceof MCStructure) return new Vector3($this->structure->getStructureWorldOrigin()->getX(), $this->structure->getStructureWorldOrigin()->getY(), $this->structure->getStructureWorldOrigin()->getZ()); else return $this->structure->position; //throw new Exception("Unknown structure type"); } diff --git a/src/xenialdan/MagicWE2/session/data/Outline.php b/src/xenialdan/MagicWE2/session/data/Outline.php index cc52578a..2ec010a6 100644 --- a/src/xenialdan/MagicWE2/session/data/Outline.php +++ b/src/xenialdan/MagicWE2/session/data/Outline.php @@ -18,32 +18,30 @@ use xenialdan\libstructure\tile\StructureBlockTile; use xenialdan\MagicWE2\selection\Selection; -class Outline -{ +class Outline{ private Selection $selection; private Player $player; private Position $position; private Block $fakeBlock; private StructureBlockTile $fakeTile; - public function __construct(Selection $selection, Player $player) - { + public function __construct(Selection $selection, Player $player){ $this->selection = $selection; $this->player = $player; - $this->fakeBlock = BlockFactory::getInstance()->get(BlockLegacyIds::STRUCTURE_BLOCK); + /** @var BlockFactory $blockFactory */ + $blockFactory = BlockFactory::getInstance(); + $this->fakeBlock = $blockFactory->get(BlockLegacyIds::STRUCTURE_BLOCK, 0); $this->position = $this->updateBlockPosition(); $this->fakeTile = new StructureBlockTile($this->position->getWorld(), $this->position); $this->fakeTile->setShowBoundingBox(true)->setFromV3($selection->getPos1())->setToV3($selection->getPos2()); $this->send(); } - public function getSelection(): Selection - { + public function getSelection() : Selection{ return $this->selection; } - public function setSelection(Selection $selection): self - { + public function setSelection(Selection $selection) : self{ $this->selection = $selection; $this->remove(); $this->updatePosition(); @@ -54,35 +52,31 @@ public function setSelection(Selection $selection): self return $this; } - public function send(): void - { + public function send() : void{ $this->player->getNetworkSession()->sendDataPacket(UpdateBlockPacket::create(BlockPosition::fromVector3($this->position->asVector3()), RuntimeBlockMapping::getInstance()->toRuntimeId($this->fakeBlock->getFullId()), UpdateBlockPacket::FLAG_NETWORK, UpdateBlockPacket::DATA_LAYER_NORMAL)); - if ($this->fakeTile instanceof Spawnable) { + if($this->fakeTile instanceof Spawnable){ $this->player->getNetworkSession()->sendDataPacket(BlockActorDataPacket::create(BlockPosition::fromVector3($this->position->asVector3()), $this->fakeTile->getSerializedSpawnCompound()), true); } } - public function remove(): void - { + public function remove() : void{ $network = $this->player->getNetworkSession(); $world = $this->player->getWorld(); $runtime_block_mapping = RuntimeBlockMapping::getInstance(); - $block = $world->getBlockAt((int)$this->position->x, (int)$this->position->y, (int)$this->position->z); + $block = $world->getBlockAt((int) $this->position->x, (int) $this->position->y, (int) $this->position->z); $network->sendDataPacket(UpdateBlockPacket::create(BlockPosition::fromVector3($this->position->asVector3()), $runtime_block_mapping->toRuntimeId($block->getFullId()), UpdateBlockPacket::FLAG_NETWORK, UpdateBlockPacket::DATA_LAYER_NORMAL), true); - $tile = $world->getTileAt((int)$this->position->x, (int)$this->position->y, (int)$this->position->z); - if ($tile instanceof Spawnable) { + $tile = $world->getTileAt((int) $this->position->x, (int) $this->position->y, (int) $this->position->z); + if($tile instanceof Spawnable){ $network->sendDataPacket(BlockActorDataPacket::create(BlockPosition::fromVector3($this->position->asVector3()), $tile->getSerializedSpawnCompound()), true); } } - public function __toString(): string - { + public function __toString() : string{ return 'Outline'; } - private function updatePosition(): void - { + private function updatePosition() : void{ $this->position = $this->updateBlockPosition(); $reflectionc = new ReflectionClass($this->fakeTile); $reflection = $reflectionc->getProperty('position'); @@ -90,13 +84,11 @@ private function updatePosition(): void $reflection->setValue($this->fakeTile, $this->position); } - private function updateBlockPosition(): Position - { + private function updateBlockPosition() : Position{ return Position::fromObject($this->player->getPosition()->withComponents(null, $this->player->getPosition()->getWorld()->getMinY(), null)->floor(), $this->player->getWorld()); } - public function getPosition(): Position - { + public function getPosition() : Position{ return $this->position; } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/AsyncActionTask.php b/src/xenialdan/MagicWE2/task/AsyncActionTask.php index 5f19f9d2..05d9195d 100644 --- a/src/xenialdan/MagicWE2/task/AsyncActionTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncActionTask.php @@ -56,6 +56,7 @@ public function __construct(UuidInterface $sessionUUID, Selection $selection, Ta $this->action = $action; $this->newBlocks = $newBlocks; $this->blockFilter = $blockFilter; + $this->manager = $selection->getIterator()->getManager(); try { $session = SessionHelper::getSessionByUUID($sessionUUID); @@ -76,12 +77,12 @@ public function __construct(UuidInterface $sessionUUID, Selection $selection, Ta * @return void * @throws Exception */ - public function onRun(): void - { + public function onRun(): void{ $this->publishProgress(new Progress(0, "Preparing {$this->action::getName()}")); /** @var Selection $selection */ - $selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 + $selection = igbinary_unserialize($this->selection); + $manager = $this->manager; $oldBlocks = new SingleClipboard($this->action->clipboardVector ?? new Vector3(0, 0, 0));//TODO Test if null V3 is ok //TODO test if the vector works $oldBlocks->selection = $selection;//TODO test. Needed to add this so that //paste works after //cut2 @@ -89,11 +90,10 @@ public function onRun(): void $messages = []; //$error = false; /** @var Progress $progress */ - foreach ($this->action->execute($this->sessionUUID, $selection, $changed, $this->newBlocks, $this->blockFilter, $oldBlocks, $messages) as $progress) { + foreach($this->action->execute($this->sessionUUID, $selection, $manager, $changed, $this->newBlocks, $this->blockFilter, $oldBlocks, $messages) as $progress){ $this->publishProgress($progress); } - $manager = $selection->getIterator()->getManager(); $resultChunks = $manager->getChunks(); $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { return $chunk->isTerrainDirty(); @@ -131,11 +131,12 @@ public function onCompletion(): void $changed = $result["changed"]; /** @var Selection $selection */ $selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 - $undoChunks = $selection->getIterator()->getManager()->getChunks(); + $undoChunks = []; $totalCount = $selection->getShape()->getTotalCount(); $world = $selection->getWorld(); foreach ($resultChunks as $hash => $chunk) { World::getXZ($hash, $x, $z); + $undoChunks[$hash] = $world->getChunk($x, $z); $world->setChunk($x, $z, $chunk); } if (!is_null($session)) { diff --git a/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php b/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php index 000c7d78..b1dd1a8e 100644 --- a/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php @@ -8,7 +8,7 @@ use pocketmine\utils\TextFormat as TF; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; -use xenialdan\libblockstate\BlockStatesParser; +use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\Progress; @@ -17,8 +17,10 @@ use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\task\action\ClipboardAction; +use function count; use function igbinary_serialize; use function igbinary_unserialize; +use function var_dump; class AsyncClipboardActionTask extends MWEAsyncTask { @@ -26,9 +28,7 @@ class AsyncClipboardActionTask extends MWEAsyncTask private string $selection; private ClipboardAction $action; private string $clipboard; - - private string $rotPath; - private string $doorRotPath; + private array $rotationData; /** * AsyncClipboardActionTask constructor. @@ -37,26 +37,25 @@ class AsyncClipboardActionTask extends MWEAsyncTask * @param ClipboardAction $action * @param SingleClipboard $clipboard */ - public function __construct(UuidInterface $sessionUUID, Selection $selection, ClipboardAction $action, SingleClipboard $clipboard) - { + public function __construct(UuidInterface $sessionUUID, Selection $selection, ClipboardAction $action, SingleClipboard $clipboard){ $this->start = microtime(true); $this->sessionUUID = $sessionUUID->toString(); $this->selection = igbinary_serialize($selection);//TODO check if needed, $clipboard already holds the selection $this->clipboard = igbinary_serialize($clipboard);//TODO check if this even needs to be serialized $this->action = $action; - $this->rotPath = Loader::getRotFlipPath(); - $this->doorRotPath = Loader::getDoorRotFlipPath(); + $this->rotationData = API::$rotationData; + var_dump(__CLASS__ . " " . __FUNCTION__ . " " . __LINE__ . " " . __FILE__, count($this->rotationData)); - try { + try{ $session = SessionHelper::getSessionByUUID($sessionUUID); - if ($session instanceof UserSession) { + if($session instanceof UserSession){ $player = $session->getPlayer(); /** @var Player $player */ $session->getBossBar()->showTo([$player]); $session->getBossBar()->setTitle("Running {$action::getName()} clipboard action");//TODO better string } - } catch (SessionException $e) { + }catch(SessionException $e){ Loader::getInstance()->getLogger()->logException($e); } } @@ -67,11 +66,10 @@ public function __construct(UuidInterface $sessionUUID, Selection $selection, Cl * @return void * @throws Exception */ - public function onRun(): void - { + public function onRun(): void{ $this->publishProgress(new Progress(0, "Preparing {$this->action::getName()}")); - BlockStatesParser::$doorRotPath = $this->doorRotPath; - BlockStatesParser::$rotPath = $this->rotPath; + + var_dump(__CLASS__ . " " . __FUNCTION__ . " " . __LINE__ . " " . __FILE__, count($this->rotationData)); /** @var Selection $selection */ $selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 @@ -80,7 +78,7 @@ public function onRun(): void $clipboard->selection = $selection;//TODO test. Needed to add this so that //paste works after //cut2 $messages = []; /** @var Progress $progress */ - foreach ($this->action->execute($this->sessionUUID, $selection, $changed, $clipboard, $messages) as $progress) { + foreach($this->action->execute($this->sessionUUID, $selection, $changed, $clipboard, $messages) as $progress){ $this->publishProgress($progress); } //TODO $clipboard->selection shape might change when using rotate. Fix this, so //paste chunks are correct diff --git a/src/xenialdan/MagicWE2/task/AsyncCopyTask.php b/src/xenialdan/MagicWE2/task/AsyncCopyTask.php index aa4384c5..563d4aad 100644 --- a/src/xenialdan/MagicWE2/task/AsyncCopyTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncCopyTask.php @@ -12,6 +12,7 @@ use xenialdan\libblockstate\BlockEntry; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -39,6 +40,7 @@ public function __construct(UuidInterface $sessionUUID, Selection $selection, Ve $this->sessionUUID = $sessionUUID->toString(); $this->selection = igbinary_serialize($selection); $this->offset = $offset->asVector3()->floor(); + $this->manager = $selection->getIterator()->getManager(); } /** @@ -59,7 +61,8 @@ public function onRun(): void $clipboard->selection = $selection; #$clipboard->setCenter(unserialize($this->offset)); $totalCount = $selection->getShape()->getTotalCount(); - $copied = $this->copyBlocks($selection, $clipboard); + $manager = $this->manager; + $copied = $this->copyBlocks($selection, $clipboard, $manager); #$clipboard->setShape($selection->getShape()); #$clipboard->chunks = $manager->getChunks(); $this->setResult(compact("clipboard", "copied", "totalCount")); @@ -71,17 +74,14 @@ public function onRun(): void * @return int * @throws Exception */ - private function copyBlocks(Selection $selection, SingleClipboard $clipboard): int - { - $iterator = $selection->getIterator(); - $manager = $iterator->getManager(); + private function copyBlocks(Selection $selection, SingleClipboard &$clipboard, AsyncWorld &$manager) : int{ $blockCount = $selection->getShape()->getTotalCount(); $i = 0; $lastprogress = 0; $this->publishProgress([0, "Running, copied $i blocks out of $blockCount"]); $min = $selection->getShape()->getMinVec3(); /** @var Block $block */ - foreach ($selection->getShape()->getBlocks($manager, BlockPalette::CREATE()) as $block) { + foreach($selection->getShape()->getBlocks($manager, BlockPalette::CREATE()) as $block){ #var_dump("copy chunk X: " . ($block->getX() >> 4) . " Y: " . ($block->getY() >> 4)); $newv3 = $block->getPosition()->subtractVector($min)->floor(); /** @noinspection PhpInternalEntityUsedInspection */ diff --git a/src/xenialdan/MagicWE2/task/AsyncCountTask.php b/src/xenialdan/MagicWE2/task/AsyncCountTask.php deleted file mode 100644 index 0e23ac1c..00000000 --- a/src/xenialdan/MagicWE2/task/AsyncCountTask.php +++ /dev/null @@ -1,123 +0,0 @@ -start = microtime(true); - $this->sessionUUID = $sessionUUID->toString(); - $this->selection = igbinary_serialize($selection); - $this->filterblocks = $filterblocks; - } - - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun(): void - { - $this->publishProgress([0, "Start"]); - /** @var Selection $selection */ - $selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 - $totalCount = $selection->getShape()->getTotalCount(); - $counts = $this->countBlocks($selection, $this->filterblocks); - $this->setResult(compact("counts", "totalCount")); - } - - /** - * @param Selection $selection - * @param BlockPalette $filterblocks - * @return array - * @throws Exception - */ - private function countBlocks(Selection $selection, BlockPalette $filterblocks): array - { - $manager = $selection->getIterator()->getManager(); - $blockCount = $selection->getShape()->getTotalCount(); - $changed = 0; - $this->publishProgress([0, "Running, counting $changed blocks out of $blockCount"]); - $lastchunkx = $lastchunkz = null; - $lastprogress = 0; - $counts = []; - /** @var Block $block */ - foreach ($selection->getShape()->getBlocks($manager, $filterblocks) as $block) { - if (is_null($lastchunkx) || ($block->getPosition()->x >> 4 !== $lastchunkx && $block->getPosition()->z >> 4 !== $lastchunkz)) { - $lastchunkx = $block->getPosition()->x >> 4; - $lastchunkz = $block->getPosition()->z >> 4; - if (is_null($manager->getChunk($block->getPosition()->x >> 4, $block->getPosition()->z >> 4))) { - #print PHP_EOL . "Not found: " . strval($block->x >> 4) . ":" . strval($block->z >> 4) . PHP_EOL; - continue; - } - } - BlockFactory::getInstance(); - $block1 = $manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()); - $tostring = $block1->getName() . " " . $block1->getId() . ":" . $block1->getMeta(); - if (!array_key_exists($tostring, $counts)) $counts[$tostring] = 0; - $counts[$tostring]++; - $changed++; - $progress = floor($changed / $blockCount * 100); - if ($lastprogress < $progress) {//this prevents spamming packets - $this->publishProgress([$progress, "Running, counting $changed blocks out of $blockCount"]); - $lastprogress = $progress; - } - } - return $counts; - } - - /** - * @throws AssumptionFailedError - */ - public function onCompletion(): void - { - try { - $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); - if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); - $result = $this->getResult(); - $counts = $result["counts"]; - $totalCount = $result["totalCount"]; - $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.count.success', [$this->generateTookString()])); - $session->sendMessage(TF::DARK_AQUA . $session->getLanguage()->translateString('task.count.result', [count($counts), $totalCount])); - uasort($counts, static function ($a, $b) { - if ($a === $b) return 0; - return ($a > $b) ? -1 : 1; - }); - foreach ($counts as $block => $count) { - $session->sendMessage(TF::AQUA . $count . "x | " . round($count / $totalCount * 100) . "% | " . $block); - } - } catch (SessionException $e) { - Loader::getInstance()->getLogger()->logException($e); - } - } -} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/AsyncFillTask.php b/src/xenialdan/MagicWE2/task/AsyncFillTask.php index 61d10a08..7f15ca67 100644 --- a/src/xenialdan/MagicWE2/task/AsyncFillTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncFillTask.php @@ -7,12 +7,6 @@ use InvalidArgumentException; use MultipleIterator; use pocketmine\block\Block; -use pocketmine\block\VanillaBlocks; -use pocketmine\math\Vector3; -use pocketmine\network\mcpe\convert\RuntimeBlockMapping; -use pocketmine\network\mcpe\protocol\types\BlockPosition; -use pocketmine\network\mcpe\protocol\UpdateBlockPacket; -use pocketmine\scheduler\ClosureTask; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; use pocketmine\world\format\Chunk; @@ -24,6 +18,7 @@ use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -52,6 +47,7 @@ public function __construct(UuidInterface $sessionUUID, Selection $selection, Bl $this->selection = igbinary_serialize($selection); //$this->newBlocks = BlockPalette::encode($newBlocks); $this->newBlocks = $newBlocks;//TODO check if serializes + $this->manager = $selection->getIterator()->getManager(); } /** @@ -60,8 +56,7 @@ public function __construct(UuidInterface $sessionUUID, Selection $selection, Bl * @return void * @throws Exception */ - public function onRun(): void - { + public function onRun(): void{ $this->publishProgress([0, "Start"]); /** @var Selection $selection */ @@ -70,10 +65,11 @@ public function onRun(): void ///** @var Block[] $newBlocks */ //$newBlocks = BlockPalette::decode($this->newBlocks);//TODO test pm4 //$oldBlocks = iterator_to_array($this->execute($selection, $manager, $newBlocks, $changed)); - $oldBlocks = iterator_to_array($this->execute($selection, $this->newBlocks, $changed)); + $manager = $this->manager; + $oldBlocks = iterator_to_array($this->execute($selection, $this->newBlocks, $changed, $manager)); - $resultChunks = $selection->getIterator()->getManager()->getChunks(); - $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { + $resultChunks = $manager->getChunks(); + $resultChunks = array_filter($resultChunks, static function(Chunk $chunk){ return $chunk->isTerrainDirty(); }); #$this->setResult(compact("resultChunks", "oldBlocks", "changed")); @@ -85,16 +81,16 @@ public function onRun(): void } /** - * @param Selection $selection + * @param Selection $selection * @param BlockPalette $newBlocks - * @param null|int $changed + * @param null|int $changed + * @param AsyncWorld $manager + * * @return Generator * @throws InvalidArgumentException * @phpstan-return Generator */ - private function execute(Selection $selection, BlockPalette $newBlocks, ?int &$changed): Generator - { - $manager = $selection->getIterator()->getManager(); + private function execute(Selection $selection, BlockPalette $newBlocks, ?int &$changed, AsyncWorld &$manager) : Generator{ $blockCount = $selection->getShape()->getTotalCount(); $lastchunkx = $lastchunkz = null; $lastprogress = 0; @@ -181,48 +177,10 @@ public function onCompletion(): void $undoChunks = $selection->getIterator()->getManager()->getChunks(); $totalCount = $selection->getShape()->getTotalCount(); $world = $selection->getWorld(); - //Awesome instant-render-changes hack - $renderHack = []; foreach($resultChunks as $hash => $chunk){ World::getXZ($hash, $x, $z); $world->setChunk($x, $z, $chunk); - for($y = World::Y_MIN; $y < World::Y_MAX; $y += Chunk::EDGE_LENGTH){ - $vector3 = new Vector3($x * 16, 0, $z * 16); - $renderHack[] = $vector3; - //$pk = UpdateBlockPacket::create($blockPosition, $fullId,0b1111, UpdateBlockPacket::DATA_LAYER_NORMAL); - /*Loader::getInstance()->getScheduler()->scheduleDelayedTask(new ClosureTask(function () use ($pk,$vector3,$world):void{ - $world->broadcastPacketToViewers($vector3,$pk); - }),5);*/ - } } - $hack1 = []; - foreach($renderHack as $value) - $hack1[] = UpdateBlockPacket::create( - BlockPosition::fromVector3($value), - RuntimeBlockMapping::getInstance()->toRuntimeId(VanillaBlocks::AIR()->getFullId()), - UpdateBlockPacket::FLAG_NETWORK, - UpdateBlockPacket::DATA_LAYER_NORMAL - ); - $hack2 = $world->createBlockUpdatePackets($renderHack); - //Awesome instant-render-changes hack - Loader::getInstance()->getScheduler()->scheduleDelayedTask(new ClosureTask(function() use ($hack1, $world) : void{ - $world->getServer()->broadcastPackets($world->getPlayers(), $hack1); - }), 1); - Loader::getInstance()->getScheduler()->scheduleDelayedTask(new ClosureTask(function() use ($hack2, $world) : void{ - $world->getServer()->broadcastPackets($world->getPlayers(), $hack2); - }), 2); - /*Loader::getInstance()->getScheduler()->scheduleDelayedTask(new ClosureTask(function () use ($renderHack,$world):void{ - $world->getServer()->broadcastPackets($world->getPlayers(),$world->createBlockUpdatePackets($renderHack)); - }),1); - Loader::getInstance()->getScheduler()->scheduleDelayedTask(new ClosureTask(function () use ($renderHack,$world):void{ - $world->getServer()->broadcastPackets($world->getPlayers(),$world->createBlockUpdatePackets($renderHack)); - }),5); - Loader::getInstance()->getScheduler()->scheduleDelayedTask(new ClosureTask(function () use ($renderHack,$world):void{ - $world->getServer()->broadcastPackets($world->getPlayers(),$world->createBlockUpdatePackets($renderHack)); - }),10); - Loader::getInstance()->getScheduler()->scheduleDelayedTask(new ClosureTask(function () use ($renderHack,$world):void{ - $world->getServer()->broadcastPackets($world->getPlayers(),$world->createBlockUpdatePackets($renderHack)); - }),15);*/ if(!is_null($session)){ $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.fill.success', [$this->generateTookString(), $changed, $totalCount])); $session->addRevert(new RevertClipboard($selection->worldId, $undoChunks, $oldBlocks)); diff --git a/src/xenialdan/MagicWE2/task/AsyncPasteAssetTask.php b/src/xenialdan/MagicWE2/task/AsyncPasteAssetTask.php index e9aeb2b9..747ff8c7 100644 --- a/src/xenialdan/MagicWE2/task/AsyncPasteAssetTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncPasteAssetTask.php @@ -53,7 +53,7 @@ public function __construct(UuidInterface $sessionUUID, Vector3 $target, Selecti $this->target = $target; #var_dump("paste", $selection->getShape()->getPasteVector(), "cb position", $clipboard->position, "offset", $this->offset, $clipboard); $this->sessionUUID = $sessionUUID->toString(); - + $this->manager = $selection->getIterator()->getManager(); $this->selection = igbinary_serialize($selection); $this->asset = igbinary_serialize($asset); } @@ -72,7 +72,7 @@ public function onRun(): void /** @var Selection $selection */ $selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 - $manager = $selection->getIterator()->getManager(); + $manager = $this->manager; // unset($touchedChunks); //$selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 @@ -110,6 +110,7 @@ private function execute(AsyncWorld $manager, Asset $asset, ?int &$changed): Gen if ($structure instanceof MCStructure) { /** @var Block $block */ foreach ($structure->blocks() as $block) {// [0,0,0 -> sizex,sizey,sizez] + if($block === null) continue; #var_dump($block->getPosition()->asVector3(), $this->pasteVector, $this->selection); $pos = $block->getPosition()->addVector($this->target)->subtract($asset->getSize()->getX() / 2, 0, $asset->getSize()->getZ() / 2); [$block->getPosition()->x, $block->getPosition()->y, $block->getPosition()->z] = [$x, $y, $z] = [$pos->getX(), $pos->getY(), $pos->getZ()]; diff --git a/src/xenialdan/MagicWE2/task/AsyncPasteTask.php b/src/xenialdan/MagicWE2/task/AsyncPasteTask.php index eda85e13..c1763427 100644 --- a/src/xenialdan/MagicWE2/task/AsyncPasteTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncPasteTask.php @@ -5,9 +5,11 @@ use Exception; use Generator; use InvalidArgumentException; +use pocketmine\data\bedrock\BiomeIds; use pocketmine\math\Vector3; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; +use pocketmine\world\format\BiomeArray; use pocketmine\world\format\Chunk; use pocketmine\world\Position; use pocketmine\world\World; @@ -18,6 +20,7 @@ use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\session\UserSession; @@ -35,13 +38,14 @@ class AsyncPasteTask extends MWEAsyncTask * @param SingleClipboard $clipboard * @throws Exception */ - public function __construct(UuidInterface $sessionUUID, SingleClipboard $clipboard) - { + public function __construct(UuidInterface $sessionUUID, SingleClipboard $clipboard, Vector3 $pasteVector){ $this->start = microtime(true); - $this->offset = $clipboard->selection->getShape()->getPasteVector()->addVector($clipboard->position)->floor(); - #var_dump("paste", $selection->getShape()->getPasteVector(), "cb position", $clipboard->position, "offset", $this->offset, $clipboard); + $this->offset = $pasteVector->addVector($clipboard->position); + //FIXME Sometimes not all "paste chunks" are set for some shapes or sizes. Figure out why. Maybe ceil/floor? + $clipboard->selection->getShape()->setPasteVector($this->offset->add($clipboard->selection->getSizeX() / 2, $clipboard->selection->getSizeY() / 2, $clipboard->selection->getSizeZ() / 2)); $this->sessionUUID = $sessionUUID->toString(); $this->clipboard = igbinary_serialize($clipboard); + $this->manager = $clipboard->selection->getIterator()->getManager(); } /** @@ -54,62 +58,53 @@ public function onRun(): void { $this->publishProgress([0, "Start"]); -// $touchedChunks = array_map(static function ($chunk) {//todo add hash as key -// return FastChunkSerializer::deserializeTerrain($chunk); -// }, igbinary_unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 -// unset($touchedChunks); - /** @var SingleClipboard $clipboard */ - $clipboard = igbinary_unserialize($this->clipboard/*, ['allowed_classes' => [SingleClipboard::class]]*/);//TODO test pm4 + $clipboard = igbinary_unserialize($this->clipboard); - $oldBlocks = iterator_to_array($this->execute($clipboard, $changed)); + $manager = $this->manager; + $oldBlocks = iterator_to_array($this->execute($clipboard, $changed, $manager)); - $manager = $clipboard->selection->getIterator()->getManager(); $resultChunks = $manager->getChunks(); - $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { - return $chunk->isTerrainDirty(); - }); +// $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { +// return $chunk->isTerrainDirty(); +// }); $this->setResult(compact("resultChunks", "oldBlocks", "changed")); } /** * @param SingleClipboard $clipboard - * @param null|int $changed + * @param null|int $changed + * @param AsyncWorld $manager + * * @return Generator - * @throws InvalidArgumentException * @phpstan-return Generator + * @throws InvalidArgumentException */ - private function execute(SingleClipboard $clipboard, ?int &$changed): Generator - { - $manager = $clipboard->selection->getIterator()->getManager(); + private function execute(SingleClipboard $clipboard, ?int &$changed, AsyncWorld &$manager) : Generator{ $blockCount = $clipboard->getTotalCount(); $x = $y = $z = null; $lastprogress = 0; - $i = 0; $changed = 0; $this->publishProgress([0, "Running, changed $changed blocks out of $blockCount"]); /** @var BlockEntry $entry */ - foreach ($clipboard->iterateEntries($x, $y, $z) as $entry) { - #var_dump("at cb xyz $x $y $z: $entry"); + foreach($clipboard->iterateEntries($x, $y, $z) as $entry){ $x += $this->offset->getFloorX(); $y += $this->offset->getFloorY(); $z += $this->offset->getFloorZ(); - #var_dump("add offset xyz $x $y $z"); /*if (API::hasFlag($this->flags, API::FLAG_POSITION_RELATIVE)){ $rel = $block->subtract($selection->shape->getPasteVector()); $block = API::setComponents($block,$rel->x,$rel->y,$rel->z);//TODO COPY TO ALL TASKS }*/ $new = $entry->toBlock(); - #$new->position(($pos = Position::fromObject(new Vector3($x, $y, $z)))->getWorld(), $pos->getX(), $pos->getY(), $pos->getZ()); - #$old->position(($pos = Position::fromObject(new Vector3($x, $y, $z)))->getWorld(), $pos->getX(), $pos->getY(), $pos->getZ()); - #var_dump("old", $old, "new", $new); - yield self::singleBlockToData(API::setComponents($manager->getBlockAt($x, $y, $z), (int)$x, (int)$y, (int)$z)); + yield self::singleBlockToData(API::setComponents($manager->getBlockAt($x, $y, $z), (int) $x, (int) $y, (int) $z)); + + if($manager->getChunk($x >> 4, $z >> 4) === null){ + $manager->setChunk($x >> 4, $z >> 4, new Chunk([], BiomeArray::fill(BiomeIds::OCEAN), false)); + } $manager->setBlockAt($x, $y, $z, $new); $changed++; - /// - $i++; - $progress = floor($i / $blockCount * 100); - if ($lastprogress < $progress) {//this prevents spamming packets + $progress = floor($changed / $blockCount * 100); + if($lastprogress < $progress){//this prevents spamming packets $this->publishProgress([$progress, "Running, changed $changed blocks out of $blockCount"]); $lastprogress = $progress; } @@ -133,20 +128,20 @@ public function onCompletion(): void $resultChunks = $result["resultChunks"]; // $undoChunks = array_map(static function ($chunk) { // return FastChunkSerializer::deserializeTerrain($chunk); -// }, igbinary_unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 +// }, igbinary_unserialize($this->touchedChunks)); $oldBlocks = $result["oldBlocks"];//already data array $changed = $result["changed"]; /** @var SingleClipboard $clipboard */ - $clipboard = igbinary_unserialize($this->clipboard/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 + $clipboard = igbinary_unserialize($this->clipboard); $selection = $clipboard->selection; - $undoChunks = $selection->getIterator()->getManager()->getChunks(); + $undoChunks = $selection->getIterator()->getManager()->getChunks();//Should be unmodified $totalCount = $selection->getShape()->getTotalCount(); $world = $selection->getWorld(); - foreach ($resultChunks as $hash => $chunk) { + foreach($resultChunks as $hash => $chunk){ World::getXZ($hash, $x, $z); $world->setChunk($x, $z, $chunk); } - if (!is_null($session)) { + if(!is_null($session)){ $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.paste.success', [$this->generateTookString(), $changed, $totalCount])); $session->addRevert(new RevertClipboard($selection->worldId, $undoChunks, $oldBlocks)); } diff --git a/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php b/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php index 4e0593ec..40f2da99 100644 --- a/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php @@ -16,6 +16,7 @@ use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -42,6 +43,7 @@ public function __construct(UuidInterface $sessionUUID, Selection $selection, Bl { $this->start = microtime(true); $this->sessionUUID = $sessionUUID->toString(); + $this->manager = $selection->getIterator()->getManager(); $this->selection = igbinary_serialize($selection); $this->searchBlocks = $searchBlocks; $this->replaceBlocks = $replaceBlocks; @@ -60,9 +62,9 @@ public function onRun(): void /** @var Selection $selection */ $selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 - $manager = $selection->getIterator()->getManager(); + $manager = $this->manager; - $oldBlocks = iterator_to_array($this->execute($selection, $this->searchBlocks, $this->replaceBlocks, $changed)); + $oldBlocks = iterator_to_array($this->execute($manager, $selection, $this->searchBlocks, $this->replaceBlocks, $changed)); $resultChunks = $manager->getChunks(); $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { @@ -72,16 +74,16 @@ public function onRun(): void } /** - * @param Selection $selection + * @param AsyncWorld $manager + * @param Selection $selection * @param BlockPalette $searchBlocks * @param BlockPalette $replaceBlocks - * @param null|int $changed + * @param null|int $changed + * * @return Generator * @throws InvalidArgumentException */ - private function execute(Selection $selection, BlockPalette $searchBlocks, BlockPalette $replaceBlocks, ?int &$changed): Generator - { - $manager = $selection->getIterator()->getManager(); + private function execute(AsyncWorld &$manager, Selection $selection, BlockPalette $searchBlocks, BlockPalette $replaceBlocks, ?int &$changed) : Generator{ $blockCount = $selection->getShape()->getTotalCount(); $lastchunkx = $lastchunkz = null; $lastprogress = 0; diff --git a/src/xenialdan/MagicWE2/task/AsyncRevertTask.php b/src/xenialdan/MagicWE2/task/AsyncRevertTask.php index ddf57980..5793bb4d 100644 --- a/src/xenialdan/MagicWE2/task/AsyncRevertTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncRevertTask.php @@ -5,6 +5,7 @@ use Exception; use Generator; use InvalidArgumentException; +use pocketmine\Server; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; use pocketmine\world\Position; @@ -13,15 +14,16 @@ use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\exception\SessionException; -use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\session\UserSession; +use function count; use function igbinary_serialize; use function igbinary_unserialize; +use function iterator_to_array; -class AsyncRevertTask extends MWEAsyncTask -{ +class AsyncRevertTask extends MWEAsyncTask{ public const TYPE_UNDO = 0; public const TYPE_REDO = 1; @@ -33,16 +35,24 @@ class AsyncRevertTask extends MWEAsyncTask /** * AsyncRevertTask constructor. - * @param UuidInterface $sessionUUID + * + * @param UuidInterface $sessionUUID * @param RevertClipboard $clipboard - * @param int $type The type of clipboard pasting. + * @param int $type The type of clipboard pasting. */ - public function __construct(UuidInterface $sessionUUID, RevertClipboard $clipboard, int $type = self::TYPE_UNDO) - { + public function __construct(UuidInterface $sessionUUID, RevertClipboard $clipboard, int $type = self::TYPE_UNDO){ $this->sessionUUID = $sessionUUID->toString(); $this->start = microtime(true); $this->clipboard = igbinary_serialize($clipboard); $this->type = $type; + $this->manager = AsyncWorld::fromRevertClipboard($clipboard); + $world = Server::getInstance()->getWorldManager()->getWorld($clipboard->worldId); + foreach($clipboard->chunks as $hash => $chunk){ + World::getXZ($hash, $x, $z); + if($type === self::TYPE_REDO){ + $this->manager->setChunk($x, $z, $world->getChunk($x, $z)); + } + } } /** @@ -51,39 +61,39 @@ public function __construct(UuidInterface $sessionUUID, RevertClipboard $clipboa * @return void * @throws Exception */ - public function onRun(): void - { + public function onRun() : void{ $this->publishProgress([0, "Start"]); /** @var RevertClipboard $clipboard */ $clipboard = igbinary_unserialize($this->clipboard/*, ['allowed_classes' => [RevertClipboard::class]]*/);//TODO test pm4 - $totalCount = count($clipboard->blocksAfter); - $manager = $clipboard::getChunkManager($clipboard->chunks); + $manager = $this->manager; $oldBlocks = []; - if ($this->type === self::TYPE_UNDO) + if($this->type === self::TYPE_UNDO) $oldBlocks = iterator_to_array($this->undoChunks($manager, $clipboard)); - if ($this->type === self::TYPE_REDO) + if($this->type === self::TYPE_REDO){ $oldBlocks = iterator_to_array($this->redoChunks($manager, $clipboard)); + } $chunks = $manager->getChunks(); - $this->setResult(compact("chunks", "oldBlocks", "totalCount")); + $this->setResult([$chunks, $oldBlocks]); } /** - * @param AsyncChunkManager $manager + * @param AsyncWorld $manager * @param RevertClipboard $clipboard + * * @return Generator * @throws InvalidArgumentException * @phpstan-return Generator */ - private function undoChunks(AsyncChunkManager $manager, RevertClipboard $clipboard): Generator - { + private function undoChunks(AsyncWorld &$manager, RevertClipboard $clipboard) : Generator{ $count = count($clipboard->blocksAfter); $changed = 0; $this->publishProgress([0, "Reverted $changed blocks out of $count"]); //$block is "data" array - foreach ($clipboard->blocksAfter as $block) { - yield $block; + foreach($clipboard->blocksAfter as $block){ $block = self::singleDataToBlock($block);//turn data into real block + $original = $manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()); + yield self::singleBlockToData($original, $block->getPosition()); $manager->setBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ(), $block); $changed++; $this->publishProgress([$changed / $count, "Reverted $changed blocks out of $count"]); @@ -91,21 +101,22 @@ private function undoChunks(AsyncChunkManager $manager, RevertClipboard $clipboa } /** - * @param AsyncChunkManager $manager + * @param AsyncWorld $manager * @param RevertClipboard $clipboard + * * @return Generator * @throws InvalidArgumentException * @phpstan-return Generator */ - private function redoChunks(AsyncChunkManager $manager, RevertClipboard $clipboard): Generator - { + private function redoChunks(AsyncWorld &$manager, RevertClipboard $clipboard) : Generator{ $count = count($clipboard->blocksAfter); $changed = 0; $this->publishProgress([0, "Redone $changed blocks out of $count"]); //$block is "data" array - foreach ($clipboard->blocksAfter as $block) { - yield $block; + foreach($clipboard->blocksAfter as $block){ $block = self::singleDataToBlock($block);//turn data into real block + $original = $manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()); + yield self::singleBlockToData($original, $block->getPosition()); $manager->setBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ(), $block); $changed++; $this->publishProgress([$changed / $count, "Redone $changed blocks out of $count"]); @@ -115,29 +126,27 @@ private function redoChunks(AsyncChunkManager $manager, RevertClipboard $clipboa /** * @throws AssumptionFailedError */ - public function onCompletion(): void - { - try { + public function onCompletion() : void{ + try{ $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); - if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); - } catch (SessionException $e) { + if($session instanceof UserSession) $session->getBossBar()->hideFromAll(); + }catch(SessionException $e){ Loader::getInstance()->getLogger()->logException($e); $session = null; } - $result = $this->getResult(); + [$chunks, $oldBlocks] = $this->getResult(); /** @var RevertClipboard $clipboard */ $clipboard = igbinary_unserialize($this->clipboard/*, ['allowed_classes' => [RevertClipboard::class]]*/);//TODO test pm4 - $clipboard->chunks = $result["chunks"]; - $totalCount = $result["totalCount"]; - $changed = count($result["oldBlocks"]); - $clipboard->blocksAfter = $result["oldBlocks"];//already is a array of data + $clipboard->chunks = $chunks; + $totalCount = $changed = count($clipboard->blocksAfter); + $clipboard->blocksAfter = $oldBlocks;//already is a array of data $world = $clipboard->getWorld(); - foreach ($clipboard->chunks as $hash => $chunk) { + foreach($clipboard->chunks as $hash => $chunk){ World::getXZ($hash, $x, $z); $world->setChunk($x, $z, $chunk); } - if (!is_null($session)) { - switch ($this->type) { + if(!is_null($session)){ + switch($this->type){ case self::TYPE_UNDO: { $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.revert.undo.success', [$this->generateTookString(), $changed, $totalCount])); diff --git a/src/xenialdan/MagicWE2/task/MWEAsyncTask.php b/src/xenialdan/MagicWE2/task/MWEAsyncTask.php index a0d51f32..1c81fa39 100644 --- a/src/xenialdan/MagicWE2/task/MWEAsyncTask.php +++ b/src/xenialdan/MagicWE2/task/MWEAsyncTask.php @@ -8,6 +8,7 @@ use pocketmine\world\Position; use Ramsey\Uuid\Uuid; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\session\UserSession; @@ -16,6 +17,7 @@ abstract class MWEAsyncTask extends AsyncTask { public string $sessionUUID; public float $start; + public AsyncWorld $manager; public function onProgressUpdate($progress): void { @@ -84,12 +86,15 @@ protected static function singleDataToBlock(array $data): Block * @param array $hackedBlockData * @return Block[] */ - public static function multipleDataToBlocks(array $hackedBlockData): array - { + public static function multipleDataToBlocks(array $hackedBlockData) : array{ $a = []; - foreach ($hackedBlockData as $datum) { + foreach($hackedBlockData as $datum){ $a[] = self::singleDataToBlock($datum); } return $a; } + + public function onError() : void{ + if($this->isCrashed()) SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID))->sendMessage("An error occurred while executing this task: Task crashed. Check console for details."); + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/ClipboardAction.php b/src/xenialdan/MagicWE2/task/action/ClipboardAction.php index fdf36b29..0adc381c 100644 --- a/src/xenialdan/MagicWE2/task/action/ClipboardAction.php +++ b/src/xenialdan/MagicWE2/task/action/ClipboardAction.php @@ -28,7 +28,7 @@ abstract class ClipboardAction * @param string[] $messages * @return Generator */ - abstract public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard $clipboard, array &$messages = []): Generator; + abstract public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard &$clipboard, array &$messages = []) : Generator; abstract public static function getName(): string; diff --git a/src/xenialdan/MagicWE2/task/action/CountAction.php b/src/xenialdan/MagicWE2/task/action/CountAction.php index 22083ff1..e2dfa392 100644 --- a/src/xenialdan/MagicWE2/task/action/CountAction.php +++ b/src/xenialdan/MagicWE2/task/action/CountAction.php @@ -9,6 +9,7 @@ use pocketmine\block\BlockFactory; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; @@ -30,26 +31,19 @@ public static function getName(): string } /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param BlockPalette $newBlocks - * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator + * * @throws Exception */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator - { - $manager = $selection->getIterator()->getManager(); + public function execute(string $sessionUUID, Selection $selection, AsyncWorld &$manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []) : Generator{ $changed = 0; #$oldBlocks = []; $count = $selection->getShape()->getTotalCount(); $lastProgress = new Progress(0, ""); $counts = []; BlockFactory::getInstance(); - foreach ($selection->getShape()->getBlocks($manager, $newBlocks) as $block) { + foreach($selection->getShape()->getBlocks($manager, $newBlocks) as $block){ $block1 = $manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()); $tostring = $block1->getName() . " " . $block1->getId() . ":" . $block1->getMeta(); if (!array_key_exists($tostring, $counts)) $counts[$tostring] = 0; diff --git a/src/xenialdan/MagicWE2/task/action/CutAction.php b/src/xenialdan/MagicWE2/task/action/CutAction.php index a15a10a9..b28251c5 100644 --- a/src/xenialdan/MagicWE2/task/action/CutAction.php +++ b/src/xenialdan/MagicWE2/task/action/CutAction.php @@ -9,6 +9,7 @@ use pocketmine\block\Block; use xenialdan\libblockstate\BlockEntry; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; @@ -32,40 +33,35 @@ public static function getName(): string } /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param BlockPalette $newBlocks - * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator + * * @throws InvalidArgumentException */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator - { - $manager = $selection->getIterator()->getManager(); + public function execute(string $sessionUUID, Selection $selection, AsyncWorld &$manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []) : Generator{ $changed = 0; $i = 0; #$oldBlocks = []; $count = $selection->getShape()->getTotalCount(); $lastProgress = new Progress(0, ""); $min = $selection->getShape()->getMinVec3(); - foreach ($selection->getShape()->getBlocks($manager, $blockFilter) as $block) {//TODO Merged iterator + foreach($selection->getShape()->getBlocks($manager, $blockFilter) as $block){//TODO Merged iterator /** @var Block $new */ $new = $newBlocks->blocks()->current();//TODO Merged iterator - if ($new->getId() === $block->getId() && $new->getMeta() === $block->getMeta()) continue;//skip same blocks + /** @noinspection PhpInternalEntityUsedInspection */ + if($new->getFullId() === $block->getFullId()) continue;//skip same blocks #$oldBlocks[] = API::setComponents($manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()),$block->x, $block->y, $block->z); - $newv3 = $block->getPosition()->subtractVector($min)->floor();//TODO check if only used for clipboard + #$newv3 = $block->getPosition()->subtractVector($min)->floor();//TODO check if only used for clipboard + $newv3 = $block->getPosition()->asVector3(); $oldBlocksSingleClipboard->addEntry($newv3->getFloorX(), $newv3->getFloorY(), $newv3->getFloorZ(), BlockEntry::fromBlock($block)); $manager->setBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ(), $new); /** @noinspection PhpInternalEntityUsedInspection */ - if ($manager->getBlockFullIdAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()) !== $block->getFullId()) { + if($manager->getBlockFullIdAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()) !== $block->getFullId()){ $changed++; } $i++; $progress = new Progress($i / $count, "Changed $changed blocks out of $count"); - if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { + if(floor($progress->progress * 100) > floor($lastProgress->progress * 100)){ yield $progress; $lastProgress = $progress; } diff --git a/src/xenialdan/MagicWE2/task/action/FlipAction.php b/src/xenialdan/MagicWE2/task/action/FlipAction.php index b15712c8..6f0694a4 100644 --- a/src/xenialdan/MagicWE2/task/action/FlipAction.php +++ b/src/xenialdan/MagicWE2/task/action/FlipAction.php @@ -46,8 +46,7 @@ public static function getName(): string * @return Generator * @throws Exception */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard $clipboard, array &$messages = []): Generator - { + public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard &$clipboard, array &$messages = []) : Generator{ //TODO modify position. For now, just flip the blocks around their own axis $changed = 0; #$oldBlocks = []; diff --git a/src/xenialdan/MagicWE2/task/action/RotateAction.php b/src/xenialdan/MagicWE2/task/action/RotateAction.php index 39f679e7..8e6d65b7 100644 --- a/src/xenialdan/MagicWE2/task/action/RotateAction.php +++ b/src/xenialdan/MagicWE2/task/action/RotateAction.php @@ -8,32 +8,28 @@ use Generator; use InvalidArgumentException; use pocketmine\block\BlockFactory; -use pocketmine\math\Vector3; -use xenialdan\libblockstate\BlockEntry; +use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; -use xenialdan\MagicWE2\selection\shape\Cuboid; class RotateAction extends ClipboardAction { public const ROTATE_90 = 90; public const ROTATE_180 = 180; public const ROTATE_270 = 270; - /** @var bool */ public bool $addClipboard = true; - /** @var string */ public string $completionString = '{%name} succeed, took {%took}, rotated {%changed} blocks out of {%total}'; - /** @var int */ private int $rotation; - /** @var bool */ public bool $aroundOrigin = true; + public array $rotationData = []; public function __construct(int $rotation, bool $aroundOrigin = true) { if ($rotation !== self::ROTATE_90 && $rotation !== self::ROTATE_180 && $rotation !== self::ROTATE_270) throw new InvalidArgumentException("Invalid rotation $rotation given"); $this->rotation = $rotation; $this->addClipboard = $aroundOrigin; + $this->rotationData = API::$rotationData; } public static function getName(): string @@ -50,96 +46,16 @@ public static function getName(): string * @return Generator * @throws Exception */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard $clipboard, array &$messages = []): Generator - { - //TODO modify position. For now, just flip the blocks around their own axis + public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard &$clipboard, array &$messages = []) : Generator{ + //TODO modify clipboard position / add relative rotation. For now, just rotate the blocks around their center $changed = 0; - #$oldBlocks = []; - $count = $selection->getShape()->getTotalCount(); - $lastProgress = new Progress(0, ""); + $count = $clipboard->getTotalCount(); + yield new Progress(0, ""); BlockFactory::getInstance(); -// /** @var BlockStatesParser $blockStatesParser */ -// $blockStatesParser = BlockStatesParser::getInstance(); - $clonedClipboard = clone $clipboard; - $clonedClipboard->clear(); - //$x = $y = $z = null; - $maxX = $clipboard->selection->getSizeX() - 1; - $maxZ = $clipboard->selection->getSizeZ() - 1; - foreach ($clipboard->iterateEntries($x, $y, $z) as $blockEntry) {//only fully works if xyz is positive //TODO make sure this is always positive, see next comment - #var_dump("$x $y $z"); - $newX = $x; - $newZ = $z; - //TODO if aroundOrigin is true (or false, unsure right now), modify the paste vector instead, and always keep the blocks in the positive range? - if ($this->rotation === self::ROTATE_90) { - $newX = -$z; - $newZ = $x; - if ($this->aroundOrigin) { - $newX += $maxZ; - } - } - if ($this->rotation === self::ROTATE_180) { - $newX = -$x; - $newZ = -$z; - if ($this->aroundOrigin) { - $newX += $maxX; - $newZ += $maxZ; - } - } - if ($this->rotation === self::ROTATE_270) { - $newX = $z; - $newZ = -$x; - if ($this->aroundOrigin) { - $newZ += $maxX; - } - } - #var_dump("$newX $y $newZ"); - $block1 = $blockEntry->toBlock(); - #$blockStatesEntry = $instance->getFromBlock($block1); - $block = $block1; - //TODO re-add flip/rotate in libblockstate -// $rotated = $blockStatesEntry->rotate($this->rotation); -// $block = $rotated->toBlock(); - $entry = BlockEntry::fromBlock($block); - #var_dump($blockStatesEntry->__toString(), $rotated->__toString(), $entry); - /** @var int $y */ - $clonedClipboard->addEntry($newX, $y, $newZ, $entry); - $changed++; - $progress = new Progress($changed / $count, "$changed/$count"); - if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { - yield $progress; - $lastProgress = $progress; - } - } + API::setRotationData($this->rotationData); + $errors = []; + $clipboard = API::rotate($clipboard, $this->rotation, $errors); - $clonedSelection = $clonedClipboard->selection; - $pos1 = $clonedSelection->pos1; - $pos2 = $clonedSelection->pos2; - if ($this->rotation === self::ROTATE_90) {//TODO rewrite to be cleaner - #$pos2 = $pos2->setComponents($pos1->x, $pos2->y, $pos1->z + $maxX); - $pos2 = new Vector3($pos1->x, $pos2->y, $pos1->z + $maxX); - $pos1 = $pos1->subtract($maxZ, 0, 0); - if ($this->aroundOrigin) { - $pos1 = $pos1->add($maxZ, 0, 0); - $pos2 = $pos2->add($maxZ, 0, 0); - } - } - if ($this->rotation === self::ROTATE_180) { - if (!$this->aroundOrigin) { - $pos1 = $pos1->subtract($maxX, 0, $maxZ); - $pos2 = $pos2->subtract($maxX, 0, $maxZ); - } - } - if ($this->rotation === self::ROTATE_270) {//TODO rewrite to be cleaner - #$pos2 = $pos2->setComponents($pos1->x + $maxZ, $pos2->y, $pos1->z); - $pos2 = new Vector3($pos1->x + $maxZ, $pos2->y, $pos1->z); - $pos1 = $pos1->subtract(0, 0, $maxX); - if ($this->aroundOrigin) { - $pos1 = $pos1->add(0, 0, $maxX); - $pos2 = $pos2->add(0, 0, $maxX); - } - } - $clonedSelection->shape = (Cuboid::constructFromPositions($pos1, $pos2));//TODO figure out how to keep the shape (not always Cuboid) - $clonedClipboard->selection = $clonedSelection; - //$clipboard = $clonedClipboard; + yield new Progress($changed / $count, "$changed/$count"); } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/SetBiomeAction.php b/src/xenialdan/MagicWE2/task/action/SetBiomeAction.php index 4a405800..d6e61d54 100644 --- a/src/xenialdan/MagicWE2/task/action/SetBiomeAction.php +++ b/src/xenialdan/MagicWE2/task/action/SetBiomeAction.php @@ -8,6 +8,7 @@ use Generator; use pocketmine\math\Vector2; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; @@ -30,26 +31,19 @@ public static function getName(): string } /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param BlockPalette $newBlocks - * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator + * * @throws Exception */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator - { - $manager = $selection->getIterator()->getManager(); + public function execute(string $sessionUUID, Selection $selection, AsyncWorld &$manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []) : Generator{ $changed = 0; #$oldBlocks = []; $count = null; $lastProgress = new Progress(0, ""); /** @var Vector2 $vec2 */ - foreach (($all = $selection->getShape()->getLayer($manager)) as $vec2) { - if (is_null($count)) $count = count(iterator_to_array($all)); + foreach(($all = $selection->getShape()->getLayer($manager)) as $vec2){ + if(is_null($count)) $count = count(iterator_to_array($all)); $manager->getChunk($vec2->x >> 4, $vec2->y >> 4)->setBiomeId(abs($vec2->x % 16), abs($vec2->y % 16), $this->biomeId); $changed++; $progress = new Progress($changed / $count, "Changed Biome for $changed/$count blocks"); diff --git a/src/xenialdan/MagicWE2/task/action/SetBlockAction.php b/src/xenialdan/MagicWE2/task/action/SetBlockAction.php index 8b040979..982e6a3d 100644 --- a/src/xenialdan/MagicWE2/task/action/SetBlockAction.php +++ b/src/xenialdan/MagicWE2/task/action/SetBlockAction.php @@ -9,6 +9,7 @@ use pocketmine\block\Block; use xenialdan\libblockstate\BlockEntry; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; @@ -26,28 +27,21 @@ public static function getName(): string } /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param BlockPalette $newBlocks - * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator + * * @throws InvalidArgumentException */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator - { - $manager = $selection->getIterator()->getManager(); + public function execute(string $sessionUUID, Selection $selection, AsyncWorld &$manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []) : Generator{ $changed = 0; $i = 0; #$oldBlocks = []; $count = $selection->getShape()->getTotalCount(); $lastProgress = new Progress(0, ""); - foreach ($selection->getShape()->getBlocks($manager, $blockFilter) as $block) {//TODO merge iterator + foreach($selection->getShape()->getBlocks($manager, $blockFilter) as $block){//TODO merge iterator /** @var Block $new */ $new = $newBlocks->blocks()->current();//TODO merge iterator - if ($new->getId() === $block->getId() && $new->getMeta() === $block->getMeta()) continue;//skip same blocks + if($new->getId() === $block->getId() && $new->getMeta() === $block->getMeta()) continue;//skip same blocks #$oldBlocks[] = API::setComponents($manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()),$block->x, $block->y, $block->z); $oldBlocksSingleClipboard->addEntry($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ(), BlockEntry::fromBlock($block)); $manager->setBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ(), $new); diff --git a/src/xenialdan/MagicWE2/task/action/TaskAction.php b/src/xenialdan/MagicWE2/task/action/TaskAction.php index 64977790..92aa6ce7 100644 --- a/src/xenialdan/MagicWE2/task/action/TaskAction.php +++ b/src/xenialdan/MagicWE2/task/action/TaskAction.php @@ -7,42 +7,37 @@ use Generator; use pocketmine\math\Vector3; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\selection\Selection; abstract class TaskAction { - /** @var string */ public string $prefix = ""; - /** @var bool */ public bool $addRevert = true; - /** @var string */ public string $completionString = '{%name} succeed, took {%took}, {%changed} blocks out of {%total} changed.'; - /** @var bool */ public bool $addClipboard = false; - /** @var null|Vector3 */ public ?Vector3 $clipboardVector = null; //TODO add $flags and define available flags in child classes //public $flags //protected const AVAILABLE_FLAGS = [];(can be overwritten), access with static::AVAILABLE_FLAGS /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param BlockPalette $newBlocks - * @param BlockPalette $blockFilter + * @param string $sessionUUID + * @param Selection $selection + * @param AsyncWorld $manager + * @param null|int $changed + * @param BlockPalette $newBlocks + * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change - * @param string[] $messages + * @param string[] $messages + * * @return Generator */ - abstract public function execute(string $sessionUUID, Selection $selection, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator; + abstract public function execute(string $sessionUUID, Selection $selection, AsyncWorld &$manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []) : Generator; abstract public static function getName(): string; - /** - * @param Vector3|null $clipboardVector - */ public function setClipboardVector(?Vector3 $clipboardVector): void { if ($clipboardVector instanceof Vector3) $clipboardVector = $clipboardVector->asVector3()->floor(); diff --git a/src/xenialdan/MagicWE2/task/action/TestAction.php b/src/xenialdan/MagicWE2/task/action/TestAction.php index 3d71dc81..22fd64b0 100644 --- a/src/xenialdan/MagicWE2/task/action/TestAction.php +++ b/src/xenialdan/MagicWE2/task/action/TestAction.php @@ -8,6 +8,7 @@ use Generator; use pocketmine\block\BlockFactory; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; @@ -29,25 +30,18 @@ public static function getName(): string } /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param BlockPalette $newBlocks - * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator + * * @throws Exception */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator - { - $manager = $selection->getIterator()->getManager(); + public function execute(string $sessionUUID, Selection $selection, AsyncWorld &$manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []) : Generator{ $changed = 0; #$oldBlocks = []; $count = $selection->getShape()->getTotalCount(); $lastProgress = new Progress(0, ""); BlockFactory::getInstance(); - foreach ($selection->getShape()->getBlocks($manager, $blockFilter) as $block) { + foreach($selection->getShape()->getBlocks($manager, $blockFilter) as $block){ $changed++; $messages[] = $block->getPosition()->asVector3()->__toString() . " " . $block->getName(); $progress = new Progress($changed / $count, "$changed/$count"); diff --git a/src/xenialdan/MagicWE2/task/action/ThawAction.php b/src/xenialdan/MagicWE2/task/action/ThawAction.php index 0a1dd55c..9a67bb2c 100644 --- a/src/xenialdan/MagicWE2/task/action/ThawAction.php +++ b/src/xenialdan/MagicWE2/task/action/ThawAction.php @@ -9,6 +9,7 @@ use pocketmine\block\VanillaBlocks; use xenialdan\libblockstate\BlockEntry; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; @@ -26,19 +27,12 @@ public static function getName(): string } /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param BlockPalette $newBlocks - * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator + * * @throws InvalidArgumentException */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator - { - $manager = $selection->getIterator()->getManager(); + public function execute(string $sessionUUID, Selection $selection, AsyncWorld &$manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []) : Generator{ $changed = 0; $i = 0; #$oldBlocks = []; @@ -47,7 +41,7 @@ public function execute(string $sessionUUID, Selection $selection, ?int &$change $blockFilterA = [VanillaBlocks::SNOW_LAYER(), VanillaBlocks::SNOW(), VanillaBlocks::ICE()]; $newBlocksA = [VanillaBlocks::AIR(), VanillaBlocks::AIR(), VanillaBlocks::WATER()]; - foreach ($blockFilterA as $ib => $blockF) { + foreach($blockFilterA as $ib => $blockF){ foreach ($selection->getShape()->getBlocks($manager, BlockPalette::CREATE()) as $block) {//TODO merged generator iterating blocks and newblocks $new = clone $newBlocksA[$ib]; #$oldBlocks[] = API::setComponents($manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()),$block->x, $block->y, $block->z); diff --git a/src/xenialdan/MagicWE2/tool/Brush.php b/src/xenialdan/MagicWE2/tool/Brush.php index 78742ad1..b4236da4 100644 --- a/src/xenialdan/MagicWE2/tool/Brush.php +++ b/src/xenialdan/MagicWE2/tool/Brush.php @@ -49,6 +49,7 @@ class Brush extends WETool { public const TAG_BRUSH_ID = "id"; public const TAG_BRUSH_PROPERTIES = "properties"; + public const TAG_BRUSH_VERSION = "version"; /** @var BrushProperties */ public BrushProperties $properties; @@ -85,9 +86,9 @@ public function toItem(): Item if (!is_string($properties)) throw new InvalidArgumentException("Brush properties could not be decoded"); $item->getNamedTag()->setTag(API::TAG_MAGIC_WE_BRUSH, CompoundTag::create() - ->setString("id", $uuid) - ->setInt("version", $this->properties->version) - ->setString("properties", $properties) + ->setString(self::TAG_BRUSH_ID, $uuid) + ->setInt(self::TAG_BRUSH_VERSION, $this->properties->version) + ->setString(self::TAG_BRUSH_PROPERTIES, $properties) ); $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . $this->getName()); $item->setLore($this->properties->generateLore()); diff --git a/src/xenialdan/MagicWE2/tool/Debug.php b/src/xenialdan/MagicWE2/tool/Debug.php new file mode 100644 index 00000000..a7c2bd30 --- /dev/null +++ b/src/xenialdan/MagicWE2/tool/Debug.php @@ -0,0 +1,199 @@ +getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE) === null) throw new NoSuchTagException("Tag " . API::TAG_MAGIC_WE . " not found"); + if($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_DEBUG) === null) throw new NoSuchTagException("Tag " . API::TAG_MAGIC_WE_DEBUG . " not found"); + $compoundTag = $item->getNamedTag()->getCompoundTag(self::TAG_DEBUG_PROPERTY); + if($compoundTag !== null){ + //blockIdentifier is the namespaced name of the block + //state is the most recently modified state of the block + foreach($compoundTag->getValue() as $blockIdentifier => $state){ + $possibleBlockstates = Loader::getInstance()->getPossibleBlockstates($state->getValue()); + if(!empty($possibleBlockstates)){ + $debug->states[$blockIdentifier][$state->getValue()] = $possibleBlockstates;//TODO + } + } + } + return $debug; + } + + /** + * @throws TypeError + */ + public function toItem(Language $lang) : Item{ + $item = VanillaItems::STICK() + ->addEnchantment(new EnchantmentInstance(Loader::$ench)) + ->setCustomName(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . $lang->translateString('tool.debug')) + ->setLore([ + TF::RESET . $lang->translateString('tool.debug.lore.1'),//TODO change lore strings in other languages than english + TF::RESET . $lang->translateString('tool.debug.lore.2'), + TF::RESET . $lang->translateString('tool.debug.lore.3'), + TF::RESET . $lang->translateString('tool.debug.lore.4') + ]); + $compound = CompoundTag::create(); + foreach($this->states as $blockIdentifier => $state){ + $compound->setString($blockIdentifier, (string) key($state));//TODO + } + #var_dump($compound); + $item->getNamedTag()->setTag(API::TAG_MAGIC_WE_DEBUG, CompoundTag::create()); + $item->getNamedTag()->setTag(API::TAG_MAGIC_WE, CompoundTag::create()); + $item->getNamedTag()->setTag(self::TAG_DEBUG_PROPERTY, $compound); + #var_dump($item->getNamedTag()); + return $item; + } + + public function getCurrentState(string $blockIdentifier) : ?string{ + if(array_key_exists($blockIdentifier, $this->states)){ + return (string) ($this->states[$blockIdentifier]); + } + return null; + } + + public function advanceState(string $blockIdentifier, bool $reverse = false) : ?string{ + if(array_key_exists($blockIdentifier, $this->states)){ + return $reverse ? ArrayUtils::regressWrap($this->states[$blockIdentifier])[0] : ArrayUtils::advanceWrap($this->states[$blockIdentifier])[0]; + } + return null; + } + + public function getName() : string{ + return "Debug";//TODO Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . $lang->translateString('tool.debug') + } + + /** + * @throws UnexpectedTagTypeException + * @throws InvalidArgumentException + * @throws AssumptionFailedError + */ + public function useSecondary(UserSession $session, Block $block) : void{ + //cycle values + /** @var BlockStatesParser $blockStatesParser */ + $blockStatesParser = BlockStatesParser::getInstance(); + $blockState = $blockStatesParser->getFromBlock($block); + $stringId = $blockState->state->getId(); + + #var_dump($this->states, $stringId); + #var_dump(Loader::getInstance()->getPossibleBlockstates($stringId)); + + $stateName = $this->getCurrentState($stringId); + #var_dump($current); + + if($stateName === null){ + $session->sendMessage(TF::RED . "States uninitialized, left click the block first"); + return; + } + + //$possibleBlockstates = Loader::getInstance()->getPossibleBlockstates($stringId); + $blockStateTag = $blockState->state->getBlockState()->getCompoundTag("states")->getTag($stateName); + #var_dump(__LINE__, $blockStateTag); + $possibleBlockstates = $this->states[$stringId][$stateName]; + #var_dump(__LINE__, $possibleBlockstates, current($possibleBlockstates)); + ArrayUtils::setPointerToValue($possibleBlockstates, $blockStateTag->getValue()); + #var_dump(__LINE__, $possibleBlockstates, current($possibleBlockstates)); + //TODO add sneaking to reverse + $session->getPlayer()->isSneaking() ? ArrayUtils::regressWrap($possibleBlockstates) : ArrayUtils::advanceWrap($possibleBlockstates); + $newValue = current($possibleBlockstates); + #var_dump($next); + try{ + $newBlockState = $blockState->replaceBlockStateValues([$stateName => $newValue]); + $block->getPosition()->getWorld()->setBlock($block->getPosition(), $newBlockState->getBlock()); + $session->getPlayer()->sendMessage(TF::GREEN . "State changed to " . $newValue); + }catch(UnexpectedTagTypeException | BlockQueryParsingFailedException $e){ + $session->sendMessage(TF::RED . "Error occurred whilst changing $stateName to " . $newValue . ": " . $e->getMessage()); + } + } + + public function usePrimary(UserSession $session, Block $block) : void{ + //cycle states + /** @var BlockStatesParser $blockStatesParser */ + $blockStatesParser = BlockStatesParser::getInstance(); + $blockState = $blockStatesParser->getFromBlock($block); + $stringId = $blockState->state->getId(); + + #var_dump($this->getCurrentState($stringId)); + #var_dump(__LINE__, $this->states); + //$array = ($this->states[$stringId] ??= $this->stateToArray($blockState->state->getBlockState()->getCompoundTag("states"))); + $cstate = $blockState->state->getBlockState()->getCompoundTag("states"); + if($cstate === null || $cstate->count() === 0){ + $session->sendMessage(TF::RED . "Block has no states"); + return; + } + $this->states[$stringId] ??= $this->stateToArray($cstate); + $array = &$this->states[$stringId]; + #var_dump(__LINE__, $this->states); + #var_dump($this->getCurrentState($stringId)); + //advance state + $session->getPlayer()->isSneaking() ? ArrayUtils::regressWrap($array) : ArrayUtils::advanceWrap($array); + #$session->getPlayer()->sendTip("Targeted blockstate: " . $this->getCurrentState($stringId)); + $session->getPlayer()->sendActionBarMessage("Targeted blockstate: " . $this->getCurrentState($stringId)); + } + + private function stateToArray(CompoundTag $cstate) : array{ + $array = []; + foreach($cstate->getValue() as $state => $value2){ + $possibleBlockstates = Loader::getInstance()->getPossibleBlockstates($state); + #var_dump(__LINE__, $state, $value2, $possibleBlockstates); + ArrayUtils::setPointerToValue($possibleBlockstates, $value2->getValue()); + $array[$state] = $possibleBlockstates; + } + #var_dump($array); + return $array; + } + + //TODO add rightClickAir and leftClickAir to Tool + public function jsonSerialize() : array{ + return $this->toItem(Loader::getInstance()->getLanguage())->jsonSerialize(); + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/tool/Flood.php b/src/xenialdan/MagicWE2/tool/Flood.php index ec07868e..2e55b41f 100644 --- a/src/xenialdan/MagicWE2/tool/Flood.php +++ b/src/xenialdan/MagicWE2/tool/Flood.php @@ -8,10 +8,8 @@ use pocketmine\math\Facing; use pocketmine\math\Vector2; use pocketmine\math\Vector3; -use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; use pocketmine\world\World; -use xenialdan\MagicWE2\helper\AsyncChunkManager; use xenialdan\MagicWE2\helper\AsyncWorld; use xenialdan\MagicWE2\helper\BlockPalette; @@ -35,8 +33,10 @@ public function __construct(int $limit) /** * Returns the blocks by their actual position - * @param AsyncWorld $manager The world or AsyncChunkManager + * + * @param AsyncWorld $manager The world or AsyncWorld * @param BlockPalette $filterblocks If not empty, applying a filter on the block list + * * @return Generator * @throws InvalidArgumentException */ @@ -54,7 +54,7 @@ public function getBlocks(AsyncWorld $manager, BlockPalette $filterblocks): Gene /** * Returns a flat layer of all included x z positions in selection - * @param AsyncWorld $manager The world or AsyncChunkManager + * @param AsyncWorld $manager The world or AsyncWorld * @return Generator * @throws InvalidArgumentException */ @@ -112,12 +112,11 @@ public function getTotalCount(): int } /** - * @param World|AsyncChunkManager $chunkManager + * @param World|AsyncWorld $chunkManager * @return array * @throws InvalidArgumentException */ - public function getTouchedChunks(AsyncChunkManager|World $chunkManager): array - { + public function getTouchedChunks(AsyncWorld|World $chunkManager) : array{ $this->validateChunkManager($chunkManager); $maxRadius = sqrt($this->limit / M_PI); $v2center = new Vector2($this->getCenter()->x, $this->getCenter()->z); @@ -153,9 +152,8 @@ public function getName(): string * @param mixed $manager * @throws InvalidArgumentException */ - public function validateChunkManager(mixed $manager): void - { - if (!$manager instanceof World && !$manager instanceof AsyncChunkManager) throw new InvalidArgumentException(get_class($manager) . " is not an instance of World or AsyncChunkManager"); + public function validateChunkManager(mixed $manager): void{ + if(!$manager instanceof World && !$manager instanceof AsyncWorld) throw new InvalidArgumentException(get_class($manager) . " is not an instance of World or AsyncWorld"); } private function getCenter(): Vector3 @@ -163,20 +161,4 @@ private function getCenter(): Vector3 //UGLY HACK TO IGNORE ERRORS FOR NOW return new Vector3(0, 0, 0); } - - /** - * Creates a chunk manager used for async editing - * @param Chunk[] $chunks - * @phpstan-param array $chunks - * @return AsyncChunkManager - */ - public static function getChunkManager(array $chunks): AsyncChunkManager - { - $manager = new AsyncChunkManager(0, World::Y_MAX); - foreach ($chunks as $hash => $chunk) { - World::getXZ($hash, $x, $z); - $manager->setChunk($x, $z, $chunk); - } - return $manager; - } } \ No newline at end of file