static void Host_Pause_f (void) { if (cmd_source == src_command) { CL_Cmd_ForwardToServer (); return; } if (!pausable->int_val) SV_ClientPrintf ("Pause not allowed.\n"); else { sv.paused ^= 1; if (sv.paused) { SV_BroadcastPrintf ("%s paused the game\n", PR_GetString (&sv_pr_state, SVstring (sv_player, netname))); } else { SV_BroadcastPrintf ("%s unpaused the game\n", PR_GetString (&sv_pr_state, SVstring (sv_player, netname))); } // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_setpause); MSG_WriteByte (&sv.reliable_datagram, sv.paused); } }
/* SV_Kick_f Kick a user off of the server */ static void SV_Kick_f (void) { client_t *cl; int argc = Cmd_Argc (); const char *reason; if (argc < 2) { SV_Printf ("usage: kick <name/userid>\n"); return; } if (!(cl = SV_Match_User (Cmd_Argv (1)))) return; // print directly, because the dropped client won't get the // SV_BroadcastPrintf message if (argc > 2) { reason = Cmd_Args (2); SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked: %s\n", cl->name, reason); SV_ClientPrintf (1, cl, PRINT_HIGH, "You were kicked from the game: %s\n", reason); } else { SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name); SV_ClientPrintf (1, cl, PRINT_HIGH, "You were kicked from the game\n"); } SV_DropClient (cl); }
/* =============== SV_PlayersOnly_f disable physics, except for players =============== */ void SV_PlayersOnly_f( void ) { if( !Cvar_VariableInteger( "sv_cheats" )) return; sv.hostflags = sv.hostflags ^ SVF_PLAYERSONLY; if(!( sv.hostflags & SVF_PLAYERSONLY )) SV_BroadcastPrintf( D_INFO, "Resume server physics\n" ); else SV_BroadcastPrintf( D_INFO, "Freeze server physics\n" ); }
/* ===================== SV_DropClient Called when the player is totally leaving the server, either willingly or unwillingly. This is NOT called if the entire server is quiting or crashing. ===================== */ void SV_DropClient(client_t *drop, char *info) { if (info) SV_BroadcastPrintf(PRINT_HIGH, "%s %s\n", *drop->Name, info); // add the disconnect MSG_WriteByte(&drop->netchan.message, svc_disconnect); if (drop->state == cs_spawned) { // call the prog function for removing a client // this will remove the body, among other things guardGame(ge.ClientDisconnect); ge->ClientDisconnect(drop->edict); unguardGame; } if (drop->download) { delete drop->download; drop->download = NULL; } drop->state = cs_zombie; // become free in a few seconds drop->Name[0] = 0; }
/* * Kick a user off of the server */ void SV_Kick_f(void) { if (!svs.initialized) { Com_Printf("No server running.\n"); return; } if (Cmd_Argc() != 2) { Com_Printf("Usage: kick <userid>\n"); return; } if (!SV_SetPlayer()) { return; } if ((sv_client->state == cs_spawned) && *sv_client->name) { SV_BroadcastPrintf(PRINT_HIGH, "%s was kicked\n", sv_client->name); } /* print directly, because the dropped client won't get the SV_BroadcastPrintf message */ SV_ClientPrintf(sv_client, PRINT_HIGH, "You were kicked from the game\n"); SV_DropClient(sv_client); sv_client->lastmessage = svs.realtime; /* min case there is a funny zombie */ }
// // [Toke - CTF] CTF_RunTics // Runs once per gametic when ctf is enabled // void CTF_RunTics (void) { for(size_t i = 0; i < NUMFLAGS; i++) { flagdata *data = &CTFdata[i]; if(data->state != flag_dropped) continue; if (!ctf_flagtimeout) continue; if(data->timeout--) continue; if(data->actor) data->actor->Destroy(); SV_CTFEvent ((flag_t)i, SCORE_RETURN, idplayer(0)); SV_BroadcastPrintf (PRINT_HIGH, "%s flag returned.\n", team_names[i]); CTF_SpawnFlag((flag_t)i); } }
/* ================== SV_Kick_f Kick a user off of the server ================== */ void SV_Kick_f (void) { int i; client_t *cl; int uid; uid = atoi(Cmd_Argv(1)); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->state) continue; if (cl->userid == uid) { SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name); // print directly, because the dropped client won't get the // SV_BroadcastPrintf message SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game\n"); SV_DropClient (cl); pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->ClientKill); return; } } Con_Printf ("Couldn't find user number %i\n", uid); }
bool exec(void) { std::string result; CMD_CoinFlip(result); SV_BroadcastPrintf(PRINT_HIGH, "%s\n", result.c_str()); return true; }
/* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages( void ) { sv_client_t *cl; int i; if( sv.state == ss_dead ) return; // send a message to each connected client for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( !cl->state ) continue; if( !cl->edict || (cl->edict->v.flags & (FL_FAKECLIENT|FL_SPECTATOR))) continue; // update any userinfo packets that have changed if( cl->sendinfo ) { cl->sendinfo = false; SV_FullClientUpdate( cl, &sv.multicast ); } if( cl->sendmovevars ) { cl->sendmovevars = false; SV_UpdatePhysinfo( cl, &sv.multicast ); } // if the reliable message overflowed, drop the client if( cl->netchan.message.overflowed ) { MSG_Clear( &cl->netchan.message ); MSG_Clear( &cl->reliable ); MSG_Clear( &cl->datagram ); SV_BroadcastPrintf( PRINT_HIGH, "%s overflowed\n", cl->name ); SV_DropClient( cl ); cl->send_message = true; } // only send messages if the client has sent one if( !cl->send_message ) continue; if( cl->state == cs_spawned ) { // don't overrun bandwidth if( SV_RateDrop( cl )) continue; SV_SendClientDatagram( cl ); } else { // just update reliable if( cl->netchan.message.cursize || svs.realtime - cl->netchan.last_sent > 1000 ) Netchan_Transmit( &cl->netchan, 0, NULL ); } // yes, message really sended cl->send_message = false; } }
/* ================== SV_Kick_f Kick a user off of the server ================== */ static void SV_Kick_f (void) { if (!svs.initialized) { Com_Printf ("No server running.\n"); return; } if (Cmd_Argc() != 2) { Com_Printf ("Usage: kick <userid>\n"); return; } if (!SV_SetPlayer ()) return; //r1: ignore kick message on connecting players (and those with no name) if (sv_client->state == cs_spawned && sv_client->name[0]) SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", sv_client->name); // print directly, because the dropped client won't get the // SV_BroadcastPrintf message SV_ClientPrintf (sv_client, PRINT_HIGH, "You were kicked from the game\n"); SV_DropClient (sv_client); sv_client->lastmessage = svs.realtime; // min case there is a funny zombie }
/* ============ Cvar_Set ============ */ void Cvar_Set (const char *var_name, const char *value) { cvar_t *var; qboolean changed; var = Cvar_FindVar (var_name); if (!var) { // there is an error in C code if this happens Con_Printf ("Cvar_Set: variable %s not found\n", var_name); return; } changed = Q_strcmp(var->string, value); Z_Free (var->string); // free the old value string var->string = (char*) Z_Malloc (Q_strlen(value)+1); Q_strcpy (var->string, value); var->value = Q_atof (var->string); if (var->server && changed) { if (sv.active) SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); } }
// Handle tic-by-tic maintenance of the warmup. void Warmup::tic() { // If autostart is zeroed out, start immediately. if (this->status == Warmup::WARMUP && sv_warmup_autostart == 0.0f) this->set_status(Warmup::COUNTDOWN); // If we're not advancing the countdown, we don't care. if (!(this->status == Warmup::COUNTDOWN || this->status == Warmup::FORCE_COUNTDOWN)) return; // If we haven't reached the level tic that we begin the map on, // we don't care. if (this->time_begin > level.time) { // Broadcast a countdown (this should be handled clientside) if ((this->time_begin - level.time) % TICRATE == 0) { SV_BroadcastWarmupState(this->status, this->get_countdown()); } return; } if (sv_warmup) this->set_status(Warmup::INGAME); else this->set_status(Warmup::DISABLED); // [SL] always reset the time (for now at least) level.time = 0; level.timeleft = sv_timelimit * TICRATE * 60; level.inttimeleft = mapchange / TICRATE; G_DeferedFullReset(); SV_BroadcastPrintf(PRINT_HIGH, "The match has started.\n"); }
/** * @brief Called when the player is totally leaving the server, either willingly * or unwillingly. This is NOT called if the entire server is quitting * or crashing. */ void SV_DropClient (client_t * drop, const char *message) { /* add the disconnect */ dbuffer msg(2 + strlen(message)); NET_WriteByte(&msg, svc_disconnect); NET_WriteString(&msg, message); NET_WriteMsg(drop->stream, msg); SV_BroadcastPrintf(PRINT_CHAT, "%s was dropped from the server - reason: %s\n", drop->name, message); if (drop->state == cs_spawned || drop->state == cs_spawning) { /* call the prog function for removing a client */ /* this will remove the body, among other things */ const ScopedMutex scopedMutex(svs.serverMutex); svs.ge->ClientDisconnect(drop->player); } NET_StreamFinished(drop->stream); drop->stream = NULL; drop->player->inuse = false; SV_SetClientState(drop, cs_free); drop->name[0] = 0; if (svs.abandon) { int count = 0; client_t *cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state >= cs_connected) count++; if (count == 0) svs.killserver = true; } }
/* ================== SV_Kick_f Kick a user off of the server ================== */ void SV_Kick_f( void ) { if( Cmd_Argc() != 2 ) { Msg( "Usage: kick <userid> | <name>\n" ); return; } if( !SV_SetPlayer( )) return; if( NET_IsLocalAddress( svs.currentPlayer->netchan.remote_address )) { Msg( "The local player cannot be kicked!\n" ); return; } SV_BroadcastPrintf( PRINT_HIGH, "%s was kicked\n", svs.currentPlayer->name ); SV_ClientPrintf( svs.currentPlayer, PRINT_HIGH, "You were kicked from the game\n" ); SV_DropClient( svs.currentPlayer ); Log_Printf( "Kick: \"%s<%i><%s><>\" was kicked by \"Console\"\n", svs.currentPlayer->name, svs.currentPlayer->userid, SV_GetClientIDString ( svs.currentPlayer ) ); // min case there is a funny zombie svs.currentPlayer->lastmessage = host.realtime; }
/* ================== SV_CheckTimeouts If a packet has not been received from a client for timeout->value seconds, drop the conneciton. Server frames are used instead of realtime to avoid dropping the local client while debugging. When a client is normally dropped, the client_t goes into a zombie state for a few seconds to make sure any final reliable message gets resent if necessary ================== */ void SV_CheckTimeouts(void) { int i; client_t * cl; int droppoint; int zombiepoint; droppoint = svs.realtime - 1000 * timeout->value; zombiepoint = svs.realtime - 1000 * zombietime->value; for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { // message times may be wrong across a changelevel if (cl->lastmessage > svs.realtime) cl->lastmessage = svs.realtime; if (cl->state == cs_zombie && cl->lastmessage < zombiepoint) { cl->state = cs_free; // can now be reused continue; } if ((cl->state == cs_connected || cl->state == cs_spawned) && cl->lastmessage < droppoint) { SV_BroadcastPrintf(PRINT_HIGH, "%s timed out\n", cl->name); SV_DropClient(cl); cl->state = cs_free; // don't bother with zombie state } } }
void SV_NoSnap_f(void) { if (*host_client->uploadfn) { *host_client->uploadfn = 0; SV_BroadcastPrintf (PRINT_HIGH, "%s refused remote screenshot\n", host_client->name); } }
/* ================== SV_CheckTimeouts If a packet has not been received from a client in timeout.value seconds, drop the conneciton. When a client is normally dropped, the client_t goes into a zombie state for a few seconds to make sure any final reliable message gets resent if necessary ================== */ void SV_CheckTimeouts (void) { int i; client_t *cl; float droptime; int nclients; droptime = curtime - sv_timeout.value; nclients = 0; for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) { if (cl->state == cs_connected || cl->state == cs_spawned) { if (!cl->spectator) nclients++; if (cl->netchan.last_received < droptime) { SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); SV_DropClient (cl); cl->state = cs_free; // don't bother with zombie state } } if (cl->state == cs_zombie && svs.realtime - cl->connection_started > sv_zombietime.value) { cl->state = cs_free; // can now be reused } } if (((int) sv_paused.value & 1) && !nclients) { // nobody left, unpause the server SV_TogglePause("Pause released since no players are left.\n"); } }
// Handle tic-by-tic maintenance of the warmup. void Warmup::tic() { // If autostart is zeroed out, start immediately. if (this->status == Warmup::WARMUP && sv_warmup_autostart == 0.0f) this->set_status(Warmup::COUNTDOWN); // If we're not advancing the countdown, we don't care. if (this->status != Warmup::COUNTDOWN) return; // If we haven't reached the level tic that we begin the map on, // we don't care. if (this->time_begin > level.time) { // Broadcast a countdown (this should be handled clientside) if ((this->time_begin - level.time) % TICRATE == 0) { SV_BroadcastWarmupState(this->status, this->get_countdown()); } return; } this->set_status(Warmup::INGAME); G_DeferedFullReset(); SV_BroadcastPrintf(PRINT_HIGH, "The match has started.\n"); }
/* ============ Cvar_Set ============ */ void Cvar_Set (char *var_name, char *value) { cvar_t *var; qboolean changed; var = Cvar_FindVar (var_name); if (!var) { // there is an error in C code if this happens Con_Printf ("Cvar_Set: variable %s not found\n", var_name); return; } changed = strcmp(var->string, value); Z_Free (var->string); // free the old value string var->string = Z_Malloc (strlen(value)+1); strcpy (var->string, value); var->value = atof (var->string); if ((var->server == 1) && changed) // JPG - so that server = 2 will mute the variable { if (sv.active) SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); } // JPG 3.00 - rcon (64 doesn't mean anything special, but we need some extra space because NET_MAXMESSAGE == RCON_BUFF_SIZE) if (rcon_active && (rcon_message.cursize < rcon_message.maxsize - strlen(var->name) - strlen(var->string) - 64)) { rcon_message.cursize--; MSG_WriteString(&rcon_message, va("\"%s\" set to \"%s\"\n", var->name, var->string)); } }
void SV_Kick_f (void) { int i; client_t *cl; int uid; uid = atoi(Cmd_Argv(1)); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->state) continue; if (cl->userid == uid) { SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name); // print directly, because the dropped client won't get the // SV_BroadcastPrintf message SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game\n"); SV_DropClient (cl); return; } } Con_Printf ("Couldn't find user number %i\n", uid); }
/* ================= SV_Drop_f The client is going to disconnect, so remove the connection immediately ================= */ static void SV_Drop_f (void) { SV_EndRedirect (); if (!host_client->spectator) SV_BroadcastPrintf (PRINT_HIGH, "%s dropped\n", host_client->name); SV_DropClient (host_client); }
/* ============ Cvar_Set ============ */ void Cvar_SetQuick_Internal (cvar_t *var, const char *value) { qboolean changed; size_t valuelen; prvm_prog_t *tmpprog; int i; changed = strcmp(var->string, value) != 0; // LordHavoc: don't reallocate when there is no change if (!changed) return; // LordHavoc: don't reallocate when the buffer is the same size valuelen = strlen(value); if (!var->string || strlen(var->string) != valuelen) { Z_Free ((char *)var->string); // free the old value string var->string = (char *)Z_Malloc (valuelen + 1); } memcpy ((char *)var->string, value, valuelen + 1); var->value = atof (var->string); var->integer = (int) var->value; if ((var->flags & CVAR_NOTIFY) && changed && sv.active) SV_BroadcastPrintf("\"%s\" changed to \"%s\"\n", var->name, var->string); #if 0 // TODO: add infostring support to the server? if ((var->flags & CVAR_SERVERINFO) && changed && sv.active) { InfoString_SetValue(svs.serverinfo, sizeof(svs.serverinfo), var->name, var->string); if (sv.active) { MSG_WriteByte (&sv.reliable_datagram, svc_serverinfostring); MSG_WriteString (&sv.reliable_datagram, var->name); MSG_WriteString (&sv.reliable_datagram, var->string); } } #endif if ((var->flags & CVAR_USERINFO) && cls.state != ca_dedicated) CL_SetInfo(var->name, var->string, true, false, false, false); else if ((var->flags & CVAR_NQUSERINFOHACK) && cls.state != ca_dedicated) { // update the cls.userinfo to have proper values for the // silly nq config variables. // // this is done when these variables are changed rather than at // connect time because if the user or code checks the userinfo and it // holds weird values it may cause confusion... if (!strcmp(var->name, "_cl_color")) { int top = (var->integer >> 4) & 15, bottom = var->integer & 15; CL_SetInfo("topcolor", va("%i", top), true, false, false, false); CL_SetInfo("bottomcolor", va("%i", bottom), true, false, false, false); if (cls.protocol != PROTOCOL_QUAKEWORLD && cls.netcon) { MSG_WriteByte(&cls.netcon->message, clc_stringcmd); MSG_WriteString(&cls.netcon->message, va("color %i %i", top, bottom)); } } else if (!strcmp(var->name, "_cl_rate"))
void QCBUILTIN PF_cl_bprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { #ifndef CLIENTONLY char *str = PF_VarString(prinst, 0, pr_globals); if (sv.active) SV_BroadcastPrintf(PRINT_HIGH, "%s", str); #endif }
// // [Toke - CTF] SV_FlagReturn // Returns the flag to its socket // void SV_FlagReturn (player_t &player, flag_t f) { SV_CTFEvent (f, SCORE_RETURN, player); CTF_SpawnFlag (f); SV_BroadcastPrintf (PRINT_HIGH, "%s has returned the %s flag\n", player.userinfo.netname, team_names[f]); }
/* ==================== SV_MVDStop stop recording a demo ==================== */ void SV_MVDStop (int reason) { if (!sv.mvdrecording) { Com_Printf ("Not recording a demo.\n"); return; } if (reason == 2) { DestCloseAllFlush(true); // stop and remove if (!demo.dest) sv.mvdrecording = false; SV_BroadcastPrintf (PRINT_CHAT, "Server recording canceled, demo removed\n"); Cvar_ForceSet(Cvar_Get("serverdemo", "", CVAR_ROM), ""); return; } // write a disconnect message to the demo file // clearup to be sure message will fit demo.dbuf->cursize = 0; demo.dbuf->h = NULL; demo.dbuf->bufsize = 0; MVDWrite_Begin(dem_all, 0, 2+strlen("EndOfDemo")); MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_disconnect); MSG_WriteString ((sizebuf_t*)demo.dbuf, "EndOfDemo"); SV_MVDWritePackets(demo.parsecount - demo.lastwritten + 1); // finish up DestCloseAllFlush(false); if (!demo.dest) sv.mvdrecording = false; if (!reason) SV_BroadcastPrintf (PRINT_CHAT, "Server recording completed\n"); else SV_BroadcastPrintf (PRINT_CHAT, "Server recording stoped\nMax demo size exceeded\n"); Cvar_ForceSet(Cvar_Get("serverdemo", "", CVAR_ROM), ""); }
/* ===================== SV_KickClient From R1Q2 ===================== */ void SV_KickClient (client_t *cl, const char *reason, const char *cprintf) { if (reason && cl->state == cs_spawned && cl->name[0]) SV_BroadcastPrintf (PRINT_HIGH, "%s was dropped: %s\n", cl->name, reason); if (cprintf) SV_ClientPrintf (cl, PRINT_HIGH, "%s", cprintf); Com_Printf ("Dropping %s, %s.\n", cl->name, reason ? reason : "SV_KickClient"); SV_DropClient (cl); }
/* ================== SV_Kick_f Kick a user off of the server ================== */ void SV_Kick_f (void) { int i, j; client_t *cl; int uid; int c; int saved_state; char reason[80] = ""; c = Cmd_Argc (); if (c < 2) { #ifndef SERVERONLY // some mods use a "kick" alias for their own needs, sigh if (CL_ClientState() && Cmd_FindAlias("kick")) { Cmd_ExecuteString (Cmd_AliasString("kick"), false); return; } #endif Com_Printf ("kick <userid> [reason]\n"); return; } uid = atoi(Cmd_Argv(1)); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->state) continue; if (cl->userid == uid) { if (c > 2) { strcpy (reason, " ("); for (j=2 ; j<c; j++) { strncat (reason, Cmd_Argv(j), sizeof(reason)-4); if (j < c-1) strncat (reason, " ", sizeof(reason)-4); } if (strlen(reason) < 3) reason[0] = '\0'; else strncat (reason, ")", sizeof(reason)); } saved_state = cl->state; cl->state = cs_free; // HACK: don't broadcast to this client SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked%s\n", cl->name, reason); cl->state = saved_state; SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game%s\n", reason); SV_DropClient (cl); return; } } Com_Printf ("Couldn't find user number %i\n", uid); }
// // [Toke - CTF] SV_FlagGrab // Event of a player picking up a flag // void SV_FlagGrab (player_t &player, flag_t f, bool firstgrab) { player.flags[f] = true; CTFdata[f].flagger = player.id; CTFdata[f].state = flag_carried; CTFdata[f].pickup_time = I_MSTime(); if (player.userinfo.team != (team_t)f) { if (firstgrab) { SV_BroadcastPrintf (PRINT_HIGH, "%s has taken the %s flag\n", player.userinfo.netname, team_names[f]); SV_CTFEvent (f, SCORE_FIRSTGRAB, player); } else { SV_BroadcastPrintf (PRINT_HIGH, "%s picked up the %s flag\n", player.userinfo.netname, team_names[f]); SV_CTFEvent (f, SCORE_GRAB, player); } } else { SV_BroadcastPrintf (PRINT_HIGH, "%s is recovering the %s flag\n", player.userinfo.netname, team_names[f]); SV_CTFEvent (f, SCORE_MANUALRETURN, player); } }
static void SV_Ban_f (void) { double mins = 30.0, m; client_t *cl; char *e; const char *a, *reason; int argc = Cmd_Argc (), argr = 2; if (argc < 2) { SV_Printf ("usage: ban <name/userid> [minutes] [reason]\n" " (default = 30, 0 = permanent).\n"); return; } if (!(cl = SV_Match_User (Cmd_Argv (1)))) return; if (argc >= 3) { a = Cmd_Argv (2); m = strtod (a, &e); if (e != a) { argr++; mins = m; if (mins < 0.0 || mins > 1000000.0) // bout 2 yrs mins = 0.0; } } if (argc > argr) { reason = Cmd_Args (argr); SV_BroadcastPrintf (PRINT_HIGH, "Admin Banned user %s %s: %s\n", cl->name, mins ? va ("for %.1f minutes", mins) : "permanently", reason); } else { SV_BroadcastPrintf (PRINT_HIGH, "Admin Banned user %s %s\n", cl->name, mins ? va ("for %.1f minutes", mins) : "permanently"); } SV_DropClient (cl); Cmd_ExecuteString (va ("addip %s %f", NET_BaseAdrToString (cl->netchan.remote_address), mins), src_command); }
/* ===================== SV_DropClientFromAdr Calls SV_DropClient, takes netadr_t instead of client pointer. ===================== */ void SV_DropClientFromAdr (netadr_t address) { // adapted Pat Aftermoon's simplified version of this client_t *drop = GetClientFromAdr(address); if (!drop) return; // make sure we have a client to drop SV_BroadcastPrintf (PRINT_HIGH, "dropping client %s\n", drop->name); SV_DropClient (drop); drop->state = cs_free; // don't bother with zombie state }