From 901c62b466918a3aa797bf397aef28af4f63ca39 Mon Sep 17 00:00:00 2001 From: VixikHD Date: Sat, 10 Apr 2021 13:44:55 +0200 Subject: [PATCH] Partially fix #142 For better performance we need php extension. (Maybe in 1.3.0) --- .../async/convert/WorldFixTask.php | 65 ++++++------- .../buildertools/editors/Fixer.php | 97 ++++++++++--------- .../schematics/format/MCEditSchematic.php | 8 +- .../format/MCStructureSchematic.php | 6 +- .../buildertools/utils/WorldFixUtil.php | 13 +-- 5 files changed, 82 insertions(+), 107 deletions(-) diff --git a/src/czechpmdevs/buildertools/async/convert/WorldFixTask.php b/src/czechpmdevs/buildertools/async/convert/WorldFixTask.php index 3e4b056..638f672 100644 --- a/src/czechpmdevs/buildertools/async/convert/WorldFixTask.php +++ b/src/czechpmdevs/buildertools/async/convert/WorldFixTask.php @@ -22,6 +22,7 @@ use czechpmdevs\buildertools\editors\Fixer; use Error; +use Generator; use pocketmine\level\format\io\BaseLevelProvider; use pocketmine\level\format\io\LevelProviderManager; use pocketmine\level\format\io\region\Anvil; @@ -29,7 +30,6 @@ use pocketmine\scheduler\AsyncTask; use pocketmine\utils\MainLogger; use function basename; -use function count; use function explode; use function glob; use function is_dir; @@ -40,11 +40,11 @@ class WorldFixTask extends AsyncTask { /** @var string */ public string $worldPath; - - /** @var int */ - public int $percentage = 0; + /** @var string */ public string $error = ""; + /** @var bool */ + public bool $done = false; /** @var float */ public float $time = 0.0; @@ -93,54 +93,44 @@ public function onRun() { return; } - $fixer = new Fixer(); - $startTime = microtime(true); - $chunksToFix = $this->getListOfChunksToFix($this->worldPath); - foreach ($chunksToFix as $index => [$chunkX, $chunkZ]) { - $chunk = $provider->loadChunk($chunkX, $chunkZ); - if($chunk === null) { - continue; - } + $fixer = Fixer::getInstance(); - for($x = 0; $x < 16; ++$x) { - for($z = 0; $z < 16; ++$z) { - for($y = 0; $y < $provider->getWorldHeight(); ++$y) { - if(($id = $chunk->getBlockId($x, $y, $z)) != 0) { - $data = $chunk->getBlockData($x, $y, $z); + $maxY = $provider->getWorldHeight(); + $chunksFixed = 0; - $fixer->fixBlock($id, $data); + foreach ($this->getListOfChunksToFix($this->worldPath) as $chunksToFix) { + foreach ($chunksToFix as [$chunkX, $chunkZ]) { + $chunk = $provider->loadChunk($chunkX, $chunkZ); + if($chunk === null) { + continue; + } - $chunk->setBlockId($x, $y, $z, $id); - $chunk->setBlockData($x, $y, $z, $data); - } - } + if($fixer->convertJavaToBedrockChunk($chunk, $maxY)) { + $provider->saveChunk($chunk); } - } - $this->percentage = (($index + 1) * 100) / count($chunksToFix); + $chunksFixed++; + MainLogger::getLogger()->debug("[BuilderTools] $chunksFixed chunks fixed!"); + if($this->forceStop) { + return; + } + } - $provider->saveChunk($chunk); $provider->doGarbageCollection(); - - MainLogger::getLogger()->debug("[BuilderTools] " . ($index + 1) . "/" . count($chunksToFix) . " chunks fixed!"); - if($this->forceStop) { - return; - } } - $this->chunkCount = count($chunksToFix); $this->time = round(microtime(true) - $startTime); + MainLogger::getLogger()->debug("[BuilderTools] World fixed in $this->time, affected $chunksFixed chunks!"); - $this->percentage = -1; - MainLogger::getLogger()->debug("[BuilderTools] World fixed in " . round(microtime(true)-$startTime) .", affected " .count($chunksToFix). " chunks!"); + $this->done = true; } /** - * @return int[][] + * @phpstan-return Generator */ - private function getListOfChunksToFix(string $worldPath): array { + private function getListOfChunksToFix(string $worldPath): Generator { $regionPath = $worldPath . DIRECTORY_SEPARATOR . "region" . DIRECTORY_SEPARATOR; $files = glob($regionPath . "*.mca*"); @@ -164,8 +154,9 @@ private function getListOfChunksToFix(string $worldPath): array { } } } - } - return $chunks; + yield $chunks; + $chunks = []; + } } } \ No newline at end of file diff --git a/src/czechpmdevs/buildertools/editors/Fixer.php b/src/czechpmdevs/buildertools/editors/Fixer.php index 558cbe0..e09983b 100644 --- a/src/czechpmdevs/buildertools/editors/Fixer.php +++ b/src/czechpmdevs/buildertools/editors/Fixer.php @@ -18,65 +18,66 @@ namespace czechpmdevs\buildertools\editors; -use pocketmine\block\BlockIds; +use pocketmine\level\format\Chunk; +use pocketmine\level\format\EmptySubChunk; +use pocketmine\level\format\SubChunk; use pocketmine\utils\SingletonTrait; -use function is_int; +use function array_key_exists; +use function json_decode; -// TODO - Quartz slabs are wrong class Fixer { use SingletonTrait; - /** @phpstan-var mixed[][] */ - private const BLOCK_FIX_DATA = [ - 125 => [BlockIds::DOUBLE_WOODEN_SLAB, ""], - 126 => [BlockIds::WOODEN_SLAB, ""], - 158 => [BlockIds::WOODEN_SLAB, 0], - 166 => [BlockIds::INVISIBLE_BEDROCK, 0], - 188 => [BlockIds::FENCE, 0], - 189 => [BlockIds::FENCE, 1], - 190 => [BlockIds::FENCE, 2], - 191 => [BlockIds::FENCE, 3], - 192 => [BlockIds::FENCE, 4], - 193 => [BlockIds::FENCE, 5], - 198 => [BlockIds::END_ROD, 0], - 199 => [BlockIds::CHORUS_PLANT, 0], - 202 => [BlockIds::PURPUR_BLOCK, 0], - 204 => [BlockIds::PURPUR_BLOCK, 0], - 208 => [BlockIds::GRASS_PATH, 0], - 251 => [BlockIds::CONCRETE, ""], - 252 => [BlockIds::CONCRETE_POWDER, ""], - 95 => [BlockIds::STAINED_GLASS, ""], - ]; - - public function fixBlock(int &$id, int &$damage): void { - if(isset(self::BLOCK_FIX_DATA[$id])) { - if(is_int(self::BLOCK_FIX_DATA[$id][1])) { - $damage = self::BLOCK_FIX_DATA[$id][1]; - } - $id = self::BLOCK_FIX_DATA[$id][0]; - } + // Script to generate this here: https://gist.github.com/VixikHD/5e7e1874298b548df3d190d95c0ed577 + private const BLOCK_FIX_DATA = '{"2000":2512,"2001":2513,"2002":2514,"2003":2515,"2004":2516,"2005":2517,"2006":2518,"2007":2519,"2008":2520,"2009":2521,"2010":2522,"2011":2523,"2012":2524,"2013":2525,"2014":2526,"2015":2527,"2016":2528,"2017":2529,"2018":2530,"2019":2531,"2020":2532,"2021":2533,"2022":2534,"2023":2535,"2024":2536,"2025":2537,"2026":2538,"2027":2539,"2028":2540,"2029":2541,"2030":2542,"2031":2543,"2528":2528,"2656":1520,"3008":1360,"3024":1361,"3040":1362,"3056":1363,"3072":1364,"3088":1365,"3168":3328,"3184":3840,"3232":3216,"3264":3216,"3328":3168,"4016":3776,"4017":3777,"4018":3778,"4019":3779,"4020":3780,"4021":3781,"4022":3782,"4023":3783,"4024":3784,"4025":3785,"4026":3786,"4027":3787,"4028":3788,"4029":3789,"4030":3790,"4031":3791,"4032":3792,"4033":3793,"4034":3794,"4035":3795,"4036":3796,"4037":3797,"4038":3798,"4039":3799,"4040":3800,"4041":3801,"4042":3802,"4043":3803,"4044":3804,"4045":3805,"4046":3806,"4047":3807,"1520":3856,"1521":3857,"1522":3858,"1523":3859,"1524":3860,"1525":3861,"1526":3862,"1527":3863,"1528":3864,"1529":3865,"1530":3866,"1531":3867,"1532":3868,"1533":3869,"1534":3870,"1535":3871,"1536":1539,"1537":1538,"1538":1537,"1539":1536,"1540":1547,"1541":1546,"1542":1545,"1543":1544,"1544":1543,"1545":1542,"1546":1541,"1547":1540,"1548":1551,"1549":1550,"1550":1549,"1551":1548,"2672":2675,"2673":2674,"2674":2673,"2675":2672,"2676":2683,"2677":2682,"2678":2681,"2679":2680,"2680":2679,"2681":2678,"2682":2677,"2683":2676,"2684":2687,"2685":2686,"2686":2685,"2687":2684,"1232":1232,"1233":1237,"1234":1236,"1235":1235,"1236":1234,"1237":1233,"1238":1232,"1239":1247,"1240":1246,"1241":1245,"1242":1244,"1243":1243,"1244":1232,"1245":1247,"1246":1246,"1247":1245,"2288":2288,"2289":2293,"2290":2292,"2291":2291,"2292":2290,"2293":2289,"2294":2288,"2295":2303,"2296":2302,"2297":2301,"2298":2300,"2299":2299,"2300":2288,"2301":2303,"2302":2302,"2303":2301,"711":710,"719":718,"2896":2497}'; - if($id == BlockIds::TRAPDOOR || $id == BlockIds::IRON_TRAPDOOR) { - $damage = $this->fixTrapdoorMeta($damage); - } + /** @var int[] */ + private array $fullBlockFixData; - if($id == BlockIds::WOODEN_BUTTON || $id == BlockIds::STONE_BUTTON) { - $damage = $this->fixButtonMeta($damage); - } + protected function __construct() { + $this->fullBlockFixData = json_decode(self::BLOCK_FIX_DATA, true); } - private function fixButtonMeta(int $meta): int { - return ((6 - $meta) % 6) & 0xf; + public function convertJavaToBedrockId(int &$fullBlock): bool { + if(!array_key_exists($fullBlock, $this->fullBlockFixData)) { + return false; + } + + $fullBlock = $this->fullBlockFixData[$fullBlock]; + return true; } - private function fixTrapdoorMeta(int $meta): int { - $key = $meta >> 2; - if($key == 0) { - return 3 - $meta; - } elseif($key == 3) { - return 27 - $meta; - } else { - return 15 - $meta; + public function convertJavaToBedrockChunk(Chunk $chunk, int $maxY = 256): bool { + $hasChanged = false; + + /** @var int|null $currentY */ + $currentY = null; + /** @var SubChunk $subChunk */ + $subChunk = null; + + for($y = 0; $y < $maxY; ++$y) { + if($currentY === null || $y >> 4 != $currentY) { + $currentY = $y >> 4; + $subChunk = $chunk->getSubChunk($y >> 4); + + if($subChunk instanceof EmptySubChunk) { + continue; + } + } + + for($x = 0; $x < 16; ++$x) { + for($z = 0; $z < 16; ++$z) { + $fullBlock = $subChunk->getFullBlock($x, $y & 0xf, $z); + if($this->convertJavaToBedrockId($fullBlock)) { + $subChunk->setBlockId($x, $y & 0xf, $z, $fullBlock >> 4); + $subChunk->setBlockData($x, $y & 0xf, $z, $fullBlock & 0xf); + + $hasChanged = true; + } + } + } } + + return $hasChanged; } } diff --git a/src/czechpmdevs/buildertools/schematics/format/MCEditSchematic.php b/src/czechpmdevs/buildertools/schematics/format/MCEditSchematic.php index 5591667..b6818f9 100644 --- a/src/czechpmdevs/buildertools/schematics/format/MCEditSchematic.php +++ b/src/czechpmdevs/buildertools/schematics/format/MCEditSchematic.php @@ -70,12 +70,8 @@ public function load(string $rawData): BlockArray { if($materials == self::MATERIALS_CLASSIC || $materials == self::MATERIALS_ALPHA) { $fixer = Fixer::getInstance(); - foreach ($blockArray->blocks as $i => $fullBlock) { - $id = $fullBlock >> 4; - $meta = $fullBlock & 0xf; - - $fixer->fixBlock($id, $meta); - $blockArray->blocks[$i] = $id << 4 | $meta; + foreach ($blockArray->blocks as &$fullBlock) { + $fixer->convertJavaToBedrockId($fullBlock); } } diff --git a/src/czechpmdevs/buildertools/schematics/format/MCStructureSchematic.php b/src/czechpmdevs/buildertools/schematics/format/MCStructureSchematic.php index e6fb816..5cf463c 100644 --- a/src/czechpmdevs/buildertools/schematics/format/MCStructureSchematic.php +++ b/src/czechpmdevs/buildertools/schematics/format/MCStructureSchematic.php @@ -21,7 +21,6 @@ namespace czechpmdevs\buildertools\schematics\format; use czechpmdevs\buildertools\blockstorage\BlockArray; -use czechpmdevs\buildertools\editors\Fixer; use czechpmdevs\buildertools\schematics\SchematicException; use pocketmine\math\Vector3; use pocketmine\nbt\LittleEndianNBTStream; @@ -67,8 +66,6 @@ public function load(string $rawData): BlockArray { $height = $size->getFloorY(); $length = $size->getFloorZ(); - /** @var Fixer $fixer */ - $fixer = Fixer::getInstance(); $blockArray = new BlockArray(); $i = 0; @@ -79,12 +76,11 @@ public function load(string $rawData): BlockArray { $id = $fullBlockId >> 4; $meta = $fullBlockId & 0xf; - $fixer->fixBlock($id, $meta); if($id > 255 || $id < 0) { $id = 0; } - $blockArray->addBlockAt($x, $y, $z, $fullBlockId >> 4, $fullBlockId & 0xf); + $blockArray->addBlockAt($x, $y, $z, $id, $meta); ++$i; } } diff --git a/src/czechpmdevs/buildertools/utils/WorldFixUtil.php b/src/czechpmdevs/buildertools/utils/WorldFixUtil.php index 2943e87..b8b2eea 100644 --- a/src/czechpmdevs/buildertools/utils/WorldFixUtil.php +++ b/src/czechpmdevs/buildertools/utils/WorldFixUtil.php @@ -71,8 +71,7 @@ public static function fixWorld(CommandSender $sender, string $worldName): void /** @var ClosureTask $task */ $task = null; - $lastPercent = 0; - BuilderTools::getInstance()->getScheduler()->scheduleDelayedRepeatingTask($task = new ClosureTask(function (int $currentTick) use (&$lastPercent, $asyncTask, $sender, &$task): void { + BuilderTools::getInstance()->getScheduler()->scheduleDelayedRepeatingTask($task = new ClosureTask(function (int $currentTick) use ($asyncTask, $sender, &$task): void { if($sender instanceof Player && !$sender->isOnline()) { $asyncTask->forceStop = true; goto finish; @@ -83,15 +82,7 @@ public static function fixWorld(CommandSender $sender, string $worldName): void goto finish; } - if($asyncTask->percentage != $lastPercent) { - if($sender instanceof Player && $asyncTask->percentage > 0) { - $sender->sendTip(BuilderTools::getPrefix() . "§aWorld is fixed from " . $asyncTask->percentage . "%%%"); - } - $lastPercent = $asyncTask->percentage; - return; - } - - if($asyncTask->percentage == -1) { + if($asyncTask->done) { $sender->sendMessage(BuilderTools::getPrefix() . "§aWorld fix task completed in $asyncTask->time ($asyncTask->chunkCount chunks updated)!"); finish: