mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Merge pull request #2071 from PocketMine/pthreads-fix
Removed pthreads workarounds, new pthreads version
This commit is contained in:
commit
ec293ebd9e
@ -9,7 +9,7 @@ branches:
|
||||
before_script:
|
||||
- mkdir plugins
|
||||
- wget -O plugins/DevTools.phar https://github.com/PocketMine/DevTools/releases/download/v1.9.0/DevTools_v1.9.0.phar
|
||||
- pecl install channel://pecl.php.net/pthreads-2.0.7
|
||||
- pecl install channel://pecl.php.net/pthreads-2.0.8
|
||||
- pecl install channel://pecl.php.net/weakref-0.2.4
|
||||
- echo | pecl install channel://pecl.php.net/yaml-1.1.1
|
||||
|
||||
|
@ -279,8 +279,8 @@ namespace pocketmine {
|
||||
if(substr_count($pthreads_version, ".") < 2){
|
||||
$pthreads_version = "0.$pthreads_version";
|
||||
}
|
||||
if(version_compare($pthreads_version, "2.0.4") < 0){
|
||||
$logger->critical("pthreads >= 2.0.4 is required, while you have $pthreads_version.");
|
||||
if(version_compare($pthreads_version, "2.0.8") < 0){
|
||||
$logger->critical("pthreads >= 2.0.8 is required, while you have $pthreads_version.");
|
||||
++$errors;
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,6 @@ class Server{
|
||||
private $nextTick = 0;
|
||||
private $tickAverage = [20,20,20,20,20];
|
||||
private $useAverage = [20,20,20,20,20];
|
||||
private $inTick = false;
|
||||
|
||||
/** @var \AttachableThreadedLogger */
|
||||
private $logger;
|
||||
@ -1061,12 +1060,8 @@ class Server{
|
||||
if($generator !== null and class_exists($generator) and is_subclass_of($generator, "pocketmine\\level\\generator\\Generator")){
|
||||
$generator = new $generator($options);
|
||||
}else{
|
||||
if(strtoupper($this->getLevelType()) == "FLAT"){
|
||||
$generator = Generator::getGenerator("flat");
|
||||
$options["preset"] = $this->getConfigString("generator-settings", "");
|
||||
}else{
|
||||
$generator = Generator::getGenerator("normal");
|
||||
}
|
||||
$options["preset"] = $this->getConfigString("generator-settings", "");
|
||||
$generator = Generator::getGenerator($this->getLevelType());
|
||||
}
|
||||
|
||||
if(($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "mcregion"))) === null){
|
||||
@ -1844,7 +1839,7 @@ class Server{
|
||||
|
||||
$this->getScheduler()->scheduleRepeatingTask(new CallbackTask([$this, "checkTicks"]), 20 * 5);
|
||||
|
||||
$this->logger->info("Default game type: " . self::getGamemodeString($this->getGamemode())); //TODO: string name
|
||||
$this->logger->info("Default game type: " . self::getGamemodeString($this->getGamemode()));
|
||||
|
||||
$this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"');
|
||||
|
||||
@ -1941,17 +1936,8 @@ class Server{
|
||||
private function tickProcessor(){
|
||||
$lastLoop = 0;
|
||||
while($this->isRunning){
|
||||
++$lastLoop;
|
||||
|
||||
if(($ticks = $this->tick()) !== true){
|
||||
if($lastLoop > 2 and $lastLoop < 16){
|
||||
usleep(1000);
|
||||
}elseif($lastLoop < 128){
|
||||
usleep(2000);
|
||||
}else{
|
||||
usleep(10000);
|
||||
}
|
||||
}
|
||||
$this->tick();
|
||||
usleep((int) max(1, ($this->nextTick - microtime(true)) * 1000000));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2032,61 +2018,56 @@ class Server{
|
||||
/**
|
||||
* Tries to execute a server tick
|
||||
*/
|
||||
public function tick(){
|
||||
if($this->inTick === false){
|
||||
$tickTime = microtime(true);
|
||||
if($tickTime < $this->nextTick){
|
||||
return false;
|
||||
}
|
||||
|
||||
Timings::$serverTickTimer->startTiming();
|
||||
|
||||
$this->inTick = true; //Fix race conditions
|
||||
++$this->tickCounter;
|
||||
|
||||
$this->checkConsole();
|
||||
|
||||
//TODO: move this to tick
|
||||
Timings::$connectionTimer->startTiming();
|
||||
foreach($this->interfaces as $interface){
|
||||
$interface->process();
|
||||
}
|
||||
Timings::$connectionTimer->stopTiming();
|
||||
|
||||
Timings::$schedulerTimer->startTiming();
|
||||
$this->scheduler->mainThreadHeartbeat($this->tickCounter);
|
||||
Timings::$schedulerTimer->stopTiming();
|
||||
$this->checkTickUpdates($this->tickCounter);
|
||||
|
||||
if(($this->tickCounter & 0b1111) === 0){
|
||||
$this->titleTick();
|
||||
if(isset($this->queryHandler) and ($this->tickCounter & 0b111111111) === 0){
|
||||
$this->queryHandler->regenerateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
$this->generationManager->handlePackets();
|
||||
|
||||
Timings::$serverTickTimer->stopTiming();
|
||||
|
||||
TimingsHandler::tick();
|
||||
|
||||
$now = microtime(true);
|
||||
array_shift($this->tickAverage);
|
||||
$this->tickAverage[] = min(20, 1 / max(0.001, $now - $tickTime));
|
||||
array_shift($this->useAverage);
|
||||
$this->useAverage[] = min(1, ($now - $tickTime) / 0.05);
|
||||
|
||||
if(($this->nextTick - $tickTime) < -1){
|
||||
$this->nextTick = $tickTime;
|
||||
}
|
||||
$this->nextTick += 0.05;
|
||||
$this->inTick = false;
|
||||
|
||||
return true;
|
||||
private function tick(){
|
||||
$tickTime = microtime(true);
|
||||
if($tickTime < $this->nextTick){
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
Timings::$serverTickTimer->startTiming();
|
||||
|
||||
++$this->tickCounter;
|
||||
|
||||
$this->checkConsole();
|
||||
|
||||
Timings::$connectionTimer->startTiming();
|
||||
foreach($this->interfaces as $interface){
|
||||
$interface->process();
|
||||
}
|
||||
Timings::$connectionTimer->stopTiming();
|
||||
|
||||
Timings::$schedulerTimer->startTiming();
|
||||
$this->scheduler->mainThreadHeartbeat($this->tickCounter);
|
||||
Timings::$schedulerTimer->stopTiming();
|
||||
|
||||
$this->checkTickUpdates($this->tickCounter);
|
||||
|
||||
if(($this->tickCounter & 0b1111) === 0){
|
||||
$this->titleTick();
|
||||
if(isset($this->queryHandler) and ($this->tickCounter & 0b111111111) === 0){
|
||||
$this->queryHandler->regenerateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
$this->generationManager->handlePackets();
|
||||
|
||||
Timings::$serverTickTimer->stopTiming();
|
||||
|
||||
TimingsHandler::tick();
|
||||
|
||||
$now = microtime(true);
|
||||
array_shift($this->tickAverage);
|
||||
$this->tickAverage[] = min(20, 1 / max(0.001, $now - $tickTime));
|
||||
array_shift($this->useAverage);
|
||||
$this->useAverage[] = min(1, ($now - $tickTime) / 0.05);
|
||||
|
||||
if(($this->nextTick - $tickTime) < -1){
|
||||
$this->nextTick = $tickTime;
|
||||
}
|
||||
$this->nextTick += 0.05;
|
||||
$this->inTick = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,6 @@ abstract class Thread extends \Thread{
|
||||
public final function start($options = PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
return parent::start($options & ~PTHREADS_INHERIT_CLASSES);
|
||||
return parent::start($options);
|
||||
}
|
||||
}
|
@ -29,6 +29,6 @@ abstract class Worker extends \Worker{
|
||||
public final function start($options = PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
return parent::start($options & ~PTHREADS_INHERIT_CLASSES);
|
||||
return parent::start($options);
|
||||
}
|
||||
}
|
@ -44,14 +44,13 @@ class GenerationChunkManager implements ChunkManager{
|
||||
protected $changes = [];
|
||||
|
||||
public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){
|
||||
if(!is_subclass_of($class, "pocketmine\\level\\generator\\Generator")){
|
||||
throw new \Exception("Class is not a subclass of Generator");
|
||||
if(!class_exists($class, true) or !is_subclass_of($class, "pocketmine\\level\\generator\\Generator")){
|
||||
throw new \Exception("Class $class does not exists or is not a subclass of Generator");
|
||||
}
|
||||
|
||||
$this->levelID = $levelID;
|
||||
$this->seed = $seed;
|
||||
$this->manager = $manager;
|
||||
|
||||
$this->generator = new $class($options);
|
||||
$this->generator->init($this, new Random($seed));
|
||||
}
|
||||
@ -75,10 +74,15 @@ class GenerationChunkManager implements ChunkManager{
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return FullChunk
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
$chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index];
|
||||
if($chunk === null){
|
||||
throw new \Exception("null chunk received");
|
||||
}
|
||||
$this->changes[$index] = $chunk;
|
||||
|
||||
return $chunk;
|
||||
@ -106,14 +110,14 @@ class GenerationChunkManager implements ChunkManager{
|
||||
unset($this->changes[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
$this->getChunk($chunkX, $chunkZ);
|
||||
$this->generator->generateChunk($chunkX, $chunkZ);
|
||||
$this->setChunkGenerated($chunkX, $chunkZ);
|
||||
try{
|
||||
$this->getChunk($chunkX, $chunkZ);
|
||||
$this->generator->generateChunk($chunkX, $chunkZ);
|
||||
$this->setChunkGenerated($chunkX, $chunkZ);
|
||||
}catch(\Exception $e){}
|
||||
}
|
||||
|
||||
public function populateChunk($chunkX, $chunkZ){
|
||||
@ -134,21 +138,36 @@ class GenerationChunkManager implements ChunkManager{
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
return $this->getChunk($chunkX, $chunkZ)->isGenerated();
|
||||
try{
|
||||
return $this->getChunk($chunkX, $chunkZ)->isGenerated();
|
||||
}catch(\Exception $e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isChunkPopulated($chunkX, $chunkZ){
|
||||
return $this->getChunk($chunkX, $chunkZ)->isPopulated();
|
||||
try{
|
||||
return $this->getChunk($chunkX, $chunkZ)->isPopulated();
|
||||
}catch(\Exception $e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function setChunkGenerated($chunkX, $chunkZ){
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setGenerated(true);
|
||||
try{
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setGenerated(true);
|
||||
}catch(\Exception $e){}
|
||||
}
|
||||
|
||||
public function setChunkPopulated($chunkX, $chunkZ){
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setPopulated(true);
|
||||
|
||||
try{
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setPopulated(true);
|
||||
}catch(\Exception $e){}
|
||||
}
|
||||
|
||||
protected function requestChunk($chunkX, $chunkZ){
|
||||
@ -181,7 +200,11 @@ class GenerationChunkManager implements ChunkManager{
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockIdAt($x, $y, $z){
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
try{
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}catch(\Exception $e){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -193,7 +216,9 @@ class GenerationChunkManager implements ChunkManager{
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockIdAt($x, $y, $z, $id){
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff);
|
||||
try{
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff);
|
||||
}catch(\Exception $e){}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,7 +231,11 @@ class GenerationChunkManager implements ChunkManager{
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockDataAt($x, $y, $z){
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
try{
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}catch(\Exception $e){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,7 +247,9 @@ class GenerationChunkManager implements ChunkManager{
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockDataAt($x, $y, $z, $data){
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f);
|
||||
try{
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f);
|
||||
}catch(\Exception $e){}
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
|
@ -89,8 +89,9 @@ class GenerationManager{
|
||||
*/
|
||||
const PACKET_SHUTDOWN = 0xff;
|
||||
|
||||
/** @var GenerationThread */
|
||||
protected $thread;
|
||||
|
||||
protected $socket;
|
||||
/** @var \Logger */
|
||||
protected $logger;
|
||||
/** @var \ClassLoader */
|
||||
@ -110,29 +111,33 @@ class GenerationManager{
|
||||
protected $shutdown = false;
|
||||
|
||||
/**
|
||||
* @param resource $socket
|
||||
* @param \Logger $logger
|
||||
* @param \ClassLoader $loader
|
||||
* @param GenerationThread $thread
|
||||
* @param \Logger $logger
|
||||
* @param \ClassLoader $loader
|
||||
*/
|
||||
public function __construct($socket, \Logger $logger, \ClassLoader $loader){
|
||||
$this->socket = $socket;
|
||||
public function __construct(GenerationThread $thread, \Logger $logger, \ClassLoader $loader){
|
||||
$this->thread = $thread;
|
||||
$this->logger = $logger;
|
||||
$this->loader = $loader;
|
||||
$chunkX = $chunkZ = null;
|
||||
|
||||
while($this->shutdown !== true){
|
||||
if(count($this->requestQueue) > 0){
|
||||
foreach($this->requestQueue as $levelID => $chunks){
|
||||
if(count($chunks) === 0){
|
||||
unset($this->requestQueue[$levelID]);
|
||||
}else{
|
||||
Level::getXZ($key = key($chunks), $chunkX, $chunkZ);
|
||||
unset($this->requestQueue[$levelID][$key]);
|
||||
$this->generateChunk($levelID, $chunkX, $chunkZ);
|
||||
try{
|
||||
if(count($this->requestQueue) > 0){
|
||||
foreach($this->requestQueue as $levelID => $chunks){
|
||||
if(count($chunks) === 0){
|
||||
unset($this->requestQueue[$levelID]);
|
||||
}else{
|
||||
Level::getXZ($key = key($chunks), $chunkX, $chunkZ);
|
||||
unset($this->requestQueue[$levelID][$key]);
|
||||
$this->generateChunk($levelID, $chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$this->readPacket();
|
||||
}
|
||||
}else{
|
||||
$this->readPacket();
|
||||
}catch(\Exception $e){
|
||||
$this->logger->warning("[Generator Thread] Exception: ".$e->getMessage() . " on file \"".$e->getFile()."\" line ".$e->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,7 +209,7 @@ class GenerationManager{
|
||||
public function requestChunk($levelID, $chunkX, $chunkZ){
|
||||
$this->needsChunk[$levelID] = [$chunkX, $chunkZ];
|
||||
$binary = chr(self::PACKET_REQUEST_CHUNK) . Binary::writeInt($levelID) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ);
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
|
||||
$this->thread->pushThreadToMainPacket($binary);
|
||||
|
||||
do{
|
||||
$this->readPacket();
|
||||
@ -221,76 +226,63 @@ class GenerationManager{
|
||||
|
||||
public function sendChunk($levelID, FullChunk $chunk){
|
||||
$binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
|
||||
@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;
|
||||
$this->thread->pushThreadToMainPacket($binary);
|
||||
}
|
||||
|
||||
protected function readPacket(){
|
||||
$len = $this->socketRead(4);
|
||||
if(($len = Binary::readInt($len)) <= 0){
|
||||
$this->shutdown = true;
|
||||
$this->getLogger()->critical("Generation Thread found a stream error, shutting down");
|
||||
if(strlen($packet = $this->thread->readMainToThreadPacket()) > 0){
|
||||
$pid = ord($packet{0});
|
||||
$offset = 1;
|
||||
if($pid === self::PACKET_REQUEST_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkX = Binary::readInt(substr($packet, $offset, 4));
|
||||
$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;
|
||||
$len = ord($packet{$offset++});
|
||||
/** @var FullChunk $class */
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$chunk = $class::fromBinary(substr($packet, $offset));
|
||||
$this->receiveChunk($levelID, $chunk);
|
||||
}elseif($pid === self::PACKET_OPEN_LEVEL){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$seed = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$len = Binary::readShort(substr($packet, $offset, 2));
|
||||
$offset += 2;
|
||||
$class = substr($packet, $offset, $len);
|
||||
$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);
|
||||
}elseif($pid === self::PACKET_ADD_NAMESPACE){
|
||||
$len = Binary::readShort(substr($packet, $offset, 2));
|
||||
$offset += 2;
|
||||
$namespace = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$path = substr($packet, $offset);
|
||||
$this->loader->addPath($path);
|
||||
}elseif($pid === self::PACKET_SHUTDOWN){
|
||||
foreach($this->levels as $level){
|
||||
$level->shutdown();
|
||||
}
|
||||
$this->levels = [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$packet = $this->socketRead($len);
|
||||
|
||||
$pid = ord($packet{0});
|
||||
$offset = 1;
|
||||
if($pid === self::PACKET_REQUEST_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkX = Binary::readInt(substr($packet, $offset, 4));
|
||||
$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;
|
||||
$len = ord($packet{$offset++});
|
||||
/** @var FullChunk $class */
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$chunk = $class::fromBinary(substr($packet, $offset));
|
||||
$this->receiveChunk($levelID, $chunk);
|
||||
}elseif($pid === self::PACKET_OPEN_LEVEL){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$seed = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$len = Binary::readShort(substr($packet, $offset, 2));
|
||||
$offset += 2;
|
||||
$class = substr($packet, $offset, $len);
|
||||
$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);
|
||||
}elseif($pid === self::PACKET_ADD_NAMESPACE){
|
||||
$len = Binary::readShort(substr($packet, $offset, 2));
|
||||
$offset += 2;
|
||||
$namespace = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$path = substr($packet, $offset);
|
||||
$this->loader->addPath($path);
|
||||
}elseif($pid === self::PACKET_SHUTDOWN){
|
||||
foreach($this->levels as $level){
|
||||
$level->shutdown();
|
||||
$this->shutdown = true;
|
||||
}
|
||||
$this->levels = [];
|
||||
}elseif(count($this->thread->getInternalQueue()) === 0){
|
||||
$this->thread->synchronized(function(){
|
||||
$this->thread->wait(50000);
|
||||
});
|
||||
|
||||
$this->shutdown = true;
|
||||
socket_close($this->socket);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,6 @@ use pocketmine\utils\Binary;
|
||||
|
||||
class GenerationRequestManager{
|
||||
|
||||
protected $socket;
|
||||
/** @var Server */
|
||||
protected $server;
|
||||
/** @var GenerationThread */
|
||||
@ -40,7 +39,6 @@ class GenerationRequestManager{
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
$this->generationThread = new GenerationThread($server->getLogger(), $server->getLoader());
|
||||
$this->socket = $this->generationThread->getExternalSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +50,7 @@ class GenerationRequestManager{
|
||||
$buffer = chr(GenerationManager::PACKET_OPEN_LEVEL) . Binary::writeInt($level->getID()) . Binary::writeInt($level->getSeed()) .
|
||||
Binary::writeShort(strlen($generator)) . $generator . serialize($options);
|
||||
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,31 +58,22 @@ class GenerationRequestManager{
|
||||
*/
|
||||
public function closeLevel(Level $level){
|
||||
$buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($level->getID());
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
public function addNamespace($namespace, $path){
|
||||
$buffer = chr(GenerationManager::PACKET_ADD_NAMESPACE) . Binary::writeShort(strlen($namespace)) . $namespace . $path;
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
}
|
||||
|
||||
protected function socketRead($len){
|
||||
$buffer = "";
|
||||
while(strlen($buffer) < $len){
|
||||
$buffer .= @socket_read($this->socket, $len - strlen($buffer));
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
protected function sendChunk($levelID, FullChunk $chunk){
|
||||
$binary = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary);
|
||||
$buffer = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
public function requestChunk(Level $level, $chunkX, $chunkZ){
|
||||
$buffer = chr(GenerationManager::PACKET_REQUEST_CHUNK) . Binary::writeInt($level->getID()) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ);
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
protected function handleRequest($levelID, $chunkX, $chunkZ){
|
||||
@ -97,7 +86,7 @@ class GenerationRequestManager{
|
||||
}
|
||||
}else{
|
||||
$buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID);
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,17 +95,12 @@ class GenerationRequestManager{
|
||||
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
}else{
|
||||
$buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID);
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public function handlePackets(){
|
||||
if(($len = @socket_read($this->socket, 4)) !== false and $len !== ""){
|
||||
if(strlen($len) < 4){
|
||||
$len .= $this->socketRead(4 - strlen($len));
|
||||
}
|
||||
|
||||
$packet = $this->socketRead(Binary::readInt($len));
|
||||
while(strlen($packet = $this->generationThread->readThreadToMainPacket()) > 0){
|
||||
$pid = ord($packet{0});
|
||||
$offset = 1;
|
||||
|
||||
@ -145,7 +129,7 @@ class GenerationRequestManager{
|
||||
|
||||
public function shutdown(){
|
||||
$buffer = chr(GenerationManager::PACKET_SHUTDOWN);
|
||||
@socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
$this->generationThread->join();
|
||||
}
|
||||
|
||||
|
@ -32,15 +32,42 @@ class GenerationThread extends Thread{
|
||||
/** @var \ThreadedLogger */
|
||||
protected $logger;
|
||||
|
||||
protected $externalSocket;
|
||||
protected $internalSocket;
|
||||
/** @var \Threaded */
|
||||
protected $externalQueue;
|
||||
/** @var \Threaded */
|
||||
protected $internalQueue;
|
||||
|
||||
public function getExternalSocket(){
|
||||
return $this->externalSocket;
|
||||
/**
|
||||
* @return \Threaded
|
||||
*/
|
||||
public function getInternalQueue(){
|
||||
return $this->internalQueue;
|
||||
}
|
||||
|
||||
public function getInternalSocket(){
|
||||
return $this->internalSocket;
|
||||
/**
|
||||
* @return \Threaded
|
||||
*/
|
||||
public function getExternalQueue(){
|
||||
return $this->externalQueue;
|
||||
}
|
||||
|
||||
public function pushMainToThreadPacket($str){
|
||||
$this->internalQueue[] = $str;
|
||||
$this->synchronized(function(){
|
||||
$this->notify();
|
||||
});
|
||||
}
|
||||
|
||||
public function readMainToThreadPacket(){
|
||||
return $this->internalQueue->shift();
|
||||
}
|
||||
|
||||
public function pushThreadToMainPacket($str){
|
||||
$this->externalQueue[] = $str;
|
||||
}
|
||||
|
||||
public function readThreadToMainPacket(){
|
||||
return $this->externalQueue->shift();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,19 +84,8 @@ class GenerationThread extends Thread{
|
||||
$this->addDependency($loadPaths, new \ReflectionClass($this->loader));
|
||||
$this->loadPaths = array_reverse($loadPaths);
|
||||
|
||||
$sockets = [];
|
||||
if(!socket_create_pair((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? AF_INET : AF_UNIX), SOCK_STREAM, 0, $sockets)){
|
||||
throw new \Exception("Could not create IPC sockets. Reason: " . socket_strerror(socket_last_error()));
|
||||
}
|
||||
|
||||
$this->internalSocket = $sockets[0];
|
||||
socket_set_block($this->internalSocket); //IMPORTANT!
|
||||
@socket_set_option($this->internalSocket, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2);
|
||||
@socket_set_option($this->internalSocket, SOL_SOCKET, SO_RCVBUF, 1024 * 1024 * 2);
|
||||
$this->externalSocket = $sockets[1];
|
||||
socket_set_nonblock($this->externalSocket);
|
||||
@socket_set_option($this->externalSocket, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2);
|
||||
@socket_set_option($this->externalSocket, SOL_SOCKET, SO_RCVBUF, 1024 * 1024 * 2);
|
||||
$this->externalQueue = new \Threaded();
|
||||
$this->internalQueue = new \Threaded();
|
||||
|
||||
$this->start();
|
||||
}
|
||||
@ -90,14 +106,15 @@ class GenerationThread extends Thread{
|
||||
|
||||
public function run(){
|
||||
error_reporting(-1);
|
||||
gc_enable();
|
||||
//Load removed dependencies, can't use require_once()
|
||||
foreach($this->loadPaths as $name => $path){
|
||||
if(!class_exists($name, false) and !class_exists($name, false)){
|
||||
if(!class_exists($name, false) and !interface_exists($name, false)){
|
||||
require($path);
|
||||
}
|
||||
}
|
||||
$this->loader->register();
|
||||
|
||||
$generationManager = new GenerationManager($this->getInternalSocket(), $this->getLogger(), $this->loader);
|
||||
$generationManager = new GenerationManager($this, $this->getLogger(), $this->loader);
|
||||
}
|
||||
}
|
@ -436,6 +436,7 @@ class PluginManager{
|
||||
*/
|
||||
public function unsubscribeFromPermission($permission, Permissible $permissible){
|
||||
if(isset($this->permSubs[$permission])){
|
||||
$this->permSubs[$permission][spl_object_hash($permissible)]->release();
|
||||
unset($this->permSubs[$permission][spl_object_hash($permissible)]);
|
||||
if(count($this->permSubs[$permission]) === 0){
|
||||
unset($this->permSubs[$permission]);
|
||||
|
@ -26,8 +26,12 @@ use pocketmine\Worker;
|
||||
class AsyncWorker extends Worker{
|
||||
|
||||
public function run(){
|
||||
require(\pocketmine\PATH . "src/spl/ClassLoader.php");
|
||||
require(\pocketmine\PATH . "src/spl/BaseClassLoader.php");
|
||||
if(!interface_exists("ClassLoader", false)){
|
||||
require(\pocketmine\PATH . "src/spl/ClassLoader.php");
|
||||
}
|
||||
if(!class_exists("BaseClassLoader", false)){
|
||||
require(\pocketmine\PATH . "src/spl/BaseClassLoader.php");
|
||||
}
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader->addPath(\pocketmine\PATH . "src");
|
||||
$autoloader->register(true);
|
||||
|
@ -46,7 +46,7 @@ class Random{
|
||||
* @param int $seed Integer to be used as seed.
|
||||
*/
|
||||
public function setSeed($seed){
|
||||
$this->seed = crc32(Binary::writeInt($seed));
|
||||
$this->seed = crc32(pack("N", $seed));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,14 +64,14 @@ class Random{
|
||||
* @return int
|
||||
*/
|
||||
public function nextSignedInt(){
|
||||
$t = crc32(Binary::writeInt($this->seed));
|
||||
$t = crc32(pack("N", $this->seed));
|
||||
$this->seed ^= $t;
|
||||
|
||||
if($t > 2147483647){
|
||||
$t -= 4294967296;
|
||||
if(PHP_INT_SIZE === 8){
|
||||
return $t << 32 >> 32;
|
||||
}else{
|
||||
return $t;
|
||||
}
|
||||
|
||||
return (int) $t;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,11 +105,11 @@ class Random{
|
||||
* Returns a random integer between $start and $end
|
||||
*
|
||||
* @param int $start default 0
|
||||
* @param int $end default PHP_INT_MAX
|
||||
* @param int $end default 0x7fffffff
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function nextRange($start = 0, $end = PHP_INT_MAX){
|
||||
public function nextRange($start = 0, $end = 0x7fffffff){
|
||||
return $start + ($this->nextInt() % ($end + 1 - $start));
|
||||
}
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6fad38f761d9ee204839eb557503645799b2ec99
|
||||
Subproject commit c32adbd6f3e06e4002fce6987651e421b784e997
|
Loading…
x
Reference in New Issue
Block a user