mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-13 01:09:44 +00:00
Fixed thread generator crashing, added thread generator garbage collector, fixed generation queues
This commit is contained in:
parent
30318569e1
commit
6977833b1a
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user