Implemented QueryPacket and QueryHandler

This commit is contained in:
Shoghi Cervantes 2014-02-10 15:05:12 +01:00
parent b9aa3a0e70
commit 7c12f6ce8a
5 changed files with 82 additions and 45 deletions

View File

@ -28,7 +28,8 @@ class ServerAPI{
private $apiList = array(); private $apiList = array();
private $asyncCnt = 0; private $asyncCnt = 0;
private $rcon; private $rcon;
private $query;
public $query;
//TODO: Instead of hard-coding functions, use PHPDoc-compatible methods to load APIs. //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("DEBUG", $this->getProperty("debug", 1));
define("ADVANCED_CACHE", $this->getProperty("enable-advanced-cache", false)); 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 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 = 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; $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] 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); 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()){ if($this->getProperty("last-update") === false or ($this->getProperty("last-update") + 3600) < time()){
console("[INFO] Checking for new server version"); 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"); 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){ if($this->getProperty("enable-query") === true){
$this->query = new Query(); $this->query = new QueryHandler();
} }
CraftingRecipes::init(); CraftingRecipes::init();
$this->server->init(); $this->server->init();

View File

@ -64,7 +64,7 @@ class PocketMinecraftServer{
$this->saveEnabled = true; $this->saveEnabled = true;
$this->tickMeasure = array_fill(0, 40, 0); $this->tickMeasure = array_fill(0, 40, 0);
$this->setType("normal"); $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->stop = false;
$this->ticks = 0; $this->ticks = 0;
if(!defined("NO_THREADS")){ if(!defined("NO_THREADS")){

View File

@ -20,18 +20,16 @@
*/ */
class MinecraftInterface{ class MinecraftInterface{
public $client;
public $bandwidth; public $bandwidth;
private $socket; private $socket;
private $packets; private $packets;
function __construct($object, $server, $port = 25565, $listen = false, $client = false, $serverip = "0.0.0.0"){ function __construct($server, $port = 25565, $serverip = "0.0.0.0"){
$this->socket = new UDPSocket($server, $port, (bool) $listen, $serverip); $this->socket = new UDPSocket($server, $port, true, $serverip);
if($this->socket->connected === false){ if($this->socket->connected === false){
console("[SEVERE] Couldn't bind to $serverip:".$port, true, true, 0); console("[SEVERE] Couldn't bind to $serverip:".$port, true, true, 0);
exit(1); exit(1);
} }
$this->bandwidth = array(0, 0, microtime(true)); $this->bandwidth = array(0, 0, microtime(true));
$this->client = (bool) $client;
$this->start = microtime(true); $this->start = microtime(true);
$this->packets = array(); $this->packets = array();
} }
@ -65,6 +63,12 @@ class MinecraftInterface{
return $parser->packet; return $parser->packet;
} }
return false; 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{ }else{
$packet = new Packet(); $packet = new Packet();
$packet->ip = $source; $packet->ip = $source;

View File

@ -24,7 +24,7 @@ Implementation of the UT3 Query Protocol (GameSpot)
Source: http://wiki.unrealadmin.org/UT3_query_protocol Source: http://wiki.unrealadmin.org/UT3_query_protocol
*/ */
class Query{ class QueryHandler{
private $socket, $server, $lastToken, $token, $longData, $timeout; private $socket, $server, $lastToken, $token, $longData, $timeout;
public function __construct(){ public function __construct(){
@ -41,7 +41,7 @@ class Query{
Then, the Query class handles itself sending the packets in raw form, because Then, the Query class handles itself sending the packets in raw form, because
packets can conflict with the MCPE ones. 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->server->schedule(20 * 30, array($this, "regenerateToken"), array(), true);
$this->regenerateToken(); $this->regenerateToken();
$this->lastToken = $this->token; $this->lastToken = $this->token;
@ -94,48 +94,41 @@ class Query{
$this->token = Utils::readInt("\x00".Utils::getRandomBytes(3, false)); $this->token = Utils::readInt("\x00".Utils::getRandomBytes(3, false));
} }
public function packetHandler($packet, $event){ public function handle(QueryPacket $packet){
if($event !== "server.unknownpacket.254"){ $packet->decode();
return; switch($packet->packetType){
} case QueryPacket::HANDSHAKE: //Handshake
$magic = substr($packet->buffer, 0, 2); $pk = new QueryPacket;
$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;
$pk->ip = $packet->ip; $pk->ip = $packet->ip;
$pk->port = $packet->port; $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); $this->server->send($pk);
break; break;
case 0: //Stat case QueryPacket::STATISTICS: //Stat
$token = Utils::readInt(substr($payload, 0, 4)); $token = Utils::readInt(substr($packet->payload, 0, 4));
if($token !== $this->token and $token !== $this->lastToken){ if($token !== $this->token and $token !== $this->lastToken){
break; break;
} }
$pk = new Packet; $pk = new QueryPacket;
$pk->ip = $packet->ip; $pk->ip = $packet->ip;
$pk->port = $packet->port; $pk->port = $packet->port;
if(strlen($payload) === 8){ $pk->packetType = QueryPacket::STATISTICS;
$pk->sessionID = $packet->sessionID;
if(strlen($packet->payload) === 8){
if($this->timeout < microtime(true)){ if($this->timeout < microtime(true)){
$this->regenerateInfo(); $this->regenerateInfo();
} }
$pk->buffer = chr(0).Utils::writeInt($sessionID).$this->longData; $pk->payload = $this->longData;
}else{ }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); $this->server->send($pk);
break; break;
} }
return true;
} }
} }

View File

@ -0,0 +1,41 @@
<?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/
*
*
*/
class QueryPacket extends Packet{
const HANDSHAKE = 9;
const STATISTICS = 0;
public $packetType;
public $sessionID;
public $payload;
public function decode(){
$this->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;
}
}