#include "quakedef.h"
#include "sv_demo.h"
#include "libcurl.h"
+#include "csprogs.h"
static void SV_SaveEntFile_f(void);
static void SV_StartDownload_f(void);
void VM_CustomStats_Clear (void);
void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
-void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_state_t *states);
+void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states);
cvar_t coop = {0, "coop","0", "coop mode, 0 = no coop, 1 = coop mode, multiple players playing through the singleplayer game (coop mode also shuts off deathmatch)"};
cvar_t deathmatch = {0, "deathmatch","0", "deathmatch mode, values depend on mod but typically 0 = no deathmatch, 1 = normal deathmatch with respawning weapons, 2 = weapons stay (players can only pick up new weapons)"};
cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"};
cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
+cvar_t sv_cullentities_trace_delay_players = {0, "sv_cullentities_trace_delay_players", "0.2", "number of seconds until the entity gets actually culled if it is a player entity"};
cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
cvar_t sv_cullentities_trace_prediction = {0, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"};
cvar_t sv_cullentities_trace_samples = {0, "sv_cullentities_trace_samples", "1", "number of samples to test for entity culling"};
cvar_t sv_cullentities_trace_samples_extra = {0, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
+cvar_t sv_cullentities_trace_samples_players = {0, "sv_cullentities_trace_samples_players", "8", "number of samples to test for entity culling when the entity is a player entity"};
cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
cvar_t sv_echobprint = {CVAR_SAVE, "sv_echobprint", "1", "prints gamecode bprint() calls to server console"};
cvar_t sv_edgefriction = {0, "edgefriction", "2", "how much you slow down when nearing a ledge you might fall off"};
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); set it to 2 to also record client->server packets (for debugging)"};
-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" };
+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 client number and the IP address + port number, separated by underscores" };
server_t sv;
{ev_entity, "nodrawtoclient"},
{ev_entity, "tag_entity"},
{ev_entity, "viewmodelforclient"},
+ {ev_float, "SendFlags"},
{ev_float, "Version"},
{ev_float, "alpha"},
{ev_float, "ammo_cells1"},
Cvar_RegisterVariable (&sv_cullentities_stats);
Cvar_RegisterVariable (&sv_cullentities_trace);
Cvar_RegisterVariable (&sv_cullentities_trace_delay);
+ Cvar_RegisterVariable (&sv_cullentities_trace_delay_players);
Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
Cvar_RegisterVariable (&sv_cullentities_trace_prediction);
Cvar_RegisterVariable (&sv_cullentities_trace_samples);
Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
+ Cvar_RegisterVariable (&sv_cullentities_trace_samples_players);
Cvar_RegisterVariable (&sv_debugmove);
Cvar_RegisterVariable (&sv_echobprint);
Cvar_RegisterVariable (&sv_edgefriction);
}
// reset csqc entity versions
- memset(client->csqcentityversion, 0, sizeof(client->csqcentityversion));
+ for (i = 0;i < prog->max_edicts;i++)
+ {
+ client->csqcentityscope[i] = 0;
+ client->csqcentitysendflags[i] = 0xFFFFFF;
+ }
SZ_Clear (&client->netconnection->message);
MSG_WriteByte (&client->netconnection->message, svc_print);
dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
MSG_WriteString (&client->netconnection->message,message);
+ 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_%d_%s.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, PRVM_NUM_FOR_EDICT(client->edict), ipaddress);
+
+ SV_StartDemoRecording(client, demofile, -1);
+ }
+
//[515]: init csprogs according to version of svprogs, check the crc, etc.
if (sv.csqc_progname[0])
{
MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize));
MSG_WriteByte (&client->netconnection->message, svc_stufftext);
MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
+
+ if(client->sv_demo_file != NULL)
+ {
+ void *csqcbuf;
+ fs_offset_t csqclen;
+ int csqccrc;
+ int i;
+ char buf[NET_MAXMESSAGE];
+ sizebuf_t sb;
+
+ csqcbuf = FS_LoadFile(sv.csqc_progname, tempmempool, true, &csqclen);
+ if(csqcbuf)
+ {
+ csqccrc = CRC_Block(csqcbuf, csqclen);
+ sb.data = (void *) buf;
+ sb.maxsize = sizeof(buf);
+ i = 0;
+ while(MakeDownloadPacket(sv.csqc_progname, csqcbuf, csqclen, csqccrc, i++, &sb, sv.protocol))
+ SV_WriteDemoMessage(client, &sb, false);
+ Mem_Free(csqcbuf);
+ }
+ }
+
//[515]: init stufftext string (it is sent before svc_serverinfo)
val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd);
if (val)
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);
- }
}
/*
static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int enumber)
{
int i;
+ unsigned int sendflags;
+ unsigned int version;
unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
unsigned int customizeentityforclient;
float f;
vec3_t cullmins, cullmaxs;
- model_t *model;
- prvm_eval_t *val;
+ dp_model_t *model;
+ prvm_eval_t *val, *val2;
// this 2 billion unit check is actually to detect NAN origins
// (we really don't want to send those)
}
}
+ // we need to do some csqc entity upkeep here
+ // get self.SendFlags and clear them
+ // (to let the QC know that they've been read)
+ val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendEntity);
+ if (val->function)
+ {
+ val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendFlags);
+ sendflags = (unsigned int)val->_float;
+ val->_float = 0;
+ // legacy self.Version system
+ val2 = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.Version);
+ if (val2->_float)
+ {
+ version = (unsigned int)val2->_float;
+ if (sv.csqcentityversion[enumber] != version)
+ sendflags = 0xFFFFFF;
+ sv.csqcentityversion[enumber] = version;
+ }
+ // move sendflags into the per-client sendflags
+ if (sendflags)
+ for (i = 0;i < svs.maxclients;i++)
+ svs.clients[i].csqcentitysendflags[enumber] |= sendflags;
+ }
+
return true;
}
void SV_MarkWriteEntityStateToClient(entity_state_t *s)
{
int isbmodel;
- model_t *model;
+ dp_model_t *model;
prvm_edict_t *ed;
if (sv.sententitiesconsideration[s->number] == sv.sententitiesmark)
return;
// or not seen by random tracelines
if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel->brush.TraceLineOfSight)
{
- int samples = s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer;
+ int samples =
+ s->number <= svs.maxclients
+ ? sv_cullentities_trace_samples_players.integer
+ :
+ s->specialvisibilityradius
+ ? sv_cullentities_trace_samples_extra.integer
+ : sv_cullentities_trace_samples.integer;
float enlarge = sv_cullentities_trace_enlarge.value;
qboolean visible = TRUE;
- do
+ if(samples > 0)
{
- if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv.writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
- break; // directly visible from the server's view
-
- if(sv_cullentities_trace_prediction.integer)
+ do
{
- vec3_t predeye;
-
- // get player velocity
- float predtime = bound(0, host_client->ping, 0.2); // / 2
- // sorry, no wallhacking by high ping please, and at 200ms
- // ping a FPS is annoying to play anyway and a player is
- // likely to have changed his direction
- VectorMA(sv.writeentitiestoclient_testeye, predtime, host_client->edict->fields.server->velocity, predeye);
- if(sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv.writeentitiestoclient_testeye, predeye)) // must be able to go there...
- {
- if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
- break; // directly visible from the predicted view
- }
- else
+ if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv.writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+ break; // directly visible from the server's view
+
+ if(sv_cullentities_trace_prediction.integer)
{
- //Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n");
+ vec3_t predeye;
+
+ // get player velocity
+ float predtime = bound(0, host_client->ping, 0.2); // / 2
+ // sorry, no wallhacking by high ping please, and at 200ms
+ // ping a FPS is annoying to play anyway and a player is
+ // likely to have changed his direction
+ VectorMA(sv.writeentitiestoclient_testeye, predtime, host_client->edict->fields.server->velocity, predeye);
+ if(sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv.writeentitiestoclient_testeye, predeye)) // must be able to go there...
+ {
+ if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+ break; // directly visible from the predicted view
+ }
+ else
+ {
+ //Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n");
+ }
}
- }
- // when we get here, we can't see the entity
- visible = false;
- }
- while(0);
-
- if(visible)
- svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
- else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number])
- {
- sv.writeentitiestoclient_stats_culled_trace++;
- return;
+ // when we get here, we can't see the entity
+ visible = false;
+ }
+ while(0);
+
+ if(visible)
+ svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] =
+ realtime + (
+ s->number <= svs.maxclients
+ ? sv_cullentities_trace_delay_players.value
+ : sv_cullentities_trace_delay.value
+ );
+ else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number])
+ {
+ sv.writeentitiestoclient_stats_culled_trace++;
+ return;
+ }
}
}
}
sv.sententities[s->number] = sv.sententitiesmark;
}
-void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg)
+void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize)
{
int i, numsendstates;
entity_state_t *s;
prvm_edict_t *camera;
// if there isn't enough space to accomplish anything, skip it
- if (msg->cursize + 25 > msg->maxsize)
+ if (msg->cursize + 25 > maxsize)
return;
sv.writeentitiestoclient_msg = msg;
if (sv_cullentities_stats.integer)
Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv.writeentitiestoclient_stats_totalentities, sv.writeentitiestoclient_stats_visibleentities, sv.writeentitiestoclient_stats_culled_pvs + sv.writeentitiestoclient_stats_culled_trace, sv.writeentitiestoclient_stats_culled_pvs, sv.writeentitiestoclient_stats_culled_trace);
- EntityFrameCSQC_WriteFrame(msg, numsendstates, sv.writeentitiestoclient_sendstates);
+ EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates);
if (client->entitydatabase5)
- EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence);
+ EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence);
else if (client->entitydatabase4)
{
- EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
+ EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
Protocol_WriteStatsReliable();
}
else if (client->entitydatabase)
{
- EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1);
+ EntityFrame_WriteFrame(msg, maxsize, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1);
Protocol_WriteStatsReliable();
}
else
{
- EntityFrameQuake_WriteFrame(msg, numsendstates, sv.writeentitiestoclient_sendstates);
+ EntityFrameQuake_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates);
Protocol_WriteStatsReliable();
}
}
SZ_Clear(&sv.datagram);
}
-static void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg)
+static void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg, int maxsize)
{
// scan the splitpoints to find out how many we can fit in
int numsegments, j, split;
if (!client->unreliablemsg_splitpoints)
return;
- // always accept the first one if it's within 1400 bytes, this ensures
+ // always accept the first one if it's within 1024 bytes, this ensures
// that very big datagrams which are over the rate limit still get
// through, just to keep it working
- if (msg->cursize + client->unreliablemsg_splitpoint[0] > msg->maxsize && msg->maxsize < 1400)
+ j = msg->cursize + client->unreliablemsg_splitpoint[0];
+ if (maxsize < 1024 && j > maxsize && j <= 1024)
{
numsegments = 1;
- msg->maxsize = 1400;
+ maxsize = 1024;
}
else
for (numsegments = 0;numsegments < client->unreliablemsg_splitpoints;numsegments++)
- if (msg->cursize + client->unreliablemsg_splitpoint[numsegments] > msg->maxsize)
+ if (msg->cursize + client->unreliablemsg_splitpoint[numsegments] > maxsize)
break;
if (numsegments > 0)
{
// note this discards ones that were accepted by the segments scan but
// can not fit, such as a really huge first one that will never ever
// fit in a packet...
- if (msg->cursize + split <= msg->maxsize)
+ if (msg->cursize + split <= maxsize)
SZ_Write(msg, client->unreliablemsg.data, split);
// remove the part we sent, keeping any remaining data
client->unreliablemsg.cursize -= split;
int stats[MAX_CL_STATS];
unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE];
+ // obey rate limit by limiting packet frequency if the packet size
+ // limiting fails
+ // (usually this is caused by reliable messages)
+ if (!NetConn_CanSend(client->netconnection))
+ return;
+
// PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
maxrate = max(NET_MINRATE, sv_maxrate.integer);
if (sv_maxrate.integer != maxrate)
Cvar_SetValueQuick(&sv_maxrate, maxrate);
+
// clientrate determines the 'cleartime' of a packet
// (how long to wait before sending another, based on this packet's size)
clientrate = bound(NET_MINRATE, client->rate, maxrate);
- if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
- {
- // for good singleplayer, send huge packets
- maxsize = sizeof(sv_sendclientdatagram_buf);
- maxsize2 = sizeof(sv_sendclientdatagram_buf);
- // never limit frequency in singleplayer
- clientrate = 1000000000;
- }
- 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_QUAKEWORLD)
+ switch (sv.protocol)
{
+ case PROTOCOL_QUAKE:
+ case PROTOCOL_QUAKEDP:
+ case PROTOCOL_NEHAHRAMOVIE:
+ case PROTOCOL_NEHAHRABJP:
+ case PROTOCOL_NEHAHRABJP2:
+ case PROTOCOL_NEHAHRABJP3:
+ case PROTOCOL_QUAKEWORLD:
// no packet size limit support on Quake protocols because it just
// causes missing entities/effects
// packets are simply sent less often to obey the rate limit
maxsize = 1024;
maxsize2 = 1024;
- }
- else if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
- {
+ break;
+ case PROTOCOL_DARKPLACES1:
+ case PROTOCOL_DARKPLACES2:
+ case PROTOCOL_DARKPLACES3:
+ case PROTOCOL_DARKPLACES4:
// no packet size limit support on DP1-4 protocols because they kick
// the client off if they overflow, and miss effects
// packets are simply sent less often to obey the rate limit
maxsize = sizeof(sv_sendclientdatagram_buf);
maxsize2 = sizeof(sv_sendclientdatagram_buf);
- }
- else
- {
+ break;
+ default:
// DP5 and later protocols support packet size limiting which is a
// better method than limiting packet frequency as QW does
//
// mods that use csqc (they are likely to use less bandwidth anyway)
if (sv.csqc_progsize > 0)
maxsize = maxsize2;
+ break;
}
- // obey rate limit by limiting packet frequency if the packet size
- // limiting fails
- // (usually this is caused by reliable messages)
- if (!NetConn_CanSend(client->netconnection))
- return;
+ if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
+ {
+ // for good singleplayer, send huge packets
+ maxsize = sizeof(sv_sendclientdatagram_buf);
+ maxsize2 = sizeof(sv_sendclientdatagram_buf);
+ // never limit frequency in singleplayer
+ clientrate = 1000000000;
+ }
// while downloading, limit entity updates to half the packet
// (any leftover space will be used for downloading)
maxsize /= 2;
msg.data = sv_sendclientdatagram_buf;
- msg.maxsize = maxsize;
+ msg.maxsize = sizeof(sv_sendclientdatagram_buf);
msg.cursize = 0;
msg.allowoverflow = false;
// add as many queued unreliable messages (effects) as we can fit
// limit effects to half of the remaining space
- msg.maxsize -= (msg.maxsize - msg.cursize) / 2;
if (client->unreliablemsg.cursize)
- SV_WriteUnreliableMessages (client, &msg);
-
- msg.maxsize = maxsize;
+ SV_WriteUnreliableMessages (client, &msg, (msg.cursize + maxsize) / 2);
// now write as many entities as we can fit, and also sends stats
- SV_WriteEntitiesToClient (client, client->edict, &msg);
+ SV_WriteEntitiesToClient (client, client->edict, &msg, maxsize);
}
else if (realtime > client->keepalivetime)
{
// the player isn't totally in the game yet
// send small keepalive messages if too much time has passed
// (may also be sending downloads)
- msg.maxsize = maxsize2;
client->keepalivetime = realtime + 5;
MSG_WriteChar (&msg, svc_nop);
}
- msg.maxsize = maxsize2;
-
// if a download is active, see if there is room to fit some download data
// in this packet
- downloadsize = maxsize * 2 - msg.cursize - 7;
+ downloadsize = min(maxsize*2,maxsize2) - msg.cursize - 7;
if (host_client->download_file && host_client->download_started && downloadsize > 0)
{
fs_offset_t downloadstart;
}
for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
- if (client->netconnection)
+ if (client->netconnection && (client->spawned || client->clientconnectcalled)) // also send MSG_ALL to people who are past ClientConnect, but not spawned yet
SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
SZ_Clear (&sv.reliable_datagram);
}
}
- host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
+ host_client->download_file = FS_OpenVirtualFile(host_client->download_name, true);
if (!host_client->download_file)
{
SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
prvm_edict_t *ent;
int i;
char *entities;
- model_t *worldmodel;
+ dp_model_t *worldmodel;
char modelname[sizeof(sv.modelname)];
Con_DPrintf("SpawnServer: %s\n", server);
// send serverinfo to all connected clients, and set up botclients coming back from a level change
for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
{
+ host_client->clientconnectcalled = false; // do NOT call ClientDisconnect if he drops before ClientConnect!
if (!host_client->active)
continue;
if (host_client->netconnection)
static void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
{
+ int i;
+ int e;
+
World_UnlinkEdict(ed); // unlink from world bsp
ed->fields.server->model = 0;
VectorClear(ed->fields.server->angles);
ed->fields.server->nextthink = -1;
ed->fields.server->solid = 0;
+
+ // make sure csqc networking is aware of the removed entity
+ e = PRVM_NUM_FOR_EDICT(ed);
+ sv.csqcentityversion[e] = 0;
+ for (i = 0;i < svs.maxclients;i++)
+ {
+ if (svs.clients[i].csqcentityscope[e])
+ svs.clients[i].csqcentityscope[e] = 1; // removed, awaiting send
+ svs.clients[i].csqcentitysendflags[e] = 0xFFFFFF;
+ }
}
static void SV_VM_CB_CountEdicts(void)
static void SV_VM_Setup(void)
{
extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
- extern cvar_t csqc_progcrc;
- extern cvar_t csqc_progsize;
size_t csprogsdatasize;
PRVM_Begin;
PRVM_InitProg( PRVM_SERVERPROG );