tgstation / tgstation13.org

Website for tgstation13
GNU General Public License v2.0
6 stars 39 forks source link

Optimize old gamebanner code #81

Open valzargaming opened 1 year ago

valzargaming commented 1 year ago

https://github.com/tgstation/tgstation13.org/blob/819893ed40d8a4ca06806341686684e980aaf0fb/getserverdata.php

I was messing around with a port I did for this code ages ago and realized that it was terribly unoptimized, so I took a shot at trying to see if I could make it better. I haven't tested the below code at all, but figured I'd throw it here in case someone wanted to update it at some point.

<?php
if (php_sapi_name() != "cli") {
    return;
}

$error = false;

include("serverinfo.php");

function export($addr, $port, $str)
{
    $error = false;
    if ($str{0} !== '?') {
        $str = ('?' . $str);
    }

    $query = "\x00\x83" . pack('n', strlen($str) + 6) . "\x00\x00\x00\x00\x00" . $str . "\x00";
    $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    stream_set_timeout($server, 2);
    socket_set_option($server, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);
    socket_set_option($server, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 2, 'usec' => 0]);
    if (!socket_connect($server, $addr, $port)) {
        $error = true;
        return "ERROR";
    }

    $bytestosend = strlen($query);
    $bytessent = 0;
    while ($bytessent < $bytestosend) {
        $result = socket_write($server, substr($query, $bytessent), $bytestosend - $bytessent);
        if ($result === FALSE) {
            die(socket_strerror(socket_last_error()));
        }
        $bytessent += $result;
    }

    $result = socket_read($server, 10000, PHP_BINARY_READ);
    socket_close($server);
    if ($result !== "") {
        if ($result{0} === "\x00" || $result{1} === "\x83") {
            $sizebytes = unpack('n', $result{2} . $result{3});
            $size = $sizebytes[1] - 1;

            if ($result{4} === "\x2a") {
                $unpackint = unpack('f', $result{5} . $result{6} . $result{7} . $result{8});
                return $unpackint[1];
            } elseif ($result{4} === "\x06") {
                $unpackstr = "";
                $index = 5;

                while ($size > 0) {
                    $size--;
                    $unpackstr .= $result{$index};
                    $index++;
                }
                return $unpackstr;
            }
        }
    }
    $error = true;
    return "ERROR";
}

// Create arrays for sockets and servers
$sockets = array();
$addresses = array();
foreach ($servers as $server) {
    $address = $server['address'];
    $port = $server['port'];
    $key = "$address:$port";
    $addresses[$key] = $address;
    $sockets[$key] = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    socket_set_nonblock($sockets[$key]);
    socket_connect($sockets[$key], $address, $port);
}

// Set timeout for socket_select
$timeout = array('sec' => 0, 'usec' => 1000);

// Create an array to hold the serverinfo data
$serverinfo = array();

// Check if cache file exists and load data if it does
if (file_exists('serverinfo.json')) {
    $cache = json_decode(file_get_contents('serverinfo.json'), true);
}

// Loop through the sockets and get all the data we can
foreach ($sockets as $key => $socket) {
    $address = $addresses[$key];
    $port = explode(':', $key)[1];

    $lastinfo = (is_array($cache) && count($cache) > $n ? $cache[$n] : array());

    $data = export($address, $port, '?status');
    if (is_string($data)) {
        $data = str_replace("\x00", "", $data);
    }

    $variable_value_array = array();

    if ((!$data || strpos($data, 'ERROR') !== false) && (array_key_exists('restarting', $lastinfo))) {
        $variable_value_array['restarting'] = $lastinfo['restarting'] + 1;
    }

    $data_array = explode('&', $data);

    foreach ($data_array as $row) {
        $parts = explode('=', $row);
        if (isset($parts[1])) {
            $variable_value_array[$parts[0]] = $parts[1];
        } else {
            $variable_value_array[$parts[0]] = null;
        }
    }

    $variable_value_array['cachetime'] = time();

    if (array_key_exists('gamestate', $variable_value_array)) {
        if ($variable_value_array['gamestate'] == 4) {
            $variable_value_array['restarting'] = 1;
        }
    }

    $serverinfo[] = $variable_value_array;
}

$jsonserverinfo = json_encode($serverinfo);
file_put_contents('serverinfo.json', $jsonserverinfo);

// Close sockets
foreach ($sockets as $socket) {
    socket_close($socket);
}

I only thought to come back to this code as I was working on optimizing Civ13's bot code for retrieving players, and remembered that I originally developed it using this repository as an example. https://github.com/VZGCoders/Civilizationbot/blob/c2c37167020c468ba6f15b280413fc9359ddbe27/civ13.php#L803