1: <?php
2:
3: namespace aliuly\common;
4: use pocketmine\utils\Binary;
5:
6: 7: 8:
9: abstract class GetMotd {
10:
11: const MAGIC = "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78";
12:
13: const PING_OPEN_CONNECTION = 0x01;
14:
15: const PONG_OPEN_CONNECTION = 0x1c;
16: 17: 18: 19: 20: 21:
22: static public function query( $Ip, $Port = 19132, $Timeout = 3 ) {
23: if( !is_int( $Timeout ) || $Timeout < 0 ) return "Invalid timeout value";
24: $sock = @fsockopen( 'udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, $Timeout );
25:
26: if( $ErrNo || $sock === false ) return "socket error: " . $ErrStr;
27: Stream_Set_Timeout( $sock, $Timeout );
28: Stream_Set_Blocking( $sock, true );
29:
30: $res = self::pingServer($sock);
31: fclose($sock);
32: return $res;
33: }
34: 35: 36: 37: 38: 39:
40: static protected function pingServer($sock) {
41: $pkt = chr(self::PING_OPEN_CONNECTION).
42: Binary::writeLong(microtime(true)*1000).
43: self::MAGIC;
44: $len = strlen($pkt);
45: if ($len !== fwrite($sock,$pkt,$len)) return "error writing socket";
46: $reply = fread( $sock, 4096 );
47: if ($reply === false) return "error reading socket";
48: 49: 50: 51: 52: 53: 54: 55: 56:
57: if (strlen($reply) < 35
58: || $reply{0} != chr(self::PONG_OPEN_CONNECTION)
59: || substr($reply,17,16) != self::MAGIC) return "invalid response";
60: $res = [
61: "latency" => intval(microtime(true)*1000 - Binary::readLong(substr($reply,1,8))),
62: "serverId" => Binary::readLong(substr($reply,9,8)),
63: ];
64: $plen = Binary::readShort(substr($reply,35,2));
65: $payload = substr($reply,37);
66: if (strlen($payload) > $plen) $payload = substr($payload,$plen);
67: $val = explode(";",$payload);
68: foreach (["mccpp", "motd", "protocol", "client-version", "players",
69: "max-players"] as $i) {
70: if (count($val)) {
71: $res[$i] = array_shift($val);
72: } else {
73: break;
74: }
75: }
76: return $res;
77: }
78: }
79: