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; $this->chunksOrder = $newOrder;
$i = 0; $i = 0;
while($generateQueue->count() > 0 and $i < 8){ while(count($this->chunksOrder) === 0 and $generateQueue->count() > 0 and $i < 8){
$d = $generateQueue->extract(); $d = $generateQueue->extract();
$this->getLevel()->generateChunk($d[0], $d[1]); $this->getLevel()->generateChunk($d[0], $d[1]);
++$i; ++$i;

View File

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

View File

@ -1144,6 +1144,10 @@ class Level implements ChunkManager, Metadatable{
} }
public function setChunk($x, $z, SimpleChunk $chunk){ 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); $this->provider->setChunk($x, $z, $chunk);
} }

View File

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

View File

@ -33,6 +33,9 @@ class GenerationChunkManager implements ChunkManager{
/** @var SimpleChunk[] */ /** @var SimpleChunk[] */
protected $chunks = []; protected $chunks = [];
/** @var \SplObjectStorage<SimpleChunk> */
protected $unloadQueue;
/** @var Generator */ /** @var Generator */
protected $generator; protected $generator;
@ -50,6 +53,8 @@ class GenerationChunkManager implements ChunkManager{
$this->seed = $seed; $this->seed = $seed;
$this->manager = $manager; $this->manager = $manager;
$this->unloadQueue = new \SplObjectStorage();
$this->generator = new $class($options); $this->generator = new $class($options);
$this->generator->init($this, new Random($seed)); $this->generator->init($this, new Random($seed));
} }
@ -76,8 +81,41 @@ class GenerationChunkManager implements ChunkManager{
*/ */
public function getChunk($chunkX, $chunkZ){ public function getChunk($chunkX, $chunkZ){
$index = Level::chunkHash($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){ public function generateChunk($chunkX, $chunkZ){
@ -101,7 +139,6 @@ class GenerationChunkManager implements ChunkManager{
$this->generator->populateChunk($chunkX, $chunkZ); $this->generator->populateChunk($chunkX, $chunkZ);
$this->setChunkPopulated($chunkX, $chunkZ); $this->setChunkPopulated($chunkX, $chunkZ);
} }
public function isChunkGenerated($chunkX, $chunkZ){ public function isChunkGenerated($chunkX, $chunkZ){
@ -123,9 +160,6 @@ class GenerationChunkManager implements ChunkManager{
protected function requestChunk($chunkX, $chunkZ){ protected function requestChunk($chunkX, $chunkZ){
$chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ); $chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ);
$this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk; $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk;
if(!$chunk->isGenerated()){
$this->generateChunk($chunkX, $chunkZ);
}
return $this->chunks[$index]; return $this->chunks[$index];
} }

View File

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