#include "sv_demo.h"
#include "image.h"
+// for secure rcon authentication
+#include "hmac.h"
+#include "mdfour.h"
+#include <time.h>
+
int current_skill;
cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
-cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands"};
+cvar_t sv_status_show_qcstatus = {CVAR_SAVE, "sv_status_show_qcstatus", "0", "show the 'qcstatus' field in status replies, not the 'frags' field. Turn this on if your mod uses this field, and the 'frags' field on the other hand has no meaningful value."};
+cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
+cvar_t rcon_secure = {CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication (1 = time based, 2 = challenge based); NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
+cvar_t rcon_secure_challengetimeout = {0, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"};
cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
qboolean allowcheats = false;
extern qboolean host_shuttingdown;
+extern cvar_t developer_entityparsing;
/*
==================
*/
void Host_Status_f (void)
{
+ char qcstatus[256];
client_t *client;
- int seconds, minutes, hours = 0, j, players;
+ int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
void (*print) (const char *fmt, ...);
+ char ip[22];
+ int frags;
if (cmd_source == src_command)
{
if (!sv.active)
return;
+
+ if(cmd_source == src_command)
+ SV_VM_Begin();
+
+ in = 0;
+ if (Cmd_Argc() == 2)
+ {
+ if (strcmp(Cmd_Argv(1), "1") == 0)
+ in = 1;
+ else if (strcmp(Cmd_Argv(1), "2") == 0)
+ in = 2;
+ }
- for (players = 0, j = 0;j < svs.maxclients;j++)
- if (svs.clients[j].active)
+ for (players = 0, i = 0;i < svs.maxclients;i++)
+ if (svs.clients[i].active)
players++;
print ("host: %s\n", Cvar_VariableString ("hostname"));
print ("version: %s build %s\n", gamename, buildstring);
print ("map: %s\n", sv.name);
print ("timing: %s\n", Host_TimingReport());
print ("players: %i active (%i max)\n\n", players, svs.maxclients);
- for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
+
+ if (in == 1)
+ print ("^2IP %%pl ping time frags no name\n");
+ else if (in == 2)
+ print ("^5IP no name\n");
+
+ for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
{
if (!client->active)
continue;
- seconds = (int)(realtime - client->connecttime);
- minutes = seconds / 60;
- if (minutes)
+
+ ++k;
+
+ if (in == 0 || in == 1)
{
- seconds -= (minutes * 60);
- hours = minutes / 60;
- if (hours)
- minutes -= (hours * 60);
+ seconds = (int)(realtime - client->connecttime);
+ minutes = seconds / 60;
+ if (minutes)
+ {
+ seconds -= (minutes * 60);
+ hours = minutes / 60;
+ if (hours)
+ minutes -= (hours * 60);
+ }
+ else
+ hours = 0;
+
+ packetloss = 0;
+ if (client->netconnection)
+ for (j = 0;j < NETGRAPH_PACKETS;j++)
+ if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
+ packetloss++;
+ packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+ ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
}
- else
- hours = 0;
- print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, client->frags, hours, minutes, seconds);
+
if(sv_status_privacy.integer && cmd_source != src_command)
- print (" %s\n", client->netconnection ? "hidden" : "botclient");
+ strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
else
- print (" %s\n", client->netconnection ? client->netconnection->address : "botclient");
+ strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
+
+ frags = client->frags;
+
+ if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
+ {
+ const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
+ if(str && *str)
+ {
+ char *p;
+ const char *q;
+ p = qcstatus;
+ for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+ if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
+ *p++ = *q;
+ *p = 0;
+ if(*qcstatus)
+ frags = atoi(qcstatus);
+ }
+ }
+
+ if (in == 0) // default layout
+ {
+ print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
+ print (" %s\n", ip);
+ }
+ else if (in == 1) // extended layout
+ {
+ print ("%s%-21s %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%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
+ }
}
+
+ if(cmd_source == src_command)
+ SV_VM_End();
}
CL_Disconnect ();
Host_ShutdownServer();
+ if(svs.maxclients != svs.maxclients_next)
+ {
+ svs.maxclients = svs.maxclients_next;
+ if (svs.clients)
+ Mem_Free(svs.clients);
+ svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
+ }
+
// remove menu
key_dest = key_game;
Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
return;
}
+ // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
+ if(!rcon_secure.integer)
+ Cvar_SetQuick(&rcon_password, "");
CL_EstablishConnection(Cmd_Argv(1));
}
void Host_Savegame_to (const char *name)
{
qfile_t *f;
- int i;
+ int i, lightstyles = 64;
char comment[SAVEGAME_COMMENT_LENGTH+1];
qboolean isserver;
+ // first we have to figure out if this can be saved in 64 lightstyles
+ // (for Quake compatibility)
+ for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
+ if (sv.lightstyles[i][0])
+ lightstyles = i+1;
+
isserver = !strcmp(PRVM_NAME, "server");
Con_Printf("Saving game to %s...\n", name);
// convert space to _ to make stdio happy
// LordHavoc: convert control characters to _ as well
for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
- if (comment[i] <= ' ')
+ if (ISWHITESPACEORCONTROL(comment[i]))
comment[i] = '_';
comment[SAVEGAME_COMMENT_LENGTH] = '\0';
}
// write the light styles
- for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+ for (i=0 ; i<lightstyles ; i++)
{
if (isserver && sv.lightstyles[i][0])
FS_Printf(f, "%s\n", sv.lightstyles[i]);
PRVM_ED_WriteGlobals (f);
for (i=0 ; i<prog->num_edicts ; i++)
{
+ FS_Printf(f,"// edict %d\n", i);
//Con_Printf("edict %d...\n", i);
PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
}
+#if 1
+ FS_Printf(f,"/*\n");
+ FS_Printf(f,"// DarkPlaces extended savegame\n");
+ // darkplaces extension - extra lightstyles, support for color lightstyles
+ for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+ if (isserver && sv.lightstyles[i][0])
+ FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
+
+ // darkplaces extension - model precaches
+ for (i=1 ; i<MAX_MODELS ; i++)
+ if (sv.model_precache[i][0])
+ FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
+
+ // darkplaces extension - sound precaches
+ for (i=1 ; i<MAX_SOUNDS ; i++)
+ if (sv.sound_precache[i][0])
+ FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
+ FS_Printf(f,"*/\n");
+#endif
+
FS_Close (f);
Con_Print("done.\n");
}
char mapname[MAX_QPATH];
float time;
const char *start;
+ const char *end;
const char *t;
- const char *oldt;
char *text;
prvm_edict_t *ent;
int i;
return;
}
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading version\n");
+
// version
COM_ParseToken_Simple(&t, false, false);
version = atoi(com_token);
return;
}
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading description\n");
+
// description
COM_ParseToken_Simple(&t, false, false);
current_skill = (int)(atof(com_token) + 0.5);
Cvar_SetValue ("skill", (float)current_skill);
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading mapname\n");
+
// mapname
COM_ParseToken_Simple(&t, false, false);
strlcpy (mapname, com_token, sizeof(mapname));
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading time\n");
+
// time
COM_ParseToken_Simple(&t, false, false);
time = atof(com_token);
allowcheats = sv_cheats.integer != 0;
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: spawning server\n");
+
SV_SpawnServer (mapname);
if (!sv.active)
{
sv.paused = true; // pause until all clients connect
sv.loadgame = true;
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading light styles\n");
+
// load the light styles
+ SV_VM_Begin();
+ // -1 is the globals
+ entnum = -1;
+
for (i = 0;i < MAX_LIGHTSTYLES;i++)
{
// light style
- oldt = t;
+ start = t;
COM_ParseToken_Simple(&t, false, false);
// if this is a 64 lightstyle savegame produced by Quake, stop now
- // we have to check this because darkplaces saves 256 lightstyle savegames
+ // we have to check this because darkplaces may save more than 64
if (com_token[0] == '{')
{
- t = oldt;
+ t = start;
break;
}
strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
}
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: skipping until globals\n");
+
// now skip everything before the first opening brace
// (this is for forward compatibility, so that older versions (at
// least ones with this fix) can load savegames with extra data before the
// first brace, as might be produced by a later engine version)
- for(;;)
+ for (;;)
{
- oldt = t;
- COM_ParseToken_Simple(&t, false, false);
+ start = t;
+ if (!COM_ParseToken_Simple(&t, false, false))
+ break;
if (com_token[0] == '{')
{
- t = oldt;
+ t = start;
break;
}
}
// load the edicts out of the savegame file
- SV_VM_Begin();
- // -1 is the globals
- entnum = -1;
+ end = t;
for (;;)
{
start = t;
if (entnum == -1)
{
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading globals\n");
+
// parse the global vars
PRVM_ED_ParseGlobals (start);
}
ent = PRVM_EDICT_NUM(entnum);
memset (ent->fields.server, 0, prog->progs->entityfields * 4);
ent->priv.server->free = false;
+
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
+
PRVM_ED_ParseEdict (start, ent);
// link it into the bsp tree
if (!ent->priv.server->free)
- SV_LinkEdict (ent, false);
+ SV_LinkEdict(ent);
}
+ end = t;
entnum++;
}
- Mem_Free(text);
prog->num_edicts = entnum;
sv.time = time;
for (i = 0;i < NUM_SPAWN_PARMS;i++)
svs.clients[0].spawn_parms[i] = spawn_parms[i];
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: skipping until extended data\n");
+
+ // read extended data if present
+ // the extended data is stored inside a /* */ comment block, which the
+ // parser intentionally skips, so we have to check for it manually here
+ if(end)
+ {
+ while (*end == '\r' || *end == '\n')
+ end++;
+ if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
+ {
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: loading extended data\n");
+
+ Con_Printf("Loading extended DarkPlaces savegame\n");
+ t = end + 2;
+ memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
+ memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
+ memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
+ while (COM_ParseToken_Simple(&t, false, false))
+ {
+ if (!strcmp(com_token, "sv.lightstyles"))
+ {
+ COM_ParseToken_Simple(&t, false, false);
+ i = atoi(com_token);
+ COM_ParseToken_Simple(&t, false, false);
+ if (i >= 0 && i < MAX_LIGHTSTYLES)
+ strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
+ else
+ Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
+ }
+ else if (!strcmp(com_token, "sv.model_precache"))
+ {
+ COM_ParseToken_Simple(&t, false, false);
+ i = atoi(com_token);
+ COM_ParseToken_Simple(&t, false, false);
+ if (i >= 0 && i < MAX_MODELS)
+ {
+ strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
+ sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
+ }
+ else
+ Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
+ }
+ else if (!strcmp(com_token, "sv.sound_precache"))
+ {
+ COM_ParseToken_Simple(&t, false, false);
+ i = atoi(com_token);
+ COM_ParseToken_Simple(&t, false, false);
+ if (i >= 0 && i < MAX_SOUNDS)
+ strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
+ else
+ Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
+ }
+ // skip any trailing text or unrecognized commands
+ while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
+ ;
+ }
+ }
+ }
+ Mem_Free(text);
+
+ if(developer_entityparsing.integer)
+ Con_Printf("Host_Loadgame_f: finished\n");
+
SV_VM_End();
// make sure we're connected to loopback
{
int i, j;
qboolean valid_colors;
+ const char *newNameSource;
char newName[sizeof(host_client->name)];
if (Cmd_Argc () == 1)
{
- Con_Printf("\"name\" is \"%s\"\n", cl_name.string);
+ Con_Printf("name: %s\n", cl_name.string);
return;
}
if (Cmd_Argc () == 2)
- strlcpy (newName, Cmd_Argv(1), sizeof (newName));
+ newNameSource = Cmd_Argv(1);
else
- strlcpy (newName, Cmd_Args(), sizeof (newName));
+ newNameSource = Cmd_Args();
+
+ strlcpy(newName, newNameSource, sizeof(newName));
if (cmd_source == src_command)
{
Cvar_Set ("_cl_name", newName);
+ if (strlen(newNameSource) >= sizeof(newName)) // overflowed
+ {
+ Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
+ Con_Printf("name: %s\n", cl_name.string);
+ }
return;
}
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++;
if (strcmp(host_client->old_name, host_client->name))
{
if (host_client->spawned)
- SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
+ 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);
if (!e)
return;
- m = Mod_ForName (Cmd_Argv(1), false, true, false);
+ m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
if (!m || !m->loaded || !m->Draw)
{
Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
if(svs.clients[i].active && svs.clients[i].netconnection)
{
host_client = &svs.clients[i];
- Host_ClientCommands(va("sendcvar %s\n", cvarname));
+ Host_ClientCommands("sendcvar %s\n", cvarname);
}
host_client = old;
}
if (Cmd_Argc() != 2)
{
- Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
+ Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
return;
}
if (sv.active)
{
Con_Print("maxplayers can not be changed while a server is running.\n");
- return;
+ Con_Print("It will be changed on next server startup (\"map\" command).\n");
}
n = atoi(Cmd_Argv(1));
n = bound(1, n, MAX_SCOREBOARD);
Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
- if (svs.clients)
- Mem_Free(svs.clients);
- svs.maxclients = n;
- svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
+ svs.maxclients_next = n;
if (n == 1)
Cvar_Set ("deathmatch", "0");
else
Cvar_Set ("deathmatch", "1");
}
+/*
+=====================
+Host_PQRcon_f
+
+ProQuake rcon support
+=====================
+*/
+void Host_PQRcon_f (void)
+{
+ int i;
+ lhnetaddress_t to;
+ lhnetsocket_t *mysocket;
+ char peer_address[64];
+
+ if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer)
+ {
+ Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
+ return;
+ }
+
+ for (i = 0;rcon_password.string[i];i++)
+ {
+ if (ISWHITESPACE(rcon_password.string[i]))
+ {
+ Con_Printf("rcon_password is not allowed to have any whitespace.\n");
+ return;
+ }
+ }
+
+ if (cls.netcon)
+ {
+ InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
+ }
+ else
+ {
+ if (!rcon_address.string[0])
+ {
+ Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
+ return;
+ }
+ strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
+ }
+ LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
+ mysocket = NetConn_ChooseClientSocketForAddress(&to);
+ if (mysocket)
+ {
+ SZ_Clear(&net_message);
+ MSG_WriteLong (&net_message, 0);
+ MSG_WriteByte (&net_message, CCREQ_RCON);
+ MSG_WriteString (&net_message, rcon_password.string);
+ MSG_WriteString (&net_message, Cmd_Args());
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
+ SZ_Clear (&net_message);
+ }
+}
+
//=============================================================================
// QuakeWorld commands
for (i = 0;rcon_password.string[i];i++)
{
- if (rcon_password.string[i] <= ' ')
+ if (ISWHITESPACE(rcon_password.string[i]))
{
Con_Printf("rcon_password is not allowed to have any whitespace.\n");
return;
LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
}
mysocket = NetConn_ChooseClientSocketForAddress(&to);
- if (mysocket)
+ if (mysocket && Cmd_Args()[0])
{
// simply put together the rcon packet and send it
- NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
+ if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
+ {
+ if(cls.rcon_commands[cls.rcon_ringpos][0])
+ {
+ char s[128];
+ LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
+ Con_Printf("rcon to %s (for command %s) failed: too many buffered commands (possibly increase MAX_RCONS)\n", s, cls.rcon_commands[cls.rcon_ringpos]);
+ cls.rcon_commands[cls.rcon_ringpos][0] = 0;
+ --cls.rcon_trying;
+ }
+ for (i = 0;i < MAX_RCONS;i++)
+ if(cls.rcon_commands[i][0])
+ if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
+ break;
+ ++cls.rcon_trying;
+ if(i >= MAX_RCONS)
+ NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
+ strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
+ cls.rcon_addresses[cls.rcon_ringpos] = to;
+ cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
+ cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
+ }
+ else if(rcon_secure.integer)
+ {
+ char buf[1500];
+ char argbuf[1500];
+ dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
+ memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
+ if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
+ {
+ buf[40] = ' ';
+ strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
+ NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
+ }
+ }
+ else
+ {
+ NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
+ }
}
}
in = Cmd_Argv(2);
out = send+4;
- send[0] = send[1] = send[2] = send[3] = 0xff;
+ send[0] = send[1] = send[2] = send[3] = -1;
l = (int)strlen (in);
for (i=0 ; i<l ; i++)
packetloss = 0;
if (svs.clients[i].netconnection)
for (j = 0;j < NETGRAPH_PACKETS;j++)
- if (svs.clients[i].netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
+ if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
packetloss++;
packetloss = packetloss * 100 / NETGRAPH_PACKETS;
ping = (int)floor(svs.clients[i].ping*1000+0.5);
Cvar_RegisterVariable (&rcon_password);
Cvar_RegisterVariable (&rcon_address);
- Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
+ Cvar_RegisterVariable (&rcon_secure);
+ Cvar_RegisterVariable (&rcon_secure_challengetimeout);
+ Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); note: if rcon_secure is set, client and server clocks must be synced e.g. via NTP");
+ Cmd_AddCommand ("srcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); this always works as if rcon_secure is set; note: client and server clocks must be synced e.g. via NTP");
+ Cmd_AddCommand ("pqrcon", Host_PQRcon_f, "sends a command to a proquake server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
Cvar_RegisterVariable(&sv_cheats);
Cvar_RegisterVariable(&sv_adminnick);
Cvar_RegisterVariable(&sv_status_privacy);
+ Cvar_RegisterVariable(&sv_status_show_qcstatus);
}
void Host_NoOperation_f(void)