mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 01:46:04 +00:00
DataPacketSendEvent and DataPacketReceiveEvent will no longer capture BatchPackets In most places strings are now used instead of DataPackets, to remove limitations on what data can be sent to a network interface Removed CraftingManager's cyclic dependency on Server There is a lot more work to do aside from this, but this commit is intended to clean up what is necessary to fix the handling of BatchPacket.
240 lines
7.5 KiB
PHP
240 lines
7.5 KiB
PHP
<?php
|
|
|
|
/*
|
|
*
|
|
* ____ _ _ __ __ _ __ __ ____
|
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* @author PocketMine Team
|
|
* @link http://www.pocketmine.net/
|
|
*
|
|
*
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace pocketmine\network\mcpe;
|
|
|
|
use pocketmine\event\player\PlayerCreationEvent;
|
|
use pocketmine\network\AdvancedNetworkInterface;
|
|
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
|
use pocketmine\network\Network;
|
|
use pocketmine\Player;
|
|
use pocketmine\Server;
|
|
use pocketmine\snooze\SleeperNotifier;
|
|
use raklib\protocol\EncapsulatedPacket;
|
|
use raklib\protocol\PacketReliability;
|
|
use raklib\RakLib;
|
|
use raklib\server\RakLibServer;
|
|
use raklib\server\ServerHandler;
|
|
use raklib\server\ServerInstance;
|
|
use raklib\utils\InternetAddress;
|
|
|
|
class RakLibInterface implements ServerInstance, AdvancedNetworkInterface{
|
|
/**
|
|
* Sometimes this gets changed when the MCPE-layer protocol gets broken to the point where old and new can't
|
|
* communicate. It's important that we check this to avoid catastrophes.
|
|
*/
|
|
private const MCPE_RAKNET_PROTOCOL_VERSION = 8;
|
|
|
|
private const MCPE_RAKNET_PACKET_ID = "\xfe";
|
|
|
|
/** @var Server */
|
|
private $server;
|
|
|
|
/** @var Network */
|
|
private $network;
|
|
|
|
/** @var RakLibServer */
|
|
private $rakLib;
|
|
|
|
/** @var Player[] */
|
|
private $players = [];
|
|
|
|
/** @var string[] */
|
|
private $identifiers = [];
|
|
|
|
/** @var int[] */
|
|
private $identifiersACK = [];
|
|
|
|
/** @var ServerHandler */
|
|
private $interface;
|
|
|
|
/** @var SleeperNotifier */
|
|
private $sleeper;
|
|
|
|
public function __construct(Server $server){
|
|
$this->server = $server;
|
|
|
|
$this->sleeper = new SleeperNotifier();
|
|
$server->getTickSleeper()->addNotifier($this->sleeper, function() : void{
|
|
$this->server->getNetwork()->processInterface($this);
|
|
});
|
|
|
|
$this->rakLib = new RakLibServer(
|
|
$this->server->getLogger(),
|
|
\pocketmine\COMPOSER_AUTOLOADER_PATH,
|
|
new InternetAddress($this->server->getIp(), $this->server->getPort(), 4),
|
|
(int) $this->server->getProperty("network.max-mtu-size", 1492),
|
|
self::MCPE_RAKNET_PROTOCOL_VERSION,
|
|
$this->sleeper
|
|
);
|
|
$this->interface = new ServerHandler($this->rakLib, $this);
|
|
}
|
|
|
|
public function start() : void{
|
|
$this->rakLib->start(PTHREADS_INHERIT_CONSTANTS | PTHREADS_INHERIT_INI); //HACK: MainLogger needs INI and constants
|
|
}
|
|
|
|
public function setNetwork(Network $network) : void{
|
|
$this->network = $network;
|
|
}
|
|
|
|
public function process() : void{
|
|
while($this->interface->handlePacket()){}
|
|
|
|
if(!$this->rakLib->isRunning() and !$this->rakLib->isShutdown()){
|
|
throw new \Exception("RakLib Thread crashed");
|
|
}
|
|
}
|
|
|
|
public function closeSession(string $identifier, string $reason) : void{
|
|
if(isset($this->players[$identifier])){
|
|
$player = $this->players[$identifier];
|
|
unset($this->identifiers[spl_object_hash($player)]);
|
|
unset($this->players[$identifier]);
|
|
unset($this->identifiersACK[$identifier]);
|
|
$player->close($player->getLeaveMessage(), $reason);
|
|
}
|
|
}
|
|
|
|
public function close(Player $player, string $reason = "unknown reason") : void{
|
|
if(isset($this->identifiers[$h = spl_object_hash($player)])){
|
|
unset($this->players[$this->identifiers[$h]]);
|
|
unset($this->identifiersACK[$this->identifiers[$h]]);
|
|
$this->interface->closeSession($this->identifiers[$h], $reason);
|
|
unset($this->identifiers[$h]);
|
|
}
|
|
}
|
|
|
|
public function shutdown() : void{
|
|
$this->server->getTickSleeper()->removeNotifier($this->sleeper);
|
|
$this->interface->shutdown();
|
|
}
|
|
|
|
public function emergencyShutdown() : void{
|
|
$this->server->getTickSleeper()->removeNotifier($this->sleeper);
|
|
$this->interface->emergencyShutdown();
|
|
}
|
|
|
|
public function openSession(string $identifier, string $address, int $port, int $clientID) : void{
|
|
$ev = new PlayerCreationEvent($this, Player::class, Player::class, $address, $port);
|
|
$this->server->getPluginManager()->callEvent($ev);
|
|
$class = $ev->getPlayerClass();
|
|
|
|
$player = new $class($this, $ev->getAddress(), $ev->getPort());
|
|
$this->players[$identifier] = $player;
|
|
$this->identifiersACK[$identifier] = 0;
|
|
$this->identifiers[spl_object_hash($player)] = $identifier;
|
|
$this->server->addPlayer($player);
|
|
}
|
|
|
|
public function handleEncapsulated(string $identifier, EncapsulatedPacket $packet, int $flags) : void{
|
|
if(isset($this->players[$identifier])){
|
|
//get this now for blocking in case the player was closed before the exception was raised
|
|
$address = $this->players[$identifier]->getAddress();
|
|
try{
|
|
if($packet->buffer !== "" and $packet->buffer{0} === self::MCPE_RAKNET_PACKET_ID){ //Batch
|
|
$this->players[$identifier]->getNetworkSession()->handleEncoded(substr($packet->buffer, 1));
|
|
}
|
|
}catch(\Throwable $e){
|
|
$logger = $this->server->getLogger();
|
|
$logger->debug("EncapsulatedPacket 0x" . bin2hex($packet->buffer));
|
|
$logger->logException($e);
|
|
|
|
$this->interface->blockAddress($address, 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function blockAddress(string $address, int $timeout = 300) : void{
|
|
$this->interface->blockAddress($address, $timeout);
|
|
}
|
|
|
|
public function unblockAddress(string $address) : void{
|
|
$this->interface->unblockAddress($address);
|
|
}
|
|
|
|
public function handleRaw(string $address, int $port, string $payload) : void{
|
|
$this->server->handlePacket($this, $address, $port, $payload);
|
|
}
|
|
|
|
public function sendRawPacket(string $address, int $port, string $payload) : void{
|
|
$this->interface->sendRaw($address, $port, $payload);
|
|
}
|
|
|
|
public function notifyACK(string $identifier, int $identifierACK) : void{
|
|
|
|
}
|
|
|
|
public function setName(string $name) : void{
|
|
$info = $this->server->getQueryInformation();
|
|
|
|
$this->interface->sendOption("name", implode(";",
|
|
[
|
|
"MCPE",
|
|
rtrim(addcslashes($name, ";"), '\\'),
|
|
ProtocolInfo::CURRENT_PROTOCOL,
|
|
ProtocolInfo::MINECRAFT_VERSION_NETWORK,
|
|
$info->getPlayerCount(),
|
|
$info->getMaxPlayerCount(),
|
|
$this->rakLib->getServerId(),
|
|
$this->server->getName(),
|
|
Server::getGamemodeName($this->server->getGamemode())
|
|
]) . ";"
|
|
);
|
|
}
|
|
|
|
public function setPortCheck(bool $name) : void{
|
|
$this->interface->sendOption("portChecking", $name);
|
|
}
|
|
|
|
public function handleOption(string $option, string $value) : void{
|
|
if($option === "bandwidth"){
|
|
$v = unserialize($value);
|
|
$this->network->addStatistics($v["up"], $v["down"]);
|
|
}
|
|
}
|
|
|
|
public function putPacket(Player $player, string $payload, bool $needACK = false, bool $immediate = true) : ?int{
|
|
if(isset($this->identifiers[$h = spl_object_hash($player)])){
|
|
$identifier = $this->identifiers[$h];
|
|
|
|
$pk = new EncapsulatedPacket();
|
|
$pk->identifierACK = $this->identifiersACK[$identifier]++;
|
|
$pk->buffer = self::MCPE_RAKNET_PACKET_ID . $payload;
|
|
$pk->reliability = PacketReliability::RELIABLE_ORDERED;
|
|
$pk->orderChannel = 0;
|
|
|
|
$this->interface->sendEncapsulated($identifier, $pk, ($needACK ? RakLib::FLAG_NEED_ACK : 0) | ($immediate ? RakLib::PRIORITY_IMMEDIATE : RakLib::PRIORITY_NORMAL));
|
|
return $pk->identifierACK;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function updatePing(string $identifier, int $pingMS) : void{
|
|
if(isset($this->players[$identifier])){
|
|
$this->players[$identifier]->updatePing($pingMS);
|
|
}
|
|
}
|
|
}
|