├── releases └── CombinedSlots-1.0.0.phar ├── README.md ├── plugin.yml ├── src └── basprohop │ ├── libraries │ ├── MinecraftQueryException.php │ └── MinecraftQuery.php │ └── CombinedSlots.php ├── resources └── config.yml └── LICENSE.md /releases/CombinedSlots-1.0.0.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faiqsohail/CombinedSlots/HEAD/releases/CombinedSlots-1.0.0.phar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CombinedSlots 2 | PocketMine-MP Plugin: Combine the slots of your servers along with online players and displays it as one! Query must be enabled for this plugin to work! 3 | -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | name: CombinedSlots 2 | main: basprohop\CombinedSlots 3 | version: 1.0.0 4 | api: [1.10.0] 5 | load: STARTUP 6 | author: basprohop 7 | description: Combines the slots of your servers and displays it as one! -------------------------------------------------------------------------------- /src/basprohop/libraries/MinecraftQueryException.php: -------------------------------------------------------------------------------- 1 | getDataFolder()); 17 | $this->saveDefaultConfig(); 18 | 19 | $this->timeout = $this->getConfig()->get("timeout"); 20 | 21 | $servers = $this->getConfig()->get("servers"); 22 | foreach($servers as $server){ 23 | $this->server[]=$server; 24 | } 25 | 26 | $this->Query = new MinecraftQuery(); 27 | $this->getServer()->getPluginManager()->registerEvents($this, $this); 28 | 29 | } 30 | 31 | public function queryRegen(QueryRegenerateEvent $ev) 32 | { 33 | $totalPlayers = 0; 34 | $maxPlayers = 0; 35 | foreach ($this->server as $server) { 36 | $server = explode(":", $server); 37 | try { 38 | $this->Query->Connect($server[0], $server[1], $this->timeout); 39 | $array = ($this->Query->GetInfo()); 40 | 41 | $totalPlayers = $totalPlayers + $array['Players']; 42 | $maxPlayers = $maxPlayers + $array['MaxPlayers']; 43 | } catch (MinecraftQueryException $e) { 44 | $this->getLogger()->critical($e->getMessage()); 45 | } 46 | } 47 | $localPlayersCount = count($this->getServer()->getOnlinePlayers()); 48 | $localMaxPlayerCount = $this->getServer()->getMaxPlayers(); 49 | 50 | $ev->setPlayerCount($localPlayersCount + $totalPlayers); 51 | $ev->setMaxPlayerCount($localMaxPlayerCount + $maxPlayers); 52 | 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/basprohop/libraries/MinecraftQuery.php: -------------------------------------------------------------------------------- 1 | Socket = @FSockOpen('udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, $Timeout); 25 | if ($ErrNo || $this->Socket === false) { 26 | throw new MinecraftQueryException('Could not create socket: ' . $ErrStr); 27 | } 28 | Stream_Set_Timeout($this->Socket, $Timeout); 29 | Stream_Set_Blocking($this->Socket, true); 30 | 31 | try { 32 | $Challenge = $this->GetChallenge(); 33 | 34 | $this->GetStatus($Challenge); 35 | } // We catch this because we want to close the socket, not very elegant 36 | catch (MinecraftQueryException $e) { 37 | FClose($this->Socket); 38 | 39 | throw new MinecraftQueryException($e->getMessage()); 40 | } 41 | 42 | FClose($this->Socket); 43 | } 44 | 45 | public function GetInfo() { 46 | return isset($this->Info) ? $this->Info : false; 47 | } 48 | 49 | public function GetPlayers() { 50 | return isset($this->Players) ? $this->Players : false; 51 | } 52 | 53 | private function GetChallenge() { 54 | $Data = $this->WriteData(self :: HANDSHAKE); 55 | 56 | if ($Data === false) { 57 | throw new MinecraftQueryException('Failed to receive challenge.'); 58 | } 59 | 60 | return Pack('N', $Data); 61 | } 62 | 63 | private function GetStatus($Challenge) { 64 | $Data = $this->WriteData(self :: STATISTIC, $Challenge . Pack('c*', 0x00, 0x00, 0x00, 0x00)); 65 | 66 | if (!$Data) { 67 | throw new MinecraftQueryException('Failed to receive status.'); 68 | } 69 | 70 | $Last = ''; 71 | $Info = Array(); 72 | 73 | $Data = SubStr($Data, 11); // splitnum + 2 int 74 | $Data = Explode("\x00\x00\x01player_\x00\x00", $Data); 75 | 76 | if (Count($Data) !== 2) { 77 | throw new MinecraftQueryException('Failed to parse server\'s response.'); 78 | } 79 | 80 | $Players = SubStr($Data[1], 0, -2); 81 | $Data = Explode("\x00", $Data[0]); 82 | 83 | // Array with known keys in order to validate the result 84 | // It can happen that server sends custom strings containing bad things (who can know!) 85 | $Keys = Array( 86 | 'hostname' => 'HostName', 87 | 'gametype' => 'GameType', 88 | 'version' => 'Version', 89 | 'plugins' => 'Plugins', 90 | 'map' => 'Map', 91 | 'numplayers' => 'Players', 92 | 'maxplayers' => 'MaxPlayers', 93 | 'hostport' => 'HostPort', 94 | 'hostip' => 'HostIp', 95 | 'game_id' => 'GameName' 96 | ); 97 | 98 | foreach ($Data as $Key => $Value) { 99 | if (~$Key & 1) { 100 | if (!Array_Key_Exists($Value, $Keys)) { 101 | $Last = false; 102 | continue; 103 | } 104 | 105 | $Last = $Keys[$Value]; 106 | $Info[$Last] = ''; 107 | } else if ($Last != false) { 108 | $Info[$Last] = mb_convert_encoding($Value, 'UTF-8'); 109 | } 110 | } 111 | 112 | // Ints 113 | $Info['Players'] = IntVal($Info['Players']); 114 | $Info['MaxPlayers'] = IntVal($Info['MaxPlayers']); 115 | $Info['HostPort'] = IntVal($Info['HostPort']); 116 | 117 | // Parse "plugins", if any 118 | if ($Info['Plugins']) { 119 | $Data = Explode(": ", $Info['Plugins'], 2); 120 | 121 | $Info['RawPlugins'] = $Info['Plugins']; 122 | $Info['Software'] = $Data[0]; 123 | 124 | if (Count($Data) == 2) { 125 | $Info['Plugins'] = Explode("; ", $Data[1]); 126 | } 127 | } else { 128 | $Info['Software'] = 'Vanilla'; 129 | } 130 | 131 | $this->Info = $Info; 132 | 133 | if (empty($Players)) { 134 | $this->Players = null; 135 | } else { 136 | $this->Players = Explode("\x00", $Players); 137 | } 138 | } 139 | 140 | private function WriteData($Command, $Append = "") { 141 | $Command = Pack('c*', 0xFE, 0xFD, $Command, 0x01, 0x02, 0x03, 0x04) . $Append; 142 | $Length = StrLen($Command); 143 | 144 | if ($Length !== FWrite($this->Socket, $Command, $Length)) { 145 | throw new MinecraftQueryException("Failed to write on socket."); 146 | } 147 | 148 | $Data = FRead($this->Socket, 4096); 149 | 150 | if ($Data === false) { 151 | throw new MinecraftQueryException("Failed to read from socket."); 152 | } 153 | 154 | if (StrLen($Data) < 5 || $Data[0] != $Command[2]) { 155 | return false; 156 | } 157 | 158 | return SubStr($Data, 5); 159 | } 160 | } 161 | --------------------------------------------------------------------------------