mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2024-11-11 11:59:21 +00:00
156 lines
4.7 KiB
PHP
156 lines
4.7 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\tools\ping_server;
|
|
|
|
use pocketmine\utils\Utils;
|
|
use raklib\protocol\MessageIdentifiers;
|
|
use raklib\protocol\PacketSerializer;
|
|
use raklib\protocol\UnconnectedPing;
|
|
use raklib\protocol\UnconnectedPong;
|
|
use function bin2hex;
|
|
use function count;
|
|
use function fgets;
|
|
use function gethostbynamel;
|
|
use function hrtime;
|
|
use function intdiv;
|
|
use function mt_rand;
|
|
use function ord;
|
|
use function sleep;
|
|
use function socket_bind;
|
|
use function socket_close;
|
|
use function socket_create;
|
|
use function socket_getsockname;
|
|
use function socket_last_error;
|
|
use function socket_recvfrom;
|
|
use function socket_select;
|
|
use function socket_sendto;
|
|
use function socket_strerror;
|
|
use function strlen;
|
|
use function time;
|
|
use function trim;
|
|
use const AF_INET;
|
|
use const MSG_DONTROUTE;
|
|
use const PHP_BINARY;
|
|
use const PHP_INT_MAX;
|
|
use const SOCK_DGRAM;
|
|
use const SOL_UDP;
|
|
use const STDIN;
|
|
|
|
require_once 'vendor/autoload.php';
|
|
|
|
function hrtime_ms() : int{
|
|
return intdiv(hrtime(true), 1_000_000);
|
|
}
|
|
|
|
function read_stdin(string $prompt) : string{
|
|
echo $prompt . ": ";
|
|
$input = fgets(STDIN);
|
|
if($input === false){
|
|
exit(1); //this probably means the user pressed ctrl+c
|
|
}
|
|
return trim($input);
|
|
}
|
|
|
|
function ping_server(\Socket $socket, string $serverIp, int $serverPort, int $timeoutSeconds, int $rakNetClientId) : bool{
|
|
$ping = new UnconnectedPing();
|
|
$ping->sendPingTime = hrtime_ms();
|
|
$ping->clientId = $rakNetClientId;
|
|
$serializer = new PacketSerializer();
|
|
$ping->encode($serializer);
|
|
if(@socket_sendto($socket, $serializer->getBuffer(), strlen($serializer->getBuffer()), MSG_DONTROUTE, $serverIp, $serverPort) === false){
|
|
\GlobalLogger::get()->error("Failed to send ping: " . socket_strerror(socket_last_error($socket)));
|
|
return false;
|
|
}
|
|
\GlobalLogger::get()->info("Ping sent to $serverIp on port $serverPort, waiting for response (press CTRL+C to abort)");
|
|
|
|
$r = [$socket];
|
|
$w = $e = null;
|
|
if(socket_select($r, $w, $e, $timeoutSeconds) === 1){
|
|
$response = @socket_recvfrom($socket, $recvBuffer, 65535, 0, $recvAddr, $recvPort);
|
|
if($response === false){
|
|
\GlobalLogger::get()->error("Error reading from socket: " . socket_strerror(socket_last_error($socket)));
|
|
return false;
|
|
}
|
|
if($recvAddr === $serverIp && $recvPort === $serverPort && $recvBuffer !== "" && ord($recvBuffer[0]) === MessageIdentifiers::ID_UNCONNECTED_PONG){
|
|
$pong = new UnconnectedPong();
|
|
$pong->decode(new PacketSerializer($recvBuffer));
|
|
\GlobalLogger::get()->info("--- Response received ---");
|
|
\GlobalLogger::get()->info("Payload: $pong->serverName");
|
|
\GlobalLogger::get()->info("Response time: " . (hrtime_ms() - $pong->sendPingTime) . " ms");
|
|
return true;
|
|
}else{
|
|
\GlobalLogger::get()->debug("Garbage packet from $recvAddr $recvPort: " . bin2hex($recvBuffer));
|
|
}
|
|
}
|
|
|
|
\GlobalLogger::get()->info("No ping response after $timeoutSeconds seconds");
|
|
return false;
|
|
}
|
|
|
|
if(count($argv) > 3){
|
|
echo "Usage: " . PHP_BINARY . " " . __FILE__ . " [server IP] [server port]\n";
|
|
exit(1);
|
|
}
|
|
|
|
if(count($argv) > 1){
|
|
$hostName = $argv[1];
|
|
}else{
|
|
do{
|
|
$hostName = read_stdin("Server address");
|
|
}while($hostName === "");
|
|
}
|
|
|
|
$serverIps = gethostbynamel($hostName);
|
|
if($serverIps === false){
|
|
\GlobalLogger::get()->critical("Unable to resolve hostname $hostName to an IP address");
|
|
exit(1);
|
|
}
|
|
if(count($serverIps) > 1){
|
|
\GlobalLogger::get()->warning("Multiple IP addresses found for hostname $hostName, using the first one: " . $serverIps[0]);
|
|
}
|
|
$server = $serverIps[0];
|
|
\GlobalLogger::get()->info("Resolved hostname to $server");
|
|
|
|
if(count($argv) > 2){
|
|
$port = (int) $argv[2];
|
|
}elseif(count($argv) > 1){
|
|
$port = 19132;
|
|
}else{
|
|
$portRaw = read_stdin("Server port (empty for 19132)");
|
|
$port = $portRaw === "" ? 19132 : (int) $portRaw;
|
|
}
|
|
|
|
$sock = Utils::assumeNotFalse(socket_create(AF_INET, SOCK_DGRAM, SOL_UDP));
|
|
|
|
socket_bind($sock, "0.0.0.0");
|
|
socket_getsockname($sock, $bindAddr, $bindPort);
|
|
\GlobalLogger::get()->info("Bound to $bindAddr on port $bindPort");
|
|
|
|
$start = time();
|
|
while(time() < $start + 60_000 && !ping_server($sock, $server, $port, 5, mt_rand(0, PHP_INT_MAX))){
|
|
sleep(1);
|
|
}
|
|
|
|
socket_close($sock);
|