Как сделать копирование /copy и вставить /paste логику с асинхронными потоками
код:
<?php
namespace FastAsyncWorldEdit;
use pocketmine\plugin\PluginBase;
use pocketmine\event\Listener;
use pocketmine\command\Command;
use pocketmine\block\BlockIds;
use pocketmine\command\CommandSender;
use pocketmine\Player;
use pocketmine\math\Vector3;
use pocketmine\block\Block;
use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
class Main extends PluginBase implements Listener {
private array $positions = [];
public function onEnable() {
$this->getServer()->getPluginManager()->registerEvents($this, $this);
}
public function onCommand(CommandSender $sender, Command $command, $label, array $args): bool {
if (!$sender instanceof Player) {
$sender->sendMessage("Эта команда доступна только для игроков.");
return true;
}
switch ($command->getName()) {
case "pos1":
$this->setPlayerPosition($sender, "pos1");
return true;
case "pos2":
$this->setPlayerPosition($sender, "pos2");
return true;
case "set":
if (!isset($args[0])) {
$sender->sendMessage("Использование: /set <блок>");
return true;
}
if (!$this->arePositionsSet($sender)) {
$sender->sendMessage("Сначала установите обе точки с помощью //pos1 и //pos2.");
return true;
}
$pos1 = $this->positions[$sender->getName()]["pos1"];
$pos2 = $this->positions[$sender->getName()]["pos2"];
$blockId = $args[0];
$block = Block::get($blockId);
if ($block === null) {
$sender->sendMessage("Неверный блок: {$blockId}");
return true;
}
$this->getServer()->getScheduler()->scheduleAsyncTask(new SetBlocksTask(
$sender->getLevel()->getName(),
[$pos1->getX(), $pos1->getY(), $pos1->getZ()],
[$pos2->getX(), $pos2->getY(), $pos2->getZ()],
$block->getId()
));
$sender->sendMessage("Блоки будут изменены асинхронно.");
return true;
}
return false;
}
/**
* Устанавливает позицию игрока (pos1 или pos2).
*/
private function setPlayerPosition(Player $player, string $positionType): void {
$this->positions[$player->getName()][$positionType] = $player->getPosition();
$player->sendMessage(ucfirst($positionType) . " установлена: " . $this->formatPosition($player->getPosition()));
}
/**
* Проверяет, установлены ли обе позиции для игрока.
*/
private function arePositionsSet(Player $player): bool {
return isset($this->positions[$player->getName()]["pos1"], $this->positions[$player->getName()]["pos2"]);
}
/**
* Форматирует позицию для отображения игроку.
*/
private function formatPosition(Vector3 $pos): string {
return "x: {$pos->getX()}, y: {$pos->getY()}, z: {$pos->getZ()}";
}
}
class SetBlocksTask extends AsyncTask {
private string $levelName;
private array $pos1;
private array $pos2;
private int $blockId;
public function __construct(string $levelName, array $pos1, array $pos2, int $blockId) {
$this->levelName = $levelName;
$this->pos1 = $pos1;
$this->pos2 = $pos2;
$this->blockId = $blockId;
}
/**
* Выполняется в отдельном потоке для генерации списка блоков.
*/
public function onRun() {
$pos1 = new Vector3($this->pos1[0], $this->pos1[1], $this->pos1[2]);
$pos2 = new Vector3($this->pos2[0], $this->pos2[1], $this->pos2[2]);
$blocks = $this->generateBlocks($pos1, $pos2);
$this->setResult($blocks);
}
/**
* Генерация всех координат блоков в выбранной области.
*/
private function generateBlocks(Vector3 $pos1, Vector3 $pos2): array {
[$x1, $y1, $z1] = [$pos1->getX(), $pos1->getY(), $pos1->getZ()];
[$x2, $y2, $z2] = [$pos2->getX(), $pos2->getY(), $pos2->getZ()];
if ($x1 > $x2) [$x1, $x2] = [$x2, $x1];
if ($y1 > $y2) [$y1, $y2] = [$y2, $y1];
if ($z1 > $z2) [$z1, $z2] = [$z2, $z1];
$blocks = [];
for ($x = $x1; $x <= $x2; $x++) {
for ($y = $y1; $y <= $y2; $y++) {
for ($z = $z1; $z <= $z2; $z++) {
$blocks[] = [$x, $y, $z];
}
}
}
return $blocks;
}
/**
* Выполняется на основном потоке после завершения задачи.
*/
public function onCompletion(Server $server) {
$level = $server->getLevelByName($this->levelName);
if ($level === null) {
return;
}
foreach ($this->getResult() as [$x, $y, $z]) {
$level->setBlock(new Vector3($x, $y, $z), Block::get($this->blockId));
}
}
}
?>