mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2024-11-30 15:52:16 +00:00
70f301e4ff
cvar type+range extension module tracking for cvars and commands lots of help text
899 lines
21 KiB
C++
899 lines
21 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "client.h"
|
|
|
|
|
|
static ping_t cl_pinglist[MAX_PINGREQUESTS];
|
|
|
|
typedef struct serverStatus_s
|
|
{
|
|
char string[BIG_INFO_STRING];
|
|
netadr_t address;
|
|
int time, startTime;
|
|
qbool pending;
|
|
qbool print;
|
|
qbool retrieved;
|
|
} serverStatus_t;
|
|
|
|
static serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS];
|
|
static int serverStatusCount;
|
|
|
|
|
|
static void CL_InitServerInfo( serverInfo_t *server, const serverAddress_t* address )
|
|
{
|
|
server->adr.type = NA_IP;
|
|
server->adr.ip[0] = address->ip[0];
|
|
server->adr.ip[1] = address->ip[1];
|
|
server->adr.ip[2] = address->ip[2];
|
|
server->adr.ip[3] = address->ip[3];
|
|
server->adr.port = address->port;
|
|
server->clients = 0;
|
|
server->hostName[0] = '\0';
|
|
server->mapName[0] = '\0';
|
|
server->maxClients = 0;
|
|
server->maxPing = 0;
|
|
server->minPing = 0;
|
|
server->ping = -1;
|
|
server->game[0] = '\0';
|
|
server->gameType = 0;
|
|
server->netType = 0;
|
|
}
|
|
|
|
#define MAX_SERVERSPERPACKET 256
|
|
|
|
|
|
void CL_ServersResponsePacket( const netadr_t& from, msg_t *msg )
|
|
{
|
|
int i, count, max, total;
|
|
serverAddress_t addresses[MAX_SERVERSPERPACKET];
|
|
int numservers;
|
|
byte* buffptr;
|
|
byte* buffend;
|
|
|
|
Com_Printf("CL_ServersResponsePacket\n");
|
|
|
|
if (cls.numglobalservers == -1) {
|
|
// state to detect lack of servers or lack of response
|
|
cls.numglobalservers = 0;
|
|
cls.numGlobalServerAddresses = 0;
|
|
}
|
|
|
|
if (cls.nummplayerservers == -1) {
|
|
cls.nummplayerservers = 0;
|
|
}
|
|
|
|
// parse through server response string
|
|
numservers = 0;
|
|
buffptr = msg->data;
|
|
buffend = buffptr + msg->cursize;
|
|
while (buffptr+1 < buffend) {
|
|
// advance to initial token
|
|
do {
|
|
if (*buffptr++ == '\\')
|
|
break;
|
|
}
|
|
while (buffptr < buffend);
|
|
|
|
if ( buffptr >= buffend - 6 ) {
|
|
break;
|
|
}
|
|
|
|
// parse out ip
|
|
addresses[numservers].ip[0] = *buffptr++;
|
|
addresses[numservers].ip[1] = *buffptr++;
|
|
addresses[numservers].ip[2] = *buffptr++;
|
|
addresses[numservers].ip[3] = *buffptr++;
|
|
|
|
// parse out port
|
|
addresses[numservers].port = (*buffptr++)<<8;
|
|
addresses[numservers].port += *buffptr++;
|
|
addresses[numservers].port = BigShort( addresses[numservers].port );
|
|
|
|
// syntax check
|
|
if (*buffptr != '\\') {
|
|
break;
|
|
}
|
|
|
|
Com_DPrintf( "server: %d ip: %d.%d.%d.%d:%d\n",numservers,
|
|
addresses[numservers].ip[0],
|
|
addresses[numservers].ip[1],
|
|
addresses[numservers].ip[2],
|
|
addresses[numservers].ip[3],
|
|
addresses[numservers].port );
|
|
|
|
numservers++;
|
|
if (numservers >= MAX_SERVERSPERPACKET) {
|
|
break;
|
|
}
|
|
|
|
// parse out EOT
|
|
if (buffptr[1] == 'E' && buffptr[2] == 'O' && buffptr[3] == 'T') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cls.masterNum == 0) {
|
|
count = cls.numglobalservers;
|
|
max = MAX_GLOBAL_SERVERS;
|
|
} else {
|
|
count = cls.nummplayerservers;
|
|
max = MAX_OTHER_SERVERS;
|
|
}
|
|
|
|
for (i = 0; i < numservers && count < max; i++) {
|
|
// build net address
|
|
serverInfo_t *server = (cls.masterNum == 0) ? &cls.globalServers[count] : &cls.mplayerServers[count];
|
|
|
|
CL_InitServerInfo( server, &addresses[i] );
|
|
// advance to next slot
|
|
count++;
|
|
}
|
|
|
|
// if getting the global list
|
|
if (cls.masterNum == 0) {
|
|
if ( cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS ) {
|
|
// if we couldn't store the servers in the main list anymore
|
|
for (; i < numservers && count >= max; i++) {
|
|
serverAddress_t *addr;
|
|
// just store the addresses in an additional list
|
|
addr = &cls.globalServerAddresses[cls.numGlobalServerAddresses++];
|
|
addr->ip[0] = addresses[i].ip[0];
|
|
addr->ip[1] = addresses[i].ip[1];
|
|
addr->ip[2] = addresses[i].ip[2];
|
|
addr->ip[3] = addresses[i].ip[3];
|
|
addr->port = addresses[i].port;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cls.masterNum == 0) {
|
|
cls.numglobalservers = count;
|
|
total = count + cls.numGlobalServerAddresses;
|
|
} else {
|
|
cls.nummplayerservers = count;
|
|
total = count;
|
|
}
|
|
|
|
Com_Printf("%d servers parsed (total %d)\n", numservers, total);
|
|
}
|
|
|
|
|
|
void CL_ServerStatusResponse( const netadr_t& from, msg_t *msg )
|
|
{
|
|
int i;
|
|
serverStatus_t* serverStatus = NULL;
|
|
for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
|
|
if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
|
|
serverStatus = &cl_serverStatusList[i];
|
|
break;
|
|
}
|
|
}
|
|
// if we didn't request this server status
|
|
if (!serverStatus) {
|
|
return;
|
|
}
|
|
|
|
const char* s = MSG_ReadStringLine( msg );
|
|
int len = 0;
|
|
|
|
Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s);
|
|
|
|
if (serverStatus->print) {
|
|
char info[MAX_INFO_STRING];
|
|
int l(0);
|
|
Com_Printf("Server settings:\n");
|
|
// print cvars
|
|
while (*s) {
|
|
for (i = 0; i < 2 && *s; i++) {
|
|
if (*s == '\\')
|
|
s++;
|
|
l = 0;
|
|
while (*s) {
|
|
info[l++] = *s;
|
|
if (l >= MAX_INFO_STRING-1)
|
|
break;
|
|
s++;
|
|
if (*s == '\\') {
|
|
break;
|
|
}
|
|
}
|
|
info[l] = '\0';
|
|
if (i) {
|
|
Com_Printf("%s\n", info);
|
|
}
|
|
else {
|
|
Com_Printf("%-24s", info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
len = strlen(serverStatus->string);
|
|
Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
|
|
|
|
if (serverStatus->print) {
|
|
Com_Printf("\nPlayers:\n");
|
|
Com_Printf("num: score: ping: name:\n");
|
|
}
|
|
for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) {
|
|
|
|
len = strlen(serverStatus->string);
|
|
Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s);
|
|
|
|
if (serverStatus->print) {
|
|
int score(0), ping(0);
|
|
sscanf(s, "%d %d", &score, &ping);
|
|
s = strchr(s, ' ');
|
|
if (s)
|
|
s = strchr(s+1, ' ');
|
|
if (s)
|
|
s++;
|
|
else
|
|
s = "unknown";
|
|
Com_Printf("%-2d %-3d %-3d %s\n", i, score, ping, s );
|
|
}
|
|
}
|
|
len = strlen(serverStatus->string);
|
|
Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
|
|
|
|
serverStatus->time = Com_Milliseconds();
|
|
serverStatus->address = from;
|
|
serverStatus->pending = qfalse;
|
|
if (serverStatus->print) {
|
|
serverStatus->retrieved = qtrue;
|
|
}
|
|
}
|
|
|
|
|
|
static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) {
|
|
if (server) {
|
|
if (info) {
|
|
server->clients = atoi(Info_ValueForKey(info, "clients"));
|
|
Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH);
|
|
Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH);
|
|
server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients"));
|
|
Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH);
|
|
server->gameType = atoi(Info_ValueForKey(info, "gametype"));
|
|
server->netType = atoi(Info_ValueForKey(info, "nettype"));
|
|
server->minPing = atoi(Info_ValueForKey(info, "minping"));
|
|
server->maxPing = atoi(Info_ValueForKey(info, "maxping"));
|
|
server->punkbuster = atoi(Info_ValueForKey(info, "punkbuster"));
|
|
}
|
|
server->ping = ping;
|
|
}
|
|
}
|
|
|
|
static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) {
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_OTHER_SERVERS; i++) {
|
|
if (NET_CompareAdr(from, cls.localServers[i].adr)) {
|
|
CL_SetServerInfo(&cls.localServers[i], info, ping);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_OTHER_SERVERS; i++) {
|
|
if (NET_CompareAdr(from, cls.mplayerServers[i].adr)) {
|
|
CL_SetServerInfo(&cls.mplayerServers[i], info, ping);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_GLOBAL_SERVERS; i++) {
|
|
if (NET_CompareAdr(from, cls.globalServers[i].adr)) {
|
|
CL_SetServerInfo(&cls.globalServers[i], info, ping);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_OTHER_SERVERS; i++) {
|
|
if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) {
|
|
CL_SetServerInfo(&cls.favoriteServers[i], info, ping);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CL_ServerInfoPacket
|
|
===================
|
|
*/
|
|
void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
|
|
int i, type;
|
|
char info[MAX_INFO_STRING];
|
|
char *infoString;
|
|
int prot;
|
|
|
|
infoString = MSG_ReadString( msg );
|
|
|
|
// if this isn't the correct protocol version, ignore it
|
|
prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
|
|
if ( prot != PROTOCOL_VERSION ) {
|
|
Com_DPrintf( "Different protocol info packet: %s\n", infoString );
|
|
return;
|
|
}
|
|
|
|
// iterate servers waiting for ping response
|
|
for (i=0; i<MAX_PINGREQUESTS; i++)
|
|
{
|
|
if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) )
|
|
{
|
|
// calc ping time
|
|
cl_pinglist[i].time = cls.realtime - cl_pinglist[i].start + 1;
|
|
Com_DPrintf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) );
|
|
|
|
// save of info
|
|
Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) );
|
|
|
|
// tack on the net type
|
|
// NOTE: make sure these types are in sync with the netnames strings in the UI
|
|
switch (from.type)
|
|
{
|
|
case NA_BROADCAST:
|
|
case NA_IP:
|
|
type = 1;
|
|
break;
|
|
default:
|
|
type = 0;
|
|
break;
|
|
}
|
|
Info_SetValueForKey( cl_pinglist[i].info, "nettype", va("%d", type) );
|
|
CL_SetServerInfoByAddress(from, infoString, cl_pinglist[i].time);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if not just sent a local broadcast or pinging local servers
|
|
if (cls.pingUpdateSource != AS_LOCAL) {
|
|
return;
|
|
}
|
|
|
|
for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) {
|
|
// empty slot
|
|
if ( cls.localServers[i].adr.port == 0 ) {
|
|
break;
|
|
}
|
|
|
|
// avoid duplicate
|
|
if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( i == MAX_OTHER_SERVERS ) {
|
|
Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" );
|
|
return;
|
|
}
|
|
|
|
// add this to the list
|
|
cls.numlocalservers = i+1;
|
|
cls.localServers[i].adr = from;
|
|
cls.localServers[i].clients = 0;
|
|
cls.localServers[i].hostName[0] = '\0';
|
|
cls.localServers[i].mapName[0] = '\0';
|
|
cls.localServers[i].maxClients = 0;
|
|
cls.localServers[i].maxPing = 0;
|
|
cls.localServers[i].minPing = 0;
|
|
cls.localServers[i].ping = -1;
|
|
cls.localServers[i].game[0] = '\0';
|
|
cls.localServers[i].gameType = 0;
|
|
cls.localServers[i].netType = from.type;
|
|
cls.localServers[i].punkbuster = 0;
|
|
|
|
Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING );
|
|
if (info[0]) {
|
|
if (info[strlen(info)-1] != '\n') {
|
|
strncat(info, "\n", sizeof(info));
|
|
}
|
|
Com_Printf( "%s: %s", NET_AdrToString( from ), info );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CL_GetServerStatus
|
|
===================
|
|
*/
|
|
serverStatus_t *CL_GetServerStatus( netadr_t from ) {
|
|
serverStatus_t *serverStatus;
|
|
int i, oldest, oldestTime;
|
|
|
|
serverStatus = NULL;
|
|
for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
|
|
if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
|
|
return &cl_serverStatusList[i];
|
|
}
|
|
}
|
|
for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
|
|
if ( cl_serverStatusList[i].retrieved ) {
|
|
return &cl_serverStatusList[i];
|
|
}
|
|
}
|
|
oldest = -1;
|
|
oldestTime = 0;
|
|
for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
|
|
if (oldest == -1 || cl_serverStatusList[i].startTime < oldestTime) {
|
|
oldest = i;
|
|
oldestTime = cl_serverStatusList[i].startTime;
|
|
}
|
|
}
|
|
if (oldest != -1) {
|
|
return &cl_serverStatusList[oldest];
|
|
}
|
|
serverStatusCount++;
|
|
return &cl_serverStatusList[serverStatusCount & (MAX_SERVERSTATUSREQUESTS-1)];
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CL_ServerStatus
|
|
===================
|
|
*/
|
|
int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ) {
|
|
int i;
|
|
netadr_t to;
|
|
serverStatus_t *serverStatus;
|
|
|
|
// if no server address then reset all server status requests
|
|
if ( !serverAddress ) {
|
|
for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
|
|
cl_serverStatusList[i].address.port = 0;
|
|
cl_serverStatusList[i].retrieved = qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
// get the address
|
|
if ( !NET_StringToAdr( serverAddress, &to ) ) {
|
|
return qfalse;
|
|
}
|
|
serverStatus = CL_GetServerStatus( to );
|
|
// if no server status string then reset the server status request for this address
|
|
if ( !serverStatusString ) {
|
|
serverStatus->retrieved = qtrue;
|
|
return qfalse;
|
|
}
|
|
|
|
// if this server status request has the same address
|
|
if ( NET_CompareAdr( to, serverStatus->address) ) {
|
|
// if we recieved an response for this server status request
|
|
if (!serverStatus->pending) {
|
|
Q_strncpyz(serverStatusString, serverStatus->string, maxLen);
|
|
serverStatus->retrieved = qtrue;
|
|
serverStatus->startTime = 0;
|
|
return qtrue;
|
|
}
|
|
// resend the request regularly
|
|
else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) {
|
|
serverStatus->print = qfalse;
|
|
serverStatus->pending = qtrue;
|
|
serverStatus->retrieved = qfalse;
|
|
serverStatus->time = 0;
|
|
serverStatus->startTime = Com_Milliseconds();
|
|
NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
|
|
return qfalse;
|
|
}
|
|
}
|
|
// if retrieved
|
|
else if ( serverStatus->retrieved ) {
|
|
serverStatus->address = to;
|
|
serverStatus->print = qfalse;
|
|
serverStatus->pending = qtrue;
|
|
serverStatus->retrieved = qfalse;
|
|
serverStatus->startTime = Com_Milliseconds();
|
|
serverStatus->time = 0;
|
|
NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
|
|
return qfalse;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_LocalServers_f
|
|
==================
|
|
*/
|
|
void CL_LocalServers_f( void ) {
|
|
char *message;
|
|
int i, j;
|
|
netadr_t to;
|
|
|
|
Com_Printf( "Scanning for servers on the local network...\n");
|
|
|
|
// reset the list, waiting for response
|
|
cls.numlocalservers = 0;
|
|
cls.pingUpdateSource = AS_LOCAL;
|
|
|
|
for (i = 0; i < MAX_OTHER_SERVERS; i++) {
|
|
qbool b = cls.localServers[i].visible;
|
|
Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i]));
|
|
cls.localServers[i].visible = b;
|
|
}
|
|
Com_Memset( &to, 0, sizeof( to ) );
|
|
|
|
// The 'xxx' in the message is a challenge that will be echoed back
|
|
// by the server. We don't care about that here, but master servers
|
|
// can use that to prevent spoofed server responses from invalid ip
|
|
message = "\377\377\377\377getinfo xxx";
|
|
|
|
// send each message twice in case one is dropped
|
|
for ( i = 0 ; i < 2 ; i++ ) {
|
|
// send a broadcast packet on each server port
|
|
// we support multiple server ports so a single machine
|
|
// can nicely run multiple servers
|
|
for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) {
|
|
to.port = BigShort( (short)(PORT_SERVER + j) );
|
|
to.type = NA_BROADCAST;
|
|
NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_GlobalServers_f
|
|
==================
|
|
*/
|
|
void CL_GlobalServers_f( void ) {
|
|
netadr_t to;
|
|
int i;
|
|
int count;
|
|
char *buffptr;
|
|
char command[1024];
|
|
|
|
if ( Cmd_Argc() < 3) {
|
|
Com_Printf( "usage: globalservers <master# 0-1> <protocol> [keywords]\n");
|
|
return;
|
|
}
|
|
|
|
cls.masterNum = atoi( Cmd_Argv(1) );
|
|
|
|
Com_Printf( "Requesting servers from the master...\n");
|
|
|
|
// reset the list, waiting for response
|
|
// -1 is used to distinguish a "no response"
|
|
|
|
if( cls.masterNum == 1 ) {
|
|
NET_StringToAdr( MASTER_SERVER_NAME, &to );
|
|
cls.nummplayerservers = -1;
|
|
cls.pingUpdateSource = AS_MPLAYER;
|
|
}
|
|
else {
|
|
NET_StringToAdr( MASTER_SERVER_NAME, &to );
|
|
cls.numglobalservers = -1;
|
|
cls.pingUpdateSource = AS_GLOBAL;
|
|
}
|
|
to.type = NA_IP;
|
|
to.port = BigShort(PORT_MASTER);
|
|
|
|
sprintf( command, "getservers %s", Cmd_Argv(2) );
|
|
|
|
// tack on keywords
|
|
buffptr = command + strlen( command );
|
|
count = Cmd_Argc();
|
|
for (i=3; i<count; i++)
|
|
buffptr += sprintf( buffptr, " %s", Cmd_Argv(i) );
|
|
|
|
NET_OutOfBandPrint( NS_SERVER, to, command );
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_GetPing
|
|
==================
|
|
*/
|
|
void CL_GetPing( int n, char *buf, int buflen, int *pingtime )
|
|
{
|
|
const char *str;
|
|
int time;
|
|
int maxPing;
|
|
|
|
if (!cl_pinglist[n].adr.port)
|
|
{
|
|
// empty slot
|
|
buf[0] = '\0';
|
|
*pingtime = 0;
|
|
return;
|
|
}
|
|
|
|
str = NET_AdrToString( cl_pinglist[n].adr );
|
|
Q_strncpyz( buf, str, buflen );
|
|
|
|
time = cl_pinglist[n].time;
|
|
if (!time)
|
|
{
|
|
// check for timeout
|
|
time = cls.realtime - cl_pinglist[n].start;
|
|
maxPing = Cvar_VariableIntegerValue( "cl_maxPing" );
|
|
if (time < maxPing)
|
|
{
|
|
// not timed out yet
|
|
time = 0;
|
|
}
|
|
}
|
|
|
|
CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time);
|
|
|
|
*pingtime = time;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_GetPingInfo
|
|
==================
|
|
*/
|
|
void CL_GetPingInfo( int n, char *buf, int buflen )
|
|
{
|
|
if (!cl_pinglist[n].adr.port)
|
|
{
|
|
// empty slot
|
|
if (buflen)
|
|
buf[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz( buf, cl_pinglist[n].info, buflen );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_ClearPing
|
|
==================
|
|
*/
|
|
void CL_ClearPing( int n )
|
|
{
|
|
if (n < 0 || n >= MAX_PINGREQUESTS)
|
|
return;
|
|
|
|
cl_pinglist[n].adr.port = 0;
|
|
}
|
|
|
|
|
|
int CL_GetPingQueueCount()
|
|
{
|
|
int count = 0;
|
|
const ping_t* pingptr = cl_pinglist;
|
|
|
|
for ( int i = 0; i < MAX_PINGREQUESTS; ++i, ++pingptr ) {
|
|
if (pingptr->adr.port) {
|
|
++count;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static ping_t* CL_GetFreePing()
|
|
{
|
|
ping_t* pingptr;
|
|
ping_t* best;
|
|
int oldest;
|
|
int i;
|
|
int time;
|
|
|
|
pingptr = cl_pinglist;
|
|
for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
|
|
{
|
|
// find free ping slot
|
|
if (pingptr->adr.port)
|
|
{
|
|
if (!pingptr->time)
|
|
{
|
|
if (cls.realtime - pingptr->start < 500)
|
|
{
|
|
// still waiting for response
|
|
continue;
|
|
}
|
|
}
|
|
else if (pingptr->time < 500)
|
|
{
|
|
// results have not been queried
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// clear it
|
|
pingptr->adr.port = 0;
|
|
return (pingptr);
|
|
}
|
|
|
|
// use oldest entry
|
|
pingptr = cl_pinglist;
|
|
best = cl_pinglist;
|
|
oldest = INT_MIN;
|
|
for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
|
|
{
|
|
// scan for oldest
|
|
time = cls.realtime - pingptr->start;
|
|
if (time > oldest)
|
|
{
|
|
oldest = time;
|
|
best = pingptr;
|
|
}
|
|
}
|
|
|
|
return (best);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_UpdateVisiblePings_f
|
|
==================
|
|
*/
|
|
qbool CL_UpdateVisiblePings_f(int source) {
|
|
int slots, i;
|
|
char buff[MAX_STRING_CHARS];
|
|
int pingTime;
|
|
int max;
|
|
qbool status = qfalse;
|
|
|
|
if (source < 0 || source > AS_FAVORITES) {
|
|
return qfalse;
|
|
}
|
|
|
|
cls.pingUpdateSource = source;
|
|
|
|
slots = CL_GetPingQueueCount();
|
|
if (slots < MAX_PINGREQUESTS) {
|
|
serverInfo_t *server = NULL;
|
|
|
|
max = (source == AS_GLOBAL) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS;
|
|
switch (source) {
|
|
case AS_LOCAL :
|
|
server = &cls.localServers[0];
|
|
max = cls.numlocalservers;
|
|
break;
|
|
case AS_MPLAYER :
|
|
server = &cls.mplayerServers[0];
|
|
max = cls.nummplayerservers;
|
|
break;
|
|
case AS_GLOBAL :
|
|
server = &cls.globalServers[0];
|
|
max = cls.numglobalservers;
|
|
break;
|
|
case AS_FAVORITES :
|
|
server = &cls.favoriteServers[0];
|
|
max = cls.numfavoriteservers;
|
|
break;
|
|
}
|
|
for (i = 0; i < max; i++) {
|
|
if (server[i].visible) {
|
|
if (server[i].ping == -1) {
|
|
int j;
|
|
|
|
if (slots >= MAX_PINGREQUESTS) {
|
|
break;
|
|
}
|
|
for (j = 0; j < MAX_PINGREQUESTS; j++) {
|
|
if (!cl_pinglist[j].adr.port) {
|
|
continue;
|
|
}
|
|
if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) {
|
|
// already on the list
|
|
break;
|
|
}
|
|
}
|
|
if (j >= MAX_PINGREQUESTS) {
|
|
status = qtrue;
|
|
for (j = 0; j < MAX_PINGREQUESTS; j++) {
|
|
if (!cl_pinglist[j].adr.port) {
|
|
break;
|
|
}
|
|
}
|
|
memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t));
|
|
cl_pinglist[j].start = cls.realtime;
|
|
cl_pinglist[j].time = 0;
|
|
NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" );
|
|
slots++;
|
|
}
|
|
}
|
|
// if the server has a ping higher than cl_maxPing or
|
|
// the ping packet got lost
|
|
else if (server[i].ping == 0) {
|
|
// if we are updating global servers
|
|
if (source == AS_GLOBAL) {
|
|
//
|
|
if ( cls.numGlobalServerAddresses > 0 ) {
|
|
// overwrite this server with one from the additional global servers
|
|
cls.numGlobalServerAddresses--;
|
|
CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]);
|
|
// NOTE: the server[i].visible flag stays untouched
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (slots) {
|
|
status = qtrue;
|
|
}
|
|
for (i = 0; i < MAX_PINGREQUESTS; i++) {
|
|
if (!cl_pinglist[i].adr.port) {
|
|
continue;
|
|
}
|
|
CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime );
|
|
if (pingTime != 0) {
|
|
CL_ClearPing(i);
|
|
status = qtrue;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
void CL_Ping_f()
|
|
{
|
|
if ( Cmd_Argc() != 2 ) {
|
|
Com_Printf( "usage: ping [server]\n");
|
|
return;
|
|
}
|
|
|
|
netadr_t to;
|
|
const char* server = Cmd_Argv(1);
|
|
if ( !NET_StringToAdr( server, &to ) )
|
|
return;
|
|
|
|
ping_t* pingptr = CL_GetFreePing();
|
|
|
|
memcpy( &pingptr->adr, &to, sizeof (netadr_t) );
|
|
pingptr->start = cls.realtime;
|
|
pingptr->time = 0;
|
|
|
|
CL_SetServerInfoByAddress(pingptr->adr, NULL, 0);
|
|
|
|
NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" );
|
|
}
|
|
|
|
|
|
void CL_ServerStatus_f()
|
|
{
|
|
const char* server;
|
|
if ( Cmd_Argc() != 2 ) {
|
|
if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
|
|
Com_Printf ("Not connected to a server.\n");
|
|
Com_Printf( "Usage: serverstatus [server]\n");
|
|
return;
|
|
}
|
|
server = cls.servername;
|
|
}
|
|
else {
|
|
server = Cmd_Argv(1);
|
|
}
|
|
|
|
netadr_t to;
|
|
if ( !NET_StringToAdr( server, &to ) )
|
|
return;
|
|
|
|
NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
|
|
|
|
serverStatus_t* serverStatus = CL_GetServerStatus( to );
|
|
serverStatus->address = to;
|
|
serverStatus->print = qtrue;
|
|
serverStatus->pending = qtrue;
|
|
}
|
|
|