*/
-#include <quakedef.h>
-#include <server.h>
+#include "quakedef.h"
+#include "utf8lib.h"
+#include "server.h"
+#include "sv_demo.h"
int current_skill;
cvar_t sv_cheats = {CVAR_SERVER | CVAR_NOTIFY, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
Con_Print("changelevel <levelname> : continue game on a new level\n");
return;
}
- // HACKHACKHACK
- if (!sv.active) {
- SV_Map_f(cmd);
+
+ if (!sv.active)
+ {
+ Con_Printf("You must be running a server to changelevel. Use 'map %s' instead\n", Cmd_Argv(cmd, 1));
return;
}
//===========================================================================
// Disable cheats if sv_cheats is turned off
-static void SV_DisableCheats_c(char *value)
+static void SV_DisableCheats_c(cvar_t *var)
{
prvm_prog_t *prog = SVVM_prog;
int i = 0;
- if (value[0] == '0' && !value[1])
+ if (var->value == 0)
{
while (svs.clients[i].edict)
{
// if running a client, try to send over network so the pause is handled by the server
if (cls.state == ca_connected)
{
- Cmd_ForwardToServer_f(cmd);
+ CL_ForwardToServer_f(cmd);
return;
}
print = Con_Printf;
}
else
{
- Cmd_ForwardToServer_f(cmd);
+ CL_ForwardToServer_f(cmd);
return;
}
}
fromServer = true;
else
{
- Cmd_ForwardToServer_f(cmd);
+ CL_ForwardToServer_f(cmd);
return;
}
}
// if running a client, try to send over network so the client's ping report parser will see the report
if (cls.state == ca_connected)
{
- Cmd_ForwardToServer_f(cmd);
+ CL_ForwardToServer_f(cmd);
return;
}
print = Con_Printf;
char qcstatus[256];
client_t *client;
int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
- void (*print) (const char *fmt, ...);
char ip[48]; // can contain a full length v6 address with [] and a port
int frags;
char vabuf[1024];
- if (cmd->source == src_command)
- {
- // if running a client, try to send over network so the client's status report parser will see the report
- if (cls.state == ca_connected)
- {
- Cmd_ForwardToServer_f(cmd);
- return;
- }
- print = Con_Printf;
- }
- else
- print = SV_ClientPrintf;
-
if (!sv.active)
return;
for (players = 0, i = 0;i < svs.maxclients;i++)
if (svs.clients[i].active)
players++;
- print ("host: %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
- print ("version: %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
- print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
- print ("map: %s\n", sv.name);
- print ("timing: %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
- print ("players: %i active (%i max)\n\n", players, svs.maxclients);
+ SV_ClientPrintf ("host: %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
+ SV_ClientPrintf ("version: %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
+ SV_ClientPrintf ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
+ SV_ClientPrintf ("map: %s\n", sv.name);
+ SV_ClientPrintf ("timing: %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
+ SV_ClientPrintf ("players: %i active (%i max)\n\n", players, svs.maxclients);
if (in == 1)
- print ("^2IP %%pl ping time frags no name\n");
+ SV_ClientPrintf ("^2IP %%pl ping time frags no name\n");
else if (in == 2)
- print ("^5IP no name\n");
+ SV_ClientPrintf ("^5IP no name\n");
for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
{
ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
}
- if(sv_status_privacy.integer && cmd->source != src_command)
+ if(sv_status_privacy.integer && cmd->source != src_command && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
else
strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
{
// LadyHavoc: this is very touchy because we must maintain ProQuake compatible status output
- print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
- print (" %s\n", ip);
+ SV_ClientPrintf ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
+ SV_ClientPrintf (" %s\n", ip);
}
else
{
// LadyHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
- print ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
- print (" %s\n", ip);
+ SV_ClientPrintf ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
+ SV_ClientPrintf (" %s\n", ip);
}
}
else if (in == 1) // extended layout
{
- print ("%s%-47s %2i %4i %2i:%02i:%02i %4i #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, packetloss, ping, hours, minutes, seconds, frags, i+1, client->name);
+ SV_ClientPrintf ("%s%-47s %2i %4i %2i:%02i:%02i %4i #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, packetloss, ping, hours, minutes, seconds, frags, i+1, client->name);
}
else if (in == 2) // reduced layout
{
- print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
+ SV_ClientPrintf ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
+ }
+ }
+}
+
+/*
+======================
+SV_Name_f
+======================
+*/
+static void SV_Name_f(cmd_state_t *cmd)
+{
+ prvm_prog_t *prog = SVVM_prog;
+ int i, j;
+ qboolean valid_colors;
+ const char *newNameSource;
+ char newName[sizeof(host_client->name)];
+
+ if (Cmd_Argc (cmd) == 1)
+ return;
+
+ if (Cmd_Argc (cmd) == 2)
+ newNameSource = Cmd_Argv(cmd, 1);
+ else
+ newNameSource = Cmd_Args(cmd);
+
+ strlcpy(newName, newNameSource, sizeof(newName));
+
+ if (cmd->source == src_command)
+ return;
+
+ if (host.realtime < host_client->nametime && strcmp(newName, host_client->name))
+ {
+ SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
+ return;
+ }
+
+ host_client->nametime = host.realtime + max(0.0f, sv_namechangetimer.value);
+
+ // point the string back at updateclient->name to keep it safe
+ strlcpy (host_client->name, newName, sizeof (host_client->name));
+
+ for (i = 0, j = 0;host_client->name[i];i++)
+ if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
+ host_client->name[j++] = host_client->name[i];
+ host_client->name[j] = 0;
+
+ if(host_client->name[0] == 1 || host_client->name[0] == 2)
+ // may interfere with chat area, and will needlessly beep; so let's add a ^7
+ {
+ memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
+ host_client->name[sizeof(host_client->name) - 1] = 0;
+ host_client->name[0] = STRING_COLOR_TAG;
+ host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
+ }
+
+ u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
+ if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
+ {
+ size_t l;
+ l = strlen(host_client->name);
+ if(l < sizeof(host_client->name) - 1)
+ {
+ // duplicate the color tag to escape it
+ host_client->name[i] = STRING_COLOR_TAG;
+ host_client->name[i+1] = 0;
+ //Con_DPrintf("abuse detected, adding another trailing color tag\n");
+ }
+ else
+ {
+ // remove the last character to fix the color code
+ host_client->name[l-1] = 0;
+ //Con_DPrintf("abuse detected, removing a trailing color tag\n");
+ }
+ }
+
+ // find the last color tag offset and decide if we need to add a reset tag
+ for (i = 0, j = -1;host_client->name[i];i++)
+ {
+ if (host_client->name[i] == STRING_COLOR_TAG)
+ {
+ if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
+ {
+ j = i;
+ // if this happens to be a reset tag then we don't need one
+ if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
+ j = -1;
+ i++;
+ continue;
+ }
+ if (host_client->name[i+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(host_client->name[i+2]) && isxdigit(host_client->name[i+3]) && isxdigit(host_client->name[i+4]))
+ {
+ j = i;
+ i += 4;
+ continue;
+ }
+ if (host_client->name[i+1] == STRING_COLOR_TAG)
+ {
+ i++;
+ continue;
+ }
+ }
+ }
+ // does not end in the default color string, so add it
+ if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
+ memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
+
+ PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
+ if (strcmp(host_client->old_name, host_client->name))
+ {
+ if (host_client->begun)
+ SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
+ strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
+ // send notification to all clients
+ MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
+ MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteString (&sv.reliable_datagram, host_client->name);
+ SV_WriteNetnameIntoDemo(host_client);
+ }
+}
+
+static void SV_Rate_f(cmd_state_t *cmd)
+{
+ int rate;
+
+ rate = atoi(Cmd_Argv(cmd, 1));
+
+ if (cmd->source == src_command)
+ return;
+
+ host_client->rate = rate;
+}
+
+static void SV_Rate_BurstSize_f(cmd_state_t *cmd)
+{
+ int rate_burstsize;
+
+ if (Cmd_Argc(cmd) != 2)
+ return;
+
+ rate_burstsize = atoi(Cmd_Argv(cmd, 1));
+
+ host_client->rate_burstsize = rate_burstsize;
+}
+
+static void SV_Color_f(cmd_state_t *cmd)
+{
+ prvm_prog_t *prog = SVVM_prog;
+
+ int top, bottom, playercolor;
+
+ top = atoi(Cmd_Argv(cmd, 1));
+ bottom = atoi(Cmd_Argv(cmd, 2));
+
+ top &= 15;
+ bottom &= 15;
+
+ playercolor = top*16 + bottom;
+
+ if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
+ {
+ Con_DPrint("Calling SV_ChangeTeam\n");
+ prog->globals.fp[OFS_PARM0] = playercolor;
+ PRVM_serverglobalfloat(time) = sv.time;
+ PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
+ prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
+ }
+ else
+ {
+ if (host_client->edict)
+ {
+ PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
+ PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
+ }
+ host_client->colors = playercolor;
+ if (host_client->old_colors != host_client->colors)
+ {
+ host_client->old_colors = host_client->colors;
+ // send notification to all clients
+ MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
+ MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
}
}
}
Cvar_Set (&cvars_all, "deathmatch", "1");
}
+/*
+======================
+SV_Playermodel_f
+======================
+*/
+// the old playermodel in cl_main has been renamed to __cl_playermodel
+static void SV_Playermodel_f(cmd_state_t *cmd)
+{
+ prvm_prog_t *prog = SVVM_prog;
+ int i, j;
+ char newPath[sizeof(host_client->playermodel)];
+
+ if (Cmd_Argc (cmd) == 1)
+ return;
+
+ if (Cmd_Argc (cmd) == 2)
+ strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
+ else
+ strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
+
+ for (i = 0, j = 0;newPath[i];i++)
+ if (newPath[i] != '\r' && newPath[i] != '\n')
+ newPath[j++] = newPath[i];
+ newPath[j] = 0;
+
+ /*
+ if (host.realtime < host_client->nametime)
+ {
+ SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
+ return;
+ }
+
+ host_client->nametime = host.realtime + 5;
+ */
+
+ // point the string back at updateclient->name to keep it safe
+ strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
+ PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
+ if (strcmp(host_client->old_model, host_client->playermodel))
+ {
+ strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
+ /*// send notification to all clients
+ MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
+ MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
+ }
+}
+
+/*
+======================
+SV_Playerskin_f
+======================
+*/
+static void SV_Playerskin_f(cmd_state_t *cmd)
+{
+ prvm_prog_t *prog = SVVM_prog;
+ int i, j;
+ char newPath[sizeof(host_client->playerskin)];
+
+ if (Cmd_Argc (cmd) == 1)
+ return;
+
+ if (Cmd_Argc (cmd) == 2)
+ strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
+ else
+ strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
+
+ for (i = 0, j = 0;newPath[i];i++)
+ if (newPath[i] != '\r' && newPath[i] != '\n')
+ newPath[j++] = newPath[i];
+ newPath[j] = 0;
+
+ /*
+ if (host.realtime < host_client->nametime)
+ {
+ SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
+ return;
+ }
+
+ host_client->nametime = host.realtime + 5;
+ */
+
+ // point the string back at updateclient->name to keep it safe
+ strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
+ PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
+ if (strcmp(host_client->old_skin, host_client->playerskin))
+ {
+ //if (host_client->begun)
+ // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
+ strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
+ /*// send notification to all clients
+ MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
+ MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+ MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
+ }
+}
+
+/*
+======================
+SV_PModel_f
+LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
+LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
+======================
+*/
+static void SV_PModel_f(cmd_state_t *cmd)
+{
+ prvm_prog_t *prog = SVVM_prog;
+
+ if (Cmd_Argc (cmd) == 1)
+ return;
+
+ PRVM_serveredictfloat(host_client->edict, pmodel) = atoi(Cmd_Argv(cmd, 1));
+}
+
/*
===============================================================================
}
}
+static void SV_SendCvar_f(cmd_state_t *cmd)
+{
+ int i;
+ const char *cvarname;
+ client_t *old;
+
+ if(Cmd_Argc(cmd) != 2)
+ return;
+
+ if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
+ return;
+
+ cvarname = Cmd_Argv(cmd, 1);
+
+ old = host_client;
+ if (cls.state != ca_dedicated)
+ i = 1;
+ else
+ i = 0;
+ for(;i<svs.maxclients;i++)
+ if(svs.clients[i].active && svs.clients[i].netconnection)
+ {
+ host_client = &svs.clients[i];
+ SV_ClientCommands("sendcvar %s\n", cvarname);
+ }
+ host_client = old;
+}
+
void SV_InitOperatorCommands(void)
{
Cvar_RegisterVariable(&sv_cheats);
Cvar_RegisterVariable(&sv_namechangetimer);
Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "status", SV_Status_f, "print server status information");
- Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "map", SV_Map_f, "kick everyone off the server and start a new level");
+ Cmd_AddCommand(CMD_SHARED, "map", SV_Map_f, "kick everyone off the server and start a new level");
Cmd_AddCommand(CMD_SHARED, "restart", SV_Restart_f, "restart current level");
Cmd_AddCommand(CMD_SHARED, "changelevel", SV_Changelevel_f, "change to another level, bringing along all connected clients");
Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "say", SV_Say_f, "send a chat message to everyone on the server");
Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "pause", SV_Pause_f, "pause the game (if the server allows pausing)");
Cmd_AddCommand(CMD_SHARED, "kick", SV_Kick_f, "kick a player off the server by number or name");
Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "ping", SV_Ping_f, "print ping times of all players on the server");
- Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "load", SV_Loadgame_f, "load a saved game file");
+ Cmd_AddCommand(CMD_SHARED, "load", SV_Loadgame_f, "load a saved game file");
Cmd_AddCommand(CMD_SHARED, "save", SV_Savegame_f, "save the game to a file");
Cmd_AddCommand(CMD_SHARED, "viewmodel", SV_Viewmodel_f, "change model of viewthing entity in current level");
Cmd_AddCommand(CMD_SHARED, "viewframe", SV_Viewframe_f, "change animation frame of viewthing entity in current level");
Cmd_AddCommand(CMD_SHARED, "maxplayers", SV_MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
Cmd_AddCommand(CMD_SHARED, "user", SV_User_f, "prints additional information about a player number or name on the scoreboard");
Cmd_AddCommand(CMD_SHARED, "users", SV_Users_f, "prints additional information about all players on the scoreboard");
+ Cmd_AddCommand(CMD_SERVER, "sendcvar", SV_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
// commands that do not have automatic forwarding from cmd_client, these are internal details of the network protocol and not of interest to users (if they know what they are doing they can still use a generic "cmd prespawn" or similar)
Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "prespawn", SV_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "noclip", SV_Noclip_f, "noclip mode (flight without collisions, move through walls)");
Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "give", SV_Give_f, "alter inventory");
Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "kill", SV_Kill_f, "die instantly");
-}
\ No newline at end of file
+
+ Cmd_AddCommand(CMD_USERINFO, "color", SV_Color_f, "change your player shirt and pants colors");
+ Cmd_AddCommand(CMD_USERINFO, "name", SV_Name_f, "change your player name");
+ Cmd_AddCommand(CMD_USERINFO, "rate", SV_Rate_f, "change your network connection speed");
+ Cmd_AddCommand(CMD_USERINFO, "rate_burstsize", SV_Rate_BurstSize_f, "change your network connection speed");
+ Cmd_AddCommand(CMD_USERINFO, "pmodel", SV_PModel_f, "(Nehahra-only) change your player model choice");
+ Cmd_AddCommand(CMD_USERINFO, "playermodel", SV_Playermodel_f, "change your player model");
+ Cmd_AddCommand(CMD_USERINFO, "playerskin", SV_Playerskin_f, "change your player skin number");
+}