felixms / arma-rcon-class-php

A lightweight client for sending commands easily to a BattlEye server.
MIT License
46 stars 22 forks source link

getSocketStream update by steffalon #32

Closed steffalon closed 6 years ago

steffalon commented 6 years ago

Issue #30 #31 fixed

This allows users to listen to a constant socket stream. It also allow users to set amount of streaming loops or debug mode when making changes to the code. Switch case "00" isn't getting used but I decided to keep it there for developers who want to make some changes in ARC script.

felixms commented 6 years ago

Thank you very much, this looks great. 👍 The only thing is, that this function should not echo something out. This output is unwanted for the most devs, because ARC runs in the background of their main program. Another “problem” is your code-style, this library complies with the PSR-2 CS in order to be readable for all devs. And switching the CS multiple times a class is not something preferable 😉 Cheers!

felixms commented 6 years ago

I just remembered, there’s a tool helping you with the style guide: https://github.com/FriendsOfPHP/PHP-CS-Fixer

steffalon commented 6 years ago

I have added PSR-2 style and made all echo's only possible via debug mode.

steffalon commented 6 years ago

@schaeferfelix I think it is all done. This method works good and also can work together with other methods for example say or sayglobal if you remove this: $this->disconnect(); // Runs if process is done.. So no reconnects are needed.

steffalon commented 6 years ago

@schaeferfelix I have found a solution. Now it is even more efficient. It can handle other methods without reconnecting with an extra boolean added called $closeCon.

For example:

$rcon->getSocketStream(1, false, true);
$rcon->command("say -1 test1");
$rcon->getSocketStream(1, false, true);
$rcon->command("say -1 test2");
$rcon->getSocketStream(1, false, true);
$rcon->command("say -1 test3");
$rcon->getSocketStream(1, false, true);
$rcon->sayGlobal("And I didn't even reconnect!");
$rcon->getSocketStream(-1, true, true); // Now keep going without sending any commands.
felixms commented 6 years ago

Very cool! 👍

What do you think about adding a callback closure to the function, which gets called whenever a package is received? Also Taylor Otwell from Laravel lately has stated in his newsletter, that many boolean arguments for a function should be avoided, in order to keep it user friendly. So instead multiple methods should be used.

So we could do some renaming for the public functions like socketLoop(int $loop, closure $callback) and socketLoopClose(int $loop, closure $callback).

Please let me know what you think and if you can implement it, I am currently on vacation and have limited possibilities to work 😄

steffalon commented 6 years ago

Hey @schaeferfelix, is this what you were looking for as 2 seperate functions?

    private $end = 0; // required to remember the sequence.

    /**
    * Get socket and continue streaming and disconnect after looping.
    *
    * @author steffalon (https://github.com/steffalon)
    * @link https://github.com/schaeferfelix/arma-rcon-class-php/issues/30 issue part 1
    * @link https://github.com/schaeferfelix/arma-rcon-class-php/issues/31 issue part 2
    *
    * @param integer $loop      Number of loops through this funtion. By default, (-1) for no ending.
    * @param boolean $debug     Useful boolean for debugging. By default, (false).
    *
    * @return boolean
    */
    public function socketLoopClose($loop = -1, $debug = false)
    {
        if ($this->end !== null) {
            $loop = $this->end + $loop;
        }
        while ($this->end !== $loop) {
            $msg = fread($this->socket, 9000);
            if ($debug) {
                echo preg_replace("/\r|\n/", "", substr($msg, 9)).PHP_EOL;
            }
            $timeout = stream_get_meta_data($this->socket);
            if ($timeout['timed_out']) {
                $this->keepAlive($debug);
            } else {
                $this->end = $this->readPackage($msg);
            }
        }
        $this->end = 0;
        $this->disconnect();
        return true; // Completed
    }

    /**
    * Get socket and continue streaming and don't disconnect after looping.
    *
    * @author steffalon (https://github.com/steffalon)
    * @link https://github.com/schaeferfelix/arma-rcon-class-php/issues/30 issue part 1
    * @link https://github.com/schaeferfelix/arma-rcon-class-php/issues/31 issue part 2
    *
    * @param integer $loop      Number of loops through this funtion. By default, (-1) for no ending.
    * @param boolean $debug     Useful boolean for debugging. By default, (false).
    *
    * @return boolean
    */
    public function socketLoop($loop = -1, $debug = false)
    {
        if ($this->end !== null) {
            $loop = $this->end + $loop;
        }
        while ($this->end !== $loop) {
            $msg = fread($this->socket, 9000);
            if ($debug) {
                echo preg_replace("/\r|\n/", "", substr($msg, 9)).PHP_EOL;
            }
            $timeout = stream_get_meta_data($this->socket);
            if ($timeout['timed_out']) {
                $this->keepAlive($debug);
            } else {
                $this->end = $this->readPackage($msg);
            }
        }
        return true; // Completed
    }

    /**
    * Reads what kind of package it is.
    *
    * @author steffalon (https://github.com/steffalon)
    * @link https://github.com/schaeferfelix/arma-rcon-class-php/issues/30 issue part 1
    * @link https://github.com/schaeferfelix/arma-rcon-class-php/issues/31 issue part 2
    *
    * @param string $msg   message received from BE with unreadable header.
    *
    * @throws \Exception by invalid BERCon login details.
    *
    * @return integer
    */
    private function readPackage($msg)
    {
        $responseCode = unpack('H*', $msg); // Make message usefull for battleye packet by unpacking it to hexbyte.
        $responseCode = str_split(substr($responseCode[1], 12), 2); // Get important hexbytes.
        switch ($responseCode[1]) { // See https://www.battleye.com/downloads/BERConProtocol.txt for packet info.
            case "00": // Login WILL NOT BE USED IN THIS LOOP!
                if ($responseCode[2] == "01") { // Login successful.
                    if ($debug) {
                        echo "Accepted BERCon login.".PHP_EOL;
                    }
                } else { // Otherwise $responseCode[2] == "0x00" (Login failed)
                    if ($debug) {
                        echo "Invalid BERCon login details. This process is getting stopped.".PHP_EOL;
                    }
                    throw new \Exception('Invalid BERCon login details. This process is getting stopped!');
                }
                break;
            case "01": // Send commands by this client.
                if (count($responseCode) == 3) {
                    break;
                }
                if ($responseCode[3] !== "00") { // This package is small.
                    if ($debug) {
                        echo "This is a small package.".PHP_EOL;
                    }
                } else {
                    if ($debug) { //This package is multi-packet.
                        echo "Multi-packet.".PHP_EOL;
                    }
                    // if (debug) var_dump($responseCode); //Useful developer information.
                    //      if ($responseCode[5] == "00") {
                    //      $getAmount = $responseCode[4];
                    //      if (debug) var_dump($getAmount);
                            // }
                }
                break;
            case "02": // Acknowledge as client.
                return $this->acknowledge($this->end, $debug);
                break;
        }
    }

What do you think about adding a callback closure to the function, which gets called whenever a package is received?

I think it is better to build a private function for that.

felixms commented 6 years ago

Yep, a private functions on which the public functions wrap around would be smart. But don’t forget the closure, which gets called with the message as payload $callback($msg). Cheers! Also happy new year to all contributors 🎉

nerdalertdk commented 6 years ago

Hi

To clean stuff up i think the $debug flag should be global and be set in the $options array. this give lesser arguments and the possibility for other to use debug if they come up with a new function.

Also the login part of the stream, is it necessary or can't we use the existing connect function ? (just asking havet, teste the code)

steffalon commented 6 years ago

@nerdalertdk

Hi

To clean stuff up i think the $debug flag should be global and be set in the $options array. this give lesser arguments and the possibility for other to use debug if they come up with a new function.

Also the login part of the stream, is it necessary or can't we use the existing connect function ? (just asking havet, teste the code)

I agree about the $debug boolean being set in $option array. But about the login part, I kept that in code because maybe it could be very useful for some developers. Also it will go into "00" if socketLoopClose() got called and is done looping so the user is disconnected and have to be re authenticate.

steffalon commented 6 years ago

@nerdalertdk @schaeferfelix I have updated it but doesn't contain a closure yet. But I haven't work with closures alot so I have to find out how it works.

nerdalertdk commented 6 years ago

@steffalon since devs "can't/should" change the ARC.php file, the login and "commands by this client" don't make since including.

What you could do was make an readPackageRaw function that just returns the $responseCode that why people can code what the need them self's

steffalon commented 6 years ago

@nerdalertdk like this?

   /**
    * Reads what kind of package it is.
    *
    * @author steffalon (https://github.com/steffalon)
    *
    * @param string $msg    message received from BE with unreadable header.
    *
    * @throws \Exception by invalid BERCon login details.
    *
    * @return integer by package status.
    * @return boolean by login status.
    */
    public function readPackageRaw($msg)
    {
        $responseCode = unpack('H*', $msg); // Make message usefull for battleye packet by unpacking it to hexbyte.
        $responseCode = str_split(substr($responseCode[1], 12), 2); // Get important hexbytes.
        switch ($responseCode[1]) { // See https://www.battleye.com/downloads/BERConProtocol.txt for packet info.
            case "00": // Login
                if ($responseCode[2] == "01") { // Login successful.
                    if ($this->options['debug']) {
                        echo "Accepted BERCon login.".PHP_EOL;
                    }
                    return true; // Login accepted
                } else { // Otherwise $responseCode[2] == "0x00" (Login failed)
                    throw new \Exception('Invalid BERCon login details!');
                    return false;
                }
                break;
            case "01": // Command got send.
                if (count($responseCode) == 3) {
                    break;
                }
                if ($responseCode[3] !== "00") { // This package is small.
                    if ($this->options['debug']) {
                        echo "This is a small package.".PHP_EOL;
                    }
                } else {
                    if ($this->options['debug']) { // This package is multi-packet.
                        echo "Multi-packet.".PHP_EOL;
                    }
                }
                return "01";
                break;
            case "02": // Acknowledge request
                return "02";
                break;
        }
    }
nerdalertdk commented 6 years ago

Hi,

No more like this. that way we can use it the way we want For me ARC is a low level library, it should only do the minimum work. the rest is op for you and what you need for your site.

public function readPackageRaw($msg)
{
        $responseCode = unpack('H*', $msg); // Make message usefull for battleye packet by unpacking it to hexbyte.
        $responseCode = str_split(substr($responseCode[1], 12), 2); // Get important hexbytes.
        return $responseCode;
}
steffalon commented 6 years ago

Alright, it is added to the code. Also I have updated the README.md for that function.