#include "sv_demo.h"
#include "image.h"
+#include "utf8lib.h"
+
// for secure rcon authentication
#include "hmac.h"
#include "mdfour.h"
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[22];
+ char ip[48]; // can contain a full length v6 address with [] and a port
int frags;
if (cmd_source == src_command)
for (j = 0;j < NETGRAPH_PACKETS;j++)
if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
packetloss++;
- packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+ packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
}
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);
+ if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
+ {
+ // LordHavoc: 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);
+ }
+ else
+ {
+ // LordHavoc: 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);
+ }
}
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);
+ 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);
}
else if (in == 2) // reduced layout
{
- print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
+ print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
}
}
}
// remove menu
+ if (key_dest == key_menu || key_dest == key_menu_grabbed)
+ MR_ToggleMenu(0);
key_dest = key_game;
svs.serverflags = 0; // haven't completed an episode yet
strlcpy(level, Cmd_Argv(1), sizeof(level));
SV_SpawnServer(level);
if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1");
+ CL_EstablishConnection("local:1", -2);
}
/*
}
// remove menu
+ if (key_dest == key_menu || key_dest == key_menu_grabbed)
+ MR_ToggleMenu(0);
key_dest = key_game;
SV_VM_Begin();
strlcpy(level, Cmd_Argv(1), sizeof(level));
SV_SpawnServer(level);
if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1");
+ CL_EstablishConnection("local:1", -2);
}
/*
}
// remove menu
+ if (key_dest == key_menu || key_dest == key_menu_grabbed)
+ MR_ToggleMenu(0);
key_dest = key_game;
allowcheats = sv_cheats.integer != 0;
strlcpy(mapname, sv.name, sizeof(mapname));
SV_SpawnServer(mapname);
if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1");
+ CL_EstablishConnection("local:1", -2);
}
/*
// will still contain its IP address, so get the address...
InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
if (temp[0])
- CL_EstablishConnection(temp);
+ CL_EstablishConnection(temp, -1);
else
Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
return;
*/
void Host_Connect_f (void)
{
- if (Cmd_Argc() != 2)
+ if (Cmd_Argc() < 2)
{
- Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
+ Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
return;
}
// clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
if(rcon_secure.integer <= 0)
Cvar_SetQuick(&rcon_password, "");
- CL_EstablishConnection(Cmd_Argv(1));
+ CL_EstablishConnection(Cmd_Argv(1), 2);
}
void Host_Savegame_to (const char *name)
{
qfile_t *f;
- int i, lightstyles = 64;
+ int i, k, l, lightstyles = 64;
char comment[SAVEGAME_COMMENT_LENGTH+1];
+ char line[MAX_INPUTLINE];
qboolean isserver;
+ char *s;
// first we have to figure out if this can be saved in 64 lightstyles
// (for Quake compatibility)
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]);
+
+ // darkplaces extension - save buffers
+ for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); i++)
+ {
+ prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
+ if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
+ {
+ for(k = 0; k < stringbuffer->num_strings; k++)
+ {
+ if (!stringbuffer->strings[k])
+ continue;
+ // Parse the string a bit to turn special characters
+ // (like newline, specifically) into escape codes
+ s = stringbuffer->strings[k];
+ for (l = 0;l < (int)sizeof(line) - 2 && *s;)
+ {
+ if (*s == '\n')
+ {
+ line[l++] = '\\';
+ line[l++] = 'n';
+ }
+ else if (*s == '\r')
+ {
+ line[l++] = '\\';
+ line[l++] = 'r';
+ }
+ else if (*s == '\\')
+ {
+ line[l++] = '\\';
+ line[l++] = '\\';
+ }
+ else if (*s == '"')
+ {
+ line[l++] = '\\';
+ line[l++] = '"';
+ }
+ else
+ line[l++] = *s;
+ s++;
+ }
+ line[l] = '\0';
+ FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
+ }
+ }
+ }
FS_Printf(f,"*/\n");
#endif
Host_Loadgame_f
===============
*/
+
void Host_Loadgame_f (void)
{
char filename[MAX_QPATH];
const char *t;
char *text;
prvm_edict_t *ent;
- int i;
+ int i, k;
int entnum;
int version;
float spawn_parms[NUM_SPAWN_PARMS];
+ prvm_stringbuffer_t *stringbuffer;
+ size_t alloclen;
if (Cmd_Argc() != 2)
{
CL_Disconnect ();
// remove menu
+ if (key_dest == key_menu || key_dest == key_menu_grabbed)
+ MR_ToggleMenu(0);
key_dest = key_game;
cls.demonum = -1; // stop demo loop in case this fails
}
}
+ // unlink all entities
+ World_UnlinkAll(&sv.world);
+
// load the edicts out of the savegame file
end = t;
for (;;)
// parse the global vars
PRVM_ED_ParseGlobals (start);
+
+ // restore the autocvar globals
+ Cvar_UpdateAllAutoCvars();
}
else
{
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);
+ sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
}
else
Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
else
Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
}
+ else if (!strcmp(com_token, "sv.bufstr"))
+ {
+ COM_ParseToken_Simple(&t, false, false);
+ i = atoi(com_token);
+ COM_ParseToken_Simple(&t, false, false);
+ k = atoi(com_token);
+ COM_ParseToken_Simple(&t, false, false);
+ stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
+ // VorteX: nasty code, cleanup required
+ // create buffer at this index
+ if(!stringbuffer)
+ stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
+ if (!stringbuffer)
+ Con_Printf("cant write string %i into buffer %i\n", k, i);
+ else
+ {
+ // code copied from VM_bufstr_set
+ // expand buffer
+ if (stringbuffer->max_strings <= i)
+ {
+ char **oldstrings = stringbuffer->strings;
+ stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
+ while (stringbuffer->max_strings <= i)
+ stringbuffer->max_strings *= 2;
+ stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
+ if (stringbuffer->num_strings > 0)
+ memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
+ if (oldstrings)
+ Mem_Free(oldstrings);
+ }
+ // allocate string
+ stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
+ if(stringbuffer->strings[k])
+ Mem_Free(stringbuffer->strings[k]);
+ stringbuffer->strings[k] = NULL;
+ alloclen = strlen(com_token) + 1;
+ stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+ memcpy(stringbuffer->strings[k], com_token, alloclen);
+ }
+ }
// skip any trailing text or unrecognized commands
while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
;
// make sure we're connected to loopback
if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1");
+ CL_EstablishConnection("local:1", -2);
}
//============================================================================
host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
}
- COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
+ 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;
Host_Playermodel_f
======================
*/
-cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
+cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz/Xonotic (changed by playermodel command)"};
// the old cl_playermodel in cl_main has been renamed to __cl_playermodel
void Host_Playermodel_f (void)
{
Host_Playerskin_f
======================
*/
-cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
+cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
void Host_Playerskin_f (void)
{
int i, j;
*/
void Host_Kick_f (void)
{
- char *who;
+ const char *who;
const char *message = NULL;
client_t *save;
int i;
SZ_Clear(&net_message);
MSG_WriteLong (&net_message, 0);
MSG_WriteByte (&net_message, CCREQ_RCON);
- SZ_Write(&net_message, (void*)rcon_password.string, n);
+ SZ_Write(&net_message, (const unsigned char*)rcon_password.string, n);
+ MSG_WriteByte (&net_message, 0); // terminate the (possibly partial) string
MSG_WriteString (&net_message, Cmd_Args());
- *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
SZ_Clear (&net_message);
}
*/
void Host_Pings_f (void)
{
- int i, j, ping, packetloss;
+ int i, j, ping, packetloss, movementloss;
char temp[128];
if (!host_client->netconnection)
for (i = 0;i < svs.maxclients;i++)
{
packetloss = 0;
+ movementloss = 0;
if (svs.clients[i].netconnection)
+ {
for (j = 0;j < NETGRAPH_PACKETS;j++)
if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
packetloss++;
- packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+ for (j = 0;j < NETGRAPH_PACKETS;j++)
+ if (svs.clients[i].movement_count[j] < 0)
+ movementloss++;
+ }
+ packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
+ movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
ping = (int)floor(svs.clients[i].ping*1000+0.5);
ping = bound(0, ping, 9999);
if (sv.protocol == PROTOCOL_QUAKEWORLD)
else
{
// write the string into the packet as multiple unterminated strings to avoid needing a local buffer
- dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
+ if(movementloss)
+ dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
+ else
+ dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
}
}
void Host_PingPLReport_f(void)
{
+ char *errbyte;
int i;
int l = Cmd_Argc();
if (l > cl.maxclients)
for (i = 0;i < l;i++)
{
cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
- cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
+ cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
+ if(errbyte && *errbyte == ',')
+ cl.scores[i].qw_movementloss = atoi(errbyte + 1);
+ else
+ cl.scores[i].qw_movementloss = 0;
}
}