1. The forums will be archived and moved to a read only mode in about 2 weeks (mid march).

Solved MinecraftQuery Error

Discussion in 'Development' started by jasonwynn10, Aug 4, 2017.

  1. jasonwynn10

    jasonwynn10 Moderator Poggit Reviewer

    Messages:
    1,489
    GitHub:
    jasonwynn10
    I have the following class being utilized within my plugin. Every time i attempt to use it, I recieve an exception: Failed to receive challenge. Did something in mcpe change which could be causing the issue or is there some way I can fix it?
    The full plugin is at https://github.com/jasonwynn10/CrossOnlineCount
    PHP:
    <?php
    namespace jasonwynn10\CrossOnlineCount\libs;

    class 
    MinecraftQuery
    {
        
    /*
         * Class written by xPaw
         *
         * Website: http://xpaw.me
         * GitHub: https://github.com/xPaw/PHP-Minecraft-Query
         */
        
    const STATISTIC 0x00;
        const 
    HANDSHAKE 0x09;
        
    /** @var resource $Socket */
        
    private $Socket;
        
    /** @var string[] $Players */
        
    private $Players;
        
    /** @var array|bool $Info */
        
    private $Info;
        
    /**
         * @param $Ip
         * @param int $Port
         * @param int $Timeout
         * @param bool $ResolveSRV
         *
         * @throws MinecraftQueryException
         */
        
    public function Connect$Ip$Port 25565$Timeout 3$ResolveSRV true )
        {
            if( !
    is_int$Timeout ) || $Timeout )
            {
                throw new \
    InvalidArgumentException'Timeout must be an integer.' );
            }
            if( 
    $ResolveSRV )
            {
                
    $this->ResolveSRV$Ip );
            }
            
    $this->Socket = @FSockOpen'udp://' $Ip, (int)$Port$ErrNo null$ErrStr null$Timeout );
            if( 
    $ErrNo || $this->Socket === false )
            {
                throw new 
    MinecraftQueryException'Could not create socket: ' $ErrStr );
            }
            
    Stream_Set_Timeout$this->Socket$Timeout );
            
    Stream_Set_Blocking$this->Sockettrue );
            try
            {
                
    $Challenge $this->GetChallenge( );
                
    $this->GetStatus$Challenge );
            }
                
    // We catch this because we want to close the socket, not very elegant
            
    catch( MinecraftQueryException $e )
            {
                
    FClose$this->Socket );
                throw new 
    MinecraftQueryException$e->getMessage( ) );
            }
            
    FClose$this->Socket );
        }
        
    /**
         * @return string[]|bool
         */
        
    public function GetInfo( )
        {
            return isset( 
    $this->Info ) ? $this->Info false;
        }
        
    /**
         * @return bool|string[]
         */
        
    public function GetPlayers( )
        {
            return isset( 
    $this->Players ) ? $this->Players false;
        }
        
    /**
         * @return string
         * @throws MinecraftQueryException
         */
        
    private function GetChallenge( )
        {
            
    $Data $this->WriteDataself::HANDSHAKE );
            if( 
    $Data === false )
            {
                throw new 
    MinecraftQueryException'Failed to receive challenge.' ); //TODO fix errors
            
    }
            return 
    Pack'N'$Data );
        }
        
    /**
         * @param string $Challenge
         *
         * @throws MinecraftQueryException
         */
        
    private function GetStatusstring $Challenge )
        {
            
    $Data $this->WriteDataself::STATISTIC$Challenge Pack'c*'0x000x000x000x00 ) );
            if( !
    $Data )
            {
                throw new 
    MinecraftQueryException'Failed to receive status.' );
            }
            
    $Last '';
            
    $Info = Array( );
            
    $Data    SubStr$Data11 ); // splitnum + 2 int
            
    $Data    Explode"\x00\x00\x01player_\x00\x00"$Data );
            if( 
    Count$Data ) !== )
            {
                throw new 
    MinecraftQueryException'Failed to parse server\'s response.' );
            }
            
    $Players SubStr$Data], 0, -);
            
    $Data    Explode"\x00"$Data] );
            
    // Array with known keys in order to validate the result
            // It can happen that server sends custom strings containing bad things (who can know!)
            
    $Keys = Array(
                
    'hostname'   => 'HostName',
                
    'gametype'   => 'GameType',
                
    'version'    => 'Version',
                
    'plugins'    => 'Plugins',
                
    'map'        => 'Map',
                
    'numplayers' => 'Players',
                
    'maxplayers' => 'MaxPlayers',
                
    'hostport'   => 'HostPort',
                
    'hostip'     => 'HostIp',
                
    'game_id'    => 'GameName'
            
    );
            foreach( 
    $Data as $Key => $Value )
            {
                if( ~
    $Key )
                {
                    if( !
    Array_Key_Exists$Value$Keys ) )
                    {
                        
    $Last false;
                        continue;
                    }
                    
    $Last $Keys$Value ];
                    
    $Info$Last ] = '';
                }
                else if( 
    $Last != false )
                {
                    
    $Info$Last ] = mb_convert_encoding$Value'UTF-8' );
                }
            }
            
    // Ints
            
    $Info'Players' ]    = IntVal$Info'Players' ] );
            
    $Info'MaxPlayers' ] = IntVal$Info'MaxPlayers' ] );
            
    $Info'HostPort' ]   = IntVal$Info'HostPort' ] );
            
    // Parse "plugins", if any
            
    if( $Info'Plugins' ] )
            {
                
    $Data Explode": "$Info'Plugins' ], );
                
    $Info'RawPlugins' ] = $Info'Plugins' ];
                
    $Info'Software' ]   = $Data];
                if( 
    Count$Data ) == )
                {
                    
    $Info'Plugins' ] = Explode"; "$Data] );
                }
            }
            else
            {
                
    $Info'Software' ] = 'Vanilla';
            }
            
    $this->Info $Info;
            if( empty( 
    $Players ) )
            {
                
    $this->Players null;
            }
            else
            {
                
    $this->Players Explode"\x00"$Players );
            }
        }
        
    /**
         * @param $Command
         * @param string $Append
         *
         * @return bool|string
         * @throws MinecraftQueryException
         */
        
    private function WriteData$Commandstring $Append "" )
        {
            
    $Command Pack'c*'0xFE0xFD$Command0x010x020x030x04 ) . $Append;
            
    $Length  StrLen$Command );
            if( 
    $Length !== FWrite$this->Socket$Command$Length ) )
            {
                throw new 
    MinecraftQueryException"Failed to write on socket." );
            }
            
    $Data FRead$this->Socket4096 );
            if( 
    $Data === false )
            {
                throw new 
    MinecraftQueryException"Failed to read from socket." );
            }
            if( 
    StrLen$Data ) < || $Data] != $Command] )
            {
                return 
    false;
            }
            return 
    SubStr$Data);
        }
        
    /**
         * @param string $Address
         */
        
    private function ResolveSRVstring &$Address)
        {
            if( 
    ip2long$Address ) !== false )
            {
                return;
            }
            
    $Record dns_get_record'_minecraft._tcp.' $AddressDNS_SRV );
            if( empty( 
    $Record ) )
            {
                return;
            }
            if( isset( 
    $Record][ 'target' ] ) )
            {
                
    $Address $Record][ 'target' ];
            }
        }
    }
     
  2. Marabou

    Marabou Baby Zombie

    Messages:
    137
    GitHub:
    wiligangster
    Use a AsyncTask.
     
  3. jasonwynn10

    jasonwynn10 Moderator Poggit Reviewer

    Messages:
    1,489
    GitHub:
    jasonwynn10
    The class is meant to be used in a non-async task
     
  4. Marabou

    Marabou Baby Zombie

    Messages:
    137
    GitHub:
    wiligangster
    This is the cause of your problem.
    Because it sends no socket
     
  5. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,968
    GitHub:
    sof3
    Using AsyncTask or not has nothing to do with whether a socket can be successfully created.

    What values have you got? Did the target server respond with any data, or did you receive nothing at all?
     
    Marabou and Thunder33345 like this.
  6. Marabou

    Marabou Baby Zombie

    Messages:
    137
    GitHub:
    wiligangster
    Create a new file name CheckServer.php.
    PS: I have tested and work fine;
    Sorry for my bad english. I use my mobile
    If you misunderstood me in my explain.
    PHP:
    <?php

    namespace Name;

    use 
    pocketmine\scheduler\AsyncTask;
    use 
    pocketmine\Server;
    use 
    pocketmine\utils\Utils;


    class 
    CheckServer extends AsyncTask {

         public 
    $arr;


         public function 
    __construct(array $arr) {
           
    $this->arr $arr;
         }

         public function 
    onRun() {
           foreach(
    $this->arr as $eid => $ip) {
                if(empty(
    $ip)) {
                    unset(
    $this->arr[$eid]);
                    continue;
              }
              
    $server explode(":"$ip);
      
              
    $Query = new Query\MinecraftQuery( );
            try {
                
    $Query->Connect($server[0], $server[1], );
            }
           catch (\
    Query\MinecraftQueryException $e) {
                return 
    "Host not respond ".$server[0]." error:".$e->getMessage();
            }

             if ((
    $info $Query->GetInfo()) !== false) {
                    
    $this->setResult($info['Players']);
             }
         }

        public function 
    onCompletion(Server $server) {
            if(
    $this->hasResult()) {
                
    $server->getPluginManager()->getPlugin("yourPluginName")->counter $this->getResult();//set the count in the Main.php use the Update() public $counter;
                    //on your onUpdate() add '$this->getServer()->getScheduler()->scheduleAsyncTask(new CheckServer($this->arr));' the '$this->arr' it's for check your array and use on the CheckServer.php ;)
     
                   /*$lines = explode("\n", $entity->getNameTag());
                    $lines[0] = $this->รงounter." Online";
                    $nametag = implode("\n", $lines);
                    $entity->setNameTag($nametag);*/

            
    }
        }
    }
     
    Last edited: Aug 4, 2017
  7. Muqsit

    Muqsit Chicken

    Messages:
    1,548
    GitHub:
    muqsit
    You can call almost any class in an async task. You can even create a new Item instance in an async task. Many think only non-pocketmine related functions can be called in an async task, so I wanted to put my two cents in.
     
    Thunder33345 and Marabou like this.
  8. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,968
    GitHub:
    sof3
    According to :shoghi:, you can even start a server instance in another thread and encounter no problems (if you have the proper configuration) (I think there's a problem with memory management though).
     
    jasonwynn10, Muqsit and Marabou like this.
  9. jasonwynn10

    jasonwynn10 Moderator Poggit Reviewer

    Messages:
    1,489
    GitHub:
    jasonwynn10
    I looked into the issue from the class's repo and found that it is a known problem. I am now using this class instead:
    PHP:
    <?php
    namespace jasonwynn10\CrossOnlineCount\libs;

    /**
     * Class MCPEQuery
     * @package jasonwynn10\CrossOnlineCount\libs
     * @author m1x
     */
    class MCPEQuery
    {
        
    /**
         * @param string $host
         * @param int $port
         * @param int $timeout
         *
         * @return array
         */
        
    static public function query(string $hostint $portint $timeout 2)
        {
            
    $socket = @fsockopen('udp://' $host$port$errno$errstr$timeout);

            if(
    $errno || $socket === false) {
                return [
    'error' => $errstr];
            }

            
    stream_Set_Timeout($socket$timeout);
            
    stream_Set_Blocking($sockettrue);

            
    $randInt mt_rand(1999999999);
            
    $reqPacket "\x01";
            
    $reqPacket .= pack('Q*'$randInt);
            
    $reqPacket .= "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"// magic string
            
    $reqPacket .= pack('Q*'0);

            
    fwrite($socket$reqPacketstrlen($reqPacket));

            
    $response fread($socket4096);

            
    fclose($socket);

            if (empty(
    $response) || $response === false) {
                return [
    'error' => 'server do not answer'];
            }
            if (
    substr($response01) !== "\x1C") {
                return [
    'error' => 'error'];
            }

            
    //        $firstPart = substr($response, 0, 16);
            //        $magic = substr($response, 16, 16);

            
    $serverInfo substr($response35);
            
    $serverInfo preg_replace("#ยง.#"""$serverInfo);
            
    $serverInfo explode(';'$serverInfo);

            return [
                
    'motd' => $serverInfo[1],
                
    'num' => $serverInfo[4],
                
    'max' => $serverInfo[5],
                
    'version' =>  $serverInfo[3],
                
    'platform' => 'PE'
            
    ];
        }
    }
    so far, the new method seems to be working.
     
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.