Skip to content

Instantly share code, notes, and snippets.

@Muqsit
Created September 9, 2023 07:58
Show Gist options
  • Save Muqsit/f98bb998dbc34a34b3a7dd30a04e99fc to your computer and use it in GitHub Desktop.
Save Muqsit/f98bb998dbc34a34b3a7dd30a04e99fc to your computer and use it in GitHub Desktop.
Terraforming regions in PocketMine-MP worlds using Scripter plugin
<?php
declare(strict_types=1);
use cosmicpe\CosmicPE;
use pocketmine\block\Block;
use pocketmine\block\BlockTypeIds;
use pocketmine\block\Liquid;
use pocketmine\block\VanillaBlocks;
use pocketmine\event\EventPriority;
use pocketmine\event\HandlerListManager;
use pocketmine\event\player\PlayerItemUseEvent;
use pocketmine\item\ItemTypeIds;
use pocketmine\math\Facing;
use pocketmine\player\Player;
use pocketmine\Server;
use pocketmine\world\World;
$server = Server::getInstance();
$player = $server->getPlayerExact("NeedleGalaxy");
/** @var CosmicPE $plugin */
$plugin = $server->getPluginManager()->getPlugin("Scripter");
$execute = function(Player $player) : void{
$test_for_top_block = static fn(Block $block) => $block->blocksDirectSkyLight();
$origin = $player->getTargetBlock(100);
if($origin === null){
return;
}
if(!$test_for_top_block($origin)){
$origin = $origin->getSide(Facing::DOWN);
if(!$test_for_top_block($origin)){
return;
}
}
$radius = mt_rand(16, 24);
$position = $origin->getPosition();
$diff = [];
$blocks = [];
for($i = -$radius; $i <= $radius; $i++){
for($j = -$radius; $j <= $radius; $j++){
$column_height = $position->y;
for($y = $column_height; $y > 0; $y--){
if($test_for_top_block($position->world->getBlockAt($position->x + $i, $y, $position->z + $j))){
$column_height = $y;
break;
}
}
$diff[] = $position->y - $column_height;
$blocks[World::blockHash($position->x + $i, 0, $position->z + $j)] = $position->world->getBlockAt($position->x + $i, $y, $position->z + $j);
}
}
// Define a kernel for smoothing. This one is a simple 3x3 averaging kernel.
// Adjust the weights as you like.
$kernel = [
[0.05, 0.1, 0.05],
[0.1, 0.4, 0.1],
[0.05, 0.1, 0.05]
];
$kernelSize = 3;
$halfKernel = intdiv($kernelSize, 2);
// Create a 2D array for $diff for easier access.
$diff2D = [];
for($i = 0; $i < 2 * $radius + 1; $i++){
for($j = 0; $j < 2 * $radius + 1; $j++){
$diff2D[$i][$j] = array_shift($diff);
}
}
$adjustTerrainHeight = function($x, $targetY, $z, $originalDiff) use ($blocks ,$position){
$block = $blocks[World::blockHash($x, 0, $z)];
if($block instanceof Liquid){
return;
}
$currentY = $position->y - $originalDiff;
$world = $position->world;
if($currentY > $targetY){
// Remove blocks to reach targetY
for($y = $currentY; $y > $targetY; $y--){
$world->setBlockAt($x, $y, $z, VanillaBlocks::AIR()); // 0 is usually Air in Minecraft.
if($world->getBlockAt($x, $y - 1, $z)->getTypeId() === BlockTypeIds::DIRT){
$world->setBlockAt($x, $y - 1, $z, VanillaBlocks::GRASS(), false);
}
}
}else if($currentY < $targetY){
// Add blocks to reach targetY, for simplicity let's use Dirt blocks
for($y = $currentY + 1; $y <= $targetY; $y++){
if($block->getTypeId() === BlockTypeIds::DIRT){
$block = VanillaBlocks::GRASS();
}
$world->setBlockAt($x, $y, $z, $block, false);
if($world->getBlockAt($x, $y - 1, $z)->getTypeId() === BlockTypeIds::GRASS){
$world->setBlockAt($x, $y - 1, $z, VanillaBlocks::DIRT(), false);
}
}
}
};
// Smooth using the kernel
for($i = 0; $i < 2 * $radius + 1; $i++){
for($j = 0; $j < 2 * $radius + 1; $j++){
$sum = 0;
$weightSum = 0;
for($ki = -$halfKernel; $ki <= $halfKernel; $ki++){
for($kj = -$halfKernel; $kj <= $halfKernel; $kj++){
if(isset($diff2D[$i + $ki][$j + $kj])){
$sum += $kernel[$ki + $halfKernel][$kj + $halfKernel] * $diff2D[$i + $ki][$j + $kj];
$weightSum += $kernel[$ki + $halfKernel][$kj + $halfKernel];
}
}
}
$smoothedValue = $sum / $weightSum;
// Adjust the block heights in the region based on the smoothed value.
$adjustedHeight = $position->y - $smoothedValue;
$adjustTerrainHeight($position->x + $i - $radius, $adjustedHeight, $position->z + $j - $radius, $diff2D[$i][$j]);
}
}
};
$listener = $plugin->getServer()->getPluginManager()->registerEvent(PlayerItemUseEvent::class, function(PlayerItemUseEvent $event) use(&$listener, $player, $execute) : void{
if($event->getPlayer() === $player){
if($event->getItem()->getTypeId() !== ItemTypeIds::STICK){
HandlerListManager::global()->getListFor(PlayerItemUseEvent::class)->unregister($listener);
return;
}
$execute($player);
}
}, EventPriority::NORMAL, $plugin);
@Muqsit
Copy link
Author

Muqsit commented Sep 9, 2023

This is a script that can be executed using Scripter.

  1. Rename NeedleGalaxy to your gamertag
  2. Run /script test.php
  3. Hold a stick in your hand
  4. Right-click the stick on the topmost block in the region you'd like to terraform
  5. The region should now be terraformed. Right click again if you'd like to terraform it further.
  6. To exit, give yourself any item other than a stick and right-click it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment