mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Added a tool to visualise behaviour of ChunkSelector
I actually intended to write a tool for debugging generation, but it turns out this, as an intermediary step, is also useful and a whole bunch of fun to play with.
This commit is contained in:
parent
2405e45b35
commit
4f8501ff34
171
tools/simulate-chunk-selector.php
Normal file
171
tools/simulate-chunk-selector.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\tools\simulate_chunk_selector;
|
||||
|
||||
use pocketmine\player\ChunkSelector;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\World;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function fwrite;
|
||||
use function getopt;
|
||||
use function imagearc;
|
||||
use function imagecolorallocate;
|
||||
use function imagecreatetruecolor;
|
||||
use function imagefill;
|
||||
use function imagefilledrectangle;
|
||||
use function imagepng;
|
||||
use function imagerectangle;
|
||||
use function imagesavealpha;
|
||||
use function is_dir;
|
||||
use function is_string;
|
||||
use function mkdir;
|
||||
use function realpath;
|
||||
use function scandir;
|
||||
use function str_pad;
|
||||
use function strval;
|
||||
use const SCANDIR_SORT_NONE;
|
||||
use const STDERR;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
function newImage(int $scale, int $radius) : \GdImage{
|
||||
$image = imagecreatetruecolor($scale * $radius * 2, $scale * $radius * 2);
|
||||
if($image === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
imagesavealpha($image, true);
|
||||
|
||||
$black = imagecolorallocate($image, 0, 0, 0);
|
||||
if($black === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
imagefill($image, 0, 0, $black);
|
||||
return $image;
|
||||
}
|
||||
|
||||
function render(int $radius, int $baseX, int $baseZ, int $chunksPerStep, int $scale, \GdImage $image, int $chunkColor, int $offsetX, int $offsetZ, string $outputFolder) : void{
|
||||
echo "Render start: radius $radius, chunks per step $chunksPerStep\n";
|
||||
$iterator = (new ChunkSelector())->selectChunks($radius, $baseX, $baseZ);
|
||||
|
||||
$middleOffsetX = $scale * ($radius + $offsetX);
|
||||
$middleOffsetZ = $scale * ($radius + $offsetZ);
|
||||
|
||||
$frame = 0;
|
||||
$seen = [];
|
||||
while($iterator->valid()){
|
||||
$frame++;
|
||||
|
||||
$black = imagecolorallocate($image, 0, 0, 0);
|
||||
$yellow = imagecolorallocate($image, 255, 255, 51);
|
||||
$red = imagecolorallocate($image, 255, 0, 0);
|
||||
if($black === false || $yellow === false || $red === false) throw new AssumptionFailedError();
|
||||
|
||||
for($i = 0; $i < $chunksPerStep; ++$i){
|
||||
$chunkHash = $iterator->current();
|
||||
if(!isset($seen[$chunkHash])){
|
||||
$color = $chunkColor;
|
||||
$seen[$chunkHash] = true;
|
||||
}else{
|
||||
$color = $yellow;
|
||||
}
|
||||
World::getXZ($chunkHash, $chunkX, $chunkZ);
|
||||
$imageX = $middleOffsetX + (($chunkX - $baseX) * $scale);
|
||||
$imageZ = $middleOffsetZ + (($chunkZ - $baseZ) * $scale);
|
||||
|
||||
imagefilledrectangle($image, $imageX, $imageZ, $imageX + $scale, $imageZ + $scale, $color);
|
||||
imagerectangle($image, $imageX, $imageZ, $imageX + $scale, $imageZ + $scale, $black);
|
||||
|
||||
$iterator->next();
|
||||
if(!$iterator->valid()){
|
||||
break;
|
||||
}
|
||||
}
|
||||
imagearc($image, $middleOffsetX, $middleOffsetZ, $radius * $scale * 2, $radius * $scale * 2, 0, 360, $red);
|
||||
|
||||
imagepng($image, Path::join($outputFolder, "frame" . str_pad(strval($frame), 5, "0", STR_PAD_LEFT) . ".png"));
|
||||
echo "\rRendered step $frame";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
$radius = null;
|
||||
$baseX = 10000 >> Chunk::COORD_BIT_SIZE;
|
||||
$baseZ = 10000 >> Chunk::COORD_BIT_SIZE;
|
||||
|
||||
$nChunksPerStep = 32;
|
||||
$scale = 10;
|
||||
|
||||
if(count(getopt("", ["help"])) !== 0){
|
||||
echo "Required parameters:\n";
|
||||
echo "--output=path/to/dir: Output folder to put the generated images into (will attempt to create if it doesn't exist)\n";
|
||||
echo "--radius=N: Radius of chunks to render (default $radius)\n";
|
||||
echo "\n";
|
||||
echo "Optional parameters:\n";
|
||||
echo "--baseX=N: Base X coordinate to use for simulation (default $baseX\n";
|
||||
echo "--baseZ=N: Base Z coordinate to use for simulation (default $baseZ)\n";
|
||||
echo "--scale=N: Height/width of square of pixels to use for each chunk (default $scale)\n";
|
||||
echo "--chunksPerStep=N: Number of chunks to process in each frame (default $nChunksPerStep)\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
foreach(getopt("", ["radius:", "baseX:", "baseZ:", "scale:", "chunksPerStep:"]) as $name => $value){
|
||||
if(!is_string($value) || (string) ((int) $value) !== $value){
|
||||
fwrite(STDERR, "Value for --$name must be an integer\n");
|
||||
exit(1);
|
||||
}
|
||||
$value = (int) $value;
|
||||
match($name){
|
||||
"radius" => ($radius = $value),
|
||||
"baseX" => ($baseX = $value),
|
||||
"baseZ" => ($baseZ = $value),
|
||||
"scale" => ($scale = $value),
|
||||
"chunksPerStep" => ($nChunksPerStep = $value),
|
||||
default => throw new AssumptionFailedError("getopt() returned unknown option")
|
||||
};
|
||||
}
|
||||
if($radius === null){
|
||||
fwrite(STDERR, "Please specify a radius using --radius\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$outputDirectory = null;
|
||||
foreach(getopt("", ["output:"]) as $name => $value){
|
||||
assert($name === "output");
|
||||
if(!is_string($value)){
|
||||
fwrite(STDERR, "Value for --$name must be a string\n");
|
||||
exit(1);
|
||||
}
|
||||
if(!@mkdir($value) && !is_dir($value)){
|
||||
fwrite(STDERR, "Output directory $value could not be created\n");
|
||||
exit(1);
|
||||
}
|
||||
$files = scandir($value, SCANDIR_SORT_NONE);
|
||||
if($files !== false && count($files) > 2){ //always returns . and ..
|
||||
fwrite(STDERR, "Output directory $value is not empty\n");
|
||||
exit(1);
|
||||
}
|
||||
$realPath = realpath($value);
|
||||
if($realPath === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
$outputDirectory = $realPath;
|
||||
}
|
||||
if($outputDirectory === null){
|
||||
fwrite(STDERR, "Please specify an output directory using --output\n");
|
||||
exit(1);
|
||||
}
|
||||
$image = newImage($scale, $radius);
|
||||
|
||||
$black = imagecolorallocate($image, 0, 0, 0);
|
||||
$green = imagecolorallocate($image, 0, 220, 0);
|
||||
if($black === false || $green === false){
|
||||
throw new AssumptionFailedError();
|
||||
}
|
||||
render($radius, $baseX, $baseZ, $nChunksPerStep, $scale, $image, $green, 0, 0, $outputDirectory);
|
Loading…
x
Reference in New Issue
Block a user