]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
Added the proper libPNG DLL name for Win64, by Willis
[xonotic/darkplaces.git] / sv_main.c
index 7935327ff6fa65d41405112a16b1b62ba9289319..c9d8e3016fe53cb89422793622e23761a22f59b0 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -24,6 +24,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 void SV_VM_Init();
 void SV_VM_Setup();
 
+void VM_AutoSentStats_Clear (void);
+void EntityFrameCSQC_ClearVersions (void);
+void EntityFrameCSQC_InitClientVersions (int client, qboolean clear);
+void VM_SV_WriteAutoSentStats (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);
+
+
 // select which protocol to host, this is fed to Protocol_EnumForName
 cvar_t sv_protocolname = {0, "sv_protocolname", "DP7"};
 cvar_t sv_ratelimitlocalplayer = {0, "sv_ratelimitlocalplayer", "0"};
@@ -34,8 +41,6 @@ static cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0"}; // tend
 static cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0"};
 static cvar_t sv_entpatch = {0, "sv_entpatch", "1"};
 
-extern cvar_t sys_ticrate;
-
 cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1"};
 cvar_t sv_gameplayfix_noairborncorpse = {0, "sv_gameplayfix_noairborncorpse", "1"};
 cvar_t sv_gameplayfix_stepdown = {0, "sv_gameplayfix_stepdown", "1"};
@@ -211,19 +216,19 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v
 
        if (volume < 0 || volume > 255)
        {
-               Con_Printf ("SV_StartSound: volume = %i", volume);
+               Con_Printf ("SV_StartSound: volume = %i\n", volume);
                return;
        }
 
        if (attenuation < 0 || attenuation > 4)
        {
-               Con_Printf ("SV_StartSound: attenuation = %f", attenuation);
+               Con_Printf ("SV_StartSound: attenuation = %f\n", attenuation);
                return;
        }
 
        if (channel < 0 || channel > 7)
        {
-               Con_Printf ("SV_StartSound: channel = %i", channel);
+               Con_Printf ("SV_StartSound: channel = %i\n", channel);
                return;
        }
 
@@ -277,6 +282,7 @@ CLIENT SPAWNING
 ==============================================================================
 */
 
+static const char *SV_InitCmd; //[515]: svprogs able to send cmd to client on connect
 /*
 ================
 SV_SendServerinfo
@@ -293,26 +299,9 @@ void SV_SendServerinfo (client_t *client)
        // edicts get reallocated on level changes, so we need to update it here
        client->edict = PRVM_EDICT_NUM((client - svs.clients) + 1);
 
-       // if client is a botclient coming from a level change, we need to set up
-       // client info that normally requires networking
-       if (!client->netconnection)
-       {
-               // set up the edict
-                PRVM_ED_ClearEdict(client->edict);
-
-               // copy spawn parms out of the client_t
-               for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
-                       (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
-
-               // call the spawn function
-               host_client->clientconnectcalled = true;
-               prog->globals.server->time = sv.time;
-               prog->globals.server->self = PRVM_EDICT_TO_PROG(client->edict);
-               PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
-               PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
-               host_client->spawned = true;
-               return;
-       }
+       // clear cached stuff that depends on the level
+       client->weaponmodel[0] = 0;
+       client->weaponmodelindex = 0;
 
        // LordHavoc: clear entityframe tracking
        client->latestframenum = 0;
@@ -339,6 +328,18 @@ void SV_SendServerinfo (client_t *client)
        dpsnprintf (message, sizeof (message), "\002\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
        MSG_WriteString (&client->message,message);
 
+       // LordHavoc: this does not work on dedicated servers, needs fixing.
+extern qboolean csqc_loaded;
+//[515]: init csprogs according to version of svprogs, check the crc, etc.
+       if(csqc_loaded && (cls.state == ca_dedicated || PRVM_NUM_FOR_EDICT(client->edict) != 1))
+       {
+               MSG_WriteByte (&client->message, svc_stufftext);
+               if(SV_InitCmd)
+                       MSG_WriteString (&client->message, va("csqc_progcrc %i;%s\n", csqc_progcrc.integer, SV_InitCmd));
+               else
+                       MSG_WriteString (&client->message, va("csqc_progcrc %i\n", csqc_progcrc.integer));
+       }
+
        MSG_WriteByte (&client->message, svc_serverinfo);
        MSG_WriteLong (&client->message, Protocol_NumberForEnum(sv.protocol));
        MSG_WriteByte (&client->message, svs.maxclients);
@@ -390,6 +391,9 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
 
        client = svs.clients + clientnum;
 
+       if(netconnection)//[515]: bots don't play with csqc =)
+               EntityFrameCSQC_InitClientVersions(clientnum, false);
+
 // set up the client_t
        if (sv.loadgame)
                memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
@@ -425,6 +429,9 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
                        client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
        }
 
+       // set up the entity for this client (including .colormap, .team, etc)
+       PRVM_ED_ClearEdict(client->edict);
+
        // don't call SendServerinfo for a fresh botclient because its fields have
        // not been set up by the qc yet
        if (client->netconnection)
@@ -465,172 +472,258 @@ crosses a waterline.
 */
 
 int sv_writeentitiestoclient_pvsbytes;
-qbyte sv_writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
+unsigned char sv_writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
 
 static int numsendentities;
 static entity_state_t sendentities[MAX_EDICTS];
 static entity_state_t *sendentitiesindex[MAX_EDICTS];
 
-void SV_PrepareEntitiesForSending(void)
+qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
 {
-       int e, i;
+       int i;
+       unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
+       unsigned int customizeentityforclient;
        float f;
-       prvm_edict_t *ent;
+       vec3_t cullmins, cullmaxs;
+       model_t *model;
        prvm_eval_t *val;
-       entity_state_t cs;
-       // send all entities that touch the pvs
-       numsendentities = 0;
-       sendentitiesindex[0] = NULL;
-       for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
-       {
-               sendentitiesindex[e] = NULL;
-               // the 2 billion unit check is actually to detect NAN origins (we really don't want to send those)
-               if (ent->priv.server->free || VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
-                       continue;
 
-               cs = defaultstate;
-               cs.active = true;
-               cs.number = e;
-               VectorCopy(ent->fields.server->origin, cs.origin);
-               VectorCopy(ent->fields.server->angles, cs.angles);
-               cs.flags = 0;
-               cs.effects = (unsigned)ent->fields.server->effects;
-               cs.colormap = (unsigned)ent->fields.server->colormap;
-               cs.skin = (unsigned)ent->fields.server->skin;
-               cs.frame = (unsigned)ent->fields.server->frame;
-               cs.viewmodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)->edict;
-               cs.exteriormodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)->edict;
-               cs.nodrawtoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)->edict;
-               cs.drawonlytoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)->edict;
-               cs.tagentity = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_entity)->edict;
-               cs.tagindex = (qbyte)PRVM_GETEDICTFIELDVALUE(ent, eval_tag_index)->_float;
-               i = (int)(PRVM_GETEDICTFIELDVALUE(ent, eval_glow_size)->_float * 0.25f);
-               cs.glowsize = (qbyte)bound(0, i, 255);
-               if (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_trail)->_float)
-                       cs.flags |= RENDER_GLOWTRAIL;
-
-               // don't need to init cs.colormod because the defaultstate did that for us
-               //cs.colormod[0] = cs.colormod[1] = cs.colormod[2] = 32;
-               val = PRVM_GETEDICTFIELDVALUE(ent, eval_colormod);
-               if (val->vector[0] || val->vector[1] || val->vector[2])
+       // EF_NODRAW prevents sending for any reason except for your own
+       // client, so we must keep all clients in this superset
+       effects = (unsigned)ent->fields.server->effects;
+
+       // 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
+       i = (int)ent->fields.server->modelindex;
+       modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
+
+       flags = 0;
+       i = (int)(PRVM_GETEDICTFIELDVALUE(ent, eval_glow_size)->_float * 0.25f);
+       glowsize = (unsigned char)bound(0, i, 255);
+       if (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_trail)->_float)
+               flags |= RENDER_GLOWTRAIL;
+
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[0]*256;
+       light[0] = (unsigned short)bound(0, f, 65535);
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[1]*256;
+       light[1] = (unsigned short)bound(0, f, 65535);
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[2]*256;
+       light[2] = (unsigned short)bound(0, f, 65535);
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_light_lev)->_float;
+       light[3] = (unsigned short)bound(0, f, 65535);
+       lightstyle = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_style)->_float;
+       lightpflags = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_pflags)->_float;
+
+       if (gamemode == GAME_TENEBRAE)
+       {
+               // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
+               if (effects & 16)
+               {
+                       effects &= ~16;
+                       lightpflags |= PFLAGS_FULLDYNAMIC;
+               }
+               // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
+               if (effects & 32)
                {
-                       i = val->vector[0] * 32.0f;cs.colormod[0] = bound(0, i, 255);
-                       i = val->vector[1] * 32.0f;cs.colormod[1] = bound(0, i, 255);
-                       i = val->vector[2] * 32.0f;cs.colormod[2] = bound(0, i, 255);
+                       effects &= ~32;
+                       light[0] = 0.2;
+                       light[1] = 1;
+                       light[2] = 0.2;
+                       light[3] = 200;
+                       lightpflags |= PFLAGS_FULLDYNAMIC;
                }
+       }
+
+       specialvisibilityradius = 0;
+       if (lightpflags & PFLAGS_FULLDYNAMIC)
+               specialvisibilityradius = max(specialvisibilityradius, light[3]);
+       if (glowsize)
+               specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
+       if (flags & RENDER_GLOWTRAIL)
+               specialvisibilityradius = max(specialvisibilityradius, 100);
+       if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
+       {
+               if (effects & EF_BRIGHTFIELD)
+                       specialvisibilityradius = max(specialvisibilityradius, 80);
+               if (effects & EF_MUZZLEFLASH)
+                       specialvisibilityradius = max(specialvisibilityradius, 100);
+               if (effects & EF_BRIGHTLIGHT)
+                       specialvisibilityradius = max(specialvisibilityradius, 400);
+               if (effects & EF_DIMLIGHT)
+                       specialvisibilityradius = max(specialvisibilityradius, 200);
+               if (effects & EF_RED)
+                       specialvisibilityradius = max(specialvisibilityradius, 200);
+               if (effects & EF_BLUE)
+                       specialvisibilityradius = max(specialvisibilityradius, 200);
+               if (effects & EF_FLAME)
+                       specialvisibilityradius = max(specialvisibilityradius, 250);
+               if (effects & EF_STARDUST)
+                       specialvisibilityradius = max(specialvisibilityradius, 100);
+       }
 
-               cs.modelindex = 0;
-               i = (int)ent->fields.server->modelindex;
-               if (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model))
-                       cs.modelindex = i;
+       // early culling checks
+       // (final culling is done by SV_MarkWriteEntityStateToClient)
+       customizeentityforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_customizeentityforclient)->function;
+       if (!customizeentityforclient)
+       {
+               if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
+                       return false;
+               // this 2 billion unit check is actually to detect NAN origins
+               // (we really don't want to send those)
+               if (VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
+                       return false;
+       }
 
-               cs.alpha = 255;
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_alpha)->_float * 255.0f);
-               if (f)
+
+       *cs = defaultstate;
+       cs->active = true;
+       cs->number = e;
+       VectorCopy(ent->fields.server->origin, cs->origin);
+       VectorCopy(ent->fields.server->angles, cs->angles);
+       cs->flags = flags;
+       cs->effects = effects;
+       cs->colormap = (unsigned)ent->fields.server->colormap;
+       cs->modelindex = modelindex;
+       cs->skin = (unsigned)ent->fields.server->skin;
+       cs->frame = (unsigned)ent->fields.server->frame;
+       cs->viewmodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)->edict;
+       cs->exteriormodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)->edict;
+       cs->nodrawtoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)->edict;
+       cs->drawonlytoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)->edict;
+       cs->customizeentityforclient = customizeentityforclient;
+       cs->tagentity = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_entity)->edict;
+       cs->tagindex = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_tag_index)->_float;
+       cs->glowsize = glowsize;
+
+       // don't need to init cs->colormod because the defaultstate did that for us
+       //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
+       val = PRVM_GETEDICTFIELDVALUE(ent, eval_colormod);
+       if (val->vector[0] || val->vector[1] || val->vector[2])
+       {
+               i = val->vector[0] * 32.0f;cs->colormod[0] = bound(0, i, 255);
+               i = val->vector[1] * 32.0f;cs->colormod[1] = bound(0, i, 255);
+               i = val->vector[2] * 32.0f;cs->colormod[2] = bound(0, i, 255);
+       }
+
+       cs->modelindex = modelindex;
+
+       cs->alpha = 255;
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_alpha)->_float * 255.0f);
+       if (f)
+       {
+               i = (int)f;
+               cs->alpha = (unsigned char)bound(0, i, 255);
+       }
+       // halflife
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_renderamt)->_float);
+       if (f)
+       {
+               i = (int)f;
+               cs->alpha = (unsigned char)bound(0, i, 255);
+       }
+
+       cs->scale = 16;
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_scale)->_float * 16.0f);
+       if (f)
+       {
+               i = (int)f;
+               cs->scale = (unsigned char)bound(0, i, 255);
+       }
+
+       cs->glowcolor = 254;
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_color)->_float);
+       if (f)
+               cs->glowcolor = (int)f;
+
+       if (PRVM_GETEDICTFIELDVALUE(ent, eval_fullbright)->_float)
+               cs->effects |= EF_FULLBRIGHT;
+
+       if (ent->fields.server->movetype == MOVETYPE_STEP)
+               cs->flags |= RENDER_STEP;
+       if ((cs->effects & EF_LOWPRECISION) && cs->origin[0] >= -32768 && cs->origin[1] >= -32768 && cs->origin[2] >= -32768 && cs->origin[0] <= 32767 && cs->origin[1] <= 32767 && cs->origin[2] <= 32767)
+               cs->flags |= RENDER_LOWPRECISION;
+       if (ent->fields.server->colormap >= 1024)
+               cs->flags |= RENDER_COLORMAPPED;
+       if (cs->viewmodelforclient)
+               cs->flags |= RENDER_VIEWMODEL; // show relative to the view
+
+       cs->light[0] = light[0];
+       cs->light[1] = light[1];
+       cs->light[2] = light[2];
+       cs->light[3] = light[3];
+       cs->lightstyle = lightstyle;
+       cs->lightpflags = lightpflags;
+
+       cs->specialvisibilityradius = specialvisibilityradius;
+
+       // calculate the visible box of this entity (don't use the physics box
+       // as that is often smaller than a model, and would not count
+       // specialvisibilityradius)
+       if ((model = sv.models[modelindex]))
+       {
+               float scale = cs->scale * (1.0f / 16.0f);
+               if (cs->angles[0] || cs->angles[2]) // pitch and roll
                {
-                       i = (int)f;
-                       cs.alpha = (qbyte)bound(0, i, 255);
+                       VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
+                       VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
                }
-               // halflife
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_renderamt)->_float);
-               if (f)
+               else if (cs->angles[1])
                {
-                       i = (int)f;
-                       cs.alpha = (qbyte)bound(0, i, 255);
+                       VectorMA(cs->origin, scale, model->yawmins, cullmins);
+                       VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
                }
-
-               cs.scale = 16;
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_scale)->_float * 16.0f);
-               if (f)
+               else
                {
-                       i = (int)f;
-                       cs.scale = (qbyte)bound(0, i, 255);
+                       VectorMA(cs->origin, scale, model->normalmins, cullmins);
+                       VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
                }
-
-               cs.glowcolor = 254;
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_color)->_float);
-               if (f)
-                       cs.glowcolor = (int)f;
-
-               if (PRVM_GETEDICTFIELDVALUE(ent, eval_fullbright)->_float)
-                       cs.effects |= EF_FULLBRIGHT;
-
-               if (ent->fields.server->movetype == MOVETYPE_STEP)
-                       cs.flags |= RENDER_STEP;
-               if ((cs.effects & EF_LOWPRECISION) && cs.origin[0] >= -32768 && cs.origin[1] >= -32768 && cs.origin[2] >= -32768 && cs.origin[0] <= 32767 && cs.origin[1] <= 32767 && cs.origin[2] <= 32767)
-                       cs.flags |= RENDER_LOWPRECISION;
-               if (ent->fields.server->colormap >= 1024)
-                       cs.flags |= RENDER_COLORMAPPED;
-               if (cs.viewmodelforclient)
-                       cs.flags |= RENDER_VIEWMODEL; // show relative to the view
-
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[0]*256;
-               cs.light[0] = (unsigned short)bound(0, f, 65535);
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[1]*256;
-               cs.light[1] = (unsigned short)bound(0, f, 65535);
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[2]*256;
-               cs.light[2] = (unsigned short)bound(0, f, 65535);
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_light_lev)->_float;
-               cs.light[3] = (unsigned short)bound(0, f, 65535);
-               cs.lightstyle = (qbyte)PRVM_GETEDICTFIELDVALUE(ent, eval_style)->_float;
-               cs.lightpflags = (qbyte)PRVM_GETEDICTFIELDVALUE(ent, eval_pflags)->_float;
-
-               if (gamemode == GAME_TENEBRAE)
+       }
+       else
+       {
+               VectorCopy(cs->origin, cullmins);
+               VectorCopy(cs->origin, cullmaxs);
+       }
+       if (specialvisibilityradius)
+       {
+               cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
+               cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
+               cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
+               cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
+               cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
+               cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
+       }
+       if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
+       {
+               VectorCopy(cullmins, ent->priv.server->cullmins);
+               VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
+               ent->priv.server->pvs_numclusters = -1;
+               if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
                {
-                       // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
-                       if (cs.effects & 16)
-                       {
-                               cs.effects &= ~16;
-                               cs.lightpflags |= PFLAGS_FULLDYNAMIC;
-                       }
-                       // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
-                       if (cs.effects & 32)
-                       {
-                               cs.effects &= ~32;
-                               cs.light[0] = 0.2;
-                               cs.light[1] = 1;
-                               cs.light[2] = 0.2;
-                               cs.light[3] = 200;
-                               cs.lightpflags |= PFLAGS_FULLDYNAMIC;
-                       }
+                       i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
+                       if (i <= MAX_ENTITYCLUSTERS)
+                               ent->priv.server->pvs_numclusters = i;
                }
+       }
 
-               cs.specialvisibilityradius = 0;
-               if (cs.lightpflags & PFLAGS_FULLDYNAMIC)
-                       cs.specialvisibilityradius = max(cs.specialvisibilityradius, cs.light[3]);
-               if (cs.glowsize)
-                       cs.specialvisibilityradius = max(cs.specialvisibilityradius, cs.glowsize * 4);
-               if (cs.flags & RENDER_GLOWTRAIL)
-                       cs.specialvisibilityradius = max(cs.specialvisibilityradius, 100);
-               if (cs.effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
+       return true;
+}
+
+void SV_PrepareEntitiesForSending(void)
+{
+       int e;
+       prvm_edict_t *ent;
+       // send all entities that touch the pvs
+       numsendentities = 0;
+       sendentitiesindex[0] = NULL;
+       memset(sendentitiesindex, 0, prog->num_edicts * sizeof(entity_state_t *));
+       for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
+       {
+               if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sendentities + numsendentities, e))
                {
-                       if (cs.effects & EF_BRIGHTFIELD)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 80);
-                       if (cs.effects & EF_MUZZLEFLASH)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 100);
-                       if (cs.effects & EF_BRIGHTLIGHT)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 400);
-                       if (cs.effects & EF_DIMLIGHT)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 200);
-                       if (cs.effects & EF_RED)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 200);
-                       if (cs.effects & EF_BLUE)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 200);
-                       if (cs.effects & EF_FLAME)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 250);
-                       if (cs.effects & EF_STARDUST)
-                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 100);
+                       sendentitiesindex[e] = sendentities + numsendentities;
+                       numsendentities++;
                }
-
-               if (numsendentities >= MAX_EDICTS)
-                       continue;
-               // 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)))
-                       continue;
-               sendentitiesindex[e] = sendentities + numsendentities;
-               sendentities[numsendentities++] = cs;
        }
 }
 
@@ -649,20 +742,26 @@ static client_t *sv_writeentitiestoclient_client;
 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
 {
        int isbmodel;
-       vec3_t entmins, entmaxs, lightmins, lightmaxs, testorigin;
+       vec3_t testorigin;
        model_t *model;
+       prvm_edict_t *ed;
        trace_t trace;
        if (sententitiesconsideration[s->number] == sententitiesmark)
                return;
        sententitiesconsideration[s->number] = sententitiesmark;
-       // viewmodels don't have visibility checking
-       if (s->viewmodelforclient)
+       sv_writeentitiestoclient_totalentities++;
+
+       if (s->customizeentityforclient)
        {
-               if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
+               prog->globals.server->self = s->number;
+               prog->globals.server->other = sv_writeentitiestoclient_clentnum;
+               PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
+               if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
                        return;
        }
+
        // never reject player
-       else if (s->number != sv_writeentitiestoclient_clentnum)
+       if (s->number != sv_writeentitiestoclient_clentnum)
        {
                // check various rejection conditions
                if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
@@ -674,7 +773,14 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                // LordHavoc: only send entities with a model or important effects
                if (!s->modelindex && s->specialvisibilityradius == 0)
                        return;
-               if (s->tagentity)
+
+               // viewmodels don't have visibility checking
+               if (s->viewmodelforclient)
+               {
+                       if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
+                               return;
+               }
+               else if (s->tagentity)
                {
                        // tag attached entities simply check their parent
                        if (!sendentitiesindex[s->tagentity])
@@ -683,85 +789,69 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                        if (sententities[s->tagentity] != sententitiesmark)
                                return;
                }
-               // skip invalid modelindexes to avoid crashes
-               else if (s->modelindex >= MAX_MODELS)
-                       return;
-               // always send world submodels, they don't generate much traffic
-               // except in PROTOCOL_QUAKE where they hog bandwidth like crazy
-               else if (!(s->effects & EF_NODEPTHTEST) && (!(isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*') || (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)))
+               // always send world submodels in newer protocols because they don't
+               // generate much traffic (in old protocols they hog bandwidth)
+               else if (!(s->effects & EF_NODEPTHTEST) && !((isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*') && (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)))
                {
-                       Mod_CheckLoaded(model);
                        // entity has survived every check so far, check if visible
-                       // enlarged box to account for prediction (not that there is
-                       // any currently, but still helps the 'run into a room and
-                       // watch items pop up' problem)
-                       entmins[0] = s->origin[0] - 32.0f;
-                       entmins[1] = s->origin[1] - 32.0f;
-                       entmins[2] = s->origin[2] - 32.0f;
-                       entmaxs[0] = s->origin[0] + 32.0f;
-                       entmaxs[1] = s->origin[1] + 32.0f;
-                       entmaxs[2] = s->origin[2] + 32.0f;
-                       // using the model's bounding box to ensure things are visible regardless of their physics box
-                       if (model)
+                       ed = PRVM_EDICT_NUM(s->number);
+
+                       // if not touching a visible leaf
+                       if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
                        {
-                               if (s->angles[0] || s->angles[2]) // pitch and roll
-                               {
-                                       VectorAdd(entmins, model->rotatedmins, entmins);
-                                       VectorAdd(entmaxs, model->rotatedmaxs, entmaxs);
-                               }
-                               else if (s->angles[1])
+                               if (ed->priv.server->pvs_numclusters < 0)
                                {
-                                       VectorAdd(entmins, model->yawmins, entmins);
-                                       VectorAdd(entmaxs, model->yawmaxs, entmaxs);
+                                       // entity too big for clusters list
+                                       if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                       {
+                                               sv_writeentitiestoclient_culled_pvs++;
+                                               return;
+                                       }
                                }
                                else
                                {
-                                       VectorAdd(entmins, model->normalmins, entmins);
-                                       VectorAdd(entmaxs, model->normalmaxs, entmaxs);
+                                       int i;
+                                       // check cached clusters list
+                                       for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
+                                               if (CHECKPVSBIT(sv_writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
+                                                       break;
+                                       if (i == ed->priv.server->pvs_numclusters)
+                                       {
+                                               sv_writeentitiestoclient_culled_pvs++;
+                                               return;
+                                       }
                                }
                        }
-                       lightmins[0] = min(entmins[0], s->origin[0] - s->specialvisibilityradius);
-                       lightmins[1] = min(entmins[1], s->origin[1] - s->specialvisibilityradius);
-                       lightmins[2] = min(entmins[2], s->origin[2] - s->specialvisibilityradius);
-                       lightmaxs[0] = max(entmaxs[0], s->origin[0] + s->specialvisibilityradius);
-                       lightmaxs[1] = max(entmaxs[1], s->origin[1] + s->specialvisibilityradius);
-                       lightmaxs[2] = max(entmaxs[2], s->origin[2] + s->specialvisibilityradius);
-                       sv_writeentitiestoclient_totalentities++;
-                       // if not touching a visible leaf
-                       if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes && sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, lightmins, lightmaxs))
-                       {
-                               sv_writeentitiestoclient_culled_pvs++;
-                               return;
-                       }
+
                        // or not seen by random tracelines
                        if (sv_cullentities_trace.integer && !isbmodel)
                        {
                                // LordHavoc: test center first
-                               testorigin[0] = (entmins[0] + entmaxs[0]) * 0.5f;
-                               testorigin[1] = (entmins[1] + entmaxs[1]) * 0.5f;
-                               testorigin[2] = (entmins[2] + entmaxs[2]) * 0.5f;
+                               testorigin[0] = (ed->priv.server->cullmins[0] + ed->priv.server->cullmaxs[0]) * 0.5f;
+                               testorigin[1] = (ed->priv.server->cullmins[1] + ed->priv.server->cullmaxs[1]) * 0.5f;
+                               testorigin[2] = (ed->priv.server->cullmins[2] + ed->priv.server->cullmaxs[2]) * 0.5f;
                                sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, sv_writeentitiestoclient_testeye, testorigin, testorigin, SUPERCONTENTS_SOLID);
-                               if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
+                               if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
                                        sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
                                else
                                {
                                        // LordHavoc: test random offsets, to maximize chance of detection
-                                       testorigin[0] = lhrandom(entmins[0], entmaxs[0]);
-                                       testorigin[1] = lhrandom(entmins[1], entmaxs[1]);
-                                       testorigin[2] = lhrandom(entmins[2], entmaxs[2]);
+                                       testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
+                                       testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
+                                       testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
                                        sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, sv_writeentitiestoclient_testeye, testorigin, testorigin, SUPERCONTENTS_SOLID);
-                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
+                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
                                                sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
                                        else
                                        {
                                                if (s->specialvisibilityradius)
                                                {
                                                        // LordHavoc: test random offsets, to maximize chance of detection
-                                                       testorigin[0] = lhrandom(lightmins[0], lightmaxs[0]);
-                                                       testorigin[1] = lhrandom(lightmins[1], lightmaxs[1]);
-                                                       testorigin[2] = lhrandom(lightmins[2], lightmaxs[2]);
+                                                       testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
+                                                       testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
+                                                       testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
                                                        sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, sv_writeentitiestoclient_testeye, testorigin, testorigin, SUPERCONTENTS_SOLID);
-                                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
+                                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
                                                                sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
                                                }
                                        }
@@ -772,12 +862,13 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                                        return;
                                }
                        }
-                       sv_writeentitiestoclient_visibleentities++;
                }
        }
+
        // this just marks it for sending
        // FIXME: it would be more efficient to send here, but the entity
        // compressor isn't that flexible
+       sv_writeentitiestoclient_visibleentities++;
        sententities[s->number] = sententitiesmark;
 }
 
@@ -787,6 +878,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
 {
        int i, numsendstates;
        entity_state_t *s;
+       extern int csqc_clent;
 
        // if there isn't enough space to accomplish anything, skip it
        if (msg->cursize + 25 > msg->maxsize)
@@ -799,8 +891,6 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        sv_writeentitiestoclient_visibleentities = 0;
        sv_writeentitiestoclient_totalentities = 0;
 
-       Mod_CheckLoaded(sv.worldmodel);
-
 // find the client's PVS
        // the real place being tested from
        VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv_writeentitiestoclient_testeye);
@@ -808,7 +898,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        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_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
+       csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
 
        sententitiesmark++;
 
@@ -830,6 +920,8 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        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_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_trace);
 
+       EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
+
        if (client->entitydatabase5)
                EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
        else if (client->entitydatabase4)
@@ -870,8 +962,8 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        int             items;
        prvm_eval_t     *val;
        vec3_t  punchvector;
-       qbyte   viewzoom;
-       int             weaponmodelindex;
+       unsigned char   viewzoom;
+       const char *s;
 
 //
 // send a damage message
@@ -915,7 +1007,14 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_punchvector)))
                VectorCopy(val->vector, punchvector);
 
-       weaponmodelindex = SV_ModelIndex(PRVM_GetString(ent->fields.server->weaponmodel), 1);
+       // cache weapon model name and index in client struct to save time
+       // (this search can be almost 1% of cpu time!)
+       s = PRVM_GetString(ent->fields.server->weaponmodel);
+       if (strcmp(s, client->weaponmodel))
+       {
+               strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
+               client->weaponmodelindex = SV_ModelIndex(s, 1);
+       }
 
        viewzoom = 255;
        if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_viewzoom)))
@@ -948,7 +1047,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        stats[STAT_ITEMS] = items;
        stats[STAT_WEAPONFRAME] = ent->fields.server->weaponframe;
        stats[STAT_ARMOR] = ent->fields.server->armorvalue;
-       stats[STAT_WEAPON] = weaponmodelindex;
+       stats[STAT_WEAPON] = client->weaponmodelindex;
        stats[STAT_HEALTH] = ent->fields.server->health;
        stats[STAT_AMMO] = ent->fields.server->currentammo;
        stats[STAT_SHELLS] = ent->fields.server->ammo_shells;
@@ -1080,7 +1179,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
 SV_SendClientDatagram
 =======================
 */
-static qbyte sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
+static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
 qboolean SV_SendClientDatagram (client_t *client)
 {
        int rate, maxrate, maxsize, maxsize2;
@@ -1123,6 +1222,7 @@ qboolean SV_SendClientDatagram (client_t *client)
 
        // add the client specific data to the datagram
        SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
+       VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
        SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
 
        // expand packet size to allow effects to go over the rate limit
@@ -1246,7 +1346,7 @@ message buffer
 void SV_SendNop (client_t *client)
 {
        sizebuf_t       msg;
-       qbyte           buf[4];
+       unsigned char           buf[4];
 
        msg.data = buf;
        msg.maxsize = sizeof(buf);
@@ -1524,7 +1624,11 @@ Tell all the clients that the server is changing levels
 */
 void SV_SendReconnect (void)
 {
-       qbyte data[128];
+#if 1
+       MSG_WriteByte(&sv.reliable_datagram, svc_stufftext);
+       MSG_WriteString(&sv.reliable_datagram, "reconnect\n");
+#else
+       unsigned char data[128];
        sizebuf_t msg;
 
        msg.data = data;
@@ -1537,6 +1641,7 @@ void SV_SendReconnect (void)
 
        if (cls.state != ca_dedicated)
                Cmd_ExecuteString ("reconnect\n", src_command);
+#endif
 }
 
 
@@ -1598,8 +1703,8 @@ void SV_IncreaseEdicts(void)
 
        for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
        {
-               ent->priv.vp = (qbyte*) prog->edictprivate + i * prog->edictprivate_size;
-               ent->fields.server = (void *)((qbyte *)prog->edictsfields + i * prog->edict_size);
+               ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
+               ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
                // link every entity except world
                if (!ent->priv.server->free)
                        SV_LinkEdict(ent, false);
@@ -1658,8 +1763,7 @@ void SV_SpawnServer (const char *server)
        }
        else
        {
-               // make sure cvars have been checked before opening the ports
-               NetConn_ServerFrame();
+               // open server port
                NetConn_OpenServerPorts(true);
        }
 
@@ -1716,13 +1820,13 @@ void SV_SpawnServer (const char *server)
        /*for (i = 0;i < prog->max_edicts;i++)
        {
                ent = prog->edicts + i;
-               ent->priv.vp = (qbyte*) prog->edictprivate + i * prog->edictprivate_size;
-               ent->fields.server = (void *)((qbyte *)prog->edictsfields + i * prog->edict_size);
+               ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
+               ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
        }*/
 
-       // fix up client->edict pointers for returning clients right away...
+       // reset client csqc entity versions right away.
        for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
-               host_client->edict = PRVM_EDICT_NUM(i + 1);
+               EntityFrameCSQC_InitClientVersions(i, true);
 
        sv.datagram.maxsize = sizeof(sv.datagram_buf);
        sv.datagram.cursize = 0;
@@ -1790,10 +1894,21 @@ void SV_SpawnServer (const char *server)
 // serverflags are for cross level information (sigils)
        prog->globals.server->serverflags = svs.serverflags;
 
+       // we need to reset the spawned flag on all connected clients here so that
+       // their thinks don't run during startup (before PutClientInServer)
+       // we also need to set up the client entities now
+       // and we need to set the ->edict pointers to point into the progs edicts
+       for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+       {
+               host_client->spawned = false;
+               host_client->edict = PRVM_EDICT_NUM(i + 1);
+               PRVM_ED_ClearEdict(host_client->edict);
+       }
+
        // load replacement entity file if found
        entities = NULL;
        if (sv_entpatch.integer)
-               entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true);
+               entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL);
        if (entities)
        {
                Con_Printf("Loaded maps/%s.ent\n", sv.name);
@@ -1811,11 +1926,6 @@ void SV_SpawnServer (const char *server)
        sv.state = ss_active;
        prog->allowworldwrites = false;
 
-       // we need to reset the spawned flag on all connected clients here so that
-       // their thinks don't run during startup (before PutClientInServer)
-       for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
-               host_client->spawned = false;
-
 // run two frames to allow everything to settle
        for (i = 0;i < 2;i++)
        {
@@ -1829,11 +1939,32 @@ void SV_SpawnServer (const char *server)
        if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
                SV_CreateBaseline ();
 
-// send serverinfo to all connected clients
-       // (note this also handles botclients coming back from a level change)
+// 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++)
-               if (host_client->active)
+       {
+               if (!host_client->active)
+                       continue;
+               if (host_client->netconnection)
                        SV_SendServerinfo(host_client);
+               else
+               {
+                       int j;
+                       // if client is a botclient coming from a level change, we need to
+                       // set up client info that normally requires networking
+
+                       // copy spawn parms out of the client_t
+                       for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
+                               (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
+
+                       // call the spawn function
+                       host_client->clientconnectcalled = true;
+                       prog->globals.server->time = sv.time;
+                       prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
+                       PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
+                       PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
+                       host_client->spawned = true;
+               }
+       }
 
        Con_DPrint("Server spawned.\n");
        NetConn_Heartbeat (2);
@@ -1894,9 +2025,9 @@ void SV_VM_CB_InitEdict(prvm_edict_t *e)
                        val->_float = svs.clients[num].colors;
                // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
                if( eval_playermodel )
-                       PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
+                       PRVM_GETEDICTFIELDVALUE(e, eval_playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
                if( eval_playerskin )
-                       PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
+                       PRVM_GETEDICTFIELDVALUE(e, eval_playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
        }
 }
 
@@ -2032,6 +2163,14 @@ int eval_button5;
 int eval_button6;
 int eval_button7;
 int eval_button8;
+int eval_button9;
+int eval_button10;
+int eval_button11;
+int eval_button12;
+int eval_button13;
+int eval_button14;
+int eval_button15;
+int eval_button16;
 int eval_buttonuse;
 int eval_buttonchat;
 int eval_glow_size;
@@ -2076,12 +2215,17 @@ int eval_cursor_trace_ent;
 int eval_colormod;
 int eval_playermodel;
 int eval_playerskin;
+int eval_SendEntity;
+int eval_Version;
+int eval_customizeentityforclient;
 
 mfunction_t *SV_PlayerPhysicsQC;
 mfunction_t *EndFrameQC;
 //KrimZon - SERVER COMMANDS IN QUAKEC
 mfunction_t *SV_ParseClientCommandQC;
 
+ddef_t *PRVM_ED_FindGlobal(const char *name);
+
 void SV_VM_FindEdictFieldOffsets(void)
 {
        eval_gravity = PRVM_ED_FindFieldOffset("gravity");
@@ -2091,6 +2235,14 @@ void SV_VM_FindEdictFieldOffsets(void)
        eval_button6 = PRVM_ED_FindFieldOffset("button6");
        eval_button7 = PRVM_ED_FindFieldOffset("button7");
        eval_button8 = PRVM_ED_FindFieldOffset("button8");
+       eval_button9 = PRVM_ED_FindFieldOffset("button9");
+       eval_button10 = PRVM_ED_FindFieldOffset("button10");
+       eval_button11 = PRVM_ED_FindFieldOffset("button11");
+       eval_button12 = PRVM_ED_FindFieldOffset("button12");
+       eval_button13 = PRVM_ED_FindFieldOffset("button13");
+       eval_button14 = PRVM_ED_FindFieldOffset("button14");
+       eval_button15 = PRVM_ED_FindFieldOffset("button15");
+       eval_button16 = PRVM_ED_FindFieldOffset("button16");
        eval_buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
        eval_buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
        eval_glow_size = PRVM_ED_FindFieldOffset("glow_size");
@@ -2135,6 +2287,9 @@ void SV_VM_FindEdictFieldOffsets(void)
        eval_colormod = PRVM_ED_FindFieldOffset("colormod");
        eval_playermodel = PRVM_ED_FindFieldOffset("playermodel");
        eval_playerskin = PRVM_ED_FindFieldOffset("playerskin");
+       eval_SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
+       eval_Version = PRVM_ED_FindFieldOffset("Version");
+       eval_customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
 
        // LordHavoc: allowing QuakeC to override the player movement code
        SV_PlayerPhysicsQC = PRVM_ED_FindFunction ("SV_PlayerPhysics");
@@ -2142,6 +2297,12 @@ void SV_VM_FindEdictFieldOffsets(void)
        EndFrameQC = PRVM_ED_FindFunction ("EndFrame");
        //KrimZon - SERVER COMMANDS IN QUAKEC
        SV_ParseClientCommandQC = PRVM_ED_FindFunction ("SV_ParseClientCommand");
+
+       //[515]: init stufftext string (it is sent before svc_serverinfo)
+       if(PRVM_ED_FindGlobal("SV_InitCmd") && PRVM_ED_FindGlobal("SV_InitCmd")->type & ev_string)
+               SV_InitCmd = PRVM_G_STRING(PRVM_ED_FindGlobal("SV_InitCmd")->ofs);
+       else
+               SV_InitCmd = NULL;
 }
 
 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
@@ -2168,6 +2329,14 @@ prvm_required_field_t reqfields[] =
        {ev_float, "button6"},
        {ev_float, "button7"},
        {ev_float, "button8"},
+       {ev_float, "button9"},
+       {ev_float, "button10"},
+       {ev_float, "button11"},
+       {ev_float, "button12"},
+       {ev_float, "button13"},
+       {ev_float, "button14"},
+       {ev_float, "button15"},
+       {ev_float, "button16"},
        {ev_float, "buttonchat"},
        {ev_float, "buttonuse"},
        {ev_float, "clientcolors"},
@@ -2189,6 +2358,7 @@ prvm_required_field_t reqfields[] =
        {ev_float, "scale"},
        {ev_float, "style"},
        {ev_float, "tag_index"},
+       {ev_float, "Version"},
        {ev_float, "viewzoom"},
        {ev_vector, "color"},
        {ev_vector, "colormod"},
@@ -2198,7 +2368,9 @@ prvm_required_field_t reqfields[] =
        {ev_vector, "movement"},
        {ev_vector, "punchvector"},
        {ev_string, "playermodel"},
-       {ev_string, "playerskin"}
+       {ev_string, "playerskin"},
+       {ev_function, "SendEntity"},
+       {ev_function, "customizeentityforclient"},
 };
 
 void SV_VM_Setup(void)
@@ -2233,6 +2405,9 @@ void SV_VM_Setup(void)
        PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields );
        SV_VM_FindEdictFieldOffsets();
 
+       VM_AutoSentStats_Clear();//[515]: csqc
+       EntityFrameCSQC_ClearVersions();//[515]: csqc
+
        PRVM_End;
 }