Generator works!

This commit is contained in:
Shoghi Cervantes 2015-03-15 16:40:18 +01:00
parent 5bfc747622
commit d5601b0c9f
No known key found for this signature in database
GPG Key ID: 78464DB0A7837F89
19 changed files with 317 additions and 182 deletions

View File

@ -71,6 +71,7 @@ use pocketmine\inventory\StonecutterShapelessRecipe;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\format\FullChunk; use pocketmine\level\format\FullChunk;
use pocketmine\level\format\LevelProvider; use pocketmine\level\format\LevelProvider;
use pocketmine\level\generator\biome\Biome;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\level\Location; use pocketmine\level\Location;
use pocketmine\level\Position; use pocketmine\level\Position;
@ -619,8 +620,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$Z = null; $Z = null;
Level::getXZ($index, $X, $Z); Level::getXZ($index, $X, $Z);
if(!$this->level->isChunkPopulated($X, $Z)){ if(!$this->level->populateChunk($X, $Z)){
$this->level->generateChunk($X, $Z);
if($this->spawned){ if($this->spawned){
continue; continue;
}else{ }else{
@ -628,6 +628,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
} }
} }
++$count; ++$count;
unset($this->loadQueue[$index]); unset($this->loadQueue[$index]);
@ -1251,6 +1252,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->entityBaseTick(1); $this->entityBaseTick(1);
if($currentTick % 80 === 0) $this->sendMessage(TextFormat::ITALIC . TextFormat::GRAY . "Y: ".round($this->y)."; biome: ". Biome::getBiome($this->level->getBiomeId((int) $this->x, (int) $this->z))->getName());
if($this->speed and $this->isSurvival()){ if($this->speed and $this->isSurvival()){
$speed = sqrt($this->speed->x ** 2 + $this->speed->z ** 2); $speed = sqrt($this->speed->x ** 2 + $this->speed->z ** 2);
if($speed > 0.45){ if($speed > 0.45){

View File

@ -21,6 +21,8 @@
namespace pocketmine\level; namespace pocketmine\level;
use pocketmine\level\format\FullChunk;
interface ChunkManager{ interface ChunkManager{
/** /**
* Gets the raw block id. * Gets the raw block id.
@ -63,4 +65,26 @@ interface ChunkManager{
* @param int $data 0-15 * @param int $data 0-15
*/ */
public function setBlockDataAt($x, $y, $z, $data); public function setBlockDataAt($x, $y, $z, $data);
/**
* @param int $chunkX
* @param int $chunkZ
*
* @return FullChunk
*/
public function getChunk($chunkX, $chunkZ);
/**
* @param int $chunkX
* @param int $chunkZ
* @param FullChunk $chunk
*/
public function setChunk($chunkX, $chunkZ, FullChunk $chunk);
/**
* Gets the level seed
*
* @return int
*/
public function getSeed();
} }

View File

@ -93,6 +93,7 @@ use pocketmine\tile\Tile;
use pocketmine\utils\Cache; use pocketmine\utils\Cache;
use pocketmine\utils\LevelException; use pocketmine\utils\LevelException;
use pocketmine\utils\MainLogger; use pocketmine\utils\MainLogger;
use pocketmine\utils\Random;
use pocketmine\utils\ReversePriorityQueue; use pocketmine\utils\ReversePriorityQueue;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use pocketmine\level\particle\Particle; use pocketmine\level\particle\Particle;
@ -191,6 +192,8 @@ class Level implements ChunkManager, Metadatable{
protected $chunkTickRadius; protected $chunkTickRadius;
protected $chunkTickList = []; protected $chunkTickList = [];
protected $chunksPerTick; protected $chunksPerTick;
protected $chunksPopulatedPerTick;
protected $chunksPopulated = 0;
protected $clearChunksOnTick; protected $clearChunksOnTick;
protected $randomTickBlocks = [ protected $randomTickBlocks = [
Block::GRASS => Grass::class, Block::GRASS => Grass::class,
@ -219,7 +222,10 @@ class Level implements ChunkManager, Metadatable{
/** @var LevelTimings */ /** @var LevelTimings */
public $timings; public $timings;
/** @var Generator */
protected $generator; protected $generator;
/** @var Generator */
protected $generatorInstance;
/** /**
* Returns the chunk unique hash/key * Returns the chunk unique hash/key
@ -298,6 +304,7 @@ class Level implements ChunkManager, Metadatable{
$this->chunkTickRadius = min($this->server->getViewDistance(), max(1, (int) $this->server->getProperty("chunk-ticking.tick-radius", 4))); $this->chunkTickRadius = min($this->server->getViewDistance(), max(1, (int) $this->server->getProperty("chunk-ticking.tick-radius", 4)));
$this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 260); $this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 260);
$this->chunksPopulatedPerTick = (int) $this->server->getProperty("chunk-generation.populations-per-tick", 1);
$this->chunkTickList = []; $this->chunkTickList = [];
$this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", false); $this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", false);
@ -308,6 +315,9 @@ class Level implements ChunkManager, Metadatable{
public function initLevel(){ public function initLevel(){
$this->server->getGenerationManager()->openLevel($this, $this->generator, $this->provider->getGeneratorOptions()); $this->server->getGenerationManager()->openLevel($this, $this->generator, $this->provider->getGeneratorOptions());
$generator = $this->generator;
$this->generatorInstance = new $generator($this->provider->getGeneratorOptions());
$this->generatorInstance->init($this, new Random($this->getSeed()));
} }
/** /**
@ -592,6 +602,8 @@ class Level implements ChunkManager, Metadatable{
$this->processChunkRequest(); $this->processChunkRequest();
$this->chunksPopulated = 0;
$this->timings->doTick->stopTiming(); $this->timings->doTick->stopTiming();
} }
@ -2275,6 +2287,32 @@ class Level implements ChunkManager, Metadatable{
} }
public function populateChunk($x, $z){
if(!$this->isChunkPopulated($x, $z)){
$populate = true;
for($xx = -1; $xx <= 1; ++$xx){
for($zz = -1; $zz <= 1; ++$zz){
if(!$this->isChunkGenerated($x + $xx, $z + $zz)){
$populate = false;
$this->generateChunk($x + $xx, $z + $zz);
}
}
}
if($this->chunksPopulated < $this->chunksPopulatedPerTick and $populate){
$this->generatorInstance->populateChunk($x, $z);
$chunk = $this->getChunk($x, $z);
$chunk->setPopulated(true);
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
++$this->chunksPopulated;
return true;
}
return false;
}
return true;
}
public function generateChunk($x, $z){ public function generateChunk($x, $z){
if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){ if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){
$this->chunkGenerationQueue[$index] = true; $this->chunkGenerationQueue[$index] = true;

View File

@ -295,13 +295,9 @@ class McRegion extends BaseLevelProvider{
} }
protected function loadRegion($x, $z){ protected function loadRegion($x, $z){
if(isset($this->regions[$index = Level::chunkHash($x, $z)])){ if(!isset($this->regions[$index = Level::chunkHash($x, $z)])){
return true; $this->regions[$index] = new RegionLoader($this, $x, $z);
} }
$this->regions[$index] = new RegionLoader($this, $x, $z);
return true;
} }
public function close(){ public function close(){

View File

@ -30,6 +30,7 @@ use pocketmine\block\IronOre;
use pocketmine\block\LapisOre; use pocketmine\block\LapisOre;
use pocketmine\block\RedstoneOre; use pocketmine\block\RedstoneOre;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\ChunkManager;
use pocketmine\level\format\FullChunk; use pocketmine\level\format\FullChunk;
use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Ore;
use pocketmine\level\generator\populator\Populator; use pocketmine\level\generator\populator\Populator;
@ -37,7 +38,7 @@ use pocketmine\math\Vector3;
use pocketmine\utils\Random; use pocketmine\utils\Random;
class Flat extends Generator{ class Flat extends Generator{
/** @var GenerationChunkManager */ /** @var ChunkManager */
private $level; private $level;
/** @var FullChunk */ /** @var FullChunk */
private $chunk; private $chunk;
@ -135,7 +136,7 @@ class Flat extends Generator{
} }
} }
public function init(GenerationChunkManager $level, Random $random){ public function init(ChunkManager $level, Random $random){
$this->level = $level; $this->level = $level;
$this->random = $random; $this->random = $random;

View File

@ -42,8 +42,6 @@ class GenerationChunkManager implements ChunkManager{
protected $seed; protected $seed;
protected $changes = [];
public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){ public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){
if(!class_exists($class, true) or !is_subclass_of($class, Generator::class)){ if(!class_exists($class, true) or !is_subclass_of($class, Generator::class)){
throw new \InvalidStateException("Class $class does not exists or is not a subclass of Noise"); throw new \InvalidStateException("Class $class does not exists or is not a subclass of Noise");
@ -88,33 +86,8 @@ class GenerationChunkManager implements ChunkManager{
return $chunk; return $chunk;
} }
/**
* @return FullChunk[]
*/
public function getChangedChunks(){
return $this->changes;
}
public function cleanChangedChunks(){
$this->changes = [];
}
public function cleanChangedChunk($index){ public function cleanChangedChunk($index){
unset($this->changes[$index]); unset($this->chunks[$index]);
}
public function doGarbageCollection(){
$count = 0;
foreach($this->chunks as $index => $chunk){
if(!isset($this->changes[$index]) or $chunk->isPopulated()){
unset($this->chunks[$index]);
unset($this->changes[$index]);
++$count;
}
}
return $count;
} }
public function generateChunk($chunkX, $chunkZ){ public function generateChunk($chunkX, $chunkZ){
@ -163,7 +136,6 @@ class GenerationChunkManager implements ChunkManager{
try{ try{
$chunk = $this->getChunk($chunkX, $chunkZ); $chunk = $this->getChunk($chunkX, $chunkZ);
$chunk->setGenerated(true); $chunk->setGenerated(true);
$this->changes[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
}catch(\Exception $e){ }catch(\Exception $e){
} }
} }
@ -172,7 +144,6 @@ class GenerationChunkManager implements ChunkManager{
try{ try{
$chunk = $this->getChunk($chunkX, $chunkZ); $chunk = $this->getChunk($chunkX, $chunkZ);
$chunk->setPopulated(true); $chunk->setPopulated(true);
$this->changes[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
}catch(\Exception $e){ }catch(\Exception $e){
} }
} }
@ -191,10 +162,6 @@ class GenerationChunkManager implements ChunkManager{
*/ */
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
$this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk; $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk;
$this->changes[$index] = $chunk;
if($chunk->isPopulated()){
//TODO: Queue to be sent
}
} }
/** /**

View File

@ -72,15 +72,10 @@ class GenerationLevelManager extends GenerationManager{
public function generateChunk($levelID, $chunkX, $chunkZ){ public function generateChunk($levelID, $chunkX, $chunkZ){
if(isset($this->levels[$levelID])){ if(isset($this->levels[$levelID])){
$this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly $this->levels[$levelID]->generateChunk($chunkX, $chunkZ); //Request population directly
if(isset($this->levels[$levelID])){ if(isset($this->levels[$levelID])){
foreach($this->levels[$levelID]->getChangedChunks() as $index => $chunk){ $this->sendChunk($levelID, $this->levels[$levelID]->getChunk($chunkX, $chunkZ));
$this->sendChunk($levelID, $chunk); $this->levels[$levelID]->cleanChangedChunk(Level::chunkHash($chunkX, $chunkZ));
$this->levels[$levelID]->cleanChangedChunk($index);
}
$this->levels[$levelID]->doGarbageCollection();
$this->levels[$levelID]->cleanChangedChunks();
} }
} }
} }

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\generator; namespace pocketmine\level\generator;
use pocketmine\block\Block;
use pocketmine\level\format\FullChunk; use pocketmine\level\format\FullChunk;
use pocketmine\level\generator\biome\Biome; use pocketmine\level\generator\biome\Biome;
use pocketmine\level\Level; use pocketmine\level\Level;
@ -111,6 +112,7 @@ class GenerationManager{
$this->logger = $logger; $this->logger = $logger;
$this->loader = $loader; $this->loader = $loader;
$chunkX = $chunkZ = null; $chunkX = $chunkZ = null;
Block::init();
Biome::init(); Biome::init();
while($this->shutdown !== true){ while($this->shutdown !== true){
@ -130,7 +132,7 @@ class GenerationManager{
$this->readPacket(); $this->readPacket();
} }
}catch(\Exception $e){ }catch(\Exception $e){
$this->logger->warning("[Noise Thread] Exception: " . $e->getMessage() . " on file \"" . $e->getFile() . "\" line " . $e->getLine()); $this->logger->warning("[Generation Thread] Exception: " . $e->getMessage() . " on file \"" . $e->getFile() . "\" line " . $e->getLine());
} }
} }
} }
@ -143,17 +145,10 @@ class GenerationManager{
protected function generateChunk($levelID, $chunkX, $chunkZ){ protected function generateChunk($levelID, $chunkX, $chunkZ){
if(isset($this->levels[$levelID])){ if(isset($this->levels[$levelID])){
$this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly $this->levels[$levelID]->generateChunk($chunkX, $chunkZ);
if(isset($this->levels[$levelID])){ if(isset($this->levels[$levelID])){
foreach($this->levels[$levelID]->getChangedChunks() as $index => $chunk){ $this->sendChunk($levelID, $this->levels[$levelID]->getChunk($chunkX, $chunkZ));
if($chunk->isPopulated()){ $this->levels[$levelID]->cleanChangedChunk(Level::chunkHash($chunkX, $chunkZ));
$this->sendChunk($levelID, $chunk);
$this->levels[$levelID]->cleanChangedChunk($index);
}
}
$this->levels[$levelID]->doGarbageCollection();
$this->levels[$levelID]->cleanChangedChunks();
} }
} }
} }

View File

@ -24,6 +24,7 @@
*/ */
namespace pocketmine\level\generator; namespace pocketmine\level\generator;
use pocketmine\level\ChunkManager;
use pocketmine\level\generator\noise\Noise; use pocketmine\level\generator\noise\Noise;
use pocketmine\level\generator\normal\Normal; use pocketmine\level\generator\normal\Normal;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -41,6 +42,11 @@ abstract class Generator{
return false; return false;
} }
/**
* @param $name
*
* @return Generator
*/
public static function getGenerator($name){ public static function getGenerator($name){
if(isset(Generator::$list[$name = strtolower($name)])){ if(isset(Generator::$list[$name = strtolower($name)])){
return Generator::$list[$name]; return Generator::$list[$name];
@ -229,7 +235,7 @@ abstract class Generator{
public abstract function __construct(array $settings = []); public abstract function __construct(array $settings = []);
public abstract function init(GenerationChunkManager $level, Random $random); public abstract function init(ChunkManager $level, Random $random);
public abstract function generateChunk($chunkX, $chunkZ); public abstract function generateChunk($chunkX, $chunkZ);

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\generator\biome; namespace pocketmine\level\generator\biome;
use pocketmine\block\Block;
use pocketmine\level\ChunkManager; use pocketmine\level\ChunkManager;
use pocketmine\level\generator\normal\biome\BeachBiome; use pocketmine\level\generator\normal\biome\BeachBiome;
use pocketmine\level\generator\normal\biome\DesertBiome; use pocketmine\level\generator\normal\biome\DesertBiome;
@ -58,6 +59,11 @@ abstract class Biome{
/** @var Populator[] */ /** @var Populator[] */
private $populators = []; private $populators = [];
private $minElevation;
private $maxElevation;
private $groundCover = [];
protected static function register($id, Biome $biome){ protected static function register($id, Biome $biome){
self::$biomes[(int) $id] = $biome; self::$biomes[(int) $id] = $biome;
$biome->setId((int) $id); $biome->setId((int) $id);
@ -116,4 +122,31 @@ abstract class Biome{
} }
public abstract function getName(); public abstract function getName();
public function getMinElevation(){
return $this->minElevation;
}
public function getMaxElevation(){
return $this->maxElevation;
}
public function setElevation($min, $max){
$this->minElevation = $min;
$this->maxElevation = $max;
}
/**
* @return Block[]
*/
public function getGroundCover(){
return $this->groundCover;
}
/**
* @param Block[] $covers
*/
public function setGroundCover(array $covers){
$this->groundCover = $covers;
}
} }

View File

@ -42,7 +42,7 @@ class BiomeSelector{
public function __construct(Random $random, Biome $fallback){ public function __construct(Random $random, Biome $fallback){
$this->fallback = $fallback; $this->fallback = $fallback;
$this->temperature = new Simplex($random, 1, 0.004, 0.5, 2); $this->temperature = new Simplex($random, 1, 0.004, 0.5, 2);
$this->rainfall = new Simplex($random, 1, 0.004, 0.5, 2); $this->rainfall = new Simplex($random, 2, 0.004, 0.5, 2);
} }
public function addBiome(Biome $biome, $start, $end){ public function addBiome(Biome $biome, $start, $end){
@ -58,8 +58,6 @@ class BiomeSelector{
*/ */
public function pickBiome($x, $z){ public function pickBiome($x, $z){
return Biome::getBiome(Biome::PLAINS);
//$temperature = $this->temperature->noise2D($x, $z); //$temperature = $this->temperature->noise2D($x, $z);
$rainfall = $this->rainfall->noise2D($x, $z); $rainfall = $this->rainfall->noise2D($x, $z);
@ -70,11 +68,15 @@ class BiomeSelector{
}elseif($rainfall > 0.6){ }elseif($rainfall > 0.6){
return Biome::getBiome(Biome::BEACH); return Biome::getBiome(Biome::BEACH);
}elseif($rainfall > 0.2){ }elseif($rainfall > 0.2){
return Biome::getBiome(Biome::PLAINS);
}elseif($rainfall > -0.3){
return Biome::getBiome(Biome::FOREST); return Biome::getBiome(Biome::FOREST);
}else{ }elseif($rainfall > -0.3){
return Biome::getBiome(Biome::PLAINS);
}elseif($rainfall > -0.6){
return Biome::getBiome(Biome::DESERT); return Biome::getBiome(Biome::DESERT);
}elseif($rainfall > -0.7){
return Biome::getBiome(Biome::BEACH);
}else{
return Biome::getBiome(Biome::OCEAN);
} }
} }

View File

@ -64,9 +64,9 @@ class Perlin extends Noise{
$y += $this->offsetY; $y += $this->offsetY;
$z += $this->offsetZ; $z += $this->offsetZ;
$floorX = (int) floor($x); $floorX = (int) $x;
$floorY = (int) floor($y); $floorY = (int) $y;
$floorZ = (int) floor($z); $floorZ = (int) $z;
$X = $floorX & 0xFF; $X = $floorX & 0xFF;
$Y = $floorY & 0xFF; $Y = $floorY & 0xFF;
@ -77,26 +77,70 @@ class Perlin extends Noise{
$z -= $floorZ; $z -= $floorZ;
//Fade curves //Fade curves
$fX = self::fade($x); //$fX = self::fade($x);
$fY = self::fade($y); //$fY = self::fade($y);
$fZ = self::fade($z); //$fZ = self::fade($z);
$fX = $x ** 3 * ($x * ($x * 6 - 15) + 10);
$fY = $y ** 3 * ($y * ($y * 6 - 15) + 10);
$fZ = $z ** 3 * ($z * ($z * 6 - 15) + 10);
//Cube corners //Cube corners
$A = $this->perm[$X] + $Y; $A = $this->perm[$X] + $Y;
$B = $this->perm[$X + 1] + $Y;
$AA = $this->perm[$A] + $Z; $AA = $this->perm[$A] + $Z;
$AB = $this->perm[$A + 1] + $Z; $AB = $this->perm[$A + 1] + $Z;
$B = $this->perm[$X + 1] + $Y;
$BA = $this->perm[$B] + $Z; $BA = $this->perm[$B] + $Z;
$BB = $this->perm[$B + 1] + $Z; $BB = $this->perm[$B + 1] + $Z;
return self::lerp($fZ, self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA], $x, $y, $z), $AA1 = self::grad($this->perm[$AA], $x, $y, $z);
self::grad($this->perm[$BA], $x - 1, $y, $z)), $BA1 = self::grad($this->perm[$BA], $x - 1, $y, $z);
self::lerp($fX, self::grad($this->perm[$AB], $x, $y - 1, $z), $AB1 = self::grad($this->perm[$AB], $x, $y - 1, $z);
self::grad($this->perm[$BB], $x - 1, $y - 1, $z))), $BB1 = self::grad($this->perm[$BB], $x - 1, $y - 1, $z);
self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA + 1], $x, $y, $z - 1), $AA2 = self::grad($this->perm[$AA + 1], $x, $y, $z - 1);
self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1)), $BA2 = self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1);
self::lerp($fX, self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1), $AB2 = self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1);
self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1)))); $BB2 = self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1);
$xLerp11 = $AA1 + $fX * ($BA1 - $AA1);
$zLerp1 = $xLerp11 + $fY * ($AB1 + $fX * ($BB1 - $AB1) - $xLerp11);
$xLerp21 = $AA2 + $fX * ($BA2 - $AA2);
return $zLerp1 + $fZ * ($xLerp21 + $fY * ($AB2 + $fX * ($BB2 - $AB2) - $xLerp21) - $zLerp1);
/*
return self::lerp(
$fZ,
self::lerp(
$fY,
self::lerp(
$fX,
self::grad($this->perm[$AA], $x, $y, $z),
self::grad($this->perm[$BA], $x - 1, $y, $z)
),
self::lerp(
$fX,
self::grad($this->perm[$AB], $x, $y - 1, $z),
self::grad($this->perm[$BB], $x - 1, $y - 1, $z)
)
),
self::lerp(
$fY,
self::lerp(
$fX,
self::grad($this->perm[$AA + 1], $x, $y, $z - 1),
self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1)
),
self::lerp(
$fX,
self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1),
self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1)
)
)
);
*/
} }
public function getNoise2D($x, $y){ public function getNoise2D($x, $y){

View File

@ -99,16 +99,14 @@ class Simplex extends Perlin{
// Skew the input space to determine which simplex cell we're in // Skew the input space to determine which simplex cell we're in
$s = ($x + $y + $z) * self::$F3; // Very nice and simple skew factor for 3D $s = ($x + $y + $z) * self::$F3; // Very nice and simple skew factor for 3D
$i = (int) floor($x + $s); $i = (int) ($x + $s);
$j = (int) floor($y + $s); $j = (int) ($y + $s);
$k = (int) floor($z + $s); $k = (int) ($z + $s);
$t = ($i + $j + $k) * self::$G3; $t = ($i + $j + $k) * self::$G3;
$X0 = $i - $t; // Unskew the cell origin back to (x,y,z) space // Unskew the cell origin back to (x,y,z) space
$Y0 = $j - $t; $x0 = $x - ($i - $t); // The x,y,z distances from the cell origin
$Z0 = $k - $t; $y0 = $y - ($j - $t);
$x0 = $x - $X0; // The x,y,z distances from the cell origin $z0 = $z - ($k - $t);
$y0 = $y - $Y0;
$z0 = $z - $Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron. // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
@ -185,47 +183,37 @@ class Simplex extends Perlin{
$ii = $i & 255; $ii = $i & 255;
$jj = $j & 255; $jj = $j & 255;
$kk = $k & 255; $kk = $k & 255;
$gi0 = $this->perm[$ii + $this->perm[$jj + $this->perm[$kk]]] % 12;
$gi1 = $this->perm[$ii + $i1 + $this->perm[$jj + $j1 + $this->perm[$kk + $k1]]] % 12; $n = 0;
$gi2 = $this->perm[$ii + $i2 + $this->perm[$jj + $j2 + $this->perm[$kk + $k2]]] % 12;
$gi3 = $this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12;
// Calculate the contribution from the four corners // Calculate the contribution from the four corners
$t0 = 0.6 - $x0 ** 2 - $y0 ** 2 - $z0 ** 2; $t0 = 0.6 - $x0 ** 2 - $y0 ** 2 - $z0 ** 2;
if($t0 < 0){ if($t0 > 0){
$n0 = 0.0; $gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj + $this->perm[$kk]]] % 12];
}else{ $n += $t0 ** 4 * ($gi0[0] * $x0 + $gi0[1] * $y0 + $gi0[2] * $z0);
$t0 **= 2;
$n0 = $t0 ** 2 * self::dot3D(self::$grad3[$gi0], $x0, $y0, $z0);
} }
$t1 = 0.6 - $x1 ** 2 - $y1 ** 2 - $z1 ** 2; $t1 = 0.6 - $x1 ** 2 - $y1 ** 2 - $z1 ** 2;
if($t1 < 0){ if($t1 > 0){
$n1 = 0.0; $gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1 + $this->perm[$kk + $k1]]] % 12];
}else{ $n += $t1 ** 4 * ($gi1[0] * $x1 + $gi1[1] * $y1 + $gi1[2] * $z1);
$t1 **= 2;
$n1 = $t1 ** 2 * self::dot3D(self::$grad3[$gi1], $x1, $y1, $z1);
} }
$t2 = 0.6 - $x2 ** 2 - $y2 ** 2 - $z2 ** 2; $t2 = 0.6 - $x2 ** 2 - $y2 ** 2 - $z2 ** 2;
if($t2 < 0){ if($t2 > 0){
$n2 = 0.0; $gi2 = self::$grad3[$this->perm[$ii + $i2 + $this->perm[$jj + $j2 + $this->perm[$kk + $k2]]] % 12];
}else{ $n += $t2 ** 4 * ($gi2[0] * $x2 + $gi2[1] * $y2 + $gi2[2] * $z2);
$t2 **= 2;
$n2 = $t2 ** 2 * self::dot3D(self::$grad3[$gi2], $x2, $y2, $z2);
} }
$t3 = 0.6 - $x3 ** 2 - $y3 ** 2 - $z3 ** 2; $t3 = 0.6 - $x3 ** 2 - $y3 ** 2 - $z3 ** 2;
if($t3 < 0){ if($t3 > 0){
$n3 = 0.0; $gi3 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12];
}else{ $n += $t3 ** 4 * ($gi3[0] * $x3 + $gi3[1] * $y3 + $gi3[2] * $z3);
$t3 **= 2;
$n3 = $t3 ** 2 * self::dot3D(self::$grad3[$gi3], $x3, $y3, $z3);
} }
// Add contributions from each corner to get the noise value. // Add contributions from each corner to get the noise value.
// The result is scaled to stay just inside [-1,1] // The result is scaled to stay just inside [-1,1]
return 32.0 * ($n0 + $n1 + $n2 + $n3); return 32.0 * $n;
} }
public function getNoise2D($x, $y){ public function getNoise2D($x, $y){
@ -234,13 +222,12 @@ class Simplex extends Perlin{
// Skew the input space to determine which simplex cell we're in // Skew the input space to determine which simplex cell we're in
$s = ($x + $y) * self::$F2; // Hairy factor for 2D $s = ($x + $y) * self::$F2; // Hairy factor for 2D
$i = (int) floor($x + $s); $i = (int) ($x + $s);
$j = (int) floor($y + $s); $j = (int) ($y + $s);
$t = ($i + $j) * self::$G2; $t = ($i + $j) * self::$G2;
$X0 = $i - $t; // Unskew the cell origin back to (x,y) space // Unskew the cell origin back to (x,y) space
$Y0 = $j - $t; $x0 = $x - ($i - $t); // The x,y distances from the cell origin
$x0 = $x - $X0; // The x,y distances from the cell origin $y0 = $y - ($j - $t);
$y0 = $y - $Y0;
// For the 2D case, the simplex shape is an equilateral triangle. // For the 2D case, the simplex shape is an equilateral triangle.
@ -267,26 +254,26 @@ class Simplex extends Perlin{
// Work out the hashed gradient indices of the three simplex corners // Work out the hashed gradient indices of the three simplex corners
$ii = $i & 255; $ii = $i & 255;
$jj = $j & 255; $jj = $j & 255;
$gi0 = $this->perm[$ii + $this->perm[$jj]] % 12;
$gi1 = $this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12;
$gi2 = $this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12;
$n = 0; $n = 0;
// Calculate the contribution from the three corners // Calculate the contribution from the three corners
$t = 0.5 - $x0 ** 2 - $y0 ** 2; $t0 = 0.5 - $x0 ** 2 - $y0 ** 2;
if($t > 0){ if($t0 > 0){
$n += $t ** 4 * self::dot2D(self::$grad3[$gi0], $x0, $y0); // (x,y) of grad3 used for 2D gradient $gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj]] % 12];
$n += $t0 ** 4 * ($gi0[0] * $x0 + $gi0[1] * $y0); // (x,y) of grad3 used for 2D gradient
} }
$t = 0.5 - $x1 ** 2 - $y1 ** 2; $t1 = 0.5 - $x1 ** 2 - $y1 ** 2;
if($t > 0){ if($t1 > 0){
$n += $t ** 4 * self::dot2D(self::$grad3[$gi1], $x1, $y1); $gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12];
$n += $t1 ** 4 * ($gi1[0] * $x1 + $gi1[1] * $y1);
} }
$t = 0.5 - $x2 ** 2 - $y2 ** 2; $t2 = 0.5 - $x2 ** 2 - $y2 ** 2;
if($t > 0){ if($t2 > 0){
$n += $t ** 4 * self::dot2D(self::$grad3[$gi2], $x2, $y2); $gi2 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12];
$n += $t2 ** 4 * ($gi2[0] * $x2 + $gi2[1] * $y2);
} }
// Add contributions from each corner to get the noise value. // Add contributions from each corner to get the noise value.

View File

@ -30,14 +30,17 @@ use pocketmine\block\Gravel;
use pocketmine\block\IronOre; use pocketmine\block\IronOre;
use pocketmine\block\LapisOre; use pocketmine\block\LapisOre;
use pocketmine\block\RedstoneOre; use pocketmine\block\RedstoneOre;
use pocketmine\level\ChunkManager;
use pocketmine\level\generator\biome\Biome; use pocketmine\level\generator\biome\Biome;
use pocketmine\level\generator\biome\BiomeSelector; use pocketmine\level\generator\biome\BiomeSelector;
use pocketmine\level\generator\GenerationChunkManager; use pocketmine\level\generator\GenerationChunkManager;
use pocketmine\level\generator\GenerationManager;
use pocketmine\level\generator\Generator; use pocketmine\level\generator\Generator;
use pocketmine\level\generator\noise\Perlin; use pocketmine\level\generator\noise\Perlin;
use pocketmine\level\generator\noise\Simplex; use pocketmine\level\generator\noise\Simplex;
use pocketmine\level\generator\normal\biome\NormalBiome; use pocketmine\level\generator\normal\biome\NormalBiome;
use pocketmine\level\generator\object\OreType; use pocketmine\level\generator\object\OreType;
use pocketmine\level\generator\populator\GroundCover;
use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Ore;
use pocketmine\level\generator\populator\Populator; use pocketmine\level\generator\populator\Populator;
use pocketmine\level\generator\populator\TallGrass; use pocketmine\level\generator\populator\TallGrass;
@ -50,12 +53,15 @@ class Normal extends Generator{
/** @var Populator[] */ /** @var Populator[] */
private $populators = []; private $populators = [];
/** @var GenerationChunkManager */ /** @var ChunkManager */
private $level; private $level;
/** @var Random */ /** @var Random */
private $random; private $random;
private $waterHeight = 62; private $waterHeight = 62;
private $bedrockDepth = 5; private $bedrockDepth = 5;
/** @var Populator[] */
private $generationPopulators = [];
/** @var Simplex */ /** @var Simplex */
private $noiseBase; private $noiseBase;
@ -92,7 +98,7 @@ class Normal extends Generator{
return []; return [];
} }
public function init(GenerationChunkManager $level, Random $random){ public function init(ChunkManager $level, Random $random){
$this->level = $level; $this->level = $level;
$this->random = $random; $this->random = $random;
$this->random->setSeed($this->level->getSeed()); $this->random->setSeed($this->level->getSeed());
@ -100,6 +106,9 @@ class Normal extends Generator{
$this->random->setSeed($this->level->getSeed()); $this->random->setSeed($this->level->getSeed());
$this->selector = new BiomeSelector($this->random, Biome::getBiome(Biome::OCEAN)); $this->selector = new BiomeSelector($this->random, Biome::getBiome(Biome::OCEAN));
$cover = new GroundCover();
$this->generationPopulators[] = $cover;
$ores = new Ore(); $ores = new Ore();
$ores->setOreTypes([ $ores->setOreTypes([
@ -113,16 +122,6 @@ class Normal extends Generator{
new OreType(new Gravel(), 10, 16, 0, 128), new OreType(new Gravel(), 10, 16, 0, 128),
]); ]);
$this->populators[] = $ores; $this->populators[] = $ores;
$trees = new Tree();
$trees->setBaseAmount(1);
$trees->setRandomAmount(1);
$this->populators[] = $trees;
$tallGrass = new TallGrass();
$tallGrass->setBaseAmount(5);
$tallGrass->setRandomAmount(0);
$this->populators[] = $tallGrass;
} }
public function generateChunk($chunkX, $chunkZ){ public function generateChunk($chunkX, $chunkZ){
@ -132,21 +131,22 @@ class Normal extends Generator{
$chunk = $this->level->getChunk($chunkX, $chunkZ); $chunk = $this->level->getChunk($chunkX, $chunkZ);
$biomeCache = [];
for($x = 0; $x < 16; ++$x){ for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){ for($z = 0; $z < 16; ++$z){
$minSum = 0; $minSum = 0;
$maxSum = 0; $maxSum = 0;
$weightSum = 0; $weightSum = 0;
$biome = $this->selector->pickBiome($chunkX * 16 + $x, $chunkZ * 16 + $z);
$chunk->setBiomeId($x, $z, $biome->getId());
$chunk->setBiomeColor($x, $z, 255 - (($biome->getId() * 1377) % 255), 255 - (($biome->getId() * 4096) % 255), 255 - (($biome->getId() * 31337) % 255));
for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){ for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){
for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){ for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){
if(isset($biomeCache[$index = (($chunkX * 16 + $x + $sx * 16) >> 2) . ":" . (($chunkZ * 16 + $z + $sz * 16) >> 2)])){ if($sx === 0 and $sz === 0){
$adjacent = $biomeCache[$index]; $adjacent = $biome;
}else{ }else{
$adjacent = $this->selector->pickBiome($chunkX * 16 + $x + $sx * 16, $chunkZ * 16 + $z + $sz * 16); $adjacent = $this->selector->pickBiome($chunkX * 16 + $x + $sx * 16, $chunkZ * 16 + $z + $sz * 16);
$biomeCache[$index] = $adjacent;
} }
/** @var NormalBiome $adjacent */ /** @var NormalBiome $adjacent */
$weight = self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE]; $weight = self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE];
@ -182,14 +182,21 @@ class Normal extends Generator{
} }
} }
} }
foreach($this->generationPopulators as $populator){
$populator->populate($this->level, $chunkX, $chunkZ, $this->random);
}
} }
public function populateChunk($chunkX, $chunkZ){ public function populateChunk($chunkX, $chunkZ){
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
foreach($this->populators as $populator){ foreach($this->populators as $populator){
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
$populator->populate($this->level, $chunkX, $chunkZ, $this->random); $populator->populate($this->level, $chunkX, $chunkZ, $this->random);
} }
$chunk = $this->level->getChunk($chunkX, $chunkZ);
$biome = Biome::getBiome($chunk->getBiomeId(7, 7));
$biome->populateChunk($this->level, $chunkX, $chunkZ, $this->random);
} }
public function getSpawn(){ public function getSpawn(){

View File

@ -21,11 +21,12 @@
namespace pocketmine\level\generator\normal\biome; namespace pocketmine\level\generator\normal\biome;
class DesertBiome extends GrassyBiome{ use pocketmine\block\Block;
class DesertBiome extends SandyBiome{
public function __construct(){ public function __construct(){
parent::__construct(); parent::__construct();
$this->setElevation(63, 74); $this->setElevation(63, 74);
} }

View File

@ -25,29 +25,4 @@ use pocketmine\level\generator\biome\Biome;
abstract class NormalBiome extends Biome{ abstract class NormalBiome extends Biome{
private $minElevation;
private $maxElevation;
private $groundCover = [];
public function getMinElevation(){
return $this->minElevation;
}
public function getMaxElevation(){
return $this->maxElevation;
}
public function setElevation($min, $max){
$this->minElevation = $min;
$this->maxElevation = $max;
}
public function getGroundCover(){
return $this->groundCover;
}
public function setGroundCover(array $covers){
$this->groundCover = $covers;
}
} }

View File

@ -27,6 +27,7 @@ abstract class SandyBiome extends NormalBiome{
public function __construct(){ public function __construct(){
$this->setGroundCover([ $this->setGroundCover([
Block::get(Block::SAND, 0),
Block::get(Block::SAND, 0), Block::get(Block::SAND, 0),
Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0),
Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0),

View File

@ -0,0 +1,58 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\level\generator\populator;
use pocketmine\block\Block;
use pocketmine\level\ChunkManager;
use pocketmine\level\generator\biome\Biome;
use pocketmine\utils\Random;
class GroundCover extends Populator{
public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){
$chunk = $level->getChunk($chunkX, $chunkZ);
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
$biome = Biome::getBiome($chunk->getBiomeId($x, $z));
$cover = $biome->getGroundCover();
if(count($cover) > 0){
$column = $chunk->getBlockIdColumn($x, $z);
for($y = 127; $y > 0; --$y){
if($column{$y} !== "\x00" and Block::get(ord($column{$y}))->isSolid()){
break;
}
}
$startY = $y;
$endY = $startY - count($cover);
for($y = $startY; $y > $endY and $y >= 0; --$y){
$b = $cover[$startY - $y];
if($b->getDamage() === 0){
$chunk->setBlockId($x, $y, $z, $b->getId());
}else{
$chunk->setBlock($x, $y, $z, $b->getId(), $b->getDamage());
}
}
}
}
}
}
}

View File

@ -55,6 +55,8 @@ chunk-generation:
use-async: true use-async: true
#Max. amount of chunks to generate per tick, only for use-async: false #Max. amount of chunks to generate per tick, only for use-async: false
per-tick: 1 per-tick: 1
#Max. amount of chunks to populate per tick
populations-per-tick: 1
chunk-gc: chunk-gc:
period-in-ticks: 600 period-in-ticks: 600