diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5432c0cd..737b0b18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,6 +4,7 @@ on: [push, pull_request] jobs: phpstan: + if: "!contains(github.event.head_commit.message, '[phpstan skip]')" runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/.poggit.yml b/.poggit.yml index 60589a8d..5c49f8c1 100644 --- a/.poggit.yml +++ b/.poggit.yml @@ -11,23 +11,23 @@ projects: phpstan: false libs: - src: BlockHorizons/libschematic/libschematic - version: ^2.0.0 - - src: thebigsmilexd/customui/customui - version: ^4.0.0 - branch: PM4 + version: ^2.0.1 + - src: WolvesFortress/FormAPI/libFormAPI + version: ^1.3.0 + branch: pm4 - src: thebigsmilexd/apibossbar/apibossbar - version: ^0.1.3 + version: ^0.1.4 branch: PM4 - src: CortexPE/Commando/Commando version: ^3.0.0 branch: PM4 - src: muqsit/invmenu/InvMenu - version: ^4.0.1 + version: ^4.1.0 branch: "4.0" - src: thebigsmilexd/libstructure/libstructure - version: ^0.1.1 + version: ^0.1.5 branch: mcstructure - - src: buchwasa/ScoreFactory/ScoreFactory + - src: buchwasa/scorefactory/ScoreFactory version: ^3.0.1 branch: pm4 ... diff --git a/COMMANDS.MD b/COMMANDS.MD new file mode 100644 index 00000000..d27e6fd5 --- /dev/null +++ b/COMMANDS.MD @@ -0,0 +1,52 @@ +# Commands +This list is automatically generated. If you have noticed an error, please create an issue. + +| Command | Description | Usage | Alias | +|---|---|---|---| +| `//pos1` | Set position 1 | `//pos1 ` | `//1` | +| `//pos2` | Set position 2 | `//pos2 ` | `//2` | +| `//hpos1` | Set position 1 to targeted block | `//hpos1 ` | `//h1` | +| `//hpos2` | Set position 2 to targeted block | `//hpos2 ` | `//h2` | +| `//chunk` | Set the selection to your current chunk | `//chunk ` | | +| `//wand` | Gives you the selection wand | `//wand ` | | +| `//togglewand` | Toggle the wand tool on/off | `//togglewand ` | `//toggleeditwand` | +| `//debug` | Gives you the debug stick, which gives information about the clicked block | `//debug ` | | +| `//toggledebug` | Toggle the debug stick on/off | `//toggledebug ` | | +| `//size` | Get information about the selection | `//size ` | | +| `//count` | Counts the number of blocks matching a mask in selection | `//count [blocks:string] [flags:text]` | `//analyze` | +| `//listchunks` | List chunks that your selection includes | `//listchunks ` | | +| `//set` | Fill a selection with the specified blocks | `//set [flags:text]` | | +| `//replace` | Replace blocks in an area with other blocks | `//replace [flags:text]` | | +| `//cylinder` | Create a cylinder | `//cylinder [height:int] [flags:text]` | `//cyl` | +| `//copy` | Copy the selection to the clipboard | `//copy [flags:text]` | | +| `//paste` | Paste the clipboard's contents | `//paste [flags:text]` | | +| `//cut` | Cut the selection to the clipboard | `//cut [flags:text]` | | +| `//cut2` | Cut the selection to the clipboard - the new way | `//cut2 [flags:text]` | | +| `//clearclipboard` | Clear your clipboard | `//clearclipboard ` | | +| `//flip` | Flip the contents of the clipboard across the origin | `//flip ` | `//mirror` | +| `//rotate` | Rotate the contents of the clipboard around the origin | `//rotate [aroundOrigin:bool]` | | +| `//undo` | Rolls back the last action | `//undo ` | | +| `//redo` | Applies the last undo action again | `//redo ` | | +| `//clearhistory` | Clear your history | `//clearhistory ` | | +| `//setrange` | Set tool range | `//setrange [range:int]` | `//toolrange` | +| `//limit` | Set the block change limit. Use -1 to disable | `//limit [limit:int]` | | +| `//help` | MagicWE help command | `//help [command:string]` | `//?, //mwe, //wehelp` | +| `//version` | MagicWE version | `//version ` | `//ver` | +| `//info` | Information about MagicWE | `//info ` | | +| `//report` | Report a bug to GitHub | `//report [title:text]` | `//bug, //github` | +| `//donate` | Donate to support development of MagicWE! | `//donate ` | `//support, //paypal` | +| `//language` | Set your language | `//language [language:string]` | `//lang` | +| `//biomelist` | Gets all biomes available | `//biomelist ` | `//biomels` | +| `//biomeinfo` | Get the biome of the targeted block | `//biomeinfo [flags:text]` | | +| `//setbiome` | Sets the biome of your current block or region | `//setbiome ` | | +| `//calculate` | Evaluate a mathematical expression | `//calculate ` | `//calc, //eval, //evaluate, //solve` | +| `//togglewaila` | Toggle the What Am I Looking At utility | `//togglewaila ` | `//waila, //wyla` | +| `//togglesidebar` | Toggle the sidebar | `//togglesidebar ` | `//sidebar` | +| `//toggleoutline` | Toggle the selection outline | `//toggleoutline ` | `//outline, //showbounds` | +| `//placeallblockstates` | Place all blockstates similar to Java debug worlds | `//placeallblockstates ` | | +| `//testapi` | Internal command for testing API methods | `//testapi ` | | +| `//generatecommandsmd` | Generates the commands.md file | `//generatecommandsmd ` | | +| `//brush` | Opens the brush tool menu | `//brush` | | +| `//brush name` | Get name or rename a brush | `/brush name [name:string]` | | +| `//palette` | Manage block palettes | `//palette ` | | +| `//flood` | Opens the flood fill tool menu | `//flood ` | `//floodfill` | \ No newline at end of file diff --git a/README.md b/README.md index 46480129..098de0e1 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,58 @@ ![MagicWE2's awesome wide banner!](https://github.com/thebigsmileXD/MagicWE2/blob/master/resources/magicwe_icon_wide.png) --- -# MagicWE2 -Lag free asynchronous world editor for [PMMP](https://github.com/pmmp/PocketMine-MP) -Try the new MagicWE, now way more powerful, with more support, more commands, new tools and more! +# MagicWE2 -[![Poggit-CI](https://poggit.pmmp.io/ci.badge/thebigsmileXD/MagicWE2/MagicWE2/master)](https://poggit.pmmp.io/ci/thebigsmileXD/MagicWE2) + +[![PHPStan](https://github.com/thebigsmileXD/MagicWE2/workflows/PHPStan/badge.svg)](https://github.com/thebigsmileXD/MagicWE2/workflows/PHPStan) +[![Donations Badge](https://raw.githubusercontent.com/k4m4/donations/master/images/badge.svg)](https://paypal.me/xenialdan) -Jokes aside, here is a list of pros: +Lag free asynchronous world editor for [PMMP](https://github.com/pmmp/PocketMine-MP) with focus on mobile usability + +## Features - Simple usage +- Asynchronous world editing +- Strong focus on simplicity - Translations - Good performance and great speeds -- Progress bars like on Windows 98! -- Sessions +- Many commands +- Progress bars +- Cached sessions - Clipboards -- Optimized item / block parsing - you can place any block, by id, name, and item! -- Alot more commands -- Command auto-completion -- Command flags (i.e. -p for relative copying/pasting, -h for hollow objects) -- UI for brush setup and configuration -- Fancy icon and optional startup ASCII art -- Direct bug reporting to GitHub +- Optimized item / block parsing - you can place any block, by id, name, item and even blockstates +- Command auto-completion (broken, [#blameMojang (Jira Issue)](https://bugs.mojang.com/browse/MCPE-138567)) +- Command specific flags (i.e. -p for relative copying/pasting, -h for hollow objects) +- UIs for brushes, palettes, assets etc. +- Selection shapes & outline +- Simplified placement of assets +- Previews of selections / structures +- Scoreboard integration +- Feature rich API +- Prefilled bug report issue url using `//report` + ## Commands -| Command | Alias | Description | Usage | -| --- | --- | --- | --- | -| `//pos1` | `//1` | `Select first position` | `//pos1` | -| `//pos2` | `//2` | `Select second position` | `//pos2` | -| `//set` | `//fill` | `Fill an area with the specified blocks` | `//set [flags:text]` | -| `//replace` | | `Replace blocks in an area with other blocks` | `//replace [flags:text]` | -| `//copy` | | `Copy an area into a clipboard` | `//copy [flags:text]` | -| `//paste` | | `Paste your clipboard` | `//paste [flags:text]` | -| `//wand` | | `Gives you the selection wand` | `//wand` | -| `//togglewand` | | `Toggle the wand tool on/off` | `//togglewand` | -| `//undo` | | `Rolls back the last action` | `//undo` | -| `//redo` | | `Applies the last undo action again` | `//redo` | -| `//debug` | | `Gives you the debug stick, which gives information about the clicked block` | `//debug` | -| `//toggledebug` | | `Toggle the debug stick on/off` | `//toggledebug` | -| `//cylinder` | `//cyl` | `Create a cylinder` | `//cylinder [height:int] [flags:text]` | -| `//count` | `//analyze` | `Count blocks in selection` | `//count [blocks:string] [flags:text]` | -| `//help` | `//?,//mwe,//wehelp` | `MagicWE help command` | `//help [command:string]` | -| `//version` | `//ver` | `MagicWE version` | `//version` | -| `//info` | | `Information about MagicWE` | `//info` | -| `//report` | `//bug,//github` | `Report a bug to GitHub` | `//report [title:text]` | -| `//donate` | `//support,//paypal` | `Donate to support development of MagicWE!` | `//donate` | -| `//brush` | | `Opens the brush tool menu` | `//brush` | -| `//flood` | | `Opens the flood tool menu` | `//flood` | + +You can find a list of commands here: [COMMANDS.MD](https://github.com/thebigsmileXD/MagicWE2/blob/outline/COMMANDS.MD) ## Planned features -- Saved sessions (saved brushes and clipboards) -- More commands, a glimpse at the plugin.yml should give you a good look what is coming up -- Command based flags, since they are currently in a global state -- Schematic and structure block data support + +- More saved session data (saved brushes, clipboards, palettes) +- More commands +- Per-command flags, since they are currently in a global state +- Schematic and structure block data support (in progress) - Clipboard naming, exporting and switching -- ScoreboardAPI integration -- Better and more brushes. For now i suggest using [BlockSniper](https://github.com/BlockHorizons/BlockSniper) for brushes! +- Better and more brushes. For now i suggest using [BlockSniper](https://github.com/BlockHorizons/BlockSniper) for + brushes! - [MyPlot](https://github.com/jasonwynn10/MyPlot) integration +- UI with edit history ## Fast updates You have an urgent issue, your server is crashing or players mess with the world and start griefing? @@ -77,9 +64,12 @@ Feel free to open issues, feature requests and criticism are welcome! If you have an urgent issue, tag me on Twitter for faster response time: [@xenialdan](https://twitter.com/xenialdan) ## Quotes -- _"MagicWE2 has a new fresh coating over the plugin, with rainbow colored sprinkle topping!"_ ~ XenialDan, 2017 + +- _"MagicWE2 has a new fresh coating over the plugin, with rainbow colored sprinkle topping!"_ +- _"MWE2 - Not to be confused with MewTwo"_ ### Foot notes + License: GNU GENERAL PUBLIC LICENSE -Readme last updated: 4th August 2019 \ No newline at end of file +Readme last updated: 4th October 2021 \ No newline at end of file diff --git a/phpstan.neon.dist b/phpstan.neon.dist index ac2bc43a..168f84a0 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -11,12 +11,14 @@ parameters: - phar:///pocketmine/PocketMine-MP.phar/vendor/autoload.php scanDirectories: - /source/src - - phar:///source/vendor/customui.phar/src + - /source/vendor + - phar:///source/vendor/libFormAPI.phar/src - phar:///source/vendor/apibossbar.phar/src - phar:///source/vendor/Commando.phar/src - phar:///source/vendor/libschematic.phar/src - phar:///source/vendor/InvMenu.phar/src - phar:///source/vendor/libstructure.phar/src - phar:///source/vendor/ScoreFactory.phar/src + - phar:///pocketmine/PocketMine-MP.phar excludes_analyse: - - source/vendor \ No newline at end of file + - source/vendor diff --git a/plugin.yml b/plugin.yml index 248563a9..58eeb54a 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,9 +1,10 @@ --- name: MagicWE2 main: xenialdan\MagicWE2\Loader -version: 10.1.2 +version: 10.1.8 api: ["4.0.0"] -php: "7.4" +php: [ "8.0" ] +softdepend: [ "DEVirion" ] authors: - XenialDan description: Lag free asynchronous world editor for PMMP with plenty of options @@ -18,6 +19,10 @@ permissions: default: op we.command.donate: default: true + we.command.asset: + default: op + we.command.palette: + default: op we.command.language: default: op we.command.help: @@ -112,6 +117,10 @@ permissions: default: op we.command.utility.togglewaila: default: op - we.command.test: + we.command.utility.togglesidebar: + default: op + we.command.utility.toggleoutline: + default: op + we.command.debug: default: op ... diff --git a/resources/config.yml b/resources/config.yml index 3ed2f82a..29584703 100644 --- a/resources/config.yml +++ b/resources/config.yml @@ -4,6 +4,9 @@ # Available languages (ISO639-2): # eng, ara, cat, ces, chi, dan, deu, ell, epo, esp, est, fre, geo, hin, ind, ita, jpn, kor, lit, mar, mas, nld, nor, pol, por, rom, rus, slo, swa, swe, tgl, tha, zho language: eng -show-startup-icon: false +# Enables special commands for testing API features +developer-commands: false +# Maximum amount of blocks that can be edited at once. Set to -1 to disable limit: -1 +# Maximum raytraced distance for ranged tools. Default 100 tool-range: 100 diff --git a/resources/lang/zho.ini b/resources/lang/zho.ini index ce23b4f2..3c909313 100644 --- a/resources/lang/zho.ini +++ b/resources/lang/zho.ini @@ -1,4 +1,4 @@ -; Updated time : 3rd 10 2019 +; Updated time : 23rd 10 2021 ; See: https://www.loc.gov/standards/iso639-2/php/English_list.php language.name="Simplified Chinese" ; general @@ -18,9 +18,9 @@ error.limitexceeded="您正在尝试修改的方块太多了。减少选择的 error.notarget="找不到目标方块。如果需要,使用//setrange增加工具范围" error.noselection="找不到选中 - 请先选中一个范围" error.selectioninvalid="选中范围无效!检查是否已设置所有位置!" -error.nosession="为创建会话 - 可能没有使用{%0}的权限" +error.nosession="未创建会话 - 可能没有使用{%0}的权限" error.noclipboard = "找不到剪贴板 - 请先创建一个剪贴板" -warning.differentworld = "[警告] 您的编辑不处于您目前所在世界!" +warning.differentworld = "[警告] 您目前所在的世界不是您正在编辑的世界!" ; commands command.info.title="信息" command.limit.current="目前极限:{%0}" @@ -84,6 +84,12 @@ tool.debug.lore.2="例如方块的名称和损耗值" tool.debug.lore.3="使用//toggledebug切换其功能" tool.debug.disabled="故障排除工具已禁用。使用//toggledebug来重新启用它" tool.debug.setenabled = "故障排除工具已{%0}!" +; WAILA tool (What am i looking at) +tool.waila = "Waila" +tool.waila.setenabled = "Waila工具已{%0}!" +; Sidebar +tool.sidebar = "侧边栏" +tool.sidebar.setenabled = "侧边栏已{%0}!" ; flood tool ui.flood.title="覆盖菜单" ui.flood.options.limit="最大方块数" diff --git a/resources/parsepoggit.php b/resources/parsepoggit.php index af2892f5..588c5e9b 100644 --- a/resources/parsepoggit.php +++ b/resources/parsepoggit.php @@ -16,7 +16,7 @@ $version = $lib["version"] ?? "*"; $branch = $lib["branch"] ?? ":default"; - $urls[] = "https://poggit.pmmp.io/v.dl/{$src}/{$version}?branch={$branch}"; + $urls[] = "https://poggit.pmmp.io/v.dl/$src/$version?branch=$branch"; } } diff --git a/src/xenialdan/MagicWE2/API.php b/src/xenialdan/MagicWE2/API.php index 0c325e18..638dc963 100644 --- a/src/xenialdan/MagicWE2/API.php +++ b/src/xenialdan/MagicWE2/API.php @@ -8,7 +8,6 @@ use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\block\BlockFactory; -use pocketmine\block\UnknownBlock; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; @@ -23,12 +22,14 @@ use RuntimeException; use xenialdan\MagicWE2\clipboard\Clipboard; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\exception\BlockQueryAlreadyParsedException; use xenialdan\MagicWE2\exception\CalculationException; -use xenialdan\MagicWE2\exception\InvalidBlockStateException; use xenialdan\MagicWE2\exception\LimitExceededException; -use xenialdan\MagicWE2\helper\BlockStatesParser; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\selection\Selection; +use xenialdan\MagicWE2\selection\shape\Cuboid; use xenialdan\MagicWE2\selection\shape\Shape; +use xenialdan\MagicWE2\session\data\Asset; use xenialdan\MagicWE2\session\Session; use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\task\action\SetBiomeAction; @@ -37,6 +38,7 @@ 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; @@ -71,21 +73,23 @@ class API public const TAG_MAGIC_WE = "MagicWE"; public const TAG_MAGIC_WE_BRUSH = "MagicWEBrush"; + public const TAG_MAGIC_WE_ASSET = "MagicWEAsset"; + public const TAG_MAGIC_WE_PALETTE = "MagicWEPalette"; //TODO Split into separate Class (SchematicStorage?) /** @var Clipboard[] */ - private static $schematics = [];//TODO + private static array $schematics = [];//TODO /** * @param Selection $selection * @param Session $session - * @param Block[] $newblocks + * @param BlockPalette $newblocks * @param int $flags * @return bool */ - public static function fillAsync(Selection $selection, Session $session, $newblocks = [], int $flags = self::FLAG_BASE): bool + public static function fillAsync(Selection $selection, Session $session, BlockPalette $newblocks, int $flags = self::FLAG_BASE): bool { - if (empty($newblocks)) { + if ($newblocks->empty()) { $session->sendMessage(TF::RED . "New blocks is empty!"); return false; } @@ -111,16 +115,16 @@ public static function fillAsync(Selection $selection, Session $session, $newblo /** * @param Selection $selection * @param Session $session - * @param Block[] $oldBlocks - * @param Block[] $newBlocks + * @param BlockPalette $oldBlocks + * @param BlockPalette $newBlocks * @param int $flags * @return bool */ - public static function replaceAsync(Selection $selection, Session $session, $oldBlocks = [], $newBlocks = [], int $flags = self::FLAG_BASE): bool + public static function replaceAsync(Selection $selection, Session $session, BlockPalette $oldBlocks, BlockPalette $newBlocks, int $flags = self::FLAG_BASE): bool { - if (empty($oldBlocks)) $session->sendMessage(TF::RED . "Old blocks is empty!"); - if (empty($newBlocks)) $session->sendMessage(TF::RED . "New blocks is empty!"); - if (empty($oldBlocks) || empty($newBlocks)) return false; + if ($oldBlocks->empty()) $session->sendMessage(TF::RED . "Old blocks is empty!"); + if ($newBlocks->empty()) $session->sendMessage(TF::RED . "New blocks is empty!"); + if ($oldBlocks->empty() || $newBlocks->empty()) return false; try { $limit = Loader::getInstance()->getConfig()->get("limit", -1); if ($limit !== -1 && $selection->getShape()->getTotalCount() > $limit) { @@ -232,7 +236,7 @@ private static function getAABBTouchedChunksTemp(ChunkManager $manager, AxisAlig continue; } print __METHOD__ . " Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } } print __METHOD__ . " Touched chunks count: " . count($touchedChunks) . PHP_EOL; @@ -242,11 +246,11 @@ private static function getAABBTouchedChunksTemp(ChunkManager $manager, AxisAlig /** * @param Selection $selection * @param Session $session - * @param Block[] $filterBlocks + * @param BlockPalette $filterBlocks * @param int $flags * @return bool */ - public static function countAsync(Selection $selection, Session $session, array $filterBlocks, int $flags = self::FLAG_BASE): bool + public static function countAsync(Selection $selection, Session $session, BlockPalette $filterBlocks, int $flags = self::FLAG_BASE): bool { try { $limit = Loader::getInstance()->getConfig()->get("limit", -1); @@ -280,7 +284,7 @@ public static function setBiomeAsync(Selection $selection, Session $session, int /** @var Player $player */ $session->getBossBar()->showTo([$player]); } - Server::getInstance()->getAsyncPool()->submitTask(new AsyncActionTask($session->getUUID(), $selection, new SetBiomeAction($biomeId), $selection->getShape()->getTouchedChunks($selection->getWorld()))); + Server::getInstance()->getAsyncPool()->submitTask(new AsyncActionTask($session->getUUID(), $selection, new SetBiomeAction($biomeId), $selection->getShape()->getTouchedChunks($selection->getWorld()), BlockPalette::CREATE(), BlockPalette::CREATE())); } catch (Exception $e) { $session->sendMessage($e->getMessage()); Loader::getInstance()->getLogger()->logException($e); @@ -305,8 +309,8 @@ public static function createBrush(Block $target, Brush $brush, Session $session { $shapeClass = $brush->properties->shape; /** @var Shape $shape */ - $shape = new $shapeClass($target->getPos()->asVector3(), ...array_values($brush->properties->shapeProperties)); - $selection = new Selection($session->getUUID(), $target->getPos()->getWorld()); + $shape = new $shapeClass($target->getPosition()->asVector3(), ...array_values($brush->properties->shapeProperties)); + $selection = new Selection($session->getUUID(), $target->getPosition()->getWorld()); $selection->setShape($shape); $actionClass = $brush->properties->action; //TODO remove hack @@ -314,7 +318,7 @@ public static function createBrush(Block $target, Brush $brush, Session $session /** @var TaskAction $action */ $action = new $actionClass(...array_values($brush->properties->actionProperties)); $action->prefix = "Brush"; - Server::getInstance()->getAsyncPool()->submitTask(new AsyncActionTask($session->getUUID(), $selection, $action, $selection->getShape()->getTouchedChunks($selection->getWorld()), $brush->properties->blocks, $brush->properties->filter)); + Server::getInstance()->getAsyncPool()->submitTask(new AsyncActionTask($session->getUUID(), $selection, $action, $selection->getShape()->getTouchedChunks($selection->getWorld()), BlockPalette::fromString($brush->properties->blocks), BlockPalette::fromString($brush->properties->filter))); } /** @@ -326,7 +330,6 @@ public static function createBrush(Block $target, Brush $brush, Session $session */ public static function floodArea(Block $target, CompoundTag $settings, Session $session, int $flags = self::FLAG_BASE): bool { //TODO - if (!$settings instanceof CompoundTag) return false; $session->sendMessage(TF::RED . "TEMPORARILY DISABLED!"); return false;/* $shape = ShapeRegistry::getShape($target->getWorld(), ShapeRegistry::TYPE_FLOOD, self::compoundToArray($settings)); @@ -336,6 +339,25 @@ public static function floodArea(Block $target, CompoundTag $settings, Session $ return self::fillAsync($shape, $session, self::blockParser($shape->options['blocks'], $messages, $error), $flags);*/ } + public static function placeAsset(Position $target, Asset $asset, CompoundTag $settings, UserSession $session): bool + { + + #$start = clone $target->asVector3()->floor()->addVector($asset->getOrigin())->floor();//start pos of paste//TODO if using rotate, this fails + #$end = $start->addVector($asset->getSize()->subtractVector($asset->getOrigin()));//add size + //[$target->x,$target->y,$target->z] = [($v=$target->asVector3())->getFloorX(),$v->getFloorY(),$v->getFloorZ()]; + $start = clone $target->asVector3();//start pos of paste//TODO if using rotate, this fails + $end = $start->addVector($asset->getSize());//add size + $shape = Cuboid::constructFromPositions(Vector3::minComponents($start, $end), Vector3::maxComponents($start, $end)); + $offset = new Vector3($asset->getSize()->getX() / 2, 0, $asset->getSize()->getZ() / 2); + #$shape->setPasteVector($target->asVector3()->subtract(($asset->getSize()->getX()) / 2, 0, ($asset->getSize()->getZ()) / 2));//TODO this causes a size +0.5x +0.5z shift + $selection = new Selection($session->getUUID(), $target->getWorld()); + $selection->setShape($shape); + $aabb = $shape->getAABB(); + $touchedChunks = self::getAABBTouchedChunksTemp($target->getWorld(), $aabb->offset(-$offset->getX(), -$offset->getY(), -$offset->getZ()));//TODO clean up or move somewhere else. Better not touch, it works. + Server::getInstance()->getAsyncPool()->submitTask(new AsyncPasteAssetTask($session->getUUID(), $target->asVector3(), $selection, $touchedChunks, $asset)); + return true; + } + /// SCHEMATIC RELATED API PART /** @@ -417,22 +439,15 @@ public static function hasFlag(int $flags, int $check): bool * @param string $fullstring * @param array $messages * @param bool $error - * @return Block[] - * @throws RuntimeException + * @return BlockPalette + * @throws BlockQueryAlreadyParsedException * @throws InvalidArgumentException - * @throws InvalidBlockStateException + * @deprecated Use BlockPalette::fromString() */ - public static function blockParser(string $fullstring, array &$messages, bool &$error): array + public static function blockParser(string $fullstring, array &$messages, bool &$error): BlockPalette { BlockFactory::getInstance(); - $blocks = BlockStatesParser::getInstance()::fromString($fullstring, true); - foreach ($blocks as $block) { - if ($block instanceof UnknownBlock) { - $messages[] = TF::GOLD . $block . " is an unknown block"; - } - } - - return $blocks; + return BlockPalette::fromString($fullstring); } /** @@ -442,7 +457,7 @@ public static function blockParser(string $fullstring, array &$messages, bool &$ * @return float|int * @throws CalculationException */ - public static function evalAsMath(string $str) + public static function evalAsMath(string $str): float|int { $error = false; $div_mul = false; @@ -452,12 +467,12 @@ public static function evalAsMath(string $str) $str = preg_replace('/[^\d.+\-*\/]/', '', $str); $str = rtrim(trim($str, '/*+'), '-'); - if ((strpos($str, '/') !== false || strpos($str, '*') !== false)) { + if ((str_contains($str, '/') || str_contains($str, '*'))) { $div_mul = true; $operators = ['*', '/']; while (!$error && !empty($operators)) { $operator = array_pop($operators); - while ($operator !== null && strpos($str, $operator) !== false) { + while ($operator !== null && str_contains($str, $operator)) { $regex = '/([\d\.]+)\\' . $operator . '(\-?[\d\.]+)/'; preg_match($regex, $str, $matches); if (isset($matches[1], $matches[2])) { @@ -480,7 +495,7 @@ public static function evalAsMath(string $str) } } - if (!$error && (strpos($str, '+') !== false || strpos($str, '-') !== false)) { + if (!$error && (str_contains($str, '+') || str_contains($str, '-'))) { $add_sub = true; preg_match_all('/([\d.]+|[+\-])/', $str, $matches); if (isset($matches[0])) { @@ -534,7 +549,22 @@ public static function compoundToArray(CompoundTag $compoundTag): array */ public static function setComponents(Block $block, int $x, int $y, int $z): Block { - [$block->getPos()->x, $block->getPos()->y, $block->getPos()->z] = [$x, $y, $z]; + [$block->getPosition()->x, $block->getPosition()->y, $block->getPosition()->z] = [$x, $y, $z]; return $block; } + + public static function vecToString(Vector3 $v): string + { + return TF::RESET . "[" . TF::RED . $v->getFloorX() . TF::RESET . ":" . TF::GREEN . $v->getFloorY() . TF::RESET . ":" . TF::AQUA . $v->getFloorZ() . TF::RESET . "]"; + } + + public static function boolToString(bool $b): string + { + return $b ? TF::RESET . TF::GREEN . "On" . TF::RESET : TF::RESET . TF::RED . "Off" . TF::RESET; + } + + public static function positiveModulo(int $i, int $n): int + { + return ($i % $n + $n) % $n; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/EventListener.php b/src/xenialdan/MagicWE2/EventListener.php index 81c8220e..bdc785ea 100644 --- a/src/xenialdan/MagicWE2/EventListener.php +++ b/src/xenialdan/MagicWE2/EventListener.php @@ -6,14 +6,18 @@ use Exception; use InvalidArgumentException; use InvalidStateException; +use jojoe77777\FormAPI\ModalForm; use JsonException; +use pocketmine\block\BlockLegacyIds; use pocketmine\entity\InvalidSkinException; use pocketmine\event\block\BlockBreakEvent; use pocketmine\event\Listener; use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerInteractEvent; +use pocketmine\event\player\PlayerItemHeldEvent; use pocketmine\event\player\PlayerItemUseEvent; use pocketmine\event\player\PlayerJoinEvent; +use pocketmine\event\player\PlayerMoveEvent; use pocketmine\event\player\PlayerQuitEvent; use pocketmine\item\ItemIds; use pocketmine\nbt\UnexpectedTagTypeException; @@ -23,7 +27,7 @@ use pocketmine\utils\TextFormat as TF; use pocketmine\world\Position; use RuntimeException; -use xenialdan\customui\windows\ModalForm; +use xenialdan\libstructure\tile\StructureBlockTile; use xenialdan\MagicWE2\event\MWESelectionChangeEvent; use xenialdan\MagicWE2\event\MWESessionLoadEvent; use xenialdan\MagicWE2\exception\SessionException; @@ -31,11 +35,12 @@ use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\tool\Brush; +use function var_dump; class EventListener implements Listener { /** @var Plugin */ - public $owner; + public Plugin $owner; public function __construct(Plugin $plugin) { @@ -44,7 +49,6 @@ public function __construct(Plugin $plugin) /** * @param PlayerJoinEvent $event - * @throws AssumptionFailedError * @throws InvalidSkinException * @throws JsonException * @throws RuntimeException @@ -65,20 +69,17 @@ public function onSessionLoad(MWESessionLoadEvent $event): void { Loader::getInstance()->wailaBossBar->addPlayer($event->getPlayer()); if (Loader::hasScoreboard()) { - try { - if (($session = $event->getSession()) instanceof UserSession && $session->isSidebarEnabled()) + $session = $event->getSession(); + if ($session instanceof UserSession && $session->isSidebarEnabled()) /** @var UserSession $session */ $session->sidebar->handleScoreboard($session); - } catch (InvalidArgumentException $e) { - Loader::getInstance()->getLogger()->logException($e); - } } } /** * @param PlayerQuitEvent $event - * @throws SessionException * @throws JsonException + * @throws SessionException */ public function onLogout(PlayerQuitEvent $event): void { @@ -132,6 +133,7 @@ public function onItemRightClick(PlayerItemUseEvent $event): void * @param BlockBreakEvent $event * @throws AssumptionFailedError * @throws Error + * @throws UnexpectedTagTypeException */ public function onBreak(BlockBreakEvent $event): void { @@ -146,13 +148,31 @@ public function onBreak(BlockBreakEvent $event): void } } +// /** +// * @param BlockPlaceEvent $event +// * @throws AssumptionFailedError +// * @throws Error +// */ +// public function onPlace(BlockPlaceEvent $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 { +// $this->onBreakBlock($event); +// } catch (Exception $error) { +// $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . "Interaction failed!"); +// $event->getPlayer()->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); +// } +// } +// } + /** * TODO use tool classes * @param BlockBreakEvent $event + * @throws AssumptionFailedError * @throws Error - * @throws SessionException * @throws InvalidArgumentException - * @throws AssumptionFailedError + * @throws SessionException */ private function onBreakBlock(BlockBreakEvent $event): void { @@ -165,11 +185,11 @@ private function onBreakBlock(BlockBreakEvent $event): void $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.wand.disabled")); break; } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $event->getBlock()->getPos()->getWorld())); // TODO check if the selection inside of the session updates + $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"); } - $selection->setPos1(new Position($event->getBlock()->getPos()->x, $event->getBlock()->getPos()->y, $event->getBlock()->getPos()->z, $event->getBlock()->getPos()->getWorld())); + $selection->setPos1(new Position($event->getBlock()->getPosition()->x, $event->getBlock()->getPosition()->y, $event->getBlock()->getPosition()->z, $event->getBlock()->getPosition()->getWorld())); break; } case ItemIds::STICK: @@ -187,15 +207,16 @@ private function onBreakBlock(BlockBreakEvent $event): void /** * TODO use tool classes * @param PlayerInteractEvent $event + * @throws AssumptionFailedError * @throws Error + * @throws InvalidArgumentException * @throws InvalidStateException * @throws SessionException - * @throws InvalidArgumentException - * @throws AssumptionFailedError + * @throws UnexpectedTagTypeException */ private function onRightClickBlock(PlayerInteractEvent $event): void { - if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE))) { + if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE)) || !is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET))) { $event->cancel(); $session = SessionHelper::getUserSession($event->getPlayer()); if (!$session instanceof UserSession) return; @@ -206,11 +227,11 @@ private function onRightClickBlock(PlayerInteractEvent $event): void $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.wand.disabled")); break; } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $event->getBlock()->getPos()->getWorld())); // TODO check if the selection inside of the session updates + $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"); } - $selection->setPos2(new Position($event->getBlock()->getPos()->x, $event->getBlock()->getPos()->y, $event->getBlock()->getPos()->z, $event->getBlock()->getPos()->getWorld())); + $selection->setPos2(new Position($event->getBlock()->getPosition()->x, $event->getBlock()->getPosition()->y, $event->getBlock()->getPosition()->z, $event->getBlock()->getPosition()->getWorld())); break; } case ItemIds::STICK: @@ -229,18 +250,39 @@ private function onRightClickBlock(PlayerInteractEvent $event): void #} break; } + case ItemIds::SCAFFOLDING: + { + $tag = $event->getItem()->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)) { + $event->getPlayer()->sendMessage("Asset placed!"); + } else { + $event->getPlayer()->sendMessage("Asset not placed!"); + } + } + break; + } + default: + { + var_dump($event->getItem()); + $event->cancel(); + break; + } } } } /** * @param PlayerInteractEvent $event + * @throws AssumptionFailedError * @throws Error + * @throws InvalidArgumentException * @throws InvalidStateException * @throws SessionException - * @throws InvalidArgumentException * @throws UnexpectedTagTypeException - * @throws AssumptionFailedError */ private function onLeftClickBlock(PlayerInteractEvent $event): void { @@ -255,11 +297,11 @@ private function onLeftClickBlock(PlayerInteractEvent $event): void $session->sendMessage(TF::RED . $session->getLanguage()->translateString("tool.wand.disabled")); break; } - $selection = $session->getLatestSelection() ?? $session->addSelection(new Selection($session->getUUID(), $event->getBlock()->getPos()->getWorld())); // TODO check if the selection inside of the session updates + $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"); } - $selection->setPos1(new Position($event->getBlock()->getPos()->x, $event->getBlock()->getPos()->y, $event->getBlock()->getPos()->z, $event->getBlock()->getPos()->getWorld())); + $selection->setPos1(new Position($event->getBlock()->getPosition()->x, $event->getBlock()->getPosition()->y, $event->getBlock()->getPosition()->z, $event->getBlock()->getPosition()->getWorld())); break; } case ItemIds::STICK: @@ -298,7 +340,7 @@ private function onRightClickAir(PlayerItemUseEvent $event): void $session = SessionHelper::getUserSession($event->getPlayer()); if (!$session instanceof UserSession) return; $target = $event->getPlayer()->getTargetBlock(Loader::getInstance()->getToolDistance()); - $brush = $session->getBrushFromItem($event->getItem()); + $brush = $session->getBrushes()->getBrushFromItem($event->getItem()); var_dump(json_encode($brush, JSON_THROW_ON_ERROR)); if ($brush instanceof Brush && !is_null($target)) {// && has perms API::createBrush($target, $brush, $session); @@ -316,14 +358,17 @@ public function onDropItem(PlayerDropItemEvent $event): void $event->cancel(); $session = SessionHelper::getUserSession($event->getPlayer()); if (!$session instanceof UserSession) return; - $brush = $session->getBrushFromItem($event->getItem()); + $brush = $session->getBrushes()->getBrushFromItem($event->getItem()); if ($brush instanceof Brush) { - $form = new ModalForm(TF::BOLD . $brush->getName(), TF::RED . - "Delete" . TF::WHITE . " brush from session or " . TF::GREEN . "remove" . TF::WHITE . " from Inventory?" . TF::EOL . - implode(TF::EOL, $event->getItem()->getLore()), TF::BOLD . TF::DARK_RED . "Delete", TF::BOLD . TF::DARK_GREEN . "Remove"); - $form->setCallable(function (Player $player, $data) use ($session, $brush) { - $session->removeBrush($brush, $data); - }); + $form = (new ModalForm(function (Player $player, $data) use ($session, $brush) { + $session->getBrushes()->removeBrush($brush, $data); + })) + ->setTitle(TF::BOLD . $brush->getName()) + ->setContent(TF::RED . + "Delete" . TF::WHITE . " brush from session or " . TF::GREEN . "remove" . TF::WHITE . " from Inventory?" . TF::EOL . + implode(TF::EOL, $event->getItem()->getLore())) + ->setButton1(TF::BOLD . TF::DARK_RED . "Delete") + ->setButton2(TF::BOLD . TF::DARK_GREEN . "Remove"); $event->getPlayer()->sendForm($form); } } else if (!is_null($event->getItem()->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE))) { @@ -336,10 +381,98 @@ public function onDropItem(PlayerDropItemEvent $event): void public function onSelectionChange(MWESelectionChangeEvent $event): void { - Loader::getInstance()->getLogger()->debug("Called " . $event->getEventName()); - if (($session = $event->getSession()) instanceof UserSession && ($player = $event->getPlayer()) !== null) { + #Loader::getInstance()->getLogger()->debug("Called " . $event->getEventName()); + $session = $event->getSession(); + if ($session instanceof UserSession && $event->getPlayer() !== null) { /** @var UserSession $session */ + if ($session->isOutlineEnabled()) $session->createOrUpdateOutline($event->getSelection()); $session->sidebar->handleScoreboard($session); } } + + /** + * TODO use tool classes + * @param PlayerItemHeldEvent $event + */ + public function onChangeSlot(PlayerItemHeldEvent $event): void + { + /*var_dump($event->getSlot()); + $player = $event->getPlayer(); + #$item = $player->getInventory()->getItemInHand(); + $item = $player->getInventory()->getItem($event->getSlot()); + $session = SessionHelper::getUserSession($player); + if (!is_null(($tag = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET)))) { + if (!$session instanceof UserSession) return; + if ($item->getId() === ItemIds::SCAFFOLDING) { + $filename = $tag->getString('filename'); + $asset = Loader::$assetCollection->assets[$filename];//TODO allow private assets again + var_dump($filename, $asset); + #$assets = AssetCollection::getInstance()->getPlayerAssets($player->getXuid()); + $session->setOutlineEnabled(true); + #foreach ($assets as $asset) { + $backwards = $player->getDirectionVector()->normalize()->multiply(-1); + $target = $player->getTargetBlock(10)->getPosition(); + $target->addVector($backwards);//this selects the block before raytrace + $target->subtract(0, 1, 0);//one block down + $target = Position::fromObject($target, $player->getWorld()); + if (/*$session->displayOutline && * / self::sendOutline($player, $target, $asset, $session)) { + $player->sendMessage("Added asset outline for $asset->filename!"); + } else { + $player->sendMessage("Did not add asset outline!"); + } + #} + } else { + $session->setOutlineEnabled(false); + } + } else { + $session->setOutlineEnabled(false); + }*/ + } + + 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); + $tile = $blockTouched->getPosition()->getWorld()->getTile($blockTouched->getPosition()->asVector3()); + 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)))) { +// $session = SessionHelper::getUserSession($player); +// if (!$session instanceof UserSession) return; +// if ($item->getId() === ItemIds::SCAFFOLDING) { +// $filename = $tag->getString('filename'); +// $asset = AssetCollection::getInstance()->assets->get($filename); +// /** @var StructureBlockInventory $inventory */ +// $inventory = $tile->getInventory(); +// $pk = new StructureBlockUpdatePacket(); +// $pk->structureEditorData = $tile->getStructureEditorData($asset); +// [$pk->x, $pk->y, $pk->z] = [$blockTouched->getPosition()->getFloorX(), $blockTouched->getPosition()->getFloorY(), $blockTouched->getPosition()->getFloorZ()]; +// $pk->isPowered = false; +// $player->getNetworkSession()->sendDataPacket($pk); +// $tile->sendInventory($player); +// var_dump($tile->getSpawnCompound()->toString(), $inventory); +// } +// } + var_dump($blockTouched); + } + } + + } + + public function onStructureBlockOutOfView(PlayerMoveEvent $event): void + { + if ($event->getFrom()->floor()->equals($event->getTo()->floor())) return; + $player = $event->getPlayer(); + $session = SessionHelper::getUserSession($player); + if (!$session instanceof UserSession) return; + $selection = $session->getLatestSelection(); + if (!$selection instanceof Selection) return; + if (!$session->isOutlineEnabled()) return; + if (!$player->isUsingChunk($session->getOutline()->getPosition()->getFloorX() >> 4, $session->getOutline()->getPosition()->getFloorZ())) { + $session->createOrUpdateOutline($selection); + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/Loader.php b/src/xenialdan/MagicWE2/Loader.php index 09ed6912..96d6e669 100644 --- a/src/xenialdan/MagicWE2/Loader.php +++ b/src/xenialdan/MagicWE2/Loader.php @@ -6,6 +6,7 @@ use InvalidArgumentException; use jackmd\scorefactory\ScoreFactory; +use jojoe77777\FormAPI\FormAPI; use JsonException; use muqsit\invmenu\InvMenuHandler; use pocketmine\block\Block; @@ -22,7 +23,7 @@ use pocketmine\utils\TextFormat as TF; use RuntimeException; use xenialdan\apibossbar\DiverseBossBar; -use xenialdan\customui\API as CustomUIAPI; +use xenialdan\libstructure\PacketListener; use xenialdan\MagicWE2\commands\biome\BiomeInfoCommand; use xenialdan\MagicWE2\commands\biome\BiomeListCommand; use xenialdan\MagicWE2\commands\biome\SetBiomeCommand; @@ -34,7 +35,9 @@ use xenialdan\MagicWE2\commands\clipboard\FlipCommand; 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; use xenialdan\MagicWE2\commands\DonateCommand; use xenialdan\MagicWE2\commands\generation\CylinderCommand; use xenialdan\MagicWE2\commands\HelpCommand; @@ -44,6 +47,7 @@ use xenialdan\MagicWE2\commands\InfoCommand; use xenialdan\MagicWE2\commands\LanguageCommand; use xenialdan\MagicWE2\commands\LimitCommand; +use xenialdan\MagicWE2\commands\palette\PaletteCommand; use xenialdan\MagicWE2\commands\region\ReplaceCommand; use xenialdan\MagicWE2\commands\region\SetCommand; use xenialdan\MagicWE2\commands\ReportCommand; @@ -56,13 +60,14 @@ use xenialdan\MagicWE2\commands\selection\Pos1Command; use xenialdan\MagicWE2\commands\selection\Pos2Command; use xenialdan\MagicWE2\commands\SetRangeCommand; -use xenialdan\MagicWE2\commands\TestCommand; use xenialdan\MagicWE2\commands\tool\DebugCommand; use xenialdan\MagicWE2\commands\tool\FloodCommand; use xenialdan\MagicWE2\commands\tool\ToggledebugCommand; use xenialdan\MagicWE2\commands\tool\TogglewandCommand; use xenialdan\MagicWE2\commands\tool\WandCommand; use xenialdan\MagicWE2\commands\utility\CalculateCommand; +use xenialdan\MagicWE2\commands\utility\ToggleOutlineCommand; +use xenialdan\MagicWE2\commands\utility\ToggleSidebarCommand; use xenialdan\MagicWE2\commands\utility\ToggleWailaCommand; use xenialdan\MagicWE2\commands\VersionCommand; use xenialdan\MagicWE2\exception\ActionRegistryException; @@ -71,35 +76,43 @@ use xenialdan\MagicWE2\helper\BlockStatesParser; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\selection\shape\ShapeRegistry; +use xenialdan\MagicWE2\session\data\AssetCollection; +use xenialdan\MagicWE2\session\PluginSession; use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\task\action\ActionRegistry; class Loader extends PluginBase { public const FAKE_ENCH_ID = 201; - public const PREFIX = TF::BOLD . TF::GOLD . "[MagicWE2]" . TF::RESET . " "; + 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 $instance; + private static ?Loader $instance; /** @var null|ShapeRegistry */ - public static $shapeRegistry; + public static ?ShapeRegistry $shapeRegistry; /** @var null|ActionRegistry */ - public static $actionRegistry; + public static ?ActionRegistry $actionRegistry; /** @var Enchantment */ - public static $ench; + public static Enchantment $ench; /** @var Language */ - private $baseLang; + private Language $baseLang; /** @var string[] Donator names */ - public $donators = []; + public array $donators = []; /** @var string */ - public $donatorData = ""; + public string $donatorData = ""; /** @var string */ - private static $rotPath; + private static string $rotPath; /** @var string */ - private static $doorRotPath; + private static string $doorRotPath; /** @var DiverseBossBar */#BossBar - public $wailaBossBar; + public DiverseBossBar $wailaBossBar; /** @var null|string */ - public static $scoreboardAPI; + public static ?string $scoreboardAPI; + /** @var AssetCollection */ + public static AssetCollection $assetCollection; /** * Returns an instance of the plugin @@ -146,12 +159,12 @@ public static function hasScoreboard(): bool * @throws RuntimeException * @throws JsonException * @throws AssumptionFailedError + * @throws InvalidArgumentException */ public function onLoad(): void { self::$instance = $this; - self::$ench = new Enchantment(self::FAKE_ENCH_ID, "", 0, ItemFlags::AXE, ItemFlags::NONE, 1); - /** @var EnchantmentIdMap $enchantmapinstance */ + self::$ench = new Enchantment("", 0, ItemFlags::AXE, ItemFlags::NONE, 1); $enchantmapinstance = EnchantmentIdMap::getInstance(); $enchantmapinstance->register(self::FAKE_ENCH_ID, self::$ench); self::$shapeRegistry = new ShapeRegistry(); @@ -162,7 +175,6 @@ public function onLoad(): void self::$rotPath = $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "rotation_flip_data.json"; self::$doorRotPath = $this->getFile() . "resources" . DIRECTORY_SEPARATOR . "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"; @@ -173,7 +185,9 @@ public function onLoad(): void } $blockstateparserInstance->setAliasMap(json_decode($fileGetContents, true, 512, JSON_THROW_ON_ERROR)); - #StructureStore::getInstance(); + //$blockstateparserInstance::runTests();//TODO REMOVE, DEBUG!!!!!!! + + self::$assetCollection = new AssetCollection(new PluginSession($this)); } /** @@ -197,10 +211,10 @@ 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); - if ($this->getConfig()->get("show-startup-icon", false)) $this->showStartupIcon(); - //$this->loadDonator(); + $registerDeveloperCommands = $this->getConfig()->get("developer-commands", false); $this->getLogger()->warning("WARNING! Commands and their permissions changed! Make sure to update your permission sets!"); 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", [ @@ -249,7 +263,7 @@ public function onEnable(): void //new PumpkinsCommand($this,"/pumpkins", "Generate pumpkin patches"), /* -- clipboard -- */ new CopyCommand($this, "/copy", "Copy the selection to the clipboard"), - new PasteCommand($this, "/paste", "Paste the clipboard’s contents"), + new PasteCommand($this, "/paste", "Paste the clipboard's contents"), new CutCommand($this, "/cut", "Cut the selection to the clipboard"), new Cut2Command($this, "/cut2", "Cut the selection to the clipboard - the new way"), new ClearClipboardCommand($this, "/clearclipboard", "Clear your clipboard"), @@ -272,7 +286,6 @@ public function onEnable(): void //new TogglePlaceCommand($this,"/toggleplace", "Switch between your position and pos1 for placement"), //new SearchItemCommand($this,"/searchitem", "Search for an item"), //new RangeCommand($this,"/range", "Set the brush range"), - new TestCommand($this, "/test", "test action"),//TODO REMOVE new SetRangeCommand($this, "/setrange", "Set tool range", ["/toolrange"]), new LimitCommand($this, "/limit", "Set the block change limit. Use -1 to disable"), new HelpCommand($this, "/help", "MagicWE help command", ["/?", "/mwe", "/wehelp"]),//Blame MCPE for client side /help shit! only the aliases work @@ -294,19 +307,29 @@ public function onEnable(): void //new ThawCommand($this,"/thaw", "Thaws blocks in the selection"), new CalculateCommand($this, "/calculate", "Evaluate a mathematical expression", ["/calc", "/eval", "/evaluate", "/solve"]), new ToggleWailaCommand($this, "/togglewaila", "Toggle the What Am I Looking At utility", ["/waila", "/wyla"]), - /* -- debugging -- */ + 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", [ + /* -- 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(CustomUIAPI::class)) { - $this->getLogger()->notice("CustomUI found, can use ui-based commands"); + if (class_exists(FormAPI::class)) { + $this->getLogger()->notice("FormAPI found, can use ui-based commands"); $this->getServer()->getCommandMap()->registerAll("MagicWE2", [ + /* -- assets -- */ + #new AssetCommand($this, "/asset", "Manage assets (schematics, structures, clipboard)"), /* -- brush -- */ new BrushCommand($this, "/brush", "Opens the brush tool menu"), + /* -- palette -- */ + new PaletteCommand($this, "/palette", "Manage block palettes"), /* -- tool -- */ new FloodCommand($this, "/flood", "Opens the flood fill tool menu", ["/floodfill"]), ]); } else { - $this->getLogger()->notice(TF::RED . "CustomUI NOT found, can NOT use ui-based commands"); + $this->getLogger()->notice(TF::RED . "FormAPI NOT found, can NOT use ui-based commands"); } if (class_exists(ScoreFactory::class)) { $this->getLogger()->notice("Scoreboard API found, can use scoreboards"); @@ -314,33 +337,6 @@ public function onEnable(): void } else { $this->getLogger()->notice(TF::RED . "Scoreboard API NOT found, can NOT use scoreboards"); } -// .mcstructure loading tests -// $world = self::getInstance()->getServer()->getWorldManager()->getDefaultWorld(); -// if ($world !== null) { -// $spawn = $world->getSafeSpawn()->asVector3(); -// $structureFiles = glob($this->getDataFolder() . 'structures' . DIRECTORY_SEPARATOR . "*.mcstructure"); -// if ($structureFiles !== false) -// foreach ($structureFiles as $file) { -// $this->getLogger()->debug(TF::GOLD . "Loading " . basename($file)); -// try { -// /** @var StructureStore $instance */ -// $instance = StructureStore::getInstance(); -// $structure = $instance->loadStructure(basename($file)); -// //this will dump wrong blocks for now -// foreach ($structure->blocks() as $block) { -// #$this->getLogger()->debug($block->getPos()->asVector3() . ' ' . BlockStatesParser::printStates(BlockStatesParser::getStateByBlock($block), false)); -// $world->setBlock(($at = $spawn->addVector($block->getPos()->asVector3())), $block); -// if (($tile = $structure->translateBlockEntity(Position::fromObject($block->getPos()->asVector3(), $world), $at)) instanceof Tile) { -// $tileAt = $world->getTileAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()); -// if ($tileAt !== null) $world->removeTile($tileAt); -// $world->addTile($tile); -// } -// } -// } catch (Exception $e) { -// $this->getLogger()->debug($e->getMessage()); -// } -// } -// } //register WAILA bar $this->wailaBossBar = new DiverseBossBar(); @@ -361,7 +357,7 @@ public function onEnable(): void if ($stateEntry instanceof BlockStatesEntry) { $sub = implode("," . TF::EOL, explode(",", BlockStatesParser::printStates($stateEntry, false))); } - $distancePercentage = round(floor($block->getPos()->distance($player->getEyePos())) / 10, 1); + $distancePercentage = round(floor($block->getPosition()->distance($player->getEyePos())) / 10, 1); Loader::getInstance()->wailaBossBar->setTitleFor([$player], $title)->setSubTitleFor([$player], $sub)->setPercentage($distancePercentage); } else Loader::getInstance()->wailaBossBar->hideFrom([$player]); @@ -416,49 +412,15 @@ public static function getInfo(): array "| Plugin API Version | " . implode(", ", self::getInstance()->getDescription()->getCompatibleApis()) . " |", "| Authors | " . implode(", ", self::getInstance()->getDescription()->getAuthors()) . " |", "| Enabled | " . (Server::getInstance()->getPluginManager()->isPluginEnabled(self::getInstance()) ? TF::GREEN . "Yes" : TF::RED . "No") . TF::RESET . " |", - "| Uses UI | " . (class_exists(CustomUIAPI::class) ? TF::GREEN . "Yes" : TF::RED . "No") . TF::RESET . " |", + "| Uses UI | " . (class_exists(FormAPI::class) ? TF::GREEN . "Yes" : TF::RED . "No") . TF::RESET . " |", "| Uses ScoreFactory | " . (class_exists(ScoreFactory::class) ? TF::GREEN . "Yes" : TF::RED . "No") . TF::RESET . " |", - "| Phar | " . (strpos(self::getInstance()->getFile(), 'phar:') !== false ? TF::GREEN . "Yes" : TF::RED . "No") . TF::RESET . " |", + "| Phar | " . (str_contains(self::getInstance()->getFile(), 'phar:') ? TF::GREEN . "Yes" : TF::RED . "No") . TF::RESET . " |", "| PMMP Protocol Version | " . Server::getInstance()->getVersion() . " |", "| PMMP Version | " . Server::getInstance()->getPocketMineVersion() . " |", "| PMMP API Version | " . Server::getInstance()->getApiVersion() . " |", ]; } - private function showStartupIcon(): void - { - $colorAxe = TF::BOLD . TF::DARK_PURPLE; - $colorAxeStem = TF::LIGHT_PURPLE; - $colorAxeSky = TF::LIGHT_PURPLE; - $colorAxeFill = TF::GOLD; - $axe = [ - " {$colorAxe}####{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeFill}####{$colorAxe}##{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeFill}######{$colorAxe}##{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeFill}########{$colorAxe}####{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeFill}######{$colorAxe}##{$colorAxeStem}##{$colorAxe}##{$colorAxeSky} ", - " {$colorAxe}######{$colorAxeStem}##{$colorAxe}##{$colorAxeFill}##{$colorAxe}##", - " {$colorAxe}##{$colorAxeStem}##{$colorAxe}##{$colorAxeFill}####{$colorAxe}##", - " {$colorAxe}##{$colorAxeStem}##{$colorAxe}## {$colorAxe}####{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeStem}##{$colorAxe}##{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeStem}##{$colorAxe}##{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeStem}##{$colorAxe}##{$colorAxeSky} ", - " {$colorAxe}##{$colorAxeStem}##{$colorAxe}##{$colorAxeSky} ", - "{$colorAxe}##{$colorAxeStem}##{$colorAxe}##{$colorAxeSky} MagicWE v.2", - "{$colorAxe}####{$colorAxeSky} by XenialDan"]; - foreach (array_map(static function ($line) { - return preg_replace_callback( - '/ +(?getLogger()->info($axeMsg); - } - /** * Returns the path to the language files folder. * diff --git a/src/xenialdan/MagicWE2/clipboard/Clipboard.php b/src/xenialdan/MagicWE2/clipboard/Clipboard.php index 997776ed..e8f8b707 100644 --- a/src/xenialdan/MagicWE2/clipboard/Clipboard.php +++ b/src/xenialdan/MagicWE2/clipboard/Clipboard.php @@ -26,10 +26,8 @@ abstract class Clipboard implements Serializable public const FLIP_NORTH = 0x03; public const FLIP_SOUTH = 0x03; - /** @var int|null */ - public $worldId; - /** @var string */ - public $customName = ""; + public ?int $worldId = null; + public string $customName = ""; /** * Creates a chunk manager used for async editing @@ -38,7 +36,7 @@ abstract class Clipboard implements Serializable */ public static function getChunkManager(array $chunks): AsyncChunkManager { - $manager = new AsyncChunkManager(); + $manager = new AsyncChunkManager(0, World::Y_MAX); foreach ($chunks as $hash => $chunk) { World::getXZ($hash, $x, $z); $manager->setChunk($x, $z, $chunk); @@ -46,11 +44,11 @@ public static function getChunkManager(array $chunks): AsyncChunkManager return $manager; } - /** - * @return World - * @throws Exception - */ - public function getWorld(): World + /** + * @return World + * @throws Exception + */ + public function getWorld(): World { if (is_null($this->worldId)) { throw new SelectionException("World is not set!"); @@ -70,27 +68,27 @@ public function setWorld(World $world): void $this->worldId = $world->getId(); } - /** - * @return int - */ - public function getWorldId(): int - { + /** + * @return int + */ + public function getWorldId(): int + { return $this->worldId; - } + } - /** - * @return string - */ - public function getCustomName(): string - { - return $this->customName; - } + /** + * @return string + */ + public function getCustomName(): string + { + return $this->customName; + } - /** - * @param string $customName - */ - public function setCustomName(string $customName): void - { - $this->customName = $customName; - } + /** + * @param string $customName + */ + public function setCustomName(string $customName): void + { + $this->customName = $customName; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php b/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php index 558bfa92..271993a8 100644 --- a/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php +++ b/src/xenialdan/MagicWE2/clipboard/RevertClipboard.php @@ -6,6 +6,7 @@ use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; +use pocketmine\world\Position; class RevertClipboard extends Clipboard { @@ -13,19 +14,19 @@ class RevertClipboard extends Clipboard * @var Chunk[] * @phpstan-var array */ - public $chunks = []; + public array $chunks = []; /** * @var array[] - * @phpstan-var array + * @phpstan-var array */ - public $blocksAfter; + public array $blocksAfter; /** * RevertClipboard constructor. * @param int $worldId * @param Chunk[] $chunks * @param array[] $blocksAfter //CHANGED AS HACK - * @phpstan-param array $blocksAfter + * @phpstan-param array $blocksAfter */ public function __construct(int $worldId, array $chunks = [], array $blocksAfter = []) { @@ -38,13 +39,13 @@ public function __construct(int $worldId, array $chunks = [], array $blocksAfter * 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() - { + * @since 5.1.0 + */ + public function serialize(): string + { $chunks = []; foreach ($this->chunks as $hash => $chunk) { - $chunks[$hash] = FastChunkSerializer::serialize($chunk); + $chunks[$hash] = FastChunkSerializer::serializeTerrain($chunk); } return serialize([ $this->worldId, @@ -53,24 +54,23 @@ public function serialize() ]); } - /** + /** * Constructs the object * @link http://php.net/manual/en/serializable.unserialize.php - * @param string $serialized

+ * @param string $data

* The string representation of the object. *

* @return void * @since 5.1.0 - * @noinspection PhpMissingParamTypeInspection */ - public function unserialize($serialized) - { + public function unserialize($data) + { [ $this->worldId, $chunks, $this->blocksAfter - ] = unserialize($serialized/*, ['allowed_classes' => [__CLASS__]]*/);//TODO test pm4 + ] = unserialize($data/*, ['allowed_classes' => [__CLASS__]]*/);//TODO test pm4 foreach ($chunks as $hash => $chunk) - $this->chunks[$hash] = FastChunkSerializer::deserialize($chunk); + $this->chunks[$hash] = FastChunkSerializer::deserializeTerrain($chunk); } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/clipboard/SingleClipboard.php b/src/xenialdan/MagicWE2/clipboard/SingleClipboard.php index 5715a006..18164845 100644 --- a/src/xenialdan/MagicWE2/clipboard/SingleClipboard.php +++ b/src/xenialdan/MagicWE2/clipboard/SingleClipboard.php @@ -13,11 +13,11 @@ class SingleClipboard extends Clipboard { /** @var BlockEntry[] */ - private $entries = []; + private array $entries = []; /** @var Selection */ - public $selection; + public Selection $selection; /** @var Vector3 */ - public $position; + public Vector3 $position; /** * SingleClipboard constructor. @@ -42,7 +42,7 @@ public function clear(): void * @param int|null $x * @param int|null $y * @param int|null $z - * @return Generator|BlockEntry[] + * @return Generator */ public function iterateEntries(?int &$x, ?int &$y, ?int &$z): Generator { @@ -55,41 +55,40 @@ public function iterateEntries(?int &$x, ?int &$y, ?int &$z): Generator 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, - $this->position - ]); - } + /** + * 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(): string + { + // TODO: Implement serialize() method. + return serialize([ + $this->entries, + $this->selection, + $this->position + ]); + } /** * Constructs the object * @link https://php.net/manual/en/serializable.unserialize.php - * @param string $serialized

+ * @param string $data

* The string representation of the object. *

* @return void * @since 5.1 - * @noinspection PhpMissingParamTypeInspection */ - public function unserialize($serialized) - { - // TODO: Implement unserialize() method. - [ + public function unserialize($data) + { + // TODO: Implement unserialize() method. + [ $this->entries, $this->selection, $this->position - ] = unserialize($serialized/*, ['allowed_classes' => [BlockEntry::class, Selection::class, Vector3::class]]*/); - } + ] = unserialize($data/*, ['allowed_classes' => [BlockEntry::class, Selection::class, Vector3::class]]*/); + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/commands/DonateCommand.php b/src/xenialdan/MagicWE2/commands/DonateCommand.php index 7ed4ce88..8222a77f 100644 --- a/src/xenialdan/MagicWE2/commands/DonateCommand.php +++ b/src/xenialdan/MagicWE2/commands/DonateCommand.php @@ -26,27 +26,22 @@ protected function prepare(): void $this->setPermission("we.command.donate"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - try { - $name = TF::DARK_PURPLE . "[" . TF::GOLD . "XenialDan" . TF::DARK_PURPLE . "] "; - $sender->sendMessage($name . "Greetings! Would you like to buy me an energy drink to stay awake during coding sessions?"); - $sender->sendMessage($name . "Donations are welcomed! Consider donating on " . TF::DARK_AQUA . "Pay" . TF::AQUA . "Pal:"); - $sender->sendMessage($name . TF::DARK_AQUA . "https://www.paypal.me/xenialdan"); - $sender->sendMessage($name . "Thank you! With " . TF::BOLD . TF::RED . "<3" . TF::RESET . TF::DARK_PURPLE . " - MagicWE2 by https://github.com/thebigsmileXD"); - $colorHeart = (random_int(0, 1) === 1 ? TF::DARK_RED : TF::DARK_PURPLE); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + try { + $name = TF::LIGHT_PURPLE . "[" . TF::GOLD . "XenialDan" . TF::LIGHT_PURPLE . "] "; + $sender->sendMessage($name . "Greetings! Would you like to buy me an energy drink to stay awake during coding sessions?"); + $sender->sendMessage($name . "Donations are welcomed! Consider donating on " . TF::DARK_AQUA . "Pay" . TF::AQUA . "Pal:"); + $sender->sendMessage($name . TF::DARK_AQUA . "https://www.paypal.me/xenialdan"); + $sender->sendMessage($name . "Thank you! With " . TF::BOLD . TF::RED . "<3" . TF::RESET . TF::LIGHT_PURPLE . " - MagicWE2 by https://github.com/thebigsmileXD"); + $colorHeart = (random_int(0, 1) === 1 ? TF::DARK_RED : TF::LIGHT_PURPLE); $sender->sendMessage( TF::BOLD . $colorHeart . " **** **** " . TF::EOL . TF::BOLD . $colorHeart . " ** ** ** ** " . TF::EOL . diff --git a/src/xenialdan/MagicWE2/commands/HelpCommand.php b/src/xenialdan/MagicWE2/commands/HelpCommand.php index e3025f90..c9ecfe70 100644 --- a/src/xenialdan/MagicWE2/commands/HelpCommand.php +++ b/src/xenialdan/MagicWE2/commands/HelpCommand.php @@ -30,25 +30,20 @@ protected function prepare(): void $this->setPermission("we.command.help"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - try { - $cmds = []; - if (empty($args["command"])) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + try { + $cmds = []; + if (empty($args["command"])) { foreach (array_filter(Loader::getInstance()->getServer()->getCommandMap()->getCommands(), static function (Command $command) use ($sender) { - return strpos($command->getName(), "/") !== false && $command->testPermissionSilent($sender); + return str_contains($command->getName(), "/") && $command->testPermissionSilent($sender); }) as $cmd) { /** @var Command $cmd */ $cmds[$cmd->getName()] = $cmd; @@ -60,15 +55,15 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $sender->sendMessage(TF::RED . str_replace("/", "//", $lang->translateString("%commands.generic.notFound"))); return; } - foreach ($cmds as $command) { - $message = TF::LIGHT_PURPLE . "/" . $command->getName(); - if (!empty(($aliases = $command->getAliases()))) { - foreach ($aliases as $i => $alias) { - $aliases[$i] = "/" . $alias; - } - $message .= TF::DARK_PURPLE . " [" . implode(",", $aliases) . "]"; + foreach ($cmds as $command) { + $message = TF::RESET . TF::BOLD . TF::GOLD . "/" . $command->getName(); + if (!empty(($aliases = $command->getAliases()))) { + foreach ($aliases as $i => $alias) { + $aliases[$i] = "/" . $alias; + } + $message .= TF::RESET . TF::LIGHT_PURPLE . " [" . implode(",", $aliases) . "]"; } - $message .= TF::AQUA . " " . $command->getDescription() . TF::EOL . " - " . $command->getUsage(); + $message .= TF::RESET . TF::WHITE . " " . $command->getDescription() . TF::EOL . " » " . $command->getUsage(); $sender->sendMessage($message); } } catch (Exception $error) { diff --git a/src/xenialdan/MagicWE2/commands/InfoCommand.php b/src/xenialdan/MagicWE2/commands/InfoCommand.php index 905a0419..6f9f5bb3 100644 --- a/src/xenialdan/MagicWE2/commands/InfoCommand.php +++ b/src/xenialdan/MagicWE2/commands/InfoCommand.php @@ -26,21 +26,16 @@ protected function prepare(): void $this->setPermission("we.command.info"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - try { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + try { $sender->sendMessage(rtrim(Loader::PREFIX, " ") . " " . $lang->translateString('command.info.title')); foreach (Loader::getInfo() as $i => $line) { if ($i <= 1) continue; diff --git a/src/xenialdan/MagicWE2/commands/LanguageCommand.php b/src/xenialdan/MagicWE2/commands/LanguageCommand.php index 4f5a40a2..d2b8ad0b 100644 --- a/src/xenialdan/MagicWE2/commands/LanguageCommand.php +++ b/src/xenialdan/MagicWE2/commands/LanguageCommand.php @@ -8,16 +8,17 @@ use CortexPE\Commando\exception\ArgumentOrderException; use Exception; use InvalidArgumentException; +use jojoe77777\FormAPI\CustomForm; use pocketmine\command\CommandSender; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; -use xenialdan\customui\elements\Dropdown; -use xenialdan\customui\elements\Label; -use xenialdan\customui\windows\CustomForm; use xenialdan\MagicWE2\commands\args\LanguageArgument; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; +use function array_search; +use function array_values; +use function is_string; class LanguageCommand extends BaseCommand { @@ -33,45 +34,39 @@ protected function prepare(): void $this->setPermission("we.command.language"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - if (isset($args["language"])) { + } + if (isset($args["language"])) { $session->setLanguage((string)$args["language"]); return; } - $languages = Loader::getInstance()->getLanguageList(); - $form = new CustomForm(Loader::PREFIX . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.language.title')); - $form->addElement(new Label($lang->translateString('ui.language.label'))); - $dropdown = new Dropdown($lang->translateString('ui.language.dropdown'), array_values($languages)); - $dropdown->setOptionAsDefault($session->getLanguage()->getName()); - $form->addElement($dropdown); - $form->setCallable(function (Player $player, $data) use ($session, $languages) { - $langShort = array_search($data[1], $languages, true); + $languages = Loader::getInstance()->getLanguageList(); + $form = (new CustomForm(function (Player $player, $data) use ($session, $languages) { + $langShort = array_search(array_values($languages)[$data[1]], $languages, true); if (!is_string($langShort)) throw new InvalidArgumentException("Invalid data received"); $session->setLanguage($langShort); - }); + })) + ->setTitle(Loader::PREFIX_FORM . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.language.title')) + ->addLabel($lang->translateString('ui.language.label')) + ->addDropdown($lang->translateString('ui.language.dropdown'), array_values($languages), array_search($session->getLanguage()->getName(), array_values($languages), true)); + //$dropdown->setOptionAsDefault($session->getLanguage()->getName()); $sender->sendForm($form); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); diff --git a/src/xenialdan/MagicWE2/commands/LimitCommand.php b/src/xenialdan/MagicWE2/commands/LimitCommand.php index 80320b37..9d9190c4 100644 --- a/src/xenialdan/MagicWE2/commands/LimitCommand.php +++ b/src/xenialdan/MagicWE2/commands/LimitCommand.php @@ -31,23 +31,18 @@ protected function prepare(): void $this->setUsage("//limit [limit: int|-1]"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - try { - if (empty($args["limit"])) { - $limit = Loader::getInstance()->getConfig()->get("limit", -1); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + try { + if (empty($args["limit"])) { + $limit = Loader::getInstance()->getConfig()->get("limit", -1); $sender->sendMessage(Loader::PREFIX . TF::GREEN . $lang->translateString('command.limit.current', [($limit < 0 ? ucfirst(Loader::getInstance()->getLanguage()->translateString('disabled')) : $limit)])); } else { Loader::getInstance()->getConfig()->set("limit", (int)$args["limit"]); diff --git a/src/xenialdan/MagicWE2/commands/ReportCommand.php b/src/xenialdan/MagicWE2/commands/ReportCommand.php index 4712eb1d..291c11a7 100644 --- a/src/xenialdan/MagicWE2/commands/ReportCommand.php +++ b/src/xenialdan/MagicWE2/commands/ReportCommand.php @@ -10,7 +10,7 @@ use Exception; use InvalidArgumentException; use pocketmine\command\CommandSender; -use pocketmine\command\ConsoleCommandSender; +use pocketmine\console\ConsoleCommandSender; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\exception\SessionException; @@ -31,21 +31,16 @@ protected function prepare(): void $this->setPermission("we.command.report"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - try { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + try { $url = "Please report your bug with this link (link also in console)" . TF::EOL; $url .= "https://github.com/thebigsmileXD/MagicWE2/issues/new?labels=Bug&body="; $url .= urlencode( diff --git a/src/xenialdan/MagicWE2/commands/SetRangeCommand.php b/src/xenialdan/MagicWE2/commands/SetRangeCommand.php index cf7556c3..160e5d02 100644 --- a/src/xenialdan/MagicWE2/commands/SetRangeCommand.php +++ b/src/xenialdan/MagicWE2/commands/SetRangeCommand.php @@ -31,23 +31,18 @@ protected function prepare(): void $this->setUsage("//setrange [range: int]"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - try { - if (empty($args["range"])) { - $range = Loader::getInstance()->getToolDistance(); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + try { + if (empty($args["range"])) { + $range = Loader::getInstance()->getToolDistance(); $sender->sendMessage(Loader::PREFIX . TF::GREEN . $lang->translateString('command.setrange.current', [$range])); } else { Loader::getInstance()->getConfig()->set("tool-range", (int)$args["range"]); diff --git a/src/xenialdan/MagicWE2/commands/VersionCommand.php b/src/xenialdan/MagicWE2/commands/VersionCommand.php index 290d38e6..99437802 100644 --- a/src/xenialdan/MagicWE2/commands/VersionCommand.php +++ b/src/xenialdan/MagicWE2/commands/VersionCommand.php @@ -26,17 +26,12 @@ protected function prepare(): void $this->setPermission("we.command.version"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); } catch (SessionException $e) { } } diff --git a/src/xenialdan/MagicWE2/commands/args/BlocksArgument.php b/src/xenialdan/MagicWE2/commands/args/BlocksArgument.php new file mode 100644 index 00000000..ce8b4fd1 --- /dev/null +++ b/src/xenialdan/MagicWE2/commands/args/BlocksArgument.php @@ -0,0 +1,48 @@ += 1; + } + + /** + * @param string $argument + * @param CommandSender $sender + * + * @return BlockPalette + * @throws SessionException + */ + 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'); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + return BlockPalette::CREATE(); + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/commands/args/LanguageArgument.php b/src/xenialdan/MagicWE2/commands/args/LanguageArgument.php index 942f98dd..1ae054e2 100644 --- a/src/xenialdan/MagicWE2/commands/args/LanguageArgument.php +++ b/src/xenialdan/MagicWE2/commands/args/LanguageArgument.php @@ -22,7 +22,7 @@ public function getTypeName(): string * @return string * @throws LanguageNotFoundException */ - public function parse(string $argument, CommandSender $sender) + public function parse(string $argument, CommandSender $sender): string { return (string)array_search($argument, Loader::getInstance()->getLanguageList(), true); } @@ -32,8 +32,8 @@ public function getEnumValues(): array return array_values(Loader::getInstance()->getLanguageList()); } - public function getEnumName(): string - { - return "language"; - } + public function getEnumName(): string + { + return "language"; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/commands/args/MirrorAxisArgument.php b/src/xenialdan/MagicWE2/commands/args/MirrorAxisArgument.php index 5090e1b5..bfdc905f 100644 --- a/src/xenialdan/MagicWE2/commands/args/MirrorAxisArgument.php +++ b/src/xenialdan/MagicWE2/commands/args/MirrorAxisArgument.php @@ -9,7 +9,7 @@ class MirrorAxisArgument extends StringEnumArgument { - protected const VALUES = ["x" => "x", "z" => "z", "y" => "y", "xz" => "xz"]; + protected const VALUES = ["x" => "x", "z" => "z", "y" => "y", "xz" => "xz"]; public function getTypeName(): string { @@ -26,7 +26,7 @@ public function getEnumName(): string * @param CommandSender $sender * @return string //TODO consider changing to Axis */ - public function parse(string $argument, CommandSender $sender) + public function parse(string $argument, CommandSender $sender): string { return (string)$this->getValue($argument); } diff --git a/src/xenialdan/MagicWE2/commands/args/RotateAngleArgument.php b/src/xenialdan/MagicWE2/commands/args/RotateAngleArgument.php index 9b2000c1..90d7e2f9 100644 --- a/src/xenialdan/MagicWE2/commands/args/RotateAngleArgument.php +++ b/src/xenialdan/MagicWE2/commands/args/RotateAngleArgument.php @@ -9,8 +9,8 @@ class RotateAngleArgument extends StringEnumArgument { - /** @var array */ - protected const VALUES = ["90" => 90, "180" => 180, "270" => 270]; + /** @var array */ + protected const VALUES = ["90" => 90, "180" => 180, "270" => 270]; public function getTypeName(): string { @@ -27,7 +27,7 @@ public function getEnumName(): string * @param CommandSender $sender * @return int */ - public function parse(string $argument, CommandSender $sender) + public function parse(string $argument, CommandSender $sender): int { return (int)$this->getValue($argument); } diff --git a/src/xenialdan/MagicWE2/commands/asset/AssetCommand.php b/src/xenialdan/MagicWE2/commands/asset/AssetCommand.php new file mode 100644 index 00000000..6018edf2 --- /dev/null +++ b/src/xenialdan/MagicWE2/commands/asset/AssetCommand.php @@ -0,0 +1,197 @@ +registerSubCommand(new BrushNameCommand("name", "Get name or rename a brush")); + $this->setPermission("we.command.asset");//TODO perm + } + + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (!$session instanceof UserSession) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $form = (new SimpleForm(function (Player $player, $data) use ($lang, $session) { + try { + $store = Loader::$assetCollection;//TODO allow private assets again + switch ($data) { + case 'ui.asset.create.fromclipboard': + { + //create clipboard asset + //input Name + //toggle lock + //toggle shared asset + //type dropdown? + $clipboard = $session->getCurrentClipboard(); + if (!$clipboard instanceof SingleClipboard) { + $player->sendMessage($lang->translateString('error.noclipboard')); + break; + } + $asset = new Asset($clipboard->getCustomName(), $clipboard, false, $player->getXuid(), false); + $player->sendForm($asset->getSettingForm()); + break; + } + case 'ui.asset.save': + { + //save asset + //dropdown asset + //dropdown type + $form = (new CustomForm(function (Player $player, $data) use (/*$lang, $session,*/ $store) { + [$filename, $type] = $data; + /** @var Asset $asset */ + $asset = $store->assets[$filename]; + $player->sendMessage('Saving ' . $asset); + //TODO async, convert + if ($type === Asset::TYPE_SCHEMATIC) { + if ($asset->structure instanceof Schematic) { + $file = pathinfo($asset->filename, PATHINFO_BASENAME) . '_' . time() . '.schematic'; + $e = ($asset->shared ? '' : ($asset->ownerXuid === null ? '' : $asset->ownerXuid . DIRECTORY_SEPARATOR)); + $file = Loader::getInstance()->getDataFolder() . 'assets' . DIRECTORY_SEPARATOR . $e . $file; + mkdir(pathinfo($file, PATHINFO_DIRNAME), 7777, true); + $asset->structure->save($file); + $player->sendMessage("Saved as $file"); + } + if ($asset->structure instanceof MCStructure || $asset->structure instanceof SingleClipboard) { + $file = pathinfo($asset->filename, PATHINFO_BASENAME) . '_' . time() . '.schematic'; + $e = ($asset->shared ? '' : ($asset->ownerXuid === null ? '' : $asset->ownerXuid . DIRECTORY_SEPARATOR)); + $file = Loader::getInstance()->getDataFolder() . 'assets' . DIRECTORY_SEPARATOR . $e . $file; + @mkdir(pathinfo($file, PATHINFO_DIRNAME), 7777, true); + $asset->toSchematic()->save($file); + $player->sendMessage("Saved as $file"); + } + } + if ($asset->structure instanceof MCStructure && $type === Asset::TYPE_MCSTRUCTURE) { + #$asset->structure->save($asset->filename.'.mcstructure'); + $player->sendMessage('TODO'); + } + //$asset->saveAs() //TODO + })) + ->setTitle(Loader::PREFIX_FORM . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.asset.select')); + $options = []; + foreach ($store->getUnlockedAssets() as $asset) { + $options[$asset->filename] = $asset->filename; + } + $form->addDropdown("Asset", array_values($options)) + ->addDropdown("File type", [Asset::TYPE_SCHEMATIC, Asset::TYPE_MCSTRUCTURE]); + $player->sendForm($form); + break; + } + case 'ui.asset.settings': + { + //save asset + //dropdown asset + //dropdown type + $form = (new CustomForm(function (Player $player, $data) use (/*$lang, $session,*/ $store) { + [$filename] = $data; + /** @var Asset $asset */ + $asset = $store->assets[$filename]; + $player->sendForm($asset->getSettingForm()); + })) + ->setTitle(Loader::PREFIX_FORM . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.asset.select')); + $options = []; + foreach (Loader::$assetCollection->getAll() as $asset) {//TODO allow private assets again + $options[$asset->filename] = $asset->filename; + } + $form->addDropdown("Asset", array_values($options)); + $player->sendForm($form); + break; + } + case 'ui.asset.global': + { + $menu = InvMenu::create(InvMenuTypeIds::TYPE_DOUBLE_CHEST); + $store = Loader::$assetCollection; + foreach ($store->getSharedAssets() as $asset) { + $menu->getInventory()->addItem($asset->toItem()); + } + $menu->send($player, "Shared assets (" . count($store->getSharedAssets()) . ")"); + break; + } + case 'ui.asset.private': + { + $menu = InvMenu::create(InvMenuTypeIds::TYPE_DOUBLE_CHEST); + $store = $session->getAssets(); + $playerAssets = $store->getPlayerAssets($player->getXuid()); + var_dump(array_keys($playerAssets), array_keys($store->getPlayerAssets())); + foreach ($playerAssets as $key => $asset) { + var_dump($key, $asset); + $menu->getInventory()->addItem($asset->toItem()); + } + $menu->send($player, "Private assets (" . count($playerAssets) . ")"); + break; + } + } + return null; + } catch (Exception $error) { + $session->sendMessage(TF::RED . $lang->translateString('error')); + $session->sendMessage(TF::RED . $error->getMessage()); + Loader::getInstance()->getLogger()->logException($error); + } + })) + ->setTitle(Loader::PREFIX_FORM . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.asset.title')) + ->setContent($lang->translateString('ui.asset.content'))//TODO + ->addButton($lang->translateString('ui.asset.private'), -1, "", 'ui.asset.private') + ->addButton($lang->translateString('ui.asset.global'), -1, "", 'ui.asset.global') + ->addButton($lang->translateString('ui.asset.create.fromclipboard'), -1, "", 'ui.asset.create.fromclipboard') + ->addButton($lang->translateString('ui.asset.settings'), -1, "", 'ui.asset.settings') + ->addButton($lang->translateString('ui.asset.save'), -1, "", 'ui.asset.save'); + $sender->sendForm($form); + } 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()); + } + } +} diff --git a/src/xenialdan/MagicWE2/commands/biome/BiomeInfoCommand.php b/src/xenialdan/MagicWE2/commands/biome/BiomeInfoCommand.php index dac556ce..d3292bf6 100644 --- a/src/xenialdan/MagicWE2/commands/biome/BiomeInfoCommand.php +++ b/src/xenialdan/MagicWE2/commands/biome/BiomeInfoCommand.php @@ -38,33 +38,28 @@ protected function prepare(): void $this->setPermission("we.command.biome.info"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $biomeNames = (new ReflectionClass(Biome::class))->getConstants(); - $biomeNames = array_flip($biomeNames); - unset($biomeNames[Biome::MAX_BIOMES]); + } + $biomeNames = (new ReflectionClass(Biome::class))->getConstants(); + $biomeNames = array_flip($biomeNames); + unset($biomeNames[Biome::MAX_BIOMES]); array_walk($biomeNames, static function (&$value, $key) { $value = BiomeRegistry::getInstance()->getBiome($key)->getName(); }); @@ -76,7 +71,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.notarget')); return; } - $biomeId = $target->getPos()->getWorld()->getOrLoadChunkAtPosition($target->getPos())->getBiomeId($target->getPos()->getX() % 16, $target->getPos()->getZ() % 16); + $biomeId = $target->getPosition()->getWorld()->getOrLoadChunkAtPosition($target->getPosition())->getBiomeId($target->getPosition()->getX() % 16, $target->getPosition()->getZ() % 16); $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.biomeinfo.attarget')); $session->sendMessage(TF::AQUA . "ID: $biomeId Name: " . $biomeNames[$biomeId]); } @@ -85,37 +80,37 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.biomeinfo.atposition')); $session->sendMessage(TF::AQUA . "ID: $biomeId Name: " . $biomeNames[$biomeId]); } - return; - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { + return; + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { + } + if (!$selection->isValid()) { throw new SelectionException($lang->translateString('error.selectioninvalid')); - } - if ($selection->getWorld() !== $sender->getWorld()) { + } + if ($selection->getWorld() !== $sender->getWorld()) { $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); - } - $touchedChunks = $selection->getShape()->getTouchedChunks($selection->getWorld()); - $biomes = []; - foreach ($touchedChunks as $touchedChunk) { - for ($x = 0; $x < 16; $x++) - for ($z = 0; $z < 16; $z++) - $biomes[] = (FastChunkSerializer::deserialize($touchedChunk)->getBiomeId($x, $z)); - } - $biomes = array_unique($biomes); - $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.biomeinfo.result', [count($biomes)])); - foreach ($biomes as $biomeId) { - $session->sendMessage(TF::AQUA . $lang->translateString('command.biomeinfo.result.line', [$biomeId, $biomeNames[$biomeId]])); - } - } 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) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - } - } + } + $touchedChunks = $selection->getShape()->getTouchedChunks($selection->getWorld()); + $biomes = []; + foreach ($touchedChunks as $touchedChunk) { + for ($x = 0; $x < 16; $x++) + for ($z = 0; $z < 16; $z++) + $biomes[] = (FastChunkSerializer::deserializeTerrain($touchedChunk)->getBiomeId($x, $z));//TODO dylan might have plans to remove biomes from this + } + $biomes = array_unique($biomes); + $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.biomeinfo.result', [count($biomes)])); + foreach ($biomes as $biomeId) { + $session->sendMessage(TF::AQUA . $lang->translateString('command.biomeinfo.result.line', [$biomeId, $biomeNames[$biomeId]])); + } + } 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) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + } } diff --git a/src/xenialdan/MagicWE2/commands/biome/BiomeListCommand.php b/src/xenialdan/MagicWE2/commands/biome/BiomeListCommand.php index bc5bdcb8..573109e9 100644 --- a/src/xenialdan/MagicWE2/commands/biome/BiomeListCommand.php +++ b/src/xenialdan/MagicWE2/commands/biome/BiomeListCommand.php @@ -29,24 +29,19 @@ protected function prepare(): void $this->setPermission("we.command.biome.list"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - /** @var Player $sender */ - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + /** @var Player $sender */ + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.biomelist.title')); - foreach ((new ReflectionClass(Biome::class))->getConstants() as $name => $value) { + } + $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.biomelist.title')); + foreach ((new ReflectionClass(Biome::class))->getConstants() as $name => $value) { if ($value === Biome::MAX_BIOMES) continue; $name = BiomeRegistry::getInstance()->getBiome($value)->getName(); $session->sendMessage(TF::AQUA . $lang->translateString('command.biomelist.result.line', [$value, $name])); @@ -58,5 +53,5 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $sender->sendMessage($this->getUsage()); } } - } + } } diff --git a/src/xenialdan/MagicWE2/commands/biome/SetBiomeCommand.php b/src/xenialdan/MagicWE2/commands/biome/SetBiomeCommand.php index c6e2b224..89ab1f9c 100644 --- a/src/xenialdan/MagicWE2/commands/biome/SetBiomeCommand.php +++ b/src/xenialdan/MagicWE2/commands/biome/SetBiomeCommand.php @@ -34,30 +34,25 @@ protected function prepare(): void $this->setPermission("we.command.biome.set"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } + } $selection = $session->getLatestSelection(); if (is_null($selection)) { throw new SelectionException($lang->translateString('error.noselection')); diff --git a/src/xenialdan/MagicWE2/commands/brush/BrushCommand.php b/src/xenialdan/MagicWE2/commands/brush/BrushCommand.php index 013e9d9b..a2e0f595 100644 --- a/src/xenialdan/MagicWE2/commands/brush/BrushCommand.php +++ b/src/xenialdan/MagicWE2/commands/brush/BrushCommand.php @@ -7,15 +7,12 @@ use CortexPE\Commando\BaseCommand; use Exception; use InvalidArgumentException; +use jojoe77777\FormAPI\SimpleForm; use muqsit\invmenu\InvMenu; +use muqsit\invmenu\type\InvMenuTypeIds; use pocketmine\command\CommandSender; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; -use xenialdan\customui\elements\Button; -use xenialdan\customui\elements\Label; -use xenialdan\customui\elements\Toggle; -use xenialdan\customui\elements\UIElement; -use xenialdan\customui\windows\SimpleForm; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -25,79 +22,73 @@ class BrushCommand extends BaseCommand { - /** - * This is where all the arguments, permissions, sub-commands, etc would be registered - * @throws InvalidArgumentException - */ - protected function prepare(): void - { - $this->registerSubCommand(new BrushNameCommand("name", "Get name or rename a brush")); + /** + * This is where all the arguments, permissions, sub-commands, etc would be registered + * @throws InvalidArgumentException + */ + protected function prepare(): void + { + $this->registerSubCommand(new BrushNameCommand("name", "Get name or rename a brush")); $this->setPermission("we.command.brush"); - } + } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { - throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $form = new SimpleForm(Loader::PREFIX . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.brush.title'), $lang->translateString('ui.brush.content')); - $form->addButton(new Button($lang->translateString('ui.brush.create'))); - $form->addButton(new Button($lang->translateString('ui.brush.getsession'))); - $form->addButton(new Button($lang->translateString('ui.brush.edithand'))); - $form->setCallable(function (Player $player, $data) use ($lang, $session) { - try { - switch ($data) { - case $lang->translateString('ui.brush.create'): - { - $brush = new Brush(new BrushProperties()); - if ($brush instanceof Brush) { - $player->sendForm($brush->getForm()); - } - break; - } - case $lang->translateString('ui.brush.getsession'): - { - $menu = InvMenu::create(InvMenu::TYPE_DOUBLE_CHEST); - foreach ($session->getBrushes() as $brush) { - $menu->getInventory()->addItem($brush->toItem()); - } - $menu->send($player, "Session brushes"); - break; - } - case $lang->translateString('ui.brush.edithand'): - { - $brush = $session->getBrushFromItem($player->getInventory()->getItemInHand()); - if ($brush instanceof Brush) { - $player->sendForm($brush->getForm(false)); - } - break; - } - } - return null; - } catch (Exception $error) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (!$session instanceof UserSession) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $form = (new SimpleForm(function (Player $player, $data) use ($lang, $session) { + try { + switch ($data) { + case 'ui.brush.create': + { + $brush = new Brush(new BrushProperties()); + $player->sendForm($brush->getForm()); + break; + } + case 'ui.brush.getsession': + { + $menu = InvMenu::create(InvMenuTypeIds::TYPE_DOUBLE_CHEST); + foreach ($session->getBrushes()->getAll() as $brush) { + $menu->getInventory()->addItem($brush->toItem()); + } + $menu->send($player, "Session brushes"); + break; + } + case 'ui.brush.edithand': + { + $brush = $session->getBrushes()->getBrushFromItem($player->getInventory()->getItemInHand()); + if ($brush instanceof Brush) { + $player->sendForm($brush->getForm(false)); + } + break; + } + } + return null; + } catch (Exception $error) { $session->sendMessage(TF::RED . $lang->translateString('error')); $session->sendMessage(TF::RED . $error->getMessage()); } - }); + })) + ->setTitle(Loader::PREFIX_FORM . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.brush.title')) + ->setContent($lang->translateString('ui.brush.content')) + ->addButton($lang->translateString('ui.brush.create'), -1, "", 'ui.brush.create') + ->addButton($lang->translateString('ui.brush.getsession'), -1, "", 'ui.brush.getsession') + ->addButton($lang->translateString('ui.brush.edithand'), -1, "", 'ui.brush.edithand'); $sender->sendForm($form); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); @@ -105,23 +96,4 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $sender->sendMessage($this->getUsage()); } } - - /** - * @param UIElement[] $elements - * @param array $data - * @return array - */ - public static function generateLore(array $elements, array $data): array - { - $return = []; - foreach ($elements as $i => $element) { - if ($element instanceof Label) continue; - if ($element instanceof Toggle) { - $return[] = ($element->getText() . ": " . ($data[$i] ? "Yes" : "No")); - continue; - } - $return[] = ($element->getText() . ": " . $data[$i]); - } - return $return; - } } diff --git a/src/xenialdan/MagicWE2/commands/brush/BrushNameCommand.php b/src/xenialdan/MagicWE2/commands/brush/BrushNameCommand.php index 482a42ac..ab81ffdf 100644 --- a/src/xenialdan/MagicWE2/commands/brush/BrushNameCommand.php +++ b/src/xenialdan/MagicWE2/commands/brush/BrushNameCommand.php @@ -30,11 +30,6 @@ protected function prepare(): void $this->setPermission("we.command.brush.name"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ public function onRun(CommandSender $sender, string $aliasUsed, array $args): void { $lang = Loader::getInstance()->getLanguage(); @@ -54,7 +49,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo if (!$session instanceof UserSession) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } - $brush = $session->getBrushFromItem($sender->getInventory()->getItemInHand()); + $brush = $session->getBrushes()->getBrushFromItem($sender->getInventory()->getItemInHand()); if ($brush instanceof Brush) { if (empty($args["name"])) { $sender->sendMessage($brush->getName()); @@ -62,7 +57,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo } $brush->properties->setCustomName((string)$args["name"]); $session->sendMessage(TF::GREEN . $lang->translateString('command.brushname.set', [$brush->getName()])); - $session->replaceBrush($brush); + $session->getBrushes()->replaceBrush($brush); } } catch (Exception | TypeError $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); diff --git a/src/xenialdan/MagicWE2/commands/clipboard/ClearClipboardCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/ClearClipboardCommand.php index 45c65689..1c6ca934 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/ClearClipboardCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/ClearClipboardCommand.php @@ -27,27 +27,22 @@ protected function prepare(): void $this->setPermission("we.command.clipboard.clear"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); if (!$session instanceof UserSession) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } diff --git a/src/xenialdan/MagicWE2/commands/clipboard/CopyCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/CopyCommand.php index 77b6b53a..1c397b29 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/CopyCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/CopyCommand.php @@ -32,31 +32,26 @@ protected function prepare(): void $this->setPermission("we.command.clipboard.copy"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); + } + $selection = $session->getLatestSelection(); if (is_null($selection)) { throw new SelectionException($lang->translateString('error.noselection')); } diff --git a/src/xenialdan/MagicWE2/commands/clipboard/Cut2Command.php b/src/xenialdan/MagicWE2/commands/clipboard/Cut2Command.php index dbc71ebb..4c92d5a3 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/Cut2Command.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/Cut2Command.php @@ -15,6 +15,7 @@ use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\task\action\CutAction; @@ -30,56 +31,52 @@ class Cut2Command extends BaseCommand */ protected function prepare(): void { + //TODO //cut [filter] [set] $this->registerArgument(0, new TextArgument("flags", true)); $this->setPermission("we.command.clipboard.cut"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { + } + if (!$selection->isValid()) { throw new SelectionException($lang->translateString('error.selectioninvalid')); - } - if ($selection->getWorld() !== $sender->getWorld()) { + } + if ($selection->getWorld() !== $sender->getWorld()) { $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); - } - #$hasFlags = isset($args["flags"]); - $action = new CutAction(); + } + #$hasFlags = isset($args["flags"]); + $action = new CutAction(); $offset = $selection->getShape()->getMinVec3()->subtractVector($session->getPlayer()->getPosition()->asVector3()->floor())->floor(); $action->setClipboardVector($offset); - Server::getInstance()->getAsyncPool()->submitTask( - new AsyncActionTask( - $session->getUUID(), - $selection, - $action, + Server::getInstance()->getAsyncPool()->submitTask( + new AsyncActionTask( + $session->getUUID(), + $selection, + $action, $selection->getShape()->getTouchedChunks($selection->getWorld()), - "air",//TODO option - "" + BlockPalette::fromString("air"),//TODO option + BlockPalette::CREATE() ) ); } catch (Exception $error) { diff --git a/src/xenialdan/MagicWE2/commands/clipboard/CutCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/CutCommand.php index 6885430f..75982cba 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/CutCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/CutCommand.php @@ -9,14 +9,13 @@ use CortexPE\Commando\exception\ArgumentOrderException; use Exception; use InvalidArgumentException; -use pocketmine\block\BlockFactory; -use pocketmine\block\BlockLegacyIds; use pocketmine\command\CommandSender; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -34,32 +33,27 @@ protected function prepare(): void $this->setPermission("we.command.clipboard.cut"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { throw new SelectionException($lang->translateString('error.noselection')); } if (!$selection->isValid()) { @@ -72,7 +66,7 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo //TODO Temp hack - add cutAsync - Update 9th Feb. 2020 LEAVE THAT ALONE! IT WORKS, DO NOT TOUCH IT! $flags = $hasFlags ? API::flagParser(explode(" ", (string)$args["flags"])) : API::FLAG_BASE; API::copyAsync($selection, $session, $flags); - API::fillAsync($selection, $session, [BlockFactory::getInstance()->get(BlockLegacyIds::AIR, 0)], $flags); + API::fillAsync($selection, $session, BlockPalette::fromString("air"), $flags); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); diff --git a/src/xenialdan/MagicWE2/commands/clipboard/FlipCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/FlipCommand.php index 9f4cc7ef..a2cb5d39 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/FlipCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/FlipCommand.php @@ -35,26 +35,21 @@ protected function prepare(): void //$this->setUsage("//flip "); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { $axis = (string)$args["axis"];//TODO change to Axis[] $sender->sendMessage(Loader::PREFIX . $lang->translateString('command.flip.try', [$axis])); $session = SessionHelper::getUserSession($sender); @@ -66,11 +61,11 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo throw new SessionException($lang->translateString('error.noclipboard')); } $action = new FlipAction($axis); - #$offset = $selection->getShape()->getMinVec3()->subtract($session->getPlayer()->asVector3()->floor())->floor(); - #$action->setClipboardVector($offset); - Server::getInstance()->getAsyncPool()->submitTask( - new AsyncClipboardActionTask( - $session->getUUID(), + #$offset = $selection->getShape()->getMinVec3()->subtract($session->getPlayer()->asVector3()->floor())->floor(); + #$action->setClipboardVector($offset); + Server::getInstance()->getAsyncPool()->submitTask( + new AsyncClipboardActionTask( + $session->getUUID(), $clipboard->selection, $action, $clipboard diff --git a/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php index 55b1ec66..6e08307c 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/PasteCommand.php @@ -11,7 +11,6 @@ use InvalidArgumentException; use pocketmine\command\CommandSender; use pocketmine\player\Player; -use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\SingleClipboard; @@ -34,12 +33,6 @@ protected function prepare(): void $this->setPermission("we.command.clipboard.paste"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - * @throws AssumptionFailedError - */ public function onRun(CommandSender $sender, string $aliasUsed, array $args): void { $lang = Loader::getInstance()->getLanguage(); @@ -50,16 +43,16 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo } } if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $clipboard = $session->getCurrentClipboard(); + } + $clipboard = $session->getCurrentClipboard(); if (is_null($clipboard)) { throw new SelectionException($lang->translateString('error.noclipboard')); } diff --git a/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php b/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php index 961e8a3d..23612a3d 100644 --- a/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php +++ b/src/xenialdan/MagicWE2/commands/clipboard/RotateCommand.php @@ -36,30 +36,25 @@ protected function prepare(): void //$this->setUsage("//rotate "); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $angle = (int)$args["angle"]; - $aroundOrigin = $args["aroundOrigin"] ?? true; - $sender->sendMessage(Loader::PREFIX . $lang->translateString('command.rotate.try', [$angle])); - $session = SessionHelper::getUserSession($sender); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $angle = (int)$args["angle"]; + //$aroundOrigin = $args["aroundOrigin"] ?? true; + $sender->sendMessage(Loader::PREFIX . $lang->translateString('command.rotate.try', [$angle])); + $session = SessionHelper::getUserSession($sender); if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } diff --git a/src/xenialdan/MagicWE2/commands/debug/GenerateCommandsMDCommand.php b/src/xenialdan/MagicWE2/commands/debug/GenerateCommandsMDCommand.php new file mode 100644 index 00000000..60d26f18 --- /dev/null +++ b/src/xenialdan/MagicWE2/commands/debug/GenerateCommandsMDCommand.php @@ -0,0 +1,89 @@ +setPermission("we.command.debug"); + } + + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + try { + $cmds = []; + foreach (array_filter(Loader::getInstance()->getServer()->getCommandMap()->getCommands(), static function (Command $command) use ($sender) { + return str_contains($command->getName(), "/"); + }) as $cmd) { + /** @var Command $cmd */ + $cmds[$cmd->getName()] = $cmd; + } + $lines = [ + '| Command | Description | Usage | Alias |', + '|---|---|---|---|' + ]; + /** @var BaseCommand $command */ + foreach ($cmds as $command) { + $aliasStr = ''; + if (!empty(($aliases = $command->getAliases()))) { + foreach ($aliases as $i => $alias) { + $aliases[$i] = "/" . $alias; + } + $aliasStr = '`' . implode(", ", $aliases) . '`'; + } + $usage = $command->getUsage(); + //subcommand hack + $subCommands = $command->getSubCommands(); + if (count($subCommands) > 0) { + $pos = stripos($usage, " \n"); + if ($pos !== false) $usage = substr($usage, 0, $pos); + } + $lines[] = "| `/{$command->getName()}` | {$command->getDescription()} | `$usage` | $aliasStr |"; + foreach ($subCommands as $subCommand) { + $aliasStr = ''; + if (!empty(($aliases = $subCommand->getAliases()))) { + foreach ($aliases as $i => $alias) { + $aliases[$i] = "/" . $alias; + } + $aliasStr = '`' . implode(", ", $aliases) . '`'; + } + $lines[] = "| `/{$command->getName()} {$subCommand->getName()}` | {$subCommand->getDescription()} | `{$command->getName()} {$subCommand->getUsageMessage()}` | $aliasStr |"; + } + } + $path = Loader::getInstance()->getDataFolder() . 'COMMANDS.MD'; + file_put_contents($path, '# Commands +This list is automatically generated. If you have noticed an error, please create an issue. + +' . implode(' +', $lines), LOCK_EX); + } 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/debug/PlaceAllBlockstatesCommand.php b/src/xenialdan/MagicWE2/commands/debug/PlaceAllBlockstatesCommand.php index c4ca5474..e449b1b4 100644 --- a/src/xenialdan/MagicWE2/commands/debug/PlaceAllBlockstatesCommand.php +++ b/src/xenialdan/MagicWE2/commands/debug/PlaceAllBlockstatesCommand.php @@ -23,25 +23,20 @@ class PlaceAllBlockstatesCommand extends BaseCommand */ protected function prepare(): void { - $this->setPermission("we.command.test"); + $this->setPermission("we.command.debug"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); return; } /** @var Player $sender */ diff --git a/src/xenialdan/MagicWE2/commands/TestCommand.php b/src/xenialdan/MagicWE2/commands/debug/TestAPICommand.php similarity index 80% rename from src/xenialdan/MagicWE2/commands/TestCommand.php rename to src/xenialdan/MagicWE2/commands/debug/TestAPICommand.php index 78cb1e7a..598580a9 100644 --- a/src/xenialdan/MagicWE2/commands/TestCommand.php +++ b/src/xenialdan/MagicWE2/commands/debug/TestAPICommand.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace xenialdan\MagicWE2\commands; +namespace xenialdan\MagicWE2\commands\debug; use CortexPE\Commando\BaseCommand; use Exception; @@ -12,13 +12,14 @@ use pocketmine\Server; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\Selection; use xenialdan\MagicWE2\task\action\TestAction; use xenialdan\MagicWE2\task\AsyncActionTask; -class TestCommand extends BaseCommand +class TestAPICommand extends BaseCommand { /** @@ -27,14 +28,9 @@ class TestCommand extends BaseCommand */ protected function prepare(): void { - $this->setPermission("we.command.test"); + $this->setPermission("we.command.debug"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ public function onRun(CommandSender $sender, string $aliasUsed, array $args): void { $lang = Loader::getInstance()->getLanguage(); @@ -45,8 +41,8 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo } } try { - //TODO REMOVE DEBUG - $pluginSession = SessionHelper::createPluginSession(Loader::getInstance()); + //TODO REMOVE DEBUG + $pluginSession = SessionHelper::createPluginSession(Loader::getInstance()); $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 0, 0, 0); $pluginSession->addSelection($selection); Server::getInstance()->getAsyncPool()->submitTask( @@ -55,8 +51,8 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $selection, new TestAction(), $selection->getShape()->getTouchedChunks($selection->getWorld()), - "minecraft:snow_block", - "minecraft:tnt" + BlockPalette::fromString("minecraft:snow_block"), + BlockPalette::fromString("minecraft:tnt") ) ); $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 1, 1, 1); @@ -66,8 +62,8 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $selection, new TestAction(), $selection->getShape()->getTouchedChunks($selection->getWorld()), - "minecraft:snow_block", - "minecraft:tnt" + BlockPalette::fromString("minecraft:snow_block"), + BlockPalette::fromString("minecraft:tnt") ) ); $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 2, 2, 2); @@ -77,8 +73,8 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $selection, new TestAction(), $selection->getShape()->getTouchedChunks($selection->getWorld()), - "minecraft:snow_block", - "minecraft:tnt" + BlockPalette::fromString("minecraft:snow_block"), + BlockPalette::fromString("minecraft:tnt") ) ); $selection = new Selection($pluginSession->getUUID(), Server::getInstance()->getWorldManager()->getDefaultWorld(), 0, 0, 0, 1, 2, 3); @@ -88,8 +84,8 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo $selection, new TestAction(), $selection->getShape()->getTouchedChunks($selection->getWorld()), - "minecraft:snow_block", - "minecraft:tnt" + BlockPalette::fromString("minecraft:snow_block"), + BlockPalette::fromString("minecraft:tnt") ) ); } catch (Exception $error) { diff --git a/src/xenialdan/MagicWE2/commands/generation/CylinderCommand.php b/src/xenialdan/MagicWE2/commands/generation/CylinderCommand.php index 12000f11..996865b6 100644 --- a/src/xenialdan/MagicWE2/commands/generation/CylinderCommand.php +++ b/src/xenialdan/MagicWE2/commands/generation/CylinderCommand.php @@ -5,7 +5,6 @@ namespace xenialdan\MagicWE2\commands\generation; use CortexPE\Commando\args\IntegerArgument; -use CortexPE\Commando\args\RawStringArgument; use CortexPE\Commando\args\TextArgument; use CortexPE\Commando\BaseCommand; use CortexPE\Commando\exception\ArgumentOrderException; @@ -13,9 +12,9 @@ use InvalidArgumentException; use pocketmine\command\CommandSender; use pocketmine\player\Player; -use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\API; +use xenialdan\MagicWE2\commands\args\BlocksArgument; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -31,19 +30,13 @@ class CylinderCommand extends BaseCommand */ protected function prepare(): void { - $this->registerArgument(0, new RawStringArgument("blocks", false)); + $this->registerArgument(0, new BlocksArgument("blocks", false)); $this->registerArgument(1, new IntegerArgument("diameter", false)); $this->registerArgument(2, new IntegerArgument("height", true)); $this->registerArgument(3, new TextArgument("flags", true)); $this->setPermission("we.command.generation.cyl"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - * @throws AssumptionFailedError - */ public function onRun(CommandSender $sender, string $aliasUsed, array $args): void { $lang = Loader::getInstance()->getLanguage(); @@ -54,33 +47,23 @@ public function onRun(CommandSender $sender, string $aliasUsed, array $args): vo } } if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $messages = []; - $error = false; - $blocks = (string)$args["blocks"];//TODO change to Palette + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $blocks = $args["blocks"]; $diameter = (int)$args["diameter"]; $height = (int)($args["height"] ?? 1); - $newblocks = API::blockParser($blocks, $messages, $error); - foreach ($messages as $message) { - $sender->sendMessage($message); - } - if (!$error) { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { - throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $cyl = new Cylinder($sender->getPosition()->asVector3()->floor(), $height, $diameter); - $cylSelection = new Selection($session->getUUID(), $sender->getWorld()); - $cylSelection->setShape($cyl); - $hasFlags = isset($args["flags"]); - API::fillAsync($cylSelection, $session, $newblocks, $hasFlags ? API::flagParser(explode(" ", (string)$args["flags"])) : API::FLAG_BASE); - } else { - throw new InvalidArgumentException("Could not fill with the selected blocks"); + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } + $cyl = new Cylinder($sender->getPosition()->asVector3()->floor(), $height, $diameter); + $cylSelection = new Selection($session->getUUID(), $sender->getWorld()); + $cylSelection->setShape($cyl); + $hasFlags = isset($args["flags"]); + API::fillAsync($cylSelection, $session, $blocks, $hasFlags ? API::flagParser(explode(" ", (string)$args["flags"])) : API::FLAG_BASE); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); diff --git a/src/xenialdan/MagicWE2/commands/history/ClearhistoryCommand.php b/src/xenialdan/MagicWE2/commands/history/ClearhistoryCommand.php index 42e1eb13..628d9532 100644 --- a/src/xenialdan/MagicWE2/commands/history/ClearhistoryCommand.php +++ b/src/xenialdan/MagicWE2/commands/history/ClearhistoryCommand.php @@ -27,27 +27,22 @@ protected function prepare(): void $this->setPermission("we.command.history.clear"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); if (!$session instanceof UserSession) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); } diff --git a/src/xenialdan/MagicWE2/commands/history/RedoCommand.php b/src/xenialdan/MagicWE2/commands/history/RedoCommand.php index c2f197d6..d9760c1d 100644 --- a/src/xenialdan/MagicWE2/commands/history/RedoCommand.php +++ b/src/xenialdan/MagicWE2/commands/history/RedoCommand.php @@ -27,26 +27,21 @@ protected function prepare(): void $this->setPermission("we.command.history.redo"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { $session = SessionHelper::getUserSession($sender); if (!$session instanceof UserSession) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); diff --git a/src/xenialdan/MagicWE2/commands/history/UndoCommand.php b/src/xenialdan/MagicWE2/commands/history/UndoCommand.php index cb55719c..155b622a 100644 --- a/src/xenialdan/MagicWE2/commands/history/UndoCommand.php +++ b/src/xenialdan/MagicWE2/commands/history/UndoCommand.php @@ -26,26 +26,22 @@ protected function prepare(): void { $this->setPermission("we.command.history.undo"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { + + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { $session = SessionHelper::getUserSession($sender); if (!$session instanceof UserSession) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); diff --git a/src/xenialdan/MagicWE2/commands/palette/PaletteCommand.php b/src/xenialdan/MagicWE2/commands/palette/PaletteCommand.php new file mode 100644 index 00000000..a4ba1e84 --- /dev/null +++ b/src/xenialdan/MagicWE2/commands/palette/PaletteCommand.php @@ -0,0 +1,190 @@ +registerSubCommand(new PaletteNameCommand("name", "Get name or rename a palette")); + $this->setPermission("we.command.palette"); + } + + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (!$session instanceof UserSession) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $form = (new SimpleForm(function (Player $player, $data) use ($lang, $session) { + try { + switch ($data) { + case 'ui.palette.get': + { + $menu = InvMenu::create(InvMenuTypeIds::TYPE_DOUBLE_CHEST); + foreach ($session->getPalettes()->getAll() as $id => $palette) { + $menu->getInventory()->addItem($palette->toItem($id)); + } + $menu->send($player, "Session palettes"); + break; + } + case 'ui.palette.fromhotbar': + { + /** @var Block[] $blocks */ + $blocks = []; + for ($i = 0; $i <= $player->getInventory()->getHotbarSize(); $i++) { + $item = $player->getInventory()->getHotbarSlotItem($i); + if (!$item->isNull() && ($block = $item->getBlock()) instanceof Block) $blocks[] = $block; + } + $palette = BlockPalette::fromBlocks($blocks); + $id = Uuid::uuid4()->toString(); + #$session->getPalettes()->palettes[$id] = $palette; + $session->getPalettes()->addPalette($palette, $id); + $session->sendMessage(TF::GREEN . $lang->translateString('Created palette from hotbar')); + $player->getInventory()->addItem($palette->toItem($id)); + break; + } + case 'ui.palette.frominventory': + { + /** @var Block[] $blocks */ + $blocks = []; + foreach ($player->getInventory()->getContents() as $item) { + $block = $item->getBlock(); + if ($block !== VanillaBlocks::AIR()) $blocks[] = $block; + } + $palette = BlockPalette::fromBlocks($blocks); + $id = Uuid::uuid4()->toString(); + #$session->getPalettes()->palettes[$id] = $palette; + $session->getPalettes()->addPalette($palette, $id); + $session->sendMessage(TF::GREEN . $lang->translateString('Created palette from inventory')); + $player->getInventory()->addItem($palette->toItem($id)); + break; + } + case 'ui.palette.modify': + { + $menu = InvMenu::create(InvMenuTypeIds::TYPE_DOUBLE_CHEST); + foreach ($session->getPalettes()->getAll() as $id => $palette) { + $menu->getInventory()->addItem($palette->toItem($id)); + } + $menu->setListener(InvMenu::readonly(function (DeterministicInvMenuTransaction $transaction) use ($session, $lang): void { + $transaction->getPlayer()->removeCurrentWindow(); + $transaction->then(function (Player $player) use ($transaction, $session, $lang): void { + $itemClicked = $transaction->getItemClicked(); + try { + $tag = $itemClicked->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_PALETTE); + if ($tag === null) { + var_dump(__LINE__); + return; + } + $id = $tag->getString("id", ""); + + $palette = $session->getPalettes()->getPalette($id); + var_dump($itemClicked, $palette, $id); + $form1 = (new SimpleForm(static function (Player $player, $data) use ($lang, $palette, $id): void { + switch ($data) { + case 'ui.palette.modify.weights': + { + $form2 = (new CustomForm(static function (Player $player, $data) use ($lang, $palette): void { + var_dump($data); + })) + ->setTitle("Modify weights"); + + foreach ($palette->randomBlockQueries->getIndexes() as $i => $block) { + /** @var BlockQuery $block */ + $form2->addSlider($block->query, 0, 100, 1, (int)$palette->randomBlockQueries->getProbabilities()[$i] ?? 1 * 100, $block->query); + } + $player->sendForm($form2); + break; + } + default: + { + var_dump($data); + } + } + })) + ->setTitle("Modify Palette") + ->setContent(implode(",", $palette->toStringArray())) + ->addButton($lang->translateString('ui.palette.modify.weights'), -1, "", 'ui.palette.modify.weights') + ->addButton($lang->translateString('ui.palette.modify.blocks'), -1, "", 'ui.palette.modify.blocks'); + $player->sendForm($form1); + } catch (PaletteException $e) { + $session->sendMessage($e->getMessage()); + Loader::getInstance()->getLogger()->logException($e); + } + }); + })); + $menu->send($player, "Select a palette to modify", function (bool $sent): void { + var_dump($sent ? "sent" : "not sent"); + }); + break; + } + default: + { + var_dump($data); + } + } + #return null; + } catch (Exception $error) { + $session->sendMessage(TF::RED . $lang->translateString('error')); + $session->sendMessage(TF::RED . $error->getMessage()); + } + })) + ->setTitle(Loader::PREFIX_FORM . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.palette.title')) + ->setContent($lang->translateString('ui.palette.content')) + ->addButton($lang->translateString('ui.palette.fromhotbar'), -1, "", 'ui.palette.fromhotbar') + ->addButton($lang->translateString('ui.palette.frominventory'), -1, "", 'ui.palette.frominventory') + #->addButton($lang->translateString('ui.palette.fromselection'), -1, "", 'ui.palette.fromselection') + ->addButton($lang->translateString('ui.palette.get'), -1, "", 'ui.palette.get') + ->addButton($lang->translateString('ui.palette.modify'), -1, "", 'ui.palette.modify'); + $sender->sendForm($form); + } 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()); + } + } +} diff --git a/src/xenialdan/MagicWE2/commands/region/OverlayCommand.php b/src/xenialdan/MagicWE2/commands/region/OverlayCommand.php index 574266f8..1d0ec46d 100644 --- a/src/xenialdan/MagicWE2/commands/region/OverlayCommand.php +++ b/src/xenialdan/MagicWE2/commands/region/OverlayCommand.php @@ -4,7 +4,6 @@ namespace xenialdan\MagicWE2\commands\region; -use CortexPE\Commando\args\RawStringArgument; use CortexPE\Commando\BaseCommand; use CortexPE\Commando\exception\ArgumentOrderException; use Exception; @@ -12,7 +11,7 @@ use pocketmine\command\CommandSender; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; -use xenialdan\MagicWE2\API; +use xenialdan\MagicWE2\commands\args\BlocksArgument; use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; @@ -28,56 +27,41 @@ class OverlayCommand extends BaseCommand */ protected function prepare(): void { - $this->registerArgument(0, new RawStringArgument("blocks", false)); + $this->registerArgument(0, new BlocksArgument("blocks", false)); $this->setPermission("we.command.region.overlay"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $messages = []; - $error = false; - $blocks = API::blockParser((string)$args["blocks"], $messages, $error);//TODO change to Palette - foreach ($messages as $message) { - $sender->sendMessage($message); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + //$blocks = $args["blocks"];//TODO reenable when used + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { + throw new SelectionException($lang->translateString('error.noselection')); + } + if (!$selection->isValid()) { + throw new SelectionException($lang->translateString('error.selectioninvalid')); } - $return = !$error; - if ($return) { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { - throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { - throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { - throw new SelectionException($lang->translateString('error.selectioninvalid')); - } - if ($selection->getWorld() !== $sender->getWorld()) { - $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); - } - #API::overlayReplaceAsync($selection, $session, [], $blocks, API::flagParser(explode(" ", strval($args["flags"])))); - } else { - throw new InvalidArgumentException("Could not replace with the selected blocks"); + if ($selection->getWorld() !== $sender->getWorld()) { + $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); } + #API::overlayReplaceAsync($selection, $session, [], $blocks, API::flagParser(explode(" ", strval($args["flags"])))); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); diff --git a/src/xenialdan/MagicWE2/commands/region/ReplaceCommand.php b/src/xenialdan/MagicWE2/commands/region/ReplaceCommand.php index 006c391d..9f4a9e7f 100644 --- a/src/xenialdan/MagicWE2/commands/region/ReplaceCommand.php +++ b/src/xenialdan/MagicWE2/commands/region/ReplaceCommand.php @@ -4,7 +4,6 @@ namespace xenialdan\MagicWE2\commands\region; -use CortexPE\Commando\args\RawStringArgument; use CortexPE\Commando\args\TextArgument; use CortexPE\Commando\BaseCommand; use CortexPE\Commando\exception\ArgumentOrderException; @@ -14,6 +13,7 @@ use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\API; +use xenialdan\MagicWE2\commands\args\BlocksArgument; use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; @@ -29,60 +29,45 @@ class ReplaceCommand extends BaseCommand */ protected function prepare(): void { - $this->registerArgument(0, new RawStringArgument("findblocks", false)); - $this->registerArgument(1, new RawStringArgument("replaceblocks", false)); + $this->registerArgument(0, new BlocksArgument("findblocks", false)); + $this->registerArgument(1, new BlocksArgument("replaceblocks", false)); $this->registerArgument(2, new TextArgument("flags", true)); $this->setPermission("we.command.region.replace"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $messages = []; - $error = false; - $findBlocks = API::blockParser((string)$args["findblocks"], $messages, $error);//TODO change to Palette - $replaceBlocks = API::blockParser((string)$args["replaceblocks"], $messages, $error);//TODO change to Palette - foreach ($messages as $message) { - $sender->sendMessage($message); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $findBlocks = $args["findblocks"]; + $replaceBlocks = $args["replaceblocks"]; + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { + throw new SelectionException($lang->translateString('error.noselection')); + } + if (!$selection->isValid()) { + throw new SelectionException($lang->translateString('error.selectioninvalid')); } - $return = !$error; - if ($return) { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { - throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { - throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { - throw new SelectionException($lang->translateString('error.selectioninvalid')); - } - if ($selection->getWorld() !== $sender->getWorld()) { - $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); - } - $hasFlags = isset($args["flags"]); - API::replaceAsync($selection, $session, $findBlocks, $replaceBlocks, $hasFlags ? API::flagParser(explode(" ", (string)$args["flags"])) : API::FLAG_BASE); - } else { - throw new InvalidArgumentException("Could not replace with the selected blocks"); + if ($selection->getWorld() !== $sender->getWorld()) { + $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); } + $hasFlags = isset($args["flags"]); + API::replaceAsync($selection, $session, $findBlocks, $replaceBlocks, $hasFlags ? API::flagParser(explode(" ", (string)$args["flags"])) : API::FLAG_BASE); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); diff --git a/src/xenialdan/MagicWE2/commands/region/SetCommand.php b/src/xenialdan/MagicWE2/commands/region/SetCommand.php index 2840a5ff..94e8a43d 100644 --- a/src/xenialdan/MagicWE2/commands/region/SetCommand.php +++ b/src/xenialdan/MagicWE2/commands/region/SetCommand.php @@ -4,7 +4,6 @@ namespace xenialdan\MagicWE2\commands\region; -use CortexPE\Commando\args\RawStringArgument; use CortexPE\Commando\args\TextArgument; use CortexPE\Commando\BaseCommand; use CortexPE\Commando\exception\ArgumentOrderException; @@ -14,6 +13,7 @@ use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\API; +use xenialdan\MagicWE2\commands\args\BlocksArgument; use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\SessionHelper; @@ -29,57 +29,43 @@ class SetCommand extends BaseCommand */ protected function prepare(): void { - $this->registerArgument(0, new RawStringArgument("blocks", false)); + $this->registerArgument(0, new BlocksArgument("blocks", false)); $this->registerArgument(1, new TextArgument("flags", true)); $this->setPermission("we.command.region.set"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $messages = []; - $error = false; - $replaceBlocks = API::blockParser((string)$args["blocks"], $messages, $error);//TODO change to Palette - foreach ($messages as $message) { - $sender->sendMessage($message); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $replaceBlocks = $args["blocks"]; + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { + throw new SelectionException($lang->translateString('error.noselection')); + } + if (!$selection->isValid()) { + throw new SelectionException($lang->translateString('error.selectioninvalid')); } - if (!$error) { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { - throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { - throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { - throw new SelectionException($lang->translateString('error.selectioninvalid')); - } - if ($selection->getWorld() !== $sender->getWorld()) { - $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); - } - $hasFlags = isset($args["flags"]); - API::fillAsync($selection, $session, $replaceBlocks, $hasFlags ? API::flagParser(explode(" ", (string)$args["flags"])) : API::FLAG_BASE); - } else { - throw new InvalidArgumentException("Could not fill with the selected blocks"); + if ($selection->getWorld() !== $sender->getWorld()) { + $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); } + $hasFlags = isset($args["flags"]); + API::fillAsync($selection, $session, $replaceBlocks, $hasFlags ? API::flagParser(explode(" ", (string)$args["flags"])) : API::FLAG_BASE); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); diff --git a/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php b/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php index 7aac45f6..2f3d77ee 100644 --- a/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php +++ b/src/xenialdan/MagicWE2/commands/selection/ChunkCommand.php @@ -32,11 +32,6 @@ protected function prepare(): void $this->setPermission("we.command.selection.chunk"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ public function onRun(CommandSender $sender, string $aliasUsed, array $args): void { $lang = Loader::getInstance()->getLanguage(); diff --git a/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php b/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php index 3c5dfda4..4140569a 100644 --- a/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/HPos1Command.php @@ -29,47 +29,42 @@ protected function prepare(): void $this->setPermission("we.command.selection.hpos"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + 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"); - } - $target = $sender->getTargetBlock(Loader::getInstance()->getToolDistance()); - if ($target === null) { - $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.notarget')); - return; - } - $selection->setPos1($target->getPos()); - } 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) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - } - } + } + $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"); + } + $target = $sender->getTargetBlock(Loader::getInstance()->getToolDistance()); + if ($target === null) { + $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.notarget')); + return; + } + $selection->setPos1($target->getPosition()); + } 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) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + } } diff --git a/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php b/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php index e813db0f..0c7286d3 100644 --- a/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/HPos2Command.php @@ -29,47 +29,42 @@ protected function prepare(): void $this->setPermission("we.command.selection.hpos"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + 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"); - } - $target = $sender->getTargetBlock(Loader::getInstance()->getToolDistance()); - if ($target === null) { - $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.notarget')); - return; - } - $selection->setPos2($target->getPos()); - } 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) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - } - } + } + $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"); + } + $target = $sender->getTargetBlock(Loader::getInstance()->getToolDistance()); + if ($target === null) { + $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.notarget')); + return; + } + $selection->setPos2($target->getPosition()); + } 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) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + } } diff --git a/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php b/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php index 85d21d2f..180f1b56 100644 --- a/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/Pos1Command.php @@ -29,42 +29,37 @@ protected function prepare(): void $this->setPermission("we.command.selection.pos"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + 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"); - } - $selection->setPos1($sender->getPosition()); - } 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) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - } - } + } + $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"); + } + $selection->setPos1($sender->getPosition()); + } 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) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + } } diff --git a/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php b/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php index 3667021c..01a4e0dc 100644 --- a/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php +++ b/src/xenialdan/MagicWE2/commands/selection/Pos2Command.php @@ -29,42 +29,37 @@ protected function prepare(): void $this->setPermission("we.command.selection.pos"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (!$session instanceof UserSession) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + 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"); - } - $selection->setPos2($sender->getPosition()); - } 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) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - } - } + } + $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"); + } + $selection->setPos2($sender->getPosition()); + } 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) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + } } diff --git a/src/xenialdan/MagicWE2/commands/selection/info/CountCommand.php b/src/xenialdan/MagicWE2/commands/selection/info/CountCommand.php index 3f950800..4df91ceb 100644 --- a/src/xenialdan/MagicWE2/commands/selection/info/CountCommand.php +++ b/src/xenialdan/MagicWE2/commands/selection/info/CountCommand.php @@ -4,7 +4,6 @@ namespace xenialdan\MagicWE2\commands\selection\info; -use CortexPE\Commando\args\RawStringArgument; use CortexPE\Commando\args\TextArgument; use CortexPE\Commando\BaseCommand; use CortexPE\Commando\exception\ArgumentOrderException; @@ -14,9 +13,10 @@ use pocketmine\player\Player; use pocketmine\Server; use pocketmine\utils\TextFormat as TF; -use xenialdan\MagicWE2\API; +use xenialdan\MagicWE2\commands\args\BlocksArgument; use xenialdan\MagicWE2\exception\SelectionException; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\task\action\CountAction; @@ -32,67 +32,51 @@ class CountCommand extends BaseCommand */ protected function prepare(): void { - $this->registerArgument(0, new RawStringArgument("blocks", true)); + $this->registerArgument(0, new BlocksArgument("blocks", true)); $this->registerArgument(1, new TextArgument("flags", true)); $this->setPermission("we.command.selection.info.count"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $error = false; - if (!empty($args["blocks"])) { - $messages = []; - API::blockParser(($filterBlocks = (string)$args["blocks"]), $messages, $error);//TODO change to Palette - foreach ($messages as $message) { - $sender->sendMessage($message); - } - } else $filterBlocks = ""; - if (!$error) { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { - throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { - throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { - throw new SelectionException($lang->translateString('error.selectioninvalid')); - } - if ($selection->getWorld() !== $sender->getWorld()) { - $session->sendMessage(TF::GOLD . $lang->translateString('warning.differentworld')); - } - Server::getInstance()->getAsyncPool()->submitTask( - new AsyncActionTask( - $session->getUUID(), - $selection, - new CountAction(), - $selection->getShape()->getTouchedChunks($selection->getWorld()), - "", - $filterBlocks - ) - ); - } else { - throw new InvalidArgumentException("Could not count the selected blocks"); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $filterBlocks = $args["blocks"] ?? BlockPalette::CREATE(); + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { + throw new SelectionException($lang->translateString('error.noselection')); + } + if (!$selection->isValid()) { + throw new SelectionException($lang->translateString('error.selectioninvalid')); + } + if ($selection->getWorld() !== $sender->getWorld()) { + $session->sendMessage(TF::GOLD . $lang->translateString('warning.differentworld')); } + Server::getInstance()->getAsyncPool()->submitTask( + new AsyncActionTask( + $session->getUUID(), + $selection, + new CountAction(), + $selection->getShape()->getTouchedChunks($selection->getWorld()), + BlockPalette::CREATE(), + $filterBlocks + ) + ); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); diff --git a/src/xenialdan/MagicWE2/commands/selection/info/ListChunksCommand.php b/src/xenialdan/MagicWE2/commands/selection/info/ListChunksCommand.php index 00c577e7..d1a83561 100644 --- a/src/xenialdan/MagicWE2/commands/selection/info/ListChunksCommand.php +++ b/src/xenialdan/MagicWE2/commands/selection/info/ListChunksCommand.php @@ -29,53 +29,48 @@ protected function prepare(): void $this->setPermission("we.command.selection.info.listchunks"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { + } + if (!$selection->isValid()) { throw new SelectionException($lang->translateString('error.selectioninvalid')); - } - if ($selection->getWorld() !== $sender->getWorld()) { + } + if ($selection->getWorld() !== $sender->getWorld()) { $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); - } - $touchedChunks = $selection->getShape()->getTouchedChunks($selection->getWorld()); - $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.listchunks.found', [count($touchedChunks)])); - foreach ($touchedChunks as $chunkHash => $touchedChunk) { - $chunk = FastChunkSerializer::deserialize($touchedChunk); + } + $touchedChunks = $selection->getShape()->getTouchedChunks($selection->getWorld()); + $session->sendMessage(TF::DARK_AQUA . $lang->translateString('command.listchunks.found', [count($touchedChunks)])); + foreach ($touchedChunks as $chunkHash => $touchedChunk) { + $chunk = FastChunkSerializer::deserializeTerrain($touchedChunk); $biomes = []; for ($x = 0; $x < 16; $x++) for ($z = 0; $z < 16; $z++) - $biomes[] = (FastChunkSerializer::deserialize($touchedChunk)->getBiomeId($x, $z)); + $biomes[] = (FastChunkSerializer::deserializeTerrain($touchedChunk)->getBiomeId($x, $z)); $biomes = array_unique($biomes); $biomecount = count($biomes); $biomes = implode(", ", $biomes); World::getXZ($chunkHash, $cx, $cz); - $session->sendMessage(TF::AQUA . "ID: {$chunkHash} | X: {$cx} Z: {$cz} | Subchunks: {$chunk->getHeight()} | Biomes: ($biomecount) $biomes"); + $session->sendMessage(TF::AQUA . "ID: $chunkHash | X: $cx Z: $cz | Subchunks: {$chunk->getHeight()} | Biomes: ($biomecount) $biomes"); } } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); diff --git a/src/xenialdan/MagicWE2/commands/selection/info/SizeCommand.php b/src/xenialdan/MagicWE2/commands/selection/info/SizeCommand.php index 9e505846..6bdce165 100644 --- a/src/xenialdan/MagicWE2/commands/selection/info/SizeCommand.php +++ b/src/xenialdan/MagicWE2/commands/selection/info/SizeCommand.php @@ -27,37 +27,32 @@ protected function prepare(): void $this->setPermission("we.command.selection.info.size"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $session = SessionHelper::getUserSession($sender); - if (is_null($session)) { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $selection = $session->getLatestSelection(); - if (is_null($selection)) { + } + $selection = $session->getLatestSelection(); + if (is_null($selection)) { throw new SelectionException($lang->translateString('error.noselection')); - } - if (!$selection->isValid()) { + } + if (!$selection->isValid()) { throw new SelectionException($lang->translateString('error.selectioninvalid')); - } + } if ($selection->getWorld() !== $sender->getWorld()) { $sender->sendMessage(Loader::PREFIX . TF::GOLD . $lang->translateString('warning.differentworld')); } diff --git a/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php b/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php index f4114fa8..70c2c41b 100644 --- a/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php +++ b/src/xenialdan/MagicWE2/commands/tool/DebugCommand.php @@ -10,8 +10,7 @@ use InvalidArgumentException; use pocketmine\command\CommandSender; use pocketmine\item\enchantment\EnchantmentInstance; -use pocketmine\item\ItemFactory; -use pocketmine\item\ItemIds; +use pocketmine\item\VanillaItems; use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; @@ -32,43 +31,38 @@ protected function prepare(): void $this->setPermission("we.command.tool.debug"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { - $item = ItemFactory::getInstance()->get(ItemIds::STICK); - $item->addEnchantment(new EnchantmentInstance(Loader::$ench)); - $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('tool.debug')); - $item->setLore([ - $lang->translateString('tool.debug.lore.1'), - $lang->translateString('tool.debug.lore.2'), - $lang->translateString('tool.debug.lore.3') - ]); + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + 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()); $sender->getInventory()->addItem($item); } 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) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - } - } + $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) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + } } diff --git a/src/xenialdan/MagicWE2/commands/tool/FloodCommand.php b/src/xenialdan/MagicWE2/commands/tool/FloodCommand.php index 581c81a5..d46c98cd 100644 --- a/src/xenialdan/MagicWE2/commands/tool/FloodCommand.php +++ b/src/xenialdan/MagicWE2/commands/tool/FloodCommand.php @@ -21,50 +21,45 @@ protected function prepare(): void $this->setPermission("we.command.tool.floodfill"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $sender->sendMessage(TF::RED . "TEMPORARILY DISABLED!"); - /* - if (!$sender instanceof Player) return; - /** @var Player $sender * / - $lang = Loader::getInstance()->getLanguage(); - try { - if ($sender instanceof Player) { - $form = new CustomForm(Loader::PREFIX . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('ui.flood.title')); - $form->addElement(new Slider($lang->translateString('ui.flood.options.limit'), 0, 5000, 500.0)); - $form->addElement(new Input($lang->translateString('ui.flood.options.blocks'), $lang->translateString('ui.flood.options.blocks.placeholder'))); - $form->addElement(new Label($lang->translateString('ui.flood.options.label.infoapply'))); - $form->setCallable(function (Player $player, $data) use ($form) { - $item = ItemFactory::get(ItemIds::BUCKET, 1); - $item->addEnchantment(new EnchantmentInstance(Enchantment::getEnchantment(Loader::FAKE_ENCH_ID))); - $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::DARK_PURPLE . 'Flood'); - $item->setLore(BrushCommand::generateLore($form->getContent(), $data)); - $item->setNamedTagEntry(new CompoundTag(API::TAG_MAGIC_WE, [ - new StringTag("blocks", $data[1]), - new FloatTag("limit", $data[0]), - ])); - $player->getInventory()->addItem($item); - }); - $sender->sendForm($form); - } else { - $sender->sendMessage(TF::RED . "Console can not use this command."); - } - } catch (\Exception $error) { - $sender->sendMessage(Loader::PREFIX . TF::RED . Loader::getInstance()->getLanguage()->translateString('error.command-error')); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - $sender->sendMessage($this->getUsage()); - } catch (\ArgumentCountError $error) { - $sender->sendMessage(Loader::PREFIX . TF::RED . Loader::getInstance()->getLanguage()->translateString('error.command-error')); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - $sender->sendMessage($this->getUsage()); - } catch (\Error $error) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - }*/ - } + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $sender->sendMessage(TF::RED . "TEMPORARILY DISABLED!"); + /* + if (!$sender instanceof Player) return; + /** @var Player $sender * / + $lang = Loader::getInstance()->getLanguage(); + try { + if ($sender instanceof Player) { + $form = new CustomForm(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . $lang->translateString('ui.flood.title')); + $form->addElement(new Slider($lang->translateString('ui.flood.options.limit'), 0, 5000, 500.0)); + $form->addElement(new Input($lang->translateString('ui.flood.options.blocks'), $lang->translateString('ui.flood.options.blocks.placeholder'))); + $form->addElement(new Label($lang->translateString('ui.flood.options.label.infoapply'))); + $form->setCallable(function (Player $player, $data) use ($form) { + $item = ItemFactory::get(ItemIds::BUCKET, 1); + $item->addEnchantment(new EnchantmentInstance(Enchantment::getEnchantment(Loader::FAKE_ENCH_ID))); + $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . 'Flood'); + $item->setLore(BrushCommand::generateLore($form->getContent(), $data)); + $item->setNamedTagEntry(new CompoundTag(API::TAG_MAGIC_WE, [ + new StringTag("blocks", $data[1]), + new FloatTag("limit", $data[0]), + ])); + $player->getInventory()->addItem($item); + }); + $sender->sendForm($form); + } else { + $sender->sendMessage(TF::RED . "Console can not use this command."); + } + } catch (\Exception $error) { + $sender->sendMessage(Loader::PREFIX . TF::RED . Loader::getInstance()->getLanguage()->translateString('error.command-error')); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + $sender->sendMessage($this->getUsage()); + } catch (\ArgumentCountError $error) { + $sender->sendMessage(Loader::PREFIX . TF::RED . Loader::getInstance()->getLanguage()->translateString('error.command-error')); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + $sender->sendMessage($this->getUsage()); + } catch (\Error $error) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + }*/ + } } diff --git a/src/xenialdan/MagicWE2/commands/tool/ToggledebugCommand.php b/src/xenialdan/MagicWE2/commands/tool/ToggledebugCommand.php index 9e4b44bb..da327a27 100644 --- a/src/xenialdan/MagicWE2/commands/tool/ToggledebugCommand.php +++ b/src/xenialdan/MagicWE2/commands/tool/ToggledebugCommand.php @@ -25,26 +25,21 @@ protected function prepare(): void $this->setPermission("we.command.tool.toggledebug"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { $session = SessionHelper::getUserSession($sender); if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); diff --git a/src/xenialdan/MagicWE2/commands/tool/TogglewandCommand.php b/src/xenialdan/MagicWE2/commands/tool/TogglewandCommand.php index f17861dc..55a5c984 100644 --- a/src/xenialdan/MagicWE2/commands/tool/TogglewandCommand.php +++ b/src/xenialdan/MagicWE2/commands/tool/TogglewandCommand.php @@ -25,26 +25,21 @@ protected function prepare(): void $this->setPermission("we.command.tool.togglewand"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { $session = SessionHelper::getUserSession($sender); if (is_null($session)) { throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); diff --git a/src/xenialdan/MagicWE2/commands/tool/WandCommand.php b/src/xenialdan/MagicWE2/commands/tool/WandCommand.php index 8177e95c..2bd4d8c3 100644 --- a/src/xenialdan/MagicWE2/commands/tool/WandCommand.php +++ b/src/xenialdan/MagicWE2/commands/tool/WandCommand.php @@ -11,8 +11,7 @@ use pocketmine\command\CommandSender; use pocketmine\item\Durable; use pocketmine\item\enchantment\EnchantmentInstance; -use pocketmine\item\ItemFactory; -use pocketmine\item\ItemIds; +use pocketmine\item\VanillaItems; use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; @@ -33,45 +32,40 @@ protected function prepare(): void $this->setPermission("we.command.tool.wand"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } - if (!$sender instanceof Player) { - $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); - return; - } - /** @var Player $sender */ - try { + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { /** @var Durable $item */ - $item = ItemFactory::getInstance()->get(ItemIds::WOODEN_AXE); - $item->addEnchantment(new EnchantmentInstance(Loader::$ench)); - $item->setUnbreakable(true); - $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::DARK_PURPLE . $lang->translateString('tool.wand')); - $item->setLore([ - $lang->translateString('tool.wand.lore.1'), - $lang->translateString('tool.wand.lore.2'), - $lang->translateString('tool.wand.lore.3') - ]); + $item = VanillaItems::WOODEN_AXE() + ->addEnchantment(new EnchantmentInstance(Loader::$ench)) + ->setUnbreakable(true) + ->setCustomName(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . $lang->translateString('tool.wand')) + ->setLore([ + TF::RESET . $lang->translateString('tool.wand.lore.1'), + TF::RESET . $lang->translateString('tool.wand.lore.2'), + TF::RESET . $lang->translateString('tool.wand.lore.3') + ]); $item->getNamedTag()->setTag(API::TAG_MAGIC_WE, CompoundTag::create()); if (!$sender->getInventory()->contains($item)) $sender->getInventory()->addItem($item); } 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) { - Loader::getInstance()->getLogger()->logException($error); - $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - } - } + $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) { + Loader::getInstance()->getLogger()->logException($error); + $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); + } + } } diff --git a/src/xenialdan/MagicWE2/commands/utility/CalculateCommand.php b/src/xenialdan/MagicWE2/commands/utility/CalculateCommand.php index 113c1e56..a30c8228 100644 --- a/src/xenialdan/MagicWE2/commands/utility/CalculateCommand.php +++ b/src/xenialdan/MagicWE2/commands/utility/CalculateCommand.php @@ -32,25 +32,20 @@ protected function prepare(): void $this->setPermission("we.command.utility.calculate"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ - public function onRun(CommandSender $sender, string $aliasUsed, array $args): void - { - $lang = Loader::getInstance()->getLanguage(); - if ($sender instanceof Player && SessionHelper::hasSession($sender)) { - try { - $lang = SessionHelper::getUserSession($sender)->getLanguage(); - } catch (SessionException $e) { - } - } + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } try { - $sender->sendMessage((string)$args["expression"] . " = " . API::evalAsMath((string)$args["expression"])); + $sender->sendMessage($args["expression"] . " = " . API::evalAsMath((string)$args["expression"])); } catch (CalculationException $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); - $sender->sendMessage(Loader::PREFIX . TF::RED . (string)$args["expression"]); + $sender->sendMessage(Loader::PREFIX . TF::RED . $args["expression"]); } catch (Exception $error) { $sender->sendMessage(Loader::PREFIX . TF::RED . $lang->translateString('error.command-error')); $sender->sendMessage(Loader::PREFIX . TF::RED . $error->getMessage()); diff --git a/src/xenialdan/MagicWE2/commands/utility/ToggleOutlineCommand.php b/src/xenialdan/MagicWE2/commands/utility/ToggleOutlineCommand.php new file mode 100644 index 00000000..5ae223a1 --- /dev/null +++ b/src/xenialdan/MagicWE2/commands/utility/ToggleOutlineCommand.php @@ -0,0 +1,54 @@ +setPermission("we.command.utility.toggleoutline"); + } + + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $sender->sendMessage($session->setOutlineEnabled(!$session->isOutlineEnabled())); + } 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()); + } + } +} diff --git a/src/xenialdan/MagicWE2/commands/utility/ToggleSidebarCommand.php b/src/xenialdan/MagicWE2/commands/utility/ToggleSidebarCommand.php new file mode 100644 index 00000000..740a4497 --- /dev/null +++ b/src/xenialdan/MagicWE2/commands/utility/ToggleSidebarCommand.php @@ -0,0 +1,54 @@ +setPermission("we.command.utility.togglesidebar"); + } + + public function onRun(CommandSender $sender, string $aliasUsed, array $args): void + { + $lang = Loader::getInstance()->getLanguage(); + if ($sender instanceof Player && SessionHelper::hasSession($sender)) { + try { + $lang = SessionHelper::getUserSession($sender)->getLanguage(); + } catch (SessionException $e) { + } + } + if (!$sender instanceof Player) { + $sender->sendMessage(TF::RED . $lang->translateString('error.runingame')); + return; + } + /** @var Player $sender */ + try { + $session = SessionHelper::getUserSession($sender); + if (is_null($session)) { + throw new SessionException($lang->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $sender->sendMessage($session->setSidebarEnabled(!$session->isSidebarEnabled())); + } 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()); + } + } +} diff --git a/src/xenialdan/MagicWE2/commands/utility/ToggleWailaCommand.php b/src/xenialdan/MagicWE2/commands/utility/ToggleWailaCommand.php index 691ea30f..4f756034 100644 --- a/src/xenialdan/MagicWE2/commands/utility/ToggleWailaCommand.php +++ b/src/xenialdan/MagicWE2/commands/utility/ToggleWailaCommand.php @@ -25,11 +25,6 @@ protected function prepare(): void $this->setPermission("we.command.utility.togglewaila"); } - /** - * @param CommandSender $sender - * @param string $aliasUsed - * @param mixed[] $args - */ public function onRun(CommandSender $sender, string $aliasUsed, array $args): void { $lang = Loader::getInstance()->getLanguage(); diff --git a/src/xenialdan/MagicWE2/event/MWEEditEvent.php b/src/xenialdan/MagicWE2/event/MWEEditEvent.php index b044df79..cd075d2f 100644 --- a/src/xenialdan/MagicWE2/event/MWEEditEvent.php +++ b/src/xenialdan/MagicWE2/event/MWEEditEvent.php @@ -15,11 +15,11 @@ class MWEEditEvent extends MWEEvent implements Cancellable use CancellableTrait; /** @var Block[] */ - private $oldBlocks; + private array $oldBlocks; /** @var Block[] */ - private $newBlocks; + private array $newBlocks; /** @var null|Session */ - private $session; + private ?Session $session; /** * MWEEditEvent constructor. @@ -30,54 +30,54 @@ class MWEEditEvent extends MWEEvent implements Cancellable */ public function __construct(Plugin $plugin, array $oldBlocks, array $newBlocks, ?Session $session) { - parent::__construct($plugin); - $this->oldBlocks = $oldBlocks; - $this->newBlocks = $newBlocks; - $this->session = $session; - } + parent::__construct($plugin); + $this->oldBlocks = $oldBlocks; + $this->newBlocks = $newBlocks; + $this->session = $session; + } - /** - * @return null|Session - */ - public function getSession(): ?Session - { - return $this->session; - } + /** + * @return null|Session + */ + public function getSession(): ?Session + { + return $this->session; + } - /** - * @return null|Player - */ - public function getPlayer(): ?Player - { - if (($session = $this->getSession()) instanceof UserSession) - /** @var UserSession $session */ - $session->getPlayer(); - return null; - } + /** + * @return null|Player + */ + public function getPlayer(): ?Player + { + if (($session = $this->getSession()) instanceof UserSession) + /** @var UserSession $session */ + return $session->getPlayer(); + return null; + } - /** - * @param null|Player $player - */ - public function setPlayer(?Player $player): void - { - if (($session = $this->getSession()) instanceof UserSession) - /** @var UserSession $session */ - $session->setPlayer($player); - } + /** + * @param null|Player $player + */ + public function setPlayer(?Player $player): void + { + if (($session = $this->getSession()) instanceof UserSession) + /** @var UserSession $session */ + $session->setPlayer($player); + } - /** - * @return Block[] - */ - public function getOldBlocks(): array - { - return $this->oldBlocks; - } + /** + * @return Block[] + */ + public function getOldBlocks(): array + { + return $this->oldBlocks; + } - /** - * @return Block[] - */ - public function getNewBlocks(): array - { + /** + * @return Block[] + */ + public function getNewBlocks(): array + { return $this->newBlocks; } diff --git a/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php b/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php index 6cdbe748..7c5e4eb4 100644 --- a/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php +++ b/src/xenialdan/MagicWE2/event/MWESelectionChangeEvent.php @@ -12,14 +12,15 @@ class MWESelectionChangeEvent extends MWEEvent { - public const TYPE_PLUGIN = 0; - public const TYPE_POS1 = 1; - public const TYPE_POS2 = 2; - public const TYPE_WORLD = 3; - public const TYPE_SHAPE = 4; + public const TYPE_CREATE = 0; + public const TYPE_PLUGIN = 1; + public const TYPE_POS1 = 2; + public const TYPE_POS2 = 3; + public const TYPE_WORLD = 4; + public const TYPE_SHAPE = 5; private Selection $selection; - private ?Session $session; + private ?Session $session = null; private int $type; public function __construct(Selection $selection, int $type) @@ -49,15 +50,15 @@ public function setSelection(Selection $selection): void $this->selection = $selection; } - /** - * @return null|Session - */ - public function getSession(): ?Session - { - return $this->session; - } + /** + * @return null|Session + */ + public function getSession(): ?Session + { + return $this->session; + } - /** + /** * @return null|Player */ public function getPlayer(): ?Player diff --git a/src/xenialdan/MagicWE2/event/MWESessionLoadEvent.php b/src/xenialdan/MagicWE2/event/MWESessionLoadEvent.php index 3bc922a2..3995844f 100644 --- a/src/xenialdan/MagicWE2/event/MWESessionLoadEvent.php +++ b/src/xenialdan/MagicWE2/event/MWESessionLoadEvent.php @@ -10,7 +10,7 @@ class MWESessionLoadEvent extends MWEEvent { /** @var Session */ - private $session; + private Session $session; /** * MWESessionLoadEvent constructor. diff --git a/src/xenialdan/MagicWE2/exception/BlockQueryAlreadyParsedException.php b/src/xenialdan/MagicWE2/exception/BlockQueryAlreadyParsedException.php new file mode 100644 index 00000000..54d4a5f0 --- /dev/null +++ b/src/xenialdan/MagicWE2/exception/BlockQueryAlreadyParsedException.php @@ -0,0 +1,10 @@ +getBlockAt($x, $y, $z)->getId(), $this->getBlockAt($x, $y, $z)->getMeta()]; + /** @noinspection PhpInternalEntityUsedInspection */ + return $this->getBlockAt($x, $y, $z)->getFullId(); } - /** - * @return Chunk[] - */ - public function getChunks(): array - { - return $this->chunks; - } + /** + * @return Chunk[] + */ + public function getChunks(): array + { + return $this->chunks; + } + + public function getWorldHeight(): int + { + return World::Y_MAX; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/BlockEntry.php b/src/xenialdan/MagicWE2/helper/BlockEntry.php index 5390aae6..56113fe4 100644 --- a/src/xenialdan/MagicWE2/helper/BlockEntry.php +++ b/src/xenialdan/MagicWE2/helper/BlockEntry.php @@ -12,9 +12,9 @@ class BlockEntry { /** @var int BlockFullId */ - public $fullId; + public int $fullId; /** @var CompoundTag|null */ - public $nbt; + public ?CompoundTag $nbt = null; /** * BlockEntry constructor. @@ -29,22 +29,18 @@ public function __construct(int $fullId, ?CompoundTag $nbt = null) public function validate(): bool { - /** @var BlockFactory $instance */ $instance = BlockFactory::getInstance(); $block = $instance->fromFullBlock($this->fullId); - [$id, $meta] = [$block->getId(), $block->getMeta()]; + //[$id, $meta] = [$block->getId(), $block->getMeta()]; + $id = $block->getId(); if ($id === BlockLegacyIds::INFO_UPDATE) { return false; } - if ($this->nbt instanceof CompoundTag && !$this->nbt->valid()) { - return false; - } return true; } public function __toString() { - /** @var BlockFactory $instance */ $instance = BlockFactory::getInstance(); $block = $instance->fromFullBlock($this->fullId); $str = __CLASS__ . " " . $this->fullId . " [{$block->getId()}:{$block->getMeta()}]"; @@ -56,7 +52,6 @@ public function __toString() public function toBlock(): Block { - /** @var BlockFactory $instance */ $instance = BlockFactory::getInstance(); return $instance->fromFullBlock($this->fullId); } diff --git a/src/xenialdan/MagicWE2/helper/BlockPalette.php b/src/xenialdan/MagicWE2/helper/BlockPalette.php index 06535cdd..c08eebc9 100644 --- a/src/xenialdan/MagicWE2/helper/BlockPalette.php +++ b/src/xenialdan/MagicWE2/helper/BlockPalette.php @@ -4,36 +4,185 @@ namespace xenialdan\MagicWE2\helper; +use Generator; +use InvalidArgumentException; use JsonException; use pocketmine\block\Block; use pocketmine\block\BlockFactory; +use pocketmine\block\utils\InvalidBlockStateException; +use pocketmine\item\enchantment\EnchantmentInstance; +use pocketmine\item\Item; +use pocketmine\item\LegacyStringToItemParserException; +use pocketmine\item\VanillaItems; +use pocketmine\nbt\NbtException; +use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\UnexpectedTagTypeException; +use pocketmine\utils\TextFormat as TF; +use xenialdan\MagicWE2\API; +use xenialdan\MagicWE2\exception\BlockQueryAlreadyParsedException; +use xenialdan\MagicWE2\Loader; +use const JSON_THROW_ON_ERROR; -abstract class BlockPalette +class BlockPalette { + /** + * Contains BlockQuery[] + * @var WeightedRandom + */ + public WeightedRandom $randomBlockQueries; + public string $name = ""; + + /** + * BlockPalette constructor. + * @param string $name + */ + public function __construct(string $name = "") + { + if ($name !== "") $this->name = $name; + $this->randomBlockQueries = new WeightedRandom(); + } + + /** + * @param string $blocksQuery + * @return BlockPalette + * @throws BlockQueryAlreadyParsedException + * @throws InvalidArgumentException + * @throws \xenialdan\MagicWE2\exception\InvalidBlockStateException + */ + public static function fromString(string $blocksQuery): BlockPalette + { + $palette = self::CREATE(); + + $pregSplit = preg_split('/,(?![^\[]*])/', trim($blocksQuery), -1, PREG_SPLIT_NO_EMPTY); + if (!is_array($pregSplit)) throw new InvalidArgumentException("Regex matching failed"); + foreach ($pregSplit as $query) { + $palette->addBlockQuery(BlockQuery::fromString($query)); + } + $palette->randomBlockQueries->setup(); + + return $palette; + } + /** * @param Block[] $blocks - * @return string - * @throws JsonException + * @return BlockPalette + */ + public static function fromBlocks(array $blocks): BlockPalette + { + $palette = self::CREATE(); + foreach ($blocks as $block) { + //TODO this really isn't optimal.. + $state = BlockStatesParser::getStateByBlock($block); + if ($state !== null) { + $palette->addBlockQuery(BlockQuery::fromString($state->blockFull)); + }//TODO exceptions + } + $palette->randomBlockQueries->setup(); + + return $palette; + } + + public function addBlockQuery(BlockQuery $query): void + { + $this->randomBlockQueries->add($query, $query->weight); + } + + //TODO addBlock + + /** + * @param int $amount + * @return Generator + * @throws InvalidArgumentException */ - public static function encode(array $blocks): string + public function blocks(int $amount = 1): Generator + { + if ($amount < 1) throw new InvalidArgumentException('$amount must be greater than 0'); + $blockFactory = BlockFactory::getInstance(); + /** @var BlockQuery $blockQuery */ + foreach ($this->randomBlockQueries->generate($amount) as $blockQuery) {//TODO yield from? + yield $blockFactory->fromFullBlock($blockQuery->blockFullId);//TODO yield blockFullId and do not yield Block? + } + } + + /** + * @return Generator + */ + public function palette(): Generator + { + $blockFactory = BlockFactory::getInstance(); + /** @var BlockQuery $blockQuery */ + foreach ($this->randomBlockQueries->indexes() as $blockQuery) {//TODO yield from? + yield $blockFactory->fromFullBlock($blockQuery->blockFullId);//TODO yield blockFullId and do not yield Block? prob nah + } + } + + public function count(): int + { + return $this->randomBlockQueries->count(); + } + + public function empty(): bool + { + return $this->randomBlockQueries->count() === 0; + } + + /** + * @return array + */ + public function toStringArray(): array { $e = []; - foreach ($blocks as $block) - /** @noinspection PhpInternalEntityUsedInspection */ $e[] = $block->getFullId(); - return json_encode($e, JSON_THROW_ON_ERROR); + /** @var BlockQuery $blockQuery */ + foreach ($this->randomBlockQueries->indexes() as $blockQuery)//TODO check if this isn't random + { + $e[] = $blockQuery->query . '%' . $blockQuery->weight; + } + return $e; } /** * @param string $blocks * @return array + * @throws BlockQueryAlreadyParsedException + * @throws InvalidArgumentException + * @throws InvalidBlockStateException * @throws JsonException + * @throws LegacyStringToItemParserException + * @throws UnexpectedTagTypeException + * @throws NbtException */ - public static function decode(string $blocks): array + public static function fromStringArray(string $blocks): array { $e = []; - foreach (json_decode($blocks, true, 512, JSON_THROW_ON_ERROR) as $block) - $e[] = BlockFactory::getInstance()->fromFullBlock($block); + foreach (json_decode($blocks, true, 512, JSON_THROW_ON_ERROR) as $query) { + $q = new BlockQuery($query, null, null, null, null, 100); + $q->parse();//TODO the weight might not be parsed + $e[] = $q; + } return $e; } + public static function CREATE(): self + { + return new self; + } + + public function toItem(string $id): Item + { + $item = VanillaItems::BLUE_DYE();//placeholder. Maybe make it the most used item or replace with bundles + $item->addEnchantment(new EnchantmentInstance(Loader::$ench)); + $item->getNamedTag()->setTag(API::TAG_MAGIC_WE_PALETTE, + CompoundTag::create() + ->setString("id", $id) + ); + $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . "Palette"); + $lines = []; + $blocks = $this->toStringArray(); + $lines[] = TF::RESET . TF::BOLD . TF::GOLD . "Blocks: "; + foreach ($blocks as $block) + $lines[] = TF::RESET . $block; + $item->setLore($lines); + return $item; + } + } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/BlockQuery.php b/src/xenialdan/MagicWE2/helper/BlockQuery.php new file mode 100644 index 00000000..c0fba812 --- /dev/null +++ b/src/xenialdan/MagicWE2/helper/BlockQuery.php @@ -0,0 +1,94 @@ +query = $query; + $this->fullBlockQuery = $fullBlockQuery; + $this->blockId = $blockId; + $this->blockStatesQuery = $blockStatesQuery; + $this->fullExtraQuery = $fullExtraQuery; + if ($weight === null) $this->weight = 1; + else $this->weight = $weight / 100; + } + + /** + * @param bool $update + * @return $this + * @throws BlockQueryAlreadyParsedException + * @throws InvalidArgumentException + * @throws InvalidBlockStateException + * @throws LegacyStringToItemParserException + * @throws UnexpectedTagTypeException + * @throws \xenialdan\MagicWE2\exception\InvalidBlockStateException + * @throws NbtException + */ + public function parse(bool $update = true): self + { + //calling methods should check with hasBlock() before parse() + if (!$update && $this->hasBlock()) throw new BlockQueryAlreadyParsedException("FullBlockID is already parsed"); + $blockstateParser = BlockStatesParser::getInstance(); + $this->blockFullId = $blockstateParser::fromString($this)->getFullId();//this should already set the blockFullId because it is a reference + //var_dump($this->hasBlock() ? "Has block, " . $this->blockFullId : "Does not have block"); + //TODO throw BlockQueryParsingFailedException if blockFullId was not set? `if(!$this->hasBlock())` + return $this; + } + + public static function fromString(string $query): self + { + // How to code ugly 101: https://3v4l.org/2KfNW + preg_match_all('/([\w:]+)(?:\[([\w=,]*)])?/m', $query, $matches, PREG_SET_ORDER); + [$blockMatch, $extraMatch] = [$matches[0] ?? [], $matches[1] ?? []]; + $blockMatch += [null, null, null]; + $extraMatch += [null, null]; + [[$fullBlockQuery, $blockId, $blockStatesQuery], [$fullExtraQuery, $weight]] = [$blockMatch, $extraMatch]; + return (new self($query, $fullBlockQuery, $blockId, $blockStatesQuery, $fullExtraQuery, $weight))->parse(); + } + + public function hasBlockStates(): bool + { + return $this->blockStatesQuery !== null; + } + + public function hasExtraQuery(): bool + { + return $this->blockStatesQuery !== null; + } + + public function hasBlock(): bool + { + return $this->blockFullId !== null; + } + +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/BlockStatesEntry.php b/src/xenialdan/MagicWE2/helper/BlockStatesEntry.php index 13fe5a04..26b84f86 100644 --- a/src/xenialdan/MagicWE2/helper/BlockStatesEntry.php +++ b/src/xenialdan/MagicWE2/helper/BlockStatesEntry.php @@ -15,19 +15,20 @@ use pocketmine\utils\TextFormat; use RuntimeException; use Throwable; +use xenialdan\MagicWE2\exception\BlockQueryAlreadyParsedException; use xenialdan\MagicWE2\exception\InvalidBlockStateException; use xenialdan\MagicWE2\task\action\FlipAction; class BlockStatesEntry { /** @var string */ - public $blockIdentifier; + public string $blockIdentifier; /** @var CompoundTag */ - public $blockStates; + public CompoundTag $blockStates; /** @var string */ - public $blockFull; + public string $blockFull; /** @var Block|null */ - public $block; + public ?Block $block = null; /** * BlockStatesEntry constructor. @@ -41,10 +42,7 @@ public function __construct(string $blockIdentifier, CompoundTag $blockStates, ? $this->blockStates = $blockStates; $this->block = $block; try { - if ($this->blockStates !== null) - $this->blockFull = TextFormat::clean(BlockStatesParser::printStates($this, false)); - else - $this->blockFull = $this->blockIdentifier; + $this->blockFull = TextFormat::clean(BlockStatesParser::printStates($this, false)); } catch (Throwable $e) { GlobalLogger::get()->logException($e); $this->blockFull = $this->blockIdentifier; @@ -62,17 +60,16 @@ public function __toString() /** * TODO hacky AF. clean up * @return Block + * @throws BlockQueryAlreadyParsedException * @throws InvalidArgumentException - * @throws RuntimeException * @throws InvalidBlockStateException */ public function toBlock(): Block { if ($this->block instanceof Block) return $this->block; BlockFactory::getInstance(); - $blocks = BlockStatesParser::getInstance()::fromString($this->blockFull, false); - $block = reset($blocks); - if($block instanceof Block) $this->block = $block; + $block = BlockPalette::fromString($this->blockFull)->blocks()->current(); + if ($block instanceof Block) $this->block = $block; return $this->block; } @@ -91,10 +88,11 @@ public function rotate(int $amount): BlockStatesEntry $block = $clone->toBlock(); $idMapName = str_replace("minecraft:", "", BlockStatesParser::getBlockIdMapName($block)); $key = $idMapName . ":" . $block->getMeta(); - if (strpos($idMapName, "_door") !== false) { - $fromMap = BlockStatesParser::getDoorRotationFlipMap()[$block->getMeta()] ?? null; + $blockstateParser = BlockStatesParser::getInstance(); + if (str_contains($idMapName, "_door")) { + $fromMap = $blockstateParser::getDoorRotationFlipMap()[$block->getMeta()] ?? null; } else { - $fromMap = BlockStatesParser::getRotationFlipMap()[$key] ?? null; + $fromMap = $blockstateParser::getRotationFlipMap()[$key] ?? null; } if ($fromMap === null) return $clone; $rotatedStates = $fromMap[$amount] ?? null; @@ -125,15 +123,15 @@ public function rotate(int $amount): BlockStatesEntry } } $clone->blockStates = $bsCompound; - $clone->blockFull = TextFormat::clean(BlockStatesParser::printStates($clone, false)); - if (strpos($idMapName, "_door") !== false) { - $clone->block = BlockStatesParser::fromString($clone->blockFull, false)[0]; + $clone->blockFull = TextFormat::clean($blockstateParser::printStates($clone, false)); + if (str_contains($idMapName, "_door")) { + $clone->block = $clone->toBlock();//TODO check } else $clone->block = null; return $clone; //TODO reduce useless calls. BSP::fromStates? - #$blockFull = TextFormat::clean(BlockStatesParser::printStates($clone, false)); - #return BlockStatesParser::getStateByBlock(BlockStatesParser::fromString($blockFull)[0]); + #$blockFull = TextFormat::clean($blockstateParser::printStates($clone, false)); + #return BlockStatesParser::getStateByBlock($blockstateParser::fromString($blockFull)[0]); } /** @@ -168,15 +166,15 @@ public function mirror(string $axis): BlockStatesEntry $bsCompound = clone $clone->blockStates;//TODO check if clone is necessary #$bsCompound->setName("minecraft:$key");//TODO this might cause issues with the parser since it stays same //seems to work ¯\_(ツ)_/¯ if ($axis === FlipAction::AXIS_Y && !(//TODO maybe add vine + mushroom block directions - $bsCompound->hasTag("attachment") || - $bsCompound->hasTag("facing_direction") || - $bsCompound->hasTag("hanging") || - $bsCompound->hasTag("lever_direction") || - $bsCompound->hasTag("rail_direction") || - $bsCompound->hasTag("top_slot_bit") || - $bsCompound->hasTag("torch_facing_direction") || - $bsCompound->hasTag("upper_block_bit") || - $bsCompound->hasTag("upside_down_bit") + $bsCompound->getTag("attachment") !== null || + $bsCompound->getTag("facing_direction") !== null || + $bsCompound->getTag("hanging") !== null || + $bsCompound->getTag("lever_direction") !== null || + $bsCompound->getTag("rail_direction") !== null || + $bsCompound->getTag("top_slot_bit") !== null || + $bsCompound->getTag("torch_facing_direction") !== null || + $bsCompound->getTag("upper_block_bit") !== null || + $bsCompound->getTag("upside_down_bit") !== null )) {//ugly hack for y flip #var_dump("nothing can be flipped around y axis"); return $clone; @@ -248,8 +246,8 @@ public function mirror(string $axis): BlockStatesEntry #var_dump($clone->blockFull); return $clone; //TODO reduce useless calls. BSP::fromStates? - #$blockFull = TextFormat::clean(BlockStatesParser::printStates($clone, false)); - #return BlockStatesParser::getStateByBlock(BlockStatesParser::fromString($blockFull)[0]); + #$blockFull = TextFormat::clean($blockstateParser::printStates($clone, false)); + #return BlockStatesParser::getStateByBlock($blockstateParser::fromString($blockFull)[0]); } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/BlockStatesParser.php b/src/xenialdan/MagicWE2/helper/BlockStatesParser.php index a21737bc..6467be84 100644 --- a/src/xenialdan/MagicWE2/helper/BlockStatesParser.php +++ b/src/xenialdan/MagicWE2/helper/BlockStatesParser.php @@ -12,19 +12,23 @@ use JsonException; use pocketmine\block\Block; use pocketmine\block\BlockFactory; -use pocketmine\block\BlockLegacyIds; use pocketmine\block\Door; use pocketmine\block\utils\BlockDataSerializer; use pocketmine\data\bedrock\LegacyBlockIdToStringIdMap; -use pocketmine\item\LegacyStringToItemParser; +use pocketmine\item\LegacyStringToItemParserException; +use pocketmine\item\StringToItemParser; use pocketmine\math\Facing; +use pocketmine\nbt\NbtException; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; +use pocketmine\nbt\UnexpectedTagTypeException; +use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary; use pocketmine\network\mcpe\convert\R12ToCurrentBlockMapEntry; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; +use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\plugin\PluginException; use pocketmine\Server; use pocketmine\utils\AssumptionFailedError; @@ -33,8 +37,11 @@ use pocketmine\utils\TextFormat as TF; use pocketmine\world\Position; use RuntimeException; +use Webmozart\PathUtil\Path; use xenialdan\MagicWE2\exception\InvalidBlockStateException; use xenialdan\MagicWE2\Loader; +use function array_key_exists; +use function file_get_contents; use const pocketmine\RESOURCE_PATH; final class BlockStatesParser @@ -42,36 +49,35 @@ final class BlockStatesParser use SingletonTrait; /** @var string */ - public static $rotPath; + public static string $rotPath = ""; /** @var string */ - public static $doorRotPath; + public static string $doorRotPath = ""; /** @var R12ToCurrentBlockMapEntry[][] *///TODO check type correct? phpstan! - private static $legacyStateMap; + private static array $legacyStateMap; /** @var array */ - private static $aliasMap = []; + private static array $aliasMap = []; /** @var array */ - private static $rotationFlipMap = []; + private static array $rotationFlipMap = []; /** @var array */ - private static $doorRotationFlipMap = []; + private static array $doorRotationFlipMap = []; private function __construct() { -// $this->loadRotationAndFlipData(Loader::getRotFlipPath()); -// $this->loadDoorRotationAndFlipData(Loader::getDoorRotFlipPath()); - $this->loadRotationAndFlipData(self::$rotPath); - $this->loadDoorRotationAndFlipData(self::$doorRotPath); + $this->loadRotationAndFlipData(Loader::getRotFlipPath()); + $this->loadDoorRotationAndFlipData(Loader::getDoorRotFlipPath()); + //$this->loadRotationAndFlipData(self::$rotPath); + //$this->loadDoorRotationAndFlipData(self::$doorRotPath); $this->loadLegacyMappings(); } private function loadLegacyMappings(): void { - /** @var R12ToCurrentBlockMapEntry[][] $legacyStateMap */ self::$legacyStateMap = []; $contents = file_get_contents(RESOURCE_PATH . "vanilla/r12_to_current_block_map.bin"); if ($contents === false) throw new PluginException("Can not get contents of r12_to_current_block_map"); - $legacyStateMapReader = new PacketSerializer($contents); + $legacyStateMapReader = PacketSerializer::decoder(file_get_contents(Path::join(RESOURCE_PATH, "vanilla", "r12_to_current_block_map.bin")), 0, new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary())); $nbtReader = new NetworkNbtSerializer(); while (!$legacyStateMapReader->feof()) { $id = $legacyStateMapReader->getString(); @@ -139,18 +145,24 @@ public static function getDoorRotationFlipMap(): array } /** - * @param string $namespacedSelectedBlockName + * @param BlockQuery $query * @param CompoundTag $states * @return Door * @throws InvalidArgumentException * @throws InvalidBlockStateException - * @throws RuntimeException + * @throws UnexpectedTagTypeException * @throws \pocketmine\block\utils\InvalidBlockStateException + * @throws LegacyStringToItemParserException + * @throws NbtException */ - private static function buildDoor(string $namespacedSelectedBlockName, CompoundTag $states): Door + private static function buildDoor(BlockQuery $query, CompoundTag $states): Door { + $query = clone $query;//TODO test, i don't want the original $query to be modified + $query->fullExtraQuery = null; + $query->fullBlockQuery = $query->blockId; + $query->blockStatesQuery = null; /** @var Door $door */ - $door = self::fromString($namespacedSelectedBlockName)[0]; + $door = self::fromString($query); $door->setOpen($states->getByte("open_bit") === 1); $door->setTop($states->getByte("upper_block_bit") === 1); $door->setHingeRight($states->getByte("door_hinge_bit") === 1); @@ -179,6 +191,7 @@ public static function getBlockIdMapName(Block $block): ?string /** * @param string $blockIdentifier * @return CompoundTag + * @throws UnexpectedTagTypeException */ protected static function getDefaultStates(string $blockIdentifier): CompoundTag { @@ -186,51 +199,37 @@ protected static function getDefaultStates(string $blockIdentifier): CompoundTag } /** - * @param string $query - * @param bool $multiple - * @return Block[] + * Parses a BlockQuery (acquired using BlockPalette::fromString()) to a block and sets the BlockQuery's blockFullId + * @param BlockQuery $query + * @return Block * @throws InvalidArgumentException * @throws InvalidBlockStateException - * @throws RuntimeException + * @throws UnexpectedTagTypeException + * @throws \pocketmine\block\utils\InvalidBlockStateException + * @throws LegacyStringToItemParserException + * @throws NbtException + * @noinspection PhpInternalEntityUsedInspection */ - public static function fromString(string $query, bool $multiple = false): array + public static function fromString(BlockQuery $query): Block { - #if (!BlockFactory::isInit()) BlockFactory::init(); - $blocks = []; - if ($multiple) { - $pregSplit = preg_split('/,(?![^\[]*])/', trim($query), -1, PREG_SPLIT_NO_EMPTY); - if (!is_array($pregSplit)) throw new InvalidArgumentException("Regex matching failed"); - foreach ($pregSplit as $b) { - /** @noinspection SlowArrayOperationsInLoopInspection */ - $blocks = array_merge($blocks, self::fromString($b, false)); - } - return $blocks; + $namespacedSelectedBlockName = !str_contains($query->blockId, "minecraft:") ? "minecraft:" . $query->blockId : $query->blockId; + $selectedBlockName = strtolower(str_replace("minecraft:", "", $namespacedSelectedBlockName));//TODO try to keep namespace "minecraft:" to support custom blocks + + $block = StringToItemParser::getInstance()->parse($selectedBlockName)?->getBlock() ?? StringToItemParser::getInstance()->parse($selectedBlockName)?->getBlock(); + //no states, just block + if (!$query->hasBlockStates()) { + $query->blockFullId = $block->getFullId(); + return $block; } - $blockData = strtolower(str_replace("minecraft:", "", $query));//TODO try to keep namespace "minecraft:" to support custom blocks - $re = '/([\w:]+)(?:\[([\w=,]*)\])?/m'; - preg_match_all($re, $blockData, $matches, PREG_SET_ORDER, 0); - if (!isset($matches[0][1])) { - throw new InvalidArgumentException("Could not detect block id"); - } - - $selectedBlockName = $matches[0][1]; - $namespacedSelectedBlockName = "minecraft:" . $selectedBlockName;//TODO try to keep namespace "minecraft:" to support custom blocks - /** @var LegacyStringToItemParser $legacyStringToItemParser */ - $legacyStringToItemParser = LegacyStringToItemParser::getInstance(); - $block = $legacyStringToItemParser->parse($selectedBlockName)->getBlock(); - if (count($matches[0]) < 3) { - return [$block]; - } $defaultStatesNamedTag = self::getDefaultStates($namespacedSelectedBlockName); if (!$defaultStatesNamedTag instanceof CompoundTag) { throw new InvalidArgumentException("Could not find default block states for $namespacedSelectedBlockName"); } - $extraData = $matches[0][2] ?? ""; - $statesExploded = explode(",", $extraData); + $blockStatesQuery = $query->blockStatesQuery ?? ""; + $statesExploded = explode(",", $blockStatesQuery); $finalStatesList = clone $defaultStatesNamedTag; - #var_dump($statesExploded, $finalStatesList->toString()); - #$finalStatesList->setName("states"); + $availableAliases = [];//TODO map in init()! No need to recreate every time! EDIT 2k20: uhm what? @ my past self, why can't you explain properly?! foreach ($finalStatesList as $stateName => $state) { if (array_key_exists($stateName, self::$aliasMap)) { @@ -241,9 +240,9 @@ public static function fromString(string $query, bool $multiple = false): array } } foreach ($statesExploded as $stateKeyValuePair) { - if (strpos($stateKeyValuePair, "=") === false) continue; + if (!str_contains($stateKeyValuePair, "=")) continue; [$stateName, $value] = explode("=", $stateKeyValuePair); - $value = strtolower(trim((string)$value)); + $value = strtolower(trim($value)); if ($value === '') { throw new InvalidBlockStateException("Empty value for state $stateName"); } @@ -252,7 +251,7 @@ public static function fromString(string $query, bool $multiple = false): array //TODO maybe validate wrong states here? i.e. stone[type=wrongtype] => Exception, "wrongtype" is invalid value $tag = $finalStatesList->getTag($stateName); if ($tag === null) { - throw new InvalidBlockStateException("Invalid state $stateName"); + throw new InvalidBlockStateException("Default states for block '$query->blockId' do not contain Tag with name '$stateName'"); } if ($tag instanceof StringTag) { $finalStatesList->setString($stateName, $value); @@ -269,47 +268,39 @@ public static function fromString(string $query, bool $multiple = false): array throw new InvalidBlockStateException("Unknown tag of type " . get_class($tag) . " detected"); } } - #var_dump($finalStatesList->toString()); - //print final list - //TODO remove. This crashes in AsyncTasks and is just for debug - #Loader::getInstance()->getLogger()->notice(self::printStates(new BlockStatesEntry($namespacedSelectedBlockName,$finalStatesList), false)); //return found block(s) - $blocks = []; //doors.. special blocks annoying -.- - $isDoor = strpos($namespacedSelectedBlockName, "_door") !== false; - if ($isDoor) { - return [self::buildDoor($namespacedSelectedBlockName, $finalStatesList)]; + if (str_contains($query->blockId, "_door")) { + $block = self::buildDoor($query, $finalStatesList); + $query->blockFullId = $block->getFullId(); + return $block; } - #var_dump((string)$finalStatesList); + /** @var Block[] $blocks */ + $blocks = []; foreach (self::$legacyStateMap[$namespacedSelectedBlockName] as $meta => $r12ToCurrentBlockMapEntry) { $clonedPrintedCompound = clone $r12ToCurrentBlockMapEntry->getBlockState()->getCompoundTag('states'); if ($clonedPrintedCompound->equals($finalStatesList)) { - #Server::getInstance()->getLogger()->notice("FOUND!"); - /** @var BlockFactory $blockFactory */ $blockFactory = BlockFactory::getInstance(); $block = $blockFactory->get($block->getId(), $meta & 0xf); - #var_dump($oldNameAndMeta,$block); - #var_dump($block, $finalStatesList); $blocks[] = $block; - #Server::getInstance()->getLogger()->debug(TF::GREEN . "Found block: " . TF::GOLD . $block); - #Server::getInstance()->getLogger()->notice(self::printStates(new BlockStatesEntry($namespacedSelectedBlockName, $clonedPrintedCompound), true));//might cause loop lol } } - #if (empty($blocks)) return [Block::get(0)];//no block found //TODO r12 map only has blocks up to id 255. On 4.0.0, return Item::fromString()? - if (empty($blocks)) throw new InvalidArgumentException("No block $namespacedSelectedBlockName matching $query could be found");//no block found //TODO r12 map only has blocks up to id 255. On 4.0.0, return Item::fromString()? - if (count($blocks) === 1) return $blocks; + if (count($blocks) < 1) throw new InvalidArgumentException("No block $namespacedSelectedBlockName matching $query->query could be found");//no block found //TODO r12 map only has blocks up to id 255. On 4.0.0, return Item::fromString()? + if (count($blocks) === 1) { + $block = $blocks[0]; + $query->blockFullId = $block->getFullId(); + return $block; + } //"Hack" to get just one block if multiple results have been found. Most times this results in the default one (meta:0) $smallestMeta = PHP_INT_MAX; - $result = null; - foreach ($blocks as $block) { - if ($block->getMeta() < $smallestMeta) { - $smallestMeta = $block->getMeta(); - $result = $block; + foreach ($blocks as $blockFromStates) { + if ($blockFromStates->getMeta() < $smallestMeta) { + $smallestMeta = $blockFromStates->getMeta(); + $block = $blockFromStates; } } - #Loader::getInstance()->getLogger()->debug(TF::LIGHT_PURPLE . "Final block: " . TF::AQUA . $result); - /** @var Block $result */ - return [$result]; + $query->blockFullId = $block->getFullId(); + return $block; } public static function getStateByBlock(Block $block): ?BlockStatesEntry @@ -317,6 +308,9 @@ public static function getStateByBlock(Block $block): ?BlockStatesEntry $name = self::getBlockIdMapName($block); if ($name === null) return null; $damage = $block->getMeta(); + if(!array_key_exists($name,self::$legacyStateMap)){ + return null; + } $blockStates = clone self::$legacyStateMap[$name][$damage]->getBlockState()->getCompoundTag('states'); if ($blockStates === null) return null; return new BlockStatesEntry($name, $blockStates, $block); @@ -331,8 +325,8 @@ public static function getStateByCompound(CompoundTag $compoundTag): ?BlockState throw new InvalidArgumentException("Could not find default block states for $namespacedSelectedBlockName"); } - if (strpos($namespacedSelectedBlockName, "_door") !== false) { - $door = self::buildDoor($namespacedSelectedBlockName, $states); + if (str_contains($namespacedSelectedBlockName, "_door")) { + $door = self::buildDoor(BlockPalette::fromString($namespacedSelectedBlockName)->randomBlockQueries->generate(1)->current(), $states); //return self::getStateByBlock($door); return new BlockStatesEntry($namespacedSelectedBlockName, $states, $door); } @@ -351,7 +345,7 @@ public static function getStateByCompound(CompoundTag $compoundTag): ?BlockState * @param BlockStatesEntry $entry * @param bool $skipDefaults * @return string - * @throws RuntimeException + * @throws UnexpectedTagTypeException */ public static function printStates(BlockStatesEntry $entry, bool $skipDefaults): string { @@ -359,7 +353,6 @@ public static function printStates(BlockStatesEntry $entry, bool $skipDefaults): $blockIdentifier = $entry->blockIdentifier; $s = []; foreach ($printedCompound as $statesTagEntryName => $statesTagEntry) { - /** @var CompoundTag $defaultStatesNamedTag */ $defaultStatesNamedTag = self::getDefaultStates($blockIdentifier); $namedTag = $defaultStatesNamedTag->getTag($statesTagEntryName); if (!$namedTag instanceof ByteTag && !$namedTag instanceof StringTag && !$namedTag instanceof IntTag) { @@ -372,7 +365,7 @@ public static function printStates(BlockStatesEntry $entry, bool $skipDefaults): if ($statesTagEntry instanceof ByteTag) { $s[] = TF::RED . $statesTagEntryName . "=" . ($statesTagEntry->getValue() ? TF::GREEN . "true" : TF::RED . "false") . TF::RESET; } else if ($statesTagEntry instanceof IntTag) { - $s[] = TF::BLUE . $statesTagEntryName . "=" . TF::BLUE . $statesTagEntry->getValue() . TF::RESET; + $s[] = TF::AQUA . $statesTagEntryName . "=" . TF::AQUA . $statesTagEntry->getValue() . TF::RESET; } else if ($statesTagEntry instanceof StringTag) { $s[] = TF::LIGHT_PURPLE . $statesTagEntryName . "=" . TF::LIGHT_PURPLE . $statesTagEntry->getValue() . TF::RESET; } @@ -456,11 +449,13 @@ public static function runTests(): void foreach ($tests as $test) { try { Loader::getInstance()->getLogger()->debug(TF::GOLD . "Search query: " . TF::LIGHT_PURPLE . $test); - foreach (self::fromString($test) as $block) { + foreach (BlockPalette::fromString($test)->palette() as $block) { assert($block instanceof Block); $blockStatesEntry = self::getStateByBlock($block); - Server::getInstance()->getLogger()->debug(TF::LIGHT_PURPLE . self::printStates($blockStatesEntry, true)); - Server::getInstance()->getLogger()->debug(TF::LIGHT_PURPLE . self::printStates($blockStatesEntry, false)); + if ($blockStatesEntry !== null) { + Server::getInstance()->getLogger()->debug(TF::LIGHT_PURPLE . self::printStates($blockStatesEntry, true)); + Server::getInstance()->getLogger()->debug(TF::LIGHT_PURPLE . self::printStates($blockStatesEntry, false)); + } Server::getInstance()->getLogger()->debug(TF::LIGHT_PURPLE . "Final block: " . TF::AQUA . $block); } } catch (Exception $e) { @@ -468,9 +463,8 @@ public static function runTests(): void continue; } } - return;//TODO + //return;//TODO //test flip+rotation - /** @noinspection PhpUnreachableStatementInspection */ // $tests2 = [ // #"minecraft:wooden_slab[wood_type=oak]", // #"minecraft:wooden_slab[wood_type=spruce,top_slot_bit=true]", @@ -589,8 +583,9 @@ private static function doorEquals(int $currentoldDamage, CompoundTag $defaultSt /** * Generates an alias map for blockstates * Only call from main thread! - * @throws InvalidStateException * @throws AssumptionFailedError + * @throws InvalidStateException + * @throws UnexpectedTagTypeException * @internal * @noinspection PhpUnusedPrivateMethodInspection */ @@ -654,7 +649,7 @@ private static function generateBlockStateAliasMapJson(): void "end_portal", ]; foreach ($fullReplace as $stateAlias => $setTo) - if (strpos($alias, $stateAlias) !== false) { + if (str_contains($alias, $stateAlias)) { $alias = $setTo; } foreach ($partReplace as $replace) @@ -678,6 +673,7 @@ private static function generateBlockStateAliasMapJson(): void * Generates an alias map for blockstates * Only call from main thread! * @throws InvalidStateException + * @throws UnexpectedTagTypeException * @internal */ public static function generatePossibleStatesJson(): void @@ -695,7 +691,7 @@ public static function generatePossibleStatesJson(): void } if (!in_array($state->getValue(), $all[$stateName], true)) { $all[(string)$stateName][] = $state->getValue(); - if (strpos($stateName, "_bit") !== false) { + if (str_contains($stateName, "_bit")) { var_dump("_bit"); } else { var_dump("no _bit"); @@ -716,7 +712,7 @@ public static function generatePossibleStatesJson(): void * @param string $property * @return mixed */ - public static function &readAnyValue(object $object, string $property) + public static function &readAnyValue(object $object, string $property): mixed { $invoke = Closure::bind(function & () use ($property) { return $this->$property; diff --git a/src/xenialdan/MagicWE2/helper/Progress.php b/src/xenialdan/MagicWE2/helper/Progress.php index 41d2690e..31f1669c 100644 --- a/src/xenialdan/MagicWE2/helper/Progress.php +++ b/src/xenialdan/MagicWE2/helper/Progress.php @@ -7,9 +7,9 @@ class Progress { /** @var float Percentage */ - public $progress = 0.0; + public float $progress = 0.0; /** @var string */ - public $string = ""; + public string $string = ""; /** * Progress constructor. @@ -20,10 +20,10 @@ public function __construct(float $progress, string $info) { $this->progress = $progress; $this->string = $info; - } + } - public function __toString() - { - return "Progress: " . $this->progress . " String: " . $this->string; - } + public function __toString() + { + return "Progress: " . $this->progress . " String: " . $this->string; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/Scoreboard.php b/src/xenialdan/MagicWE2/helper/Scoreboard.php index d2d782e2..64f02a37 100644 --- a/src/xenialdan/MagicWE2/helper/Scoreboard.php +++ b/src/xenialdan/MagicWE2/helper/Scoreboard.php @@ -11,6 +11,7 @@ use pocketmine\utils\TextFormat as TF; use ReflectionClass; use ReflectionException; +use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\Selection; @@ -28,27 +29,34 @@ public function handleScoreboard(UserSession $session): void $line = 0; $selection = $session->getLatestSelection(); ScoreFactory::setScoreLine($player, ++$line, TF::GOLD . $session->getLanguage()->translateString("spacer", ["Selection"])); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Position: " . TF::RESET . "{$this->vecToString($selection->getPos1()->asVector3())} » {$this->vecToString($selection->getPos2()->asVector3())}"); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "World: " . TF::RESET . $selection->getWorld()->getFolderName()); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Shape: " . TF::RESET . (new ReflectionClass($selection->shape))->getShortName()); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Size: " . TF::RESET . "{$this->vecToString(new Vector3($selection->getSizeX(),$selection->getSizeY(),$selection->getSizeZ()))} ({$selection->getShape()->getTotalCount()})"); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Position: " . TF::RESET . API::vecToString($selection->getPos1()->asVector3()) . " » " . API::vecToString($selection->getPos2()->asVector3())); + 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 + 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::GOLD . $session->getLanguage()->translateString("spacer", ["Settings"])); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Tool Range: " . TF::RESET . Loader::getInstance()->getToolDistance()); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Tool Range: " . TF::RESET . Loader::getInstance()->getToolDistance()); $editLimit = Loader::getInstance()->getEditLimit(); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Limit: " . TF::RESET . ($editLimit === -1 ? $this->boolToString(false) : $editLimit)); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Wand Tool: " . TF::RESET . $this->boolToString($session->isWandEnabled())); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Debug Tool: " . TF::RESET . $this->boolToString($session->isDebugToolEnabled())); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "WAILA: " . TF::RESET . $this->boolToString($session->isWailaEnabled())); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Limit: " . TF::RESET . ($editLimit === -1 ? API::boolToString(false) : $editLimit)); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Wand Tool: " . TF::RESET . API::boolToString($session->isWandEnabled())); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Debug Tool: " . TF::RESET . API::boolToString($session->isDebugToolEnabled())); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " WAILA: " . TF::RESET . API::boolToString($session->isWailaEnabled())); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Outline: " . TF::RESET . API::boolToString($session->isOutlineEnabled())); if (($cb = $session->getCurrentClipboard()) instanceof SingleClipboard) { ScoreFactory::setScoreLine($player, ++$line, TF::GOLD . $session->getLanguage()->translateString("spacer", ["Clipboard"])); /** @var SingleClipboard $cb */ if ($cb->customName !== "") - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Name: " . TF::RESET . $cb->customName); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Name: " . TF::RESET . $cb->customName); if ($cb->selection instanceof Selection) { - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Shape: " . TF::RESET . (new ReflectionClass($cb->selection->shape))->getShortName()); - ScoreFactory::setScoreLine($player, ++$line, TF::ITALIC . "Size: " . TF::RESET . "{$this->vecToString(new Vector3($cb->selection->getSizeX(),$cb->selection->getSizeY(),$cb->selection->getSizeZ()))} ({$cb->getTotalCount()})"); + if ($cb->selection->shape === null) + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Shape: " . TF::RESET . 'N/A'); + else + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Shape: " . TF::RESET . (new ReflectionClass($cb->selection->shape))->getShortName()); + ScoreFactory::setScoreLine($player, ++$line, TF::BOLD . " Size: " . TF::RESET . API::vecToString(new Vector3($cb->selection->getSizeX(), $cb->selection->getSizeY(), $cb->selection->getSizeZ())) . " ({$cb->getTotalCount()})"); } } //todo current block palette, schematics, brushes @@ -57,14 +65,4 @@ public function handleScoreboard(UserSession $session): void } } } - - private function vecToString(Vector3 $v): string - { - return TF::RESET . "[" . TF::RED . $v->getFloorX() . TF::RESET . ":" . TF::GREEN . $v->getFloorY() . TF::RESET . ":" . TF::BLUE . $v->getFloorZ() . TF::RESET . "]"; - } - - private function boolToString(bool $b): string - { - return $b ? TF::RESET . TF::GREEN . "On" . TF::RESET : TF::RESET . TF::RED . "Off" . TF::RESET; - } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/helper/SessionHelper.php b/src/xenialdan/MagicWE2/helper/SessionHelper.php index a2fac7ff..aec4901b 100644 --- a/src/xenialdan/MagicWE2/helper/SessionHelper.php +++ b/src/xenialdan/MagicWE2/helper/SessionHelper.php @@ -4,7 +4,6 @@ namespace xenialdan\MagicWE2\helper; -use Ds\Map; use Exception; use InvalidArgumentException; use JsonException; @@ -14,9 +13,9 @@ use pocketmine\player\Player; use pocketmine\plugin\Plugin; use pocketmine\Server; -use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; +use Ramsey\Uuid\Rfc4122\UuidV4; +use Ramsey\Uuid\UuidInterface; use RuntimeException; use xenialdan\MagicWE2\event\MWESessionLoadEvent; use xenialdan\MagicWE2\exception\SessionException; @@ -28,21 +27,22 @@ use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\tool\Brush; use xenialdan\MagicWE2\tool\BrushProperties; +use function array_filter; +use function array_values; +use function count; class SessionHelper { - /** @var Map */ - private static $userSessions; - /** @var Map */ - private static $pluginSessions; + /** @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)) { throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); } - self::$userSessions = new Map(); - self::$pluginSessions = new Map(); } /** @@ -52,14 +52,14 @@ public static function init(): void public static function addSession(Session $session): void { if ($session instanceof UserSession) { - self::$userSessions->put($session->getUUID(), $session); + self::$userSessions[$session->getUUID()->toString()] = $session; 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->put($session->getUUID(), $session); + } else if ($session instanceof PluginSession) self::$pluginSessions[$session->getUUID()->toString()] = $session; } /** @@ -72,8 +72,8 @@ public static function destroySession(Session $session, bool $save = true): void { if ($session instanceof UserSession) { $session->cleanupInventory(); - self::$userSessions->remove($session->getUUID()); - } else if ($session instanceof PluginSession) self::$pluginSessions->remove($session->getUUID()); + unset(self::$userSessions[$session->getUUID()->toString()]); + } else if ($session instanceof PluginSession) unset(self::$pluginSessions[($session->getUUID()->toString())]); if ($save && $session instanceof UserSession) { $session->save(); } @@ -133,67 +133,47 @@ public static function hasSession(Player $player): bool */ public static function getUserSession(Player $player): ?UserSession { - if (self::$userSessions->isEmpty()) return null; - $filtered = self::$userSessions->filter(function (UUID $uuid, Session $session) use ($player) { + 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 ($filtered->isEmpty()) return null; + 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 $filtered->values()->first(); + return array_values($filtered)[0]; } /** * TODO cleanup or optimize - * @param UUID $uuid + * @param UuidInterface $uuid * @return null|Session * @throws SessionException */ - public static function getSessionByUUID(UUID $uuid): ?Session + public static function getSessionByUUID(UuidInterface $uuid): ?Session { - $v = null; - if (self::$userSessions->hasKey($uuid)) { - $v = self::$userSessions->get($uuid, null); - } else if (self::$pluginSessions->hasKey($uuid)) { - $v = self::$pluginSessions->get($uuid, null); - } else { - /* - * Sadly, this part is necessary. If you use UUID::fromString, the object "id" in the map does not match anymore - */ - $userFiltered = self::$userSessions->filter(function (UUID $uuid2, Session $session) use ($uuid) { - return $uuid2->equals($uuid); - }); - if (!$userFiltered->isEmpty()) $v = $userFiltered->values()->first(); - else { - $pluginFiltered = self::$pluginSessions->filter(function (UUID $uuid2, Session $session) use ($uuid) { - return $uuid2->equals($uuid); - }); - if (!$pluginFiltered->isEmpty()) $v = $pluginFiltered->values()->first(); - } - } + $v = self::$userSessions[$uuid->toString()] ?? self::$pluginSessions[$uuid->toString()] ?? null; if (!$v instanceof Session) throw new SessionException("Session with uuid {$uuid->toString()} not found"); return $v; } /** - * @return array|UserSession[] + * @return array */ public static function getUserSessions(): array { - return self::$userSessions->values()->toArray(); + return self::$userSessions; } /** - * @return array|PluginSession[] + * @return array */ public static function getPluginSessions(): array { - return self::$pluginSessions->values()->toArray(); + return self::$pluginSessions; } /** * @param Player $player * @return UserSession|null - * @throws AssumptionFailedError * @throws InvalidSkinException * @throws JsonException * @throws RuntimeException @@ -207,24 +187,24 @@ public static function loadUserSession(Player $player): ?UserSession 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) { - Loader::getInstance()->getLogger()->error("Could not load user session from json file {$path}: " . json_last_error_msg()); + 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 { - $session->setUUID(UUID::fromString($data["uuid"])); + $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 $brushUUID => $brushJson) { + foreach ($data["brushes"] as $brushJson) { try { $properties = BrushProperties::fromJson($brushJson["properties"]); $brush = new Brush($properties); - $session->addBrush($brush); - } catch (InvalidArgumentException $e) { + $session->getBrushes()->addBrush($brush); + } catch (InvalidArgumentException) { continue; } } @@ -252,13 +232,14 @@ public static function loadUserSession(Player $player): ?UserSession $latestSelection["pos2"]["z"], $shape ?? null ); - if ($selection instanceof Selection && $selection->isValid()) { + if ($selection->isValid()) { $session->addSelection($selection); } } } catch (RuntimeException $e) { } } + $session->setOutlineEnabled($data["outlineEnabled"]); //TODO clipboard } catch (Exception $exception) { return null; diff --git a/src/xenialdan/MagicWE2/helper/StructureStore.php b/src/xenialdan/MagicWE2/helper/StructureStore.php index f9e78b22..3d3a9211 100644 --- a/src/xenialdan/MagicWE2/helper/StructureStore.php +++ b/src/xenialdan/MagicWE2/helper/StructureStore.php @@ -6,7 +6,10 @@ use BlockHorizons\libschematic\Schematic; use InvalidArgumentException; +use pocketmine\nbt\NbtDataException; +use pocketmine\nbt\UnexpectedTagTypeException; use pocketmine\utils\SingletonTrait; +use UnexpectedValueException; use xenialdan\libstructure\exception\StructureFileException; use xenialdan\libstructure\exception\StructureFormatException; use xenialdan\libstructure\format\MCStructure; @@ -19,16 +22,16 @@ final class StructureStore /** * @var MCStructure[] */ - private $structures; + private array $structures; /** * @var Schematic[] */ - private $schematics; + private array $schematics; + /** @noinspection MkdirRaceConditionInspection */ public function __construct() { - @mkdir(Loader::getInstance()->getDataFolder() . 'structures'); - @mkdir(Loader::getInstance()->getDataFolder() . 'schematics'); + @mkdir(Loader::getInstance()->getDataFolder() . 'assets'); } /** @@ -38,14 +41,16 @@ public function __construct() * @throws InvalidArgumentException * @throws StructureFileException * @throws StructureFormatException + * @throws UnexpectedValueException + * @throws NbtDataException + * @throws UnexpectedTagTypeException */ public function loadStructure(string $filename, bool $override = true): MCStructure { $id = pathinfo($filename, PATHINFO_FILENAME); if (!$override && array_key_exists($id, $this->structures)) throw new InvalidArgumentException("Can not override $id"); - $path = Loader::getInstance()->getDataFolder() . 'structures' . DIRECTORY_SEPARATOR . $id . '.mcstructure';//TODO redundant? + $path = Loader::getInstance()->getDataFolder() . 'assets' . DIRECTORY_SEPARATOR . $id . '.mcstructure'; $structure = new MCStructure(); - $structure->parse($path); $this->structures[$id] = $structure; return $this->structures[$id]; @@ -75,7 +80,7 @@ public function loadSchematic(string $filename, bool $override = true): Schemati { $id = pathinfo($filename, PATHINFO_FILENAME); if (!$override && array_key_exists($id, $this->schematics)) throw new InvalidArgumentException("Can not override $id"); - $path = Loader::getInstance()->getDataFolder() . 'schematics' . DIRECTORY_SEPARATOR . $id . '.schematic'; + $path = Loader::getInstance()->getDataFolder() . 'assets' . DIRECTORY_SEPARATOR . $id . '.schematic'; $schematic = new Schematic(); $schematic->parse($path); $this->schematics[$id] = $schematic; diff --git a/src/xenialdan/MagicWE2/helper/WeightedRandom.php b/src/xenialdan/MagicWE2/helper/WeightedRandom.php new file mode 100644 index 00000000..d9d2f537 --- /dev/null +++ b/src/xenialdan/MagicWE2/helper/WeightedRandom.php @@ -0,0 +1,188 @@ +probabilities[] = $weight; + $this->indexes[] = $value; + } + + final public function count(): int + { + return count($this->probabilities); + } + + private function normalize(): void + { + $sum = array_sum($this->probabilities); + if($sum < 1) return; + foreach ($this->probabilities as &$weight) { + $weight /= $sum; + } + } + + final public function setup(): void + { + $probabilities_c = $this->count(); + if ($probabilities_c === 0) { + return; + } + + // Store the underlying generator. + $this->random = new Random(Binary::readLong(Binary::writeInt(mt_rand()) . Binary::writeInt(mt_rand()))); + $this->aliases = []; + + $this->normalize(); + + // Compute the average probability and cache it for later use. + $average = 1.0 / $probabilities_c; + + $probabilities = $this->probabilities; + + // Create two stacks to act as worklists as we populate the tables. + $small = new SplDoublyLinkedList(); + $large = new SplDoublyLinkedList(); + + // Populate the stacks with the input probabilities. + for ($i = 0; $i < $probabilities_c; ++$i) { + /** + * If the probability is below the average probability, then we add + * it to the small list; otherwise we add it to the large list. + */ + if ($probabilities[$i] >= $average) { + $large->push($i); + } else { + $small->push($i); + } + } + + /** + * As a note: in the mathematical specification of the algorithm, we + * will always exhaust the small list before the big list. However, + * due to floating point inaccuracies, this is not necessarily true. + * Consequently, this inner loop (which tries to pair small and large + * elements) will have to check that both lists aren't empty. + */ + while (!$small->isEmpty() && !$large->isEmpty()) { + /* Get the index of the small and the large probabilities. */ + $less = $small->pop(); + $more = $large->pop(); + + /** + * These probabilities have not yet been scaled up to be such that + * 1/n is given weight 1.0. We do this here instead. + */ + $this->probabilities[$less] = $probabilities[$less] * $probabilities_c; + $this->aliases[$less] = $more; + + /** + * Decrease the probability of the larger one by the appropriate + * amount. + */ + $probabilities[$more] = ($probabilities[$more] + $probabilities[$less]) - $average; + + /** + * If the new probability is less than the average, add it into the + * small list; otherwise add it to the large list. + */ + if ($probabilities[$more] >= 1.0 / $probabilities_c) { + $large->push($more); + } else { + $small->push($more); + } + } + + /** + * At this point, everything is in one list, which means that the + * remaining probabilities should all be 1/n. Based on this, set them + * appropriately. Due to numerical issues, we can't be sure which + * stack will hold the entries, so we empty both. + */ + while (!$small->isEmpty()) { + $this->probabilities[$small->pop()] = 1.0; + } + while (!$large->isEmpty()) { + $this->probabilities[$large->pop()] = 1.0; + } + } + + /** + * @param int $count + * @return Generator + */ + final public function generateIndexes(int $count): Generator + { + $probabilities_c = count($this->probabilities); + if ($probabilities_c > 0) { + while (--$count >= 0) { + $index = $this->random->nextBoundedInt($probabilities_c); + yield $this->random->nextFloat() <= $this->probabilities[$index] ? $index : $this->aliases[$index]; + } + } + } + + /** + * @param int $count + * @return Generator + */ + public function generate(int $count): Generator + { + foreach ($this->generateIndexes($count) as $index) { + yield $this->indexes[$index]; + } + } + + /** + * @return Generator + */ + public function indexes(): Generator + { + foreach ($this->indexes as $index) { + yield $index; + } + } + + /** + * @return array + */ + public function getIndexes(): array + { + return $this->indexes; + } + + /** + * @return float[] + */ + public function getProbabilities(): array + { + return $this->probabilities; + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/selection/Selection.php b/src/xenialdan/MagicWE2/selection/Selection.php index 2cb0afba..aa2c082f 100644 --- a/src/xenialdan/MagicWE2/selection/Selection.php +++ b/src/xenialdan/MagicWE2/selection/Selection.php @@ -10,9 +10,10 @@ use pocketmine\Server; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\Position; use pocketmine\world\World; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use RuntimeException; use Serializable; use xenialdan\MagicWE2\event\MWESelectionChangeEvent; @@ -30,21 +31,21 @@ class Selection implements Serializable, JsonSerializable { /** @var int|null */ - public $worldId; + public ?int $worldId = null; /** @var Vector3|null */ - public $pos1; + public ?Vector3 $pos1 = null; /** @var Vector3|null */ - public $pos2; - /** @var UUID */ - public $uuid; - /** @var UUID */ - public $sessionUUID; + public ?Vector3 $pos2 = null; + /** @var UuidInterface */ + public UuidInterface $uuid; + /** @var UuidInterface */ + public UuidInterface $sessionUUID; /** @var Shape|null */ - public $shape; + public ?Shape $shape = null; /** * Selection constructor. - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param World $world * @param ?int $minX * @param ?int $minY @@ -54,7 +55,7 @@ class Selection implements Serializable, JsonSerializable * @param ?int $maxZ * @param ?Shape $shape */ - public function __construct(UUID $sessionUUID, World $world, $minX = null, $minY = null, $minZ = null, $maxX = null, $maxY = null, $maxZ = null, ?Shape $shape = null) + 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) { $this->sessionUUID = $sessionUUID; $this->worldId = $world->getId(); @@ -65,7 +66,11 @@ public function __construct(UUID $sessionUUID, World $world, $minX = null, $minY $this->pos2 = (new Vector3($maxX, $maxY, $maxZ))->floor(); } if ($shape !== null) $this->shape = $shape; - $this->setUUID(UUID::fromRandom()); + $this->setUUID(Uuid::uuid4()); + try { + (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_CREATE))->call(); + } catch (RuntimeException $e) { + } } /** @@ -91,7 +96,7 @@ public function setWorld(World $world): void { $this->worldId = $world->getId(); try { - ($ev = new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_WORLD))->call(); + (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_WORLD))->call(); } catch (RuntimeException $e) { } } @@ -128,7 +133,7 @@ public function setPos1(Position $position): void if ($session instanceof Session) { $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('selection.pos1.set', [$this->pos1->getX(), $this->pos1->getY(), $this->pos1->getZ()])); try { - ($ev = new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_POS1))->call(); + (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_POS1))->call(); } catch (RuntimeException $e) { } } @@ -169,7 +174,7 @@ public function setPos2(Position $position): void if ($session instanceof Session) { $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('selection.pos2.set', [$this->pos2->getX(), $this->pos2->getY(), $this->pos2->getZ()])); try { - ($ev = new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_POS2))->call(); + (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_POS2))->call(); } catch (RuntimeException $e) { } } @@ -195,7 +200,7 @@ public function setShape(Shape $shape): void { $this->shape = $shape; try { - ($ev = new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_SHAPE))->call(); + (new MWESelectionChangeEvent($this, MWESelectionChangeEvent::TYPE_SHAPE))->call(); } catch (RuntimeException $e) { }//might cause duplicated call } @@ -246,17 +251,17 @@ public function getSizeZ(): int } /** - * @param UUID $uuid + * @param UuidInterface $uuid */ - public function setUUID(UUID $uuid): void + public function setUUID(UuidInterface $uuid): void { $this->uuid = $uuid; } /** - * @return UUID + * @return UuidInterface */ - public function getUUID(): UUID + public function getUUID(): UuidInterface { return $this->uuid; } @@ -267,7 +272,7 @@ public function getUUID(): UUID * @return string the string representation of the object or null * @since 5.1.0 */ - public function serialize() + public function serialize(): string { return serialize([ $this->worldId, @@ -282,17 +287,15 @@ public function serialize() /** * Constructs the object * @link http://php.net/manual/en/serializable.unserialize.php - * @param string $serialized

+ * @param string $data

* The string representation of the object. *

* @return void * @since 5.1.0 - * @noinspection PhpMissingParamTypeInspection */ - public function unserialize($serialized) + public function unserialize($data) { - var_dump($serialized); - /** @var Vector3 $pos1 , $pos2 */ + var_dump($data); [ $this->worldId, $this->pos1, @@ -300,17 +303,17 @@ public function unserialize($serialized) $this->uuid, $this->sessionUUID, $this->shape - ] = unserialize($serialized/*, ['allowed_classes' => [__CLASS__, Vector3::class,UUID::class,Shape::class]]*/);//TODO test pm4 + ] = unserialize($data/*, ['allowed_classes' => [__CLASS__, Vector3::class,UuidInterface::class,Shape::class]]*/);//TODO test pm4 } /** * Specify data which should be serialized to JSON * @link http://php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed data which can be serialized by json_encode, + * @return array data which can be serialized by json_encode, * which is a value of any type other than a resource. * @since 5.4.0 */ - public function jsonSerialize() + public function jsonSerialize(): array { $arr = (array)$this; if ($this->shape !== null) diff --git a/src/xenialdan/MagicWE2/selection/shape/Cone.php b/src/xenialdan/MagicWE2/selection/shape/Cone.php index 8814e408..00a3a3fd 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cone.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cone.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -13,15 +12,16 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Cone extends Shape { /** @var int */ - public $height = 5; + public int $height = 5; /** @var int */ - public $diameter = 5; + public int $diameter = 5; /** @var bool */ - public $flipped = false; + public bool $flipped = false; /** * Cone constructor. @@ -41,12 +41,12 @@ public function __construct(Vector3 $pasteVector, int $height, int $diameter, bo /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); $reducePerLayer = ($this->diameter / $this->height); @@ -66,10 +66,10 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: 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->getPos()->y >= World::Y_MAX || $block->getPos()->y < 0) continue;//TODO fuufufufuuu - if (empty($filterblocks)) yield $block; + 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 as $filterblock) { + 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))))) yield $block; } @@ -83,10 +83,10 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); @@ -105,7 +105,7 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator * @return string[] fastSerialized chunks * @throws Exception */ - public function getTouchedChunks($manager): array + public function getTouchedChunks(AsyncChunkManager|World $manager): array {//TODO optimize to remove "corner" chunks $this->validateChunkManager($manager); $maxX = ($this->getMaxVec3()->x + 1) >> 4; @@ -120,7 +120,7 @@ public function getTouchedChunks($manager): array continue; } print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } } print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; diff --git a/src/xenialdan/MagicWE2/selection/shape/Cube.php b/src/xenialdan/MagicWE2/selection/shape/Cube.php index 640d9ceb..131aa84c 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cube.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cube.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -13,11 +12,12 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Cube extends Shape { /** @var int */ - public $width = 5; + public int $width = 5; public function __construct(Vector3 $pasteVector, int $width) { @@ -28,43 +28,43 @@ public function __construct(Vector3 $pasteVector, int $width) /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator - { + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator + { $this->validateChunkManager($manager); 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++) { for ($z = (int)floor($this->getMinVec3()->z), $rz = 0; $z <= floor($this->getMaxVec3()->z); $z++, $rz++) { - $block = API::setComponents($manager->getBlockAt($x, $y, $z), (int)$x, (int)$y, (int)$z); + $block = API::setComponents($manager->getBlockAt($x, $y, $z), $x, $y, $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->getPos()->y >= World::Y_MAX || $block->getPos()->y < 0) continue;//TODO check for removal because relative might be at other y - if (API::hasFlag($flags, API::FLAG_HOLLOW) && ($block->getPos()->x > $this->getMinVec3()->getX() && $block->getPos()->x < $this->getMaxVec3()->getX()) && ($block->getPos()->y > $this->getMinVec3()->getY() && $block->getPos()->y < $this->getMaxVec3()->getY()) && ($block->getPos()->z > $this->getMinVec3()->getZ() && $block->getPos()->z < $this->getMaxVec3()->getZ())) continue; - if (empty($filterblocks)) yield $block; + if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//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 as $filterblock) { + 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))))) yield $block; } } } - } - } - } + } + } + } /** * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); for ($x = (int)floor($this->getMinVec3()->x); $x <= floor($this->getMaxVec3()->x); $x++) { @@ -74,13 +74,13 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator } } - /** - * @param World|AsyncChunkManager $manager - * @return string[] fastSerialized chunks - * @throws Exception - */ - public function getTouchedChunks($manager): array - { + /** + * @param World|AsyncChunkManager $manager + * @return string[] fastSerialized chunks + * @throws Exception + */ + public function getTouchedChunks(AsyncChunkManager|World $manager): array + { $this->validateChunkManager($manager); $maxX = ($this->getMaxVec3()->x + 1) >> 4; $minX = $this->getMinVec3()->x >> 4; @@ -94,32 +94,32 @@ public function getTouchedChunks($manager): array continue; } print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } - } - print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; - return $touchedChunks; - } + } + print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; + return $touchedChunks; + } - public function getAABB(): AxisAlignedBB - { - return new AxisAlignedBB( - ceil($this->pasteVector->x - $this->width / 2), - $this->pasteVector->y, - ceil($this->pasteVector->z - $this->width / 2), - -1 + ceil($this->pasteVector->x - $this->width / 2) + $this->width, - -1 + $this->pasteVector->y + $this->width, - -1 + ceil($this->pasteVector->z - $this->width / 2) + $this->width - ); - } + public function getAABB(): AxisAlignedBB + { + return new AxisAlignedBB( + ceil($this->pasteVector->x - $this->width / 2), + $this->pasteVector->y, + ceil($this->pasteVector->z - $this->width / 2), + -1 + ceil($this->pasteVector->x - $this->width / 2) + $this->width, + -1 + $this->pasteVector->y + $this->width, + -1 + ceil($this->pasteVector->z - $this->width / 2) + $this->width + ); + } - public function getTotalCount(): int - { - return $this->width ** 3; - } + public function getTotalCount(): int + { + return $this->width ** 3; + } - public static function getName(): string - { - return "Cube"; - } + public static function getName(): string + { + return "Cube"; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/selection/shape/Cuboid.php b/src/xenialdan/MagicWE2/selection/shape/Cuboid.php index 4f04b235..a79f18aa 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cuboid.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cuboid.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -13,15 +12,16 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Cuboid extends Shape { /** @var int */ - public $width = 5; + public int $width = 5; /** @var int */ - public $height = 5; + public int $height = 5; /** @var int */ - public $depth = 5; + public int $depth = 5; /** * Cuboid constructor. @@ -32,14 +32,14 @@ class Cuboid extends Shape */ public function __construct(Vector3 $pasteVector, int $width, int $height, int $depth) { - $this->pasteVector = $pasteVector; - $this->width = $width; - $this->height = $height; - $this->depth = $depth; - } + $this->pasteVector = $pasteVector; + $this->width = $width; + $this->height = $height; + $this->depth = $depth; + } - public static function constructFromPositions(Vector3 $pos1, Vector3 $pos2): self - { + 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; @@ -49,44 +49,44 @@ public static function constructFromPositions(Vector3 $pos1, Vector3 $pos2): sel /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator - { + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator + { $this->validateChunkManager($manager); 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++) { - $block = API::setComponents($manager->getBlockAt($x, $y, $z), (int)$x, (int)$y, (int)$z); + $block = API::setComponents($manager->getBlockAt($x, $y, $z), $x, $y, $z); #var_dump("shape getblocks", $block); 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->getPos()->y >= World::Y_MAX || $block->getPos()->y < 0) continue;//TODO check for removal because relative might be at other y - if (API::hasFlag($flags, API::FLAG_HOLLOW) && ($block->getPos()->x > $this->getMinVec3()->getX() && $block->getPos()->x < $this->getMaxVec3()->getX()) && ($block->getPos()->y > $this->getMinVec3()->getY() && $block->getPos()->y < $this->getMaxVec3()->getY()) && ($block->getPos()->z > $this->getMinVec3()->getZ() && $block->getPos()->z < $this->getMaxVec3()->getZ())) continue; - if (empty($filterblocks)) yield $block; + if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//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 as $filterblock) { + 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))))) yield $block; } } } - } - } - } + } + } + } /** * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); for ($x = (int)floor($this->getMinVec3()->x); $x <= floor($this->getMaxVec3()->x); $x++) { @@ -96,13 +96,13 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator } } - /** - * @param World|AsyncChunkManager $manager - * @return string[] fastSerialized chunks - * @throws Exception - */ - public function getTouchedChunks($manager): array - { + /** + * @param World|AsyncChunkManager $manager + * @return string[] fastSerialized chunks + * @throws Exception + */ + public function getTouchedChunks(AsyncChunkManager|World $manager): array + { $this->validateChunkManager($manager); $maxX = ($this->getMaxVec3()->x + 1) >> 4; $minX = $this->getMinVec3()->x >> 4; @@ -115,34 +115,34 @@ public function getTouchedChunks($manager): array if ($chunk === null) { continue; } - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } } - return $touchedChunks; - } + return $touchedChunks; + } - public function getAABB(): AxisAlignedBB - { - return new AxisAlignedBB( - ceil($this->pasteVector->x - $this->width / 2), - $this->pasteVector->y, - ceil($this->pasteVector->z - $this->depth / 2), - -1 + ceil($this->pasteVector->x - $this->width / 2) + $this->width, - -1 + $this->pasteVector->y + $this->height, - -1 + ceil($this->pasteVector->z - $this->depth / 2) + $this->depth - ); - } + public function getAABB(): AxisAlignedBB + { + return new AxisAlignedBB( + ceil($this->pasteVector->x - $this->width / 2), + $this->pasteVector->y, + ceil($this->pasteVector->z - $this->depth / 2), + -1 + ceil($this->pasteVector->x - $this->width / 2) + $this->width, + -1 + $this->pasteVector->y + $this->height, + -1 + ceil($this->pasteVector->z - $this->depth / 2) + $this->depth + ); + } - /** - * @return int - */ - public function getTotalCount(): int - { - return $this->width * $this->height * $this->depth; - } + /** + * @return int + */ + public function getTotalCount(): int + { + return $this->width * $this->height * $this->depth; + } - public static function getName(): string - { - return "Cuboid"; - } + public static function getName(): string + { + return "Cuboid"; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/selection/shape/Custom.php b/src/xenialdan/MagicWE2/selection/shape/Custom.php index 90345fdb..e4255c88 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Custom.php +++ b/src/xenialdan/MagicWE2/selection/shape/Custom.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; use pocketmine\math\Vector3; @@ -12,11 +11,12 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Custom extends Shape { /** @var Vector3[] */ - public $positions = []; + public array $positions = []; /** * Custom constructor. @@ -32,12 +32,12 @@ public function __construct(Vector3 $pasteVector, array $positions) /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); foreach ($this->positions as $position) { @@ -50,10 +50,10 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); /* Mapping: $walked[$hash]=true */ @@ -71,10 +71,11 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator * @return string[] fastSerialized chunks * @throws Exception */ - public function getTouchedChunks($manager): array + public function getTouchedChunks(AsyncChunkManager|World $manager): array { $this->validateChunkManager($manager); $touchedChunks = []; + /** @var Vector2 $vector2 */ foreach ($this->getLayer($manager) as $vector2) { $x = $vector2->getFloorX() >> 4; $z = $vector2->getFloorY() >> 4; @@ -82,7 +83,7 @@ public function getTouchedChunks($manager): array continue; } print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; return $touchedChunks; diff --git a/src/xenialdan/MagicWE2/selection/shape/Cylinder.php b/src/xenialdan/MagicWE2/selection/shape/Cylinder.php index 8a97dc8c..1540bf6f 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Cylinder.php +++ b/src/xenialdan/MagicWE2/selection/shape/Cylinder.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -13,13 +12,14 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Cylinder extends Shape { /** @var int */ - public $height = 1; + public int $height = 1; /** @var int */ - public $diameter = 5; + public int $diameter = 5; /** * Cylinder constructor. @@ -30,20 +30,20 @@ class Cylinder extends Shape public function __construct(Vector3 $pasteVector, int $height, int $diameter) { $this->pasteVector = $pasteVector; - $this->height = $height; - $this->diameter = $diameter; - } + $this->height = $height; + $this->diameter = $diameter; + } - /** + /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator - { + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator + { $this->validateChunkManager($manager); $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++) { @@ -57,27 +57,27 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: 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->getPos()->y >= World::Y_MAX || $block->getPos()->y < 0) continue;//TODO fuufufufuuu - if (empty($filterblocks)) yield $block; + if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fuufufufuuu + if ($filterblocks->empty()) yield $block; else { - foreach ($filterblocks as $filterblock) { + 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))))) yield $block; } } } - } - } - } + } + } + } /** * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); @@ -91,13 +91,13 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator } } - /** - * @param World|AsyncChunkManager $manager - * @return string[] fastSerialized chunks - * @throws Exception - */ - public function getTouchedChunks($manager): array - {//TODO optimize to remove "corner" chunks + /** + * @param World|AsyncChunkManager $manager + * @return string[] fastSerialized chunks + * @throws Exception + */ + public function getTouchedChunks(AsyncChunkManager|World $manager): array + {//TODO optimize to remove "corner" chunks $this->validateChunkManager($manager); $maxX = ($this->getMaxVec3()->x + 1) >> 4; $minX = $this->getMinVec3()->x >> 4; @@ -111,32 +111,32 @@ public function getTouchedChunks($manager): array continue; } print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } - } - print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; - return $touchedChunks; - } + } + print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; + return $touchedChunks; + } - public function getAABB(): AxisAlignedBB - { - return new AxisAlignedBB( - floor($this->pasteVector->x - $this->diameter / 2), - $this->pasteVector->y, - floor($this->pasteVector->z - $this->diameter / 2), - -1 + floor($this->pasteVector->x - $this->diameter / 2) + $this->diameter, - -1 + $this->pasteVector->y + $this->height, - -1 + floor($this->pasteVector->z - $this->diameter / 2) + $this->diameter - ); - } + public function getAABB(): AxisAlignedBB + { + return new AxisAlignedBB( + floor($this->pasteVector->x - $this->diameter / 2), + $this->pasteVector->y, + floor($this->pasteVector->z - $this->diameter / 2), + -1 + floor($this->pasteVector->x - $this->diameter / 2) + $this->diameter, + -1 + $this->pasteVector->y + $this->height, + -1 + floor($this->pasteVector->z - $this->diameter / 2) + $this->diameter + ); + } - public function getTotalCount(): int - { + public function getTotalCount(): int + { return (int)ceil(M_PI * (($this->diameter / 2) ** 2) * $this->height); - } + } - public static function getName(): string - { - return "Cylinder"; - } + public static function getName(): string + { + return "Cylinder"; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php b/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php index b2df0172..5131445a 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php +++ b/src/xenialdan/MagicWE2/selection/shape/Ellipsoid.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -13,15 +12,16 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Ellipsoid extends Shape { /** @var int */ - public $width = 5; + public int $width = 5; /** @var int */ - public $height = 5; + public int $height = 5; /** @var int */ - public $depth = 5; + public int $depth = 5; /** * Pyramid constructor. @@ -32,37 +32,37 @@ class Ellipsoid extends Shape */ public function __construct(Vector3 $pasteVector, int $width, int $height, int $depth) { - $this->pasteVector = $pasteVector; - $this->width = $width; - $this->height = $height; - $this->depth = $depth; - } + $this->pasteVector = $pasteVector; + $this->width = $width; + $this->height = $height; + $this->depth = $depth; + } - /** + /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator - { - $this->validateChunkManager($manager); - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); - $this->pasteVector = $this->getPasteVector()->add(0, -0.5, 0); - - $xrad = $this->width / 2; - $yrad = $this->height / 2; - $zrad = $this->depth / 2; - $xradSquared = $xrad ** 2; - $yradSquared = $yrad ** 2; - $zradSquared = $zrad ** 2; - $targetX = $this->pasteVector->getX(); - $targetY = $this->pasteVector->getY(); - $targetZ = $this->pasteVector->getZ(); - - for ($x = (int)floor($centerVec2->x - $this->width / 2 /*- 1*/); $x <= floor($centerVec2->x + $this->width / 2 /*+ 1*/); $x++) { + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator + { + $this->validateChunkManager($manager); + $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); + $this->pasteVector = $this->getPasteVector()->add(0, -0.5, 0); + + $xrad = $this->width / 2; + $yrad = $this->height / 2; + $zrad = $this->depth / 2; + $xradSquared = $xrad ** 2; + $yradSquared = $yrad ** 2; + $zradSquared = $zrad ** 2; + $targetX = $this->pasteVector->getX(); + $targetY = $this->pasteVector->getY(); + $targetZ = $this->pasteVector->getZ(); + + 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++) { $ySquared = ($targetY - $y + $yrad) ** 2; @@ -76,37 +76,37 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: 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->getPos()->y >= World::Y_MAX || $block->getPos()->y < 0) continue;//TODO fuufufufuuu - if (empty($filterblocks)) yield $block; + if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fuufufufuuu + if ($filterblocks->empty()) yield $block; else { - foreach ($filterblocks as $filterblock) { + 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))))) yield $block; } } } - } - } - } + } + } + } /** * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator - { - $this->validateChunkManager($manager); - $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); - - $xrad = $this->width / 2; - $zrad = $this->depth / 2; - $xradSquared = $xrad ** 2; - $zradSquared = $zrad ** 2; - $targetX = $this->pasteVector->getX(); - $targetZ = $this->pasteVector->getZ(); + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator + { + $this->validateChunkManager($manager); + $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); + + $xrad = $this->width / 2; + $zrad = $this->depth / 2; + $xradSquared = $xrad ** 2; + $zradSquared = $zrad ** 2; + $targetX = $this->pasteVector->getX(); + $targetZ = $this->pasteVector->getZ(); for ($x = (int)floor($centerVec2->x - $this->width / 2 /*- 1*/); $x <= floor($centerVec2->x + $this->width / 2 /*+ 1*/); $x++) { $xSquared = ($targetX - $x) ** 2; @@ -117,15 +117,15 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator yield new Vector2($x, $z); } } - } - - /** - * @param World|AsyncChunkManager $manager - * @return string[] fastSerialized chunks - * @throws Exception - */ - public function getTouchedChunks($manager): array - {//TODO optimize to remove "corner" chunks + } + + /** + * @param World|AsyncChunkManager $manager + * @return string[] fastSerialized chunks + * @throws Exception + */ + public function getTouchedChunks(AsyncChunkManager|World $manager): array + {//TODO optimize to remove "corner" chunks $this->validateChunkManager($manager); $maxX = ($this->getMaxVec3()->x + 1) >> 4; $minX = $this->getMinVec3()->x >> 4; @@ -139,32 +139,32 @@ public function getTouchedChunks($manager): array continue; } print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } - } - print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; - return $touchedChunks; - } - - public function getAABB(): AxisAlignedBB - { - return new AxisAlignedBB( - floor($this->pasteVector->x - $this->width / 2), - $this->pasteVector->y, - floor($this->pasteVector->z - $this->depth / 2), - -1 + floor($this->pasteVector->x - $this->width / 2) + $this->width, - -1 + $this->pasteVector->y + $this->height, - -1 + floor($this->pasteVector->z - $this->depth / 2) + $this->depth - ); - } - - public function getTotalCount(): int - { + } + print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; + return $touchedChunks; + } + + public function getAABB(): AxisAlignedBB + { + return new AxisAlignedBB( + floor($this->pasteVector->x - $this->width / 2), + $this->pasteVector->y, + floor($this->pasteVector->z - $this->depth / 2), + -1 + floor($this->pasteVector->x - $this->width / 2) + $this->width, + -1 + $this->pasteVector->y + $this->height, + -1 + floor($this->pasteVector->z - $this->depth / 2) + $this->depth + ); + } + + public function getTotalCount(): int + { return (int)floor(4 * M_PI * (($this->width / 2) + 1) * (($this->height / 2) + 1) * (($this->depth / 2) + 1) / 3); - } + } - public static function getName(): string - { - return "Ellipsoid"; - } + public static function getName(): string + { + return "Ellipsoid"; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/selection/shape/Pyramid.php b/src/xenialdan/MagicWE2/selection/shape/Pyramid.php index 7c1d6b63..0ac07608 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Pyramid.php +++ b/src/xenialdan/MagicWE2/selection/shape/Pyramid.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -13,17 +12,18 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Pyramid extends Shape { /** @var int */ - public $width = 5; + public int $width = 5; /** @var int */ - public $height = 5; + public int $height = 5; /** @var int */ - public $depth = 5; + public int $depth = 5; /** @var bool */ - public $flipped = false; + public bool $flipped = false; /** * Pyramid constructor. @@ -34,24 +34,24 @@ class Pyramid extends Shape * @param bool $flipped */ public function __construct(Vector3 $pasteVector, int $width, int $height, int $depth, bool $flipped = false) - { - $this->pasteVector = $pasteVector; - $this->width = $width; - $this->height = $height; - $this->depth = $depth; - $this->flipped = $flipped; - } + { + $this->pasteVector = $pasteVector; + $this->width = $width; + $this->height = $height; + $this->depth = $depth; + $this->flipped = $flipped; + } - /** + /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator - { + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator + { $this->validateChunkManager($manager); $reduceXPerLayer = -($this->width / $this->height); $reduceZPerLayer = -($this->depth / $this->height); @@ -75,27 +75,27 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: 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->getPos()->y >= World::Y_MAX || $block->getPos()->y < 0) continue;//TODO fuufufufuuu - if (empty($filterblocks)) yield $block; + if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fuufufufuuu + if ($filterblocks->empty()) yield $block; else { - foreach ($filterblocks as $filterblock) { + 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))))) yield $block; } } } - } - } - } + } + } + } /** * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); @@ -107,13 +107,13 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator } } - /** - * @param World|AsyncChunkManager $manager - * @return string[] fastSerialized chunks - * @throws Exception - */ - public function getTouchedChunks($manager): array - {//TODO optimize to remove "corner" chunks + /** + * @param World|AsyncChunkManager $manager + * @return string[] fastSerialized chunks + * @throws Exception + */ + public function getTouchedChunks(AsyncChunkManager|World $manager): array + {//TODO optimize to remove "corner" chunks $this->validateChunkManager($manager); $maxX = ($this->getMaxVec3()->x + 1) >> 4; $minX = $this->getMinVec3()->x >> 4; @@ -127,32 +127,32 @@ public function getTouchedChunks($manager): array continue; } print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } - } - print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; - return $touchedChunks; - } + } + print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; + return $touchedChunks; + } - public function getAABB(): AxisAlignedBB - { - return new AxisAlignedBB( - floor($this->pasteVector->x - $this->width / 2), - $this->pasteVector->y, - floor($this->pasteVector->z - $this->depth / 2), - -1 + floor($this->pasteVector->x - $this->width / 2) + $this->width, - -1 + $this->pasteVector->y + $this->height, - -1 + floor($this->pasteVector->z - $this->depth / 2) + $this->depth - ); - } + public function getAABB(): AxisAlignedBB + { + return new AxisAlignedBB( + floor($this->pasteVector->x - $this->width / 2), + $this->pasteVector->y, + floor($this->pasteVector->z - $this->depth / 2), + -1 + floor($this->pasteVector->x - $this->width / 2) + $this->width, + -1 + $this->pasteVector->y + $this->height, + -1 + floor($this->pasteVector->z - $this->depth / 2) + $this->depth + ); + } - 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 - { - return "Pyramid"; - } + 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 5acd29b7..2e83dc39 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Shape.php +++ b/src/xenialdan/MagicWE2/selection/shape/Shape.php @@ -7,19 +7,18 @@ use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\math\AxisAlignedBB; -use pocketmine\math\Vector2; use pocketmine\math\Vector3; -use pocketmine\world\ChunkManager; use pocketmine\world\format\Chunk; use pocketmine\world\World; use Serializable; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; abstract class Shape implements Serializable { /** @var null|Vector3 */ - public $pasteVector; + public ?Vector3 $pasteVector = null; public function getPasteVector(): ?Vector3 { @@ -31,14 +30,14 @@ public function setPasteVector(Vector3 $pasteVector): void $this->pasteVector = $pasteVector->asVector3(); } - /** - * Creates a chunk manager used for async editing - * @param Chunk[] $chunks - * @return AsyncChunkManager - */ - public static function getChunkManager(array $chunks): AsyncChunkManager - { - $manager = new AsyncChunkManager(); + /** + * Creates a chunk manager used for async editing + * @param Chunk[] $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, $chunkX, $chunkZ); $manager->setChunk($chunkX, $chunkZ, $chunk); @@ -46,92 +45,92 @@ public static function getChunkManager(array $chunks): AsyncChunkManager return $manager; } - /** - * @param mixed $manager - * @throws InvalidArgumentException - */ - public function validateChunkManager($manager): void - { - if (!$manager instanceof World && !$manager instanceof AsyncChunkManager) throw new InvalidArgumentException(get_class($manager) . " is not an instance of World or AsyncChunkManager"); - } + /** + * @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"); + } - abstract public function getTotalCount(): int; + abstract public function getTotalCount(): int; - /** + /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags * @return Generator|Block[] * @throws Exception + * @noinspection PhpDocSignatureInspection */ - abstract public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator; + abstract public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator; /** * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator + * @throws Exception + */ + abstract public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator; + + /** + * @param World|AsyncChunkManager $manager + * @return string[] fastSerialized chunks * @throws Exception */ - abstract public function getLayer($manager, int $flags = API::FLAG_BASE): Generator; - - /** - * @param ChunkManager $manager - * @return string[] fastSerialized chunks - * @throws Exception - */ - abstract public function getTouchedChunks(ChunkManager $manager): array; - - abstract public function getAABB(): AxisAlignedBB; - - /** - * @return Vector3 - */ - public function getMinVec3(): Vector3 - { - return new Vector3($this->getAABB()->minX, $this->getAABB()->minY, $this->getAABB()->minZ); - } - - /** - * @return Vector3 - */ - public function getMaxVec3(): Vector3 - { - return new Vector3($this->getAABB()->maxX, $this->getAABB()->maxY, $this->getAABB()->maxZ); - } - - abstract public static function getName(): string; - - public function getShapeProperties(): array - { + abstract public function getTouchedChunks(AsyncChunkManager|World $manager): array; + + abstract public function getAABB(): AxisAlignedBB; + + /** + * @return Vector3 + */ + public function getMinVec3(): Vector3 + { + return new Vector3($this->getAABB()->minX, $this->getAABB()->minY, $this->getAABB()->minZ); + } + + /** + * @return Vector3 + */ + public function getMaxVec3(): Vector3 + { + return new Vector3($this->getAABB()->maxX, $this->getAABB()->maxY, $this->getAABB()->maxZ); + } + + abstract public static function getName(): string; + + public function getShapeProperties(): array + { return array_diff(get_object_vars($this), get_class_vars(__CLASS__)); - } - - /** - * 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() - { - return serialize((array)$this); - } - - /** + } + + /** + * 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); + } + + /** * Constructs the object * @link http://php.net/manual/en/serializable.unserialize.php - * @param string $serialized

+ * @param string $data

* The string representation of the object. *

* @return void * @since 5.1.0 - * @noinspection PhpMissingParamTypeInspection */ - public function unserialize($serialized) - { - $unserialize = unserialize($serialized/*, ['allowed_classes' => [__CLASS__]]*/);//TODO test pm4 + public function unserialize($data) + { + $unserialize = unserialize($data/*, ['allowed_classes' => [__CLASS__]]*/);//TODO test pm4 array_walk($unserialize, function ($value, $key) { $this->$key = $value; }); diff --git a/src/xenialdan/MagicWE2/selection/shape/ShapeRegistry.php b/src/xenialdan/MagicWE2/selection/shape/ShapeRegistry.php index 7dfd1f74..a32cbc66 100644 --- a/src/xenialdan/MagicWE2/selection/shape/ShapeRegistry.php +++ b/src/xenialdan/MagicWE2/selection/shape/ShapeRegistry.php @@ -9,7 +9,7 @@ class ShapeRegistry//todo use SingletonTrait { /** @var string[] */ - private static $shapes = []; + private static array $shapes = []; public const CUBOID = "Cuboid"; public const CUBE = "Cube"; @@ -20,57 +20,57 @@ class ShapeRegistry//todo use SingletonTrait public const PYRAMID = "Pyramid"; public const ELLIPSOID = "Ellipsoid"; - public function __construct() - { - self::registerShape(self::SPHERE, Sphere::class); - self::registerShape(self::CUBE, Cube::class); - self::registerShape(self::CUBOID, Cuboid::class); - self::registerShape(self::CYLINDER, Cylinder::class); - self::registerShape(self::CONE, Cone::class); - self::registerShape(self::PYRAMID, Pyramid::class); - self::registerShape(self::ELLIPSOID, Ellipsoid::class); - self::registerShape(self::CUSTOM, Custom::class); - } + public function __construct() + { + self::registerShape(self::SPHERE, Sphere::class); + self::registerShape(self::CUBE, Cube::class); + self::registerShape(self::CUBOID, Cuboid::class); + self::registerShape(self::CYLINDER, Cylinder::class); + self::registerShape(self::CONE, Cone::class); + self::registerShape(self::PYRAMID, Pyramid::class); + self::registerShape(self::ELLIPSOID, Ellipsoid::class); + self::registerShape(self::CUSTOM, Custom::class); + } - public static function registerShape(string $name, string $class): void - { - self::$shapes[$name] = $class; - } + public static function registerShape(string $name, string $class): void + { + self::$shapes[$name] = $class; + } - /** - * @return array - */ - public static function getShapes(): array - { - return self::$shapes; - } + /** + * @return array + */ + public static function getShapes(): array + { + return self::$shapes; + } - /** - * @param string $name - * @return string - * @throws ShapeNotFoundException - */ - public static function getShape(string $name): string - { - if (isset(self::$shapes[$name])) return self::$shapes[$name]; - throw new ShapeNotFoundException("Shape $name not found"); - } + /** + * @param string $name + * @return string + * @throws ShapeNotFoundException + */ + public static function getShape(string $name): string + { + if (isset(self::$shapes[$name])) return self::$shapes[$name]; + throw new ShapeNotFoundException("Shape $name not found"); + } - /** - * @param string $shapeClass - * @return string - * @throws ShapeNotFoundException - */ - public static function getShapeName(string $shapeClass): string - { - $names = array_flip(self::$shapes); - if (isset($names[$shapeClass])) return $names[$shapeClass]; - throw new ShapeNotFoundException("Shape $shapeClass not found"); - } + /** + * @param string $shapeClass + * @return string + * @throws ShapeNotFoundException + */ + public static function getShapeName(string $shapeClass): string + { + $names = array_flip(self::$shapes); + if (isset($names[$shapeClass])) return $names[$shapeClass]; + throw new ShapeNotFoundException("Shape $shapeClass not found"); + } - public static function getDefaultShapeProperties(string $className): array - { - return array_diff_key(get_class_vars($className), get_class_vars(Shape::class)); - } + public static function getDefaultShapeProperties(string $className): array + { + return array_diff_key(get_class_vars($className), get_class_vars(Shape::class)); + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/selection/shape/Sphere.php b/src/xenialdan/MagicWE2/selection/shape/Sphere.php index eaa51370..f452900c 100644 --- a/src/xenialdan/MagicWE2/selection/shape/Sphere.php +++ b/src/xenialdan/MagicWE2/selection/shape/Sphere.php @@ -4,7 +4,6 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -13,11 +12,12 @@ use pocketmine\world\World; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; class Sphere extends Shape { /** @var int */ - public $diameter = 5; + public int $diameter = 5; /** * Sphere constructor. @@ -33,12 +33,12 @@ public function __construct(Vector3 $pasteVector, int $diameter) /** * Returns the blocks by their actual position * @param World|AsyncChunkManager $manager The world or AsyncChunkManager - * @param Block[] $filterblocks If not empty, applying a filter on the block list + * @param BlockPalette $filterblocks If not empty, applying a filter on the block list * @param int $flags - * @return Generator|Block[] + * @return Generator * @throws Exception */ - public function getBlocks($manager, array $filterblocks = [], int $flags = API::FLAG_BASE): Generator + public function getBlocks(AsyncChunkManager|World $manager, BlockPalette $filterblocks, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); for ($x = (int)floor($this->pasteVector->x - $this->diameter / 2 - 1); $x <= floor($this->pasteVector->x + $this->diameter / 2 + 1); $x++) { @@ -51,10 +51,10 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: 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->getPos()->y >= World::Y_MAX || $block->getPos()->y < 0) continue;//TODO fufufufuuu - if (empty($filterblocks)) yield $block; + if ($block->getPosition()->y >= World::Y_MAX || $block->getPosition()->y < 0) continue;//TODO fufufufuuu + if ($filterblocks->empty()) yield $block; else { - foreach ($filterblocks as $filterblock) { + 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))))) yield $block; } @@ -68,10 +68,10 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); $centerVec2 = new Vector2($this->getPasteVector()->getX(), $this->getPasteVector()->getZ()); @@ -90,7 +90,7 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator * @return string[] fastSerialized chunks * @throws Exception */ - public function getTouchedChunks($manager): array + public function getTouchedChunks(AsyncChunkManager|World $manager): array {//TODO optimize to remove "corner" chunks $this->validateChunkManager($manager); $maxX = ($this->getMaxVec3()->x + 1) >> 4; @@ -105,7 +105,7 @@ public function getTouchedChunks($manager): array continue; } print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } } print "Touched chunks count: " . count($touchedChunks) . PHP_EOL; diff --git a/src/xenialdan/MagicWE2/session/PluginSession.php b/src/xenialdan/MagicWE2/session/PluginSession.php index ad6698ee..737b3bf0 100644 --- a/src/xenialdan/MagicWE2/session/PluginSession.php +++ b/src/xenialdan/MagicWE2/session/PluginSession.php @@ -4,44 +4,44 @@ namespace xenialdan\MagicWE2\session; -use Ds\Deque; use pocketmine\plugin\Plugin; -use pocketmine\uuid\UUID; +use Ramsey\Uuid\Uuid; +use SplDoublyLinkedList; use xenialdan\MagicWE2\Loader; class PluginSession extends Session { /** @var Plugin */ - private $plugin; + private Plugin $plugin; public function __construct(Plugin $plugin) { $this->plugin = $plugin; - $this->setUUID(UUID::fromRandom()); - $this->undoHistory = new Deque(); - $this->redoHistory = new Deque(); + $this->setUUID(Uuid::uuid4()); + $this->undoHistory = new SplDoublyLinkedList(); + $this->redoHistory = new SplDoublyLinkedList(); } public function getPlugin(): Plugin - { - return $this->plugin; - } - - public function __toString() - { - return __CLASS__ . - " UUID: " . $this->getUUID()->__toString() . - " Plugin: " . $this->getPlugin()->getName() . - " Selections: " . count($this->getSelections()) . - " Latest: " . $this->getLatestSelectionUUID() . - " Clipboards: " . count($this->getClipboards()) . - " Current: " . $this->getCurrentClipboardIndex() . - " Undos: " . count($this->undoHistory) . - " Redos: " . count($this->redoHistory); - } - - public function sendMessage(string $message): void - { - $this->plugin->getLogger()->info(Loader::PREFIX . $message); - } + { + return $this->plugin; + } + + public function __toString() + { + return __CLASS__ . + " UUID: " . $this->getUUID()->__toString() . + " Plugin: " . $this->getPlugin()->getName() . + " Selections: " . count($this->getSelections()) . + " Latest: " . $this->getLatestSelectionUUID() . + " Clipboards: " . count($this->getClipboards()) . + " Current: " . $this->getCurrentClipboardIndex() . + " Undos: " . count($this->undoHistory) . + " Redos: " . count($this->redoHistory); + } + + public function sendMessage(string $message): void + { + $this->plugin->getLogger()->info(Loader::PREFIX . $message); + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/session/Session.php b/src/xenialdan/MagicWE2/session/Session.php index 06ac0dfe..4f50881d 100644 --- a/src/xenialdan/MagicWE2/session/Session.php +++ b/src/xenialdan/MagicWE2/session/Session.php @@ -4,15 +4,15 @@ namespace xenialdan\MagicWE2\session; -use Ds\Deque; use Exception; use InvalidArgumentException; use pocketmine\lang\Language; use pocketmine\Server; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\World; +use Ramsey\Uuid\UuidInterface; use RuntimeException; +use SplDoublyLinkedList; use xenialdan\MagicWE2\clipboard\Clipboard; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\Loader; @@ -23,35 +23,35 @@ abstract class Session { public const MAX_CLIPBOARDS = 5; public const MAX_HISTORY = 32; - /** @var UUID */ - private $uuid; + /** @var UuidInterface */ + private UuidInterface $uuid; //todo change to a list of objects with a pointer of the latest action /** @var Selection[] */ - private $selections = []; - /** @var UUID|null */ - private $latestselection; + private array $selections = []; + /** @var UuidInterface|null */ + private ?UuidInterface $latestselection = null; //todo change to a list of objects with a pointer of the latest action /** @var Clipboard[] */ - private $clipboards = []; + private array $clipboards = []; /** @var int */ - private $currentClipboard = -1; - /** @var Deque */ - public $undoHistory; - /** @var Deque */ - public $redoHistory; + private int $currentClipboard = -1; + /** @var SplDoublyLinkedList */ + public SplDoublyLinkedList $undoHistory; + /** @var SplDoublyLinkedList */ + public SplDoublyLinkedList $redoHistory; /** - * @return UUID + * @return UuidInterface */ - public function getUUID(): UUID + public function getUUID(): UuidInterface { return $this->uuid; } /** - * @param UUID $uuid + * @param UuidInterface $uuid */ - public function setUUID(UUID $uuid): void + public function setUUID(UuidInterface $uuid): void { $this->uuid = $uuid; } @@ -64,15 +64,14 @@ public function &addSelection(Selection $selection): ?Selection { $this->selections[$selection->getUUID()->toString()] = $selection; $this->setLatestSelectionUUID($selection->getUUID()); - $selection = $this->getLatestSelection(); - return $selection; + return $this->getLatestSelection(); } /** - * @param UUID $uuid + * @param UuidInterface $uuid * @return null|Selection */ - public function &getSelectionByUUID(UUID $uuid): ?Selection + public function &getSelectionByUUID(UuidInterface $uuid): ?Selection { $selection = $this->selections[$uuid->toString()] ?? null; return $selection; @@ -103,203 +102,203 @@ public function &getLatestSelection(): ?Selection } /** - * @return Selection[] - */ - public function getSelections(): array - { - return $this->selections; - } + * @return Selection[] + */ + public function getSelections(): array + { + return $this->selections; + } - /** - * @param mixed $selections - */ - public function setSelections($selections): void - { - $this->selections = $selections; - } + /** + * @param mixed $selections + */ + public function setSelections(mixed $selections): void + { + $this->selections = $selections; + } - /** - * @return UUID|null - */ - public function getLatestSelectionUUID(): ?UUID - { - return $this->latestselection; - } + /** + * @return UuidInterface|null + */ + public function getLatestSelectionUUID(): ?UuidInterface + { + return $this->latestselection; + } - /** - * @param UUID $latestselection - */ - public function setLatestSelectionUUID(UUID $latestselection): void - { - $this->latestselection = $latestselection; - } + /** + * @param UuidInterface $latestselection + */ + public function setLatestSelectionUUID(UuidInterface $latestselection): void + { + $this->latestselection = $latestselection; + } - /** - * @return int - */ - public function getCurrentClipboardIndex(): int - { - return $this->currentClipboard; - } + /** + * @return int + */ + public function getCurrentClipboardIndex(): int + { + return $this->currentClipboard; + } - /** - * @return null|Clipboard - */ - public function getCurrentClipboard(): ?Clipboard - { - return $this->clipboards[$this->currentClipboard] ?? null; - } + /** + * @return null|Clipboard + */ + public function getCurrentClipboard(): ?Clipboard + { + return $this->clipboards[$this->currentClipboard] ?? null; + } - /** - * @param string $name - * @return null|Clipboard - */ - public function getClipboardByName(string $name): ?Clipboard - { - foreach ($this->clipboards as $clipboard) { - if ($clipboard->getCustomName() === $name) return $clipboard; - } - return null; - } + /** + * @param string $name + * @return null|Clipboard + */ + public function getClipboardByName(string $name): ?Clipboard + { + foreach ($this->clipboards as $clipboard) { + if ($clipboard->getCustomName() === $name) return $clipboard; + } + return null; + } - /** - * @param int $id - * @return null|Clipboard - */ - public function getClipboardById(int $id): ?Clipboard - { - return $this->clipboards[$id] ?? null; - } + /** + * @param int $id + * @return null|Clipboard + */ + public function getClipboardById(int $id): ?Clipboard + { + return $this->clipboards[$id] ?? null; + } - /** - * TODO - * @return Clipboard[] - */ - public function getClipboards(): array - { - return $this->clipboards; - } + /** + * TODO + * @return Clipboard[] + */ + public function getClipboards(): array + { + return $this->clipboards; + } - /** - * TODO - * @param Clipboard[] $clipboards - * @return bool - */ - public function setClipboards(array $clipboards): bool - { - $this->clipboards = $clipboards; - return true; - } + /** + * TODO + * @param Clipboard[] $clipboards + * @return bool + */ + public function setClipboards(array $clipboards): bool + { + $this->clipboards = $clipboards; + return true; + } - /** - * @param Clipboard $clipboard - * @param bool $setAsCurrent - * @return int The index of the clipboard - */ - public function addClipboard(Clipboard $clipboard, bool $setAsCurrent = true): int - { - $amount = array_push($this->clipboards, $clipboard); - if ($amount > self::MAX_CLIPBOARDS) array_shift($this->clipboards); - $i = array_search($clipboard, $this->clipboards, true); - if ($i !== false) { - if ($setAsCurrent) $this->currentClipboard = (int)$i; - return (int)$i; - } - return -1; - } + /** + * @param Clipboard $clipboard + * @param bool $setAsCurrent + * @return int The index of the clipboard + */ + public function addClipboard(Clipboard $clipboard, bool $setAsCurrent = true): int + { + $amount = array_push($this->clipboards, $clipboard); + if ($amount > self::MAX_CLIPBOARDS) array_shift($this->clipboards); + $i = array_search($clipboard, $this->clipboards, true); + if ($i !== false) { + if ($setAsCurrent) $this->currentClipboard = (int)$i; + return (int)$i; + } + return -1; + } - /** - * @param RevertClipboard $revertClipboard - */ - public function addRevert(RevertClipboard $revertClipboard): void - { - $this->redoHistory->clear(); - $this->undoHistory->push($revertClipboard); - while ($this->undoHistory->count() > self::MAX_HISTORY) { - $this->undoHistory->shift(); - } - } + /** + * @param RevertClipboard $revertClipboard + */ + public function addRevert(RevertClipboard $revertClipboard): void + { + $this->redoHistory = new SplDoublyLinkedList(); + $this->undoHistory->push($revertClipboard); + while ($this->undoHistory->count() > self::MAX_HISTORY) { + $this->undoHistory->shift(); + } + } - /** - * @throws Exception - */ - public function undo(): void - { - if ($this->undoHistory->count() === 0) { - $this->sendMessage(TF::RED . $this->getLanguage()->translateString('session.undo.none')); - return; - } - /** @var RevertClipboard $revertClipboard */ - $revertClipboard = $this->undoHistory->pop(); + /** + * @throws Exception + */ + public function undo(): void + { + 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) { + 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)])); - } + 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)])); + } - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function redo(): void - { - if ($this->redoHistory->count() === 0) { - $this->sendMessage(TF::RED . $this->getLanguage()->translateString('session.redo.none')); - return; - } - /** @var RevertClipboard $revertClipboard */ - $revertClipboard = $this->redoHistory->pop(); - Server::getInstance()->getAsyncPool()->submitTask(new AsyncRevertTask($this->getUUID(), $revertClipboard, AsyncRevertTask::TYPE_REDO)); - $this->sendMessage(TF::GREEN . $this->getLanguage()->translateString('session.redo.left', [count($this->redoHistory)])); - } + /** + * @throws InvalidArgumentException + * @throws RuntimeException + */ + public function redo(): void + { + if ($this->redoHistory->count() === 0) { + $this->sendMessage(TF::RED . $this->getLanguage()->translateString('session.redo.none')); + return; + } + /** @var RevertClipboard $revertClipboard */ + $revertClipboard = $this->redoHistory->pop(); + Server::getInstance()->getAsyncPool()->submitTask(new AsyncRevertTask($this->getUUID(), $revertClipboard, AsyncRevertTask::TYPE_REDO)); + $this->sendMessage(TF::GREEN . $this->getLanguage()->translateString('session.redo.left', [count($this->redoHistory)])); + } - public function clearHistory(): void - { - $this->undoHistory->clear(); - $this->redoHistory->clear(); - } + public function clearHistory(): void + { + $this->undoHistory = new SplDoublyLinkedList(); + $this->redoHistory = new SplDoublyLinkedList(); + } - public function clearClipboard(): void - { - $this->setClipboards([]); - $this->currentClipboard = -1; - } + public function clearClipboard(): void + { + $this->setClipboards([]); + $this->currentClipboard = -1; + } - /** - * @return Language - */ - public function getLanguage(): Language - { - return Loader::getInstance()->getLanguage(); - } + /** + * @return Language + */ + public function getLanguage(): Language + { + return Loader::getInstance()->getLanguage(); + } abstract public function sendMessage(string $message): void; - public function __toString() - { - return __CLASS__ . - " UUID: " . $this->getUUID()->__toString() . - " Selections: " . count($this->getSelections()) . - " Latest: " . $this->getLatestSelectionUUID() . - " Clipboards: " . count($this->getClipboards()) . - " Current: " . $this->getCurrentClipboardIndex() . - " Undos: " . count($this->undoHistory) . - " Redos: " . count($this->redoHistory); - } + public function __toString() + { + return __CLASS__ . + " UUID: " . $this->getUUID()->__toString() . + " Selections: " . count($this->getSelections()) . + " Latest: " . $this->getLatestSelectionUUID() . + " Clipboards: " . count($this->getClipboards()) . + " Current: " . $this->getCurrentClipboardIndex() . + " Undos: " . count($this->undoHistory) . + " Redos: " . count($this->redoHistory); + } - /* - * TODO list: - * session storing/recovering from file/cleanup if too old - * session items - * recover session items + commands to get back already created/configured items/tool/brushes - * proper multi-selection-usage - * setState/getState on big actions, status bar/boss bar/texts/titles/popups - * inspect other player's sessions - * destroy session if owning player lost permission/gets banned - * optimise destroySession/__destruct of sessions - * clipboard selection (renaming?) - */ + /* + * TODO list: + * session storing/recovering from file/cleanup if too old + * session items + * recover session items + commands to get back already created/configured items/tool/brushes + * proper multi-selection-usage + * setState/getState on big actions, status bar/boss bar/texts/titles/popups + * inspect other player's sessions + * destroy session if owning player lost permission/gets banned + * optimise destroySession/__destruct of sessions + * clipboard selection (renaming?) + */ } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/session/UserSession.php b/src/xenialdan/MagicWE2/session/UserSession.php index 9960dfa1..7146fb37 100644 --- a/src/xenialdan/MagicWE2/session/UserSession.php +++ b/src/xenialdan/MagicWE2/session/UserSession.php @@ -4,50 +4,39 @@ namespace xenialdan\MagicWE2\session; -use Ds\Deque; -use Exception; -use InvalidArgumentException; use jackmd\scorefactory\ScoreFactory; -use JsonException; use JsonSerializable; -use pocketmine\item\Item; use pocketmine\lang\Language; use pocketmine\lang\LanguageNotFoundException; -use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; -use TypeError; +use SplDoublyLinkedList; use xenialdan\apibossbar\BossBar; use xenialdan\MagicWE2\API; -use xenialdan\MagicWE2\exception\ActionNotFoundException; -use xenialdan\MagicWE2\exception\BrushException; -use xenialdan\MagicWE2\exception\ShapeNotFoundException; use xenialdan\MagicWE2\helper\Scoreboard; use xenialdan\MagicWE2\Loader; -use xenialdan\MagicWE2\tool\Brush; -use xenialdan\MagicWE2\tool\BrushProperties; +use xenialdan\MagicWE2\selection\Selection; +use xenialdan\MagicWE2\session\data\AssetCollection; +use xenialdan\MagicWE2\session\data\BrushCollection; +use xenialdan\MagicWE2\session\data\Outline; +use xenialdan\MagicWE2\session\data\PaletteCollection; +use function mkdir; class UserSession extends Session implements JsonSerializable //TODO use JsonMapper { - /** @var Player|null */ - private $player; - /** @var BossBar */ - private $bossBar; - /** @var Scoreboard|null */ - public $sidebar; - /** @var bool */ - private $wandEnabled = true; - /** @var bool */ - private $debugToolEnabled = true; - /** @var bool */ - private $wailaEnabled = true; - /** @var bool */ - private $sidebarEnabled = true;//TODO settings/commands - /** @var Brush[] */ - private $brushes = []; - /** @var Language|null */ - private $lang; + private ?Player $player = null; + private BossBar $bossBar; + public ?Scoreboard $sidebar = null; + private bool $wandEnabled = true; + private bool $debugToolEnabled = true; + private bool $wailaEnabled = true; + private bool $sidebarEnabled = true;//TODO settings/commands + private bool $outlineEnabled = true; + private ?Outline $outline = null; + private BrushCollection $brushes; + private AssetCollection $assets; + private PaletteCollection $palettes; + private ?Language $lang = null; public function __construct(Player $player) { @@ -59,8 +48,11 @@ public function __construct(Player $player) if (Loader::hasScoreboard()) { $this->sidebar = new Scoreboard(); } - $this->undoHistory = new Deque(); - $this->redoHistory = new Deque(); + $this->undoHistory = new SplDoublyLinkedList(); + $this->redoHistory = new SplDoublyLinkedList(); + $this->brushes = new BrushCollection($this); + $this->assets = new AssetCollection($this); + $this->palettes = new PaletteCollection($this); try { if (is_null($this->lang)) $this->lang = new Language(Language::FALLBACK_LANGUAGE, Loader::getInstance()->getLanguageFolder()); @@ -78,9 +70,6 @@ public function __destruct() } } - /** - * @return Language - */ public function getLanguage(): Language { return $this->lang; @@ -103,70 +92,45 @@ public function setLanguage(string $langShort): void } } - /** - * @param null|Player $player - */ public function setPlayer(?Player $player): void { $this->player = $player; } - /** - * @return null|Player - */ public function getPlayer(): ?Player { return $this->player; } - /** - * @return bool - */ public function isWandEnabled(): bool { return $this->wandEnabled; } - /** - * @param bool $wandEnabled - * @return string - */ public function setWandEnabled(bool $wandEnabled): string { $this->wandEnabled = $wandEnabled; + $this->sidebar?->handleScoreboard($this); return Loader::PREFIX . $this->getLanguage()->translateString('tool.wand.setenabled', [($wandEnabled ? TF::GREEN . $this->getLanguage()->translateString('enabled') : TF::RED . $this->getLanguage()->translateString('disabled'))]); } - /** - * @return bool - */ public function isDebugToolEnabled(): bool { return $this->debugToolEnabled; } - /** - * @param bool $debugToolEnabled - * @return string - */ public function setDebugToolEnabled(bool $debugToolEnabled): string { $this->debugToolEnabled = $debugToolEnabled; + $this->sidebar?->handleScoreboard($this); return Loader::PREFIX . $this->getLanguage()->translateString('tool.debug.setenabled', [($debugToolEnabled ? TF::GREEN . $this->getLanguage()->translateString('enabled') : TF::RED . $this->getLanguage()->translateString('disabled'))]); } - /** - * @return bool - */ public function isSidebarEnabled(): bool { return $this->sidebarEnabled; } - /** - * @param bool $sidebarEnabled - * @return string - */ public function setSidebarEnabled(bool $sidebarEnabled): string { $player = $this->getPlayer(); @@ -180,18 +144,11 @@ public function setSidebarEnabled(bool $sidebarEnabled): string return Loader::PREFIX . $this->getLanguage()->translateString('tool.sidebar.setenabled', [($sidebarEnabled ? TF::GREEN . $this->getLanguage()->translateString('enabled') : TF::RED . $this->getLanguage()->translateString('disabled'))]); } - /** - * @return bool - */ public function isWailaEnabled(): bool { return $this->wailaEnabled; } - /** - * @param bool $wailaEnabled - * @return string - */ public function setWailaEnabled(bool $wailaEnabled): string { $player = $this->getPlayer(); @@ -202,123 +159,75 @@ public function setWailaEnabled(bool $wailaEnabled): string } else { Loader::getInstance()->wailaBossBar->hideFrom([$player]); } + $this->sidebar?->handleScoreboard($this); return Loader::PREFIX . $this->getLanguage()->translateString('tool.waila.setenabled', [($wailaEnabled ? TF::GREEN . $this->getLanguage()->translateString('enabled') : TF::RED . $this->getLanguage()->translateString('disabled'))]); } - /** - * @return BossBar - */ - public function getBossBar(): BossBar + public function isOutlineEnabled(): bool { - return $this->bossBar; + return $this->outlineEnabled; } - /** - * TODO exception for not a brush - * @param Item $item - * @return Brush - * @throws Exception - */ - public function getBrushFromItem(Item $item): Brush + public function setOutlineEnabled(bool $outlineEnabled): string { - if ((($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))) instanceof CompoundTag) { - $version = $entry->getInt("version", 0); - if ($version !== BrushProperties::VERSION) { - throw new BrushException("Brush can not be restored - version mismatch"); - } - /** @var BrushProperties $properties */ - $properties = json_decode($entry->getString("properties"), false, 512, JSON_THROW_ON_ERROR); - $uuid = UUID::fromString($properties->uuid); - $brush = $this->getBrush($uuid); - if ($brush instanceof Brush) { - return $brush; + $player = $this->getPlayer(); + if (!$player instanceof Player) return TF::RED . "Session has no player"; + $this->outlineEnabled = $outlineEnabled; + if ($outlineEnabled) { + $selection = $this->getLatestSelection(); + if ($selection instanceof Selection && $selection->isValid()) $this->outline = $this->createOrUpdateOutline($selection); + } else { + if ($this->outline instanceof Outline) { + $this->outline->remove(); } - $brush = new Brush($properties); - $this->addBrush($brush); - return $brush; } - throw new BrushException("The item is not a valid brush!"); + $this->sidebar?->handleScoreboard($this); + return Loader::PREFIX . $this->getLanguage()->translateString('tool.outline.setenabled', [($outlineEnabled ? TF::GREEN . $this->getLanguage()->translateString('enabled') : TF::RED . $this->getLanguage()->translateString('disabled'))]); } - /** - * TODO exception for not a brush - * @param UUID $uuid - * @return null|Brush - */ - public function getBrush(UUID $uuid): ?Brush + public function getOutline(): ?Outline { - return $this->brushes[$uuid->toString()] ?? null; + return $this->outline; } - /** - * TODO exception for not a brush - * @param Brush $brush UUID will be set automatically - * @return void - */ - public function addBrush(Brush $brush): void + public function createOrUpdateOutline(Selection $selection): Outline { - $this->brushes[$brush->properties->uuid] = $brush; - $this->sendMessage($this->getLanguage()->translateString('session.brush.added', [$brush->getName()])); + return $this->outline?->setSelection($selection) ?? new Outline($selection, $this->getPlayer()); } - /** - * @param Brush $brush UUID will be set automatically - * @param bool $delete If true, it will be removed from the session brushes - * @return void - */ - public function removeBrush(Brush $brush, bool $delete = false): void + public function getBossBar(): BossBar { - if ($delete) unset($this->brushes[$brush->properties->uuid]); - foreach ($this->getPlayer()->getInventory()->getContents() as $slot => $item) { - if (($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH)) instanceof CompoundTag) { - if ($entry->getString("id") === $brush->properties->uuid) { - $this->getPlayer()->getInventory()->clear($slot); - } - } - } - if ($delete) $this->sendMessage($this->getLanguage()->translateString('session.brush.deleted', [$brush->getName(), $brush->properties->uuid])); - else $this->sendMessage($this->getLanguage()->translateString('session.brush.removed', [$brush->getName(), $brush->properties->uuid])); + return $this->bossBar; } - /** - * TODO exception for not a brush - * @param Brush $brush UUID will be set automatically - * @return void - * @throws ActionNotFoundException - * @throws InvalidArgumentException - * @throws ShapeNotFoundException - * @throws JsonException - * @throws TypeError - */ - public function replaceBrush(Brush $brush): void + public function getBrushes(): BrushCollection { - $this->brushes[$brush->properties->uuid] = $brush; - $new = $brush->toItem(); - foreach ($this->getPlayer()->getInventory()->getContents() as $slot => $item) { - if (($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH)) instanceof CompoundTag) { - if ($entry->getString("id") === $brush->properties->uuid) { - $this->getPlayer()->getInventory()->setItem($slot, $new); - } - } - } + return $this->brushes; } - /** - * @return Brush[] - */ - public function getBrushes(): array + public function getAssets(): AssetCollection { - return $this->brushes; + return $this->assets; + } + + public function getPalettes(): PaletteCollection + { + return $this->palettes; } public function cleanupInventory(): void { foreach ($this->getPlayer()->getInventory()->getContents() as $slot => $item) { - /** @var CompoundTag $entry */ - if (!is_null(($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH)))) { + if (!is_null(($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH)))) { + $this->getPlayer()->getInventory()->clear($slot); + } + if (!is_null(($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE)))) { $this->getPlayer()->getInventory()->clear($slot); } - if (!is_null(($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE)))) { + if (!is_null(($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_ASSET)))) { + $this->getPlayer()->getInventory()->clear($slot); + } + if (!is_null(($item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_PALETTE)))) { $this->getPlayer()->getInventory()->clear($slot); } } @@ -333,6 +242,7 @@ public function __toString() " Debug tool enabled: " . ($this->isDebugToolEnabled() ? "enabled" : "disabled") . " WAILA enabled: " . ($this->isWailaEnabled() ? "enabled" : "disabled") . " Sidebar enabled: " . ($this->sidebarEnabled ? "enabled" : "disabled") . + " Outline enabled: " . ($this->outlineEnabled ? "enabled" : "disabled") . " BossBar: " . $this->getBossBar()->entityId . " Selections: " . count($this->getSelections()) . " Latest: " . $this->getLatestSelectionUUID() . @@ -340,7 +250,9 @@ public function __toString() " Current: " . $this->getCurrentClipboardIndex() . " Undos: " . count($this->undoHistory) . " Redos: " . count($this->redoHistory) . - " Brushes: " . count($this->brushes); + " Brushes: " . count($this->brushes->brushes) . + " Assets: " . count($this->assets->assets) . + " Palettes: " . count($this->palettes->palettes); } public function sendMessage(string $message): void @@ -348,14 +260,7 @@ public function sendMessage(string $message): void $this->player->sendMessage(Loader::PREFIX . $message); } - /** - * Specify data which should be serialized to JSON - * @link http://php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed data which can be serialized by json_encode, - * which is a value of any type other than a resource. - * @since 5.4.0 - */ - public function jsonSerialize() + public function jsonSerialize(): array { return [ "uuid" => $this->getUUID()->toString(), @@ -363,7 +268,9 @@ public function jsonSerialize() "debugToolEnabled" => $this->debugToolEnabled, "wailaEnabled" => $this->wailaEnabled, "sidebarEnabled" => $this->sidebarEnabled, - "brushes" => $this->brushes, + "outlineEnabled" => $this->outlineEnabled, + "brushes" => $this->brushes->brushes, + //todo assets, palettes "latestSelection" => $this->getLatestSelection(), "currentClipboard" => $this->getCurrentClipboard(), "language" => $this->getLanguage()->getLang() @@ -372,6 +279,7 @@ public function jsonSerialize() public function save(): void { + @mkdir(Loader::getInstance()->getDataFolder() . "sessions", 0777, true); file_put_contents(Loader::getInstance()->getDataFolder() . "sessions" . DIRECTORY_SEPARATOR . $this->getPlayer()->getName() . ".json", json_encode($this, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) diff --git a/src/xenialdan/MagicWE2/session/data/Asset.php b/src/xenialdan/MagicWE2/session/data/Asset.php new file mode 100644 index 00000000..7300676b --- /dev/null +++ b/src/xenialdan/MagicWE2/session/data/Asset.php @@ -0,0 +1,292 @@ +filename = $filename; + $this->displayname = pathinfo($filename, PATHINFO_FILENAME); + $this->structure = $value; + $this->locked = $locked; + $this->ownerXuid = $ownerXuid; + $this->shared = $shared; + } + + 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 SingleClipboard) return new Vector3($this->structure->selection->getSizeX(), $this->structure->selection->getSizeY(), $this->structure->selection->getSizeZ()); + throw new Exception("Unknown structure type"); + } + + public function getTotalCount(): int + { + if ($this->structure instanceof Schematic || $this->structure instanceof MCStructure) return $this->getSize()->getFloorX() * $this->getSize()->getFloorY() * $this->getSize()->getFloorZ(); + if ($this->structure instanceof SingleClipboard) return $this->structure->getTotalCount(); + throw new Exception("Unknown structure type"); + } + + 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 SingleClipboard) return $this->structure->position; + throw new Exception("Unknown structure type"); + } + + /** + * @param bool $renew + * @return Item + * @throws InvalidArgumentException + * @throws NbtException + */ + public function toItem(bool $renew = false): Item + { + if ($this->item !== null && !$renew) return $this->item; + $item = ItemFactory::getInstance()->get(ItemIds::SCAFFOLDING); + $item->addEnchantment(new EnchantmentInstance(Loader::$ench)); + try { + ['filename' => $filename, 'displayname' => $displayname, 'type' => $type, 'locked' => $locked, 'owner' => $owner, 'shared' => $shared] = $this->jsonSerialize(); + $item->getNamedTag()->setTag(API::TAG_MAGIC_WE_ASSET, + CompoundTag::create() + ->setString("filename", $filename) + ->setString("displayname", $displayname) + ->setString("type", $type) + ->setByte("locked", $locked ? 1 : 0) + ->setString("owner", $owner) + ->setByte("shared", $shared ? 1 : 0) + ); + $item->setCustomName(Loader::PREFIX_ASSETS . TF::BOLD . TF::LIGHT_PURPLE . $displayname); + $item->setLore($this->generateLore()); + $this->item = $item; + } catch (TypeError $e) { + Loader::getInstance()->getLogger()->logException($e); + } + return $item; + } + + /** + * @return array + * @throws Exception + */ + private function generateLore(): array + { + $return = []; + ['filename' => $filename, 'displayname' => $displayname, 'type' => $type, 'locked' => $locked, 'owner' => $ownerXuid, 'shared' => $shared] = $this->jsonSerialize(); + if (pathinfo($filename, PATHINFO_FILENAME) !== $displayname) + $return[] = TF::RESET . TF::BOLD . TF::GOLD . "Filename: " . TF::RESET . $filename; + $return[] = TF::RESET . TF::BOLD . TF::GOLD . "Type: " . TF::RESET . ucfirst($type); + $return[] = TF::RESET . TF::BOLD . TF::GOLD . "Locked: " . TF::RESET . ($locked ? TF::GREEN . "Yes" : TF::RED . "No"); + $return[] = TF::RESET . TF::BOLD . TF::GOLD . "Origin: " . TF::RESET . API::vecToString($this->getOrigin()); + $return[] = TF::RESET . TF::BOLD . TF::GOLD . "Size: " . TF::RESET . API::vecToString($this->getSize()) . " ({$this->getTotalCount()})"; + $return[] = TF::RESET . TF::BOLD . TF::GOLD . "Owner: " . TF::RESET . $ownerXuid ?? 'none'; + $return[] = TF::RESET . TF::BOLD . TF::GOLD . "Shared: " . TF::RESET . ($shared ? TF::GREEN . "Yes" : TF::RED . "No"); + return $return; + } + + public function toSchematic(): Schematic + { + $structure = $this->structure; + if ($structure instanceof Schematic) return $structure; + if ($structure instanceof MCStructure) { + $schematic = new Schematic(); + $blocks = iterator_to_array($structure->blocks()); + $schematic->setWidth((int)$this->getSize()->getX()); + $schematic->setHeight((int)$this->getSize()->getY()); + $schematic->setLength((int)$this->getSize()->getZ()); + $schematic->setBlockArray($blocks); + return $schematic; + } + if ($structure instanceof SingleClipboard) { + $schematic = new Schematic(); + $blocks = []; + foreach ($structure->iterateEntries($x, $y, $z) as $block) { + $blocks[] = API::setComponents($block->toBlock(), (int)$x, (int)$y, (int)$z);//turn BlockEntry to blocks + } + $schematic->setBlockArray($blocks); + return $schematic; + } + throw new Exception("Unknown structure type"); + } + + public function toMCStructure(): MCStructure + { + $structure = $this->structure; + if ($structure instanceof MCStructure) return $structure; + throw new PluginException("Can't do this yet"); +// if($structure instanceof Schematic) { +// /*$schematic = new (); +// $blocks=[]; +// foreach ($structure->iterateEntries($x, $y, $z) as $blockEntry) { +// $blocks[] = API::setComponents($blockEntry->toBlock(), (int)$x, (int)$y, (int)$z);//turn BlockEntry to blocks +// } +// $size = $structure->getSize(); +// #$aabb = new AxisAlignedBB(0,0,0,$size->getX(),$size->getY(),$size->getZ()); +// $schematic->setBlockArray(new $blocks); +// $schematic->setWidth($size->getX()); +// $schematic->setHeight($size->getY()); +// $schematic->setLength($size->getZ()); +// return $schematic;*/ +// } +// if($structure instanceof SingleClipboard) { +// $schematic = new Schematic(); +// $blocks=[]; +// foreach ($structure->iterateEntries($x, $y, $z) as $blockEntry) { +// $blocks[] = API::setComponents($blockEntry->toBlock(), (int)$x, (int)$y, (int)$z);//turn BlockEntry to blocks +// } +// $size = $structure->getSize(); +// #$aabb = new AxisAlignedBB(0,0,0,$size->getX(),$size->getY(),$size->getZ()); +// $schematic->setBlockArray(new $blocks); +// $schematic->setWidth($size->getX()); +// $schematic->setHeight($size->getY()); +// $schematic->setLength($size->getZ()); +// return $schematic; +// } +// throw new PluginException("Wrong type"); + } + + public function __toString(): string + { + return 'Asset ' . implode(' ', $this->generateLore()); + } + + /** + * @param bool $new true if creating new brush + * @param array $errors + * @return CustomForm + * @throws Exception + * @throws AssumptionFailedError + */ + public function getSettingForm(bool $new = true, array $errors = []): CustomForm + { + //export clipboard + //input Name + //toggle lock + //toggle shared asset + //type dropdown? + try { + // Form + //TODO display errors + $form = (new CustomForm(function (Player $player, $data) /*use ($form, $new)*/ { + var_dump(__LINE__, $data); + [$filename, $this->locked, $shared] = $data; + var_dump($filename, $this->locked ? "true" : "false", $shared ? "true" : "false"); + + try { + $session = SessionHelper::getUserSession($player); + if (!$session instanceof UserSession) { + throw new SessionException(Loader::getInstance()->getLanguage()->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + if ($this->locked) { + $session->sendMessage('error.asset.locked'); + return; + } + + /*//Resend form upon error + if (!empty($error)) { + $player->sendForm($this->getForm($new, $error)); + return; + }*/ + $this->filename = $filename; + $this->displayname = pathinfo($filename, PATHINFO_FILENAME); + $this->shared = $shared; + var_dump($this->filename, $this->displayname); + #var_dump(__LINE__, $this); + #print_r(AssetCollection::getInstance()->assets); + #print_r(AssetCollection::getInstance()->assets->toArray()); + #print_r(AssetCollection::getInstance()->assets->values()->toArray()); + #print_r(AssetCollection::getInstance()->assets->keys()->toArray()); + #print_r(AssetCollection::getInstance()->getAssets()); + if($shared){ + Loader::$assetCollection->assets[$this->filename] = $this;//overwrites + }else{ + $session->getAssets()->assets[$this->filename] = $this;//overwrites + } + $player->sendMessage("Asset stored in " . ($shared ? 'global' : 'private') . ' collection'); + $player->sendMessage((string)$this); + #$player->sendMessage((string)$this->toItem(true)); + } catch (Exception $ex) { + $player->sendMessage($ex->getMessage()); + Loader::getInstance()->getLogger()->logException($ex); + } + })) + ->setTitle("Asset settings") + ->addInput("Filename", "Filename", $this->filename) + ->addToggle("Lock asset", $this->locked) + ->addToggle("Shared asset", $this->shared); + foreach ($this->generateLore() as $value) { + $form->addLabel($value); + } + return $form; + } catch (Exception $e) { + Loader::getInstance()->getLogger()->logException($e); + throw new AssumptionFailedError("Could not create asset setting form: " . $e->getMessage()); + } + } + + public function jsonSerialize(): array + { + return [ + 'filename' => $this->filename, + 'displayname' => $this->displayname, + //'type' => $this->structure instanceof Schematic ? self::TYPE_SCHEMATIC : ($this->structure instanceof MCStructure ? self::TYPE_MCSTRUCTURE : ($this->structure instanceof SingleClipboard ? self::TYPE_CLIPBOARD : '')), + 'type' => $this->structure instanceof Schematic ? self::TYPE_SCHEMATIC : ($this->structure instanceof MCStructure ? self::TYPE_MCSTRUCTURE : self::TYPE_CLIPBOARD), + 'locked' => $this->locked, + 'owner' => $this->ownerXuid ?? 'none', + 'shared' => $this->shared, + ]; + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/session/data/AssetCollection.php b/src/xenialdan/MagicWE2/session/data/AssetCollection.php new file mode 100644 index 00000000..dfdc62d3 --- /dev/null +++ b/src/xenialdan/MagicWE2/session/data/AssetCollection.php @@ -0,0 +1,94 @@ + */ + public array $assets = []; + private Session $session; + + public function __construct(Session $session) + { + $this->session = $session; + $this->initFolders(); + } + + /** + * @return Session + */ + public function getSession(): Session + { + return $this->session; + } + + /** @return Asset[] */ + public function getAll(): array + { + return $this->assets; + } + + /** @return Asset[] */ + public function getUnlockedAssets(): array + { + return array_filter($this->assets, function (Asset $value) { + return !$value->locked; + }); + } + + /** @return Asset[] */ + public function getSharedAssets(): array + { + //TODO remove + return array_filter($this->assets, function (Asset $value) { + return $value->shared; + }); + } + + /** + * @param string|null $xuid If null, returns all player assets, if string, returns a player's assets + * @return Asset[] + */ + public function getPlayerAssets(?string $xuid = null): array + { + //TODO remove + return array_filter($this->assets, function (string $key, Asset $value) use ($xuid) { + if ($xuid === null) return $value->ownerXuid !== null; + else return $value->ownerXuid === $xuid; + }); + } + + private function initFolders(): void + { + //Load mcstructure and schematic files and lock them to prevent editing + $store = $this; + $globStructure = glob(Loader::getInstance()->getDataFolder() . 'assets' . DIRECTORY_SEPARATOR . "*.mcstructure"); + $globSchematic = glob(Loader::getInstance()->getDataFolder() . 'assets' . DIRECTORY_SEPARATOR . "*.schematic"); + if($globStructure && $globSchematic) { + $schematicFiles = array_merge($globStructure, $globSchematic); + if ($schematicFiles !== false) + foreach ($schematicFiles as $file) { + ['basename' => $basename, 'extension' => $extension] = pathinfo($file); + Loader::getInstance()->getLogger()->debug(TF::GOLD . "Loading " . $basename); + try { + if ($extension === 'mcstructure') { + $store->assets[$basename] = new Asset($basename, StructureStore::getInstance()->loadStructure($basename), true, null, true); + } else if ($extension === 'schematic') { + $store->assets[$basename] = new Asset($basename, StructureStore::getInstance()->loadSchematic($basename), true, null, true); + } + } catch (StructureFileException $e) { + Loader::getInstance()->getLogger()->debug($e->getMessage()); + } + } + } + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/session/data/BrushCollection.php b/src/xenialdan/MagicWE2/session/data/BrushCollection.php new file mode 100644 index 00000000..23fb68da --- /dev/null +++ b/src/xenialdan/MagicWE2/session/data/BrushCollection.php @@ -0,0 +1,135 @@ + */ + public array $brushes = []; + private UserSession $session; + + public function __construct(UserSession $session) + { + $this->session = $session; + } + + /** + * @return UserSession + */ + public function getSession(): UserSession + { + return $this->session; + } + + /** @return Brush[] */ + public function getAll(): array + { + return $this->brushes; + } + + /** + * TODO exception for not a brush + * @param Item $item + * @return Brush + * @throws Exception + */ + public function getBrushFromItem(Item $item): Brush + { + if ((($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH))) instanceof CompoundTag) { + $version = $entry->getInt("version", 0); + if ($version !== BrushProperties::VERSION) { + throw new BrushException("Brush can not be restored - version mismatch"); + } + /** @var BrushProperties $properties */ + $properties = json_decode($entry->getString("properties"), false, 512, JSON_THROW_ON_ERROR); + $brush = $this->getBrush($properties->uuid); + if ($brush instanceof Brush) { + return $brush; + } + $brush = new Brush($properties); + $this->addBrush($brush); + return $brush; + } + throw new BrushException("The item is not a valid brush!"); + } + + public function getBrush(string $id): ?Brush + { + return $this->brushes[$id];//TODO allow finding by custom name + } + + /** + * TODO exception for not a brush + * @param Brush $brush UuidInterface will be set automatically + * @return void + */ + public function addBrush(Brush $brush): void + { + $this->brushes[$brush->properties->uuid] = $brush; + $this->getSession()->sendMessage($this->getSession()->getLanguage()->translateString('session.brush.added', [$brush->getName()])); + } + + /** + * @param Brush $brush UuidInterface will be set automatically + * @param bool $delete If true, it will be removed from the session brushes + * @return void + * @throws UnexpectedTagTypeException + */ + public function removeBrush(Brush $brush, bool $delete = false): void + { + if ($delete) unset($this->brushes[$brush->properties->uuid]); + foreach ($this->getSession()->getPlayer()->getInventory()->getContents() as $slot => $item) { + if (($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH)) instanceof CompoundTag) { + if ($entry->getString("id") === $brush->properties->uuid) { + $this->getSession()->getPlayer()->getInventory()->clear($slot); + } + } + } + if ($delete) $this->getSession()->sendMessage($this->getSession()->getLanguage()->translateString('session.brush.deleted', [$brush->getName(), $brush->properties->uuid])); + else $this->getSession()->sendMessage($this->getSession()->getLanguage()->translateString('session.brush.removed', [$brush->getName(), $brush->properties->uuid])); + } + + /** + * TODO exception for not a brush + * @param Brush $brush UuidInterface will be set automatically + * @return void + * @throws ActionNotFoundException + * @throws InvalidArgumentException + * @throws JsonException + * @throws ShapeNotFoundException + * @throws TypeError + * @throws UnexpectedTagTypeException + */ + public function replaceBrush(Brush $brush): void + { + $this->brushes[$brush->properties->uuid] = $brush; + $new = $brush->toItem(); + foreach ($this->getSession()->getPlayer()->getInventory()->getContents() as $slot => $item) { + if (($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_BRUSH)) instanceof CompoundTag) { + if ($entry->getString("id") === $brush->properties->uuid) { + $this->getSession()->getPlayer()->getInventory()->setItem($slot, $new); + } + } + } + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/session/data/Outline.php b/src/xenialdan/MagicWE2/session/data/Outline.php new file mode 100644 index 00000000..2c9d0037 --- /dev/null +++ b/src/xenialdan/MagicWE2/session/data/Outline.php @@ -0,0 +1,102 @@ +selection = $selection; + $this->player = $player; + $this->fakeBlock = BlockFactory::getInstance()->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 + { + return $this->selection; + } + + public function setSelection(Selection $selection): self + { + $this->selection = $selection; + $this->remove(); + $this->updatePosition(); + //TODO change position of fakeTile using reflection + $this->fakeTile->setShowBoundingBox(true)->setFromV3($selection->getPos1())->setToV3($selection->getPos2()); + $this->fakeTile->setDirty(); + $this->send(); + return $this; + } + + public function send(): void + { + $this->player->getNetworkSession()->sendDataPacket(UpdateBlockPacket::create($this->position->x, $this->position->y, $this->position->z, RuntimeBlockMapping::getInstance()->toRuntimeId($this->fakeBlock->getFullId()))); + if ($this->fakeTile instanceof Spawnable) { + $this->player->getNetworkSession()->sendDataPacket(BlockActorDataPacket::create($this->position->x, $this->position->y, $this->position->z, $this->fakeTile->getSerializedSpawnCompound()), true); + } + } + + public function remove(): void + { + $network = $this->player->getNetworkSession(); + $world = $this->player->getWorld(); + $runtime_block_mapping = RuntimeBlockMapping::getInstance(); + $block = $world->getBlockAt($this->position->x, $this->position->y, $this->position->z); + $network->sendDataPacket(UpdateBlockPacket::create($this->position->x, $this->position->y, $this->position->z, $runtime_block_mapping->toRuntimeId($block->getFullId())), true); + + $tile = $world->getTileAt($this->position->x, $this->position->y, $this->position->z); + if ($tile instanceof Spawnable) { + $network->sendDataPacket(BlockActorDataPacket::create($this->position->x, $this->position->y, $this->position->z, $tile->getSerializedSpawnCompound()), true); + } + } + + public function __toString(): string + { + return 'Outline'; + } + + private function updatePosition(): Position + { + $this->position = $this->updateBlockPosition(); + $reflectionc = new ReflectionClass($this->fakeTile); + $reflection = $reflectionc->getProperty('position'); + $reflection->setAccessible(true); + $reflection->setValue($this->fakeTile, $this->position); + return $this->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 + { + return $this->position; + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/session/data/PaletteCollection.php b/src/xenialdan/MagicWE2/session/data/PaletteCollection.php new file mode 100644 index 00000000..f6bd2eed --- /dev/null +++ b/src/xenialdan/MagicWE2/session/data/PaletteCollection.php @@ -0,0 +1,80 @@ + */ + public array $palettes = []; + private UserSession $session; + + public function __construct(UserSession $session) + { + $this->session = $session; + } + + /** + * @return UserSession + */ + public function getSession(): UserSession + { + return $this->session; + } + + /** @return BlockPalette[] */ + public function getAll(): array + { + return $this->palettes; + } + + public function getPalette(string $id): ?BlockPalette + { + return $this->palettes[$id];//TODO allow finding by custom name + } + + public function addPalette(BlockPalette $palette, string $id): void + { + $this->palettes[$id] = $palette; + } + + public function toJson(): string + { + //TODO + $queries = []; + foreach ($this->getAll() as $id => $palette) { + $queries[$id] = $palette->toStringArray(); + } + return json_encode( + $queries + ); + } + + /** + * @param Item $item + * @return BlockPalette + * @throws PaletteException + * @throws UnexpectedTagTypeException + */ + public function getPaletteFromItem(Item $item): BlockPalette + { + if ((($entry = $item->getNamedTag()->getCompoundTag(API::TAG_MAGIC_WE_PALETTE))) !== null) { + $id = $entry->getString("id");//todo check if not found + $palette = $this->getPalette($id); + if ($palette instanceof BlockPalette) { + return $palette; + } + throw new PaletteException("No palette with the id $id could be found!"); + } + throw new PaletteException("The item is not a valid palette!"); + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/session/data/TODO.md b/src/xenialdan/MagicWE2/session/data/TODO.md new file mode 100644 index 00000000..9143b8a6 --- /dev/null +++ b/src/xenialdan/MagicWE2/session/data/TODO.md @@ -0,0 +1,5 @@ +# Session data serialization +- [] Brushes +- [x] Assets (session + global) +- [] Palettes +- [] Clipboards \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/AsyncActionTask.php b/src/xenialdan/MagicWE2/task/AsyncActionTask.php index dfd6de15..425e2b27 100644 --- a/src/xenialdan/MagicWE2/task/AsyncActionTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncActionTask.php @@ -3,19 +3,20 @@ namespace xenialdan\MagicWE2\task; use Exception; -use InvalidArgumentException; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; use pocketmine\world\World; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\SessionException; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; @@ -26,70 +27,70 @@ class AsyncActionTask extends MWEAsyncTask { - /* - * Intention: - * Shape: get blocks from a shape. Shape can contain options - * Filterblocks: filter out blocks that are not needed - * Action: action to run on the remaining blocks, return previous blocks - * Strings: Begin, completion, bossbar, other stuff can be in the action - */ + /* + * Intention: + * Shape: get blocks from a shape. Shape can contain options + * Filterblocks: filter out blocks that are not needed + * Action: action to run on the remaining blocks, return previous blocks + * Strings: Begin, completion, bossbar, other stuff can be in the action + */ /** @var string */ - private $touchedChunks; + private string $touchedChunks; /** @var string */ - private $selection; - /** @var string */ - private $blockFilter; - /** @var string */ - private $newBlocks; + private string $selection; + /** @var BlockPalette */ + private BlockPalette $blockFilter; + /** @var BlockPalette */ + private BlockPalette $newBlocks; /** @var TaskAction */ - private $action; + private TaskAction $action; /** * AsyncActionTask constructor. - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param Selection $selection * @param TaskAction $action * @param string[] $touchedChunks serialized chunks - * @param string $newBlocks - * @param string $blockFilter + * @param BlockPalette $newBlocks + * @param BlockPalette $blockFilter */ - public function __construct(UUID $sessionUUID, Selection $selection, TaskAction $action, array $touchedChunks, string $newBlocks = "", string $blockFilter = "") - { - $this->start = microtime(true); - $this->sessionUUID = $sessionUUID->toString(); - $this->selection = serialize($selection); - $this->action = $action; - $this->touchedChunks = serialize($touchedChunks); - $this->newBlocks = $newBlocks; - $this->blockFilter = $blockFilter; + public function __construct(UuidInterface $sessionUUID, Selection $selection, TaskAction $action, array $touchedChunks, BlockPalette $newBlocks, BlockPalette $blockFilter) + { + $this->start = microtime(true); + $this->sessionUUID = $sessionUUID->toString(); + $this->selection = serialize($selection); + $this->action = $action; + $this->touchedChunks = serialize($touchedChunks); + $this->newBlocks = $newBlocks; + $this->blockFilter = $blockFilter; - try { - $session = SessionHelper::getSessionByUUID($sessionUUID); - if ($session instanceof UserSession) { - $player = $session->getPlayer(); - /** @var Player $player */ - $session->getBossBar()->showTo([$player]); - $session->getBossBar()->setTitle("Running {$action::getName()} action");//TODO better string - } - } catch (SessionException $e) { - Loader::getInstance()->getLogger()->logException($e); - } - } + try { + $session = SessionHelper::getSessionByUUID($sessionUUID); + if ($session instanceof UserSession) { + $player = $session->getPlayer(); + /** @var Player $player */ + $session->getBossBar()->showTo([$player]); + $session->getBossBar()->setTitle("Running {$action::getName()} action");//TODO better string + } + } catch (SessionException $e) { + Loader::getInstance()->getLogger()->logException($e); + } + } - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun(): void + /** + * Actions to execute when run + * + * @return void + * @throws Exception + */ + public function onRun(): void { $this->publishProgress(new Progress(0, "Preparing {$this->action::getName()}")); $touchedChunks = unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/); $touchedChunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, $touchedChunks); $manager = Shape::getChunkManager($touchedChunks); @@ -102,31 +103,26 @@ public function onRun(): void $oldBlocks->selection = $selection;//TODO test. Needed to add this so that //paste works after //cut2 #$oldBlocks = []; $messages = []; - $error = false; - $newBlocks = API::blockParser($this->newBlocks, $messages, $error);//TODO error handling - $blockFilter = API::blockParser($this->blockFilter, $messages, $error);//TODO error handling + //$error = false; /** @var Progress $progress */ - foreach ($this->action->execute($this->sessionUUID, $selection, $manager, $changed, $newBlocks, $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); } $resultChunks = $manager->getChunks(); $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { - return $chunk->isDirty(); + return $chunk->isTerrainDirty(); }); $this->setResult(compact("resultChunks", "oldBlocks", "changed", "messages")); } /** - * @throws InvalidArgumentException * @throws AssumptionFailedError - * @throws Exception - * @throws Exception */ public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); } catch (SessionException $e) { Loader::getInstance()->getLogger()->logException($e); @@ -136,7 +132,7 @@ public function onCompletion(): void /** @var Chunk[] $resultChunks */ $resultChunks = $result["resultChunks"]; $undoChunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 /** @var SingleClipboard $oldBlocks *///TODO make sure changed everywhere $oldBlocks = $result["oldBlocks"]; @@ -163,6 +159,6 @@ public function onCompletion(): void $session->addRevert(new RevertClipboard($selection->worldId, $undoChunks, self::multipleBlocksToData($oldBlocksBlocks))); if ($this->action->addClipboard) $session->addClipboard($oldBlocks); - } - } + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php b/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php index 2e10b805..d882b660 100644 --- a/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncClipboardActionTask.php @@ -3,11 +3,11 @@ namespace xenialdan\MagicWE2\task; use Exception; -use InvalidArgumentException; use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\BlockStatesParser; @@ -22,23 +22,23 @@ class AsyncClipboardActionTask extends MWEAsyncTask { /** @var string */ - private $selection; + private string $selection; /** @var ClipboardAction */ - private $action; + private ClipboardAction $action; /** @var string */ - private $clipboard; + private string $clipboard; private string $rotPath; private string $doorRotPath; /** * AsyncClipboardActionTask constructor. - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param Selection $selection * @param ClipboardAction $action * @param SingleClipboard $clipboard */ - public function __construct(UUID $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(); @@ -58,17 +58,17 @@ public function __construct(UUID $sessionUUID, Selection $selection, ClipboardAc $session->getBossBar()->setTitle("Running {$action::getName()} clipboard action");//TODO better string } } catch (SessionException $e) { - Loader::getInstance()->getLogger()->logException($e); - } - } + Loader::getInstance()->getLogger()->logException($e); + } + } - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun(): void + /** + * Actions to execute when run + * + * @return void + * @throws Exception + */ + public function onRun(): void { $this->publishProgress(new Progress(0, "Preparing {$this->action::getName()}")); BlockStatesParser::$doorRotPath = $this->doorRotPath; @@ -90,14 +90,12 @@ public function onRun(): void } /** - * @throws InvalidArgumentException * @throws AssumptionFailedError - * @throws Exception */ public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); } catch (SessionException $e) { Loader::getInstance()->getLogger()->logException($e); diff --git a/src/xenialdan/MagicWE2/task/AsyncCopyTask.php b/src/xenialdan/MagicWE2/task/AsyncCopyTask.php index 281d3326..f15762da 100644 --- a/src/xenialdan/MagicWE2/task/AsyncCopyTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncCopyTask.php @@ -1,19 +1,20 @@ -start = microtime(true); - $this->chunks = serialize($chunks); - $this->sessionUUID = $sessionUUID->toString(); - $this->selection = serialize($selection); - $this->offset = $offset->asVector3()->floor(); - $this->flags = $flags; - } + public function __construct(UuidInterface $sessionUUID, Selection $selection, Vector3 $offset, array $chunks, int $flags) + { + $this->start = microtime(true); + $this->chunks = serialize($chunks); + $this->sessionUUID = $sessionUUID->toString(); + $this->selection = serialize($selection); + $this->offset = $offset->asVector3()->floor(); + $this->flags = $flags; + } - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun(): void + /** + * Actions to execute when run + * + * @return void + * @throws Exception + */ + public function onRun(): void { $this->publishProgress([0, "Start"]); $chunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, unserialize($this->chunks/*, ['allowed_classes' => false]*/));//TODO test pm4 /** @var Selection $selection */ $selection = unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 @@ -75,17 +76,17 @@ public function onRun(): void $totalCount = $selection->getShape()->getTotalCount(); $copied = $this->copyBlocks($selection, $manager, $clipboard); #$clipboard->setShape($selection->getShape()); - #$clipboard->chunks = $manager->getChunks(); - $this->setResult(compact("clipboard", "copied", "totalCount")); - } + #$clipboard->chunks = $manager->getChunks(); + $this->setResult(compact("clipboard", "copied", "totalCount")); + } - /** - * @param Selection $selection - * @param AsyncChunkManager $manager - * @param SingleClipboard $clipboard - * @return int - * @throws Exception - */ + /** + * @param Selection $selection + * @param AsyncChunkManager $manager + * @param SingleClipboard $clipboard + * @return int + * @throws Exception + */ private function copyBlocks(Selection $selection, AsyncChunkManager $manager, SingleClipboard $clipboard): int { $blockCount = $selection->getShape()->getTotalCount(); @@ -94,9 +95,10 @@ private function copyBlocks(Selection $selection, AsyncChunkManager $manager, Si $this->publishProgress([0, "Running, copied $i blocks out of $blockCount"]); $min = $selection->getShape()->getMinVec3(); /** @var Block $block */ - foreach ($selection->getShape()->getBlocks($manager, [], $this->flags) as $block) { + foreach ($selection->getShape()->getBlocks($manager, BlockPalette::CREATE(), $this->flags) as $block) { #var_dump("copy chunk X: " . ($block->getX() >> 4) . " Y: " . ($block->getY() >> 4)); - $newv3 = $block->getPos()->subtractVector($min)->floor(); + $newv3 = $block->getPosition()->subtractVector($min)->floor(); + /** @noinspection PhpInternalEntityUsedInspection */ $clipboard->addEntry($newv3->getFloorX(), $newv3->getFloorY(), $newv3->getFloorZ(), new BlockEntry($block->getFullId()));//TODO test tiles #var_dump("copied selection block", $block); $i++; @@ -106,13 +108,13 @@ private function copyBlocks(Selection $selection, AsyncChunkManager $manager, Si $lastprogress = $progress; } } - return $i; - } + return $i; + } - public function onCompletion(): void + public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); $result = $this->getResult(); $copied = $result["copied"]; @@ -121,11 +123,7 @@ public function onCompletion(): void $totalCount = $result["totalCount"]; $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.copy.success', [$this->generateTookString(), $copied, $totalCount])); $session->addClipboard($clipboard); - } catch (SessionException $e) { - Loader::getInstance()->getLogger()->logException($e); - } catch (InvalidArgumentException $e) { - Loader::getInstance()->getLogger()->logException($e); - } catch (AssumptionFailedError $e) { + } catch (SessionException | AssumptionFailedError $e) { Loader::getInstance()->getLogger()->logException($e); } } diff --git a/src/xenialdan/MagicWE2/task/AsyncCountTask.php b/src/xenialdan/MagicWE2/task/AsyncCountTask.php index 3082410e..7b089278 100644 --- a/src/xenialdan/MagicWE2/task/AsyncCountTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncCountTask.php @@ -3,15 +3,16 @@ namespace xenialdan\MagicWE2\task; use Exception; -use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\format\io\FastChunkSerializer; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\Selection; @@ -21,85 +22,83 @@ class AsyncCountTask extends MWEAsyncTask { /** @var string */ - private $touchedChunks; + private string $touchedChunks; /** @var string */ - private $selection; + private string $selection; /** @var int */ - private $flags; - /** @var string */ - private $newBlocks; + private int $flags; + /** @var BlockPalette */ + private BlockPalette $filterblocks; /** * AsyncCountTask constructor. * @param Selection $selection - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param string[] $touchedChunks serialized chunks - * @param Block[] $newBlocks + * @param BlockPalette $filterblocks * @param int $flags - * @throws Exception - */ - public function __construct(UUID $sessionUUID, Selection $selection, array $touchedChunks, array $newBlocks, int $flags) - { - $this->start = microtime(true); - $this->touchedChunks = serialize($touchedChunks); - $this->sessionUUID = $sessionUUID->toString(); - $this->selection = serialize($selection); - $this->newBlocks = serialize($newBlocks); - $this->flags = $flags; - } + * @throws Exception + */ + public function __construct(UuidInterface $sessionUUID, Selection $selection, array $touchedChunks, BlockPalette $filterblocks, int $flags) + { + $this->start = microtime(true); + $this->touchedChunks = serialize($touchedChunks); + $this->sessionUUID = $sessionUUID->toString(); + $this->selection = serialize($selection); + $this->filterblocks = $filterblocks; + $this->flags = $flags; + } - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun(): void + /** + * Actions to execute when run + * + * @return void + * @throws Exception + */ + public function onRun(): void { $this->publishProgress([0, "Start"]); $chunks = unserialize($this->touchedChunks/*, ['allowed_classes' => [false]]*/);//TODO test pm4 foreach ($chunks as $hash => $data) { - $chunks[$hash] = FastChunkSerializer::deserialize($data); + $chunks[$hash] = FastChunkSerializer::deserializeTerrain($data); } /** @var Selection $selection */ $selection = unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 $manager = Shape::getChunkManager($chunks); unset($chunks); - /** @var Block[] $newBlocks */ - $newBlocks = unserialize($this->newBlocks/*, ['allowed_classes' => [Block::class]]*/);//TODO test pm4 $totalCount = $selection->getShape()->getTotalCount(); - $counts = $this->countBlocks($selection, $manager, $newBlocks); + $counts = $this->countBlocks($selection, $manager, $this->filterblocks); $this->setResult(compact("counts", "totalCount")); } - /** - * @param Selection $selection - * @param AsyncChunkManager $manager - * @param Block[] $newBlocks - * @return array - * @throws Exception - */ - private function countBlocks(Selection $selection, AsyncChunkManager $manager, array $newBlocks): array - { - $blockCount = $selection->getShape()->getTotalCount(); - $changed = 0; - $this->publishProgress([0, "Running, changed $changed blocks out of $blockCount"]); - $lastchunkx = $lastchunkz = null; - $lastprogress = 0; - $counts = []; - /** @var Block $block */ - foreach ($selection->getShape()->getBlocks($manager, $newBlocks, $this->flags) as $block) { - if (is_null($lastchunkx) || ($block->getPos()->x >> 4 !== $lastchunkx && $block->getPos()->z >> 4 !== $lastchunkz)) { - $lastchunkx = $block->getPos()->x >> 4; - $lastchunkz = $block->getPos()->z >> 4; - if (is_null($manager->getChunk($block->getPos()->x >> 4, $block->getPos()->z >> 4))) { + /** + * @param Selection $selection + * @param AsyncChunkManager $manager + * @param BlockPalette $filterblocks + * @return array + * @throws Exception + */ + private function countBlocks(Selection $selection, AsyncChunkManager $manager, BlockPalette $filterblocks): array + { + $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, $this->flags) 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->getBlockArrayAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()); - $tostring = (BlockFactory::getInstance()->get($block1[0], $block1[1]))->getName() . " " . $block1[0] . ":" . $block1[1]; + $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++; @@ -113,13 +112,12 @@ private function countBlocks(Selection $selection, AsyncChunkManager $manager, a } /** - * @throws InvalidArgumentException * @throws AssumptionFailedError */ public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); $result = $this->getResult(); $counts = $result["counts"]; @@ -134,7 +132,7 @@ public function onCompletion(): void $session->sendMessage(TF::AQUA . $count . "x | " . round($count / $totalCount * 100) . "% | " . $block); } } catch (SessionException $e) { - Loader::getInstance()->getLogger()->logException($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 1015afde..8249edee 100644 --- a/src/xenialdan/MagicWE2/task/AsyncFillTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncFillTask.php @@ -5,13 +5,16 @@ use Exception; use Generator; use InvalidArgumentException; +use MultipleIterator; use pocketmine\block\Block; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; +use pocketmine\world\Position; use pocketmine\world\World; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\exception\SessionException; @@ -26,30 +29,37 @@ class AsyncFillTask extends MWEAsyncTask { /** @var string */ - private $touchedChunks; + private string $touchedChunks; /** @var string */ - private $selection; + private string $selection; /** @var int */ - private $flags; - /** @var string */ - private $newBlocks; + private int $flags; + ///** @var string */ + //private $newBlocks; + /** @var BlockPalette */ + private BlockPalette $newBlocks; /** * AsyncFillTask constructor. - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param Selection $selection * @param string[] $touchedChunks serialized chunks - * @param Block[] $newBlocks + * @param BlockPalette $newBlocks * @param int $flags * @throws Exception */ - public function __construct(UUID $sessionUUID, Selection $selection, array $touchedChunks, array $newBlocks, int $flags) + public function __construct(UuidInterface $sessionUUID, Selection $selection, array $touchedChunks, BlockPalette $newBlocks, int $flags) { $this->start = microtime(true); $this->sessionUUID = $sessionUUID->toString(); - $this->selection = igbinary_serialize($selection); - $this->touchedChunks = igbinary_serialize($touchedChunks); - $this->newBlocks = BlockPalette::encode($newBlocks); + $s1 = igbinary_serialize($selection); + if ($s1 === null) throw new Exception("Couldn't serialize selection"); + $s2 = igbinary_serialize($touchedChunks); + if ($s2 === null) throw new Exception("Couldn't serialize touched chunks"); + $this->selection = $s1; + $this->touchedChunks = $s2; + //$this->newBlocks = BlockPalette::encode($newBlocks); + $this->newBlocks = $newBlocks;//TODO check if serializes var_dump($this->newBlocks); $this->flags = $flags; } @@ -65,7 +75,7 @@ public function onRun(): void $this->publishProgress([0, "Start"]); $touchedChunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, igbinary_unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 $manager = Shape::getChunkManager($touchedChunks); @@ -74,13 +84,14 @@ public function onRun(): void /** @var Selection $selection */ $selection = igbinary_unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 - /** @var Block[] $newBlocks */ - $newBlocks = BlockPalette::decode($this->newBlocks);//TODO test pm4 - $oldBlocks = iterator_to_array($this->execute($selection, $manager, $newBlocks, $changed)); + ///** @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, $manager, $this->newBlocks, $changed)); $resultChunks = $manager->getChunks(); $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { - return $chunk->isDirty(); + return $chunk->isTerrainDirty(); }); #$this->setResult(compact("resultChunks", "oldBlocks", "changed")); $this->setResult([ @@ -93,13 +104,13 @@ public function onRun(): void /** * @param Selection $selection * @param AsyncChunkManager $manager - * @param Block[] $newBlocks + * @param BlockPalette $newBlocks * @param null|int $changed - * @return Generator|array[] - * @phpstan-return Generator - * @throws Exception + * @return Generator + * @throws InvalidArgumentException + * @phpstan-return Generator */ - private function execute(Selection $selection, AsyncChunkManager $manager, array $newBlocks, ?int &$changed): Generator + private function execute(Selection $selection, AsyncChunkManager $manager, BlockPalette $newBlocks, ?int &$changed): Generator { $blockCount = $selection->getShape()->getTotalCount(); $lastchunkx = $lastchunkz = null; @@ -107,27 +118,33 @@ private function execute(Selection $selection, AsyncChunkManager $manager, array $i = 0; $changed = 0; $this->publishProgress([0, "Running, changed $changed blocks out of $blockCount"]); - /** @var Block $block */ - foreach ($selection->getShape()->getBlocks($manager, [], $this->flags) as $block) { + $iterators = new MultipleIterator(); + $iterators->attachIterator($selection->getShape()->getBlocks($manager, BlockPalette::CREATE(), $this->flags)); + $iterators->attachIterator($newBlocks->blocks($blockCount)); + foreach ($iterators as [$block, $new]) { + /** + * @var Block $block + * @var Block $new + */ /*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 }*/ - if (is_null($lastchunkx) || ($block->getPos()->x >> 4 !== $lastchunkx && $block->getPos()->z >> 4 !== $lastchunkz)) { - $lastchunkx = $block->getPos()->x >> 4; - $lastchunkz = $block->getPos()->z >> 4; - if (is_null($manager->getChunk($block->getPos()->x >> 4, $block->getPos()->z >> 4))) { + 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; } } - $new = clone $newBlocks[array_rand($newBlocks)]; - if ($new->getId() === $block->getId() && $new->getMeta() === $block->getMeta()) continue;//skip same blocks - #yield self::undoBlockHackToArray($manager->getBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()),$block->getPos()); - yield self::singleBlockToData(API::setComponents($manager->getBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()), (int)$block->getPos()->x, (int)$block->getPos()->y, (int)$block->getPos()->z)); + if ($new->getId() === $block->getId() && $new->getMeta() === $block->getMeta()) continue;//skip same blocks//TODO better method + #yield self::undoBlockHackToArray($manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()),$block->getPosition()); + yield self::singleBlockToData(API::setComponents($manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()), (int)$block->getPosition()->x, (int)$block->getPosition()->y, (int)$block->getPosition()->z)); #yield $block;//backup - $manager->setBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), $new); - if ($manager->getBlockArrayAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()) !== [$block->getId(), $block->getMeta()]) { + $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()) { $changed++; } /// @@ -141,15 +158,12 @@ private function execute(Selection $selection, AsyncChunkManager $manager, array } /** - * @throws InvalidArgumentException * @throws AssumptionFailedError - * @throws Exception - * @throws Exception */ public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); } catch (SessionException $e) { Loader::getInstance()->getLogger()->logException($e); @@ -159,7 +173,7 @@ public function onCompletion(): void /** @var Chunk[] $resultChunks */ $resultChunks = $result["resultChunks"]; $undoChunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, igbinary_unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4) #$oldBlocks = igbinary_unserialize($result["oldBlocks"]); $oldBlocks = $result["oldBlocks"];//this is already a data map @@ -170,9 +184,9 @@ public function onCompletion(): void // */ // foreach ($oldBlocks as [$fullId, $pos]) { // $b = BlockFactory::getInstance()->fromFullBlock($fullId); -// $b->getPos()->x = $pos->x; -// $b->getPos()->y = $pos->y; -// $b->getPos()->z = $pos->z; +// $b->getPosition()->x = $pos->x; +// $b->getPosition()->y = $pos->y; +// $b->getPosition()->z = $pos->z; // $oldBlocks2[] = $b; // } // var_dump($oldBlocks2); diff --git a/src/xenialdan/MagicWE2/task/AsyncPasteAssetTask.php b/src/xenialdan/MagicWE2/task/AsyncPasteAssetTask.php new file mode 100644 index 00000000..66d31887 --- /dev/null +++ b/src/xenialdan/MagicWE2/task/AsyncPasteAssetTask.php @@ -0,0 +1,232 @@ +start = microtime(true); + $this->pasteVector = $selection->getShape()->getPasteVector();#->addVector($asset->getOrigin())->floor(); + $this->target = $target; + #var_dump("paste", $selection->getShape()->getPasteVector(), "cb position", $clipboard->position, "offset", $this->offset, $clipboard); + $this->sessionUUID = $sessionUUID->toString(); + $this->selection = $selection; + $this->touchedChunks = serialize($touchedChunks); + $this->asset = serialize($asset); + } + + /** + * Actions to execute when run + * + * @return void + * @throws InvalidArgumentException + * @throws OutOfBoundsException + */ + public function onRun(): void + { + $this->publishProgress([0, "Start"]); + + $touchedChunks = array_map(static function ($chunk) {//todo add hash as key + return FastChunkSerializer::deserializeTerrain($chunk); + }, unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 + + $manager = Shape::getChunkManager($touchedChunks); + unset($touchedChunks); + + //$selection = unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 + //$selection = $this->selection; + + /** @var Asset $asset */ + $asset = unserialize($this->asset/*, ['allowed_classes' => [SingleClipboard::class]]*/);//TODO test pm4 + $oldBlocks = iterator_to_array($this->execute($manager, $asset, $changed)); + + $resultChunks = $manager->getChunks(); + $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { + return $chunk->isTerrainDirty(); + }); + $this->setResult(compact("resultChunks", "oldBlocks", "changed")); + } + + /** + * @param AsyncChunkManager $manager + * @param Asset $asset + * @param null|int $changed + * @return Generator + * @throws InvalidArgumentException + * @throws OutOfBoundsException + * @phpstan-return Generator + */ + private function execute(AsyncChunkManager $manager, Asset $asset, ?int &$changed): Generator + { + $blockCount = $asset->getTotalCount(); + $lastchunkx = $lastchunkz = $x = $y = $z = null; + $lastprogress = 0; + $i = 0; + $changed = 0; + $this->publishProgress([0, "Running, changed $changed blocks out of $blockCount"]); + $structure = $asset->structure; + if ($structure instanceof MCStructure) { + /** @var Block $block */ + foreach ($structure->blocks() as $block) {// [0,0,0 -> sizex,sizey,sizez] + #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()]; + #var_dump($block->getPosition()->asVector3()); + if (($x >> 4 !== $lastchunkx && $z >> 4 !== $lastchunkz) || is_null($lastchunkx)) { + $lastchunkx = $x >> 4; + $lastchunkz = $z >> 4; + if (is_null($manager->getChunk($x >> 4, $z >> 4))) { + print PHP_EOL . "Paste chunk not found in async paste manager: " . ($x >> 4) . ":" . ($z >> 4) . PHP_EOL; + continue; + } + } + $new = $block; + yield self::singleBlockToData(API::setComponents($manager->getBlockAt((int)$x, (int)$y, (int)$z), (int)$x, (int)$y, (int)$z)); + $manager->setBlockAt((int)$x, (int)$y, (int)$z, $new); + $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; + } + } + } else if ($structure instanceof SingleClipboard) { + /** @var BlockEntry $entry */ + foreach ($structure->iterateEntries($x, $y, $z) as $entry) { + $v = new Vector3($x, $y, $z); + $pos = $v->addVector($this->target)->subtract($asset->getSize()->getX() / 2, 0, $asset->getSize()->getZ() / 2); + [$v->x, $v->y, $v->z] = /*[$x, $y, $z] =*/ + [$pos->getX(), $pos->getY(), $pos->getZ()]; + if (($v->x >> 4 !== $lastchunkx && $v->z >> 4 !== $lastchunkz) || is_null($lastchunkx)) { + $lastchunkx = $v->x >> 4; + $lastchunkz = $v->z >> 4; + if (is_null($manager->getChunk($v->x >> 4, $v->z >> 4))) { + print PHP_EOL . "Paste chunk not found in async paste manager: " . ($v->x >> 4) . ":" . ($v->z >> 4) . PHP_EOL; + continue; + } + } + $new = $entry->toBlock(); + yield self::singleBlockToData(API::setComponents($manager->getBlockAt((int)$v->x, (int)$v->y, (int)$v->z), (int)$v->x, (int)$v->y, (int)$v->z)); + $manager->setBlockAt((int)$v->x, (int)$v->y, (int)$v->z, $new); + $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; + } + } + } else if ($structure instanceof Schematic) { + foreach ($structure->blocks() as $block) { + $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()]; + if (($x >> 4 !== $lastchunkx && $z >> 4 !== $lastchunkz) || is_null($lastchunkx)) { + $lastchunkx = $x >> 4; + $lastchunkz = $z >> 4; + if (is_null($manager->getChunk($x >> 4, $z >> 4))) { + print PHP_EOL . "Paste chunk not found in async paste manager: " . ($x >> 4) . ":" . ($z >> 4) . PHP_EOL; + continue; + } + } + $new = $block; + yield self::singleBlockToData(API::setComponents($manager->getBlockAt($x, $y, $z), (int)$x, (int)$y, (int)$z)); + $manager->setBlockAt($x, $y, $z, $new); + $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; + } + } + } + } + + /** + * @throws AssumptionFailedError + */ + public function onCompletion(): 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(static function ($chunk) { + return FastChunkSerializer::deserializeTerrain($chunk); + }, unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 + $oldBlocks = $result["oldBlocks"];//already data array + $changed = $result["changed"]; + //$selection = unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 + $selection = $this->selection; + $totalCount = $selection->getShape()->getTotalCount(); + $world = $this->selection->getWorld(); + foreach ($resultChunks as $hash => $chunk) { + World::getXZ($hash, $x, $z); + $world->setChunk($x, $z, $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->worldId, $undoChunks, $oldBlocks)); + } + } +} \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/AsyncPasteTask.php b/src/xenialdan/MagicWE2/task/AsyncPasteTask.php index 04ee1638..d4af52ef 100644 --- a/src/xenialdan/MagicWE2/task/AsyncPasteTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncPasteTask.php @@ -8,10 +8,12 @@ use pocketmine\math\Vector3; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; +use pocketmine\world\Position; use pocketmine\world\World; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\clipboard\SingleClipboard; @@ -27,23 +29,23 @@ class AsyncPasteTask extends MWEAsyncTask { /** @var string */ - private $touchedChunks; + private string $touchedChunks; /** @var string */ - private $selection; + private string $selection; /** @var string */ - private $clipboard; + private string $clipboard; /** @var Vector3 */ - private $offset; + private Vector3 $offset; /** * AsyncPasteTask constructor. - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param Selection $selection * @param string[] $touchedChunks serialized chunks * @param SingleClipboard $clipboard * @throws Exception */ - public function __construct(UUID $sessionUUID, Selection $selection, array $touchedChunks, SingleClipboard $clipboard) + public function __construct(UuidInterface $sessionUUID, Selection $selection, array $touchedChunks, SingleClipboard $clipboard) { $this->start = microtime(true); $this->offset = $selection->getShape()->getPasteVector()->addVector($clipboard->position)->floor(); @@ -65,7 +67,7 @@ public function onRun(): void $this->publishProgress([0, "Start"]); $touchedChunks = array_map(static function ($chunk) {//todo add hash as key - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 $manager = Shape::getChunkManager($touchedChunks); @@ -80,7 +82,7 @@ public function onRun(): void $resultChunks = $manager->getChunks(); $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { - return $chunk->isDirty(); + return $chunk->isTerrainDirty(); }); $this->setResult(compact("resultChunks", "oldBlocks", "changed")); } @@ -90,9 +92,9 @@ public function onRun(): void * @param AsyncChunkManager $manager * @param SingleClipboard $clipboard * @param null|int $changed - * @return Generator|array[] - * @phpstan-return Generator + * @return Generator * @throws InvalidArgumentException + * @phpstan-return Generator */ private function execute(Selection $selection, AsyncChunkManager $manager, SingleClipboard $clipboard, ?int &$changed): Generator { @@ -127,9 +129,7 @@ private function execute(Selection $selection, AsyncChunkManager $manager, Singl #var_dump("old", $old, "new", $new); yield self::singleBlockToData(API::setComponents($manager->getBlockAt($x, $y, $z), (int)$x, (int)$y, (int)$z)); $manager->setBlockAt($x, $y, $z, $new); - if ($manager->getBlockArrayAt($x, $y, $z) !== [$manager->getBlockAt($x, $y, $z)->getId(), $manager->getBlockAt($x, $y, $z)->getMeta()]) {//TODO remove? Just useless waste imo - $changed++; - } + $changed++; /// $i++; $progress = floor($i / $blockCount * 100); @@ -142,14 +142,11 @@ private function execute(Selection $selection, AsyncChunkManager $manager, Singl /** * @throws AssumptionFailedError - * @throws InvalidArgumentException - * @throws Exception - * @throws Exception */ public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); } catch (SessionException $e) { Loader::getInstance()->getLogger()->logException($e); @@ -159,7 +156,7 @@ public function onCompletion(): void /** @var Chunk[] $resultChunks */ $resultChunks = $result["resultChunks"]; $undoChunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 $oldBlocks = $result["oldBlocks"];//already data array $changed = $result["changed"]; diff --git a/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php b/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php index a99b32a2..0d64d755 100644 --- a/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncReplaceTask.php @@ -5,17 +5,21 @@ use Exception; use Generator; use InvalidArgumentException; +use MultipleIterator; use pocketmine\block\Block; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\FastChunkSerializer; +use pocketmine\world\Position; use pocketmine\world\World; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\Selection; @@ -25,49 +29,49 @@ class AsyncReplaceTask extends MWEAsyncTask { /** @var string */ - private $touchedChunks; + private string $touchedChunks; /** @var string */ - private $selection; + private string $selection; /** @var int */ - private $flags; - /** @var string */ - private $replaceBlocks; - /** @var string */ - private $newBlocks; + private int $flags; + /** @var BlockPalette */ + private BlockPalette $replaceBlocks; + /** @var BlockPalette */ + private BlockPalette $newBlocks; /** * AsyncReplaceTask constructor. * @param Selection $selection - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param string[] $touchedChunks serialized chunks - * @param Block[] $replaceBlocks - * @param Block[] $newBlocks - * @param int $flags - * @throws Exception - */ - public function __construct(UUID $sessionUUID, Selection $selection, array $touchedChunks, array $replaceBlocks, array $newBlocks, int $flags) - { - $this->start = microtime(true); - $this->sessionUUID = $sessionUUID->toString(); - $this->selection = serialize($selection); - $this->touchedChunks = serialize($touchedChunks); - $this->replaceBlocks = serialize($replaceBlocks); - $this->newBlocks = serialize($newBlocks); - $this->flags = $flags; - } + * @param BlockPalette $replaceBlocks + * @param BlockPalette $newBlocks + * @param int $flags + * @throws Exception + */ + public function __construct(UuidInterface $sessionUUID, Selection $selection, array $touchedChunks, BlockPalette $replaceBlocks, BlockPalette $newBlocks, int $flags) + { + $this->start = microtime(true); + $this->sessionUUID = $sessionUUID->toString(); + $this->selection = serialize($selection); + $this->touchedChunks = serialize($touchedChunks); + $this->replaceBlocks = $replaceBlocks; + $this->newBlocks = $newBlocks; + $this->flags = $flags; + } - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun(): void + /** + * Actions to execute when run + * + * @return void + * @throws Exception + */ + public function onRun(): void { $this->publishProgress([0, "Start"]); $touchedChunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 $manager = Shape::getChunkManager($touchedChunks); @@ -76,53 +80,54 @@ public function onRun(): void /** @var Selection $selection */ $selection = unserialize($this->selection/*, ['allowed_classes' => [Selection::class]]*/);//TODO test pm4 - /** @var Block[] $replaceBlocks */ - $replaceBlocks = unserialize($this->replaceBlocks/*, ['allowed_classes' => [Block::class]]*/);//TODO test pm4 - /** @var Block[] $newBlocks */ - $newBlocks = unserialize($this->newBlocks/*, ['allowed_classes' => [Block::class]]*/);//TODO test pm4 - - $oldBlocks = iterator_to_array($this->execute($selection, $manager, $replaceBlocks, $newBlocks, $changed)); + $oldBlocks = iterator_to_array($this->execute($selection, $manager, $this->replaceBlocks, $this->newBlocks, $changed)); $resultChunks = $manager->getChunks(); $resultChunks = array_filter($resultChunks, static function (Chunk $chunk) { - return $chunk->isDirty(); + return $chunk->isTerrainDirty(); }); $this->setResult(compact("resultChunks", "oldBlocks", "changed")); } - /** + /** * @param Selection $selection * @param AsyncChunkManager $manager - * @param array $replaceBlocks - * @param Block[] $newBlocks + * @param BlockPalette $replaceBlocks + * @param BlockPalette $newBlocks * @param null|int $changed - * @return Generator|array[] - * @phpstan-return Generator - * @throws Exception + * @return Generator + * @throws InvalidArgumentException + * @phpstan-return Generator */ - private function execute(Selection $selection, AsyncChunkManager $manager, array $replaceBlocks, array $newBlocks, ?int &$changed): Generator - { - $blockCount = $selection->getShape()->getTotalCount(); - $lastchunkx = $lastchunkz = null; - $lastprogress = 0; - $i = 0; - $changed = 0; - $this->publishProgress([0, "Running, changed $changed blocks out of $blockCount"]); - /** @var Block $block */ - foreach ($selection->getShape()->getBlocks($manager, $replaceBlocks, $this->flags) as $block) { - if (is_null($lastchunkx) || ($block->getPos()->x >> 4 !== $lastchunkx && $block->getPos()->z >> 4 !== $lastchunkz)) { - $lastchunkx = $block->getPos()->x >> 4; - $lastchunkz = $block->getPos()->z >> 4; - if (is_null($manager->getChunk($block->getPos()->x >> 4, $block->getPos()->z >> 4))) { + private function execute(Selection $selection, AsyncChunkManager $manager, BlockPalette $replaceBlocks, BlockPalette $newBlocks, ?int &$changed): Generator + { + $blockCount = $selection->getShape()->getTotalCount(); + $lastchunkx = $lastchunkz = null; + $lastprogress = 0; + $i = 0; + $changed = 0; + $this->publishProgress([0, "Running, changed $changed blocks out of $blockCount"]); + $iterators = new MultipleIterator(); + $iterators->attachIterator($selection->getShape()->getBlocks($manager, $replaceBlocks, $this->flags)); + $iterators->attachIterator($newBlocks->blocks($blockCount)); + foreach ($iterators as [$block, $new]) { + /** + * @var Block $block + * @var Block $new + */ + 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; } } - $new = clone $newBlocks[array_rand($newBlocks)]; if ($new->getId() === $block->getId() && $new->getMeta() === $block->getMeta()) continue;//skip same blocks - yield self::singleBlockToData(API::setComponents($manager->getBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()), (int)$block->getPos()->x, (int)$block->getPos()->y, (int)$block->getPos()->z)); - $manager->setBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), $new); - if ($manager->getBlockArrayAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()) !== [$block->getId(), $block->getMeta()]) { + yield self::singleBlockToData(API::setComponents($manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ()), (int)$block->getPosition()->x, (int)$block->getPosition()->y, (int)$block->getPosition()->z)); + $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()) { $changed++; } /// @@ -136,15 +141,12 @@ private function execute(Selection $selection, AsyncChunkManager $manager, array } /** - * @throws InvalidArgumentException * @throws AssumptionFailedError - * @throws Exception - * @throws Exception */ public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); } catch (SessionException $e) { Loader::getInstance()->getLogger()->logException($e); @@ -154,7 +156,7 @@ public function onCompletion(): void /** @var Chunk[] $resultChunks */ $resultChunks = $result["resultChunks"]; $undoChunks = array_map(static function ($chunk) { - return FastChunkSerializer::deserialize($chunk); + return FastChunkSerializer::deserializeTerrain($chunk); }, unserialize($this->touchedChunks/*, ['allowed_classes' => false]*/));//TODO test pm4 $oldBlocks = $result["oldBlocks"];//this is already as data $changed = $result["changed"]; diff --git a/src/xenialdan/MagicWE2/task/AsyncRevertTask.php b/src/xenialdan/MagicWE2/task/AsyncRevertTask.php index 399083ea..1d80c933 100644 --- a/src/xenialdan/MagicWE2/task/AsyncRevertTask.php +++ b/src/xenialdan/MagicWE2/task/AsyncRevertTask.php @@ -7,8 +7,10 @@ use InvalidArgumentException; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; +use pocketmine\world\Position; use pocketmine\world\World; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; use xenialdan\MagicWE2\clipboard\RevertClipboard; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\AsyncChunkManager; @@ -23,31 +25,31 @@ class AsyncRevertTask extends MWEAsyncTask public const TYPE_REDO = 1; /** @var string */ - private $clipboard; + private string $clipboard; /** @var int */ - private $type; + private int $type; /** * AsyncRevertTask constructor. - * @param UUID $sessionUUID + * @param UuidInterface $sessionUUID * @param RevertClipboard $clipboard * @param int $type The type of clipboard pasting. */ - public function __construct(UUID $sessionUUID, RevertClipboard $clipboard, $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 = serialize($clipboard); - $this->type = $type; - } + $this->start = microtime(true); + $this->clipboard = serialize($clipboard); + $this->type = $type; + } - /** - * Actions to execute when run - * - * @return void - * @throws Exception - */ - public function onRun(): void + /** + * Actions to execute when run + * + * @return void + * @throws Exception + */ + public function onRun(): void { $this->publishProgress([0, "Start"]); /** @var RevertClipboard $clipboard */ @@ -66,9 +68,9 @@ public function onRun(): void /** * @param AsyncChunkManager $manager * @param RevertClipboard $clipboard - * @return Generator|array[] - * @phpstan-return Generator + * @return Generator * @throws InvalidArgumentException + * @phpstan-return Generator */ private function undoChunks(AsyncChunkManager $manager, RevertClipboard $clipboard): Generator { @@ -79,7 +81,7 @@ private function undoChunks(AsyncChunkManager $manager, RevertClipboard $clipboa foreach ($clipboard->blocksAfter as $block) { yield $block; $block = self::singleDataToBlock($block);//turn data into real block - $manager->setBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), $block); + $manager->setBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ(), $block); $changed++; $this->publishProgress([$changed / $count, "Reverted $changed blocks out of $count"]); } @@ -88,9 +90,9 @@ private function undoChunks(AsyncChunkManager $manager, RevertClipboard $clipboa /** * @param AsyncChunkManager $manager * @param RevertClipboard $clipboard - * @return Generator|array[] - * @phpstan-return Generator + * @return Generator * @throws InvalidArgumentException + * @phpstan-return Generator */ private function redoChunks(AsyncChunkManager $manager, RevertClipboard $clipboard): Generator { @@ -101,21 +103,19 @@ private function redoChunks(AsyncChunkManager $manager, RevertClipboard $clipboa foreach ($clipboard->blocksAfter as $block) { yield $block; $block = self::singleDataToBlock($block);//turn data into real block - $manager->setBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), $block); + $manager->setBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ(), $block); $changed++; $this->publishProgress([$changed / $count, "Redone $changed blocks out of $count"]); } } /** - * @throws InvalidArgumentException * @throws AssumptionFailedError - * @throws Exception */ public function onCompletion(): void { try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); if ($session instanceof UserSession) $session->getBossBar()->hideFromAll(); } catch (SessionException $e) { Loader::getInstance()->getLogger()->logException($e); @@ -142,12 +142,12 @@ public function onCompletion(): void break; } case self::TYPE_REDO: - { - $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.revert.redo.success', [$this->generateTookString(), $changed, $totalCount])); - $session->undoHistory->push($clipboard); - break; - } - } - } - } + { + $session->sendMessage(TF::GREEN . $session->getLanguage()->translateString('task.revert.redo.success', [$this->generateTookString(), $changed, $totalCount])); + $session->undoHistory->push($clipboard); + break; + } + } + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/MWEAsyncTask.php b/src/xenialdan/MagicWE2/task/MWEAsyncTask.php index 6e696fee..d1767b99 100644 --- a/src/xenialdan/MagicWE2/task/MWEAsyncTask.php +++ b/src/xenialdan/MagicWE2/task/MWEAsyncTask.php @@ -5,8 +5,8 @@ use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\scheduler\AsyncTask; -use pocketmine\uuid\UUID; use pocketmine\world\Position; +use Ramsey\Uuid\Uuid; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\helper\SessionHelper; @@ -15,9 +15,9 @@ abstract class MWEAsyncTask extends AsyncTask { /** @var string */ - public $sessionUUID; + public string $sessionUUID; /** @var float */ - public $start; + public float $start; public function onProgressUpdate($progress): void { @@ -25,7 +25,7 @@ public function onProgressUpdate($progress): void $progress = new Progress($progress[0] / 100, $progress[1]); } try { - $session = SessionHelper::getSessionByUUID(UUID::fromString($this->sessionUUID)); + $session = SessionHelper::getSessionByUUID(Uuid::fromString($this->sessionUUID)); /** @var Progress $progress */ if ($session instanceof UserSession) $session->getBossBar()->setPercentage($progress->progress)->setSubTitle(str_replace("%", "%%%%", $progress->string . " | " . floor($progress->progress * 100) . "%")); else $session->sendMessage($progress->string . " | " . floor($progress->progress * 100) . "%");//TODO remove, debug @@ -48,7 +48,7 @@ public function generateTookString(): string public static function singleBlockToData(Block $block, ?Position $position = null): array { /** @noinspection PhpInternalEntityUsedInspection */ - return [$block->getFullId(), $position ?? $block->getPos()]; + return [$block->getFullId(), $position ?? $block->getPosition()]; } /** @@ -74,10 +74,10 @@ protected static function singleDataToBlock(array $data): Block $block = BlockFactory::getInstance()->fromFullBlock($data[0]); /** @var Position $pos */ $pos = $data[1]; - $block->getPos()->world = $pos->world; - $block->getPos()->x = $pos->x; - $block->getPos()->y = $pos->y; - $block->getPos()->z = $pos->z; + $block->getPosition()->world = $pos->world; + $block->getPosition()->x = $pos->x; + $block->getPosition()->y = $pos->y; + $block->getPosition()->z = $pos->z; return $block; } diff --git a/src/xenialdan/MagicWE2/task/action/ActionRegistry.php b/src/xenialdan/MagicWE2/task/action/ActionRegistry.php index 62c503f5..3f568368 100644 --- a/src/xenialdan/MagicWE2/task/action/ActionRegistry.php +++ b/src/xenialdan/MagicWE2/task/action/ActionRegistry.php @@ -9,7 +9,7 @@ class ActionRegistry { /** @var string[] */ - private static $actions = []; + private static array $actions = []; public function __construct() { @@ -20,44 +20,44 @@ public function __construct() } public static function registerAction(string $name, string $class): void - { - self::$actions[$name] = $class; - } - - /** - * @return array - */ - public static function getActions(): array - { - return self::$actions; - } - - /** - * @param string $name - * @return string - * @throws ActionNotFoundException - */ - public static function getAction(string $name): string - { - if (isset(self::$actions[$name])) return self::$actions[$name]; - throw new ActionNotFoundException("Action $name not found"); - } - - /** - * @param string $actionClass - * @return string - * @throws ActionNotFoundException - */ - public static function getActionName(string $actionClass): string - { - $names = array_flip(self::$actions); - if (isset($names[$actionClass])) return $names[$actionClass]; - throw new ActionNotFoundException("Action $actionClass not found"); - } - - public static function getDefaultActionProperties(string $className): array - { - return array_diff_key(get_class_vars($className), get_class_vars(TaskAction::class)); - } + { + self::$actions[$name] = $class; + } + + /** + * @return array + */ + public static function getActions(): array + { + return self::$actions; + } + + /** + * @param string $name + * @return string + * @throws ActionNotFoundException + */ + public static function getAction(string $name): string + { + if (isset(self::$actions[$name])) return self::$actions[$name]; + throw new ActionNotFoundException("Action $name not found"); + } + + /** + * @param string $actionClass + * @return string + * @throws ActionNotFoundException + */ + public static function getActionName(string $actionClass): string + { + $names = array_flip(self::$actions); + if (isset($names[$actionClass])) return $names[$actionClass]; + throw new ActionNotFoundException("Action $actionClass not found"); + } + + public static function getDefaultActionProperties(string $className): array + { + return array_diff_key(get_class_vars($className), get_class_vars(TaskAction::class)); + } } \ 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 0470331c..fdf36b29 100644 --- a/src/xenialdan/MagicWE2/task/action/ClipboardAction.php +++ b/src/xenialdan/MagicWE2/task/action/ClipboardAction.php @@ -7,19 +7,18 @@ use Generator; use pocketmine\math\Vector3; use xenialdan\MagicWE2\clipboard\SingleClipboard; -use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; abstract class ClipboardAction { /** @var string */ - public $prefix = ""; + public string $prefix = ""; /** @var string */ public string $completionString = '{%name} succeed, took {%took}, {%changed} entries out of {%total} changed.'; /** @var bool */ public bool $addClipboard = false; /** @var null|Vector3 */ - public $clipboardVector; + public ?Vector3 $clipboardVector = null; /** * @param string $sessionUUID @@ -27,9 +26,9 @@ abstract class ClipboardAction * @param null|int $changed * @param SingleClipboard $clipboard * @param string[] $messages - * @return Generator|Progress[] + * @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 16a573df..dd6b5085 100644 --- a/src/xenialdan/MagicWE2/task/action/CountAction.php +++ b/src/xenialdan/MagicWE2/task/action/CountAction.php @@ -6,20 +6,20 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\utils\TextFormat as TF; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; class CountAction extends TaskAction { /** @var bool */ - public $addRevert = false; + public bool $addRevert = false; /** @var string */ - public $completionString = '{%name} succeed, took {%took}, analyzed {%changed} blocks'; + public string $completionString = '{%name} succeed, took {%took}, analyzed {%changed} blocks'; public function __construct() { @@ -30,19 +30,19 @@ public static function getName(): string return "Analyze"; } - /** - * @param string $sessionUUID - * @param Selection $selection - * @param AsyncChunkManager $manager - * @param null|int $changed - * @param Block[] $newBlocks - * @param Block[] $blockFilter - * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change - * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - */ - public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $manager, ?int &$changed, array $newBlocks, array $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator + /** + * @param string $sessionUUID + * @param Selection $selection + * @param AsyncChunkManager $manager + * @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, AsyncChunkManager $manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator { $changed = 0; #$oldBlocks = []; @@ -51,8 +51,8 @@ public function execute(string $sessionUUID, Selection $selection, AsyncChunkMan $counts = []; BlockFactory::getInstance(); foreach ($selection->getShape()->getBlocks($manager, $newBlocks) as $block) { - $block1 = $manager->getBlockArrayAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()); - $tostring = (BlockFactory::getInstance()->get($block1[0], $block1[1]))->getName() . " " . $block1[0] . ":" . $block1[1]; + $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++; @@ -60,15 +60,15 @@ public function execute(string $sessionUUID, Selection $selection, AsyncChunkMan if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { yield $progress; $lastProgress = $progress; - } - } - $messages[] = TF::DARK_AQUA . count($counts) . " blocks found in a total of $count blocks"; - uasort($counts, static function ($a, $b) { + } + } + $messages[] = TF::DARK_AQUA . count($counts) . " blocks found in a total of $count blocks"; + uasort($counts, static function ($a, $b) { if ($a === $b) return 0; return ($a > $b) ? -1 : 1; }); - foreach ($counts as $block => $countb) { - $messages[] = TF::AQUA . $countb . "x | " . round($countb / $count * 100) . "% | " . $block; - } - } + foreach ($counts as $block => $countb) { + $messages[] = TF::AQUA . $countb . "x | " . round($countb / $count * 100) . "% | " . $block; + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/CutAction.php b/src/xenialdan/MagicWE2/task/action/CutAction.php index 5cbc01bf..f3f8c0bb 100644 --- a/src/xenialdan/MagicWE2/task/action/CutAction.php +++ b/src/xenialdan/MagicWE2/task/action/CutAction.php @@ -4,23 +4,24 @@ namespace xenialdan\MagicWE2\task\action; -use Exception; use Generator; +use InvalidArgumentException; use pocketmine\block\Block; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\AsyncChunkManager; use xenialdan\MagicWE2\helper\BlockEntry; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; class CutAction extends TaskAction { /** @var string */ - public $completionString = '{%name} succeed, took {%took}, {%changed} blocks out of {%total} cut.'; + public string $completionString = '{%name} succeed, took {%took}, {%changed} blocks out of {%total} cut.'; # /** @var bool */ # public $addRevert = true; /** @var bool */ - public $addClipboard = true; + public bool $addClipboard = true; public function __construct() { @@ -31,19 +32,19 @@ public static function getName(): string return "Cut"; } - /** - * @param string $sessionUUID - * @param Selection $selection - * @param AsyncChunkManager $manager - * @param null|int $changed - * @param Block[] $newBlocks - * @param Block[] $blockFilter - * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change - * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - */ - public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $manager, ?int &$changed, array $newBlocks, array $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator + /** + * @param string $sessionUUID + * @param Selection $selection + * @param AsyncChunkManager $manager + * @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, AsyncChunkManager $manager, ?int &$changed, BlockPalette $newBlocks, BlockPalette $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator { $changed = 0; $i = 0; @@ -51,22 +52,24 @@ public function execute(string $sessionUUID, Selection $selection, AsyncChunkMan $count = $selection->getShape()->getTotalCount(); $lastProgress = new Progress(0, ""); $min = $selection->getShape()->getMinVec3(); - foreach ($selection->getShape()->getBlocks($manager, $blockFilter) as $block) { - $new = clone $newBlocks[array_rand($newBlocks)]; + 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 - #$oldBlocks[] = API::setComponents($manager->getBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()),$block->x, $block->y, $block->z); - $newv3 = $block->getPos()->subtractVector($min)->floor();//TODO check if only used for clipboard + #$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 $oldBlocksSingleClipboard->addEntry($newv3->getFloorX(), $newv3->getFloorY(), $newv3->getFloorZ(), BlockEntry::fromBlock($block)); - $manager->setBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), $new); - if ($manager->getBlockArrayAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()) !== [$block->getId(), $block->getMeta()]) { + $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()) { $changed++; } $i++; - $progress = new Progress($i / $count, "Changed {$changed} blocks out of {$count}"); + $progress = new Progress($i / $count, "Changed $changed blocks out of $count"); if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { yield $progress; $lastProgress = $progress; - } - } - } + } + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/FlipAction.php b/src/xenialdan/MagicWE2/task/action/FlipAction.php index aa5f9819..a37a73c6 100644 --- a/src/xenialdan/MagicWE2/task/action/FlipAction.php +++ b/src/xenialdan/MagicWE2/task/action/FlipAction.php @@ -4,11 +4,13 @@ namespace xenialdan\MagicWE2\task\action; -use Exception; use Generator; use InvalidArgumentException; use pocketmine\block\BlockFactory; +use RuntimeException; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\exception\BlockQueryAlreadyParsedException; +use xenialdan\MagicWE2\exception\InvalidBlockStateException; use xenialdan\MagicWE2\helper\BlockEntry; use xenialdan\MagicWE2\helper\BlockStatesParser; use xenialdan\MagicWE2\helper\Progress; @@ -25,7 +27,7 @@ class FlipAction extends ClipboardAction /** @var string */ public string $completionString = '{%name} succeed, took {%took}, flipped {%changed} blocks out of {%total}'; /** @var string */ - private $axis; + private string $axis; public function __construct(string $axis)//TODO use pm Axis { @@ -38,16 +40,20 @@ public static function getName(): string return "Flip"; } - /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param SingleClipboard $clipboard - * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard &$clipboard, array &$messages = []): Generator + /** + * @param string $sessionUUID + * @param Selection $selection + * @param null|int $changed + * @param SingleClipboard $clipboard + * @param string[] $messages + * @return Generator + * @throws InvalidArgumentException + * @throws RuntimeException + * @throws \pocketmine\block\utils\InvalidBlockStateException + * @throws BlockQueryAlreadyParsedException + * @throws InvalidBlockStateException + */ + 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; @@ -58,34 +64,38 @@ public function execute(string $sessionUUID, Selection $selection, ?int &$change $clonedClipboard = clone $clipboard; $x = $y = $z = null; $maxX = $clipboard->selection->getSizeX() - 1; - $maxY = $clipboard->selection->getSizeY() - 1; - $maxZ = $clipboard->selection->getSizeZ() - 1; - foreach ($clipboard->iterateEntries($x, $y, $z) as $blockEntry) { - #var_dump("$x $y $z"); - if ($this->axis === self::AXIS_Z || $this->axis === self::AXIS_XZ) - $x = $maxX - $x; - if ($this->axis === self::AXIS_X || $this->axis === self::AXIS_XZ) - $z = $maxZ - $z; - if ($this->axis === self::AXIS_Y) - $y = $maxY - $y; - #var_dump("$x $y $z"); + $maxY = $clipboard->selection->getSizeY() - 1; + $maxZ = $clipboard->selection->getSizeZ() - 1; + foreach ($clipboard->iterateEntries($x, $y, $z) as $blockEntry) { + #var_dump("$x $y $z"); + if ($this->axis === self::AXIS_Z || $this->axis === self::AXIS_XZ) + $x = $maxX - $x; + if ($this->axis === self::AXIS_X || $this->axis === self::AXIS_XZ) + $z = $maxZ - $z; + if ($this->axis === self::AXIS_Y) + $y = $maxY - $y; + #var_dump("$x $y $z"); $block1 = $blockEntry->toBlock(); $blockStatesEntry = BlockStatesParser::getInstance()::getStateByBlock($block1); - $mirrored = $blockStatesEntry->mirror($this->axis); - $block = $mirrored->toBlock(); - $entry = BlockEntry::fromBlock($block); + if($blockStatesEntry === null){ + $block = $block1; + }else{ + $mirrored = $blockStatesEntry->mirror($this->axis); + $block = $mirrored->toBlock(); + } + $entry = BlockEntry::fromBlock($block); //var_dump($blockStatesEntry->__toString(), $mirrored->__toString(), $block); - /** @var int $x */ - /** @var int $y */ - /** @var int $z */ - $clonedClipboard->addEntry($x, $y, $z, $entry); - $changed++; - $progress = new Progress($changed / $count, "$changed/$count"); - if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { - yield $progress; - $lastProgress = $progress; - } - } - $clipboard = $clonedClipboard; - } + /** @var int $x */ + /** @var int $y */ + /** @var int $z */ + $clonedClipboard->addEntry($x, $y, $z, $entry); + $changed++; + $progress = new Progress($changed / $count, "$changed/$count"); + if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { + yield $progress; + $lastProgress = $progress; + } + } + //$clipboard = $clonedClipboard; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/RotateAction.php b/src/xenialdan/MagicWE2/task/action/RotateAction.php index 57f1ca98..de55d735 100644 --- a/src/xenialdan/MagicWE2/task/action/RotateAction.php +++ b/src/xenialdan/MagicWE2/task/action/RotateAction.php @@ -4,12 +4,14 @@ namespace xenialdan\MagicWE2\task\action; -use Exception; use Generator; use InvalidArgumentException; use pocketmine\block\BlockFactory; use pocketmine\math\Vector3; +use RuntimeException; use xenialdan\MagicWE2\clipboard\SingleClipboard; +use xenialdan\MagicWE2\exception\BlockQueryAlreadyParsedException; +use xenialdan\MagicWE2\exception\InvalidBlockStateException; use xenialdan\MagicWE2\helper\BlockEntry; use xenialdan\MagicWE2\helper\BlockStatesParser; use xenialdan\MagicWE2\helper\Progress; @@ -39,19 +41,23 @@ public function __construct(int $rotation, bool $aroundOrigin = true) public static function getName(): string { - return "Rotate"; - } + return "Rotate"; + } - /** - * @param string $sessionUUID - * @param Selection $selection - * @param null|int $changed - * @param SingleClipboard $clipboard - * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - */ - public function execute(string $sessionUUID, Selection $selection, ?int &$changed, SingleClipboard &$clipboard, array &$messages = []): Generator + /** + * @param string $sessionUUID + * @param Selection $selection + * @param null|int $changed + * @param SingleClipboard $clipboard + * @param string[] $messages + * @return Generator + * @throws InvalidArgumentException + * @throws RuntimeException + * @throws \pocketmine\block\utils\InvalidBlockStateException + * @throws BlockQueryAlreadyParsedException + * @throws InvalidBlockStateException + */ + 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; @@ -93,11 +99,14 @@ public function execute(string $sessionUUID, Selection $selection, ?int &$change } #var_dump("$newX $y $newZ"); $block1 = $blockEntry->toBlock(); - /** @var BlockStatesParser $instance */ $instance = BlockStatesParser::getInstance(); $blockStatesEntry = $instance::getStateByBlock($block1); - $rotated = $blockStatesEntry->rotate($this->rotation); - $block = $rotated->toBlock(); + if ($blockStatesEntry === null) { + $block = $block1; + } else { + $rotated = $blockStatesEntry->rotate($this->rotation); + $block = $rotated->toBlock(); + } $entry = BlockEntry::fromBlock($block); #var_dump($blockStatesEntry->__toString(), $rotated->__toString(), $entry); /** @var int $y */ @@ -139,6 +148,6 @@ public function execute(string $sessionUUID, Selection $selection, ?int &$change } $clonedSelection->shape = (Cuboid::constructFromPositions($pos1, $pos2));//TODO figure out how to keep the shape (not always Cuboid) $clonedClipboard->selection = $clonedSelection; - $clipboard = $clonedClipboard; + //$clipboard = $clonedClipboard; } } \ 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 395e4042..0ce92568 100644 --- a/src/xenialdan/MagicWE2/task/action/SetBiomeAction.php +++ b/src/xenialdan/MagicWE2/task/action/SetBiomeAction.php @@ -6,18 +6,19 @@ use Exception; use Generator; -use pocketmine\block\Block; +use pocketmine\math\Vector2; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; class SetBiomeAction extends TaskAction { /** @var bool */ - public $addRevert = false; + public bool $addRevert = false; /** @var int */ - private $biomeId; + private int $biomeId; public function __construct(int $biomeId) { @@ -29,33 +30,34 @@ public static function getName(): string return "Set biome"; } - /** - * @param string $sessionUUID - * @param Selection $selection - * @param AsyncChunkManager $manager - * @param null|int $changed - * @param Block[] $newBlocks - * @param Block[] $blockFilter - * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change - * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - */ - public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $manager, ?int &$changed, array $newBlocks, array $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator + /** + * @param string $sessionUUID + * @param Selection $selection + * @param AsyncChunkManager $manager + * @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, AsyncChunkManager $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)); $manager->getChunk($vec2->x >> 4, $vec2->y >> 4)->setBiomeId($vec2->x % 16, $vec2->y % 16, $this->biomeId); $changed++; $progress = new Progress($changed / $count, "Changed Biome for $changed/$count blocks"); - if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { - yield $progress; - $lastProgress = $progress; - } - } - } + if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { + yield $progress; + $lastProgress = $progress; + } + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/SetBlockAction.php b/src/xenialdan/MagicWE2/task/action/SetBlockAction.php index 1d84af51..0702382d 100644 --- a/src/xenialdan/MagicWE2/task/action/SetBlockAction.php +++ b/src/xenialdan/MagicWE2/task/action/SetBlockAction.php @@ -4,61 +4,64 @@ namespace xenialdan\MagicWE2\task\action; -use Exception; use Generator; +use InvalidArgumentException; use pocketmine\block\Block; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\AsyncChunkManager; use xenialdan\MagicWE2\helper\BlockEntry; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; class SetBlockAction extends TaskAction { - public function __construct() - { - } + public function __construct() + { + } - public static function getName(): string - { - return "Set block"; - } + public static function getName(): string + { + return "Set block"; + } - /** - * @param string $sessionUUID - * @param Selection $selection - * @param AsyncChunkManager $manager - * @param null|int $changed - * @param Block[] $newBlocks - * @param Block[] $blockFilter - * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change - * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - */ - public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $manager, ?int &$changed, array $newBlocks, array $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator + /** + * @param string $sessionUUID + * @param Selection $selection + * @param AsyncChunkManager $manager + * @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, AsyncChunkManager $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) { - $new = clone $newBlocks[array_rand($newBlocks)]; + 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 - #$oldBlocks[] = API::setComponents($manager->getBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()),$block->x, $block->y, $block->z); - $oldBlocksSingleClipboard->addEntry($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), BlockEntry::fromBlock($block)); - $manager->setBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), $new); - if ($manager->getBlockArrayAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()) !== [$block->getId(), $block->getMeta()]) { - $changed++; - } - $i++; - $progress = new Progress($i / $count, "Changed {$changed} blocks out of {$count}"); - if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { - yield $progress; - $lastProgress = $progress; - } - } - } + #$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); + /** @noinspection PhpInternalEntityUsedInspection */ + 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)) { + yield $progress; + $lastProgress = $progress; + } + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/TaskAction.php b/src/xenialdan/MagicWE2/task/action/TaskAction.php index 20f83d54..1cc7a2a8 100644 --- a/src/xenialdan/MagicWE2/task/action/TaskAction.php +++ b/src/xenialdan/MagicWE2/task/action/TaskAction.php @@ -5,25 +5,24 @@ namespace xenialdan\MagicWE2\task\action; use Generator; -use pocketmine\block\Block; use pocketmine\math\Vector3; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\AsyncChunkManager; -use xenialdan\MagicWE2\helper\Progress; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\selection\Selection; abstract class TaskAction { /** @var string */ - public $prefix = ""; + public string $prefix = ""; /** @var bool */ - public $addRevert = true; + public bool $addRevert = true; /** @var string */ - public $completionString = '{%name} succeed, took {%took}, {%changed} blocks out of {%total} changed.'; + public string $completionString = '{%name} succeed, took {%took}, {%changed} blocks out of {%total} changed.'; /** @var bool */ - public $addClipboard = false; + public bool $addClipboard = false; /** @var null|Vector3 */ - public $clipboardVector; + 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 @@ -33,22 +32,22 @@ abstract class TaskAction * @param Selection $selection * @param AsyncChunkManager $manager * @param null|int $changed - * @param Block[] $newBlocks - * @param Block[] $blockFilter + * @param BlockPalette $newBlocks + * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator|Progress[] - */ - abstract public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $manager, ?int &$changed, array $newBlocks, array $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator; + * @return Generator + */ + abstract public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $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(); - $this->clipboardVector = $clipboardVector; - } + /** + * @param Vector3|null $clipboardVector + */ + public function setClipboardVector(?Vector3 $clipboardVector): void + { + if ($clipboardVector instanceof Vector3) $clipboardVector = $clipboardVector->asVector3()->floor(); + $this->clipboardVector = $clipboardVector; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/task/action/TestAction.php b/src/xenialdan/MagicWE2/task/action/TestAction.php index 7d446503..5697af07 100644 --- a/src/xenialdan/MagicWE2/task/action/TestAction.php +++ b/src/xenialdan/MagicWE2/task/action/TestAction.php @@ -6,19 +6,19 @@ use Exception; use Generator; -use pocketmine\block\Block; use pocketmine\block\BlockFactory; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\AsyncChunkManager; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; class TestAction extends TaskAction { /** @var bool */ - public $addRevert = false; + public bool $addRevert = false; /** @var string */ - public $completionString = '{%name} succeed, took {%took}, tested {%changed} blocks'; + public string $completionString = '{%name} succeed, took {%took}, tested {%changed} blocks'; public function __construct() { @@ -29,28 +29,28 @@ public static function getName(): string return "Test"; } - /** - * @param string $sessionUUID - * @param Selection $selection - * @param AsyncChunkManager $manager - * @param null|int $changed - * @param Block[] $newBlocks - * @param Block[] $blockFilter - * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change - * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - */ - public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $manager, ?int &$changed, array $newBlocks, array $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator + /** + * @param string $sessionUUID + * @param Selection $selection + * @param AsyncChunkManager $manager + * @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, AsyncChunkManager $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, []) as $block) { + foreach ($selection->getShape()->getBlocks($manager, $blockFilter) as $block) { $changed++; - $messages[] = $block->getPos()->asVector3()->__toString() . " " . $block->getName(); + $messages[] = $block->getPosition()->asVector3()->__toString() . " " . $block->getName(); $progress = new Progress($changed / $count, "$changed/$count"); if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { yield $progress; diff --git a/src/xenialdan/MagicWE2/task/action/ThawAction.php b/src/xenialdan/MagicWE2/task/action/ThawAction.php index 37c22f8e..28efa177 100644 --- a/src/xenialdan/MagicWE2/task/action/ThawAction.php +++ b/src/xenialdan/MagicWE2/task/action/ThawAction.php @@ -4,69 +4,66 @@ namespace xenialdan\MagicWE2\task\action; -use Exception; use Generator; -use pocketmine\block\Block; -use xenialdan\MagicWE2\API; +use InvalidArgumentException; +use pocketmine\block\VanillaBlocks; use xenialdan\MagicWE2\clipboard\SingleClipboard; use xenialdan\MagicWE2\helper\AsyncChunkManager; use xenialdan\MagicWE2\helper\BlockEntry; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\Progress; use xenialdan\MagicWE2\selection\Selection; class ThawAction extends TaskAction { - public function __construct() - { - } + public function __construct() + { + } - public static function getName(): string - { - return "Thaw"; - } + public static function getName(): string + { + return "Thaw"; + } - /** + /** * @param string $sessionUUID * @param Selection $selection * @param AsyncChunkManager $manager * @param null|int $changed - * @param Block[] $newBlocks - * @param Block[] $blockFilter + * @param BlockPalette $newBlocks + * @param BlockPalette $blockFilter * @param SingleClipboard $oldBlocksSingleClipboard blocks before the change * @param string[] $messages - * @return Generator|Progress[] - * @throws Exception - * @noinspection SuspiciousAssignmentsInspection + * @return Generator + * @throws InvalidArgumentException */ - public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $manager, ?int &$changed, array $newBlocks, array $blockFilter, SingleClipboard $oldBlocksSingleClipboard, array &$messages = []): Generator + public function execute(string $sessionUUID, Selection $selection, AsyncChunkManager $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, ""); + $lastProgress = new Progress(0, "Thaw action is still under TODO"); - $m = []; - $e = false; - $blockFilter = API::blockParser("snow_block,snow_layer,ice", $m, $e); - $newBlocks = API::blockParser("air,air,water", $m, $e); - foreach ($blockFilter as $ib => $blockF) { - foreach ($selection->getShape()->getBlocks($manager, [$blockF]) as $block) { - $new = clone $newBlocks[$ib]; - #$oldBlocks[] = API::setComponents($manager->getBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ()),$block->x, $block->y, $block->z); - $oldBlocksSingleClipboard->addEntry($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), BlockEntry::fromBlock($block)); - $manager->setBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ(), $new); - if ($manager->getBlockAt($block->getPos()->getFloorX(), $block->getPos()->getFloorY(), $block->getPos()->getFloorZ())->getId() !== $block->getId()) { + $blockFilterA = [VanillaBlocks::SNOW_LAYER(), VanillaBlocks::SNOW(), VanillaBlocks::ICE()]; + $newBlocksA = [VanillaBlocks::AIR(), VanillaBlocks::AIR(), VanillaBlocks::WATER()]; + 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); + $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); + if ($manager->getBlockAt($block->getPosition()->getFloorX(), $block->getPosition()->getFloorY(), $block->getPosition()->getFloorZ())->getId() !== $block->getId()) { $changed++; } $i++; - $progress = new Progress($i / $count, "Changed {$changed} blocks out of {$count}"); + $progress = new Progress($i / $count, "Changed $changed blocks out of $count"); if (floor($progress->progress * 100) > floor($lastProgress->progress * 100)) { yield $progress; $lastProgress = $progress; } } - } - } + } + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/tool/Brush.php b/src/xenialdan/MagicWE2/tool/Brush.php index 9bfe3d38..f386f1df 100644 --- a/src/xenialdan/MagicWE2/tool/Brush.php +++ b/src/xenialdan/MagicWE2/tool/Brush.php @@ -6,35 +6,43 @@ use Exception; use InvalidArgumentException; +use jojoe77777\FormAPI\CustomForm; use JsonException; use pocketmine\data\bedrock\BiomeIds; -use pocketmine\item\Durable; use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\item\Item; -use pocketmine\item\ItemFactory; -use pocketmine\item\ItemIds; +use pocketmine\item\VanillaItems; use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat as TF; -use pocketmine\uuid\UUID; use pocketmine\world\biome\BiomeRegistry; +use Ramsey\Uuid\Uuid; use ReflectionClass; use TypeError; -use xenialdan\customui\elements\Dropdown; -use xenialdan\customui\elements\Input; -use xenialdan\customui\elements\Label; -use xenialdan\customui\elements\Toggle; -use xenialdan\customui\windows\CustomForm; use xenialdan\MagicWE2\API; use xenialdan\MagicWE2\exception\ActionNotFoundException; use xenialdan\MagicWE2\exception\SessionException; use xenialdan\MagicWE2\exception\ShapeNotFoundException; +use xenialdan\MagicWE2\helper\BlockPalette; use xenialdan\MagicWE2\helper\SessionHelper; use xenialdan\MagicWE2\Loader; use xenialdan\MagicWE2\selection\shape\ShapeRegistry; use xenialdan\MagicWE2\session\UserSession; use xenialdan\MagicWE2\task\action\ActionRegistry; +use function array_flip; +use function array_intersect_key; +use function array_keys; +use function array_replace; +use function array_search; +use function array_slice; +use function array_values; +use function array_walk; +use function gettype; +use function is_bool; +use function is_int; +use function trim; +use function ucfirst; class Brush extends WETool { @@ -42,7 +50,7 @@ class Brush extends WETool public const TAG_BRUSH_PROPERTIES = "properties"; /** @var BrushProperties */ - public $properties; + public BrushProperties $properties; /** * Brush constructor. @@ -68,10 +76,9 @@ public function getName(): string */ public function toItem(): Item { - /** @var Durable $item */ - $item = ItemFactory::getInstance()->get(ItemIds::WOODEN_SHOVEL); + $item = VanillaItems::WOODEN_SHOVEL(); $item->addEnchantment(new EnchantmentInstance(Loader::$ench)); - $uuid = $this->properties->uuid ?? UUID::fromRandom()->toString(); + $uuid = $this->properties->uuid ?? Uuid::uuid4()->toString(); $this->properties->uuid = $uuid; $properties = json_encode($this->properties, JSON_THROW_ON_ERROR); if (!is_string($properties)) throw new InvalidArgumentException("Brush properties could not be decoded"); @@ -81,7 +88,7 @@ public function toItem(): Item ->setInt("version", $this->properties->version) ->setString("properties", $properties) ); - $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::DARK_PURPLE . $this->getName()); + $item->setCustomName(Loader::PREFIX . TF::BOLD . TF::LIGHT_PURPLE . $this->getName()); $item->setLore($this->properties->generateLore()); $item->setUnbreakable(); return $item; @@ -101,58 +108,42 @@ public function getForm(bool $new = true, array $errors = []): CustomForm return TF::EOL . TF::RED . $value; }, $errors); $brushProperties = $this->properties ?? new BrushProperties(); - $form = new CustomForm("Brush settings"); - // Shape - #$form->addElement(new Label((isset($errors['shape']) ? TF::RED : "") . "Shape" . ($errors['shape'] ?? ""))); + + $dropdownShapeOptions = []; if ($new) { - $dropdownShape = new Dropdown((isset($errors['shape']) ? TF::RED : "") . "Shape" . ($errors['shape'] ?? "")); foreach (Loader::getShapeRegistry()::getShapes() as $name => $class) { if ($name === ShapeRegistry::CUSTOM) continue; - $dropdownShape->addOption($name, $class === $brushProperties->shape); - } - $form->addElement($dropdownShape); - } else { - $form->addElement(new Label($brushProperties->getShapeName())); - } - // Action - $dropdownAction = new Dropdown("Action"); - foreach (ActionRegistry::getActions() as $name => $class) { - $dropdownAction->addOption($name, $class === $brushProperties->action); + $dropdownShapeOptions[(string)$name] = $class === $brushProperties->shape; + } } - $form->addElement($dropdownAction); - // Name - $form->addElement(new Input("Name", "Name", $new ? "" : $this->getName())); - // Blocks - $form->addElement(new Input((isset($errors['blocks']) ? TF::RED : "") . "Blocks" . ($errors['blocks'] ?? ""), "grass,stone:1", $brushProperties->blocks)); - // Filter - $form->addElement(new Input((isset($errors['filter']) ? TF::RED : "") . "Filter" . ($errors['filter'] ?? ""), "air", $brushProperties->filter)); - // Biome - $dropdownBiome = new Dropdown((isset($errors['biome']) ? TF::RED : "") . "Biome" . ($errors['biome'] ?? "")); + $dropdownActionOptions = []; + foreach (ActionRegistry::getActions() as $name => $class) { + $dropdownActionOptions[$name] = $class === $brushProperties->action; + } + $dropdownBiomeOptions = []; foreach ((new ReflectionClass(BiomeIds::class))->getConstants() as $name => $value) { if ($value === BiomeIds::HELL) continue; - $dropdownBiome->addOption(BiomeRegistry::getInstance()->getBiome($value)->getName(), $value === $brushProperties->biomeId); - } - $form->addElement($dropdownBiome); - // Hollow - $form->addElement(new Toggle("Hollow", $brushProperties->hollow)); - // Extra properties - if (!$new) { - foreach ($this->getExtradataForm($brushProperties->shape)->getContent() as $element) { - $form->addElement($element); - } + $dropdownBiomeOptions[BiomeRegistry::getInstance()->getBiome($value)->getName()] = $value === $brushProperties->biomeId; } - // Function - $form->setCallable(function (Player $player, $data) use ($form, $new) { - #var_dump(__LINE__, $data); - #$data = array_slice($data, 0, 7); - [$shape, $action, $name, $blocks, $filter, $biome, $hollow] = $data; - $extraData = []; - #var_dump(__LINE__, array_slice($data, 7)); - $base = ShapeRegistry::getDefaultShapeProperties(ShapeRegistry::getShape($shape)); - foreach (array_slice($data, 7, null, true) as $i => $value) { - #var_dump($i, $value, gettype($value), gettype($base[lcfirst($form->getElement($i)->getText())])); - if (is_int($base[lcfirst($form->getElement($i)->getText())])) $value = (int)$value; - $extraData[lcfirst($form->getElement($i)->getText())] = $value;//TODO + + $form = (new CustomForm(function (Player $player, $data) use ($new, $dropdownShapeOptions, $dropdownActionOptions, $dropdownBiomeOptions) { + if ($data === null) return; + #var_dump(__LINE__, $data); + #$data = array_slice($data, 0, 7); + [$shape, $action, $name, $blocks, $filter, $biome, $hollow] = $data; + if ($new) $shape = array_keys($dropdownShapeOptions)[$shape]; + //else $shape = array_keys($data)[0]??get_class($this->properties->shape); + $action = array_keys($dropdownActionOptions)[$action]; + $biome = array_keys($dropdownBiomeOptions)[$biome];//TODO throw exception if not valid + + $extraData = []; + #var_dump(__LINE__, array_slice($data, 7)); + $base = ShapeRegistry::getDefaultShapeProperties(($new ? ShapeRegistry::getShape($shape) : $this->properties->shape)); + $slice = array_values(array_slice($data, 7, null, true));//TODO use label? + $j = 0; + foreach ($base as $i => $value) { + $extraData[$i] = is_int($value) ? (int)$slice[$j] : $slice[$j];//TODO enhance + $j++; } #var_dump(__LINE__, $extraData); //prepare data @@ -170,113 +161,133 @@ public function getForm(bool $new = true, array $errors = []): CustomForm //error checks $error = []; try { - $m = []; - $e = false; - API::blockParser($blocks, $m, $e); - if ($e) throw new InvalidArgumentException(implode(TF::EOL, $m)); - if (empty($blocks)) throw new AssumptionFailedError("Blocks cannot be empty!"); + $p = BlockPalette::fromString($blocks); + if ($p->empty()) throw new AssumptionFailedError("Blocks cannot be empty!"); } catch (Exception $ex) { - $error['blocks'] = $ex->getMessage(); - } - try { - $m = []; - $e = false; - API::blockParser($filter, $m, $e); - if ($e) throw new InvalidArgumentException(implode(TF::EOL, $m)); - } catch (Exception $ex) { - $error['filter'] = $ex->getMessage(); - } - try { - $shape = Loader::getShapeRegistry()::getShape($shape); - } catch (Exception $ex) { - $error['shape'] = $ex->getMessage(); - } - try { - $action = Loader::getActionRegistry()::getAction($action); - } catch (Exception $ex) { - $error['action'] = $ex->getMessage(); - } - try { + $error['blocks'] = $ex->getMessage(); + } + try { + BlockPalette::fromString($filter); + } catch (Exception $ex) { + $error['filter'] = $ex->getMessage(); + } + try { + $shape = ($new ? ShapeRegistry::getShape($shape) : $this->properties->shape); + } catch (Exception $ex) { + $error['shape'] = $ex->getMessage(); + } + try { + $action = Loader::getActionRegistry()::getAction($action); + } catch (Exception $ex) { + $error['action'] = $ex->getMessage(); + } + try { if (!is_int($biomeId)) throw new AssumptionFailedError("Biome not found"); - } catch (Exception $ex) { - $error['biome'] = $ex->getMessage(); - } + } catch (Exception $ex) { + $error['biome'] = $ex->getMessage(); + } - //Set properties (called before resending, so form contains errors) - if (!empty(trim(TF::clean($name)))) $this->properties->customName = $name; - if (!isset($error['shape'])) { - $this->properties->shape = $shape; - if (!$new && !empty($extraData)) - $this->properties->shapeProperties = $extraData; - } - if (!isset($error['action'])) $this->properties->action = $action; - /*if (!isset($error['blocks']))*/ - $this->properties->blocks = $blocks; - /*if (!isset($error['filter']))*/ - $this->properties->filter = $filter; - $this->properties->hollow = $hollow; + //Set properties (called before resending, so form contains errors) + if (!empty(trim(TF::clean($name)))) $this->properties->customName = $name; + if (!isset($error['shape'])) { + $this->properties->shape = $shape; + if (!$new && !empty($extraData)) + $this->properties->shapeProperties = $extraData; + } + if (!isset($error['action'])) $this->properties->action = $action; + /*if (!isset($error['blocks']))*/ + $this->properties->blocks = $blocks; + /*if (!isset($error['filter']))*/ + $this->properties->filter = $filter; + $this->properties->hollow = $hollow; - //Resend form upon error - if (!empty($error)) { - $player->sendForm($this->getForm($new, $error)); - return; - } + //Resend form upon error + if (!empty($error)) { + $player->sendForm($this->getForm($new, $error)); + return; + } - //Debug - #print_r($extraData); - try { - $brush = $this; - $session = SessionHelper::getUserSession($player); - if (!$session instanceof UserSession) { + //Debug + #print_r($extraData); + try { + $brush = $this; + $session = SessionHelper::getUserSession($player); + if (!$session instanceof UserSession) { throw new SessionException(Loader::getInstance()->getLanguage()->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - if (!$new) { - $session->replaceBrush($brush); - } else { - $player->sendForm($this->getExtradataForm($this->properties->shape)); - } - } catch (Exception $ex) { - $player->sendMessage($ex->getMessage()); - Loader::getInstance()->getLogger()->logException($ex); - } - }); - return $form; - } catch (Exception $e) { + } + if (!$new) { + $session->getBrushes()->replaceBrush($brush); + } else { + $player->sendForm($this->getExtradataForm($this->properties->shape)); + } + } catch (Exception $ex) { + $player->sendMessage($ex->getMessage()); + Loader::getInstance()->getLogger()->logException($ex); + } + })) + ->setTitle("Brush settings"); + // Shape + #$form->addElement(new Label((isset($errors['shape']) ? TF::RED : "") . "Shape" . ($errors['shape'] ?? ""))); + if ($new) { + $form->addDropdown((isset($errors['shape']) ? TF::RED : "") . "Shape" . ($errors['shape'] ?? ""), array_keys($dropdownShapeOptions)); + } else { + $form->addLabel($brushProperties->getShapeName()); + } + // Action + $form->addDropdown("Action", array_keys($dropdownActionOptions)); + // Name + $form->addInput("Name", "Name", $new ? "" : $this->getName()); + // Blocks + $form->addInput((isset($errors['blocks']) ? TF::RED : "") . "Blocks" . ($errors['blocks'] ?? ""), "grass,stone:1", $brushProperties->blocks); + // Filter + $form->addInput((isset($errors['filter']) ? TF::RED : "") . "Filter" . ($errors['filter'] ?? ""), "air", $brushProperties->filter); + // Biome + $form->addDropdown((isset($errors['biome']) ? TF::RED : "") . "Biome" . ($errors['biome'] ?? ""), array_keys($dropdownBiomeOptions)); + // Hollow + $form->addToggle("Hollow", $brushProperties->hollow); + // Extra properties + if (!$new) { + $form = $this->getExtradataForm($brushProperties->shape, $form);//TODO check if elements are added + } + // Function + return $form; + } catch (Exception $e) { throw new AssumptionFailedError("Could not create brush form"); - } - } - - private function getExtradataForm(string $shapeClass): CustomForm - { - $form = new CustomForm("Shape settings"); - #foreach (($defaultReplaced = array_merge(ShapeRegistry::getDefaultShapeProperties($shapeClass), $this->properties->shapeProperties)) as $name => $value) { - $base = ShapeRegistry::getDefaultShapeProperties($shapeClass); - foreach (($defaultReplaced = array_replace($base, array_intersect_key($this->properties->shapeProperties, $base))) as $name => $value) { - if (is_bool($value)) $form->addElement(new Toggle(ucfirst($name), $value)); - else $form->addElement(new Input(ucfirst($name), $name . " (" . gettype($value) . ")", (string)$value)); } - #var_dump($this->properties->shapeProperties); - #var_dump('Base', $base); - #var_dump('Default Replaced', $defaultReplaced); - $form->setCallable(function (Player $player, $data) use ($defaultReplaced, $base) { - //TODO validation, resending etc. - $extraData = []; - $names = array_keys($defaultReplaced); - foreach ($data as $index => $value) { - if (is_int($base[$names[$index]])) $value = (int)$value; - $extraData[$names[$index]] = $value; - } - $this->properties->shapeProperties = $extraData; + } - $brush = $this; - $session = SessionHelper::getUserSession($player); - if (!$session instanceof UserSession) { - throw new SessionException(Loader::getInstance()->getLanguage()->translateString('error.nosession', [Loader::getInstance()->getName()])); - } - $this->properties->uuid = UUID::fromRandom()->toString(); - $session->addBrush($brush); - $player->getInventory()->addItem($brush->toItem()); - }); - return $form; - } + private function getExtradataForm(string $shapeClass, ?CustomForm $form = null): CustomForm + { + #foreach (($defaultReplaced = array_merge(ShapeRegistry::getDefaultShapeProperties($shapeClass), $this->properties->shapeProperties)) as $name => $value) { + $base = ShapeRegistry::getDefaultShapeProperties($shapeClass); + $defaultReplaced = array_replace($base, array_intersect_key($this->properties->shapeProperties, $base)); + $form = ($form ?? new CustomForm(function (Player $player, $data) use ($defaultReplaced, $base) { + //TODO validation, resending etc. + $extraData = []; + $names = array_keys($defaultReplaced); + foreach ($data as $index => $value) { + if (is_int($base[$names[$index]])) $value = (int)$value; + $extraData[$names[$index]] = $value; + } + $this->properties->shapeProperties = $extraData; + + $brush = $this; + $session = SessionHelper::getUserSession($player); + if (!$session instanceof UserSession) { + throw new SessionException(Loader::getInstance()->getLanguage()->translateString('error.nosession', [Loader::getInstance()->getName()])); + } + $this->properties->uuid = Uuid::uuid4()->toString(); + $session->getBrushes()->addBrush($brush); + $player->getInventory()->addItem($brush->toItem()); + })) + ->setTitle("Shape settings"); + foreach ($defaultReplaced as $name => $value) { + if (is_bool($value)) $form->addToggle(ucfirst($name), $value); + else $form->addInput(ucfirst($name), $name . " (" . gettype($value) . ")", (string)$value); + } + #var_dump($this->properties->shapeProperties); + #var_dump('Base', $base); + #var_dump('Default Replaced', $defaultReplaced); + return $form; + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/tool/BrushProperties.php b/src/xenialdan/MagicWE2/tool/BrushProperties.php index 7b86e50d..d0ca2b10 100644 --- a/src/xenialdan/MagicWE2/tool/BrushProperties.php +++ b/src/xenialdan/MagicWE2/tool/BrushProperties.php @@ -22,129 +22,129 @@ class BrushProperties implements JsonSerializable public const VERSION = 1; /** @var int */ - public $version = self::VERSION; + public int $version = self::VERSION; /** @var string */ - public $customName = ""; + public string $customName = ""; /** @var string */ - public $shape = Sphere::class; + public string $shape = Sphere::class; /** @var array */ - public $shapeProperties = []; + public array $shapeProperties = []; /** @var string */ - public $action = SetBlockAction::class; + public string $action = SetBlockAction::class; /** @var array */ - public $actionProperties = []; + public array $actionProperties = []; /** @var bool */ - public $hollow = false;//TODO consider moving into shape properties + public bool $hollow = false;//TODO consider moving into shape properties /** @var string */ - public $blocks = "stone"; + public string $blocks = "stone"; /** @var string */ - public $filter = ""; + public string $filter = ""; /** @var int */ - public $biomeId = BiomeIds::PLAINS; + public int $biomeId = BiomeIds::PLAINS; /** @var string */ - public $uuid; + public string $uuid; /** * Specify data which should be serialized to JSON * @link http://php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed data which can be serialized by json_encode, + * @return array data which can be serialized by json_encode, * which is a value of any type other than a resource. * @since 5.4.0 */ - public function jsonSerialize() + public function jsonSerialize(): array { - return (array)$this; - } + return (array)$this; + } - /** - * @param array $json - * @return BrushProperties - * @throws InvalidArgumentException - */ - public static function fromJson(array $json): BrushProperties - { - if (($json["version"] ?? 0) !== self::VERSION) throw new InvalidArgumentException("Version mismatch"); - $properties = new self; - foreach ($json as $key => $value) { - $properties->$key = $value; - } - return $properties; - } + /** + * @param array $json + * @return BrushProperties + * @throws InvalidArgumentException + */ + public static function fromJson(array $json): BrushProperties + { + if (($json["version"] ?? 0) !== self::VERSION) throw new InvalidArgumentException("Version mismatch"); + $properties = new self; + foreach ($json as $key => $value) { + $properties->$key = $value; + } + return $properties; + } - public function getName(): string - { - $str = ""; - try { - $str = trim(($this->hasCustomName() ? $this->customName : $this->getShapeName()) /*. " " . $this->action->getName() . */); - } catch (ShapeNotFoundException $e) { - } - if (stripos(TF::clean($str), "brush") === false) { - $str .= " Brush"; - } - return $str; - } + public function getName(): string + { + $str = ""; + try { + $str = trim(($this->hasCustomName() ? $this->customName : $this->getShapeName()) /*. " " . $this->action->getName() . */); + } catch (ShapeNotFoundException $e) { + } + if (stripos(TF::clean($str), "brush") === false) { + $str .= " Brush"; + } + return $str; + } - /** - * @return string - * @throws ShapeNotFoundException - */ - public function getShapeName(): string - { - return is_subclass_of($this->shape, Shape::class) ? ShapeRegistry::getShapeName($this->shape) : ""; - } + /** + * @return string + * @throws ShapeNotFoundException + */ + public function getShapeName(): string + { + return is_subclass_of($this->shape, Shape::class) ? ShapeRegistry::getShapeName($this->shape) : ""; + } - /** - * @return string - * @throws ActionNotFoundException - */ - public function getActionName(): string - { - return is_subclass_of($this->action, TaskAction::class) ? ActionRegistry::getActionName($this->action) : ""; - } + /** + * @return string + * @throws ActionNotFoundException + */ + public function getActionName(): string + { + return is_subclass_of($this->action, TaskAction::class) ? ActionRegistry::getActionName($this->action) : ""; + } - public function hasCustomName(): bool - { - return !empty($this->customName); - } + public function hasCustomName(): bool + { + return !empty($this->customName); + } - /** - * @param string $customName If empty, the name will be reset - */ - public function setCustomName(string $customName = ""): void - { - $this->customName = $customName; - } + /** + * @param string $customName If empty, the name will be reset + */ + public function setCustomName(string $customName = ""): void + { + $this->customName = $customName; + } - /** + /** * @return array * @throws ActionNotFoundException * @throws ShapeNotFoundException * @noinspection NestedTernaryOperatorInspection */ - public function generateLore(): array - { - $shapeProperties = array_map(static function ($k, $v): string { - return TF::GOLD . " " . ucfirst($k) . " = " . (is_bool($v) ? ($v ? "Yes" : "No") : $v); + public function generateLore(): array + { + $shapeProperties = array_map(static function ($k, $v): string { + return TF::RESET . TF::AQUA . " " . ucfirst($k) . ": " . TF::RESET . (is_bool($v) ? ($v ? TF::GREEN . "Yes" : TF::RED . "No") : $v); }, array_keys($this->shapeProperties), $this->shapeProperties); $actionProperties = array_map(static function ($k, $v): string { - return TF::GOLD . " " . ucfirst($k) . " = " . (is_bool($v) ? ($v ? "Yes" : "No") : $v); + return TF::RESET . TF::AQUA . " " . ucfirst($k) . ": " . TF::RESET . (is_bool($v) ? ($v ? TF::GREEN . "Yes" : TF::RED . "No") : $v); }, array_keys($this->actionProperties), $this->actionProperties); - return array_merge( - [ - TF::GOLD . "Shape: {$this->getShapeName()}", - ], - $shapeProperties, - [ - TF::GOLD . "Action: {$this->getActionName()}", - ], - $actionProperties, - [ - TF::GOLD . "Blocks: {$this->blocks}", - TF::GOLD . "Filter: {$this->filter}", - TF::GOLD . "Biome: {$this->biomeId}", - TF::GOLD . "Hollow: " . ($this->hollow ? "Yes" : "No"), - //TF::GOLD . "UUID: {$this->uuid}", - ] - ); - } + return array_merge( + [ + TF::RESET . TF::BOLD . TF::GOLD . "Shape: " . TF::RESET . $this->getShapeName(), + ], + $shapeProperties, + [ + TF::RESET . TF::BOLD . TF::GOLD . "Action: " . TF::RESET . $this->getActionName(), + ], + $actionProperties, + [ + TF::RESET . TF::BOLD . TF::GOLD . "Blocks: " . TF::RESET . $this->blocks, + TF::RESET . TF::BOLD . TF::GOLD . "Filter: " . TF::RESET . $this->filter, + TF::RESET . TF::BOLD . TF::GOLD . "Biome: " . TF::RESET . $this->biomeId, + TF::RESET . TF::BOLD . TF::GOLD . "Hollow: " . TF::RESET . ($this->hollow ? TF::GREEN . "Yes" : TF::RED . "No"), + //TF::GOLD . "UuidInterface: {$this->uuid}", + ] + ); + } } \ No newline at end of file diff --git a/src/xenialdan/MagicWE2/tool/Flood.php b/src/xenialdan/MagicWE2/tool/Flood.php index c6ba3104..18d9fcc7 100644 --- a/src/xenialdan/MagicWE2/tool/Flood.php +++ b/src/xenialdan/MagicWE2/tool/Flood.php @@ -1,4 +1,4 @@ -validateChunkManager($manager); $this->y = $this->getCenter()->getFloorY(); @@ -60,14 +61,14 @@ public function getBlocks($manager, array $filterblocks = [], int $flags = API:: * Returns a flat layer of all included x z positions in selection * @param World|AsyncChunkManager $manager The world or AsyncChunkManager * @param int $flags - * @return Generator|Vector2[] + * @return Generator * @throws Exception */ - public function getLayer($manager, int $flags = API::FLAG_BASE): Generator + public function getLayer(AsyncChunkManager|World $manager, int $flags = API::FLAG_BASE): Generator { $this->validateChunkManager($manager); - foreach ($this->getBlocks($manager, []) as $block) { - yield new Vector2($block->getPos()->x, $block->getPos()->z); + foreach ($this->getBlocks($manager, BlockPalette::CREATE()) as $block) { + yield new Vector2($block->getPosition()->x, $block->getPosition()->z); } } @@ -77,15 +78,15 @@ public function getLayer($manager, int $flags = API::FLAG_BASE): Generator * @throws InvalidArgumentException * @noinspection SlowArrayOperationsInLoopInspection */ - private function walk($manager): array + private function walk(AsyncChunkManager|World $manager): array { $this->validateChunkManager($manager); /** @var Block[] $walkTo */ $walkTo = []; foreach ($this->nextToCheck as $next) { - $sides = iterator_to_array($this->getHorizontalSides($manager, $next->getPos())); + $sides = iterator_to_array($this->getHorizontalSides($manager, $next->getPosition())); $walkTo = array_merge($walkTo, array_filter($sides, function (Block $side) use ($walkTo) { - return $side->getId() === 0 && !in_array($side, $walkTo, true) && !in_array($side, $this->walked, true) && !in_array($side, $this->nextToCheck, true) && $side->getPos()->distanceSquared($this->getCenter()) <= ($this->limit / M_PI); + return $side->getId() === 0 && !in_array($side, $walkTo, true) && !in_array($side, $this->walked, true) && !in_array($side, $this->nextToCheck, true) && $side->getPosition()->distanceSquared($this->getCenter()) <= ($this->limit / M_PI); })); } $this->walked = array_merge($this->walked, $walkTo); @@ -97,10 +98,10 @@ private function walk($manager): array /** * @param World|AsyncChunkManager $manager * @param Vector3 $vector3 - * @return Generator|Block[] + * @return Generator * @throws InvalidArgumentException */ - private function getHorizontalSides($manager, Vector3 $vector3): Generator + private function getHorizontalSides(AsyncChunkManager|World $manager, Vector3 $vector3): Generator { $this->validateChunkManager($manager); foreach ([Facing::NORTH, Facing::SOUTH, Facing::WEST, Facing::EAST] as $vSide) { @@ -121,7 +122,7 @@ public function getTotalCount(): int * @return array * @throws InvalidArgumentException */ - public function getTouchedChunks($chunkManager): array + public function getTouchedChunks(AsyncChunkManager|World $chunkManager): array { $this->validateChunkManager($chunkManager); $maxRadius = sqrt($this->limit / M_PI); @@ -142,7 +143,7 @@ public function getTouchedChunks($chunkManager): array continue; } #print "Touched Chunk at: $x:$z" . PHP_EOL; - $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serialize($chunk); + $touchedChunks[World::chunkHash($x, $z)] = FastChunkSerializer::serializeTerrain($chunk); } } #print "Touched chunks count: " . count($touchedChunks) . PHP_EOL;; @@ -158,7 +159,7 @@ public function getName(): string * @param mixed $manager * @throws InvalidArgumentException */ - public function validateChunkManager($manager): void + 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"); } @@ -177,7 +178,7 @@ private function getCenter(): Vector3 */ public static function getChunkManager(array $chunks): AsyncChunkManager { - $manager = new AsyncChunkManager(); + $manager = new AsyncChunkManager(0, World::Y_MAX); foreach ($chunks as $hash => $chunk) { World::getXZ($hash, $x, $z); $manager->setChunk($x, $z, $chunk); diff --git a/src/xenialdan/MagicWE2/tool/WETool.php b/src/xenialdan/MagicWE2/tool/WETool.php index 59295ec2..67222c03 100644 --- a/src/xenialdan/MagicWE2/tool/WETool.php +++ b/src/xenialdan/MagicWE2/tool/WETool.php @@ -7,6 +7,6 @@ abstract class WETool { - abstract public function getName(): string; + abstract public function getName(): string; } \ No newline at end of file