/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "qwsvdef.h" #ifndef CLIENTONLY qboolean sv_allow_cheats; int fp_messages=4, fp_persecond=4, fp_secondsdead=10; char fp_msg[255] = { 0 }; extern cvar_t cl_warncmd; cvar_t sv_cheats = {"sv_cheats", "0", NULL, CVAR_LATCH}; extern redirect_t sv_redirected; extern cvar_t sv_public; //generic helper function for naming players. client_t *SV_GetClientForString(char *name, int *id) { int i; char *s, *s2; char nicename[80]; client_t *cl; int first=0; if (id && *id != -1) first = *id; if (!strcmp(name, "*")) //match with all { for (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++) { if (cl->state<=cs_zombie) continue; *id=i+1; return cl; } *id=sv.allocated_client_slots; return NULL; } //check to make sure it's all an int for (s = name; *s; s++) { if (*s < '0' || *s > '9') break; } //we got to the end of the string and found only numbers. - it's a uid. if (!*s) { int uid = Q_atoi(name); for (i = first, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++) { if (cl->state<=cs_zombie) continue; if (cl->userid == uid) { *id=sv.allocated_client_slots; return cl; } } return NULL; } for (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++) { if (cl->state<=cs_zombie) continue; s = nicename; s2 = cl->name; while(*s2) { *s = *s2 & ~128; s2++; if (*s == '3') *s = 'e'; else if (*s == '4') *s = 'a'; else if (*s == '1' || *s == '7') *s = 'l'; else if (*s >= 18 && *s < 27) *s = *s - 18 + '0'; else if (*s >= 'A' && *s <= 'Z') *s = *s - 'A' + 'a'; else if (*s<' ' || *s == '~') continue; s++; } *s = '\0'; if (strstr(nicename, name)) { *id=i+1; return cl; } } return NULL; } /* =============================================================================== OPERATOR CONSOLE ONLY COMMANDS These commands can only be entered from stdin or by a remote operator datagram =============================================================================== */ /* ==================== SV_SetMaster_f Make a master server current ==================== */ void Master_ClearAll(void); void Master_Add(char *stringadr); void SV_SetMaster_f (void) { int i; Cvar_Set(&sv_public, "1"); //go public. Master_ClearAll(); if (!strcmp(Cmd_Argv(1), "none")) { Con_Printf ("Entering no-master mode\n"); return; } for (i=1 ; istate) continue; if (cl->userid == idnum) { host_client = cl; sv_player = host_client->edict; return true; } } Con_TPrintf (STL_USERIDNOTONSERVER, idnum); return false; } /* ================== SV_God_f Sets client to godmode ================== */ void SV_God_f (void) { if (!sv_allow_cheats) { Con_TPrintf (STL_NEEDCHEATPARM); return; } if (!SV_SetPlayer ()) return; sv_player->v->flags = (int)sv_player->v->flags ^ FL_GODMODE; if ((int)sv_player->v->flags & FL_GODMODE) SV_ClientTPrintf (host_client, PRINT_HIGH, STL_GODON); else SV_ClientTPrintf (host_client, PRINT_HIGH, STL_GODOFF); } void SV_Noclip_f (void) { if (!sv_allow_cheats) { Con_TPrintf (STL_NEEDCHEATPARM); return; } if (!SV_SetPlayer ()) return; if (sv_player->v->movetype != MOVETYPE_NOCLIP) { sv_player->v->movetype = MOVETYPE_NOCLIP; SV_ClientTPrintf (host_client, PRINT_HIGH, STL_NOCLIPON); } else { sv_player->v->movetype = MOVETYPE_WALK; SV_ClientTPrintf (host_client, PRINT_HIGH, STL_NOCLIPOFF); } } /* ================== SV_Give_f ================== */ void SV_Give_f (void) { char *t; int v; if (!sv_allow_cheats) { Con_TPrintf (STL_NEEDCHEATPARM); return; } if (!SV_SetPlayer ()) { int oldself; oldself = pr_global_struct->self; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); Con_Printf("Result: %s\n", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args())); pr_global_struct->self = oldself; return; } if (!svprogfuncs) return; t = Cmd_Argv(2); v = atoi (Cmd_Argv(3)); switch (t[0]) { case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': sv_player->v->items = (int)sv_player->v->items | IT_SHOTGUN<< (t[0] - '2'); break; case 's': sv_player->v->ammo_shells = v; break; case 'n': sv_player->v->ammo_nails = v; break; case 'r': sv_player->v->ammo_rockets = v; break; case 'h': sv_player->v->health = v; break; case 'c': sv_player->v->ammo_cells = v; break; default: { int oldself; oldself = pr_global_struct->self; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); Con_Printf("Result: %s\n", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args())); pr_global_struct->self = oldself; } } } int ShowMapList (char *name, int flags, void *parm) { if (name[5] == 'b' && name[6] == '_') //skip box models return true; Con_Printf("%s\n", name+5); return true; } void SV_MapList_f(void) { COM_EnumerateFiles("maps/*.bsp", ShowMapList, NULL); } /* ====================== SV_Map_f handle a map command from the console or progs. ====================== */ void SV_Map_f (void) { char level[MAX_QPATH]; char spot[MAX_QPATH]; char expanded[MAX_QPATH]; char *nextserver; qboolean issamelevel = false; qboolean newunit = false; qboolean cinematic = false; qboolean waschangelevel = false; qboolean wasspmap = false; int i; char *startspot; if (Cmd_Argc() != 2 && Cmd_Argc() != 3) { Con_TPrintf (STL_MAPCOMMANDUSAGE); return; } strcpy (level, Cmd_Argv(1)); startspot = ((Cmd_Argc() == 2)?NULL:Cmd_Argv(2)); waschangelevel = !strcmp(Cmd_Argv(0), "changelevel"); wasspmap = !strcmp(Cmd_Argv(0), "spmap"); nextserver = strchr(level, '+'); if (nextserver) { *nextserver = '\0'; nextserver++; } if (startspot) { strcpy(spot, startspot); startspot = spot; } else if ((startspot = strchr(level, '$'))) { strcpy(spot, startspot+1); *startspot = '\0'; startspot = spot; } else startspot = NULL; if (!strcmp(level, ".")) //restart current { COM_StripExtension(COM_SkipPath(sv.modelname), level); issamelevel = true; Q_strncpyz(spot, Info_ValueForKey(svs.info, "*startspot"), sizeof(spot)); startspot = spot; } COM_FlushFSCache(); // check to make sure the level exists if (*level == '*') { memmove(level, level+1, strlen(level)); newunit=true; } if (strlen(level) > 4 && !strcmp(level + strlen(level)-4, ".cin")) { cinematic = true; } else { sprintf (expanded, "maps/%s.bsp", level); if (!COM_FCheckExists (expanded)) { //doesn't exist, so try lowercase. Q3 does this. for (i = 0; i < sizeof(level); i++) { if (level[i] >= 'A' && level[i] <= 'Z') level[i] = level[i] - 'A' + 'a'; } sprintf (expanded, "maps/%s.bsp", level); if (!COM_FCheckExists (expanded)) { Con_TPrintf (STL_CANTFINDMAP, expanded); return; } } } if (sv.mvdrecording) SV_MVDStop_f(); #ifndef SERVERONLY if (!isDedicated) //otherwise, info used on map loading isn't present Cmd_ExecuteString(va("fullserverinfo \"%s\"\n", svs.info), RESTRICT_SERVER); if (!sv.state && cls.state) CL_Disconnect(); #endif SV_SaveSpawnparms (issamelevel); if (startspot && !issamelevel && !newunit) { #ifdef Q2SERVER if (ge) { qboolean savedinuse[MAX_CLIENTS]; for (i=0 ; iinuse; svs.clients[i].q2edict->inuse = false; } SV_SaveLevelCache(false); for (i=0 ; iinuse = savedinuse[i]; } } else #endif SV_SaveLevelCache(false); } #ifdef Q3SERVER { cvar_t *gametype; gametype = Cvar_Get("g_gametype", "0", CVAR_LATCH, "Q3 compatability"); if (wasspmap) Cvar_ForceSet(gametype, "2"); else if (gametype->value == 2) //singleplayer Cvar_ForceSet(gametype, "0"); } #endif for (i=0 ; ics_connected) //so that we don't send a datagram svs.clients[i].state=cs_connected; } SV_BroadcastCommand ("changing\n"); SV_SendMessagesToAll (); #ifndef SERVERONLY S_StopAllSounds (true); SCR_BeginLoadingPlaque(); #endif if (newunit || !startspot || !SV_LoadLevelCache(level, startspot, false)) { if (waschangelevel && !startspot) startspot = ""; SV_SpawnServer (level, startspot, false, cinematic); } SV_BroadcastCommand ("reconnect\n"); if (!issamelevel) { cvar_t *nsv; nsv = Cvar_Get("nextserver", "", 0, ""); if (nextserver) Cvar_Set(nsv, va("gamemap \"%s\"", nextserver)); else Cvar_Set(nsv, ""); } } void SV_KillServer_f(void) { SV_UnspawnServer(); } /* ================== SV_Kick_f Kick a user off of the server ================== */ void SV_Kick_f (void) { client_t *cl; int clnum=-1; while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASKICKED, cl->name); // print directly, because the dropped client won't get the // SV_BroadcastPrintf message SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREKICKED); SV_DropClient (cl); } if (clnum == -1) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } void SV_BanIP_f (void) { client_t *cl; int clnum=-1; while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) if (cl) { bannedips_t *nb; nb = Z_Malloc(sizeof(bannedips_t)); nb->next = svs.bannedips; nb->adr = cl->netchan.remote_address; if (*Cmd_Argv(2)) //explicit blocking of all ports of a client ip nb->adr.port = 0; svs.bannedips = nb; SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name); // print directly, because the dropped client won't get the // SV_BroadcastPrintf message SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREBANNED); SV_DropClient (cl); } if (clnum == -1) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } void SV_BanName_f (void) { client_t *cl; int clnum=-1; rankstats_t rs; while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name); // print directly, because the dropped client won't get the // SV_BroadcastPrintf message SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREBANNED); if (cl->rankid) { if (Rank_GetPlayerStats(cl->rankid, &rs)) { rs.flags1 |= RANK_BANNED; Rank_SetPlayerStats(cl->rankid, &rs); } } else Con_Printf("User is not using an account\n"); SV_DropClient (cl); } if (clnum == -1) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } void SV_ForceName_f (void) { client_t *cl; int clnum=-1; int i; while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { Info_SetValueForKey(cl->userinfo, "name", Cmd_Argv(2), MAX_INFO_STRING); SV_ExtractFromUserinfo(cl); Q_strncpyz(cl->name, Cmd_Argv(2), sizeof(cl->name)); i = cl - svs.clients; MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); MSG_WriteByte (&sv.reliable_datagram, i); MSG_WriteString (&sv.reliable_datagram, "name"); MSG_WriteString (&sv.reliable_datagram, cl->name); return; } if (clnum == -1) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } void SV_CripplePlayer_f (void) { client_t *cl; int clnum=-1; int persist = *Cmd_Argv(2); while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { if (!cl->iscrippled) { if (persist) { cl->iscrippled = 2; SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCRIPPLEDPERMANENTLY, cl->name); } else { cl->iscrippled = true; SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCRIPPLED, cl->name); } } else { cl->iscrippled = false; SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUARNTCRIPPLED); } } if (clnum == -1) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } void SV_Mute_f (void) { client_t *cl; int clnum=-1; int persist = *Cmd_Argv(2); while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { if (!cl->ismuted) { if (persist) { cl->ismuted = 2; SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISMUTEDPERMANENTLY, cl->name); } else { cl->ismuted = true; SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISMUTED, cl->name); } } else { cl->ismuted = false; SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUARNTMUTED); } } if (clnum == -1) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } void SV_Cuff_f (void) { client_t *cl; int clnum=-1; int persist = *Cmd_Argv(2); while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { if (!cl->iscuffed) { if (persist) { cl->iscuffed = 2; SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCUFFEDPERMANENTLY, cl->name); } else { cl->iscuffed = true; SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCUFFED, cl->name); } } else { cl->iscuffed = false; SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUARNTCUFFED); } return; } if (clnum == -1) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } void SV_StuffToClient_f(void) { //with this we emulate the progs 'stuffcmds' builtin client_t *cl; int clnum=-1; char *clientname = Cmd_Argv(1); char *str; char *c; char *key; Cmd_ShiftArgs(1, Cmd_ExecLevel==RESTRICT_LOCAL); if (!strcmp(Cmd_Argv(1), "bind")) { key = Z_Malloc(strlen(Cmd_Argv(2))+1); strcpy(key, Cmd_Argv(2)); Cmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL); } else key = NULL; str = Cmd_Args(); while(*str <= ' ') //strim leading spaces { if (!*str) break; str++; } //a list of safe, allowed commands. Allows any extention of this. if (strchr(str, '\n') || strchr(str, ';') || ( strncmp(str, "setinfo", 7) && strncmp(str, "quit", 4) && strncmp(str, "gl_fb", 5) && strncmp(str, "r_fb", 4) && strncmp(str, "say", 3) && //note that the say parsing could be useful here. strncmp(str, "echo", 4) && strncmp(str, "name", 4) && strncmp(str, "skin", 4) && strncmp(str, "color", 5) && strncmp(str, "cmd", 3) && strncmp(str, "fov", 3) && strncmp(str, "connect", 7) && strncmp(str, "rate", 4) && strncmp(str, "cd", 2) && strncmp(str, "easyrecord", 10) && strncmp(str, "leftisright", 11) && strncmp(str, "menu_", 5) && strncmp(str, "r_fullbright", 12) && strncmp(str, "toggleconsole", 13) && strncmp(str, "v_i", 3) && //idlescale vars strncmp(str, "bf", 2) && strncmp(str, "+", 1) && strncmp(str, "-", 1) && strncmp(str, "impulse", 7) && 1)) { Con_Printf("You're not allowed to stuffcmd that\n"); if (key) Z_Free(key); return; } while((cl = SV_GetClientForString(clientname, &clnum))) { if (cl->protocol == SCP_QUAKE2) ClientReliableWrite_Begin (cl, svcq2_stufftext, 3+strlen(str) + (key?strlen(key)+6:0)); else ClientReliableWrite_Begin (cl, svc_stufftext, 3+strlen(str) + (key?strlen(key)+6:0)); if (key) { for (c = "bind "; *c; c++) ClientReliableWrite_Byte (cl, *c); for (c = key; *c; c++) ClientReliableWrite_Byte (cl, *c); ClientReliableWrite_Byte (cl, ' '); } for (c = str; *c; c++) ClientReliableWrite_Byte (cl, *c); ClientReliableWrite_Byte (cl, '\n'); ClientReliableWrite_Byte (cl, '\0'); } if (key) Z_Free(key); } /* ================ SV_Status_f ================ */ void SV_Status_f (void) { int i, j, l; client_t *cl; float cpu, avg, pak; char *s; int columns = 80; if (sv_redirected != RD_OBLIVION && (sv_redirected != RD_NONE #ifndef SERVERONLY || (vid.width < 68*8 && qrenderer != QR_NONE) #endif )) columns = 40; if (!sv.state) { if (net_local_sv_ipadr.type != NA_LOOPBACK) Con_Printf ("ip address : %s\n",NET_AdrToString (net_local_sv_ipadr)); Con_Printf("Server is not running\n"); return; } if (Cmd_Argc()>1) columns = atoi(Cmd_Argv(1)); cpu = (svs.stats.latched_active+svs.stats.latched_idle); if (cpu) cpu = 100*svs.stats.latched_active/cpu; avg = 1000*svs.stats.latched_active / STATFRAMES; pak = (float)svs.stats.latched_packets/ STATFRAMES; if (net_local_sv_ipadr.type != NA_LOOPBACK) { extern cvar_t pr_imitatemvdsv; if (pr_imitatemvdsv.value) Con_Printf ("net address : %s\n",NET_AdrToString (net_local_sv_ipadr)); else Con_Printf ("ip address : %s\n",NET_AdrToString (net_local_sv_ipadr)); } Con_Printf ("cpu utilization : %3i%%\n",(int)cpu); Con_Printf ("avg response time: %i ms\n",(int)avg); Con_Printf ("packets/frame : %5.2f\n", pak); //not relevent as a limit. // min fps lat drp if (columns < 80) { // most remote clients are 40 columns // 0123456789012345678901234567890123456789 Con_Printf ("name userid frags\n"); Con_Printf (" address rate ping drop\n"); Con_Printf (" ---------------- ---- ---- -----\n"); for (i=0,cl=svs.clients ; istate) continue; Con_Printf ("%-16.16s ", cl->name); Con_Printf ("%6i %5i", cl->userid, (int)cl->old_frags); if (cl->spectator) Con_Printf(" (s)\n"); else Con_Printf("\n"); if (cl->istobeloaded && cl->state == cs_zombie) s = "LoadZombie"; else if (cl->protocol == SCP_BAD) s = "bot"; else s = NET_BaseAdrToString ( cl->netchan.remote_address); Con_Printf (" %-16.16s", s); if (cl->state == cs_connected) { Con_Printf ("CONNECTING\n"); continue; } if (cl->state == cs_zombie) { Con_Printf ("ZOMBIE\n"); continue; } Con_Printf ("%4i %4i %5.2f\n" , (int)(1000*cl->netchan.frame_rate) , (int)SV_CalcPing (cl) , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence); } } else { Con_Printf ("frags userid address name rate ping drop qport dl%% dls\n"); Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- ----- --- ----\n"); for (i=0,cl=svs.clients ; istate) continue; Con_Printf ("%5i %6i ", (int)cl->old_frags, cl->userid); if (cl->istobeloaded && cl->state == cs_zombie) s = "LoadZombie"; else if (cl->protocol == SCP_BAD) s = "bot"; else s = NET_BaseAdrToString ( cl->netchan.remote_address); Con_Printf ("%s", s); l = 16 - strlen(s); for (j=0 ; jname); l = 16 - strlen(cl->name); for (j=0 ; jstate == cs_connected) { Con_Printf ("CONNECTING "); } else if (cl->state == cs_zombie) { Con_Printf ("ZOMBIE "); } else Con_Printf ("%4i %4i %5.1f %4i" , (int)(1000*cl->netchan.frame_rate) , (int)SV_CalcPing (cl) , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence , cl->netchan.qport); if (cl->download) { Con_Printf (" %3i %4i", (cl->downloadcount*100)/cl->downloadsize, cl->downloadsize/1024); } if (cl->spectator) Con_Printf(" (s)\n"); else Con_Printf("\n"); } } Con_Printf ("\n"); } /* ================== SV_ConSay_f ================== */ void SV_ConSay_f(void) { client_t *client; int j; char *p; char text[1024]; if (Cmd_Argc () < 2) return; Q_strcpy (text, "console: "); p = Cmd_Args(); if (*p == '"') { p++; p[Q_strlen(p)-1] = 0; } Q_strcat(text, p); for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state == cs_free) continue; SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text); } if (sv.mvdrecording) { MVDWrite_Begin (dem_all, 0, strlen(text)+3); MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_print); MSG_WriteByte ((sizebuf_t*)demo.dbuf, PRINT_CHAT); MSG_WriteString ((sizebuf_t*)demo.dbuf, text); } } void SV_ConSayOne_f (void) { char text[2048]; client_t *to; int i; char *s; int clnum=-1; if (Cmd_Argc () < 3) return; while((to = SV_GetClientForString(Cmd_Argv(1), &clnum))) { Q_strcpy (text, "{console}: "); for (i = 2; ; i++) { s = Cmd_Argv(i); if (!*s) break; if (strlen(text) + strlen(s) + 2 >= sizeof(text)-1) break; strcat(text, " "); strcat(text, s); } strcat(text, "\n"); SV_ClientPrintf(to, PRINT_CHAT, "%s", text); } if (!clnum) Con_TPrintf(STL_USERDOESNTEXIST, Cmd_Argv(1)); } /* ================== SV_Heartbeat_f ================== */ void SV_Heartbeat_f (void) { svs.last_heartbeat = -9999; } void SV_SendServerInfoChange(char *key, const char *value) { if (!sv.state) return; #ifdef Q2SERVER if (ge) return; //FIXME!!! #endif MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo); MSG_WriteString (&sv.reliable_datagram, key); MSG_WriteString (&sv.reliable_datagram, value); } /* =========== SV_Serverinfo_f Examine or change the serverinfo string =========== */ char *CopyString(char *s); void SV_Serverinfo_f (void) { cvar_t *var; char value[512]; int i; if (Cmd_Argc() == 1) { Con_TPrintf (STL_SERVERINFOSETTINGS); Info_Print (svs.info); return; } if (Cmd_Argc() < 3) { Con_TPrintf (STL_SERVERINFOSYNTAX); return; } if (Cmd_Argv(1)[0] == '*') { Con_TPrintf (TL_STARKEYPROTECTED); return; } Q_strncpyz(value, Cmd_Argv(2), sizeof(value)); value[sizeof(value)-1] = '\0'; for (i = 3; i < Cmd_Argc(); i++) { strncat(value, " ", sizeof(value)-1); strncat(value, Cmd_Argv(i), sizeof(value)-1); } Info_SetValueForKey (svs.info, Cmd_Argv(1), value, MAX_SERVERINFO_STRING); // if this is a cvar, change it too var = Cvar_FindVar (Cmd_Argv(1)); if (var) { Cvar_Set(var, value); /* Z_Free (var->string); // free the old value string var->string = CopyString (value); var->value = Q_atof (var->string); */ } SV_SendServerInfoChange(Cmd_Argv(1), value); } /* =========== SV_Serverinfo_f Examine or change the serverinfo string =========== */ char *CopyString(char *s); void SV_Localinfo_f (void) { char *old; if (Cmd_Argc() == 1) { Con_TPrintf (STL_LOCALINFOSETTINGS); Info_Print (localinfo); return; } if (Cmd_Argc() != 3) { Con_TPrintf (STL_LOCALINFOSYNTAX); return; } if (Cmd_Argv(1)[0] == '*') { Con_TPrintf (TL_STARKEYPROTECTED); return; } old = Info_ValueForKey(localinfo, Cmd_Argv(1)); Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING); PR_LocalInfoChanged(Cmd_Argv(1), old, Cmd_Argv(2)); Con_DPrintf("Localinfo %s changed (%s -> %s)\n", Cmd_Argv(1), old, Cmd_Argv(2)); } void SV_SaveInfo(FILE *f, char *info, char *commandname) { char *command; char *value; while(*info == '\\') { command = info+1; value = strchr(command, '\\'); info = strchr(value+1, '\\'); if (!info) //eot.. info = value+strlen(value); if (*command == '*') //unsettable, so don't write it for later setting. continue; fwrite(commandname, strlen(commandname), 1, f); fwrite(" ", 1, 1, f); fwrite(command, value-command, 1, f); fwrite(" ", 1, 1, f); fwrite(value+1, info-(value+1), 1, f); fwrite("\n", 1, 1, f); } } void SV_SaveInfos(FILE *f) { fwrite("\n", 1, 1, f); SV_SaveInfo(f, svs.info, "serverinfo"); fwrite("\n", 1, 1, f); SV_SaveInfo(f, localinfo, "localinfo"); } /* void SV_ResetInfos(void) { // TODO: add me } */ /* =========== SV_User_f Examine a users info strings =========== */ void SV_User_f (void) { if (Cmd_Argc() != 2) { Con_TPrintf (STL_USERINFOSYNTAX); return; } if (!SV_SetPlayer ()) return; Info_Print (host_client->userinfo); } /* ================ SV_Floodport_f Sets the gamedir and path to a different directory. ================ */ void SV_Floodprot_f (void) { int arg1, arg2, arg3; if (Cmd_Argc() == 1) { if (fp_messages) { Con_TPrintf (STL_FLOODPROTSETTINGS, fp_messages, fp_persecond, fp_secondsdead); return; } else Con_TPrintf (STL_FLOODPROTNOTON); } if (Cmd_Argc() != 4) { Con_TPrintf (STL_FLOODPROTSYNTAX); return; } arg1 = atoi(Cmd_Argv(1)); arg2 = atoi(Cmd_Argv(2)); arg3 = atoi(Cmd_Argv(3)); if (arg1<=0 || arg2 <= 0 || arg3<=0) { Con_TPrintf (STL_NONEGATIVEVALUES); return; } if (arg1 > 10) { Con_TPrintf (STL_TRACK10PLUSSMESSAGES); return; } fp_messages = arg1; fp_persecond = arg2; fp_secondsdead = arg3; } void SV_Floodprotmsg_f (void) { if (Cmd_Argc() == 1) { Con_TPrintf(STL_FLOODPROTCURRENTMESSAGE, fp_msg); return; } else if (Cmd_Argc() != 2) { Con_TPrintf(STL_FLOODPROTMESSAGESYNTAX); return; } sprintf(fp_msg, "%s", Cmd_Argv(1)); } /* ================ SV_Gamedir Sets the fake *gamedir to a different directory. ================ */ void SV_Gamedir (void) { char *dir; if (Cmd_Argc() == 1) { Con_TPrintf (STL_CURRENTGAMEDIR, Info_ValueForKey (svs.info, "*gamedir")); return; } if (Cmd_Argc() != 2) { Con_TPrintf (STL_SVGAMEDIRUSAGE); return; } dir = Cmd_Argv(1); if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":") ) { Con_TPrintf (STL_GAMEDIRCANTBEPATH); return; } Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); } /* ================ SV_Gamedir_f Sets the gamedir and path to a different directory. ================ */ void SV_Gamedir_f (void) { char *dir; if (Cmd_Argc() == 1) { Con_TPrintf (STL_CURRENTGAMEDIR, com_gamedir); return; } if (Cmd_Argc() != 2) { Con_TPrintf (STL_GAMEDIRUSAGE); return; } dir = Cmd_Argv(1); if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":") ) { Con_TPrintf (STL_GAMEDIRCANTBEPATH); return; } COM_Gamedir (dir); Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); } extern char gamedirfile[MAX_OSPATH]; /* ================ SV_Snap ================ */ void SV_Snap (int uid) { client_t *cl; char pcxname[80]; char checkname[MAX_OSPATH]; int i; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->state) continue; if (cl->userid == uid) break; } if (i >= MAX_CLIENTS) { Con_TPrintf (STL_USERDOESNTEXIST); return; } if (!ISQWCLIENT(cl)) { Con_Printf("Can only snap QW clients\n"); return; } sprintf(pcxname, "%d-00.pcx", uid); sprintf(checkname, "%s/snap", gamedirfile); Sys_mkdir(gamedirfile); Sys_mkdir(checkname); for (i=0 ; i<=99 ; i++) { pcxname[strlen(pcxname) - 6] = i/10 + '0'; pcxname[strlen(pcxname) - 5] = i%10 + '0'; sprintf (checkname, "%s/snap/%s", gamedirfile, pcxname); if (Sys_FileTime(checkname) == -1) break; // file doesn't exist } if (i==100) { Con_TPrintf (STL_SNAPTOOMANYFILES); return; } strcpy(cl->uploadfn, checkname); memcpy(&cl->snap_from, &net_from, sizeof(net_from)); if (sv_redirected != RD_NONE) cl->remote_snap = true; else cl->remote_snap = false; ClientReliableWrite_Begin (cl, svc_stufftext, 24); ClientReliableWrite_String (cl, "cmd snap\n"); Con_TPrintf (STL_SNAPREQUEST, uid); } /* ================ SV_Snap_f ================ */ void SV_Snap_f (void) { int uid; if (Cmd_Argc() != 2) { Con_TPrintf (STL_SNAPUSAGE); return; } uid = atoi(Cmd_Argv(1)); SV_Snap(uid); } /* ================ SV_Snap ================ */ void SV_SnapAll_f (void) { client_t *cl; int i; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state < cs_connected || cl->spectator) continue; SV_Snap(cl->userid); } } float timer; float lasttimer; int ticsleft; float timerinterval; int timerlevel; cvar_t *timercommand; void SV_CheckTimer(void) { float ctime = Sys_DoubleTime(); // if (ctime < lasttimer) //new map? (shouldn't happen) // timer = ctime+5; //trigger in a few secs lasttimer = ctime; if (ticsleft) { if (timer < ctime) { timer += timerinterval; if (ticsleft > 0) ticsleft--; if (timercommand) { Cbuf_AddText(timercommand->string, timerlevel); Cbuf_AddText("\n", timerlevel); } } } } void SV_SetTimer_f(void) { int count; float interval; char *command; if (Cmd_Argc() < 2) { Con_Printf("%s \n", Cmd_Argv(0)); return; } count = atoi(Cmd_Argv(1)); interval = atof(Cmd_Argv(2)); if (!count && Cmd_Argc() == 2) { ticsleft = 0; return; } if (interval <= 0 || (count <= 0 && count != -1)) //makes sure the args are right. :) { Con_Printf("%s count interval command\n", Cmd_Argv(0)); return; } Cmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL); //strip the two vars command = Cmd_Args(); timercommand = Cvar_Get("sv_timer", "", CVAR_NOSET, NULL); Cvar_ForceSet(timercommand, command); timer = Sys_DoubleTime() + interval; ticsleft = count; timerinterval = interval; timerlevel = Cmd_ExecLevel; } void SV_SendGameCommand_f(void) { #ifdef Q3SERVER if (SVQ3_ConsoleCommand()) return; #endif #ifdef Q2SERVER if (ge) { ge->ServerCommand(); } else #endif Con_Printf("This command requires a Q2 sever\n"); } /* ================== SV_InitOperatorCommands ================== */ void SV_InitOperatorCommands (void) { #ifndef SERVERONLY if (isDedicated) #endif { Cmd_AddCommand ("quit", SV_Quit_f); Cmd_AddCommand ("say", SV_ConSay_f); Cmd_AddCommand ("sayone", SV_ConSayOne_f); Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); //commands that conflict with client commands. Cmd_AddCommand ("user", SV_User_f); Cmd_AddCommand ("god", SV_God_f); Cmd_AddCommand ("give", SV_Give_f); Cmd_AddCommand ("noclip", SV_Noclip_f); } Cvar_Register(&sv_cheats, "Server Permissions"); if (COM_CheckParm ("-cheats")) { Cvar_Set(&sv_cheats, "1"); } Cmd_AddCommand ("logfile", SV_Logfile_f); Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f); Cmd_AddCommand ("snap", SV_Snap_f); Cmd_AddCommand ("snapall", SV_SnapAll_f); Cmd_AddCommand ("kick", SV_Kick_f); Cmd_AddCommand ("mute", SV_Mute_f); Cmd_AddCommand ("cuff", SV_Cuff_f); Cmd_AddCommand ("renameclient", SV_ForceName_f); Cmd_AddCommand ("cripple", SV_CripplePlayer_f); Cmd_AddCommand ("banname", SV_BanName_f); Cmd_AddCommand ("banip", SV_BanIP_f); // Cmd_AddCommand ("ban", SV_BanName_f); Cmd_AddCommand ("status", SV_Status_f); Cmd_AddCommand ("sv", SV_SendGameCommand_f); Cmd_AddCommand ("killserver", SV_KillServer_f); Cmd_AddCommand ("map", SV_Map_f); #ifdef Q3SERVER Cmd_AddCommand ("spmap", SV_Map_f); #endif Cmd_AddCommand ("gamemap", SV_Map_f); Cmd_AddCommand ("changelevel", SV_Map_f); Cmd_AddCommand ("listmaps", SV_MapList_f); Cmd_AddCommand ("setmaster", SV_SetMaster_f); Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); Cmd_AddCommand ("localinfo", SV_Localinfo_f); Cmd_AddCommand ("gamedir", SV_Gamedir_f); Cmd_AddCommand ("sv_gamedir", SV_Gamedir); Cmd_AddCommand ("floodprot", SV_Floodprot_f); Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f); Cmd_AddCommand ("sv_settimer", SV_SetTimer_f); Cmd_AddCommand ("stuffcmd", SV_StuffToClient_f); cl_warncmd.value = 1; } #endif