diff --git a/src/API/ServerAPI.php b/src/API/ServerAPI.php index 4700ea2fb..8a999a593 100644 --- a/src/API/ServerAPI.php +++ b/src/API/ServerAPI.php @@ -28,7 +28,8 @@ class ServerAPI{ private $apiList = array(); private $asyncCnt = 0; private $rcon; - private $query; + + public $query; //TODO: Instead of hard-coding functions, use PHPDoc-compatible methods to load APIs. @@ -139,6 +140,13 @@ class ServerAPI{ define("DEBUG", $this->getProperty("debug", 1)); define("ADVANCED_CACHE", $this->getProperty("enable-advanced-cache", false)); define("MAX_CHUNK_RATE", 20 / $this->getProperty("max-chunks-per-second", 8)); //Default rate ~512 kB/s + if(ADVANCED_CACHE == true){ + console("[INFO] Advanced cache enabled"); + } + if($this->getProperty("upnp-forwarding") == true){ + console("[INFO] [UPnP] Trying to port forward..."); + UPnP_PortForward($this->getProperty("server-port")); + } $this->server = new PocketMinecraftServer($this->getProperty("server-name"), $this->getProperty("gamemode"), ($seed = $this->getProperty("level-seed")) != "" ? (int) $seed:false, $this->getProperty("server-port"), ($ip = $this->getProperty("server-ip")) != "" ? $ip:"0.0.0.0"); $this->server->api = $this; @@ -146,15 +154,6 @@ class ServerAPI{ console("[INFO] This server is running PocketMine-MP version ".($version->isDev() ? FORMAT_YELLOW:"").MAJOR_VERSION.FORMAT_RESET." \"".CODENAME."\" (MCPE: ".CURRENT_MINECRAFT_VERSION.") (API ".CURRENT_API_VERSION.")", true, true, 0); console("[INFO] PocketMine-MP is distributed under the LGPL License", true, true, 0); - if(ADVANCED_CACHE == true){ - console("[INFO] Advanced cache enabled"); - } - - if($this->getProperty("upnp-forwarding") === true){ - console("[INFO] [UPnP] Trying to port forward..."); - UPnP_PortForward($this->getProperty("server-port")); - } - if($this->getProperty("last-update") === false or ($this->getProperty("last-update") + 3600) < time()){ console("[INFO] Checking for new server version"); console("[INFO] Last check: ".FORMAT_AQUA.date("Y-m-d H:i:s", $this->getProperty("last-update"))."\x1b[0m"); @@ -348,7 +347,7 @@ class ServerAPI{ } if($this->getProperty("enable-query") === true){ - $this->query = new Query(); + $this->query = new QueryHandler(); } CraftingRecipes::init(); $this->server->init(); diff --git a/src/PocketMinecraftServer.php b/src/PocketMinecraftServer.php index 5a646e468..ac655f6cf 100644 --- a/src/PocketMinecraftServer.php +++ b/src/PocketMinecraftServer.php @@ -64,7 +64,7 @@ class PocketMinecraftServer{ $this->saveEnabled = true; $this->tickMeasure = array_fill(0, 40, 0); $this->setType("normal"); - $this->interface = new MinecraftInterface($this, "255.255.255.255", $this->port, true, false, $this->serverip); + $this->interface = new MinecraftInterface("255.255.255.255", $this->port, $this->serverip); $this->stop = false; $this->ticks = 0; if(!defined("NO_THREADS")){ diff --git a/src/network/MinecraftInterface.php b/src/network/MinecraftInterface.php index 1af83104d..dfa9c6b89 100644 --- a/src/network/MinecraftInterface.php +++ b/src/network/MinecraftInterface.php @@ -20,18 +20,16 @@ */ class MinecraftInterface{ - public $client; public $bandwidth; private $socket; private $packets; - function __construct($object, $server, $port = 25565, $listen = false, $client = false, $serverip = "0.0.0.0"){ - $this->socket = new UDPSocket($server, $port, (bool) $listen, $serverip); + function __construct($server, $port = 25565, $serverip = "0.0.0.0"){ + $this->socket = new UDPSocket($server, $port, true, $serverip); if($this->socket->connected === false){ console("[SEVERE] Couldn't bind to $serverip:".$port, true, true, 0); exit(1); } $this->bandwidth = array(0, 0, microtime(true)); - $this->client = (bool) $client; $this->start = microtime(true); $this->packets = array(); } @@ -65,6 +63,12 @@ class MinecraftInterface{ return $parser->packet; } return false; + }elseif($pid === 0xfe and $buffer{1} === "\xfd" and ServerAPI::request()->api->query instanceof QueryHandler){ + $packet = new QueryPacket; + $packet->ip = $source; + $packet->port = $port; + $packet->buffer = $buffer; + ServerAPI::request()->api->query->handle($packet); }else{ $packet = new Packet(); $packet->ip = $source; diff --git a/src/network/Query.php b/src/network/query/QueryHandler.php similarity index 74% rename from src/network/Query.php rename to src/network/query/QueryHandler.php index 3bfaab813..f300695bb 100644 --- a/src/network/Query.php +++ b/src/network/query/QueryHandler.php @@ -24,7 +24,7 @@ Implementation of the UT3 Query Protocol (GameSpot) Source: http://wiki.unrealadmin.org/UT3_query_protocol */ -class Query{ +class QueryHandler{ private $socket, $server, $lastToken, $token, $longData, $timeout; public function __construct(){ @@ -41,7 +41,7 @@ class Query{ Then, the Query class handles itself sending the packets in raw form, because packets can conflict with the MCPE ones. */ - $this->server->addHandler("server.unknownpacket.254", array($this, "packetHandler"), 50); + $this->server->schedule(20 * 30, array($this, "regenerateToken"), array(), true); $this->regenerateToken(); $this->lastToken = $this->token; @@ -94,48 +94,41 @@ class Query{ $this->token = Utils::readInt("\x00".Utils::getRandomBytes(3, false)); } - public function packetHandler($packet, $event){ - if($event !== "server.unknownpacket.254"){ - return; - } - $magic = substr($packet->buffer, 0, 2); - $offset = 2; - if($magic !== "\xfe\xfd"){ - return; - } - $type = ord($packet->buffer{2}); - ++$offset; - $sessionID = Utils::readInt(substr($packet->buffer, $offset, 4)); - $offset += 4; - $payload = substr($packet->buffer, $offset); - switch($type){ - case 9: //Handshake - $pk = new Packet; + public function handle(QueryPacket $packet){ + $packet->decode(); + switch($packet->packetType){ + case QueryPacket::HANDSHAKE: //Handshake + $pk = new QueryPacket; $pk->ip = $packet->ip; $pk->port = $packet->port; - $pk->buffer = chr(9).Utils::writeInt($sessionID).$this->token."\x00"; + $pk->packetType = QueryPacket::HANDSHAKE; + $pk->sessionID = $packet->sessionID; + $pk->payload = $this->token."\x00"; + $pk->encode(); $this->server->send($pk); break; - case 0: //Stat - $token = Utils::readInt(substr($payload, 0, 4)); + case QueryPacket::STATISTICS: //Stat + $token = Utils::readInt(substr($packet->payload, 0, 4)); if($token !== $this->token and $token !== $this->lastToken){ break; } - $pk = new Packet; + $pk = new QueryPacket; $pk->ip = $packet->ip; - $pk->port = $packet->port; - if(strlen($payload) === 8){ + $pk->port = $packet->port; + $pk->packetType = QueryPacket::STATISTICS; + $pk->sessionID = $packet->sessionID; + if(strlen($packet->payload) === 8){ if($this->timeout < microtime(true)){ $this->regenerateInfo(); } - $pk->buffer = chr(0).Utils::writeInt($sessionID).$this->longData; + $pk->payload = $this->longData; }else{ - $pk->buffer = chr(0).Utils::writeInt($sessionID).$this->server->name."\x00".(($this->server->gamemode & 0x01) === 0 ? "SMP":"CMP")."\x00".$this->server->api->level->getDefault()->getName()."\x00".count($this->server->clients)."\x00".$this->server->maxClients."\x00".Utils::writeLShort($this->server->api->getProperty("server-port")).$this->server->api->getProperty("server-ip", "0.0.0.0")."\x00"; + $pk->payload = $this->server->name."\x00".(($this->server->gamemode & 0x01) === 0 ? "SMP":"CMP")."\x00".$this->server->api->level->getDefault()->getName()."\x00".count($this->server->clients)."\x00".$this->server->maxClients."\x00".Utils::writeLShort($this->server->api->getProperty("server-port")).$this->server->api->getProperty("server-ip", "0.0.0.0")."\x00"; } + $pk->encode(); $this->server->send($pk); break; } - return true; } -} +} \ No newline at end of file diff --git a/src/network/query/QueryPacket.php b/src/network/query/QueryPacket.php new file mode 100644 index 000000000..d0f0f148c --- /dev/null +++ b/src/network/query/QueryPacket.php @@ -0,0 +1,41 @@ +packetType = ord($this->buffer{2}); + $this->sessionID = Utils::readInt(substr($this->buffer, 3, 4)); + $this->payload = substr($this->buffer, 7); + } + + public function encode(){ + $this->buffer .= chr($this->packetType); + $this->buffer .= Utils::writeInt($this->sessionID); + $this->buffer .= $this->payload; + } +} \ No newline at end of file