Skip to content

Commit

Permalink
Partially fix #142
Browse files Browse the repository at this point in the history
For better performance we need php extension. (Maybe in 1.3.0)
  • Loading branch information
VixikHD committed Apr 10, 2021
1 parent 5b0264e commit 901c62b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 107 deletions.
65 changes: 28 additions & 37 deletions src/czechpmdevs/buildertools/async/convert/WorldFixTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@

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;
use pocketmine\level\format\io\region\RegionLoader;
use pocketmine\scheduler\AsyncTask;
use pocketmine\utils\MainLogger;
use function basename;
use function count;
use function explode;
use function glob;
use function is_dir;
Expand All @@ -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;
Expand Down Expand Up @@ -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<int[][]>
*/
private function getListOfChunksToFix(string $worldPath): array {
private function getListOfChunksToFix(string $worldPath): Generator {
$regionPath = $worldPath . DIRECTORY_SEPARATOR . "region" . DIRECTORY_SEPARATOR;

$files = glob($regionPath . "*.mca*");
Expand All @@ -164,8 +154,9 @@ private function getListOfChunksToFix(string $worldPath): array {
}
}
}
}

return $chunks;
yield $chunks;
$chunks = [];
}
}
}
97 changes: 49 additions & 48 deletions src/czechpmdevs/buildertools/editors/Fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
}
Expand Down
13 changes: 2 additions & 11 deletions src/czechpmdevs/buildertools/utils/WorldFixUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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:
Expand Down

0 comments on commit 901c62b

Please sign in to comment.