Fixed thread generator crashing, added thread generator garbage collector, fixed generation queues

This commit is contained in:
Shoghi Cervantes 2014-06-14 23:59:31 +02:00
parent 30318569e1
commit 6977833b1a
6 changed files with 90 additions and 17 deletions

View File

@ -645,7 +645,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->chunksOrder = $newOrder;
$i = 0;
while($generateQueue->count() > 0 and $i < 8){
while(count($this->chunksOrder) === 0 and $generateQueue->count() > 0 and $i < 8){
$d = $generateQueue->extract();
$this->getLevel()->generateChunk($d[0], $d[1]);
++$i;

View File

@ -997,8 +997,8 @@ class Server{
$level = new Level($this, $name, $path, $provider);
$this->levels[$level->getID()] = $level;
for($Z = 6; $Z <= 10; ++$Z){
for($X = 6; $X <= 10; ++$X){
for($Z = 5; $Z <= 11; ++$Z){
for($X = 5; $X <= 11; ++$X){
$level->generateChunk($X, $Z);
}
}

View File

@ -1144,6 +1144,10 @@ class Level implements ChunkManager, Metadatable{
}
public function setChunk($x, $z, SimpleChunk $chunk){
$index = Level::chunkHash($x, $z);
foreach($this->getUsingChunk($x, $z) as $player){
$player->setChunkIndex($index, 0xff);
}
$this->provider->setChunk($x, $z, $chunk);
}

View File

@ -40,6 +40,8 @@ class SimpleChunk{
protected $flags = 0;
protected $changed = false;
/**
* @param int $chunkX
* @param int $chunkZ
@ -57,6 +59,15 @@ class SimpleChunk{
}
}
public function hasChanged($set = true){
if($this->changed === true and $set === true){
$this->changed = false;
return true;
}
return $this->changed;
}
/**
* @return int
*/
@ -103,6 +114,7 @@ class SimpleChunk{
* @param bool $value
*/
public function setGenerated($value = true){
$this->changed = true;
$this->flags = ($this->flags & ~self::FLAG_GENERATED) | ($value === true ? self::FLAG_GENERATED : 0);
}
@ -110,6 +122,7 @@ class SimpleChunk{
* @param bool $value
*/
public function setPopulated($value = true){
$this->changed = true;
$this->flags = ($this->flags & ~self::FLAG_POPULATED) | ($value === true ? self::FLAG_POPULATED : 0);
}
@ -131,6 +144,7 @@ class SimpleChunk{
* @param int $blockId 0-255
*/
public function setBlockId($x, $y, $z, $blockId){
$this->changed = true;
@$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x} = chr($blockId);
}
@ -157,6 +171,7 @@ class SimpleChunk{
* @param int $data 0-15
*/
public function setBlockData($x, $y, $z, $data){
$this->changed = true;
$i = (($y & 0x0f) << 7) + ($z << 3) + ($x >> 1);
$old_m = ord($this->meta[$y >> 4]{$i});
if(($x & 1) === 0){
@ -190,6 +205,7 @@ class SimpleChunk{
* @param string $meta
*/
public function setSection($y, $ids = null, $meta = null){
$this->changed = true;
if($ids !== null){
$this->ids[$y] = $ids;
}

View File

@ -33,6 +33,9 @@ class GenerationChunkManager implements ChunkManager{
/** @var SimpleChunk[] */
protected $chunks = [];
/** @var \SplObjectStorage<SimpleChunk> */
protected $unloadQueue;
/** @var Generator */
protected $generator;
@ -50,6 +53,8 @@ class GenerationChunkManager implements ChunkManager{
$this->seed = $seed;
$this->manager = $manager;
$this->unloadQueue = new \SplObjectStorage();
$this->generator = new $class($options);
$this->generator->init($this, new Random($seed));
}
@ -76,8 +81,41 @@ class GenerationChunkManager implements ChunkManager{
*/
public function getChunk($chunkX, $chunkZ){
$index = Level::chunkHash($chunkX, $chunkZ);
$chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index];
$this->unloadQueue->detach($chunk);
return $chunk;
}
return !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index];
/**
* @param bool $set
*
* @return SimpleChunk[]
*/
public function getChangedChunks($set = true){
$changed = [];
foreach($this->chunks as $chunk){
if($chunk->hasChanged($set)){
$changed[] = $chunk;
}
}
return $changed;
}
public function doGarbageCollection(){
if($this->unloadQueue->count() > 0){
/** @var SimpleChunk $chunk */
foreach($this->unloadQueue as $chunk){
if(!$chunk->hasChanged(false)){
unset($this->chunks[Level::chunkHash($chunk->getX(), $chunk->getZ())]);
}
$this->unloadQueue->detach($chunk);
}
}
foreach($this->chunks as $chunk){
$this->unloadQueue->attach($chunk);
}
}
public function generateChunk($chunkX, $chunkZ){
@ -101,7 +139,6 @@ class GenerationChunkManager implements ChunkManager{
$this->generator->populateChunk($chunkX, $chunkZ);
$this->setChunkPopulated($chunkX, $chunkZ);
}
public function isChunkGenerated($chunkX, $chunkZ){
@ -123,9 +160,6 @@ class GenerationChunkManager implements ChunkManager{
protected function requestChunk($chunkX, $chunkZ){
$chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ);
$this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk;
if(!$chunk->isGenerated()){
$this->generateChunk($chunkX, $chunkZ);
}
return $this->chunks[$index];
}

View File

@ -22,6 +22,7 @@
namespace pocketmine\level\generator;
use pocketmine\level\format\SimpleChunk;
use pocketmine\level\Level;
use pocketmine\utils\Binary;
class GenerationManager{
@ -97,6 +98,8 @@ class GenerationManager{
/** @var GenerationChunkManager[] */
protected $levels = [];
protected $generatedQueue = [];
/** @var \SplQueue */
protected $requestQueue;
@ -131,16 +134,23 @@ class GenerationManager{
protected function openLevel($levelID, $seed, $class, array $options){
if(!isset($this->levels[$levelID])){
$this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options);
$this->generatedQueue[$levelID] = [];
}
}
protected function generateChunk($levelID, $chunkX, $chunkZ){
if(isset($this->levels[$levelID])){
if(isset($this->levels[$levelID]) and !isset($this->generatedQueue[$levelID][$index = Level::chunkHash($chunkX, $chunkZ)])){
$this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly
if(isset($this->levels[$levelID])){
$this->sendChunk($levelID, $this->levels[$levelID]->getChunk($chunkX, $chunkZ));
$this->generatedQueue[$levelID][$index] = true;
if(count($this->generatedQueue[$levelID]) > 6){
$this->levels[$levelID]->doGarbageCollection();
foreach($this->levels[$levelID]->getChangedChunks(true) as $chunk){
$this->sendChunk($levelID, $chunk);
}
$this->generatedQueue[$levelID] = [];
}
}
//TODO: wait for queue generation (to wait for extra chunk changes)
}
}
@ -148,6 +158,7 @@ class GenerationManager{
if(!isset($this->levels[$levelID])){
$this->levels[$levelID]->shutdown();
unset($this->levels[$levelID]);
unset($this->generatedQueue[$levelID]);
}
}
@ -193,13 +204,24 @@ class GenerationManager{
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
}
protected function socketRead($len){
$buffer = "";
while(strlen($buffer) < $len){
$buffer .= @socket_read($this->socket, $len - strlen($buffer));
}
return $buffer;
}
protected function readPacket(){
$len = @socket_read($this->socket, 4);
if($len === false or $len === ""){
$len = $this->socketRead(4);
if(($len = Binary::readInt($len)) <= 0){
$this->shutdown = true;
$this->getLogger()->critical("Generation Thread found a stream error, shutting down");
return;
}
$packet = socket_read($this->socket, Binary::readInt($len));
$packet = $this->socketRead($len);
$pid = ord($packet{0});
$offset = 1;
if($pid === self::PACKET_REQUEST_CHUNK){
@ -209,13 +231,11 @@ class GenerationManager{
$offset += 4;
$chunkZ = Binary::readInt(substr($packet, $offset, 4));
$this->enqueueChunk($levelID, $chunkX, $chunkZ);
}elseif($pid === self::PACKET_SEND_CHUNK){
$levelID = Binary::readInt(substr($packet, $offset, 4));
$offset += 4;
$chunk = SimpleChunk::fromBinary(substr($packet, $offset));
$this->receiveChunk($levelID, $chunk);
}elseif($pid === self::PACKET_OPEN_LEVEL){
$levelID = Binary::readInt(substr($packet, $offset, 4));
$offset += 4;
@ -227,7 +247,6 @@ class GenerationManager{
$offset += $len;
$options = unserialize(substr($packet, $offset));
$this->openLevel($levelID, $seed, $class, $options);
}elseif($pid === self::PACKET_CLOSE_LEVEL){
$levelID = Binary::readInt(substr($packet, $offset, 4));
$this->closeLevel($levelID);