Commit abb66d39 authored by jan.koester's avatar jan.koester
Browse files

fixed

parent f88a0c3f
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
#include <iostream>

#include "hldsview.h"
#include "exception.h"

int main(int argc, char *argv[]){
    if(argc < 3){
        std::cerr << "Usage: " << argv[0] << " <address> <port>" << std::endl;
        return 1;
    }
    try{
        gameinfo::HldsView hlds(argv[1], atoi(argv[2]));

@@ -23,7 +28,9 @@ int main(int argc, char *argv[]){
        std::cout << "Players: " << hldsdata.Players  << "("
                  << hldsdata.BotsAmount << ")" << "/"
                  << hldsdata.MaxPlayers << std::endl;
    }catch(char &e){
        std::cerr << e << std::endl;
    }catch(gameinfo::GameInfoException &e){
        std::cerr << "GameInfo Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}
+71 −71
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include <algorithm>
#include <cstring>
#include <thread>
#include <sstream>
#include <iomanip>

#include <netplus/exception.h>

@@ -79,17 +81,17 @@ void gameinfo::HldsView::refresh(HldsData &info) {
    auto start = std::chrono::steady_clock::now();
    auto deadline = start + std::chrono::milliseconds(1000);

    auto recvWithRetry = [&](netplus::buffer& buf) -> size_t {
    auto recvWithRetry = [&](netplus::buffer& buf, size_t capacity) -> size_t {
        while (std::chrono::steady_clock::now() < deadline) {
            try {
                buf.size = capacity;  // reset to full capacity before each recv
                return clientSocket->recvData(buf);
            } catch (netplus::NetException& e) {
                if (e.getErrorType() == netplus::NetException::Note) {
                    // kurz warten und erneut probieren
                    std::this_thread::sleep_for(std::chrono::milliseconds(5));
                    continue;
                }
                throw; // echte Fehler
                throw;
            }
        }
        netplus::NetException e;
@@ -105,30 +107,24 @@ void gameinfo::HldsView::refresh(HldsData &info) {
        clientSocket->sendData(send);

        netplus::buffer hrecv(1400);
        size_t rcv = recvWithRetry(hrecv);

        if (rcv > 8 && (uint8_t)hrecv.data.buf[4] == 0x41) {
            std::memcpy(challange, hrecv.data.buf + 5, 4);
        }
        const size_t hrecv_capacity = 1400;

        for (;;) {
            if (challange[0] != 0) {
            size_t rcv = recvWithRetry(hrecv, hrecv_capacity);

            if (rcv >= 9 && (uint8_t)hrecv.data.buf[4] == 0x41) {
                // Challenge response: store challenge and resend with it
                std::memcpy(challange, hrecv.data.buf + 5, 4);
                std::memcpy(send.data.buf, payload_template,
                            A2S_INFO_PAYLOAD_LEN_WITHOUT_CHALLENGE);
                std::memcpy(send.data.buf + A2S_INFO_PAYLOAD_CHALLENGE_OFFSET,
                            challange, sizeof(challange));
                send.size = A2S_INFO_PAYLOAD_LEN_WITH_CHALLENGE;
            } else {
                send.size = A2S_INFO_PAYLOAD_LEN_WITHOUT_CHALLENGE;
            }

                clientSocket->sendData(send);

            rcv = recvWithRetry(hrecv);

            if (rcv > 8 && (uint8_t)hrecv.data.buf[4] == 0x41) {
                std::memcpy(challange, hrecv.data.buf + 5, 4);
                continue; // erneut senden
                continue;
            }

            // Should be an A2S_INFO response (0x49)
            _parse(info, hrecv.data.buf, rcv);
            return;
        }
@@ -150,94 +146,98 @@ void gameinfo::HldsView::_parse(HldsData &info,const char* data, size_t len){
    info.Port=sport;
    info.header=0x00;

    for(int i=0; i<len; ++i){
        if(data[i]==0x49){
            info.header=0x49;
            data+=++i;
            len-=i;
        }
    }

    // Helper: hex dump for diagnostics
    auto hexdump = [](const char* buf, size_t n) -> std::string {
        std::ostringstream oss;
        size_t limit = std::min(n, (size_t)64);
        for(size_t i = 0; i < limit; ++i)
            oss << std::hex << std::setfill('0') << std::setw(2)
                << ((unsigned int)(uint8_t)buf[i]) << ' ';
        if(n > limit) oss << "...";
        return oss.str();
    };

    if(info.header!=0x49){
    // A2S_INFO response format: FF FF FF FF 49 <payload...>
    // Minimum: 5 byte header + 1 byte protocol = 6 bytes
    if(len < 6
       || (uint8_t)data[0] != 0xFF || (uint8_t)data[1] != 0xFF
       || (uint8_t)data[2] != 0xFF || (uint8_t)data[3] != 0xFF
       || (uint8_t)data[4] != 0x49){
         GameInfoException e;
         e[GameInfoException::Error] << "_parse: no hlds server!";
         e[GameInfoException::Error] << "_parse: no hlds server! len="
             << (int)len << " hex: " << hexdump(data, len);
         throw e;
    }

    info.header=0x49;
    const char* origData = data;
    size_t origLen = len;
    data+=5;
    len-=5;

    info.protocol=data[0];
    data=data+1;
    len-=1;

    for(int i=0; i<len; ++i){
        if(data[i]=='\0'){
            info.ServerName.assign(data,data+i);
            data+=++i;
            len-=i;
            break;
        }
    }

    for(int i=0; i<len; ++i){
    // Helper: read a null-terminated string and advance
    auto readString = [&](std::string &dest) -> bool {
        for(size_t i=0; i<len; ++i){
            if(data[i]=='\0'){
            info.MapName.assign(data,data+i);
            data+=++i;
                dest.assign(data,data+i);
                ++i;
                data+=i;
                len-=i;
            break;
                return true;
            }
        }
        return false;
    };

    for(int i=0; i<len; ++i){
        if(data[i]=='\0'){
            info.ModName.assign(data,data+i);
            data+=++i;
            len-=i;
            break;
        }
    if(!readString(info.ServerName) || !readString(info.MapName)
       || !readString(info.ModName) || !readString(info.GameName)){
        GameInfoException e;
        e[GameInfoException::Error] << "_parse: truncated packet (strings) remaining="
            << (int)len << " hex(full): " << hexdump(origData, origLen);
        throw e;
    }


    for(int i=0; i<len; ++i){
        if(data[i]=='\0'){
            info.GameName.assign(data,data+i);
            data+=++i;
            len-=i;
            break;
        }
    // Fixed fields: steamid(2) + players(1) + maxplayers(1) + bots(1)
    //             + servertype(1) + env(1) + visibility(1) + vac(1) = 9 bytes
    if(len < 9){
        GameInfoException e;
        e[GameInfoException::Error] << "_parse: truncated packet (fields)";
        throw e;
    }

    memcpy(&info.steamid,data,sizeof(short));
    data=data+sizeof(short);
    len-=sizeof(short);

    info.Players=data[0];
    info.Players=(uint8_t)data[0];
    data=data+1;
    len-=1;

    info.MaxPlayers=data[0];
    info.MaxPlayers=(uint8_t)data[0];
    data=data+1;
    len-=1;

    info.BotsAmount=data[0];
    info.BotsAmount=(uint8_t)data[0];
    data=data+1;
    len-=1;

    info.servertype=data[0];
    info.servertype=(uint8_t)data[0];
    data=data+1;
    len-=1;

    info.env=data[0];
    info.env=(uint8_t)data[0];
    data=data+1;
    len-=1;

    if(data[0]==1)
        info.pwProtected=true;
    else
        info.pwProtected=false;
    info.pwProtected = (data[0] == 1);
    data=data+1;
    len-=1;

    info.vac=data[0];
    info.vac=(uint8_t)data[0];
    data=data+1;
    len-=1;
}