static char localmodels[MAX_MODELS][5]; // inline model names for precache
mempool_t *sv_edicts_mempool = NULL;
+mempool_t *sv_clients_mempool = NULL;
//============================================================================
for (i = 0;i < MAX_MODELS;i++)
sprintf (localmodels[i], "*%i", i);
- sv_edicts_mempool = Mem_AllocPool("edicts");
+ sv_edicts_mempool = Mem_AllocPool("server edicts");
+ sv_clients_mempool = Mem_AllocPool("server clients");
}
/*
char message[128];
// edicts get reallocated on level changes, so we need to update it here
- client->edict = EDICT_NUM((client - svs.clients) + 1);
+ client->edict = EDICT_NUM(client->number + 1);
// LordHavoc: clear entityframe tracking
client->entityframenumber = 0;
- EntityFrame_ClearDatabase(&client->entitydatabase);
+ if (client->entitydatabase4)
+ EntityFrame4_FreeDatabase(client->entitydatabase4);
+ client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_clients_mempool);
MSG_WriteByte (&client->message, svc_print);
sprintf (message, "\002\nServer: %s build %s (progs %i crc)", gamename, buildstring, pr_crc);
MSG_WriteString (&client->message,message);
MSG_WriteByte (&client->message, svc_serverinfo);
- MSG_WriteLong (&client->message, DPPROTOCOL_VERSION3);
- MSG_WriteByte (&client->message, svs.maxclients);
+ MSG_WriteLong (&client->message, DPPROTOCOL_VERSION4);
+ MSG_WriteByte (&client->message, MAX_SCOREBOARD);
if (!coop.integer && deathmatch.integer)
MSG_WriteByte (&client->message, GAME_DEATHMATCH);
int i;
float spawn_parms[NUM_SPAWN_PARMS];
- client = svs.clients + clientnum;
+ client = svs.connectedclients[clientnum];
// set up the client_t
if (sv.loadgame)
strcpy(client->name, "unconnected");
strcpy(client->old_name, "unconnected");
- client->active = true;
+ client->number = clientnum;
client->spawned = false;
client->edict = EDICT_NUM(clientnum+1);
client->message.data = client->msgbuf;
testorigin[1] = lhrandom(entmins[1], entmaxs[1]);
testorigin[2] = lhrandom(entmins[2], entmaxs[2]);
- if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL).fraction == 1)
+ if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL).fraction == 1)
client->visibletime[e] = realtime + 1;
else
{
testorigin[1] = bound(entmins[1], testeye[1], entmaxs[1]);
testorigin[2] = bound(entmins[2], testeye[2], entmaxs[2]);
- if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL).fraction == 1)
+ if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL).fraction == 1)
client->visibletime[e] = realtime + 1;
else if (realtime > client->visibletime[e])
{
// we can omit invisible entities with no effects that are not clients
// LordHavoc: this could kill tags attached to an invisible entity, I
// just hope we never have to support that case
- if (cs.number > svs.maxclients && ((cs.effects & EF_NODRAW) || (!cs.modelindex && !cs.specialvisibilityradius)))
+ if (cs.number > MAX_SCOREBOARD && ((cs.effects & EF_NODRAW) || (!cs.modelindex && !cs.specialvisibilityradius)))
continue;
sendentitiesindex[e] = sendentities + numsendentities;
sendentities[numsendentities++] = cs;
static int sv_writeentitiestoclient_culled_trace;
static int sv_writeentitiestoclient_visibleentities;
static int sv_writeentitiestoclient_totalentities;
-static entity_frame_t sv_writeentitiestoclient_entityframe;
+//static entity_frame_t sv_writeentitiestoclient_entityframe;
static int sv_writeentitiestoclient_clentnum;
static qbyte *sv_writeentitiestoclient_pvs;
static vec3_t sv_writeentitiestoclient_testeye;
testorigin[0] = (entmins[0] + entmaxs[0]) * 0.5f;
testorigin[1] = (entmins[1] + entmaxs[1]) * 0.5f;
testorigin[2] = (entmins[2] + entmaxs[2]) * 0.5f;
- trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL);
+ trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL);
if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
else
testorigin[0] = lhrandom(entmins[0], entmaxs[0]);
testorigin[1] = lhrandom(entmins[1], entmaxs[1]);
testorigin[2] = lhrandom(entmins[2], entmaxs[2]);
- trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL);
+ trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL);
if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
else
testorigin[0] = lhrandom(lightmins[0], lightmaxs[0]);
testorigin[1] = lhrandom(lightmins[1], lightmaxs[1]);
testorigin[2] = lhrandom(lightmins[2], lightmaxs[2]);
- trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL);
+ trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL);
if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
}
int i;
vec3_t testorigin;
entity_state_t *s;
+ entity_database4_t *d;
+ int maxbytes, n, startnumber;
+ entity_state_t *e, inactiveentitystate;
+ sizebuf_t buf;
+ qbyte data[128];
+ // prepare the buffer
+ memset(&buf, 0, sizeof(buf));
+ buf.data = data;
+ buf.maxsize = sizeof(data);
+
+ // this state's number gets played around with later
+ ClearStateToDefault(&inactiveentitystate);
+ //inactiveentitystate = defaultstate;
sv_writeentitiestoclient_client = client;
for (i = 0;i < numsendentities;i++)
SV_MarkWriteEntityStateToClient(sendentities + i);
- EntityFrame_Clear(&sv_writeentitiestoclient_entityframe, testorigin, ++client->entityframenumber);
- for (i = 0;i < numsendentities;i++)
+ d = client->entitydatabase4;
+ // calculate maximum bytes to allow in this packet
+ // deduct 4 to account for the end data
+ maxbytes = min(msg->maxsize, MAX_PACKETFRAGMENT) - 4;
+
+ d->currentcommit = d->commit + EntityFrame4_SV_ChooseCommitToReplace(d);
+ d->currentcommit->numentities = 0;
+ d->currentcommit->framenum = ++client->entityframenumber;
+ MSG_WriteByte(msg, svc_entities);
+ MSG_WriteLong(msg, d->referenceframenum);
+ MSG_WriteLong(msg, d->currentcommit->framenum);
+ if (d->currententitynumber >= sv.max_edicts)
+ startnumber = 1;
+ else
+ startnumber = bound(1, d->currententitynumber, sv.max_edicts - 1);
+ MSG_WriteShort(msg, startnumber);
+ // reset currententitynumber so if the loop does not break it we will
+ // start at beginning next frame (if it does break, it will set it)
+ d->currententitynumber = 1;
+ for (i = 0, n = startnumber;n < sv.max_edicts;n++)
{
- s = sendentities + i;
- if (sententities[s->number] == sententitiesmark)
+ // find the old state to delta from
+ e = EntityFrame4_GetReferenceEntity(d, n);
+ // prepare the buffer
+ SZ_Clear(&buf);
+ // make the message
+ if (sententities[n] == sententitiesmark)
{
+ // entity exists, build an update (if empty there is no change)
+ // find the state in the list
+ for (;i < numsendentities && sendentities[i].number < n;i++);
+ s = sendentities + i;
+ if (s->number != n)
+ Sys_Error("SV_WriteEntitiesToClient: s->number != n\n");
+ // build the update
if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
{
s->flags |= RENDER_EXTERIORMODEL;
- EntityFrame_AddEntity(&sv_writeentitiestoclient_entityframe, s);
+ EntityState_Write(s, &buf, e);
s->flags &= ~RENDER_EXTERIORMODEL;
}
else
- EntityFrame_AddEntity(&sv_writeentitiestoclient_entityframe, s);
+ EntityState_Write(s, &buf, e);
+ }
+ else
+ {
+ s = &inactiveentitystate;
+ s->number = n;
+ if (e->active)
+ {
+ // entity used to exist but doesn't anymore, send remove
+ MSG_WriteShort(&buf, n | 0x8000);
+ }
+ }
+ // if the commit is full, we're done this frame
+ if (msg->cursize + buf.cursize > maxbytes)
+ {
+ // next frame we will continue where we left off
+ break;
+ }
+ // add the entity to the commit
+ EntityFrame4_AddCommitEntity(d, s);
+ // if the message is empty, skip out now
+ if (buf.cursize)
+ {
+ // write the message to the packet
+ SZ_Write(msg, buf.data, buf.cursize);
}
}
- EntityFrame_Write(&client->entitydatabase, &sv_writeentitiestoclient_entityframe, msg);
+ d->currententitynumber = n;
+
+ // remove world message (invalid, and thus a good terminator)
+ MSG_WriteShort(msg, 0x8000);
+ // write the number of the end entity
+ MSG_WriteShort(msg, d->currententitynumber);
+ // just to be sure
+ d->currentcommit = NULL;
if (sv_cullentities_stats.integer)
Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d portal %d trace\n", client->name, sv_writeentitiestoclient_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_portal + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_portal, sv_writeentitiestoclient_culled_trace);
char *s;
// check for changes to be sent over the reliable streams
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
// only update the client fields if they've spawned in
- if (host_client->spawned)
+ if ((host_client = svs.connectedclients[i]) && host_client->spawned)
{
// update the host_client fields we care about according to the entity fields
sv_player = host_client->edict;
if (strcmp(host_client->old_name, host_client->name))
{
strcpy(host_client->old_name, host_client->name);
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ for (j = 0;j < MAX_SCOREBOARD;j++)
{
- if (!client->active || !client->spawned)
+ if (!(client = svs.connectedclients[j]) || !client->spawned)
continue;
MSG_WriteByte (&client->message, svc_updatename);
MSG_WriteByte (&client->message, i);
if (host_client->old_colors != host_client->colors)
{
host_client->old_colors = host_client->colors;
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ for (j = 0;j < MAX_SCOREBOARD;j++)
{
- if (!client->active || !client->spawned)
+ if (!(client = svs.connectedclients[j]) || !client->spawned)
continue;
MSG_WriteByte (&client->message, svc_updatecolors);
MSG_WriteByte (&client->message, i);
if (host_client->old_frags != host_client->frags)
{
host_client->old_frags = host_client->frags;
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+ for (j = 0;j < MAX_SCOREBOARD;j++)
{
- if (!client->active || !client->spawned)
+ if (!(client = svs.connectedclients[j]) || !client->spawned)
continue;
MSG_WriteByte (&client->message, svc_updatefrags);
MSG_WriteByte (&client->message, i);
}
}
- for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
- {
- if (!client->active)
- continue;
- SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
- }
+ for (j = 0;j < MAX_SCOREBOARD;j++)
+ if ((client = svs.connectedclients[j]))
+ SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
SZ_Clear (&sv.reliable_datagram);
}
SV_UpdateToReliableMessages();
// build individual updates
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!host_client->active)
+ if (!(host_client = svs.connectedclients[i]))
continue;
- if (host_client->deadsocket)
+ if (host_client->deadsocket || !host_client->netconnection || host_client->message.overflowed)
{
SV_DropClient (true); // if the message couldn't send, kick off
continue;
}
}
- // check for an overflowed message. Should only happen
- // on a very fucked up connection that backs up a lot, then
- // changes level
- if (host_client->message.overflowed)
- {
- SV_DropClient (true); // overflowed
- host_client->message.overflowed = false;
- continue;
- }
-
if (host_client->message.cursize || host_client->dropasap)
{
if (!NetConn_CanSendMessage (host_client->netconnection))
if (svent->e->free)
continue;
- if (entnum > svs.maxclients && !svent->v->modelindex)
+ if (entnum > MAX_SCOREBOARD && !svent->v->modelindex)
continue;
// create entity baseline
VectorCopy (svent->v->angles, svent->e->baseline.angles);
svent->e->baseline.frame = svent->v->frame;
svent->e->baseline.skin = svent->v->skin;
- if (entnum > 0 && entnum <= svs.maxclients)
+ if (entnum > 0 && entnum <= MAX_SCOREBOARD)
{
svent->e->baseline.colormap = entnum;
svent->e->baseline.modelindex = SV_ModelIndex("progs/player.mdl");
svs.serverflags = pr_global_struct->serverflags;
- for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+ for (i = 0;i < MAX_SCOREBOARD;i++)
{
- if (!host_client->active)
+ if (!(host_client = svs.connectedclients[i]))
continue;
// call the progs to get default spawn parms for the new client
}
SV_ClearWorld();
- sv.max_edicts = min(sv.max_edicts + 32, MAX_EDICTS);
+ sv.max_edicts = min(sv.max_edicts + 256, MAX_EDICTS);
sv.edictsengineprivate = Mem_Alloc(sv_edicts_mempool, sv.max_edicts * sizeof(edict_engineprivate_t));
sv.edictsfields = Mem_Alloc(sv_edicts_mempool, sv.max_edicts * pr_edict_size);
sv.moved_edicts = Mem_Alloc(sv_edicts_mempool, sv.max_edicts * sizeof(edict_t *));
// tell all connected clients that we are going to a new level
//
if (sv.active)
- SV_SendReconnect ();
+ SV_SendReconnect();
else
- NetConn_OpenServerPorts(svs.maxclients > 1);
+ NetConn_OpenServerPorts(true);
//
// make cvars consistant
// allocate server memory
// start out with just enough room for clients and a reasonable estimate of entities
- sv.max_edicts = ((svs.maxclients + 128) + 31) & ~31;
+ sv.max_edicts = max(MAX_SCOREBOARD + 1, 512);
sv.max_edicts = min(sv.max_edicts, MAX_EDICTS);
// clear the edict memory pool
sv.signon.data = sv.signon_buf;
// leave slots at start for clients only
- sv.num_edicts = svs.maxclients+1;
+ sv.num_edicts = MAX_SCOREBOARD+1;
sv.state = ss_loading;
sv.paused = false;
#endif
// send serverinfo to all connected clients
- for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
- if (host_client->active)
- SV_SendServerinfo (host_client);
+ for (i = 0;i < MAX_SCOREBOARD;i++)
+ if ((host_client = svs.connectedclients[i]))
+ SV_SendServerinfo(host_client);
Con_DPrintf ("Server spawned.\n");
NetConn_Heartbeat (2);