From cdee52c9e8fa4e96659b00a3f51869f7ecc295f2 Mon Sep 17 00:00:00 2001 From: Eric Estabrooks Date: Wed, 19 Jun 2019 13:25:07 -0400 Subject: [PATCH] Merging broo's changes --- Makefile.am | 4 +- Makefile.noauto | 4 +- qstat.h | 40 ++- skulltag_huffman.c | 630 +++++++++++++++++++++++++++++++++++++++++++++ skulltag_huffman.h | 42 +++ zandronum.c | 354 +++++++++++++++++++++++++ zandronum.h | 23 ++ 7 files changed, 1094 insertions(+), 3 deletions(-) create mode 100644 skulltag_huffman.c create mode 100644 skulltag_huffman.h create mode 100644 zandronum.c create mode 100644 zandronum.h diff --git a/Makefile.am b/Makefile.am index 24003cbc..55e48b29 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,7 +41,9 @@ qstat_SOURCES = \ farmsim.c farmsim.h \ ksp.c ksp.h \ tf.c tf.h \ - armyops.c armyops.h + armyops.c armyops.h \ + skulltag_huffman.c skulltag_huffman.h \ + zandronum.c zandronum.h dist_configfiles_DATA = qstat.cfg configfilesdir = $(sysconfdir) diff --git a/Makefile.noauto b/Makefile.noauto index 55f36e17..0f7a8517 100644 --- a/Makefile.noauto +++ b/Makefile.noauto @@ -47,7 +47,9 @@ SRC = \ farmsim.c \ ksp.c \ tf.c \ - armyops.c + armyops.c \ + skulltag_huffman.c \ + zandronum.c OBJ = $(SRC:.c=.obj) O = $(SRC:.c=.o) diff --git a/qstat.h b/qstat.h index 0fcc9e18..edd0c54a 100644 --- a/qstat.h +++ b/qstat.h @@ -127,6 +127,7 @@ typedef query_status_t (*PacketFunc)(struct qserver *, char *rawpkt, int pktlen) #include "ksp.h" #include "tf.h" #include "armyops.h" +#include "zandronum.h" /* * Various magic numbers. @@ -273,8 +274,9 @@ typedef query_status_t (*PacketFunc)(struct qserver *, char *rawpkt, int pktlen) #define KSP_PROTOCOL_SERVER 77 #define TF_PROTOCOL_SERVER 78 #define TEE_MASTER 79 +#define ZANDRONUM_SERVER 80 -#define LAST_BUILTIN_SERVER 80 +#define LAST_BUILTIN_SERVER 81 #define TF_SINGLE_QUERY (1 << 1) #define TF_OUTFILE (1 << 2) @@ -3751,6 +3753,42 @@ server_type *find_server_type_string(char *type_string); NULL, /* player_query_func */ deal_with_teemaster_packet, /* packet_func */ }, + { + /* Zandronum server */ + ZANDRONUM_SERVER, /* id */ + "ZAND", /* type_prefix */ + "zand", /* type_string */ + "-zand", /* type_option */ + "Zandronum", /* game_name */ + 0, /* master */ + 10666, /* default_port */ + 0, /* port_offset */ + 0, /* flags */ + "gametype", /* game_rule */ + "ZANDRONUM", /* template_var */ + NULL, /* status_packet */ + 0, /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + NULL, /* display_player_func */ + NULL, /* display_rule_func */ + NULL, /* display_raw_player_func */ + NULL, /* display_raw_rule_func */ + xml_display_player_info, /* display_xml_player_func */ + NULL, /* display_xml_rule_func */ + json_display_player_info, /* display_json_player_func */ + NULL, /* display_json_rule_func */ + send_zandronum_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_zandronum_packet, /* packet_func */ + }, { Q_UNKNOWN_TYPE, /* id */ "", /* type_prefix */ diff --git a/skulltag_huffman.c b/skulltag_huffman.c new file mode 100644 index 00000000..c33960f7 --- /dev/null +++ b/skulltag_huffman.c @@ -0,0 +1,630 @@ +//------------------------------------------------------------------------------- +// +// huffman.c +// +// +// +// Version 5, released 5/13/2009. Compatible with Skulltag launchers and servers. +// +//------------------------------------------------------------------------------- + +#include +#include +#include + +#include +#include + +#define USE_WINDOWS_DWORD +#include "skulltag_huffman.h" +#define M_Malloc malloc + +//***************************************************************************** +// PROTOTYPES + +static void huffman_BuildTree( float *freq ); +static void huffman_PutBit( unsigned char *puf, int pos, int bit ); +static int huffman_GetBit( unsigned char *puf, int pos ); +#ifdef _DEBUG +static void huffman_ZeroFreq( void ); +#endif +static void huffman_RecursiveFreeNode( huffnode_t *pNode ); + +//***************************************************************************** +// VARIABLES + +int LastCompMessageSize = 0; + +static huffnode_t *HuffTree=0; +static hufftab_t HuffLookup[256]; + +#ifdef _DEBUG +static int HuffIn=0; +static int HuffOut=0; +#endif + + +static unsigned char Masks[8]= +{ + 0x1, + 0x2, + 0x4, + 0x8, + 0x10, + 0x20, + 0x40, + 0x80 +}; + +static float HuffFreq[256]= +{ + 0.14473691, + 0.01147017, + 0.00167522, + 0.03831121, + 0.00356579, + 0.03811315, + 0.00178254, + 0.00199644, + 0.00183511, + 0.00225716, + 0.00211240, + 0.00308829, + 0.00172852, + 0.00186608, + 0.00215921, + 0.00168891, + 0.00168603, + 0.00218586, + 0.00284414, + 0.00161833, + 0.00196043, + 0.00151029, + 0.00173932, + 0.00218370, + 0.00934121, + 0.00220530, + 0.00381211, + 0.00185456, + 0.00194675, + 0.00161977, + 0.00186680, + 0.00182071, + 0.06421956, + 0.00537786, + 0.00514019, + 0.00487155, + 0.00493925, + 0.00503143, + 0.00514019, + 0.00453520, + 0.00454241, + 0.00485642, + 0.00422407, + 0.00593387, + 0.00458130, + 0.00343687, + 0.00342823, + 0.00531592, + 0.00324890, + 0.00333388, + 0.00308613, + 0.00293776, + 0.00258918, + 0.00259278, + 0.00377105, + 0.00267488, + 0.00227516, + 0.00415997, + 0.00248763, + 0.00301555, + 0.00220962, + 0.00206990, + 0.00270369, + 0.00231694, + 0.00273826, + 0.00450928, + 0.00384380, + 0.00504728, + 0.00221251, + 0.00376961, + 0.00232990, + 0.00312574, + 0.00291688, + 0.00280236, + 0.00252436, + 0.00229461, + 0.00294353, + 0.00241201, + 0.00366590, + 0.00199860, + 0.00257838, + 0.00225860, + 0.00260646, + 0.00187256, + 0.00266552, + 0.00242641, + 0.00219450, + 0.00192082, + 0.00182071, + 0.02185930, + 0.00157439, + 0.00164353, + 0.00161401, + 0.00187544, + 0.00186248, + 0.03338637, + 0.00186968, + 0.00172132, + 0.00148509, + 0.00177749, + 0.00144620, + 0.00192442, + 0.00169683, + 0.00209439, + 0.00209439, + 0.00259062, + 0.00194531, + 0.00182359, + 0.00159096, + 0.00145196, + 0.00128199, + 0.00158376, + 0.00171412, + 0.00243433, + 0.00345704, + 0.00156359, + 0.00145700, + 0.00157007, + 0.00232342, + 0.00154198, + 0.00140730, + 0.00288807, + 0.00152830, + 0.00151246, + 0.00250203, + 0.00224420, + 0.00161761, + 0.00714383, + 0.08188576, + 0.00802537, + 0.00119484, + 0.00123805, + 0.05632671, + 0.00305156, + 0.00105584, + 0.00105368, + 0.00099246, + 0.00090459, + 0.00109473, + 0.00115379, + 0.00261223, + 0.00105656, + 0.00124381, + 0.00100326, + 0.00127550, + 0.00089739, + 0.00162481, + 0.00100830, + 0.00097229, + 0.00078864, + 0.00107240, + 0.00084409, + 0.00265760, + 0.00116891, + 0.00073102, + 0.00075695, + 0.00093916, + 0.00106880, + 0.00086786, + 0.00185600, + 0.00608367, + 0.00133600, + 0.00075695, + 0.00122077, + 0.00566955, + 0.00108249, + 0.00259638, + 0.00077063, + 0.00166586, + 0.00090387, + 0.00087074, + 0.00084914, + 0.00130935, + 0.00162409, + 0.00085922, + 0.00093340, + 0.00093844, + 0.00087722, + 0.00108249, + 0.00098598, + 0.00095933, + 0.00427593, + 0.00496661, + 0.00102775, + 0.00159312, + 0.00118404, + 0.00114947, + 0.00104936, + 0.00154342, + 0.00140082, + 0.00115883, + 0.00110769, + 0.00161112, + 0.00169107, + 0.00107816, + 0.00142747, + 0.00279804, + 0.00085922, + 0.00116315, + 0.00119484, + 0.00128559, + 0.00146204, + 0.00130215, + 0.00101551, + 0.00091756, + 0.00161184, + 0.00236375, + 0.00131872, + 0.00214120, + 0.00088875, + 0.00138570, + 0.00211960, + 0.00094060, + 0.00088083, + 0.00094564, + 0.00090243, + 0.00106160, + 0.00088659, + 0.00114514, + 0.00095861, + 0.00108753, + 0.00124165, + 0.00427016, + 0.00159384, + 0.00170547, + 0.00104431, + 0.00091395, + 0.00095789, + 0.00134681, + 0.00095213, + 0.00105944, + 0.00094132, + 0.00141883, + 0.00102127, + 0.00101911, + 0.00082105, + 0.00158448, + 0.00102631, + 0.00087938, + 0.00139290, + 0.00114658, + 0.00095501, + 0.00161329, + 0.00126542, + 0.00113218, + 0.00123661, + 0.00101695, + 0.00112930, + 0.00317976, + 0.00085346, + 0.00101190, + 0.00189849, + 0.00105728, + 0.00186824, + 0.00092908, + 0.00160896, +}; + +//============================================================================= +// +// HUFFMAN_Construct +// +// Builds the Huffman tree. +// +//============================================================================= + +void HUFFMAN_Construct( void ) +{ +#ifdef _DEBUG + huffman_ZeroFreq(); +#endif + + huffman_BuildTree(HuffFreq); + + // Free the table when the program closes. + atexit( HUFFMAN_Destruct ); +} + +//============================================================================= +// +// HUFFMAN_Destruct +// +// Frees memory allocated by the Huffman tree. +// +//============================================================================= + +void HUFFMAN_Destruct( void ) +{ + huffman_RecursiveFreeNode( HuffTree ); + + free( HuffTree ); + HuffTree = NULL; +} + +//============================================================================= +// +// HUFFMAN_Encode +// +//============================================================================= + +void HUFFMAN_Encode( unsigned char *in, unsigned char *out, int inlen, int *outlen ) +{ + int i,j,bitat; + unsigned int t; + bitat=0; + for (i=0;i>=1; + } + bitat+=HuffLookup[in[i]].len; + } + *outlen=1+(bitat+7)/8; + *out=8*((*outlen)-1)-bitat; + if(*outlen >= inlen+1) + { + *out=0xff; + memcpy(out+1,in,inlen); + *outlen=inlen+1; + } +/*#if _DEBUG + + HuffIn+=inlen; + HuffOut+=*outlen; + + { + unsigned char *buf; + int tlen; + + buf= (unsigned char *)Malloc(inlen); + + HuffDecode(out,buf,*outlen,&tlen); + if(!tlen==inlen) + I_FatalError("bogus compression"); + for (i=0;ione; + else + tmp=tmp->zero; + bits++; + } while (tmp->zero); + *out++=tmp->val; + (*outlen)++; + } +} + +#ifdef _DEBUG +static int freqs[256]; +void huffman_ZeroFreq(void) +{ + memset(freqs, 0, 256*sizeof(int)); +} + + +void CalcFreq(unsigned char *packet, int packetlen) +{ + int ix; + + for (ix=0; ix.01) + { + for (ix=0; ix<256; ix++) + { + sprintf(string, "\t%.8f,\n",((float)freqs[ix])/total); + //OutputDebugString(string); +// Printf(PRINT_HIGH, string); + } + } + huffman_ZeroFreq(); +} +#endif + + +void FindTab(huffnode_t *tmp,int len,unsigned int bits) +{ +// if(!tmp) +// I_FatalError("no huff node"); + if (tmp->zero) + { +// if(!tmp->one) +// I_FatalError("no one in node"); +// if(len>=32) +// I_FatalError("compression screwd"); + FindTab(tmp->zero,len+1,bits<<1); + FindTab(tmp->one,len+1,(bits<<1)|1); + return; + } + HuffLookup[tmp->val].len=len; + HuffLookup[tmp->val].bits=bits; + return; +} + +//============================================================================= +// +// huffman_PutBit +// +//============================================================================= + +void huffman_PutBit(unsigned char *buf,int pos,int bit) +{ + if (bit) + buf[pos/8]|=Masks[pos%8]; + else + buf[pos/8]&=~Masks[pos%8]; +} + +//============================================================================= +// +// huffman_GetBit +// +//============================================================================= + +int huffman_GetBit(unsigned char *buf,int pos) +{ + if (buf[pos/8]&Masks[pos%8]) + return 1; + else + return 0; +} + +//============================================================================= +// +// huffman_BuildTree +// +//============================================================================= + +void huffman_BuildTree( float *freq ) +{ + float min1,min2; + int i,j,minat1,minat2; + huffnode_t *work[256]; + huffnode_t *tmp; + for (i=0;i<256;i++) + { + work[i]=(huffnode_t *)M_Malloc(sizeof(huffnode_t)); + + + work[i]->val=(unsigned char)i; + work[i]->freq=freq[i]; + work[i]->zero=0; + work[i]->one=0; + HuffLookup[i].len=0; + } + for (i=0;i<255;i++) + { + minat1=-1; + minat2=-1; + min1=1E30; + min2=1E30; + for (j=0;j<256;j++) + { + if (!work[j]) + continue; + if (work[j]->freqfreq; + } + else if (work[j]->freqfreq; + } + } +/* + if (minat1<0) + I_FatalError("minatl: %f",minat1); + if (minat2<0) + I_FatalError("minat2: %f",minat2); +*/ + tmp= (huffnode_t *)M_Malloc(sizeof(huffnode_t)); + + + tmp->zero=work[minat2]; + tmp->one=work[minat1]; + tmp->freq=work[minat2]->freq+work[minat1]->freq; + tmp->val=0xff; + work[minat1]=tmp; + work[minat2]=0; + } + HuffTree=tmp; + FindTab(HuffTree,0,0); + +#if _DEBUG + for (i=0;i<256;i++) + { +// if(!HuffLookup[i].len&&HuffLookup[i].len<=32) +// I_FatalError("bad frequency table"); + //Printf(PRINT_HIGH, "%d %d %2X\n", HuffLookup[i].len, HuffLookup[i].bits, i); + } +#endif +} + +//============================================================================= +// +// huffman_RecursiveFreeNode +// +//============================================================================= + +static void huffman_RecursiveFreeNode( huffnode_t *pNode ) +{ + if ( pNode->one ) + { + huffman_RecursiveFreeNode( pNode->one ); + + free( pNode->one ); + pNode->one = NULL; + } + + if ( pNode->zero ) + { + huffman_RecursiveFreeNode( pNode->zero ); + + free( pNode->zero ); + pNode->zero = NULL; + } +} diff --git a/skulltag_huffman.h b/skulltag_huffman.h new file mode 100644 index 00000000..15335154 --- /dev/null +++ b/skulltag_huffman.h @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------- +// +// skulltag_huffman.h +// +// +// +// Version 5, released 5/13/2009. Compatible with Skulltag launchers and servers. +// +//------------------------------------------------------------------------------- + +#ifndef __HUFFMAN_H__ +#define __HUFFMAN_H__ + +//***************************************************************************** +// STRUCTURES + +typedef struct huffnode_s +{ + struct huffnode_s *zero; + struct huffnode_s *one; + unsigned char val; + float freq; + +} huffnode_t; + +typedef struct +{ + unsigned int bits; + int len; + +} hufftab_t; + +//***************************************************************************** +// PROTOTYPES + +void HUFFMAN_Construct( void ); +void HUFFMAN_Destruct( void ); + +void HUFFMAN_Encode( unsigned char *in, unsigned char *out, int inlen, int *outlen ); +void HUFFMAN_Decode( unsigned char *in, unsigned char *out, int inlen, int *outlen ); + +#endif // __HUFFMAN_H__ diff --git a/zandronum.c b/zandronum.c new file mode 100644 index 00000000..aabef32a --- /dev/null +++ b/zandronum.c @@ -0,0 +1,354 @@ +/* + * simple zandronum query - no master stuff + * Copyright 2018 DayenTech + * + */ + +#include +#ifndef _WIN32 + #include +#endif +#include +#include +#include +#include +#include +#include "zandronum.h" +#include "skulltag_huffman.h" + +#include "debug.h" + +// global get_player_info + +/* + * Query flags from https://wiki.zandronum.com/Launcher_protocol + */ +#define SQF_NAME 0x00000001 /* The name of the server */ +#define SQF_URL 0x00000002 /* The associated website */ +#define SQF_EMAIL 0x00000004 /* Contact address */ +#define SQF_MAPNAME 0x00000008 /* Current map being played */ +#define SQF_MAXCLIENTS 0x00000010 /* Maximum amount of clients who can connect to the server */ +#define SQF_MAXPLAYERS 0x00000020 /* Maximum amount of players who can join the game (the rest must spectate) */ +#define SQF_PWADS 0x00000040 /* PWADs loaded by the server */ +#define SQF_GAMETYPE 0x00000080 /* Game type code */ +#define SQF_GAMENAME 0x00000100 /* Game mode name */ +#define SQF_IWAD 0x00000200 /* The IWAD used by the server */ +#define SQF_FORCEPASSWORD 0x00000400 /* Whether or not the server enforces a password */ +#define SQF_FORCEJOINPASSWORD 0x00000800 /* Whether or not the server enforces a join password */ +#define SQF_GAMESKILL 0x00001000 /* The skill level on the server */ +#define SQF_BOTSKILL 0x00002000 /* The skill level of any bots on the server */ +#define SQF_DMFLAGS 0x00004000 /* (Deprecated) The values of dmflags, dmflags2 and compatflags. Use SQF_ALL_DMFLAGS instead. */ +#define SQF_LIMITS 0x00010000 /* Timelimit, fraglimit, etc. */ +#define SQF_TEAMDAMAGE 0x00020000 /* Team damage factor. */ +#define SQF_TEAMSCORES 0x00040000 /* (Deprecated) The scores of the red and blue teams. Use SQF_TEAMINFO_* instead. */ +#define SQF_NUMPLAYERS 0x00080000 /* Amount of players currently on the server. */ +#define SQF_PLAYERDATA 0x00100000 /* Information of each player in the server. */ +#define SQF_TEAMINFO_NUMBER 0x00200000 /* Amount of teams available. */ +#define SQF_TEAMINFO_NAME 0x00400000 /* Names of teams. */ +#define SQF_TEAMINFO_COLOR 0x00800000 /* RGB colors of teams. */ +#define SQF_TEAMINFO_SCORE 0x01000000 /* Scores of teams. */ +#define SQF_TESTING_SERVER 0x02000000 /* Whether or not the server is a testing server, also the name of the testing binary. */ +#define SQF_DATA_MD5SUM 0x04000000 /* (Deprecated) Used to retrieve the MD5 checksum of skulltag_data.pk3, now obsolete and returns an empty string instead. */ +#define SQF_ALL_DMFLAGS 0x08000000 /* Values of various dmflags used by the server. */ +#define SQF_SECURITY_SETTINGS 0x10000000 /* Security setting values (for now only whether the server enforces the master banlist) */ +#define SQF_OPTIONAL_WADS 0x20000000 /* Which PWADs are optional */ +#define SQF_DEH 0x40000000 /* List of DEHACKED patches loaded by the server. */ + +enum ZANDRONUM_GAMEMODE { + GAMEMODE_COOPERATIVE, + GAMEMODE_SURVIVAL, + GAMEMODE_INVASION, + GAMEMODE_DEATHMATCH, + GAMEMODE_TEAMPLAY, + GAMEMODE_DUEL, + GAMEMODE_TERMINATOR, + GAMEMODE_LASTMANSTANDING, + GAMEMODE_TEAMLMS, + GAMEMODE_POSSESSION, + GAMEMODE_TEAMPOSSESSION, + GAMEMODE_TEAMGAME, + GAMEMODE_CTF, + GAMEMODE_ONEFLAGCTF, + GAMEMODE_SKULLTAG, + GAMEMODE_DOMINATION +}; + +static const uint32_t zandronum_base_flags = (SQF_NAME | SQF_MAPNAME | SQF_MAXPLAYERS | SQF_GAMETYPE | SQF_GAMENAME | SQF_NUMPLAYERS); + +typedef struct zandronum_server_query_s { + uint32_t challenge; // 199 + uint32_t flags; + uint32_t time; + uint32_t flags2; // 0 - for SQF_EXTENDED_INFO +} zandronum_server_query_t; + +static bool huffman_constructed = false; + +char* build_zandronum_query_packet(char* query_buf, int* packet_len) { + if (!huffman_constructed) { + HUFFMAN_Construct(); + huffman_constructed = true; + } + zandronum_server_query_t query; + memset(&query, 0, sizeof(query)); + query.challenge = 199; + query.flags = zandronum_base_flags; + if (get_player_info) { // global + query.flags |= SQF_PLAYERDATA; + } + HUFFMAN_Encode((unsigned char*)&query, (unsigned char*)query_buf, sizeof(query), packet_len); + return query_buf; +} + +query_status_t send_zandronum_request_packet(struct qserver *server) { + int rc = 0; + int packet_len = -1; + char *packet = NULL; + char query_buf[4096] = { 0 }; + + server->next_player_info = NO_PLAYER_INFO; + + packet_len = sizeof(query_buf); + packet = build_zandronum_query_packet(query_buf, &packet_len); + + rc = send(server->fd, packet, packet_len, 0); + if (rc == SOCKET_ERROR) { + return (send_error(server, rc)); + } + + if (server->retry1 == n_retries) { + gettimeofday(&server->packet_time1, NULL); + server->n_requests++; + } else { + server->n_retries++; + } + server->retry1--; + server->n_packets++; + + return (INPROGRESS); +} + +typedef struct zandronum_response_header_s { + uint32_t response; // 5660023 accepted, ..24 denied to many requests, ..25 banned + uint32_t time; // waste of bytes + // ... +} zandronum_response_header_t; + +static char* zandronum_skip_string(char* start, char* end) { + for (; start < end; start++) { + if (start[0] == 0) { + break; + } + } + return start+1; +} + +query_status_t deal_with_zandronum_packet(struct qserver *server, char *rawpkt, int pktlen) { + static char packet_buffer[4096]; + zandronum_response_header_t response_header; + char* version; + char* ptr = packet_buffer; + char* end; + uint32_t flags; + uint32_t expected_flags; + int decode_len; + int game_mode = GAMEMODE_COOPERATIVE; + struct player* z_player = (struct player*)NULL; + struct player* n_player = (struct player*)NULL; + uint16_t points; + uint16_t ping; + + HUFFMAN_Decode((unsigned char*)rawpkt, (unsigned char*)&packet_buffer[0], pktlen, &decode_len); + if ((decode_len >= sizeof(packet_buffer)) || (decode_len < sizeof(zandronum_response_header_t))) { + debug(2, "bad decode length: %d\n", decode_len); + return PKT_ERROR; + } + end = ptr + decode_len; + + memcpy(&response_header, packet_buffer, sizeof(response_header)); + if (response_header.response != 5660023) { + debug(2, "bad response: %d\n", response_header.response); + return PKT_ERROR; + } + + server->n_servers++; + if (server->server_name == NULL) { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } else { + gettimeofday(&server->packet_time1, NULL); + } + ptr += sizeof(response_header); + if (ptr >= end) { + debug(2, "response too small"); + return PKT_ERROR; + } + version = ptr; + ptr = zandronum_skip_string(ptr, end); + if (ptr >= end) { + return PKT_ERROR; + } + debug(1, "Zandronum Version: %s\n", version); + + expected_flags = zandronum_base_flags; + if (get_player_info) { // global var + expected_flags |= SQF_PLAYERDATA; + } + memcpy(&flags, ptr, sizeof(flags)); + if (flags & ~expected_flags) { + debug(2, "Bad Flags, Got %0x - Wanted %0x\n", flags, expected_flags); + return PKT_ERROR; // server returned stuff we didn't ask for + } + ptr += sizeof(flags); + if (ptr >= end) { + return PKT_ERROR; + } + if (flags & SQF_NAME) { + debug(1, "Server name: %s\n", ptr); + server->server_name = strdup(ptr); + ptr = zandronum_skip_string(ptr, end); + if (ptr >= end) { + return PKT_ERROR; + } + } + + if (flags & SQF_MAPNAME) { + debug(1, "Map name: %s\n", ptr); + server->map_name = strdup(ptr); + ptr = zandronum_skip_string(ptr, end); + if (ptr >= end) { + return PKT_ERROR; + } + } + + if (flags & SQF_MAXPLAYERS) { + server->max_players = (int)(ptr[0]); + ptr++; + if (ptr >= end) { + return PKT_ERROR; + } + } + + if (flags & SQF_GAMETYPE) { + game_mode = (int)(ptr[0]); + debug(1, "Game mode %d\n", game_mode); + ptr += 3; // skip instagib and buckshot flags + if (ptr >= end) { + return PKT_ERROR; + } + } + + if (flags & SQF_GAMENAME) { + debug(1, "Game name %s\n", ptr); + server->game = strdup(ptr); + add_rule(server, (char*)"gametype", server->game, NO_FLAGS); + ptr = zandronum_skip_string(ptr, end); + if (ptr >= end) { + return PKT_ERROR; + } + } + + if (flags & SQF_NUMPLAYERS) { + server->num_players = (int)(ptr[0]); + debug(1, "number of players: %d\n", server->num_players); + ptr++; + if (ptr >= end) { + return PKT_ERROR; + } + } + + if (server->num_players < 1) { + server->num_players = 0; + return DONE_FORCE; + } + + if (!(flags & SQF_PLAYERDATA)) { + server->num_players = 0; + return DONE_FORCE; + } + + server->players = (struct player*)NULL; + + for (int x=0; x < server->num_players; x++) { + n_player = (struct player*)malloc(sizeof(struct player)); + memset(n_player, 0, sizeof(struct player)); + if (server->players == (struct player*)NULL) { + server->players = n_player; + } else { + z_player->next = n_player; + } + z_player = n_player; + n_player->name = strdup(ptr); + ptr = zandronum_skip_string(ptr, end); + if (ptr >= end) { + goto PLAYER_ERROR; + } + + memcpy(&points, ptr, sizeof(points)); + ptr += sizeof(points); + if (ptr >= end) { + goto PLAYER_ERROR; + } + n_player->frags = points; + + memcpy(&ping, ptr, sizeof(ping)); + ptr += sizeof(ping); + if (ptr >= end) { + goto PLAYER_ERROR; + } + n_player->ping = ping; + + if ((int)ptr[0] == 1) { + server->num_spectators++; + } + ptr++; + if (ptr >= end) { + goto PLAYER_ERROR; + } + if ((int)ptr[0] == 1) { // I'm a bot! + int nb_size = strlen(n_player->name) + 7; + char * name_buffer = (char*)malloc(nb_size); + snprintf(name_buffer, nb_size, "bot - %s", n_player->name); + free(n_player->name); + n_player->name = name_buffer; + } + ptr++; + if (ptr >= end) { + goto PLAYER_ERROR; + } + switch (game_mode) { + case GAMEMODE_TEAMPLAY: + case GAMEMODE_TEAMLMS: + case GAMEMODE_TEAMPOSSESSION: + case GAMEMODE_TEAMGAME: + case GAMEMODE_CTF: + case GAMEMODE_ONEFLAGCTF: + case GAMEMODE_SKULLTAG: + case GAMEMODE_DOMINATION: + { // team games have a team number + n_player->team = (int)ptr[0]; + ptr++; + if (ptr >= end) { + goto PLAYER_ERROR; + } + } break; + default: break; + } + n_player->connect_time = (int)ptr[0] * 60; // qstat expects connect_time in seconds + ptr++; + if (ptr > end) { + goto PLAYER_ERROR; + } + debug(1, "adding player: %s %d %d %d\n", n_player->name, n_player->frags, n_player->ping, n_player->connect_time); + } + return DONE_FORCE; + + PLAYER_ERROR: // error occurred while unpacking players + debug(1, "not enough player info returned\n"); + for (z_player = server->players; z_player; z_player = n_player) { + n_player = z_player->next; + free(z_player); + } + server->players = (struct player*)NULL; + return PKT_ERROR; +} + diff --git a/zandronum.h b/zandronum.h new file mode 100644 index 00000000..d6cd7dac --- /dev/null +++ b/zandronum.h @@ -0,0 +1,23 @@ +/* + * qstat + * + * + * Zandronum protocol + * Copyright 2018 DayenTech + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + * + * Currently does not include querying master server + */ +#ifndef QSTAT_ZANDRONUM_H +#define QSTAT_ZANDRONUM_H + +#include "qstat.h" +#include "qserver.h" + +#define ZANDRONUM_DEFAULT_PORT 10666 + +query_status_t send_zandronum_request_packet(struct qserver *server); +query_status_t deal_with_zandronum_packet(struct qserver *server, char *rawpkt, int pktlen); + +#endif