From f9bf5c1f636dd407026227e2e2c6b0049600678b Mon Sep 17 00:00:00 2001 From: thebigsmileXD Date: Sat, 1 Feb 2020 04:58:11 +0100 Subject: [PATCH] Experimental rewrite of Clipboards - copy & paste Not yet tested! --- src/xenialdan/MagicWE2/API.php | 17 +- .../MagicWE2/clipboard/Clipboard.php | 2 - .../MagicWE2/clipboard/CopyClipboard.php | 190 ------------------ .../MagicWE2/clipboard/RevertClipboard.php | 2 + .../MagicWE2/clipboard/SingleClipboard.php | 75 +++++++ .../commands/clipboard/PasteCommand.php | 4 +- src/xenialdan/MagicWE2/helper/BlockEntry.php | 56 ++++++ .../MagicWE2/task/AsyncClipboardTask.php | 159 --------------- src/xenialdan/MagicWE2/task/AsyncCopyTask.php | 30 ++- .../MagicWE2/task/AsyncPasteTask.php | 165 +++++++++++++++ 10 files changed, 323 insertions(+), 377 deletions(-) delete mode 100644 src/xenialdan/MagicWE2/clipboard/CopyClipboard.php create mode 100644 src/xenialdan/MagicWE2/clipboard/SingleClipboard.php create mode 100644 src/xenialdan/MagicWE2/helper/BlockEntry.php delete mode 100644 src/xenialdan/MagicWE2/task/AsyncClipboardTask.php create mode 100644 src/xenialdan/MagicWE2/task/AsyncPasteTask.php diff --git a/src/xenialdan/MagicWE2/API.php b/src/xenialdan/MagicWE2/API.php index 2f78878b..c48a6472 100644 --- a/src/xenialdan/MagicWE2/API.php +++ b/src/xenialdan/MagicWE2/API.php @@ -20,7 +20,7 @@ use pocketmine\utils\TextFormat as TF; use RuntimeException; use xenialdan\MagicWE2\clipboard\Clipboard; -use xenialdan\MagicWE2\clipboard\CopyClipboard; +use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\CalculationException; use xenialdan\MagicWE2\exception\LimitExceededException; use xenialdan\MagicWE2\selection\Selection; @@ -30,10 +30,10 @@ use xenialdan\MagicWE2\task\action\SetBiomeAction; use xenialdan\MagicWE2\task\action\TaskAction; use xenialdan\MagicWE2\task\AsyncActionTask; -use xenialdan\MagicWE2\task\AsyncClipboardTask; use xenialdan\MagicWE2\task\AsyncCopyTask; use xenialdan\MagicWE2\task\AsyncCountTask; use xenialdan\MagicWE2\task\AsyncFillTask; +use xenialdan\MagicWE2\task\AsyncPasteTask; use xenialdan\MagicWE2\task\AsyncReplaceTask; use xenialdan\MagicWE2\tool\Brush; @@ -158,24 +158,27 @@ public static function copyAsync(Selection $selection, Session $session, int $fl /** * TODO: flag parsing, Position to paste at - * @param CopyClipboard $clipboard TODO should this be Clipboard? + * @param SingleClipboard $clipboard TODO should this be Clipboard? * @param Session $session * @param Position $target * @param int $flags * @return bool */ - public static function pasteAsync(CopyClipboard $clipboard, Session $session, Position $target, int $flags = self::FLAG_BASE) + public static function pasteAsync(SingleClipboard $clipboard, Session $session, Position $target, int $flags = self::FLAG_BASE) { #return false; try { $limit = Loader::getInstance()->getConfig()->get("limit", -1); - if ($clipboard->getShape()->getTotalCount() > $limit && $limit !== -1) { + if ($clipboard->getTotalCount() > $limit && $limit !== -1) { throw new LimitExceededException($session->getLanguage()->translateString('error.limitexceeded')); } - $c = $clipboard->getCenter(); + #$c = $clipboard->getCenter(); #$clipboard->setCenter($target->asVector3());//TODO check if ($session instanceof UserSession) $session->getBossBar()->showTo([$session->getPlayer()]); - Server::getInstance()->getAsyncPool()->submitTask(new AsyncClipboardTask($session->getUUID(), $clipboard, $clipboard->getTouchedChunks($c), AsyncClipboardTask::TYPE_PASTE, $flags)); + $shape = $clipboard->selection->getShape(); + $shape->setPasteVector($target->asVector3()); + $touchedChunks = $shape->getTouchedChunks($target->getLevel());//TODO check if this is an ugly hack + Server::getInstance()->getAsyncPool()->submitTask(new AsyncPasteTask($session->getUUID(), $clipboard->selection, $touchedChunks, $clipboard, $flags)); } catch (Exception $e) { $session->sendMessage($e->getMessage()); Loader::getInstance()->getLogger()->logException($e); diff --git a/src/xenialdan/MagicWE2/clipboard/Clipboard.php b/src/xenialdan/MagicWE2/clipboard/Clipboard.php index 76c6f017..a0da3411 100644 --- a/src/xenialdan/MagicWE2/clipboard/Clipboard.php +++ b/src/xenialdan/MagicWE2/clipboard/Clipboard.php @@ -25,8 +25,6 @@ abstract class Clipboard implements Serializable const FLIP_NORTH = 0x03; const FLIP_SOUTH = 0x03; - /** @var Chunk[] */ - public $chunks = []; /** @var int|null */ public $levelid; /** @var string */ diff --git a/src/xenialdan/MagicWE2/clipboard/CopyClipboard.php b/src/xenialdan/MagicWE2/clipboard/CopyClipboard.php deleted file mode 100644 index d8c1ba99..00000000 --- a/src/xenialdan/MagicWE2/clipboard/CopyClipboard.php +++ /dev/null @@ -1,190 +0,0 @@ -levelid = $levelId; - $this->chunks = $chunks; - } - - /** - * @return Vector3 - */ - public function getCenter(): Vector3 - { - return $this->center; - } - - /** - * @param Vector3 $center - */ - public function setCenter(Vector3 $center): void - { - if ($center instanceof Position && $center->getLevel() !== null) { - $this->levelid = $center->getLevel()->getId(); - } - $this->center = $center->asVector3(); - } - - /** - * @return Shape - * @throws Exception - */ - public function getShape(): Shape - { - if (!$this->shape instanceof Shape) throw new Exception("Shape is not valid"); - return $this->shape; - } - - /** - * @param Shape $shape - */ - public function setShape(Shape $shape): void - { - $this->shape = $shape; - } - - /** - * @return AxisAlignedBB - * @throws Exception - * @deprecated - */ - public function getAxisAlignedBB(): AxisAlignedBB - { - return $this->getShape()->getAABB(); - } - - /** - * @param Vector3 $center - * @return array of fastSerialized chunks - * @throws Exception - */ - public function getTouchedChunks(Vector3 $center): array - { - $shape = $this->getShape(); - $shape->setPasteVector($center); - return $shape->getTouchedChunks($this->getLevel()); - } - - /** - * Returns the blocks by their actual position - * @param Level|AsyncChunkManager $manager The level or AsyncChunkManager - * @param int $flags - * @return Generator|Block[] - * @throws Exception - * @deprecated - */ - public function getBlocks($manager, int $flags = API::FLAG_BASE): Generator - { - $this->validateChunkManager($manager); - foreach ($this->getShape()->getBlocks($manager) as $block) yield $block; - } - - /** - * @param mixed $manager - * @throws Exception - */ - public function validateChunkManager($manager): void - { - if (!$manager instanceof Level && !$manager instanceof AsyncChunkManager) throw new Exception(get_class($manager) . " is not an instance of Level or AsyncChunkManager"); - } - - /** - * Approximated count of blocks - * @return int - * @throws Exception - * @deprecated - */ - public function getTotalCount() - { - return $this->getShape()->getTotalCount(); - } - - /** - * 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 - * @throws Exception - */ - public function serialize() - { - $chunks = []; - $pasteChunks = []; - foreach ($this->chunks as $hash => $chunk) - $chunks[$hash] = $chunk->fastSerialize(); - foreach ($this->pasteChunks as $hash => $pasteChunk) - $pasteChunks[$hash] = $pasteChunk->fastSerialize(); - return serialize([ - $this->levelid, - $this->center->asVector3(), - $chunks, - $pasteChunks, - $this->getShape() - ]); - } - - /** - * Constructs the object - * @link http://php.net/manual/en/serializable.unserialize.php - * @param string $serialized

- * The string representation of the object. - *

- * @return void - * @since 5.1.0 - */ - public function unserialize($serialized) - { - #var_dump("Called " . __METHOD__); - [ - $this->levelid, - $this->center, - $chunks, - $pasteChunks, - $this->shape - ] = unserialize($serialized); - foreach ($chunks as $hash => $chunk)//TODO save serialized chunks instead? - $this->chunks[$hash] = Chunk::fastDeserialize($chunk); - foreach ($pasteChunks as $hash => $pasteChunk)//TODO save serialized chunks instead? - $this->pasteChunks[$hash] = Chunk::fastDeserialize($pasteChunk); - #print "Touched chunks unserialize count: " . count($this->chunks) . PHP_EOL; - } - - public function __toString() - { - return __CLASS__ . " Chunk count: " . count($this->chunks); - } -} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php b/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php index f9454bee..1147ed52 100644 --- a/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php +++ b/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php @@ -10,6 +10,8 @@ class RevertClipboard extends Clipboard { + /** @var Chunk[] */ + public $chunks = []; /** @var Block[] */ public $blocksAfter; diff --git a/src/xenialdan/MagicWE2/clipboard/SingleClipboard.php b/src/xenialdan/MagicWE2/clipboard/SingleClipboard.php new file mode 100644 index 00000000..63c99721 --- /dev/null +++ b/src/xenialdan/MagicWE2/clipboard/SingleClipboard.php @@ -0,0 +1,75 @@ +entries[Level::blockHash($x, $y, $z)] = $entry; + } + + /** + * @param int $x + * @param int $y + * @param int $z + * @return Generator|BlockEntry[] + */ + public function iterateEntries(&$x, &$y, &$z): Generator + { + foreach ($this->entries as $hash => $entry) { + Level::getBlockXYZ($hash, $x, $y, $z); + yield $entry; + } + } + + public function getTotalCount(): int + { + return count($this->entries); + } + + /** + * String representation of object + * @link https://php.net/manual/en/serializable.serialize.php + * @return string the string representation of the object or null + * @since 5.1 + */ + public function serialize() + { + // TODO: Implement serialize() method. + return serialize([ + $this->entries, + $this->selection + ]); + } + + /** + * Constructs the object + * @link https://php.net/manual/en/serializable.unserialize.php + * @param string $serialized

+ * The string representation of the object. + *

+ * @return void + * @since 5.1 + */ + public function unserialize($serialized) + { + // TODO: Implement unserialize() method. + [ + $this->entries, + $this->selection + ] = unserialize($serialized); + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php index c1568cef..0654a93e 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php @@ -15,7 +15,7 @@ use pocketmine\Player; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\API; -use xenialdan\MagicWE2\clipboard\CopyClipboard; +use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -61,7 +61,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo if (is_null($clipboard)) { throw new Exception($lang->translateString('error.noclipboard')); } - if (!$clipboard instanceof CopyClipboard) {//TODO check if i want to pass ANY clipboard instead + if (!$clipboard instanceof SingleClipboard) {//TODO check if i want to pass ANY clipboard instead throw new Exception($lang->translateString('error.noclipboard')); } /*if (!API::hasFlag(API::flagParser(explode(" ", strval($args["flags"]))), API::FLAG_POSITION_RELATIVE)) { diff --git a/src/xenialdan/MagicWE2/helper/BlockEntry.php b/src/xenialdan/MagicWE2/helper/BlockEntry.php new file mode 100644 index 00000000..2d8b6849 --- /dev/null +++ b/src/xenialdan/MagicWE2/helper/BlockEntry.php @@ -0,0 +1,56 @@ +runtimeId = $runtimeId; + $this->nbt = $nbt; + } + + public function validate(): bool + { + [$id, $meta] = RuntimeBlockMapping::fromStaticRuntimeId($this->runtimeId); + if ($id === BlockIds::INFO_UPDATE) return false; + if ($this->nbt instanceof CompoundTag && !$this->nbt->valid()) return false; + return true; + } + + public function __toString() + { + [$id, $meta] = RuntimeBlockMapping::fromStaticRuntimeId($this->runtimeId); + $str = __CLASS__ . " " . $this->runtimeId . " [$id:$meta]"; + if ($this->nbt instanceof CompoundTag) { + $str .= " " . str_replace("\n", "", $this->nbt->toString()); + } + return $str; + } + + public function toBlock(): Block + { + if (!BlockFactory::isInit()) BlockFactory::init(); + [$id, $meta] = RuntimeBlockMapping::fromStaticRuntimeId($this->runtimeId); + return BlockFactory::get($id, $meta); + } + +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/AsyncClipboardTask.php b/src/xenialdan/MagicWE2/task/AsyncClipboardTask.php deleted file mode 100644 index 12ddc34c..00000000 --- a/src/xenialdan/MagicWE2/task/AsyncClipboardTask.php +++ /dev/null @@ -1,159 +0,0 @@ -start = microtime(true); - #$clipboard->pasteChunks = $touchedChunks; - $clipboard->pasteChunks = array_map(function ($chunk) { - return Chunk::fastDeserialize($chunk); - }, $touchedChunks); - $this->touchedChunks = serialize($touchedChunks); - $this->sessionUUID = $sessionUUID->toString(); - $this->clipboard = serialize($clipboard); - $this->flags = $flags; - $this->type = $type; - } - - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun() - { - $this->publishProgress([0, "Start"]); - - /** @var CopyClipboard $clipboard */ - $clipboard = unserialize($this->clipboard); - #$clipboard->pasteChunks = array_map(function ($chunk) { - # return Chunk::fastDeserialize($chunk); - #}, $clipboard->pasteChunks); - $pasteChunkManager = Clipboard::getChunkManager($clipboard->pasteChunks); - - $oldBlocks = iterator_to_array($this->execute($clipboard, $pasteChunkManager, $changed)); - - $resultChunks = $pasteChunkManager->getChunks(); - $resultChunks = array_filter($resultChunks, function (Chunk $chunk) { - return $chunk->hasChanged(); - }); - $this->setResult(compact("resultChunks", "oldBlocks", "changed")); - } - - /** - * @param CopyClipboard $clipboard - * @param AsyncChunkManager $pasteChunkManager - * @param null|int $changed - * @return Generator|Block[] blocks before the change - * @throws Exception - */ - private function execute(CopyClipboard $clipboard, AsyncChunkManager $pasteChunkManager, ?int &$changed): Generator - { - $blockCount = $clipboard->getShape()->getTotalCount(); - $chunkManager = Clipboard::getChunkManager($clipboard->chunks); - $lastprogress = 0; - $changed = 0; - $this->publishProgress([0, "Running, changed $changed blocks out of $blockCount"]); - if (!BlockFactory::isInit()) BlockFactory::init(); - /** @var Block $block */ - foreach ($clipboard->getShape()->getBlocks($chunkManager, [], $this->flags) as $block) { - var_dump("Block clipboard used", $block); - $block1 = $pasteChunkManager->getBlockAt($block->getFloorX(), $block->getFloorY(), $block->getFloorZ())->setComponents($block->x, $block->y, $block->z); - var_dump("Block pastechunk had", $block); - yield $block1;//TODO check - $pasteChunkManager->setBlockAt($block->getFloorX(), $block->getFloorY(), $block->getFloorZ(), $block); - var_dump("Block clipboard pasted", $block); - $changed++; - $progress = floor($changed / $blockCount * 100); - if ($lastprogress < $progress) {//this prevents spamming packets - $this->publishProgress([$progress, "Running, changed $changed blocks out of $blockCount"]); - $lastprogress = $progress; - } - } - } - - /** - * @param Server $server - * @throws Exception - */ - public function onCompletion(Server $server): void - { - try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); - if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); - } catch (SessionException $e) { - Loader::getInstance()->getLogger()->logException($e); - $session = null; - } - $result = $this->getResult(); - $undoChunks = array_map(function ($chunk) { - return Chunk::fastDeserialize($chunk); - }, unserialize($this->touchedChunks)); - $oldBlocks = $result["oldBlocks"]; - $changed = $result["changed"]; - /** @var Chunk[] $resultChunks */ - $resultChunks = $result["resultChunks"]; - /** @var CopyClipboard $clipboard */ - $clipboard = unserialize($this->clipboard); - $totalCount = $clipboard->getShape()->getTotalCount(); - /** @var Level $level */ - $level = $clipboard->getLevel(); - foreach ($resultChunks as $hash => $chunk) { - $level->setChunk($chunk->getX(), $chunk->getZ(), $chunk, false); - } - if (is_null($session)) return; - switch ($this->type) { - case self::TYPE_PASTE: - {//TODO translation - $session->sendMessage(TF::GREEN . "Async " . (API::hasFlag($this->flags, API::FLAG_POSITION_RELATIVE) ? "relative" : "absolute") . " Clipboard pasting succeed, took " . $this->generateTookString() . ", $changed blocks out of $totalCount changed."); - $session->addRevert(new RevertClipboard($clipboard->levelid, $undoChunks, $oldBlocks)); - break; - } - } - } -} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/AsyncCopyTask.php b/src/xenialdan/MagicWE2/task/AsyncCopyTask.php index 1d28bad0..79c878e1 100644 --- a/src/xenialdan/MagicWE2/task/AsyncCopyTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncCopyTask.php @@ -5,14 +5,15 @@ use Exception; use pocketmine\block\Block; use pocketmine\level\format\Chunk; -use pocketmine\level\Level; use pocketmine\math\Vector3; +use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use pocketmine\Server; use pocketmine\utils\TextFormat as TF; use pocketmine\utils\UUID; -use xenialdan\MagicWE2\clipboard\CopyClipboard; +use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockEntry; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\Selection; @@ -67,23 +68,23 @@ public function onRun() var_dump("shape", $selection->getShape()); $manager = Shape::getChunkManager($chunks); unset($chunks); - $clipboard = new CopyClipboard($selection->levelid); - $clipboard->setCenter(unserialize($this->offset)); + $clipboard = new SingleClipboard(); + #$clipboard->setCenter(unserialize($this->offset)); $totalCount = $selection->getShape()->getTotalCount(); $copied = $this->copyBlocks($selection, $manager, $clipboard); - $clipboard->setShape($selection->getShape()); - $clipboard->chunks = $manager->getChunks(); + #$clipboard->setShape($selection->getShape()); + #$clipboard->chunks = $manager->getChunks(); $this->setResult(compact("clipboard", "copied", "totalCount")); } /** * @param Selection $selection * @param AsyncChunkManager $manager - * @param CopyClipboard $clipboard + * @param SingleClipboard $clipboard * @return int * @throws Exception */ - private function copyBlocks(Selection $selection, AsyncChunkManager $manager, CopyClipboard &$clipboard): int + private function copyBlocks(Selection $selection, AsyncChunkManager $manager, SingleClipboard &$clipboard): int { $blockCount = $selection->getShape()->getTotalCount(); $i = 0; @@ -91,13 +92,8 @@ private function copyBlocks(Selection $selection, AsyncChunkManager $manager, Co $this->publishProgress([0, "Running, copied $i blocks out of $blockCount"]); /** @var Block $block */ foreach ($selection->getShape()->getBlocks($manager, [], $this->flags) as $block) { - $chunk = $clipboard->chunks[Level::chunkHash($block->x >> 4, $block->z >> 4)] ?? null; - if ($chunk === null) { - $chunk = $manager->getChunk($block->x >> 4, $block->z >> 4); - $clipboard->chunks[Level::chunkHash($block->x >> 4, $block->z >> 4)] = $chunk; - } - $manager->setBlockAt($block->getFloorX(), $block->getFloorY(), $block->getFloorZ(), $block); - var_dump("copied manager block", $manager->getBlockAt($block->getFloorX(), $block->getFloorY(), $block->getFloorZ())); + $clipboard->addEntry($block->getFloorX(), $block->getFloorY(), $block->getFloorZ(), new BlockEntry(RuntimeBlockMapping::toStaticRuntimeId($block->getId(), $block->getDamage())));//TODO test tiles + var_dump("copied selection block", $block); $i++; $progress = floor($i / $blockCount * 100); if ($lastprogress < $progress) {//this prevents spamming packets @@ -115,12 +111,12 @@ public function onCompletion(Server $server): void if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); $result = $this->getResult(); $copied = $result["copied"]; - /** @var CopyClipboard $clipboard */ + /** @var SingleClipboard $clipboard */ $clipboard = $result["clipboard"]; $totalCount = $result["totalCount"]; $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.copy.success', [$this->generateTookString(), $copied, $totalCount])); $session->addClipboard($clipboard); - var_dump("clipboard shape blocks", $clipboard->getBlocks(CopyClipboard::getChunkManager($clipboard->chunks))); + var_dump("clipboard shape blocks", iterator_to_array($clipboard->iterateEntries($x, $y, $z))); } catch (SessionException $e) { Loader::getInstance()->getLogger()->logException($e); } diff --git a/src/xenialdan/MagicWE2/task/AsyncPasteTask.php b/src/xenialdan/MagicWE2/task/AsyncPasteTask.php new file mode 100644 index 00000000..23449693 --- /dev/null +++ b/src/xenialdan/MagicWE2/task/AsyncPasteTask.php @@ -0,0 +1,165 @@ +start = microtime(true); + $this->sessionUUID = $sessionUUID->toString(); + $this->selection = serialize($selection); + $this->touchedChunks = serialize($touchedChunks); + $this->clipboard = serialize($clipboard); + $this->flags = $flags; + } + + /** + * Actions to execute when run + * + * @return void + * @throws Exception + */ + public function onRun() + { + $this->publishProgress([0, "Start"]); + + $touchedChunks = array_map(function ($chunk) { + return Chunk::fastDeserialize($chunk); + }, unserialize($this->touchedChunks)); + + $manager = Shape::getChunkManager($touchedChunks); + unset($touchedChunks); + + /** @var Selection $selection */ + $selection = unserialize($this->selection); + + /** @var SingleClipboard $clipboard */ + $clipboard = unserialize($this->clipboard); + $oldBlocks = iterator_to_array($this->execute($selection, $manager, $clipboard, $changed)); + + $resultChunks = $manager->getChunks(); + $resultChunks = array_filter($resultChunks, function (Chunk $chunk) { + return $chunk->hasChanged(); + }); + $this->setResult(compact("resultChunks", "oldBlocks", "changed")); + } + + /** + * @param Selection $selection + * @param AsyncChunkManager $manager + * @param SingleClipboard $clipboard + * @param null|int $changed + * @return Generator|Block[] + * @throws Exception + */ + private function execute(Selection $selection, AsyncChunkManager $manager, SingleClipboard $clipboard, ?int &$changed): Generator + { + $blockCount = $clipboard->getTotalCount(); + $lastchunkx = $lastchunkz = 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) { + /*if (API::hasFlag($this->flags, API::FLAG_POSITION_RELATIVE)){ + $rel = $block->subtract($selection->shape->getPasteVector()); + $block->setComponents($rel->x,$rel->y,$rel->z);//TODO COPY TO ALL TASKS + }*/ + if (is_null($lastchunkx) || $x >> 4 !== $lastchunkx && $z >> 4 !== $lastchunkz) { + $lastchunkx = $x >> 4; + $lastchunkz = $z >> 4; + if (is_null($manager->getChunk($x >> 4, $z >> 4))) { + #print PHP_EOL . "Not found: " . strval($block->x >> 4) . ":" . strval($block->z >> 4) . PHP_EOL; + continue; + } + } + /** @var Block $new */ + $new = $entry->toBlock()->setComponents($x, $y, $z); + yield $manager->getBlockAt($x, $y, $z)->setComponents($x, $y, $z); + $manager->setBlockAt($x, $y, $z, $new); + if ($manager->getBlockArrayAt($x, $y, $z) !== [$new->getId(), $new->getDamage()]) {//TODO remove? Just useless waste imo + $changed++; + } + /// + $i++; + $progress = floor($i / $blockCount * 100); + if ($lastprogress < $progress) {//this prevents spamming packets + $this->publishProgress([$progress, "Running, changed $changed blocks out of $blockCount"]); + $lastprogress = $progress; + } + } + } + + /** + * @param Server $server + * @throws Exception + */ + public function onCompletion(Server $server): void + { + try { + $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); + } catch (SessionException $e) { + Loader::getInstance()->getLogger()->logException($e); + $session = null; + } + $result = $this->getResult(); + /** @var Chunk[] $resultChunks */ + $resultChunks = $result["resultChunks"]; + $undoChunks = array_map(function ($chunk) { + return Chunk::fastDeserialize($chunk); + }, unserialize($this->touchedChunks)); + $oldBlocks = $result["oldBlocks"]; + $changed = $result["changed"]; + /** @var Selection $selection */ + $selection = unserialize($this->selection); + $totalCount = $selection->getShape()->getTotalCount(); + /** @var Level $level */ + $level = $selection->getLevel(); + foreach ($resultChunks as $hash => $chunk) { + $level->setChunk($chunk->getX(), $chunk->getZ(), $chunk, false); + } + if (!is_null($session)) { + $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.fill.success', [$this->generateTookString(), $changed, $totalCount])); + $session->addRevert(new RevertClipboard($selection->levelid, $undoChunks, $oldBlocks)); + } + } +} \ No newline at end of file