void SV_MasterHeartbeat( const char *hbname ) { int i; int netenabled; netenabled = Cvar_VariableIntegerValue( "net_enabled" ); // "dedicated 1" is for LAN play, "dedicated 2" is for Internet play if ( !com_dedicated || com_dedicated->integer != 2 || !( netenabled & ( NET_ENABLEV4 | NET_ENABLEV6 ) ) ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; SV_ResolveMasterServers(); // send to group masters for ( i = 0; i < MAX_MASTER_SERVERS; i++ ) { if ( masterServerAddr[ i ].ipv4.type == NA_BAD && masterServerAddr[ i ].ipv6.type == NA_BAD ) { continue; } Com_Printf(_( "Sending heartbeat to %s\n"), sv_master[ i ]->string ); // this command should be changed if the server info / status format // ever incompatibly changes if ( masterServerAddr[ i ].ipv4.type != NA_BAD ) { NET_OutOfBandPrint( NS_SERVER, masterServerAddr[ i ].ipv4, "heartbeat %s\n", hbname ); } if ( masterServerAddr[ i ].ipv6.type != NA_BAD ) { NET_OutOfBandPrint( NS_SERVER, masterServerAddr[ i ].ipv6, "heartbeat %s\n", hbname ); } } }
/* ================= SV_ReadPackets ================= */ void SV_PacketEvent( netadr_t from, msg_t *msg ) { int i; client_t *cl; int qport; // check for connectionless packet (0xffffffff) first if ( msg->cursize >= 4 && *(int *)msg->data == -1) { SV_ConnectionlessPacket( from, msg ); return; } // read the qport out of the message so we can fix up // stupid address translating routers MSG_BeginReading( msg ); MSG_ReadLong( msg ); // sequence number MSG_ReadLong( msg ); // sequence number qport = MSG_ReadShort( msg ) & 0xffff; // find which client the message is from for (i=0, cl=svs.clients ; i < 1 ; i++,cl++) { if (cl->state == CS_FREE) { continue; } if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) { continue; } // it is possible to have multiple clients from a single IP // address, so they are differentiated by the qport variable if (cl->netchan.qport != qport) { continue; } // the IP port can't be used to differentiate them, because // some address translating routers periodically change UDP // port assignments if (cl->netchan.remoteAddress.port != from.port) { Com_Printf( "SV_ReadPackets: fixing up a translated port\n" ); cl->netchan.remoteAddress.port = from.port; } // make sure it is a valid, in sequence packet if (Netchan_Process(&cl->netchan, msg)) { // zombie clients stil neet to do the Netchan_Process // to make sure they don't need to retransmit the final // reliable message, but they don't do any other processing if (cl->state != CS_ZOMBIE) { cl->lastPacketTime = sv.time; // don't timeout cl->frames[ cl->netchan.incomingAcknowledged & PACKET_MASK ] .messageAcked = sv.time; SV_ExecuteClientMessage( cl, msg ); } } return; } // if we received a sequenced packet from an address we don't reckognize, // send an out of band disconnect packet to it NET_OutOfBandPrint( NS_SERVER, from, "disconnect" ); }
void SVC_Info( netadr_t* from ) { int i, count; char *gamedir; char infostring[MAX_INFO_STRING]; char* g_password; if ( !SV_VerifyChallenge( Cmd_Argv( 1 ) ) ) { return; } g_password = Cvar_VariableString("g_password"); // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( getclient(i)->state >= CS_CONNECTED ) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) ); Info_SetValueForKey( infostring, "protocol", va("%i", protocol->integer)); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", mapname->string ); Info_SetValueForKey( infostring, "clients", va( "%i", count ) ); #ifdef xDEBUG Info_SetValueForKey( infostring, "sv_maxclients", va( "%i", sv_maxclients->integer - sv_privateClients->integer ) ); #else Info_SetValueForKey( infostring, "sv_maxclients", va( "%i", sv_maxclients->integer - sv_privateClients->integer ) ); #endif Info_SetValueForKey( infostring, "gametype", g_gametype->string ); Info_SetValueForKey( infostring, "pure", va( "%i", sv_pure->integer ) ); Info_SetValueForKey(infostring, "codextended", va("v%d", CURRENTBUILD)); if ( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va( "%i", sv_minPing->integer ) ); } if ( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va( "%i", sv_maxPing->integer ) ); } gamedir = Cvar_VariableString( "fs_game" ); if ( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } Info_SetValueForKey( infostring, "sv_allowAnonymous", va( "%i", sv_allowAnonymous->integer ) ); if(*g_password) Info_SetValueForKey( infostring, "pswrd", "1"); else Info_SetValueForKey( infostring, "pswrd", "0"); NET_OutOfBandPrint( NS_SERVER, *from, "infoResponse\n%s", infostring ); }
void SV_MasterHeartbeat( void ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; int time; // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; //we need to use this instead of svs.time since svs.time resets over map changes (or rather //every time the game restarts), and we don't really need to resolve every map change time = Com_Milliseconds(); // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified || SV_MasterNeedsResolving(i, time) ) { sv_master[i]->modified = qfalse; g_lastResolveTime[i] = time; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = BigShort( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], BigShort( adr[i].port ) ); } Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME ); } }
void SVC_Status( netadr_t* from ) { char player[1024]; char status[MAX_MSGLEN]; int i; client_t *cl; int/*playerState_t*/ *ps; int statusLength; int playerLength; char infostring[MAX_INFO_STRING]; int custom_mod = 0; char *fs_game = Cvar_VariableString("fs_game"); if(fs_game && *fs_game) custom_mod = 1; challenge_t* challenge; if ( !SV_VerifyChallenge( Cmd_Argv( 1 ) ) ) { return; } strcpy( infostring, Cvar_InfoString( 4 )); //1.5 uses 8196 Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) ); status[0] = 0; statusLength = 0; for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { cl = getclient(i); if ( cl->state >= CS_CONNECTED ) { //ps = SV_GameClientNum( i ); Com_sprintf( player, sizeof( player ), "%i %i \"%s\"\n", SV_GetClientScore(cl), cl->ping, cl->name ); playerLength = strlen( player ); if ( statusLength + playerLength >= sizeof( status ) ) { break; // can't hold any more } strcpy( status + statusLength, player ); statusLength += playerLength; } } #if CODPATCH == 5 if(sv_disableClientConsole->integer) Info_SetValueForKey(infostring, "con_disabled", va("%i", sv_disableClientConsole->integer)); #endif char *g_password = Cvar_VariableString("g_password"); Info_SetValueForKey(infostring, "pswrd", va("%i", (g_password && *g_password) ? 1 : 0)); Info_SetValueForKey(infostring, "mod", va("%i", custom_mod)); NET_OutOfBandPrint( NS_SERVER, *from, "statusResponse\n%s\n%s", infostring, status ); }
void CL_GlobalServers_f( void ) { netadr_t to; int i; int count; char *buffptr; char command[1024]; if (Cmd_Argc() < 3) { Com_Printf( 0, "usage: globalservers <master# 0-1> <protocol> [keywords]\n"); return; } cls.masterNum = atoi( Cmd_Argv(1) ); Com_Printf( 0, "Requesting servers from the master...\n"); // reset the list, waiting for response // -1 is used to distinguish a "no response" // TODO: supply multiple masters here NET_StringToAdr( "iw4.prod.fourdeltaone.net", &to ); cls.numglobalservers = -1; cls.pingUpdateSource = 0; to.type = NA_IP; to.port = htons(20810); sprintf( command, "getservers IW4 %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 ); NET_StringToAdr( "master.alterrev.net", &to ); to.type = NA_IP; to.port = htons(20810); strcpy(command, "getservers IW4 142 full empty"); NET_OutOfBandPrint(NS_SERVER, to, command); }
void SV_MasterHeartbeat( const char *hbname ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; // DHM - Nerve :: Update Server doesn't send heartbeat #ifdef UPDATE_SERVER return; #endif // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = BigShort( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], BigShort( adr[i].port ) ); } Com_Printf( "Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", hbname ); } }
/* ================= SV_GetChallenge A "getchallenge" OOB command has been received Returns a challenge number that can be used in a subsequent connectResponse command. We do this to prevent denial of service attacks that flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. If we are authorizing, a challenge request will cause a packet to be sent to the authorize server. When an authorizeip is returned, a challenge response will be sent to that ip. ================= */ void SV_GetChallenge( netadr_t from ) { int i; int oldest; int oldestTime; challenge_t *challenge; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } oldest = 0; oldestTime = 0x7fffffff; // see if we already have a challenge for this ip challenge = &svs.challenges[0]; for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { if ( !challenge->connected && NET_CompareAdr( from, challenge->adr ) ) { break; } if ( challenge->time < oldestTime ) { oldestTime = challenge->time; oldest = i; } } if (i == MAX_CHALLENGES) { // this is the first time this client has asked for a challenge challenge = &svs.challenges[oldest]; challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time; challenge->adr = from; challenge->firstTime = svs.time; challenge->time = svs.time; challenge->connected = qfalse; i = oldest; } // if they are on a lan address, send the challengeResponse immediately /*if ( Sys_IsLANAddress( from ) ) { challenge->pingTime = svs.time; NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge ); return; }*/ // if they have been challenging for a long time and we // haven't heard anything from the authorize server, go ahead and // let them in, assuming the id server is down if ( svs.time - challenge->firstTime > AUTHORIZE_TIMEOUT ) { Com_DPrintf( "authorize server timed out\n" ); challenge->pingTime = svs.time; NET_OutOfBandPrint( NS_SERVER, challenge->adr, "challengeResponse %i", challenge->challenge ); return; } }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count; char *gamedir; char infostring[MAX_INFO_STRING]; cvar_t *g_hiddenClients; /* * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led * to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory. */ // A maximum challenge length of 128 should be more than plenty. if(strlen(Cmd_Argv(1)) > 128) return; // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } g_hiddenClients = Cvar_Get( "g_hiddenClients", "0", 0 ); if( g_hiddenClients->integer > sv_maxclients->integer - sv_privateClients->integer ) { Cvar_Set( "g_hiddenClients", sv_maxclients->string - sv_privateClients->integer ); } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer - sv_privateClients->integer - g_hiddenClients->integer ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); if( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) ); } if( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) ); } gamedir = Cvar_VariableString( "fs_game" ); if( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
/* ================= SV_GetChallenge A "getchallenge" OOB command has been received Returns a challenge number that can be used in a subsequent connectResponse command. We do this to prevent denial of service attacks that flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. If we are authorizing, a challenge request will cause a packet to be sent to the authorize server. When an authorizeip is returned, a challenge response will be sent to that ip. ioquake3/openjk: we added a possibility for clients to add a challenge to their packets, to make it more difficult for malicious servers to hi-jack client connections. ================= */ void SV_GetChallenge( netadr_t from ) { int i; int oldest; int oldestTime; int clientChallenge; challenge_t *challenge; // ignore if we are in single player /* if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } */ if (Cvar_VariableValue("ui_singlePlayerActive")) { return; } oldest = 0; oldestTime = 0x7fffffff; // see if we already have a challenge for this ip challenge = &svs.challenges[0]; clientChallenge = atoi(Cmd_Argv(1)); for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { if(!challenge->connected && NET_CompareAdr(from, challenge->adr)) { break; } if ( challenge->time < oldestTime ) { oldestTime = challenge->time; oldest = i; } } if (i == MAX_CHALLENGES) { // this is the first time this client has asked for a challenge challenge = &svs.challenges[oldest]; challenge->adr = from; challenge->firstTime = svs.time; challenge->time = svs.time; challenge->connected = qfalse; } // always generate a new challenge number, so the client cannot circumvent sv_maxping challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time; challenge->wasrefused = qfalse; challenge->pingTime = svs.time; NET_OutOfBandPrint( NS_SERVER, challenge->adr, "challengeResponse %i %i", challenge->challenge, clientChallenge ); }
/* ================== SV_Ban_f Ban a user from being able to play on this server through the auth server ================== */ static void SV_Ban_f(void) { client_t *cl; // make sure server is running if (!com_sv_running->integer) { Com_Printf("Server is not running.\n"); return; } if (Cmd_Argc() != 2) { Com_Printf("Usage: banUser <player name>\n"); return; } cl = SV_GetPlayerByName(); if (!cl) { return; } if (cl->netchan.remoteAddress.type == NA_LOOPBACK) { SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); return; } // look up the authorize server's IP if (!svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD) { Com_Printf("Resolving %s\n", AUTHORIZE_SERVER_NAME); if (!NET_StringToAdr(AUTHORIZE_SERVER_NAME, &svs.authorizeAddress)) { Com_Printf("Couldn't resolve address\n"); return; } svs.authorizeAddress.port = BigShort(PORT_AUTHORIZE); Com_Printf("%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], BigShort(svs.authorizeAddress.port)); } // otherwise send their ip to the authorize server if (svs.authorizeAddress.type != NA_BAD) { NET_OutOfBandPrint(NS_SERVER, svs.authorizeAddress, "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3]); Com_Printf("%s was banned from coming back\n", rc(cl->name)); } }
/* ================ SVC_Status Responds with all the info that qplug or qspy can see about the server and all connected players. Used for getting detailed information after the simple info query. ================ */ void SVC_Status( netadr_t from ) { char player[1024]; char status[MAX_MSGLEN]; int i; client_t *cl; playerState_t *ps; int statusLength; int playerLength; char infostring[MAX_INFO_STRING]; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) { return; } // DHM - Nerve #ifdef UPDATE_SERVER return; #endif strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) ); // echo back the parameter to status. so master servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) ); // add "demo" to the sv_keywords if restricted if ( Cvar_VariableValue( "fs_restrict" ) ) { char keywords[MAX_INFO_STRING]; Com_sprintf( keywords, sizeof( keywords ), "demo %s", Info_ValueForKey( infostring, "sv_keywords" ) ); Info_SetValueForKey( infostring, "sv_keywords", keywords ); } status[0] = 0; statusLength = 0; for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if ( cl->state >= CS_CONNECTED ) { ps = SV_GameClientNum( i ); Com_sprintf( player, sizeof( player ), "%i %i \"%s\"\n", ps->persistant[PERS_SCORE], cl->ping, cl->name ); playerLength = strlen( player ); if ( statusLength + playerLength >= sizeof( status ) ) { break; // can't hold any more } strcpy( status + statusLength, player ); statusLength += playerLength; } } NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status ); }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count; char *gamedir; char infostring[MAX_INFO_STRING]; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) { return; } // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) ); Info_SetValueForKey( infostring, "protocol", va( "%i", PROTOCOL_VERSION ) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va( "%i", count ) ); Info_SetValueForKey( infostring, "sv_maxclients", va( "%i", sv_maxclients->integer - sv_privateClients->integer ) ); Info_SetValueForKey( infostring, "gametype", va( "%i", sv_gametype->integer ) ); //fretn Info_SetValueForKey( infostring, "coop", va( "%i", sv_coop->integer ) ); Info_SetValueForKey( infostring, "pure", va( "%i", sv_pure->integer ) ); if ( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va( "%i", sv_minPing->integer ) ); } if ( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va( "%i", sv_maxPing->integer ) ); } gamedir = Cvar_VariableString( "fs_game" ); if ( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } Info_SetValueForKey( infostring, "sv_allowAnonymous", va( "%i", sv_allowAnonymous->integer ) ); // Rafael gameskill Info_SetValueForKey( infostring, "gameskill", va( "%i", sv_gameskill->integer ) ); // done NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
/* ================ SVC_Status Responds with all the info that qplug or qspy can see about the server and all connected players. Used for getting detailed information after the simple info query. ================ */ void SVC_Status( netadr_t from, const Cmd::Args& args ) { char player[ 1024 ]; char status[ MAX_MSGLEN ]; int i; client_t *cl; playerState_t *ps; int statusLength; int playerLength; char infostring[ MAX_INFO_STRING ]; //bani - bugtraq 12534 if ( args.Argc() > 1 && !SV_VerifyChallenge( args.Argv(1).c_str() ) ) { return; } Q_strncpyz( infostring, Cvar_InfoString( CVAR_SERVERINFO, false ), MAX_INFO_STRING ); if ( args.Argc() > 1 ) { // echo back the parameter to status. so master servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", args.Argv(1).c_str(), false ); } status[ 0 ] = 0; statusLength = 0; for ( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[ i ]; if ( cl->state >= CS_CONNECTED ) { ps = SV_GameClientNum( i ); Com_sprintf( player, sizeof( player ), "%i %i \"%s\"\n", ps->persistant[ PERS_SCORE ], cl->ping, cl->name ); playerLength = strlen( player ); if ( statusLength + playerLength >= (int) sizeof( status ) ) { break; // can't hold any more } strcpy( status + statusLength, player ); statusLength += playerLength; } } NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status ); }
/* ================= SV_GetChallenge A "getchallenge" OOB command has been received Returns a challenge number that can be used in a subsequent connectResponse command. We do this to prevent denial of service attacks that flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. If we are authorizing, a challenge request will cause a packet to be sent to the authorize server. When an authorizeip is returned, a challenge response will be sent to that ip. ================= */ void SV_GetChallenge( netadr_t from ) { int i; int oldest; int oldestTime; challenge_t *challenge; // ignore if we are in single player /* if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } */ if (Cvar_VariableValue("ui_singlePlayerActive")) { return; } oldest = 0; oldestTime = 0x7fffffff; // see if we already have a challenge for this ip challenge = &svs.challenges[0]; for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { if ( !challenge->connected && NET_CompareAdr( from, challenge->adr ) ) { break; } if ( challenge->time < oldestTime ) { oldestTime = challenge->time; oldest = i; } } if (i == MAX_CHALLENGES) { // this is the first time this client has asked for a challenge challenge = &svs.challenges[oldest]; challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time; challenge->adr = from; challenge->firstTime = svs.time; challenge->time = svs.time; challenge->connected = qfalse; i = oldest; } // if they are on a lan address, send the challengeResponse immediately if ( Sys_IsLANAddress( from ) ) { challenge->pingTime = svs.time; NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge ); return; } }
/* ================= SV_GetChallenge A "getchallenge" OOB command has been received Returns a challenge number that can be used in a subsequent connectResponse command. We do this to prevent denial of service attacks that flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. ================= */ void SV_GetChallenge( netadr_t from ) { int i; int oldest; int oldestTime; int oldestClientTime; int clientChallenge; challenge_t *challenge; qboolean wasfound = qfalse; oldest = 0; oldestClientTime = oldestTime = 0x7fffffff; // see if we already have a challenge for this ip challenge = &svs.challenges[0]; clientChallenge = atoi(Cmd_Argv(1)); for (i = 0; i < MAX_CHALLENGES; i++, challenge++) { if (!challenge->connected && NET_CompareAdr(from, challenge->adr)) { wasfound = qtrue; if (challenge->time < oldestClientTime) oldestClientTime = challenge->time; } if (wasfound && i >= MAX_CHALLENGES_MULTI) { i = MAX_CHALLENGES; break; } if ( challenge->time < oldestTime ) { oldestTime = challenge->time; oldest = i; } } if (i == MAX_CHALLENGES) { // this is the first time this client has asked for a challenge challenge = &svs.challenges[oldest]; challenge->clientChallenge = clientChallenge; challenge->adr = from; challenge->connected = qfalse; } // always generate a new challenge number, so the client cannot circumvent sv_maxping challenge->challenge = ((rand() << 16) ^ rand()) ^ svs.time; challenge->wasrefused = qfalse; challenge->time = svs.time; challenge->pingTime = svs.time; NET_OutOfBandPrint(NS_SERVER, challenge->adr, "challengeResponse %i %i", challenge->challenge, clientChallenge); }
void band_test() { int argc = Cmd_Argc(); if(argc != 3) { printf("Error\n"); return; } int index = 0; if(index < 0 || index >= sv_maxclients->integer) { printf("Error, wrong index.\n"); return; } client_t* cl = getclient(index); NET_OutOfBandPrint( NS_SERVER, cl->remoteAddress, Cmd_Argv(2)); }
void CL_CheckAutoUpdate(void) { char info[MAX_INFO_STRING]; #ifndef FEATURE_AUTOUPDATE return; #endif if (!com_autoupdate->integer) { Com_DPrintf("Updater is disabled by com_autoupdate 0.\n"); return; } // Only check once per session if (autoupdate.updateChecked) { return; } // Resolve update server Com_Printf("Updater: resolving %s... ", UPDATE_SERVER_NAME); if (!NET_StringToAdr(va("%s:%i", UPDATE_SERVER_NAME, PORT_UPDATE), &autoupdate.autoupdateServer, NA_UNSPEC)) { Com_Printf("couldn't resolve address\n"); autoupdate.updateChecked = qtrue; return; } else { Com_Printf("resolved to %s\n", NET_AdrToString(autoupdate.autoupdateServer)); } info[0] = 0; Info_SetValueForKey(info, "version", ETLEGACY_VERSION_SHORT); Info_SetValueForKey(info, "platform", CPUSTRING); Info_SetValueForKey(info, va("etl_bin_%s.pk3", ETLEGACY_VERSION_SHORT), Com_MD5File(va("legacy/etl_bin_%s.pk3", ETLEGACY_VERSION_SHORT), 0, NULL, 0)); Info_SetValueForKey(info, va("pak3_%s.pk3", ETLEGACY_VERSION_SHORT), Com_MD5File(va("legacy/pak3_%s.pk3", ETLEGACY_VERSION_SHORT), 0, NULL, 0)); NET_OutOfBandPrint(NS_CLIENT, autoupdate.autoupdateServer, "getUpdateInfo \"%s\"", info); autoupdate.updateChecked = qtrue; }
/* ================= SV_GetChallenge A "getchallenge" OOB command has been received Returns a challenge number that can be used in a subsequent connectResponse command. We do this to prevent denial of service attacks that flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. If we are authorizing, a challenge request will cause a packet to be sent to the authorize server. When an authorizeip is returned, a challenge response will be sent to that IP address. ================= */ void SV_GetChallenge( netadr_t from ) { int i; int oldest; int oldestTime; challenge_t *challenge; oldest = 0; oldestTime = 0x7fffffff; // see if we already have a challenge for this IP address challenge = &svs.challenges[ 0 ]; for ( i = 0; i < MAX_CHALLENGES; i++, challenge++ ) { if ( !challenge->connected && NET_CompareAdr( from, challenge->adr ) ) { break; } if ( challenge->time < oldestTime ) { oldestTime = challenge->time; oldest = i; } } if ( i == MAX_CHALLENGES ) { // this is the first time this client has asked for a challenge challenge = &svs.challenges[ oldest ]; challenge->challenge = ( ( rand() << 16 ) ^ rand() ) ^ svs.time; challenge->adr = from; challenge->firstTime = svs.time; challenge->firstPing = 0; challenge->time = svs.time; challenge->connected = false; i = oldest; } challenge->pingTime = svs.time; NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "challengeResponse %i", challenge->challenge ); return; }
void getNego(netadr_t from, msg_t* msg) { char* status; if(!isSearching) { status = "NET"; // NET: Not eligible for transmission } else { status = "OK"; // OK: Oll Korrect ;) negociatedHost = true; natosa(from, &partner); connectState = connecting; } NET_OutOfBandPrint(NS_SERVER, from, "negoResponse\n%s", status); }
void SV_MasterHeartbeat( void ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i], NA_UNSPEC ) ) { Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); continue; } if ( !strchr( sv_master[i]->string, ':' ) ) { adr[i].port = BigShort( PORT_MASTER ); } Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i])); } Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME ); } }
/* ================== 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 ); }
void gsc_player_outofbandprint(int id) { char* cmd; // print\ninsert test message here!!!\n if ( ! stackGetParams("s", &cmd)) { printf("scriptengine> ERROR: gsc_player_outofbandprint(): param \"cmd\"[1] has to be an string!\n"); stackPushUndefined(); return; } #if COD_VERSION == COD2_1_0 int remoteaddress_offset = 452036; #else int remoteaddress_offset = 452308; #endif int info_player = PLAYERBASE(id); netadr_t * from = (netadr_t*)(info_player + remoteaddress_offset); NET_OutOfBandPrint(0, *from, cmd); // 0 = SERVER, 1 = CLIENT stackReturnInt(1); }
/* ================ SVC_Status Responds with all the info that qplug or qspy can see about the server and all connected players. Used for getting detailed information after the simple info query. ================ */ void SVC_Status( netadr_t from ) { char player[1024]; char status[MAX_MSGLEN]; int i; client_t *cl; int statusLength; int playerLength; int score; char infostring[MAX_INFO_STRING]; strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) ); // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); status[0] = 0; statusLength = 0; for (i=0 ; i < 1 ; i++) { cl = &svs.clients[i]; if ( cl->state >= CS_CONNECTED ) { if ( cl->gentity && cl->gentity->client ) { score = cl->gentity->client->persistant[PERS_SCORE]; } else { score = 0; } Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", score, cl->ping, cl->name); playerLength = strlen(player); if (statusLength + playerLength >= sizeof(status) ) { break; // can't hold any more } strcpy (status + statusLength, player); statusLength += playerLength; } } NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, 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" ); }
/* ================ SVC_FlushRedirect ================ */ void SV_FlushRedirect( char *outputbuf ) { NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf ); }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count, bots; char *gamedir; char infostring[MAX_INFO_STRING]; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } /* * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led * to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory. */ // A maximum challenge length of 128 should be more than plenty. if(strlen(Cmd_Argv(1)) > 128) return; // don't count privateclients count = 0; bots = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; if (svs.clients[i].netchan.remoteAddress.type == NA_BOT) bots++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "bots", va("%i", bots) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); //@Barbatos #ifdef USE_AUTH Info_SetValueForKey( infostring, "auth", Cvar_VariableString("auth") ); #endif //@Barbatos: if it's a passworded server, let the client know (for the server browser) if(Cvar_VariableValue("g_needpass") == 1) Info_SetValueForKey( infostring, "password", va("%i", 1)); if( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) ); } if( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) ); } gamedir = Cvar_VariableString( "fs_game" ); if( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } Info_SetValueForKey(infostring, "modversion", Cvar_VariableString("g_modversion")); NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
/* ================== SV_DirectConnect A "connect" OOB command has been received ================== */ void SV_DirectConnect( netadr_t from ) { char userinfo[MAX_INFO_STRING]; int i; client_t *cl, *newcl; MAC_STATIC client_t temp; gentity_t *ent; int clientNum; int version; int qport; int challenge; char *denied; Com_DPrintf ("SVC_DirectConnect ()\n"); Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); if ( version != PROTOCOL_VERSION ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION ); Com_DPrintf (" rejected connect from version %i\n", version); return; } qport = atoi( Info_ValueForKey( userinfo, "qport" ) ); challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); // see if the challenge is valid (local clients don't need to challenge) if ( !NET_IsLocalAddress (from) ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nNo challenge for address.\n" ); return; } else { // force the "ip" info key to "localhost" Info_SetValueForKey( userinfo, "ip", "localhost" ); } newcl = &temp; memset (newcl, 0, sizeof(client_t)); // if there is already a slot for this ip, reuse it for (i=0,cl=svs.clients ; i < 1 ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { if (( sv.time - cl->lastConnectTime) < (sv_reconnectlimit->integer * 1000)) { Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from)); return; } Com_Printf ("%s:reconnect\n", NET_AdrToString (from)); newcl = cl; goto gotnewcl; } } newcl = NULL; for ( i = 0; i < 1 ; i++ ) { cl = &svs.clients[i]; if (cl->state == CS_FREE) { newcl = cl; break; } } if ( !newcl ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is full.\n" ); Com_DPrintf ("Rejected a connection.\n"); return; } gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; clientNum = newcl - svs.clients; ent = SV_GentityNum( clientNum ); newcl->gentity = ent; // save the address Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport); // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) ); // get the game a chance to reject this connection or modify the userinfo denied = ge->ClientConnect( clientNum, qtrue, eSavedGameJustLoaded ); // firstTime = qtrue if ( denied ) { NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", denied ); Com_DPrintf ("Game rejected a connection: %s.\n", denied); return; } SV_UserinfoChanged( newcl ); // send the connect packet to the client NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" ); newcl->state = CS_CONNECTED; newcl->nextSnapshotTime = sv.time; newcl->lastPacketTime = sv.time; newcl->lastConnectTime = sv.time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newcl->gamestateMessageNum = -1; }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count; const char *gamedir; char infostring[MAX_INFO_STRING]; #if defined RTCW_MP const char *antilag; // DHM - Nerve #ifdef UPDATE_SERVER return; #endif #endif // RTCW_XX #if defined RTCW_ET const char *antilag; const char *weaprestrict; const char *balancedteams; // ignore if we are in single player if ( SV_GameIsSinglePlayer() ) { return; } #endif // RTCW_XX #if !defined RTCW_ET // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) { return; } #endif // RTCW_XX #if defined RTCW_ET //bani - bugtraq 12534 if ( !SV_VerifyChallenge( Cmd_Argv( 1 ) ) ) { return; } #endif // RTCW_XX // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) ); Info_SetValueForKey( infostring, "protocol", va( "%i", PROTOCOL_VERSION ) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); #if defined RTCW_ET Info_SetValueForKey( infostring, "serverload", va( "%i", svs.serverLoad ) ); #endif // RTCW_XX Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va( "%i", count ) ); Info_SetValueForKey( infostring, "sv_maxclients", va( "%i", sv_maxclients->integer - sv_privateClients->integer ) ); #if !defined RTCW_ET Info_SetValueForKey( infostring, "gametype", va( "%i", sv_gametype->integer ) ); #else Info_SetValueForKey( infostring, "gametype", Cvar_VariableString( "g_gametype" ) ); #endif // RTCW_XX Info_SetValueForKey( infostring, "pure", va( "%i", sv_pure->integer ) ); if ( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va( "%i", sv_minPing->integer ) ); } if ( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va( "%i", sv_maxPing->integer ) ); } gamedir = Cvar_VariableString( "fs_game" ); if ( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } Info_SetValueForKey( infostring, "sv_allowAnonymous", va( "%i", sv_allowAnonymous->integer ) ); // Rafael gameskill #if !defined RTCW_ET Info_SetValueForKey( infostring, "gameskill", va( "%i", sv_gameskill->integer ) ); #else // Info_SetValueForKey (infostring, "gameskill", va ("%i", sv_gameskill->integer)); #endif // RTCW_XX // done #if !defined RTCW_SP Info_SetValueForKey( infostring, "friendlyFire", va( "%i", sv_friendlyFire->integer ) ); // NERVE - SMF Info_SetValueForKey( infostring, "maxlives", va( "%i", sv_maxlives->integer ? 1 : 0 ) ); // NERVE - SMF #if !defined RTCW_ET Info_SetValueForKey( infostring, "tourney", va( "%i", sv_tourney->integer ) ); // NERVE - SMF #endif // RTCW_XX #if defined RTCW_ET Info_SetValueForKey( infostring, "needpass", va( "%i", sv_needpass->integer ? 1 : 0 ) ); #endif // RTCW_XX Info_SetValueForKey( infostring, "gamename", GAMENAME_STRING ); // Arnout: to be able to filter out Quake servers // TTimo antilag = Cvar_VariableString( "g_antilag" ); if ( antilag ) { Info_SetValueForKey( infostring, "g_antilag", antilag ); } #endif // RTCW_XX #if defined RTCW_ET weaprestrict = Cvar_VariableString( "g_heavyWeaponRestriction" ); if ( weaprestrict ) { Info_SetValueForKey( infostring, "weaprestrict", weaprestrict ); } balancedteams = Cvar_VariableString( "g_balancedteams" ); if ( balancedteams ) { Info_SetValueForKey( infostring, "balancedteams", balancedteams ); } #endif // RTCW_XX NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
/* ================= SV_SendServerCommand Sends a reliable command string to be interpreted by the client game module: "cp", "print", "chat", etc A NULL client will broadcast to all clients ================= */ void QDECL SV_SendServerCommand( client_t *cl, const char *fmt, ... ) { va_list argptr; byte message[MAX_MSGLEN]; client_t *client; int j; va_start( argptr,fmt ); #if defined RTCW_SP vsprintf( (char *)message, fmt,argptr ); #else Q_vsnprintf( (char *)message, sizeof( message ), fmt, argptr ); #endif // RTCW_XX va_end( argptr ); #if !defined RTCW_SP // do not forward server command messages that would be too big to clients // ( q3infoboom / q3msgboom stuff ) if ( strlen( (char *)message ) > 1022 ) { return; } #endif // RTCW_XX if ( cl != NULL ) { SV_AddServerCommand( cl, (char *)message ); return; } // hack to echo broadcast prints to console if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5 ) ) { Com_Printf( "broadcast: %s\n", SV_ExpandNewlines( (char *)message ) ); } // send the data to all relevent clients for ( j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++ ) { if ( client->state < CS_PRIMED ) { continue; } // Ridah, don't need to send messages to AI #if !defined RTCW_ET if ( client->gentity && client->gentity->r.svFlags & SVF_CASTAI ) { #else if ( client->gentity && client->gentity->r.svFlags & SVF_BOT ) { #endif // RTCW_XX continue; } // done. SV_AddServerCommand( client, (char *)message ); } } /* ============================================================================== MASTER SERVER FUNCTIONS ============================================================================== */ /* ================ SV_MasterHeartbeat Send a message to the masters every few minutes to let it know we are alive, and log information. We will also have a heartbeat sent when a server changes from empty to non-empty, and full to non-full, but not on every player enter or exit. ================ */ #define HEARTBEAT_MSEC 300 * 1000 #if !defined RTCW_ET #define HEARTBEAT_GAME "Wolfenstein-1" #else //#define HEARTBEAT_GAME "Wolfenstein-1" //#define HEARTBEAT_DEAD "WolfFlatline-1" // NERVE - SMF #define HEARTBEAT_GAME "EnemyTerritory-1" #endif // RTCW_XX #if defined RTCW_SP void SV_MasterHeartbeat( void ) { #else #if !defined RTCW_ET #define HEARTBEAT_DEAD "WolfFlatline-1" // NERVE - SMF #else #define HEARTBEAT_DEAD "ETFlatline-1" // NERVE - SMF #endif // RTCW_XX void SV_MasterHeartbeat( const char *hbname ) { #endif // RTCW_XX static netadr_t adr[MAX_MASTER_SERVERS]; int i; #if defined RTCW_MP // DHM - Nerve :: Update Server doesn't send heartbeat #ifdef UPDATE_SERVER return; #endif #endif // RTCW_XX #if defined RTCW_ET if ( SV_GameIsSinglePlayer() ) { return; // no heartbeats for SP } #endif // RTCW_XX // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = rtcw::Endian::be( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], rtcw::Endian::be( adr[i].port ) ); } Com_Printf( "Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes #if defined RTCW_SP NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME ); #else NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", hbname ); #endif // RTCW_XX } } #if !defined RTCW_SP /* ================= SV_MasterGameCompleteStatus NERVE - SMF - Sends gameCompleteStatus messages to all master servers ================= */ void SV_MasterGameCompleteStatus() { static netadr_t adr[MAX_MASTER_SERVERS]; int i; #if defined RTCW_ET if ( SV_GameIsSinglePlayer() ) { return; // no master game status for SP } #endif // RTCW_XX // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send master game status } // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = rtcw::Endian::be ( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], rtcw::Endian::be ( adr[i].port ) ); } Com_Printf( "Sending gameCompleteStatus to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes SVC_GameCompleteStatus( adr[i] ); } } #endif // RTCW_XX /* ================= SV_MasterShutdown Informs all masters that this server is going down ================= */ void SV_MasterShutdown( void ) { // send a hearbeat right now svs.nextHeartbeatTime = -9999; #if defined RTCW_SP SV_MasterHeartbeat(); #else SV_MasterHeartbeat( HEARTBEAT_DEAD ); // NERVE - SMF - changed to flatline #endif // RTCW_XX // send it again to minimize chance of drops #if defined RTCW_SP svs.nextHeartbeatTime = -9999; SV_MasterHeartbeat(); #else // svs.nextHeartbeatTime = -9999; // SV_MasterHeartbeat( HEARTBEAT_DEAD ); #endif // RTCW_XX // when the master tries to poll the server, it won't respond, so // it will be removed from the list }