// sv_main.c -- server main program
#include "quakedef.h"
+#include "sv_demo.h"
#include "libcurl.h"
static void SV_SaveEntFile_f(void);
cvar_t sv_clmovement_enable = {0, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"};
cvar_t sv_clmovement_minping = {0, "sv_clmovement_minping", "0", "if client ping is below this time in milliseconds, then their ability to use cl_movement prediction is disabled for a while (as they don't need it)"};
cvar_t sv_clmovement_minping_disabletime = {0, "sv_clmovement_minping_disabletime", "1000", "when client falls below minping, disable their prediction for this many milliseconds (should be at least 1000 or else their prediction may turn on/off frequently)"};
-cvar_t sv_clmovement_waitforinput = {0, "sv_clmovement_waitforinput", "16", "when a client does not send input for this many frames, force them to move anyway (unlike QuakeWorld)"};
+cvar_t sv_clmovement_waitforinput = {0, "sv_clmovement_waitforinput", "4", "when a client does not send input for this many frames, force them to move anyway (unlike QuakeWorld)"};
cvar_t sv_cullentities_nevercullbmodels = {0, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"};
cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"};
cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
+cvar_t sv_autodemo_perclient = {CVAR_SAVE, "sv_autodemo_perclient", "0", "set to 1 to enable autorecorded per-client demos (they'll start to record at the beginning of a match)"};
+cvar_t sv_autodemo_perclient_nameformat = {CVAR_SAVE, "sv_autodemo_perclient_nameformat", "sv_autodemos/%Y-%m-%d_%H-%M", "The format of the sv_autodemo_perclient filename, followed by the map name, the IP address + port number, and the client number, separated by underscores" };
+
+
server_t sv;
server_static_t svs;
}
Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
+ Cvar_RegisterVariable (&sv_autodemo_perclient);
+ Cvar_RegisterVariable (&sv_autodemo_perclient_nameformat);
+
// any special defaults for gamemodes go here
if (gamemode == GAME_HIPNOTIC)
{
}
else
MSG_WriteShort (&sv.datagram, (ent<<3) | channel);
- if (field_mask & SND_LARGESOUND)
+ if ((field_mask & SND_LARGESOUND) || sv.protocol == PROTOCOL_NEHAHRABJP2)
MSG_WriteShort (&sv.datagram, sound_num);
else
MSG_WriteByte (&sv.datagram, sound_num);
// LordHavoc: clear entityframe tracking
client->latestframenum = 0;
+ // initialize the movetime, so a speedhack can't make use of the time before this client joined
+ client->cmd.time = sv.time;
+
if (client->entitydatabase)
EntityFrame_FreeDatabase(client->entitydatabase);
if (client->entitydatabase4)
memset(client->stats, 0, sizeof(client->stats));
memset(client->statsdeltabits, 0, sizeof(client->statsdeltabits));
- if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
{
if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
client->num_pings = 0;
#endif
client->ping = 0;
+
+ SV_StopDemoRecording(client); // to split up demos into different files
+ if(sv_autodemo_perclient.integer && client->netconnection)
+ {
+ char demofile[MAX_OSPATH];
+ char levelname[MAX_QPATH];
+ char ipaddress[MAX_QPATH];
+ size_t i;
+
+ // start a new demo file
+ strlcpy(levelname, FS_FileWithoutPath(sv.worldmodel->name), sizeof(levelname));
+ if (strrchr(levelname, '.'))
+ *(strrchr(levelname, '.')) = 0;
+
+ LHNETADDRESS_ToString(&(client->netconnection->peeraddress), ipaddress, sizeof(ipaddress), true);
+ for(i = 0; ipaddress[i]; ++i)
+ if(!isalnum(ipaddress[i]))
+ ipaddress[i] = '-';
+ dpsnprintf (demofile, sizeof(demofile), "%s_%s_%s_%d.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, ipaddress, PRVM_NUM_FOR_EDICT(client->edict));
+
+ SV_StartDemoRecording(client, demofile, -1);
+ }
}
/*
VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv.writeentitiestoclient_testeye);
sv.writeentitiestoclient_pvsbytes = 0;
if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
- sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_testeye, 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs));
+ sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_testeye, 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs), false);
sv.writeentitiestoclient_cliententitynumber = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
{
if (ent->fields.server->punchangle[i])
bits |= (SU_PUNCH1<<i);
- if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
if (punchvector[i])
bits |= (SU_PUNCHVEC1<<i);
if (ent->fields.server->velocity[i])
statsf[STAT_FRAGLIMIT] = fraglimit.value;
statsf[STAT_TIMELIMIT] = timelimit.value;
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
{
if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
bits |= SU_ITEMS;
{
if (bits & (SU_PUNCH1<<i))
{
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
else
MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
}
if (bits & (SU_VELOCITY1<<i))
{
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
else
MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
if (bits & SU_VIEWZOOM)
MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
}
- else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
+ else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
{
if (bits & SU_WEAPONFRAME)
MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
if (bits & SU_ARMOR)
MSG_WriteByte (msg, stats[STAT_ARMOR]);
if (bits & SU_WEAPON)
- MSG_WriteByte (msg, stats[STAT_WEAPON]);
+ {
+ if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ MSG_WriteShort (msg, stats[STAT_WEAPON]);
+ else
+ MSG_WriteByte (msg, stats[STAT_WEAPON]);
+ }
MSG_WriteShort (msg, stats[STAT_HEALTH]);
MSG_WriteByte (msg, stats[STAT_AMMO]);
MSG_WriteByte (msg, stats[STAT_SHELLS]);
maxsize = sizeof(sv_sendclientdatagram_buf);
maxsize2 = sizeof(sv_sendclientdatagram_buf);
}
- else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
+ else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
{
// no packet size limit support on older protocols because DP1-4 kick
// the client off if they overflow, and quake protocol shows less than
SZ_Write (&msg, data, downloadsize);
}
+ // reliable only if none is in progress
+ if(client->sendsignon != 2 && !client->netconnection->sendMessageLength)
+ SV_WriteDemoMessage(client, &(client->netconnection->message));
+ // unreliable
+ SV_WriteDemoMessage(client, &msg);
+
// send the datagram
NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate, client->sendsignon == 2);
if (client->sendsignon == 1 && !client->netconnection->message.cursize)
MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
MSG_WriteByte (&sv.reliable_datagram, i);
MSG_WriteString (&sv.reliable_datagram, host_client->name);
+ SV_WriteNetnameIntoDemo(host_client);
}
// DP_SV_CLIENTCOLORS
*/
int SV_ModelIndex(const char *s, int precachemode)
{
- int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
+ int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_MODELS);
char filename[MAX_QPATH];
if (!s || !*s)
return 0;
{
if (precachemode)
{
- if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
+ if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
{
Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
return 0;
*/
int SV_SoundIndex(const char *s, int precachemode)
{
- int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS);
+ int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_SOUNDS);
char filename[MAX_QPATH];
if (!s || !*s)
return 0;
{
if (precachemode)
{
- if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
+ if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
{
Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
return 0;
argc = 0;
for (;;)
{
- if (!COM_ParseToken_Simple(&text, true) || !strcmp(com_token, "\n"))
+ if (!COM_ParseToken_Simple(&text, true, false) || !strcmp(com_token, "\n"))
break;
if (argc < 16)
{
large = false;
if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
+ {
large = true;
+ if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ large = false;
+ }
// add to the message
if (large)
MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
}
+ else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ {
+ MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
+ MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
+ }
else
{
MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
Mod_PurgeUnused();
// create a baseline for more efficient communications
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
SV_CreateBaseline ();
// send serverinfo to all connected clients, and set up botclients coming back from a level change
prog->limit_edicts = 2048; // guessing
else if (sv.protocol == PROTOCOL_NEHAHRAMOVIE)
prog->limit_edicts = 2048; // guessing!
+ else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ prog->limit_edicts = 4096; // guessing!
else
prog->limit_edicts = MAX_EDICTS;
prog->reserved_edicts = svs.maxclients;