|null */ public ?NonThreadSafeValue $crashInfo = null; /** @phpstan-var NonThreadSafeValue */ protected NonThreadSafeValue $address; /** * @phpstan-param ThreadSafeArray $mainToThreadBuffer * @phpstan-param ThreadSafeArray $threadToMainBuffer */ public function __construct( protected ThreadSafeLogger $logger, protected ThreadSafeArray $mainToThreadBuffer, protected ThreadSafeArray $threadToMainBuffer, InternetAddress $address, protected int $serverId, protected int $maxMtuSize, protected int $protocolVersion, protected SleeperHandlerEntry $sleeperEntry ){ $this->mainPath = \pocketmine\PATH; $this->address = new NonThreadSafeValue($address); } /** * @return void */ public function shutdownHandler(){ if($this->cleanShutdown !== true && $this->crashInfo === null){ $error = error_get_last(); if($error !== null){ $this->logger->emergency("Fatal error: " . $error["message"] . " in " . $error["file"] . " on line " . $error["line"]); $this->setCrashInfo(RakLibThreadCrashInfo::fromLastErrorInfo($error)); }else{ $this->logger->emergency("RakLib shutdown unexpectedly"); } } } public function getCrashInfo() : ?RakLibThreadCrashInfo{ return $this->crashInfo?->deserialize(); } private function setCrashInfo(RakLibThreadCrashInfo $info) : void{ $this->synchronized(function() use ($info) : void{ $this->crashInfo = new NonThreadSafeValue($info); $this->notify(); }); } public function startAndWait(int $options = NativeThread::INHERIT_NONE) : void{ $this->start($options); $this->synchronized(function() : void{ while(!$this->ready && $this->crashInfo === null){ $this->wait(); } $crashInfo = $this->crashInfo?->deserialize(); if($crashInfo !== null){ if($crashInfo->getClass() === SocketException::class){ throw new SocketException($crashInfo->getMessage()); } throw new \RuntimeException("RakLib failed to start: " . $crashInfo->makePrettyMessage()); } }); } protected function onRun() : void{ try{ gc_enable(); ini_set("display_errors", '1'); ini_set("display_startup_errors", '1'); register_shutdown_function([$this, "shutdownHandler"]); try{ $socket = new ServerSocket($this->address->deserialize()); }catch(SocketException $e){ $this->setCrashInfo(RakLibThreadCrashInfo::fromThrowable($e)); return; } $manager = new Server( $this->serverId, $this->logger, $socket, $this->maxMtuSize, new SimpleProtocolAcceptor($this->protocolVersion), new UserToRakLibThreadMessageReceiver(new PthreadsChannelReader($this->mainToThreadBuffer)), new RakLibToUserThreadMessageSender(new SnoozeAwarePthreadsChannelWriter($this->threadToMainBuffer, $this->sleeperEntry->createNotifier())), new ExceptionTraceCleaner($this->mainPath) ); $this->synchronized(function() : void{ $this->ready = true; $this->notify(); }); while(!$this->isKilled){ $manager->tickProcessor(); } $manager->waitShutdown(); $this->cleanShutdown = true; }catch(\Throwable $e){ $this->setCrashInfo(RakLibThreadCrashInfo::fromThrowable($e)); $this->logger->logException($e); } } public function getThreadName() : string{ return "RakLib"; } }