static void CD_f () { char *command; int cdstate; if ( Cmd_Argc() < 2 ) { return; } command = Cmd_Argv( 1 ); if ( !Q_strcasecmp( command, "on" ) ) { enabled = true; } if ( !Q_strcasecmp( command, "off" ) ) { if ( !cd_id ) { return; } cdstate = SDL_CDStatus( cd_id ); if ( ( cdstate == CD_PLAYING ) || ( cdstate == CD_PAUSED ) ) { CDAudio_Stop(); } enabled = false; return; } if ( !Q_strcasecmp( command, "play" ) ) { CDAudio_Play( (byte) atoi( Cmd_Argv( 2 ) ), false ); return; } if ( !Q_strcasecmp( command, "loop" ) ) { CDAudio_Play( (byte) atoi( Cmd_Argv( 2 ) ), true ); return; } if ( !Q_strcasecmp( command, "stop" ) ) { CDAudio_Stop(); return; } if ( !Q_strcasecmp( command, "pause" ) ) { CDAudio_Pause(); return; } if ( !Q_strcasecmp( command, "resume" ) ) { CDAudio_Resume(); return; } if ( !Q_strcasecmp( command, "eject" ) ) { CDAudio_Eject(); return; } if ( !Q_strcasecmp( command, "info" ) ) { if ( !cd_id ) { return; } cdstate = SDL_CDStatus( cd_id ); Com_Printf( "%d tracks\n", cd_id->numtracks ); if ( cdstate == CD_PLAYING ) { Com_Printf( "Currently %s track %d\n", playLooping ? "looping" : "playing", cd_id->cur_track + 1 ); } else if ( cdstate == CD_PAUSED ) { Com_Printf( "Paused %s track %d\n", playLooping ? "looping" : "playing", cd_id->cur_track + 1 ); } return; } }
/* =================== CL_GetServerCommand Set up argc/argv for the given command =================== */ qboolean CL_GetServerCommand( int serverCommandNumber ) { char *s; char *cmd; static char bigConfigString[BIG_INFO_STRING]; int argc; // if we have irretrievably lost a reliable command, drop the connection if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) { // when a demo record was started after the client got a whole bunch of // reliable commands then the client never got those first reliable commands if ( clc.demoplaying ) return qfalse; Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" ); return qfalse; } if ( serverCommandNumber > clc.serverCommandSequence ) { Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" ); return qfalse; } s = clc.serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ]; clc.lastExecutedServerCommand = serverCommandNumber; Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s ); rescan: Cmd_TokenizeString( s ); cmd = Cmd_Argv(0); argc = Cmd_Argc(); if ( !strcmp( cmd, "disconnect" ) ) { // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552 // allow server to indicate why they were disconnected if ( argc >= 2 ) Com_Error( ERR_SERVERDISCONNECT, "Server disconnected - %s", Cmd_Argv( 1 ) ); else Com_Error( ERR_SERVERDISCONNECT, "Server disconnected" ); } if ( !strcmp( cmd, "bcs0" ) ) { Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) ); return qfalse; } if ( !strcmp( cmd, "bcs1" ) ) { s = Cmd_Argv(2); if( strlen(bigConfigString) + strlen(s) >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); } strcat( bigConfigString, s ); return qfalse; } if ( !strcmp( cmd, "bcs2" ) ) { s = Cmd_Argv(2); if( strlen(bigConfigString) + strlen(s) + 1 >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); } strcat( bigConfigString, s ); strcat( bigConfigString, "\"" ); s = bigConfigString; goto rescan; } if ( !strcmp( cmd, "cs" ) ) { CL_ConfigstringModified(); // reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString() Cmd_TokenizeString( s ); return qtrue; } if ( !strcmp( cmd, "map_restart" ) ) { // clear notify lines and outgoing commands before passing // the restart to the cgame Con_ClearNotify(); // reparse the string, because Con_ClearNotify() may have done another Cmd_TokenizeString() Cmd_TokenizeString( s ); Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) ); return qtrue; } // the clientLevelShot command is used during development // to generate 128*128 screenshots from the intermission // point of levels for the menu system to use // we pass it along to the cgame to make apropriate adjustments, // but we also clear the console and notify lines here if ( !strcmp( cmd, "clientLevelShot" ) ) { // don't do it if we aren't running the server locally, // otherwise malicious remote servers could overwrite // the existing thumbnails if ( !com_sv_running->integer ) { return qfalse; } // close the console Con_Close(); // take a special screenshot next frame Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" ); return qtrue; } // we may want to put a "connect to other server" command here // cgame can now act on the command return qtrue; }
/* ============ Cmd_ArgvBuffer The interpreted versions use this because they can't have pointers returned to them ============ */ void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) { Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength ); }
/* ================ SV_MapRestart_f Completely restarts a level, but doesn't send a new gamestate to the clients. This allows fair starts with variable load times. ================ */ static void SV_MapRestart_f(void) { int i; client_t *client; char *denied; qboolean isBot; int delay = 0; gamestate_t new_gs, old_gs; // NERVE - SMF // make sure we aren't restarting twice in the same frame if (com_frameTime == sv.serverId) { return; } // make sure server is running if (!com_sv_running->integer) { Com_Printf("Server is not running.\n"); return; } // ydnar: allow multiple delayed server restarts [atvi bug 3813] //% if ( sv.restartTime ) { //% return; //% } if (Cmd_Argc() > 1) { delay = atoi(Cmd_Argv(1)); } if (delay) { sv.restartTime = svs.time + delay * 1000; SV_SetConfigstring(CS_WARMUP, va("%i", sv.restartTime)); return; } // NERVE - SMF - read in gamestate or just default to GS_PLAYING old_gs = atoi(Cvar_VariableString("gamestate")); if (SV_GameIsSinglePlayer() || SV_GameIsCoop()) { new_gs = GS_PLAYING; } else { if (Cmd_Argc() > 2) { new_gs = atoi(Cmd_Argv(2)); } else { new_gs = GS_PLAYING; } } if (!SV_TransitionGameState(new_gs, old_gs, delay)) { return; } // check for changes in variables that can't just be restarted // check for maxclients change if (sv_maxclients->modified) { char mapname[MAX_QPATH]; Com_Printf("sv_maxclients variable change -- restarting.\n"); // restart the map the slow way Q_strncpyz(mapname, Cvar_VariableString("mapname"), sizeof(mapname)); SV_SpawnServer(mapname, qfalse); return; } // Check for loading a saved game if (Cvar_VariableIntegerValue("savegame_loading")) { // open the current savegame, and find out what the time is, everything else we can ignore char savemap[MAX_QPATH]; byte *buffer; int size, savegameTime; char *cl_profileStr = Cvar_VariableString("cl_profile"); if (com_gameInfo.usesProfiles) { Com_sprintf(savemap, sizeof(savemap), "profiles/%s/save/current.sav", cl_profileStr); } else { Q_strncpyz(savemap, "save/current.sav", sizeof(savemap)); } size = FS_ReadFile(savemap, NULL); if (size < 0) { Com_Printf("Can't find savegame %s\n", savemap); return; } //buffer = Hunk_AllocateTempMemory(size); FS_ReadFile(savemap, (void **)&buffer); // the mapname is at the very start of the savegame file savegameTime = *( int * )(buffer + sizeof(int) + MAX_QPATH); if (savegameTime >= 0) { svs.time = savegameTime; } Hunk_FreeTempMemory(buffer); } // done. // toggle the server bit so clients can detect that a // map_restart has happened svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // generate a new serverid // TTimo - don't update restartedserverId there, otherwise we won't deal correctly with multiple map_restart sv.serverId = com_frameTime; Cvar_Set("sv_serverid", va("%i", sv.serverId)); // reset all the vm data in place without changing memory allocation // note that we do NOT set sv.state = SS_LOADING, so configstrings that // had been changed from their default values will generate broadcast updates sv.state = SS_LOADING; sv.restarting = qtrue; Cvar_Set("sv_serverRestarting", "1"); SV_RestartGameProgs(); // run a few frames to allow everything to settle for (i = 0; i < GAME_INIT_FRAMES; i++) { VM_Call(gvm, GAME_RUN_FRAME, svs.time); svs.time += FRAMETIME; } // create a baseline for more efficient communications // Gordon: meh, this wont work here as the client doesn't know it has happened // SV_CreateBaseline (); sv.state = SS_GAME; sv.restarting = qfalse; // connect and begin all the clients for (i = 0 ; i < sv_maxclients->integer ; i++) { client = &svs.clients[i]; // send the new gamestate to all connected clients if (client->state < CS_CONNECTED) { continue; } if (client->netchan.remoteAddress.type == NA_BOT) { if (SV_GameIsSinglePlayer() || SV_GameIsCoop()) { continue; // dont carry across bots in single player } isBot = qtrue; } else { isBot = qfalse; } // add the map_restart command SV_AddServerCommand(client, "map_restart\n"); // connect the client again, without the firstTime flag denied = VM_ExplicitArgPtr(gvm, VM_Call(gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot)); if (denied) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient(client, denied); if ((!SV_GameIsSinglePlayer()) || (!isBot)) { Com_Printf("SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i); // bk010125 } continue; } client->state = CS_ACTIVE; SV_ClientEnterWorld(client, &client->lastUsercmd); } // run another frame to allow things to look at all the players VM_Call(gvm, GAME_RUN_FRAME, svs.time); svs.time += FRAMETIME; Cvar_Set("sv_serverRestarting", "0"); }
static void ParseMasterArgs(netadr_t *broadcast) { void *data; ssize_t len; void (*parse)(void *, size_t, size_t); size_t chunk; char *s, *p; int i, argc; Cmd_TokenizeString(m_servers.args, qfalse); argc = Cmd_Argc(); if (!argc) { // default action to take when no URLs are given ParseAddressBook(); broadcast->type = NA_BROADCAST; broadcast->port = BigShort(PORT_SERVER); return; } for (i = 0; i < argc; i++) { s = Cmd_Argv(i); if (!*s) continue; // parse binary format specifier parse = ParsePlain; chunk = 0; if (*s == '+' || *s == '-') { parse = ParseBinary; chunk = strtoul(s, &p, 10); if (s == p) { chunk = 6; s = p + 1; } else { if (chunk < 6) goto ignore; s = p; } } if (!strncmp(s, "file://", 7)) { len = FS_LoadFile(s + 7, &data); if (len < 0) continue; (*parse)(data, len, chunk); FS_FreeFile(data); continue; } if (!strncmp(s, "http://", 7)) { #if USE_CURL len = HTTP_FetchFile(s + 7, &data); if (len < 0) continue; (*parse)(data, len, chunk); Z_Free(data); #else Com_Printf("Can't fetch '%s', no HTTP support compiled in.\n", s); #endif continue; } if (!strncmp(s, "favorites://", 12)) { ParseAddressBook(); continue; } if (!strncmp(s, "broadcast://", 12)) { broadcast->type = NA_BROADCAST; broadcast->port = BigShort(PORT_SERVER); continue; } if (!strncmp(s, "quake2://", 9)) { AddServer(NULL, s + 9); continue; } ignore: Com_Printf("Ignoring invalid master URL: %s\n", s); } }
static void SV_DelBanFromList(qboolean isexception) { int index, count = 0, todel, mask; netadr_t ip; char *banstring; if(Cmd_Argc() != 2) { Com_Printf ("Usage: %s (ip[/subnet] | num)\n", Cmd_Argv(0)); return; } banstring = Cmd_Argv(1); if(strchr(banstring, '.') || strchr(banstring, ':')) { serverBan_t *curban; if(SV_ParseCIDRNotation(&ip, &mask, banstring)) { Com_Printf("Error: Invalid address %s\n", banstring); return; } index = 0; while(index < serverBansCount) { curban = &serverBans[index]; if(curban->isexception == isexception && curban->subnet >= mask && NET_CompareBaseAdrMask(curban->ip, ip, mask)) { Com_Printf("Deleting %s %s/%d\n", isexception ? "exception" : "ban", NET_AdrToString(curban->ip), curban->subnet); SV_DelBanEntryFromList(index); } else index++; } } else { todel = atoi(Cmd_Argv(1)); if(todel < 1 || todel > serverBansCount) { Com_Printf("Error: Invalid ban number given\n"); return; } for(index = 0; index < serverBansCount; index++) { if(serverBans[index].isexception == isexception) { count++; if(count == todel) { Com_Printf("Deleting %s %s/%d\n", isexception ? "exception" : "ban", NET_AdrToString(serverBans[index].ip), serverBans[index].subnet); SV_DelBanEntryFromList(index); break; } } } } SV_WriteBans(); }
/* ----------------------------------------------------------------------------- Function: Cmd_Alias_f -Creates a new command that executes a command string (possibly ; seperated). Parameters: Nothing. Returns: Nothing. Notes: ----------------------------------------------------------------------------- */ PRIVATE void Cmd_Alias_f( void ) { cmdalias_t *a; char cmd[ 1024 ]; int i, c; char *s; W32 hashid; if( Cmd_Argc() == 1 ) { Com_Printf( "Current alias commands:\n" ); for( a = cmd_alias ; a ; a = a->next ) { Com_Printf( "%s : %s\n", a->name, a->value ); } return; } s = Cmd_Argv( 1 ); if( strlen( s ) >= MAX_ALIAS_NAME ) { Com_Printf( "Alias name is too long\n" ); return; } hashid = my_strhash( s ); // if the alias already exists, reuse it for( a = cmd_alias ; a ; a = a->next ) { if( hashid == a->id ) { Z_Free( a->value ); break; } } if( ! a ) { a = Z_Malloc( sizeof( cmdalias_t ) ); a->next = cmd_alias; cmd_alias = a; } my_strlcpy( a->name, s, sizeof( a->name ) ); a->id = hashid; // copy the rest of the command line cmd[ 0 ] = '\0'; // start out with a NUL-terminated string c = Cmd_Argc(); for( i = 2; i < c; ++i ) { my_strlcat( cmd, Cmd_Argv( i ), sizeof( cmd ) ); if( i != (c - 1) ) { my_strlcat( cmd, " ", sizeof( cmd ) ); } } my_strlcat( cmd, "\n", sizeof( cmd ) ); a->value = my_CopyString( cmd ); }
void KeyUp(kbutton_t *b) { int k; char *c; unsigned uptime; c = Cmd_Argv(1); if (c[0]) { k = (int)strtol(c, (char **)NULL, 10); } else { /* typed manually at the console, assume for unsticking, so clear all */ b->down[0] = b->down[1] = 0; b->state = 4; /* impulse up */ return; } if (b->down[0] == k) { b->down[0] = 0; } else if (b->down[1] == k) { b->down[1] = 0; } else { return; /* key up without coresponding down (menu pass through) */ } if (b->down[0] || b->down[1]) { return; /* some other key is still holding it down */ } if (!(b->state & 1)) { return; /* still up (this should not happen) */ } /* save timestamp */ c = Cmd_Argv(2); uptime = (int)strtol(c, (char **)NULL, 10); if (uptime) { b->msec += uptime - b->downtime; } else { b->msec += 10; } b->state &= ~1; /* now up */ b->state |= 4; /* impulse up */ }
void IN_Impulse(void) { in_impulse = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); }
/* ==================== CL_PlayDemo_f playdemo <demoname> ==================== */ void CL_PlayDemo_f( void ) { string filename; string demoname; int i; if( Cmd_Argc() != 2 ) { Msg( "Usage: playdemo <demoname>\n" ); return; } if( cls.demoplayback ) { CL_StopPlayback(); } if( cls.demorecording ) { Msg( "Can't playback during demo record.\n"); return; } Q_strncpy( demoname, Cmd_Argv( 1 ), sizeof( demoname ) - 1 ); Q_snprintf( filename, sizeof( filename ), "demos/%s.dem", demoname ); if( !FS_FileExists( filename, true )) { MsgDev( D_ERROR, "couldn't open %s\n", filename ); cls.demonum = -1; // stop demo loop return; } cls.demofile = FS_Open( filename, "rb", true ); Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); Q_strncpy( menu.globals->demoname, demoname, sizeof( menu.globals->demoname )); // read in the m_DemoHeader FS_Read( cls.demofile, &demo.header, sizeof( demoheader_t )); if( demo.header.id != IDEMOHEADER ) { MsgDev( D_ERROR, "%s is not a demo file\n", filename ); FS_Close( cls.demofile ); cls.demofile = NULL; cls.demonum = -1; // stop demo loop return; } if( demo.header.net_protocol != PROTOCOL_VERSION || demo.header.dem_protocol != DEMO_PROTOCOL ) { MsgDev( D_ERROR, "demo protocol outdated\n" "Demo file protocols Network(%i), Demo(%i)\n" "Server protocol is at Network(%i), Demo(%i)\n", demo.header.net_protocol, demo.header.dem_protocol, PROTOCOL_VERSION, DEMO_PROTOCOL ); FS_Close( cls.demofile ); cls.demofile = NULL; cls.demonum = -1; // stop demo loop return; } // now read in the directory structure. FS_Seek( cls.demofile, demo.header.directory_offset, SEEK_SET ); FS_Read( cls.demofile, &demo.directory.numentries, sizeof( int )); if( demo.directory.numentries < 1 || demo.directory.numentries > 1024 ) { MsgDev( D_ERROR, "demo had bogus # of directory entries: %i\n", demo.directory.numentries ); FS_Close( cls.demofile ); cls.demofile = NULL; cls.demonum = -1; // stop demo loop cls.changedemo = false; return; } if( cls.changedemo ) { S_StopAllSounds(); SCR_BeginLoadingPlaque( false ); CL_ClearState (); CL_InitEdicts (); // re-arrange edicts } else { // NOTE: at this point demo is still valid CL_Disconnect(); Host_ShutdownServer(); Con_Close(); UI_SetActiveMenu( false ); } // allocate demo entries demo.directory.entries = Mem_Alloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); for( i = 0; i < demo.directory.numentries; i++ ) { FS_Read( cls.demofile, &demo.directory.entries[i], sizeof( demoentry_t )); } demo.entryIndex = 0; demo.entry = &demo.directory.entries[demo.entryIndex]; FS_Seek( cls.demofile, demo.entry->offset, SEEK_SET ); cls.demoplayback = true; cls.state = ca_connected; cl.background = (cls.demonum != -1) ? true : false; demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, net_qport->integer ); demo.framecount = 0; cls.lastoutgoingcommand = -1; cls.nextcmdtime = host.realtime; // g-cont. is this need? Q_strncpy( cls.servername, demoname, sizeof( cls.servername )); // begin a playback demo }
/* ==================== CL_Record_f record <demoname> Begins recording a demo from the current position ==================== */ void CL_Record_f( void ) { const char *name; string demoname, demopath, demoshot; int n; if( Cmd_Argc() == 1 ) { name = "new"; } else if( Cmd_Argc() == 2 ) { name = Cmd_Argv( 1 ); } else { Msg( "Usage: record <demoname>\n" ); return; } if( cls.demorecording ) { Msg( "Already recording.\n"); return; } if( cls.demoplayback ) { Msg( "Can't record during demo playback.\n"); return; } if( !cls.demoheader || cls.state != ca_active ) { Msg( "You must be in a level to record.\n"); return; } if( !Q_stricmp( name, "new" )) { // scan for a free filename for( n = 0; n < 10000; n++ ) { CL_DemoGetName( n, demoname ); if( !FS_FileExists( va( "demos/%s.dem", demoname ), true )) break; } if( n == 10000 ) { Msg( "^3ERROR: no free slots for demo recording\n" ); return; } } else Q_strncpy( demoname, name, sizeof( demoname )); // open the demo file Q_sprintf( demopath, "demos/%s.dem", demoname ); Q_sprintf( demoshot, "demos/%s.bmp", demoname ); // make sure what old demo is removed if( FS_FileExists( demopath, false )) FS_Delete( demopath ); if( FS_FileExists( demoshot, false )) FS_Delete( demoshot ); // write demoshot for preview Cbuf_AddText( va( "demoshot \"%s\"\n", demoname )); Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); Q_strncpy( menu.globals->demoname, demoname, sizeof( menu.globals->demoname )); CL_WriteDemoHeader( demopath ); }
/* ================== SVC_DirectConnect A connection request that did not come from the master ================== */ void SVC_DirectConnect (void) { char userinfo[MAX_INFO_STRING]; netadr_t adr; int32_t i; client_t *cl, *newcl; client_t temp; edict_t *ent; int32_t edictnum; int32_t version; int32_t qport; int32_t challenge; int32_t previousclients; // rich: connection limit per IP adr = net_from; Com_DPrintf ("SVC_DirectConnect ()\n"); version = atoi(Cmd_Argv(1)); if (version != PROTOCOL_VERSION) { Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION); Com_DPrintf (" rejected connect from version %i\n", version); return; } qport = atoi(Cmd_Argv(2)); challenge = atoi(Cmd_Argv(3)); // r1ch: limit connections from a single IP previousclients = 0; for (i=0,cl=svs.clients; i<(int32_t)maxclients->value; i++,cl++) { if (cl->state == cs_free) continue; if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)) { // r1ch: zombies are less dangerous if (cl->state == cs_zombie) previousclients++; else previousclients += 2; } } if (previousclients >= (int32_t)sv_iplimit->value * 2) { Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nToo many connections from your host.\n"); Com_DPrintf (" too many connections\n"); return; } // end r1ch fix strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1); userinfo[sizeof(userinfo) - 1] = 0; // force the IP key/value pair so the game can filter based on ip Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from)); // attractloop servers are ONLY for local clients if (sv.attractloop) { if (!NET_IsLocalAddress (adr)) { Com_Printf ("Remote connect in attract loop. Ignored.\n"); Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n"); return; } } // see if the challenge is valid if (!NET_IsLocalAddress (adr)) { for (i=0 ; i<MAX_CHALLENGES ; i++) { if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) { if (challenge == svs.challenges[i].challenge) break; // good Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n"); return; } } if (i == MAX_CHALLENGES) { Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n"); return; } } 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<maxclients->value ; i++,cl++) { if (cl->state == cs_free) continue; if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) && ( cl->netchan.qport == qport || adr.port == cl->netchan.remote_address.port ) ) { if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int32_t)sv_reconnect_limit->value * 1000)) { Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr)); return; } // r1ch: !! fix nasty bug where non-disconnected clients (from dropped disconnect // packets) could be overwritten! if (cl->state != cs_zombie) { Com_DPrintf (" client already found\n"); // If we legitly get here, spoofed udp isn't possible (passed challenge) and client addr/port combo // is exactly the same, so we can assume its really a dropped/crashed client. i hope... Com_Printf ("Dropping %s, ghost reconnect\n", cl->name); SV_DropClient (cl); } // end r1ch fix Com_Printf ("%s:reconnect\n", NET_AdrToString (adr)); SV_CleanClient (cl); // r1ch: clean up last client data newcl = cl; goto gotnewcl; } } // find a client slot newcl = NULL; for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++) { if (cl->state == cs_free) { newcl = cl; break; } } if (!newcl) { Netchan_OutOfBandPrint (NS_SERVER, adr, "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; sv_client = newcl; edictnum = (newcl-svs.clients)+1; ent = EDICT_NUM(edictnum); newcl->edict = ent; newcl->challenge = challenge; // save challenge for checksumming // get the game a chance to reject this connection or modify the userinfo if (!(ge->ClientConnect (ent, userinfo))) { if (*Info_ValueForKey (userinfo, "rejmsg")) Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n", Info_ValueForKey (userinfo, "rejmsg")); else Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" ); Com_DPrintf ("Game rejected a connection.\n"); return; } // parse some info from the info strings strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1); SV_UserinfoChanged (newcl); // send the connect packet to the client Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect"); Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport); newcl->state = cs_connected; SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) ); newcl->datagram.allowoverflow = true; newcl->lastmessage = svs.realtime; // don't timeout newcl->lastconnect = svs.realtime; }
void CL_Say_f (void) { char output[8192]; char string[256]; char *msg; int c; output[0] = '\0'; if (cls.state == ca_disconnected || cls.demoplayback) { #ifndef CLIENT_ONLY if (sv.state) SV_ConSay_f(); else #endif Con_TPrintf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } if (!strcmp("sayone", Cmd_Argv(0))) { if (strcmp(Info_ValueForKey(cl.serverinfo, "*distrib"), DISTRIBUTION) || atoi(Info_ValueForKey(cl.serverinfo, "*ver")) < PRE_SAYONE) { Con_Printf ("%s is only available with server support\n", Cmd_Argv(0)); return; } } Q_strncpyz(output, Cmd_Argv(0), sizeof(string)); for (msg = output; *msg; msg++) if (*msg >= 'A' && *msg <= 'Z') *msg = *msg - 'A' + 'a'; msg = Cmd_Args(); if (Cmd_Argc() > 1) { Q_strncatz(output, " \"", sizeof(output)); while(*msg) { c = *msg; if (c == '%') { char *message = NULL; msg++; if (message == NULL) switch(*msg) { case 'n': Q_strncatz(output, name.string, sizeof(output)); msg++; continue; case 'h': Q_strncatz(output, va("%i", cl.stats[0][STAT_HEALTH]), sizeof(output)); msg++; continue; case 'a': Q_strncatz(output, va("%i", cl.stats[0][STAT_ARMOR]), sizeof(output)); msg++; continue; case 'A': Q_strncatz(output, TP_ArmourType(), sizeof(output)); msg++; continue; case 'l': Q_strncatz(output, CL_LocationName(cl.simorg[0]), sizeof(output)); msg++; continue; case 'S': Q_strncatz(output, TP_ClassForTFSkin(), sizeof(output)); msg++; continue; case '%': c = '%'; break; default: c = '%'; msg--; break; } } else if (c == '$') { msg++; switch(*msg) { case '\\': c = 0x0D; break; case ':': c = 0x0A; break; case '[': c = 0x10; break; case ']': c = 0x11; break; case 'G': c = 0x86; break; case 'R': c = 0x87; break; case 'Y': c = 0x88; break; case 'B': c = 0x89; break; case '(': c = 0x80; break; case '=': c = 0x81; break; case ')': c = 0x82; break; case 'a': c = 0x83; break; case '<': c = 0x1d; break; case '-': c = 0x1e; break; case '>': c = 0x1f; break; case ',': c = 0x1c; break; case '.': c = 0x9c; break; case 'b': c = 0x8b; break; case 'c': case 'd': c = 0x8d; break; case '$': c = '$'; break; case '^': c = '^'; break; case 'x': c = INVIS_CHAR1; break; case 'y': c = INVIS_CHAR2; break; case 'z': c = INVIS_CHAR3; break; default: msg--; break; } } Q_strncatz(output, va("%c", c), sizeof(output)); msg++; } Q_strncatz(output, "\"", sizeof(output)); } CL_SendClientCommand("%s", output); }
/* <18d23> ../engine/cvar.c:671 */ void Cmd_CvarList_f(void) { cvar_t *var; int iCvars; int iArgs; const char *partial, *arg1; int ipLen; qboolean bAOnly; qboolean bSOnly; char szTemp[MAX_PATH]; FileHandle_t f; FileHandle_t fp; qboolean bLogging; iCvars = 0; partial = NULL; bAOnly = FALSE; bSOnly = FALSE; f = NULL; fp = NULL; bLogging = FALSE; iArgs = Cmd_Argc(); if (iArgs > 1) { arg1 = Cmd_Argv(1); if (!Q_stricmp(arg1, "?")) { Con_Printf("CvarList : List all cvars\nCvarList [Partial] : List cvars starting with 'Partial'\nCvarList log [Partial] : Logs cvars to file \"cvarlist.txt\" in the gamedir.\n"); return; } if (!Q_stricmp(arg1, "log")) { // Open log int i; for (i = 0; i < 100; i++) { Q_snprintf(szTemp, ARRAYSIZE(szTemp) - 1, "cvarlist%02d.txt", i); szTemp[ARRAYSIZE(szTemp) - 1] = 0; fp = FS_Open(szTemp, "r"); if (!fp) { break; } FS_Close(fp); } if (i >= 100) { Con_Printf("Can't cvarlist! Too many existing cvarlist output files in the gamedir!\n"); return; } f = FS_Open(szTemp, "wt"); if (!f) { Con_Printf("Couldn't open \"%s\" for writing!\n", szTemp); return; } bLogging = TRUE; // Get next argument into partial, if present if (iArgs > 2) { partial = Cmd_Argv(2); ipLen = Q_strlen(partial); } } else if (!Q_stricmp(arg1, "-a")) { bAOnly = TRUE; } else if (!Q_stricmp(arg1, "-s")) { bSOnly = TRUE; } else { partial = arg1; ipLen = Q_strlen(partial); } } // Print cvars Con_Printf("CVar List\n--------------\n"); for (var = cvar_vars; var; var = var->next) { if (bAOnly && !(var->flags & FCVAR_ARCHIVE)) { continue; } if (bSOnly && !(var->flags & FCVAR_SERVER)) { continue; } if (partial && Q_strnicmp(var->name, partial, ipLen)) { continue; } Cmd_CvarListPrintCvar(var, f); iCvars++; } if (partial && *partial) { Con_Printf("--------------\n%3i CVars for [%s]\nCvarList ? for syntax\n", iCvars, partial); } else { Con_Printf("--------------\n%3i Total CVars\nCvarList ? for syntax\n", iCvars); } // Close log if (bLogging) { FS_Close(f); Con_Printf("cvarlist logged to %s\n", szTemp); } }
/* ================ SV_MapRestart_f Completely restarts a level, but doesn't send a new gamestate to the clients. This allows fair starts with variable load times. ================ */ static void SV_MapRestart_f( void ) { int i; client_t *client; char *denied; qboolean isBot; int delay; // make sure we aren't restarting twice in the same frame if ( com_frameTime == sv.serverId ) { return; } // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( sv.restartTime ) { return; } if (Cmd_Argc() > 1 ) { delay = atoi( Cmd_Argv(1) ); } else { delay = 5; } if( delay && !Cvar_VariableValue("g_doWarmup") ) { sv.restartTime = sv.time + delay * 1000; SV_SetConfigstring( CS_WARMUP, va("%i", sv.restartTime) ); return; } // check for changes in variables that can't just be restarted // check for maxclients change if ( sv_maxclients->modified || sv_gametype->modified ) { char mapname[MAX_QPATH]; Com_Printf( "variable change -- restarting.\n" ); // restart the map the slow way Q_strncpyz( mapname, Cvar_VariableString( "mapname" ), sizeof( mapname ) ); SV_SpawnServer( mapname, qfalse ); return; } // toggle the server bit so clients can detect that a // map_restart has happened svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // generate a new serverid // TTimo - don't update restartedserverId there, otherwise we won't deal correctly with multiple map_restart sv.serverId = com_frameTime; Cvar_Set( "sv_serverid", va("%i", sv.serverId ) ); // if a map_restart occurs while a client is changing maps, we need // to give them the correct time so that when they finish loading // they don't violate the backwards time check in cl_cgame.c for (i=0 ; i<sv_maxclients->integer ; i++) { if (svs.clients[i].state == CS_PRIMED) { svs.clients[i].oldServerTime = sv.restartTime; } } // reset all the vm data in place without changing memory allocation // note that we do NOT set sv.state = SS_LOADING, so configstrings that // had been changed from their default values will generate broadcast updates sv.state = SS_LOADING; sv.restarting = qtrue; SV_RestartGameProgs(); // run a few frames to allow everything to settle for (i = 0; i < 3; i++) { VM_Call (gvm, GAME_RUN_FRAME, sv.time); sv.time += 100; svs.time += 100; } sv.state = SS_GAME; sv.restarting = qfalse; // connect and begin all the clients for (i=0 ; i<sv_maxclients->integer ; i++) { client = &svs.clients[i]; // send the new gamestate to all connected clients if ( client->state < CS_CONNECTED) { continue; } if ( client->netchan.remoteAddress.type == NA_BOT ) { isBot = qtrue; } else { isBot = qfalse; } // add the map_restart command SV_AddServerCommand( client, "map_restart\n" ); // connect the client again, without the firstTime flag denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) ); if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( client, denied ); Com_Printf( "SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i ); continue; } if(client->state == CS_ACTIVE) SV_ClientEnterWorld(client, &client->lastUsercmd); else { // If we don't reset client->lastUsercmd and are restarting during map load, // the client will hang because we'll use the last Usercmd from the previous map, // which is wrong obviously. SV_ClientEnterWorld(client, NULL); } } // run another frame to allow things to look at all the players VM_Call (gvm, GAME_RUN_FRAME, sv.time); sv.time += 100; svs.time += 100; }
/* ============ Cvar_List_f ============ */ void Cvar_List_f( void ) { cvar_t *var; int i; char *match; if ( Cmd_Argc() > 1 ) { match = Cmd_Argv( 1 ); } else { match = NULL; } i = 0; for (var = cvar_vars ; var ; var = var->next, i++) { // Dont show internal cvars if ( var->flags & CVAR_INTERNAL ) { continue; } if (match && !Com_Filter(match, var->name, qfalse)) continue; if (var->flags & CVAR_SERVERINFO) { Com_Printf("S"); } else { Com_Printf(" "); } if (var->flags & CVAR_USERINFO) { Com_Printf("U"); } else { Com_Printf(" "); } if (var->flags & CVAR_ROM) { Com_Printf("R"); } else { Com_Printf(" "); } if (var->flags & CVAR_INIT) { Com_Printf("I"); } else { Com_Printf(" "); } if (var->flags & CVAR_ARCHIVE) { Com_Printf("A"); } else { Com_Printf(" "); } if (var->flags & CVAR_LATCH) { Com_Printf("L"); } else { Com_Printf(" "); } if (var->flags & CVAR_CHEAT) { Com_Printf("C"); } else { Com_Printf(" "); } Com_Printf (" %s \"%s\"\n", var->name, var->string); } Com_Printf ("\n%i total cvars\n", i); Com_Printf ("%i cvar indexes\n", cvar_numIndexes); }
static void SV_AddBanToList(qboolean isexception) { char *banstring; char addy2[NET_ADDRSTRMAXLEN]; netadr_t ip; int index, argc, mask; serverBan_t *curban; argc = Cmd_Argc(); if(argc < 2 || argc > 3) { Com_Printf ("Usage: %s (ip[/subnet] | clientnum [subnet])\n", Cmd_Argv(0)); return; } if(serverBansCount > ARRAY_LEN(serverBans)) { Com_Printf ("Error: Maximum number of bans/exceptions exceeded.\n"); return; } banstring = Cmd_Argv(1); if(strchr(banstring, '.') || strchr(banstring, ':')) { // This is an ip address, not a client num. if(SV_ParseCIDRNotation(&ip, &mask, banstring)) { Com_Printf("Error: Invalid address %s\n", banstring); return; } } else { client_t *cl; // client num. if(!com_sv_running->integer) { Com_Printf("Server is not running.\n"); return; } cl = SV_GetPlayerByNum(); if(!cl) { Com_Printf("Error: Playernum %s does not exist.\n", Cmd_Argv(1)); return; } ip = cl->netchan.remoteAddress; if(argc == 3) { mask = atoi(Cmd_Argv(2)); if(ip.type == NA_IP) { if(mask < 1 || mask > 32) mask = 32; } else { if(mask < 1 || mask > 128) mask = 128; } } else mask = (ip.type == NA_IP6) ? 128 : 32; } if(ip.type != NA_IP && ip.type != NA_IP6) { Com_Printf("Error: Can ban players connected via the internet only.\n"); return; } // first check whether a conflicting ban exists that would supersede the new one. for(index = 0; index < serverBansCount; index++) { curban = &serverBans[index]; if(curban->subnet <= mask) { if((curban->isexception || !isexception) && NET_CompareBaseAdrMask(curban->ip, ip, curban->subnet)) { Q_strncpyz(addy2, NET_AdrToString(ip), sizeof(addy2)); Com_Printf("Error: %s %s/%d supersedes %s %s/%d\n", curban->isexception ? "Exception" : "Ban", NET_AdrToString(curban->ip), curban->subnet, isexception ? "exception" : "ban", addy2, mask); return; } } if(curban->subnet >= mask) { if(!curban->isexception && isexception && NET_CompareBaseAdrMask(curban->ip, ip, mask)) { Q_strncpyz(addy2, NET_AdrToString(curban->ip), sizeof(addy2)); Com_Printf("Error: %s %s/%d supersedes already existing %s %s/%d\n", isexception ? "Exception" : "Ban", NET_AdrToString(ip), mask, curban->isexception ? "exception" : "ban", addy2, curban->subnet); return; } } } // now delete bans that are superseded by the new one index = 0; while(index < serverBansCount) { curban = &serverBans[index]; if(curban->subnet > mask && (!curban->isexception || isexception) && NET_CompareBaseAdrMask(curban->ip, ip, mask)) SV_DelBanEntryFromList(index); else index++; } serverBans[serverBansCount].ip = ip; serverBans[serverBansCount].subnet = mask; serverBans[serverBansCount].isexception = isexception; serverBansCount++; SV_WriteBans(); Com_Printf("Added %s: %s/%d\n", isexception ? "ban exception" : "ban", NET_AdrToString(ip), mask); }
/* * SVC_InfoResponse * * Responds with short info for broadcast scans * The second parameter should be the current protocol version number. */ static void SVC_InfoResponse( const socket_t *socket, const netadr_t *address ) { int i, count; char *string; qboolean allow_empty = qfalse, allow_full = qfalse; if( sv_showInfoQueries->integer ) Com_Printf( "Info Packet %s\n", NET_AddressToString( address ) ); // KoFFiE: When not public and coming from a LAN address // assume broadcast and respond anyway, otherwise ignore if( ( ( !sv_public->integer ) && ( !NET_IsLANAddress( address ) ) ) || ( sv_maxclients->integer == 1 ) ) { return; } // ignore when in invalid server state if( sv.state < ss_loading || sv.state > ss_game ) return; // don't reply when we are locked for mm // if( SV_MM_IsLocked() ) // return; // different protocol version if( atoi( Cmd_Argv( 1 ) ) != APP_PROTOCOL_VERSION ) return; // check for full/empty filtered states for( i = 0; i < Cmd_Argc(); i++ ) { if( !Q_stricmp( Cmd_Argv( i ), "full" ) ) allow_full = qtrue; if( !Q_stricmp( Cmd_Argv( i ), "empty" ) ) allow_empty = qtrue; } count = 0; for( i = 0; i < sv_maxclients->integer; i++ ) { if( svs.clients[i].state >= CS_CONNECTED ) { count++; } } if( ( count == sv_maxclients->integer ) && !allow_full ) { return; } if( ( count == 0 ) && !allow_empty ) { return; } string = SV_ShortInfoString(); if( string ) Netchan_OutOfBandPrint( socket, address, "info\n%s", string ); }
static void CD_f (void) { char *command; int ret; int n; if (Cmd_Argc() < 2) return; command = Cmd_Argv (1); if (Q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (Q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (Q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 100; n++) remap[n] = n; CDAudio_GetAudioDiskInfo(); return; } if (Q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 100; n++) if (remap[n] != n) Com_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n+1)); return; } if (Q_strcasecmp(command, "close") == 0) { CDAudio_CloseDoor(); return; } if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) { Com_Printf("No CD in player.\n"); return; } } if (Q_strcasecmp(command, "play") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); return; } if (Q_strcasecmp(command, "loop") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); return; } if (Q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (Q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (Q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (Q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cdValid = false; return; } if (Q_strcasecmp(command, "info") == 0) { Com_Printf("%u tracks\n", maxTrack); if (playing) Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); else if (wasPlaying) Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); Com_Printf("Volume is %f\n", cdvolume); return; } }
/* * SVC_DirectConnect * A connection request that did not come from the master */ static void SVC_DirectConnect( const socket_t *socket, const netadr_t *address ) { #ifdef TCP_ALLOW_CONNECT int incoming = 0; #endif char userinfo[MAX_INFO_STRING]; client_t *cl, *newcl; int i, version, game_port, challenge; int previousclients; int session_id; char *session_id_str; unsigned int ticket_id; qboolean tv_client; Com_DPrintf( "SVC_DirectConnect (%s)\n", Cmd_Args() ); version = atoi( Cmd_Argv( 1 ) ); if( version != APP_PROTOCOL_VERSION ) { if( version <= 6 ) { // before reject packet was added Netchan_OutOfBandPrint( socket, address, "print\nServer is version %4.2f. Protocol %3i\n", APP_VERSION, APP_PROTOCOL_VERSION ); } else { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer and client don't have the same version\n", DROP_TYPE_GENERAL, 0 ); } Com_DPrintf( " rejected connect from protocol %i\n", version ); return; } game_port = atoi( Cmd_Argv( 2 ) ); challenge = atoi( Cmd_Argv( 3 ) ); tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? qtrue : qfalse ); if( !Info_Validate( Cmd_Argv( 4 ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nInvalid userinfo string\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: invalid userinfo string\n", NET_AddressToString( address ) ); return; } Q_strncpyz( userinfo, Cmd_Argv( 4 ), sizeof( userinfo ) ); // force the IP key/value pair so the game can filter based on ip if( !Info_SetValueForKey( userinfo, "socket", NET_SocketTypeToString( socket->type ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (socket)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: couldn't set userinfo (socket)\n", NET_AddressToString( address ) ); return; } if( !Info_SetValueForKey( userinfo, "ip", NET_AddressToString( address ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (ip)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) ); return; } if( Cmd_Argc() >= 7 ) { // we have extended information, ticket-id and session-id Com_Printf("Extended information %s\n", Cmd_Argv(6) ); ticket_id = (unsigned int)atoi( Cmd_Argv(6) ); session_id_str = Info_ValueForKey( userinfo, "cl_mm_session" ); if( session_id_str != NULL ) session_id = atoi( session_id_str ); else session_id = 0; } else { ticket_id = 0; session_id = 0; } #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { // find the connection for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) continue; if( NET_CompareAddress( &svs.incoming[i].address, address ) && socket == &svs.incoming[i].socket ) break; } if( i == MAX_INCOMING_CONNECTIONS ) { Com_Error( ERR_FATAL, "Incoming connection not found.\n" ); return; } incoming = i; } #endif // see if the challenge is valid for( i = 0; i < MAX_CHALLENGES; i++ ) { if( NET_CompareBaseAddress( address, &svs.challenges[i].adr ) ) { if( challenge == svs.challenges[i].challenge ) { svs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge svs.challenges[i].time = 0; NET_InitAddress( &svs.challenges[i].adr, NA_NOTRANSMIT ); break; // good } Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nBad challenge\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } } if( i == MAX_CHALLENGES ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nNo challenge for address\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } //r1: limit connections from a single IP if( sv_iplimit->integer ) { previousclients = 0; for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) ) { //r1: zombies are less dangerous if( cl->state == CS_ZOMBIE ) previousclients++; else previousclients += 2; } } if( previousclients >= sv_iplimit->integer * 2 ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nToo many connections from your host\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "%s:connect rejected : too many connections\n", NET_AddressToString( address ) ); return; } } newcl = NULL; // if there is already a slot for this ip, reuse it for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareAddress( address, &cl->netchan.remoteAddress ) || ( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) && cl->netchan.game_port == game_port ) ) { if( !NET_IsLocalAddress( address ) && ( svs.realtime - cl->lastconnect ) < (unsigned)( sv_reconnectlimit->integer * 1000 ) ) { Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AddressToString( address ) ); return; } Com_Printf( "%s:reconnect\n", NET_AddressToString( address ) ); newcl = cl; break; } } // find a client slot if( !newcl ) { for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) { newcl = cl; break; } // overwrite fakeclient if no free spots found if( cl->state && cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) newcl = cl; } if( !newcl ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "Server is full. Rejected a connection.\n" ); return; } if( newcl->state && newcl->edict && ( newcl->edict->r.svflags & SVF_FAKECLIENT ) ) SV_DropClient( newcl, DROP_TYPE_GENERAL, "Need room for a real player" ); } // get the game a chance to reject this connection or modify the userinfo if( !SV_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, qfalse, tv_client, ticket_id, session_id ) ) { char *rejtype, *rejflag, *rejtypeflag, *rejmsg; rejtype = Info_ValueForKey( userinfo, "rejtype" ); if( !rejtype ) rejtype = "0"; rejflag = Info_ValueForKey( userinfo, "rejflag" ); if( !rejflag ) rejflag = "0"; // hax because Info_ValueForKey can only be called twice in a row rejtypeflag = va( "%s\n%s", rejtype, rejflag ); rejmsg = Info_ValueForKey( userinfo, "rejmsg" ); if( !rejmsg ) rejmsg = "Game module rejected connection"; Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg ); Com_DPrintf( "Game rejected a connection.\n" ); return; } // send the connect packet to the client Netchan_OutOfBandPrint( socket, address, "client_connect" ); // free the incoming entry #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { svs.incoming[incoming].active = qfalse; svs.incoming[incoming].socket.open = qfalse; } #endif }
/* ================== SV_Map_f Restart the server on a different map ================== */ static void SV_Map_f(void) { char *cmd; char *map; char smapname[MAX_QPATH]; char mapname[MAX_QPATH]; qboolean killBots, cheat, buildScript; char expanded[MAX_QPATH]; int savegameTime = -1; char *cl_profileStr = Cvar_VariableString("cl_profile"); map = Cmd_Argv(1); if (!map) { return; } if (!com_gameInfo.spEnabled) { if (!Q_stricmp(Cmd_Argv(0), "spdevmap") || !Q_stricmp(Cmd_Argv(0), "spmap")) { Com_Printf("Single Player is not enabled.\n"); return; } } buildScript = Cvar_VariableIntegerValue("com_buildScript"); if (SV_GameIsSinglePlayer()) { if (!buildScript && sv_reloading->integer && sv_reloading->integer != RELOAD_NEXTMAP) // game is in 'reload' mode, don't allow starting new maps yet. { return; } // Trap a savegame load if (strstr(map, ".sav")) { // open the savegame, read the mapname, and copy it to the map string char savemap[MAX_QPATH]; char savedir[MAX_QPATH]; byte *buffer; int size, csize; if (com_gameInfo.usesProfiles && cl_profileStr[0]) { Com_sprintf(savedir, sizeof(savedir), "profiles/%s/save/", cl_profileStr); } else { Q_strncpyz(savedir, "save/", sizeof(savedir)); } if (!(strstr(map, savedir) == map)) { Com_sprintf(savemap, sizeof(savemap), "%s%s", savedir, map); } else { strcpy(savemap, map); } size = FS_ReadFile(savemap, NULL); if (size < 0) { Com_Printf("Can't find savegame %s\n", savemap); return; } //buffer = Hunk_AllocateTempMemory(size); FS_ReadFile(savemap, (void **)&buffer); if (Q_stricmp(savemap, va("%scurrent.sav", savedir)) != 0) { // copy it to the current savegame file FS_WriteFile(va("%scurrent.sav", savedir), buffer, size); // make sure it is the correct size csize = FS_ReadFile(va("%scurrent.sav", savedir), NULL); if (csize != size) { Hunk_FreeTempMemory(buffer); FS_Delete(va("%scurrent.sav", savedir)); // TTimo #ifdef __linux__ Com_Error(ERR_DROP, "Unable to save game.\n\nPlease check that you have at least 5mb free of disk space in your home directory."); #else Com_Error(ERR_DROP, "Insufficient free disk space.\n\nPlease free at least 5mb of free space on game drive."); #endif return; } } // set the cvar, so the game knows it needs to load the savegame once the clients have connected Cvar_Set("savegame_loading", "1"); // set the filename Cvar_Set("savegame_filename", savemap); // the mapname is at the very start of the savegame file Com_sprintf(savemap, sizeof(savemap), ( char * )(buffer + sizeof(int))); // skip the version Q_strncpyz(smapname, savemap, sizeof(smapname)); map = smapname; savegameTime = *( int * )(buffer + sizeof(int) + MAX_QPATH); if (savegameTime >= 0) { svs.time = savegameTime; } Hunk_FreeTempMemory(buffer); } else { Cvar_Set("savegame_loading", "0"); // make sure it's turned off // set the filename Cvar_Set("savegame_filename", ""); } } else { Cvar_Set("savegame_loading", "0"); // make sure it's turned off // set the filename Cvar_Set("savegame_filename", ""); } // make sure the level exists before trying to change, so that // a typo at the server console won't end the game Com_sprintf(expanded, sizeof(expanded), "maps/%s.bsp", map); if (FS_ReadFile(expanded, NULL) == -1) { Com_Printf("Can't find map %s\n", expanded); return; } Cvar_Set("gamestate", va("%i", GS_INITIALIZE)); // NERVE - SMF - reset gamestate on map/devmap Cvar_Set("g_currentRound", "0"); // NERVE - SMF - reset the current round Cvar_Set("g_nextTimeLimit", "0"); // NERVE - SMF - reset the next time limit // START Mad Doctor I changes, 8/14/2002. Need a way to force load a single player map as single player if (!Q_stricmp(Cmd_Argv(0), "spdevmap") || !Q_stricmp(Cmd_Argv(0), "spmap")) { // This is explicitly asking for a single player load of this map Cvar_Set("g_gametype", va("%i", com_gameInfo.defaultSPGameType)); // force latched values to get set Cvar_Get("g_gametype", va("%i", com_gameInfo.defaultSPGameType), CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH); // enable bot support for AI Cvar_Set("bot_enable", "1"); } // Rafael gameskill // Cvar_Get ("g_gameskill", "3", CVAR_SERVERINFO | CVAR_LATCH); // done cmd = Cmd_Argv(0); if (!Q_stricmp(cmd, "devmap")) { cheat = qtrue; killBots = qtrue; } else if (!Q_stricmp(Cmd_Argv(0), "spdevmap")) { cheat = qtrue; killBots = qtrue; } else { cheat = qfalse; killBots = qfalse; } // save the map name here cause on a map restart we reload the q3config.cfg // and thus nuke the arguments of the map command Q_strncpyz(mapname, map, sizeof(mapname)); // start up the map SV_SpawnServer(mapname, killBots); // set the cheat value // if the level was started with "map <levelname>", then // cheats will not be allowed. If started with "devmap <levelname>" // then cheats will be allowed if (cheat) { Cvar_Set("sv_cheats", "1"); } else { Cvar_Set("sv_cheats", "0"); } }
// Checks if we have a keycode at the end, e.g. +fire 8 5 3 120 // if it's >= 32, treat is as keycodes, otherwise an impulse static qbool IN_IsLastArgKeyCode(void) { return atoi(Cmd_Argv(Cmd_Argc() - 1)) >= 32; }
/* ================= SV_LoadGame_f ================= */ void SV_LoadGame_f(void) { char filename[MAX_QPATH], mapname[MAX_QPATH], savedir[MAX_QPATH]; byte *buffer; int size; char *cl_profileStr = Cvar_VariableString("cl_profile"); // dont allow command if another loadgame is pending if (Cvar_VariableIntegerValue("savegame_loading")) { return; } if (sv_reloading->integer) { // (SA) disabling // if(sv_reloading->integer && sv_reloading->integer != RELOAD_FAILED ) // game is in 'reload' mode, don't allow starting new maps yet. return; } Q_strncpyz(filename, Cmd_Argv(1), sizeof(filename)); if (!filename[0]) { Com_Printf("You must specify a savegame to load\n"); return; } if (com_gameInfo.usesProfiles && cl_profileStr[0]) { Com_sprintf(savedir, sizeof(savedir), "profiles/%s/save/", cl_profileStr); } else { Q_strncpyz(savedir, "save/", sizeof(savedir)); } /*if ( Q_strncmp( filename, "save/", 5 ) && Q_strncmp( filename, "save\\", 5 ) ) { Q_strncpyz( filename, va("save/%s", filename), sizeof( filename ) ); }*/ // go through a va to avoid vsnprintf call with same source and target Q_strncpyz(filename, va("%s%s", savedir, filename), sizeof(filename)); // enforce .sav extension if (!strstr(filename, ".") || Q_strncmp(strstr(filename, ".") + 1, "sav", 3)) { Q_strcat(filename, sizeof(filename), ".sav"); } // use '/' instead of '\\' for directories while (strstr(filename, "\\")) { *(char *)strstr(filename, "\\") = '/'; } size = FS_ReadFile(filename, NULL); if (size < 0) { Com_Printf("Can't find savegame %s\n", filename); return; } //buffer = Hunk_AllocateTempMemory(size); FS_ReadFile(filename, (void **)&buffer); // read the mapname, if it is the same as the current map, then do a fast load Com_sprintf(mapname, sizeof(mapname), (const char *)(buffer + sizeof(int))); if (com_sv_running->integer && (com_frameTime != sv.serverId)) { // check mapname if (!Q_stricmp(mapname, sv_mapname->string)) // same { if (Q_stricmp(filename, va("%scurrent.sav", savedir)) != 0) { // copy it to the current savegame file FS_WriteFile(va("%scurrent.sav", savedir), buffer, size); } Hunk_FreeTempMemory(buffer); Cvar_Set("savegame_loading", "2"); // 2 means it's a restart, so stop rendering until we are loaded // set the filename Cvar_Set("savegame_filename", filename); // quick-restart the server SV_MapRestart_f(); // savegame will be loaded after restart return; } } Hunk_FreeTempMemory(buffer); // otherwise, do a slow load if (Cvar_VariableIntegerValue("sv_cheats")) { Cbuf_ExecuteText(EXEC_APPEND, va("spdevmap %s", filename)); } else // no cheats { Cbuf_ExecuteText(EXEC_APPEND, va("spmap %s", filename)); } }
// This is the same command as impulse but cl_weaponpreselect can be used in here, while for impulses cannot be used. void IN_Weapon(void) { int c, best, mode; if ((c = Cmd_Argc() - 1) < 1) { Com_Printf("Usage: %s w1 [w2 [w3..]]\nWill pre-select best available weapon from given sequence.\n", Cmd_Argv(0)); return; } // read user input IN_RememberWpOrder(); best = IN_BestWeapon(); mode = (int) cl_weaponpreselect.value; // cl_weaponpreselect behaviour: // 0: select best weapon right now // 1: always only pre-select; switch to it on +attack // 2: user is holding +attack -> select, otherwise just pre-select // 3: same like 1, but only in deathmatch 1 // 4: same like 2, but only in deathmatch 1 if (mode == 3) { mode = (cl.deathmatch == 1) ? 1 : 0; } else if (mode == 4) { mode = (cl.deathmatch == 1) ? 2 : 0; } switch (mode) { case 2: if ((in_attack.state & 3) && best) // user is holding +attack and there is some weapon available in_impulse = best; break; case 1: break; // don't select weapon immediately default: case 0: // no pre-selection if (best) in_impulse = best; break; } }
void Master_SetMaster_f() { int AreAdding; const char * arg_1, * arg_2; int port; netadr_t netaddress; master_server_t * ptr, * ptr2; if(Cmd_Argc() < 2 || Cmd_Argc() > 4) { Con_Printf("Setmaster <add | remove | enable | disable> <IP:port>\n"); Con_Printf(" Current master list:\n"); for(ptr = masterlist; ptr != NULL; ptr = ptr->next) { Con_Printf(" %s\n", NET_AdrToString(ptr->address)); } Con_Printf(" End if list\n"); return; } arg_1 = Cmd_Argv(1); if(arg_1[0] == '\0') { return; } if(Q_strcasecmp(arg_1, "disable") == 0) { gfNoMasterServer = 1; return; } if(Q_strcasecmp(arg_1, "enable") == 0) { gfNoMasterServer = 0; return; } if(Q_strcasecmp(arg_1, "add") == 0) { AreAdding = 1; } else if(Q_strcasecmp(arg_1, "remove") == 0) { AreAdding = 0; } else { Con_Printf("Setmaster: Unknown command \"%s\".\n", arg_1); return; } arg_2 = Cmd_Argv(2); if(Cmd_Argc() == 4) { port = Q_atoi(Cmd_Argv(3)); if(port < 1 || port > 65535) { Con_Printf("Setmaster: Invalid port. Ports can't be larger than 65535.\n"); return; } } else { port = 27010; } if(NET_StringToAdr(arg_2, &netaddress) == 0) { Con_Printf("Setmaster: Passed address could not be processed by StringToAdr.\n"); return; } netaddress.port = port; if(AreAdding) { Master_Init(); Master_AddServer(&netaddress); gfNoMasterServer = 0; Con_Printf("Master server %s:%u added.\n", arg_2, port); } else { //case 1: first node. if(NET_CompareAdr(masterlist->address, netaddress) != 0) { ptr = masterlist; masterlist = masterlist->next; Q_Free(ptr); return; } //case 2: some node afterwards for(ptr = masterlist; ptr->next != NULL; ptr = ptr->next) { if(NET_CompareAdr(ptr->next->address, netaddress) != 0) { ptr2 = ptr->next; ptr->next = ptr->next->next; Q_Free(ptr2); return; } } //case 3: not here Con_Printf("Master %s:%u couldn't be removed. Couldn't find it.\n", arg_2, port); } }
/* ================== SV_PP_Cmd_Argc_to_idnum @Barbatos: wow this one looks horrible, FIXME! ================== */ int SV_Argc_to_idnum( int arg_num ) { client_t *cl; int idnum; int i, k, f, g; int len, nlen, slen; int count; char *search; char *name; // make sure server is running if ( !com_sv_running->integer ) { return -1; } if ( Cmd_Argc() < 1 ) { Com_Printf( "No player specified.\n" ); return -1; } search = Cmd_Argv( arg_num ); if(strlen( search ) < 3 ) { for (i = 0; search[i]; i++) { if (search[i] < '0' || search[i] > '9') { Com_Printf( "Bad slot number: \"%s\".\n", search); return -1; } } idnum = atoi( search ); if ( idnum < 0 || idnum >= sv_maxclients->integer ) { Com_Printf( "Bad client slot: %i.\n", idnum ); return -1; } cl = &svs.clients[idnum]; if ( !cl->state ) { Com_Printf( "Client %i is not active.\n", idnum ); return -1; } return idnum; } else { f = 0; g = 0; count = 0; idnum = -1; slen = strlen(search); for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state >= CS_CONNECTED ) { name = cl->name; nlen = strlen(name); len = nlen - slen; if(len>=0) { for (k=0; k<=len; k++, name++) { if( toupper(name[0])==toupper(search[0]) ) { for (g=1,f=1; search[f] && name[g]; g++) { if( Q_IsColorString( &name[g] ) ) { g++; } else { if( toupper(name[g])!=toupper(search[f]) ) break; f++; } } if (f==slen) { count++; idnum = i; break; } } } } } } if( count == 1 ) { return idnum; } if( count > 0 ) { Com_Printf( "Too many players found for \"%s\".\n", search ); return -1; } if( count == 0 ) { Com_Printf( "No player found for \"%s\".\n", search ); return -1; } } return -1; }
/** * Take a screenshot of the frame buffer * @param[in] x * @param[in] y * @param[in] width * @param[in] height * @param[in] filename Force to use a filename. Else NULL to autogen a filename * @param[in] ext Force to use an image format (tga/png/jpg). Else NULL to use value of r_screenshot_format */ void R_ScreenShot (int x, int y, int width, int height, const char *filename, const char *ext) { char checkName[MAX_OSPATH]; int type, shotNum, quality = 100; byte *buffer; qFILE f; int rowPack; glGetIntegerv(GL_PACK_ALIGNMENT, &rowPack); glPixelStorei(GL_PACK_ALIGNMENT, 1); /* Find out what format to save in */ if (ext == NULL) ext = r_screenshot_format->string; if (!Q_strcasecmp(ext, "png")) type = SSHOTTYPE_PNG; else if (!Q_strcasecmp(ext, "jpg")) type = SSHOTTYPE_JPG; else type = SSHOTTYPE_TGA_COMP; /* Set necessary values */ switch (type) { case SSHOTTYPE_TGA_COMP: Com_Printf("Taking TGA screenshot...\n"); ext = "tga"; break; case SSHOTTYPE_PNG: Com_Printf("Taking PNG screenshot...\n"); ext = "png"; break; case SSHOTTYPE_JPG: if (Cmd_Argc() == 3) quality = atoi(Cmd_Argv(2)); else quality = r_screenshot_jpeg_quality->integer; if (quality > 100 || quality <= 0) quality = 100; Com_Printf("Taking JPG screenshot (at %i%% quality)...\n", quality); ext = "jpg"; break; } /* Find a file name to save it to */ if (filename) { Com_sprintf(checkName, sizeof(checkName), "scrnshot/%s.%s", filename, ext); } else { for (shotNum = 0; shotNum < 1000; shotNum++) { Com_sprintf(checkName, sizeof(checkName), "scrnshot/ufo%i%i.%s", shotNum / 10, shotNum % 10, ext); if (FS_CheckFile("%s", checkName) == -1) break; } if (shotNum == 1000) { Com_Printf("R_ScreenShot_f: screenshot limit (of 1000) exceeded!\n"); return; } } /* Open it */ FS_OpenFile(checkName, &f, FILE_WRITE); if (!f.f) { Com_Printf("R_ScreenShot_f: Couldn't create file: %s\n", checkName); return; } /* Allocate room for a copy of the framebuffer */ buffer = (byte *)Mem_PoolAlloc(width * height * 3, vid_imagePool, 0); if (!buffer) { Com_Printf("R_ScreenShot_f: Could not allocate %i bytes for screenshot!\n", width * height * 3); FS_CloseFile(&f); return; } /* Read the framebuffer into our storage */ glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer); R_CheckError(); /* Write */ switch (type) { case SSHOTTYPE_TGA_COMP: R_WriteCompressedTGA(&f, buffer, width, height); break; case SSHOTTYPE_PNG: R_WritePNG(&f, buffer, width, height); break; case SSHOTTYPE_JPG: R_WriteJPG(&f, buffer, width, height, quality); break; } /* Finish */ FS_CloseFile(&f); Mem_Free(buffer); Com_Printf("Wrote %s to %s\n", checkName, FS_Gamedir()); glPixelStorei(GL_PACK_ALIGNMENT, rowPack); }
/* ================== SV_Map_f Restart the server on a different map ================== */ static void SV_Map_f( void ) { char *cmd; char *map; qboolean killBots, cheat; char expanded[MAX_QPATH]; char mapname[MAX_QPATH]; map = Cmd_Argv(1); if ( !map ) { return; } // make sure the level exists before trying to change, so that // a typo at the server console won't end the game Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", map); if ( FS_ReadFile (expanded, NULL) == -1 ) { Com_Printf ("Can't find map %s\n", expanded); return; } // force latched values to get set Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH ); cmd = Cmd_Argv(0); if( Q_stricmpn( cmd, "sp", 2 ) == 0 ) { Cvar_SetValue( "g_gametype", GT_SINGLE_PLAYER ); Cvar_SetValue( "g_doWarmup", 0 ); // may not set sv_maxclients directly, always set latched Cvar_SetLatched( "sv_maxclients", "8" ); cmd += 2; if (!Q_stricmp( cmd, "devmap" ) ) { cheat = qtrue; } else { cheat = qfalse; } killBots = qtrue; } else { if ( !Q_stricmp( cmd, "devmap" ) ) { cheat = qtrue; killBots = qtrue; } else { cheat = qfalse; killBots = qfalse; } if( sv_gametype->integer == GT_SINGLE_PLAYER ) { Cvar_SetValue( "g_gametype", GT_FFA ); } } // save the map name here cause on a map restart we reload the q3config.cfg // and thus nuke the arguments of the map command Q_strncpyz(mapname, map, sizeof(mapname)); // start up the map SV_SpawnServer( mapname, killBots ); // set the cheat value // if the level was started with "map <levelname>", then // cheats will not be allowed. If started with "devmap <levelname>" // then cheats will be allowed if ( cheat ) { Cvar_Set( "sv_cheats", "1" ); } else { Cvar_Set( "sv_cheats", "0" ); } }
void TP_MsgTrigger_f (void) { int c; char *name; msg_trigger_t *trig; c = Cmd_Argc(); if (c > 5) { Com_Printf ("msg_trigger <trigger name> \"string\" [-l <level>]\n"); return; } if (c == 1) { if (!msg_triggers) Com_Printf ("no triggers defined\n"); else for (trig=msg_triggers; trig; trig=trig->next) Com_Printf ("%s : \"%s\"\n", trig->name, trig->string); return; } name = Cmd_Argv(1); if (strlen(name) > 31) { Com_Printf ("trigger name too long\n"); return; } if (c == 2) { trig = TP_FindTrigger (name); if (trig) Com_Printf ("%s: \"%s\"\n", trig->name, trig->string); else Com_Printf ("trigger \"%s\" not found\n", name); return; } if (c >= 3) { if (strlen(Cmd_Argv(2)) > 63) { Com_Printf ("trigger string too long\n"); return; } if (!(trig = TP_FindTrigger (name))) { // allocate new trigger trig = (msg_trigger_t *) Q_malloc(sizeof(msg_trigger_t)); trig->next = msg_triggers; msg_triggers = trig; strlcpy (trig->name, name, sizeof (trig->name)); trig->level = PRINT_HIGH; } strlcpy (trig->string, Cmd_Argv(2), sizeof(trig->string)); if (c == 5 && !strcasecmp (Cmd_Argv(3), "-l")) { if (!strcmp(Cmd_Argv(4), "t")) { trig->level = 4; } else { trig->level = Q_atoi (Cmd_Argv(4)); if ((unsigned) trig->level > PRINT_CHAT) trig->level = PRINT_HIGH; } } } }
/* ================ 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 || 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; 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 ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); #ifdef USE_VOIP if (sv_voip->integer) { Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) ); } #endif 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 ); }