66    private const MCPE_RAKNET_PROTOCOL_VERSION = 11;
 
   68    private const MCPE_RAKNET_PACKET_ID = 
"\xfe";
 
   73    private int $rakServerId;
 
   77    private array $sessions = [];
 
   82    private int $sleeperNotifierId;
 
   88    public function __construct(
 
   98        $this->packetBroadcaster = $packetBroadcaster;
 
   99        $this->entityEventBroadcaster = $entityEventBroadcaster;
 
  100        $this->typeConverter = $typeConverter;
 
  102        $this->rakServerId = mt_rand(0, PHP_INT_MAX);
 
  104        $sleeperEntry = $this->
server->getTickSleeper()->addNotifier(
function() : 
void{
 
  105            Timings::$connection->startTiming();
 
  107                while($this->eventReceiver->handle($this));
 
  109                Timings::$connection->stopTiming();
 
  112        $this->sleeperNotifierId = $sleeperEntry->getNotifierId();
 
  115        $mainToThreadBuffer = 
new ThreadSafeArray();
 
  117        $threadToMainBuffer = 
new ThreadSafeArray();
 
  120            $this->
server->getLogger(),
 
  125            $this->
server->getConfigGroup()->getPropertyInt(YmlServerProperties::NETWORK_MAX_MTU_SIZE, 1492),
 
  126            self::MCPE_RAKNET_PROTOCOL_VERSION,
 
  138        $this->
server->getLogger()->debug(
"Waiting for RakLib to start...");
 
  140            $this->rakLib->startAndWait();
 
  144        $this->
server->getLogger()->debug(
"RakLib booted successfully");
 
 
  147    public function setNetwork(
Network $network) : void{
 
  148        $this->network = $network;
 
  152        if(!$this->rakLib->isRunning()){
 
  153            $e = $this->rakLib->getCrashInfo();
 
  157            throw new \Exception(
"RakLib Thread crashed without crash information");
 
 
  162        if(isset($this->sessions[$sessionId])){
 
  163            $session = $this->sessions[$sessionId];
 
  164            unset($this->sessions[$sessionId]);
 
  165            $session->onClientDisconnect(match($reason){
 
  166                DisconnectReason::CLIENT_DISCONNECT => KnownTranslationFactory::pocketmine_disconnect_clientDisconnect(),
 
  167                DisconnectReason::PEER_TIMEOUT => KnownTranslationFactory::pocketmine_disconnect_error_timeout(),
 
  168                DisconnectReason::CLIENT_RECONNECT => KnownTranslationFactory::pocketmine_disconnect_clientReconnect(),
 
  169                default => 
"Unknown RakLib disconnect reason (ID $reason)" 
 
  174    public function close(
int $sessionId) : void{
 
  175        if(isset($this->sessions[$sessionId])){
 
  176            unset($this->sessions[$sessionId]);
 
  177            $this->interface->closeSession($sessionId);
 
  182        $this->
server->getTickSleeper()->removeNotifier($this->sleeperNotifierId);
 
  183        $this->rakLib->quit();
 
 
  186    public function onClientConnect(
int $sessionId, 
string $address, 
int $port, 
int $clientID) : void{
 
  189            $this->network->getSessionManager(),
 
  192            $this->packetBroadcaster,
 
  193            $this->entityEventBroadcaster,
 
  195            $this->typeConverter,
 
  199        $this->sessions[$sessionId] = $session;
 
  202    public function onPacketReceive(
int $sessionId, 
string $packet) : void{
 
  203        if(isset($this->sessions[$sessionId])){
 
  204            if($packet === 
"" || $packet[0] !== self::MCPE_RAKNET_PACKET_ID){
 
  205                $this->sessions[$sessionId]->getLogger()->debug(
"Non-FE packet received: " . base64_encode($packet));
 
  209            $session = $this->sessions[$sessionId];
 
  210            $address = $session->getIp();
 
  211            $buf = substr($packet, 1);
 
  212            $name = $session->getDisplayName();
 
  214                $session->handleEncoded($buf);
 
  215            }
catch(PacketHandlingException $e){
 
  216                $logger = $session->getLogger();
 
  218                $session->disconnectWithError(
 
  219                    reason: 
"Bad packet: " . $e->getMessage(),
 
  220                    disconnectScreenMessage: KnownTranslationFactory::pocketmine_disconnect_error_badPacket()
 
  223                $logger->debug(implode(
"\n", Utils::printableExceptionInfo($e)));
 
  225                $this->interface->blockAddress($address, 5);
 
  226            }
catch(\Throwable $e){
 
  228                $this->server->getLogger()->emergency(
"Crash occurred while handling a packet from session: $name");
 
  234    public function blockAddress(
string $address, 
int $timeout = 300) : void{
 
  235        $this->interface->blockAddress($address, $timeout);
 
 
  239        $this->interface->unblockAddress($address);
 
 
  242    public function onRawPacketReceive(
string $address, 
int $port, 
string $payload) : void{
 
  243        $this->network->processRawPacket($this, $address, $port, $payload);
 
  246    public function sendRawPacket(
string $address, 
int $port, 
string $payload) : void{
 
  247        $this->interface->sendRaw($address, $port, $payload);
 
 
  251        $this->interface->addRawPacketFilter($regex);
 
 
  254    public function onPacketAck(
int $sessionId, 
int $identifierACK) : void{
 
  255        if(isset($this->sessions[$sessionId])){
 
  256            $this->sessions[$sessionId]->handleAckReceipt($identifierACK);
 
  260    public function setName(
string $name) : void{
 
  261        $info = $this->
server->getQueryInformation();
 
  263        $this->interface->setName(implode(
";",
 
  266                rtrim(addcslashes($name, 
";"), 
'\\'),
 
  267                ProtocolInfo::CURRENT_PROTOCOL,
 
  268                ProtocolInfo::MINECRAFT_VERSION_NETWORK,
 
  269                $info->getPlayerCount(),
 
  270                $info->getMaxPlayerCount(),
 
  272                $this->server->getName(),
 
  273                match($this->
server->getGamemode()){
 
  274                    GameMode::SURVIVAL => 
"Survival",
 
  275                    GameMode::ADVENTURE => 
"Adventure",
 
  276                    default => 
"Creative" 
  282    public function setPortCheck(
bool $name) : void{
 
  283        $this->interface->setPortCheck($name);
 
  286    public function setPacketLimit(
int $limit) : void{
 
  287        $this->interface->setPacketsPerTickLimit($limit);
 
  290    public function onBandwidthStatsUpdate(
int $bytesSentDiff, 
int $bytesReceivedDiff) : void{
 
  291        $this->network->getBandwidthTracker()->add($bytesSentDiff, $bytesReceivedDiff);
 
  294    public function putPacket(
int $sessionId, 
string $payload, 
bool $immediate = 
true, ?
int $receiptId = 
null) : void{
 
  295        if(isset($this->sessions[$sessionId])){
 
  296            $pk = 
new EncapsulatedPacket();
 
  297            $pk->buffer = self::MCPE_RAKNET_PACKET_ID . $payload;
 
  298            $pk->reliability = PacketReliability::RELIABLE_ORDERED;
 
  299            $pk->orderChannel = 0;
 
  300            $pk->identifierACK = $receiptId;
 
  302            $this->interface->sendEncapsulated($sessionId, $pk, $immediate);
 
  306    public function onPingMeasure(
int $sessionId, 
int $pingMS) : void{
 
  307        if(isset($this->sessions[$sessionId])){
 
  308            $this->sessions[$sessionId]->updatePing($pingMS);