]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
renamed COM_ParseTokenConsole to COM_ParseToken_Console
[xonotic/darkplaces.git] / sv_main.c
index ced0a7129c92b0d82f44f1e67ed0d3e083f850db..0fcde551b9f25be19878fdad09eb01be3fd14511 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -22,13 +22,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "libcurl.h"
 
-void SV_VM_Init();
-void SV_VM_Setup();
+extern void SV_Phys_Init (void);
+static void SV_SaveEntFile_f(void);
+static void SV_StartDownload_f(void);
+static void SV_Download_f(void);
+static 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 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);
 
 
@@ -69,6 +70,40 @@ cvar_t sv_gameplayfix_droptofloorstartsolid = {0, "sv_gameplayfix_droptofloorsta
 
 cvar_t sv_progs = {0, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
 
+cvar_t pr_checkextension = {CVAR_READONLY, "pr_checkextension", "1", "indicates to QuakeC that the standard quakec extensions system is available (if 0, quakec should not attempt to use extensions)"};
+cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
+cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
+cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
+cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
+cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
+cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
+cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
+cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
+cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
+cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
+cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
+cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
+cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
+
 // TODO: move these cvars here
 extern cvar_t sv_clmovement_enable;
 extern cvar_t sv_clmovement_minping;
@@ -80,12 +115,125 @@ server_static_t svs;
 
 mempool_t *sv_mempool = NULL;
 
-//============================================================================
+extern cvar_t slowmo;
+extern float           scr_centertime_off;
 
-extern void SV_Phys_Init (void);
-static void SV_SaveEntFile_f(void);
-static void SV_StartDownload_f(void);
-static void SV_Download_f(void);
+// MUST match effectnameindex_t in client.h
+static const char *standardeffectnames[EFFECT_TOTAL] =
+{
+       "",
+       "TE_GUNSHOT",
+       "TE_GUNSHOTQUAD",
+       "TE_SPIKE",
+       "TE_SPIKEQUAD",
+       "TE_SUPERSPIKE",
+       "TE_SUPERSPIKEQUAD",
+       "TE_WIZSPIKE",
+       "TE_KNIGHTSPIKE",
+       "TE_EXPLOSION",
+       "TE_EXPLOSIONQUAD",
+       "TE_TAREXPLOSION",
+       "TE_TELEPORT",
+       "TE_LAVASPLASH",
+       "TE_SMALLFLASH",
+       "TE_FLAMEJET",
+       "EF_FLAME",
+       "TE_BLOOD",
+       "TE_SPARK",
+       "TE_PLASMABURN",
+       "TE_TEI_G3",
+       "TE_TEI_SMOKE",
+       "TE_TEI_BIGEXPLOSION",
+       "TE_TEI_PLASMAHIT",
+       "EF_STARDUST",
+       "TR_ROCKET",
+       "TR_GRENADE",
+       "TR_BLOOD",
+       "TR_WIZSPIKE",
+       "TR_SLIGHTBLOOD",
+       "TR_KNIGHTSPIKE",
+       "TR_VORESPIKE",
+       "TR_NEHAHRASMOKE",
+       "TR_NEXUIZPLASMA",
+       "TR_GLOWTRAIL",
+       "SVC_PARTICLE"
+};
+
+#define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
+
+prvm_required_field_t reqfields[] =
+{
+       {ev_entity, "cursor_trace_ent"},
+       {ev_entity, "drawonlytoclient"},
+       {ev_entity, "exteriormodeltoclient"},
+       {ev_entity, "nodrawtoclient"},
+       {ev_entity, "tag_entity"},
+       {ev_entity, "viewmodelforclient"},
+       {ev_float, "Version"},
+       {ev_float, "alpha"},
+       {ev_float, "ammo_cells1"},
+       {ev_float, "ammo_lava_nails"},
+       {ev_float, "ammo_multi_rockets"},
+       {ev_float, "ammo_nails1"},
+       {ev_float, "ammo_plasma"},
+       {ev_float, "ammo_rockets1"},
+       {ev_float, "ammo_shells1"},
+       {ev_float, "button3"},
+       {ev_float, "button4"},
+       {ev_float, "button5"},
+       {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"},
+       {ev_float, "cursor_active"},
+       {ev_float, "disableclientprediction"},
+       {ev_float, "fullbright"},
+       {ev_float, "glow_color"},
+       {ev_float, "glow_size"},
+       {ev_float, "glow_trail"},
+       {ev_float, "gravity"},
+       {ev_float, "idealpitch"},
+       {ev_float, "items2"},
+       {ev_float, "light_lev"},
+       {ev_float, "modelflags"},
+       {ev_float, "pflags"},
+       {ev_float, "ping"},
+       {ev_float, "pitch_speed"},
+       {ev_float, "pmodel"},
+       {ev_float, "renderamt"}, // HalfLife support
+       {ev_float, "rendermode"}, // HalfLife support
+       {ev_float, "scale"},
+       {ev_float, "style"},
+       {ev_float, "tag_index"},
+       {ev_float, "viewzoom"},
+       {ev_function, "SendEntity"},
+       {ev_function, "contentstransition"}, // DRESK - Support for Entity Contents Transition Event
+       {ev_function, "customizeentityforclient"},
+       {ev_string, "netaddress"},
+       {ev_string, "playermodel"},
+       {ev_string, "playerskin"},
+       {ev_vector, "color"},
+       {ev_vector, "colormod"},
+       {ev_vector, "cursor_screen"},
+       {ev_vector, "cursor_trace_endpos"},
+       {ev_vector, "cursor_trace_start"},
+       {ev_vector, "movement"},
+       {ev_vector, "punchvector"},
+};
+
+
+
+//============================================================================
 
 void SV_AreaStats_f(void)
 {
@@ -123,6 +271,9 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_accelerate);
        Cvar_RegisterVariable (&sv_airaccelerate);
        Cvar_RegisterVariable (&sv_wateraccelerate);
+       Cvar_RegisterVariable (&sv_jumpvelocity);
+       Cvar_RegisterVariable (&sv_airaccel_qw);
+       Cvar_RegisterVariable (&sv_airaccel_sideways_friction);
        Cvar_RegisterVariable (&sv_clmovement_enable);
        Cvar_RegisterVariable (&sv_clmovement_minping);
        Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
@@ -161,7 +312,44 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
        Cvar_RegisterVariable (&sv_progs);
 
-       SV_VM_Init();
+       Cvar_RegisterVariable (&pr_checkextension);
+       Cvar_RegisterVariable (&nomonsters);
+       Cvar_RegisterVariable (&gamecfg);
+       Cvar_RegisterVariable (&scratch1);
+       Cvar_RegisterVariable (&scratch2);
+       Cvar_RegisterVariable (&scratch3);
+       Cvar_RegisterVariable (&scratch4);
+       Cvar_RegisterVariable (&savedgamecfg);
+       Cvar_RegisterVariable (&saved1);
+       Cvar_RegisterVariable (&saved2);
+       Cvar_RegisterVariable (&saved3);
+       Cvar_RegisterVariable (&saved4);
+       // LordHavoc: Nehahra uses these to pass data around cutscene demos
+       if (gamemode == GAME_NEHAHRA)
+       {
+               Cvar_RegisterVariable (&nehx00);
+               Cvar_RegisterVariable (&nehx01);
+               Cvar_RegisterVariable (&nehx02);
+               Cvar_RegisterVariable (&nehx03);
+               Cvar_RegisterVariable (&nehx04);
+               Cvar_RegisterVariable (&nehx05);
+               Cvar_RegisterVariable (&nehx06);
+               Cvar_RegisterVariable (&nehx07);
+               Cvar_RegisterVariable (&nehx08);
+               Cvar_RegisterVariable (&nehx09);
+               Cvar_RegisterVariable (&nehx10);
+               Cvar_RegisterVariable (&nehx11);
+               Cvar_RegisterVariable (&nehx12);
+               Cvar_RegisterVariable (&nehx13);
+               Cvar_RegisterVariable (&nehx14);
+               Cvar_RegisterVariable (&nehx15);
+               Cvar_RegisterVariable (&nehx16);
+               Cvar_RegisterVariable (&nehx17);
+               Cvar_RegisterVariable (&nehx18);
+               Cvar_RegisterVariable (&nehx19);
+       }
+       Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
+
        SV_Phys_Init();
 
        sv_mempool = Mem_AllocPool("server", 0, NULL);
@@ -209,6 +397,7 @@ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
                MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
        MSG_WriteByte (&sv.datagram, count);
        MSG_WriteByte (&sv.datagram, color);
+       SV_FlushBroadcastMessages();
 }
 
 /*
@@ -246,6 +435,7 @@ void SV_StartEffect (vec3_t org, int modelindex, int startframe, int framecount,
                MSG_WriteByte (&sv.datagram, framecount);
                MSG_WriteByte (&sv.datagram, framerate);
        }
+       SV_FlushBroadcastMessages();
 }
 
 /*
@@ -325,6 +515,7 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v
                MSG_WriteByte (&sv.datagram, sound_num);
        for (i = 0;i < 3;i++)
                MSG_WriteCoord (&sv.datagram, entity->fields.server->origin[i]+0.5*(entity->fields.server->mins[i]+entity->fields.server->maxs[i]), sv.protocol);
+       SV_FlushBroadcastMessages();
 }
 
 /*
@@ -367,6 +558,9 @@ void SV_SendServerinfo (client_t *client)
        if (client->entitydatabase5)
                EntityFrame5_FreeDatabase(client->entitydatabase5);
 
+       memset(client->stats, 0, sizeof(client->stats));
+       memset(client->statsdeltabits, 0, sizeof(client->statsdeltabits));
+
        if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
        {
                if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
@@ -377,6 +571,9 @@ void SV_SendServerinfo (client_t *client)
                        client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
        }
 
+       // reset csqc entity versions
+       memset(client->csqcentityversion, 0, sizeof(client->csqcentityversion));
+
        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);
@@ -477,9 +674,6 @@ 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));
@@ -495,6 +689,9 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
        client->edict = PRVM_EDICT_NUM(clientnum+1);
        if (client->netconnection)
                client->netconnection->message.allowoverflow = true;            // we can catch it
+       // prepare the unreliable message buffer
+       client->unreliablemsg.data = client->unreliablemsg_data;
+       client->unreliablemsg.maxsize = sizeof(client->unreliablemsg_data);
        // updated by receiving "rate" command from client
        client->rate = NET_MINRATE;
        // no limits for local player
@@ -534,17 +731,6 @@ FRAME UPDATES
 ===============================================================================
 */
 
-/*
-==================
-SV_ClearDatagram
-
-==================
-*/
-void SV_ClearDatagram (void)
-{
-       SZ_Clear (&sv.datagram);
-}
-
 /*
 =============================================================================
 
@@ -556,23 +742,34 @@ crosses a waterline.
 =============================================================================
 */
 
-int sv_writeentitiestoclient_pvsbytes;
-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];
-
-qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
+static qboolean SV_BuildEntityState (entity_state_t *cs, prvm_edict_t *ent, int enumber)
 {
        int i;
+       unsigned int tagentity;
        unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
        unsigned int customizeentityforclient;
        float f;
-       vec3_t cullmins, cullmaxs;
+       vec3_t cullmins, cullmaxs, netcenter;
        model_t *model;
        prvm_eval_t *val;
 
+       // see if the customizeentityforclient extension is used by this entity
+       customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
+       if (customizeentityforclient)
+       {
+               prog->globals.server->self = enumber;
+               prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber;
+               PRVM_ExecuteProgram(customizeentityforclient, "customizeentityforclient: NULL function");
+               // customizeentityforclient can return false to reject the entity
+               if (!PRVM_G_FLOAT(OFS_RETURN))
+                       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;
+
        // 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;
@@ -581,13 +778,15 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
        // 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;
+       modelindex = (i >= 1 && i < MAX_MODELS && ent->fields.server->model && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
 
        flags = 0;
        i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
        glowsize = (unsigned char)bound(0, i, 255);
        if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
                flags |= RENDER_GLOWTRAIL;
+       if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict)
+               flags |= RENDER_VIEWMODEL;
 
        f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
        light[0] = (unsigned short)bound(0, f, 65535);
@@ -647,140 +846,74 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
                        specialvisibilityradius = max(specialvisibilityradius, 100);
        }
 
-       // early culling checks
-       // (final culling is done by SV_MarkWriteEntityStateToClient)
-       customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
-       if (!customizeentityforclient)
+       // don't send uninteresting entities
+       if (enumber != sv.writeentitiestoclient_cliententitynumber)
        {
-               if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
+               if (!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)
+               if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict == sv.writeentitiestoclient_cliententitynumber)
+                       return false;
+               if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict != sv.writeentitiestoclient_cliententitynumber)
+                       return false;
+               if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict != sv.writeentitiestoclient_cliententitynumber)
+                       return false;
+               if (flags & RENDER_VIEWMODEL && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict != sv.writeentitiestoclient_cliententitynumber)
+                       return false;
+               if (effects & EF_NODRAW)
                        return false;
        }
 
-
-       *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_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict;
-       cs->exteriormodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict;
-       cs->nodrawtoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict;
-       cs->drawonlytoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict;
-       cs->customizeentityforclient = customizeentityforclient;
-       cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
-       cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.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_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
-       if (val->vector[0] || val->vector[1] || val->vector[2])
-       {
-               i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
-               i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
-               i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
-       }
-
-       cs->modelindex = modelindex;
-
-       cs->alpha = 255;
-       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
-       if (f)
-       {
-               i = (int)f;
-               cs->alpha = (unsigned char)bound(0, i, 255);
-       }
-       // halflife
-       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
-       if (f)
-       {
-               i = (int)f;
-               cs->alpha = (unsigned char)bound(0, i, 255);
-       }
-
-       cs->scale = 16;
-       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
-       if (f)
-       {
-               i = (int)f;
-               cs->scale = (unsigned char)bound(0, i, 255);
-       }
-
-       cs->glowcolor = 254;
-       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
-       if (f)
-               cs->glowcolor = (int)f;
-
-       if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.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;
+       // don't send child if parent was rejected
+       // FIXME: it would be better to force the parent to send...
+       tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
+       if (tagentity && !SV_BuildEntityState(NULL, PRVM_EDICT_NUM(tagentity), tagentity))
+               return false;
 
        // 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
+               float scale = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float;
+               if (scale)
+                       scale *= (1.0f / 16.0f);
+               else
+                       scale = 1;
+               if (ent->fields.server->angles[0] || ent->fields.server->angles[2]) // pitch and roll
                {
-                       VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
-                       VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
+                       VectorMA(ent->fields.server->origin, scale, model->rotatedmins, cullmins);
+                       VectorMA(ent->fields.server->origin, scale, model->rotatedmaxs, cullmaxs);
                }
-               else if (cs->angles[1])
+               else if (ent->fields.server->angles[1])
                {
-                       VectorMA(cs->origin, scale, model->yawmins, cullmins);
-                       VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
+                       VectorMA(ent->fields.server->origin, scale, model->yawmins, cullmins);
+                       VectorMA(ent->fields.server->origin, scale, model->yawmaxs, cullmaxs);
                }
                else
                {
-                       VectorMA(cs->origin, scale, model->normalmins, cullmins);
-                       VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
+                       VectorMA(ent->fields.server->origin, scale, model->normalmins, cullmins);
+                       VectorMA(ent->fields.server->origin, scale, model->normalmaxs, cullmaxs);
                }
        }
        else
        {
                // if there is no model (or it could not be loaded), use the physics box
-               VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
-               VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
+               VectorAdd(ent->fields.server->origin, ent->fields.server->mins, cullmins);
+               VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, 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);
+               cullmins[0] = min(cullmins[0], ent->fields.server->origin[0] - specialvisibilityradius);
+               cullmins[1] = min(cullmins[1], ent->fields.server->origin[1] - specialvisibilityradius);
+               cullmins[2] = min(cullmins[2], ent->fields.server->origin[2] - specialvisibilityradius);
+               cullmaxs[0] = max(cullmaxs[0], ent->fields.server->origin[0] + specialvisibilityradius);
+               cullmaxs[1] = max(cullmaxs[1], ent->fields.server->origin[1] + specialvisibilityradius);
+               cullmaxs[2] = max(cullmaxs[2], ent->fields.server->origin[2] + specialvisibilityradius);
        }
+
        // calculate center of bbox for network prioritization purposes
-       VectorMAM(0.5f, cullmins, 0.5f, cullmaxs, cs->netcenter);
+       VectorMAM(0.5f, cullmins, 0.5f, cullmaxs, netcenter);
+
        // if culling box has moved, update pvs cluster links
        if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
        {
@@ -799,119 +932,36 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
                }
        }
 
-       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))
-               {
-                       sendentitiesindex[e] = sendentities + numsendentities;
-                       numsendentities++;
-               }
-       }
-}
-
-static int sententitiesmark = 0;
-static int sententities[MAX_EDICTS];
-static int sententitiesconsideration[MAX_EDICTS];
-static int sv_writeentitiestoclient_culled_pvs;
-static int sv_writeentitiestoclient_culled_trace;
-static int sv_writeentitiestoclient_visibleentities;
-static int sv_writeentitiestoclient_totalentities;
-//static entity_frame_t sv_writeentitiestoclient_entityframe;
-static int sv_writeentitiestoclient_clentnum;
-static vec3_t sv_writeentitiestoclient_testeye;
-static client_t *sv_writeentitiestoclient_client;
-
-void SV_MarkWriteEntityStateToClient(entity_state_t *s)
-{
-       int isbmodel;
-       model_t *model;
-       prvm_edict_t *ed;
-       if (sententitiesconsideration[s->number] == sententitiesmark)
-               return;
-       sententitiesconsideration[s->number] = sententitiesmark;
-       sv_writeentitiestoclient_totalentities++;
-
-       if (s->customizeentityforclient)
+       if (enumber != sv.writeentitiestoclient_cliententitynumber && !(effects & EF_NODEPTHTEST) && !(flags & RENDER_VIEWMODEL) && !tagentity)
        {
-               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
-       if (s->number != sv_writeentitiestoclient_clentnum)
-       {
-               // check various rejection conditions
-               if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
-                       return;
-               if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
-                       return;
-               if (s->effects & EF_NODRAW)
-                       return;
-               // LordHavoc: only send entities with a model or important effects
-               if (!s->modelindex && s->specialvisibilityradius == 0)
-                       return;
-
-               isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
-               // 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])
-                               return;
-                       SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
-                       if (sententities[s->tagentity] != sententitiesmark)
-                               return;
-               }
-               // always send world submodels in newer protocols because they don't
-               // generate much traffic (in old protocols they hog bandwidth)
-               // but only if sv_cullentities_alwayssendbmodels is on
-               else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
+               qboolean isbmodel = (model = sv.models[modelindex]) != NULL && model->name[0] == '*';
+               if (!isbmodel || !sv_cullentities_nevercullbmodels.integer)
                {
-                       // entity has survived every check so far, check if visible
-                       ed = PRVM_EDICT_NUM(s->number);
+                       // cull based on visibility
 
                        // if not touching a visible leaf
-                       if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
+                       if (sv_cullentities_pvs.integer && sv.writeentitiestoclient_pvsbytes)
                        {
-                               if (ed->priv.server->pvs_numclusters < 0)
+                               if (ent->priv.server->pvs_numclusters < 0)
                                {
                                        // 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))
+                                       if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv.writeentitiestoclient_pvs, cullmins, cullmaxs))
                                        {
-                                               sv_writeentitiestoclient_culled_pvs++;
-                                               return;
+                                               sv.writeentitiestoclient_stats_culled_pvs++;
+                                               return false;
                                        }
                                }
                                else
                                {
                                        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]))
+                                       for (i = 0;i < ent->priv.server->pvs_numclusters;i++)
+                                               if (CHECKPVSBIT(sv.writeentitiestoclient_pvs, ent->priv.server->pvs_clusterlist[i]))
                                                        break;
-                                       if (i == ed->priv.server->pvs_numclusters)
+                                       if (i == ent->priv.server->pvs_numclusters)
                                        {
-                                               sv_writeentitiestoclient_culled_pvs++;
-                                               return;
+                                               sv.writeentitiestoclient_stats_culled_pvs++;
+                                               return false;
                                        }
                                }
                        }
@@ -919,14 +969,14 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                        // or not seen by random tracelines
                        if (sv_cullentities_trace.integer && !isbmodel)
                        {
-                               int samples = s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer;
+                               int samples = specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer;
                                float enlarge = sv_cullentities_trace_enlarge.value;
 
-                               qboolean visible = TRUE;
+                               qboolean visible = true;
 
                                do
                                {
-                                       if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv_writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                       if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv.writeentitiestoclient_testeye, cullmins, cullmaxs))
                                                break; // directly visible from the server's view
 
                                        if(sv_cullentities_trace_prediction.integer)
@@ -938,10 +988,10 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                                                        // 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...
+                                               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))
+                                                       if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, cullmins, cullmaxs))
                                                                break; // directly visible from the predicted view
                                                }
                                                else
@@ -951,88 +1001,162 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                                        }
 
                                        // when we get here, we can't see the entity
-                                       visible = FALSE;
+                                       visible = false;
                                }
                                while(0);
 
                                if(visible)
-                                       sv_writeentitiestoclient_client->visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
-
-                               if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
+                                       svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[enumber] = realtime + sv_cullentities_trace_delay.value;
+                               else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[enumber])
                                {
-                                       sv_writeentitiestoclient_culled_trace++;
-                                       return;
+                                       sv.writeentitiestoclient_stats_culled_trace++;
+                                       return false;
                                }
                        }
                }
        }
 
-       // 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;
-}
+       // if the caller was just checking...  return true
+       if (!cs)
+               return true;
+
+       *cs = defaultstate;
+       cs->active = true;
+       cs->number = enumber;
+       VectorCopy(netcenter, cs->netcenter);
+       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->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
+       cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.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_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
+       if (val->vector[0] || val->vector[1] || val->vector[2])
+       {
+               i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
+               i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
+               i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
+       }
+
+       cs->modelindex = modelindex;
+
+       cs->alpha = 255;
+       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
+       if (f)
+       {
+               i = (int)f;
+               cs->alpha = (unsigned char)bound(0, i, 255);
+       }
+       // halflife
+       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
+       if (f)
+       {
+               i = (int)f;
+               cs->alpha = (unsigned char)bound(0, i, 255);
+       }
+
+       cs->scale = 16;
+       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
+       if (f)
+       {
+               i = (int)f;
+               cs->scale = (unsigned char)bound(0, i, 255);
+       }
+
+       cs->glowcolor = 254;
+       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
+       if (f)
+               cs->glowcolor = (int)f;
+
+       if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
+               cs->effects |= EF_FULLBRIGHT;
 
-entity_state_t sendstates[MAX_EDICTS];
-extern int csqc_clientnum;
+       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.modelflags);
+       if (val && val->_float)
+               cs->effects |= ((unsigned int)val->_float & 0xff) << 24;
 
-void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
+       if (ent->fields.server->movetype == MOVETYPE_STEP)
+               cs->flags |= RENDER_STEP;
+       if (cs->number != sv.writeentitiestoclient_cliententitynumber && (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 (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->edict && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->edict == sv.writeentitiestoclient_cliententitynumber)
+               cs->flags |= RENDER_EXTERIORMODEL;
+
+       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;
+
+       return true;
+}
+
+static void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg)
 {
-       int i, numsendstates;
-       entity_state_t *s;
+       int i;
+       int numsendstates;
+       prvm_edict_t *ent;
 
        // if there isn't enough space to accomplish anything, skip it
        if (msg->cursize + 25 > msg->maxsize)
                return;
 
-       sv_writeentitiestoclient_client = client;
+       sv.writeentitiestoclient_msg = msg;
+       sv.writeentitiestoclient_clientnumber = client - svs.clients;
 
-       sv_writeentitiestoclient_culled_pvs = 0;
-       sv_writeentitiestoclient_culled_trace = 0;
-       sv_writeentitiestoclient_visibleentities = 0;
-       sv_writeentitiestoclient_totalentities = 0;
+       sv.writeentitiestoclient_stats_culled_pvs = 0;
+       sv.writeentitiestoclient_stats_culled_trace = 0;
+       sv.writeentitiestoclient_stats_visibleentities = 0;
+       sv.writeentitiestoclient_stats_totalentities = 0;
 
 // find the client's PVS
        // the real place being tested from
-       VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv_writeentitiestoclient_testeye);
-       sv_writeentitiestoclient_pvsbytes = 0;
+       VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv.writeentitiestoclient_testeye);
+       sv.writeentitiestoclient_pvsbytes = 0;
        if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
-               sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
+               sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_testeye, 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs));
 
-       sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
-       csqc_clientnum = sv_writeentitiestoclient_clentnum - 1;
-
-       sententitiesmark++;
-
-       for (i = 0;i < numsendentities;i++)
-               SV_MarkWriteEntityStateToClient(sendentities + i);
+       sv.writeentitiestoclient_cliententitynumber = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
 
+       // send all entities that touch the pvs
        numsendstates = 0;
-       for (i = 0;i < numsendentities;i++)
-       {
-               if (sententities[sendentities[i].number] == sententitiesmark)
-               {
-                       s = &sendstates[numsendstates++];
-                       *s = sendentities[i];
-                       if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
-                               s->flags |= RENDER_EXTERIORMODEL;
-               }
-       }
+       for (i = 1, ent = PRVM_NEXT_EDICT(prog->edicts);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
+               if (!ent->priv.server->free && SV_BuildEntityState(sv.writeentitiestoclient_sendstates + numsendstates, ent, i))
+                       numsendstates++;
 
        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);
+               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, sendstates);
+       EntityFrameCSQC_WriteFrame(msg, numsendstates, sv.writeentitiestoclient_sendstates);
 
        if (client->entitydatabase5)
-               EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
+               EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence);
        else if (client->entitydatabase4)
-               EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
+       {
+               EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
+               Protocol_WriteStatsReliable();
+       }
        else if (client->entitydatabase)
-               EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
+       {
+               EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1);
+               Protocol_WriteStatsReliable();
+       }
        else
-               EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
+       {
+               EntityFrameQuake_WriteFrame(msg, numsendstates, sv.writeentitiestoclient_sendstates);
+               Protocol_WriteStatsReliable();
+       }
 }
 
 /*
@@ -1041,7 +1165,7 @@ SV_CleanupEnts
 
 =============
 */
-void SV_CleanupEnts (void)
+static void SV_CleanupEnts (void)
 {
        int             e;
        prvm_edict_t    *ent;
@@ -1067,6 +1191,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        vec3_t  punchvector;
        int             viewzoom;
        const char *s;
+       float   *statsf = (float *)stats;
 
 //
 // send a damage message
@@ -1090,15 +1215,23 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        SV_SetIdealPitch ();            // how much to look up / down ideally
 
 // a fixangle might get lost in a dropped packet.  Oh well.
-       if ( ent->fields.server->fixangle )
+       if(ent->fields.server->fixangle)
+       {
+               // angle fixing was requested by global thinking code...
+               // so store the current angles for later use
+               memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
+               host_client->fixangle_angles_set = TRUE;
+
+               // and clear fixangle for the next frame
+               ent->fields.server->fixangle = 0;
+       }
+
+       if (host_client->fixangle_angles_set)
        {
                MSG_WriteByte (msg, svc_setangle);
                for (i=0 ; i < 3 ; i++)
-                       MSG_WriteAngle (msg, ent->fields.server->angles[i], sv.protocol);
-               // LordHavoc: moved fixangle = 0 to the physics code so it is
-               // repeatedly sent to predicted clients even though they don't always
-               // move each frame
-               //ent->fields.server->fixangle = 0;
+                       MSG_WriteAngle (msg, host_client->fixangle_angles[i], sv.protocol);
+               host_client->fixangle_angles_set = FALSE;
        }
 
        // stuff the sigil bits into the high bits of items for sbar, or else
@@ -1169,6 +1302,25 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
        //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
 
+       // movement settings for prediction
+       statsf[STAT_MOVEVARS_TICRATE] = sys_ticrate.value;
+       statsf[STAT_MOVEVARS_TIMESCALE] = slowmo.value;
+       statsf[STAT_MOVEVARS_GRAVITY] = sv_gravity.value;
+       statsf[STAT_MOVEVARS_STOPSPEED] = sv_stopspeed.value;
+       statsf[STAT_MOVEVARS_MAXSPEED] = sv_maxspeed.value;
+       statsf[STAT_MOVEVARS_SPECTATORMAXSPEED] = sv_maxspeed.value; // FIXME: QW has a separate cvar for this
+       statsf[STAT_MOVEVARS_ACCELERATE] = sv_accelerate.value;
+       statsf[STAT_MOVEVARS_AIRACCELERATE] = sv_airaccelerate.value >= 0 ? sv_airaccelerate.value : sv_accelerate.value;
+       statsf[STAT_MOVEVARS_WATERACCELERATE] = sv_wateraccelerate.value >= 0 ? sv_wateraccelerate.value : sv_accelerate.value;
+       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
+       statsf[STAT_MOVEVARS_ENTGRAVITY] = val ? val->_float : 1.0f;
+       statsf[STAT_MOVEVARS_JUMPVELOCITY] = sv_jumpvelocity.value;
+       statsf[STAT_MOVEVARS_EDGEFRICTION] = sv_edgefriction.value;
+       statsf[STAT_MOVEVARS_MAXAIRSPEED] = sv_maxairspeed.value;
+       statsf[STAT_MOVEVARS_STEPHEIGHT] = sv_stepheight.value;
+       statsf[STAT_MOVEVARS_AIRACCEL_QW] = sv_airaccel_qw.value;
+       statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_airaccel_sideways_friction.value;
+
        if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
        {
                if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
@@ -1280,44 +1432,104 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        }
 }
 
+void SV_FlushBroadcastMessages(void)
+{
+       int i;
+       client_t *client;
+       if (sv.datagram.cursize <= 0)
+               return;
+       for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
+       {
+               if (!client->spawned || !client->netconnection || client->unreliablemsg.cursize + sv.datagram.cursize > client->unreliablemsg.maxsize || client->unreliablemsg_splitpoints >= (int)(sizeof(client->unreliablemsg_splitpoint)/sizeof(client->unreliablemsg_splitpoint[0])))
+                       continue;
+               SZ_Write(&client->unreliablemsg, sv.datagram.data, sv.datagram.cursize);
+               client->unreliablemsg_splitpoint[client->unreliablemsg_splitpoints++] = client->unreliablemsg.cursize;
+       }
+       SZ_Clear(&sv.datagram);
+}
+
+static void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg)
+{
+       // 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
+       // 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)
+       {
+               numsegments = 1;
+               msg->maxsize = 1400;
+       }
+       else
+               for (numsegments = 0;numsegments < client->unreliablemsg_splitpoints;numsegments++)
+                       if (msg->cursize + client->unreliablemsg_splitpoint[numsegments] > msg->maxsize)
+                               break;
+       if (numsegments > 0)
+       {
+               // some will fit, so add the ones that will fit
+               split = client->unreliablemsg_splitpoint[numsegments-1];
+               // 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)
+                       SZ_Write(msg, client->unreliablemsg.data, split);
+               // remove the part we sent, keeping any remaining data
+               client->unreliablemsg.cursize -= split;
+               if (client->unreliablemsg.cursize > 0)
+                       memmove(client->unreliablemsg.data, client->unreliablemsg.data + split, client->unreliablemsg.cursize);
+               // adjust remaining splitpoints
+               client->unreliablemsg_splitpoints -= numsegments;
+               for (j = 0;j < client->unreliablemsg_splitpoints;j++)
+                       client->unreliablemsg_splitpoint[j] = client->unreliablemsg_splitpoint[numsegments + j] - split;
+       }
+}
+
 /*
 =======================
 SV_SendClientDatagram
 =======================
 */
-static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
-void SV_SendClientDatagram (client_t *client)
+static void SV_SendClientDatagram (client_t *client)
 {
-       int rate, maxrate, maxsize, maxsize2, downloadsize;
+       int clientrate, maxrate, maxsize, maxsize2, downloadsize;
        sizebuf_t msg;
        int stats[MAX_CL_STATS];
+       unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE];
+
+       // 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
+               // for good singleplayer, send huge packets and never limit frequency
+               clientrate = 1000000000;
                maxsize = sizeof(sv_sendclientdatagram_buf);
                maxsize2 = sizeof(sv_sendclientdatagram_buf);
        }
        else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
        {
-               // no rate limiting support on older protocols because dp protocols
-               // 1-4 kick the client off if they overflow, and quake protocol shows
-               // less than the full entity set if rate limited
+               // no packet size limit support on older protocols because DP1-4 kick
+               // the client off if they overflow, and quake protocol shows less than
+               // the full entity set if rate limited
                maxsize = 1400;
                maxsize2 = 1400;
        }
        else
        {
-               // 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);
-
+               // DP5 and later protocols support packet size limiting which is a
+               // better method than limiting packet frequency as QW does
+               //
                // this rate limiting does not understand sys_ticrate 0
                // (but no one should be running that on a server!)
-               rate = bound(NET_MINRATE, client->rate, maxrate);
-               rate = (int)(rate * sys_ticrate.value);
-               maxsize = bound(50, rate, 1400);
+               maxsize = (int)(clientrate * sys_ticrate.value);
+               maxsize = bound(100, maxsize, 1400);
                maxsize2 = 1400;
        }
 
@@ -1330,24 +1542,37 @@ void SV_SendClientDatagram (client_t *client)
        msg.maxsize = maxsize;
        msg.cursize = 0;
 
-       if (host_client->spawned)
+       // 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))
+       {
+               // send the datagram
+               //NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate);
+               return;
+       }
+       else if (host_client->spawned)
        {
                MSG_WriteByte (&msg, svc_time);
                MSG_WriteFloat (&msg, sv.time);
 
                // 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);
+               // now update the stats[] array using any registered custom fields
+               VM_SV_UpdateCustomStats (client, client->edict, &msg, stats);
+               // set host_client->statsdeltabits
+               Protocol_UpdateClientStats (stats);
 
-               // expand packet size to allow effects to go over the rate limit
-               // (dropping them is FAR too ugly)
-               msg.maxsize = maxsize2;
+               // 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);
 
-               // copy the server datagram if there is space
-               // FIXME: put in delayed queue of effects to send
-               if (sv.datagram.cursize > 0 && msg.cursize + sv.datagram.cursize <= msg.maxsize)
-                       SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
+               msg.maxsize = maxsize;
+
+               // now write as many entities as we can fit, and also sends stats
+               SV_WriteEntitiesToClient (client, client->edict, &msg);
        }
        else if (realtime > client->keepalivetime)
        {
@@ -1384,7 +1609,7 @@ void SV_SendClientDatagram (client_t *client)
        }
 
 // send the datagram
-       NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
+       NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate);
 }
 
 /*
@@ -1392,7 +1617,7 @@ void SV_SendClientDatagram (client_t *client)
 SV_UpdateToReliableMessages
 =======================
 */
-void SV_UpdateToReliableMessages (void)
+static void SV_UpdateToReliableMessages (void)
 {
        int i, j;
        client_t *client;
@@ -1417,7 +1642,7 @@ void SV_UpdateToReliableMessages (void)
                if (strcmp(host_client->old_name, host_client->name))
                {
                        if (host_client->spawned)
-                               SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
+                               SV_BroadcastPrintf("%s^%i changed name to %s\n", host_client->old_name, STRING_COLOR_DEFAULT, host_client->name);
                        strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
                        // send notification to all clients
                        MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
@@ -1485,11 +1710,13 @@ SV_SendClientMessages
 */
 void SV_SendClientMessages (void)
 {
-       int i, prepared = false;
+       int i;
 
        if (sv.protocol == PROTOCOL_QUAKEWORLD)
                Sys_Error("SV_SendClientMessages: no quakeworld support\n");
 
+       SV_FlushBroadcastMessages();
+
 // update frags, names, etc
        SV_UpdateToReliableMessages();
 
@@ -1507,12 +1734,6 @@ void SV_SendClientMessages (void)
                        continue;
                }
 
-               if (!prepared)
-               {
-                       prepared = true;
-                       // only prepare entities once per frame
-                       SV_PrepareEntitiesForSending();
-               }
                SV_SendClientDatagram (host_client);
        }
 
@@ -1520,13 +1741,13 @@ void SV_SendClientMessages (void)
        SV_CleanupEnts();
 }
 
-void SV_StartDownload_f(void)
+static void SV_StartDownload_f(void)
 {
        if (host_client->download_file)
                host_client->download_started = true;
 }
 
-void SV_Download_f(void)
+static void SV_Download_f(void)
 {
        const char *whichpack, *whichpack2, *extension;
 
@@ -1765,47 +1986,6 @@ int SV_SoundIndex(const char *s, int precachemode)
        return 0;
 }
 
-// MUST match effectnameindex_t in client.h
-static const char *standardeffectnames[EFFECT_TOTAL] =
-{
-       "",
-       "TE_GUNSHOT",
-       "TE_GUNSHOTQUAD",
-       "TE_SPIKE",
-       "TE_SPIKEQUAD",
-       "TE_SUPERSPIKE",
-       "TE_SUPERSPIKEQUAD",
-       "TE_WIZSPIKE",
-       "TE_KNIGHTSPIKE",
-       "TE_EXPLOSION",
-       "TE_EXPLOSIONQUAD",
-       "TE_TAREXPLOSION",
-       "TE_TELEPORT",
-       "TE_LAVASPLASH",
-       "TE_SMALLFLASH",
-       "TE_FLAMEJET",
-       "EF_FLAME",
-       "TE_BLOOD",
-       "TE_SPARK",
-       "TE_PLASMABURN",
-       "TE_TEI_G3",
-       "TE_TEI_SMOKE",
-       "TE_TEI_BIGEXPLOSION",
-       "TE_TEI_PLASMAHIT",
-       "EF_STARDUST",
-       "TR_ROCKET",
-       "TR_GRENADE",
-       "TR_BLOOD",
-       "TR_WIZSPIKE",
-       "TR_SLIGHTBLOOD",
-       "TR_KNIGHTSPIKE",
-       "TR_VORESPIKE",
-       "TR_NEHAHRASMOKE",
-       "TR_NEXUIZPLASMA",
-       "TR_GLOWTRAIL",
-       "SVC_PARTICLE"
-};
-
 /*
 ================
 SV_ParticleEffectIndex
@@ -1836,7 +2016,7 @@ int SV_ParticleEffectIndex(const char *name)
                                argc = 0;
                                for (;;)
                                {
-                                       if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
+                                       if (!COM_ParseToken_Simple(&text, true) || !strcmp(com_token, "\n"))
                                                break;
                                        if (argc < 16)
                                        {
@@ -1891,7 +2071,7 @@ SV_CreateBaseline
 
 ================
 */
-void SV_CreateBaseline (void)
+static void SV_CreateBaseline (void)
 {
        int i, entnum, large;
        prvm_edict_t *svent;
@@ -1984,49 +2164,6 @@ void SV_SaveSpawnparms (void)
                        host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
        }
 }
-/*
-void SV_IncreaseEdicts(void)
-{
-       int i;
-       prvm_edict_t *ent;
-       int oldmax_edicts = prog->max_edicts;
-       void *oldedictsengineprivate = prog->edictprivate;
-       void *oldedictsfields = prog->edictsfields;
-       void *oldmoved_edicts = sv.moved_edicts;
-
-       if (prog->max_edicts >= MAX_EDICTS)
-               return;
-
-       // links don't survive the transition, so unlink everything
-       for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
-       {
-               if (!ent->priv.server->free)
-                       SV_UnlinkEdict(prog->edicts + i);
-               memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
-       }
-       World_Clear(&sv.world);
-
-       prog->max_edicts   = min(prog->max_edicts + 256, MAX_EDICTS);
-       prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
-       prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);
-       sv.moved_edicts = PR_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
-
-       memcpy(prog->edictprivate, oldedictsengineprivate, oldmax_edicts * sizeof(edict_engineprivate_t));
-       memcpy(prog->edictsfields, oldedictsfields, oldmax_edicts * prog->edict_size);
-
-       for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
-       {
-               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);
-       }
-
-       PR_Free(oldedictsengineprivate);
-       PR_Free(oldedictsfields);
-       PR_Free(oldmoved_edicts);
-}*/
 
 /*
 ================
@@ -2035,7 +2172,6 @@ SV_SpawnServer
 This is called at the start of each level
 ================
 */
-extern float           scr_centertime_off;
 
 void SV_SpawnServer (const char *server)
 {
@@ -2134,30 +2270,6 @@ void SV_SpawnServer (const char *server)
 // load progs to get entity field count
        //PR_LoadProgs ( sv_progs.string );
 
-       // allocate server memory
-       /*// start out with just enough room for clients and a reasonable estimate of entities
-       prog->max_edicts = max(svs.maxclients + 1, 512);
-       prog->max_edicts = min(prog->max_edicts, MAX_EDICTS);
-
-       // prvm_edict_t structures (hidden from progs)
-       prog->edicts = PR_Alloc(MAX_EDICTS * sizeof(prvm_edict_t));
-       // engine private structures (hidden from progs)
-       prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
-       // progs fields, often accessed by server
-       prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);*/
-       // used by PushMove to move back pushed entities
-       sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
-       /*for (i = 0;i < prog->max_edicts;i++)
-       {
-               ent = prog->edicts + i;
-               ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
-               ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
-       }*/
-
-       // reset client csqc entity versions right away.
-       for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
-               EntityFrameCSQC_InitClientVersions(i, true);
-
        sv.datagram.maxsize = sizeof(sv.datagram_buf);
        sv.datagram.cursize = 0;
        sv.datagram.data = sv.datagram_buf;
@@ -2308,14 +2420,11 @@ void SV_SpawnServer (const char *server)
 /////////////////////////////////////////////////////
 // SV VM stuff
 
-void SV_VM_CB_BeginIncreaseEdicts(void)
+static void SV_VM_CB_BeginIncreaseEdicts(void)
 {
        int i;
        prvm_edict_t *ent;
 
-       PRVM_Free( sv.moved_edicts );
-       sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
-
        // links don't survive the transition, so unlink everything
        for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
        {
@@ -2326,7 +2435,7 @@ void SV_VM_CB_BeginIncreaseEdicts(void)
        World_Clear(&sv.world);
 }
 
-void SV_VM_CB_EndIncreaseEdicts(void)
+static void SV_VM_CB_EndIncreaseEdicts(void)
 {
        int i;
        prvm_edict_t *ent;
@@ -2337,7 +2446,7 @@ void SV_VM_CB_EndIncreaseEdicts(void)
                        SV_LinkEdict(ent, false);
 }
 
-void SV_VM_CB_InitEdict(prvm_edict_t *e)
+static void SV_VM_CB_InitEdict(prvm_edict_t *e)
 {
        // LordHavoc: for consistency set these here
        int num = PRVM_NUM_FOR_EDICT(e) - 1;
@@ -2361,10 +2470,23 @@ void SV_VM_CB_InitEdict(prvm_edict_t *e)
                        PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
                if( prog->fieldoffsets.playerskin >= 0 )
                        PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
+               // Assign netaddress (IP Address, etc)
+               if(prog->fieldoffsets.netaddress >= 0)
+               { // Valid Field; Process
+                       if(svs.clients[num].netconnection != NULL)
+                       {// Valid Address; Assign
+                               // Acquire Readable Address
+                               LHNETADDRESS_ToString(&svs.clients[num].netconnection->peeraddress, svs.clients[num].netaddress, sizeof(svs.clients[num].netaddress), false);
+                               PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.netaddress)->string = PRVM_SetEngineString(svs.clients[num].netaddress);
+                       }
+                       else
+                               // Invalid / Bot
+                               PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.netaddress)->string = PRVM_SetEngineString("null/botclient");
+               }
        }
 }
 
-void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
+static void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
 {
        World_UnlinkEdict(ed);          // unlink from world bsp
 
@@ -2380,7 +2502,7 @@ void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
        ed->fields.server->solid = 0;
 }
 
-void SV_VM_CB_CountEdicts(void)
+static void SV_VM_CB_CountEdicts(void)
 {
        int             i;
        prvm_edict_t    *ent;
@@ -2408,7 +2530,7 @@ void SV_VM_CB_CountEdicts(void)
        Con_Printf("step      :%3i\n", step);
 }
 
-qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
+static qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
 {
        // remove things from different skill levels or deathmatch
        if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
@@ -2430,152 +2552,7 @@ qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
        return true;
 }
 
-cvar_t pr_checkextension = {CVAR_READONLY, "pr_checkextension", "1", "indicates to QuakeC that the standard quakec extensions system is available (if 0, quakec should not attempt to use extensions)"};
-cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
-cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
-cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
-cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
-cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
-cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
-cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
-cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
-cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
-cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
-cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
-cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
-cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
-
-void SV_VM_Init(void)
-{
-       Cvar_RegisterVariable (&pr_checkextension);
-       Cvar_RegisterVariable (&nomonsters);
-       Cvar_RegisterVariable (&gamecfg);
-       Cvar_RegisterVariable (&scratch1);
-       Cvar_RegisterVariable (&scratch2);
-       Cvar_RegisterVariable (&scratch3);
-       Cvar_RegisterVariable (&scratch4);
-       Cvar_RegisterVariable (&savedgamecfg);
-       Cvar_RegisterVariable (&saved1);
-       Cvar_RegisterVariable (&saved2);
-       Cvar_RegisterVariable (&saved3);
-       Cvar_RegisterVariable (&saved4);
-       // LordHavoc: Nehahra uses these to pass data around cutscene demos
-       if (gamemode == GAME_NEHAHRA)
-       {
-               Cvar_RegisterVariable (&nehx00);
-               Cvar_RegisterVariable (&nehx01);
-               Cvar_RegisterVariable (&nehx02);
-               Cvar_RegisterVariable (&nehx03);
-               Cvar_RegisterVariable (&nehx04);
-               Cvar_RegisterVariable (&nehx05);
-               Cvar_RegisterVariable (&nehx06);
-               Cvar_RegisterVariable (&nehx07);
-               Cvar_RegisterVariable (&nehx08);
-               Cvar_RegisterVariable (&nehx09);
-               Cvar_RegisterVariable (&nehx10);
-               Cvar_RegisterVariable (&nehx11);
-               Cvar_RegisterVariable (&nehx12);
-               Cvar_RegisterVariable (&nehx13);
-               Cvar_RegisterVariable (&nehx14);
-               Cvar_RegisterVariable (&nehx15);
-               Cvar_RegisterVariable (&nehx16);
-               Cvar_RegisterVariable (&nehx17);
-               Cvar_RegisterVariable (&nehx18);
-               Cvar_RegisterVariable (&nehx19);
-       }
-       Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
-}
-
-#define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
-
-prvm_required_field_t reqfields[] =
-{
-       {ev_entity, "cursor_trace_ent"},
-       {ev_entity, "drawonlytoclient"},
-       {ev_entity, "exteriormodeltoclient"},
-       {ev_entity, "nodrawtoclient"},
-       {ev_entity, "tag_entity"},
-       {ev_entity, "viewmodelforclient"},
-       {ev_float, "alpha"},
-       {ev_float, "ammo_cells1"},
-       {ev_float, "ammo_lava_nails"},
-       {ev_float, "ammo_multi_rockets"},
-       {ev_float, "ammo_nails1"},
-       {ev_float, "ammo_plasma"},
-       {ev_float, "ammo_rockets1"},
-       {ev_float, "ammo_shells1"},
-       {ev_float, "button3"},
-       {ev_float, "button4"},
-       {ev_float, "button5"},
-       {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"},
-       {ev_float, "cursor_active"},
-       {ev_float, "fullbright"},
-       {ev_float, "glow_color"},
-       {ev_float, "glow_size"},
-       {ev_float, "glow_trail"},
-       {ev_float, "gravity"},
-       {ev_float, "idealpitch"},
-       {ev_float, "items2"},
-       {ev_float, "light_lev"},
-       {ev_float, "pflags"},
-       {ev_float, "ping"},
-       {ev_float, "pitch_speed"},
-       {ev_float, "pmodel"},
-       {ev_float, "renderamt"}, // HalfLife support
-       {ev_float, "rendermode"}, // HalfLife support
-       {ev_float, "scale"},
-       {ev_float, "style"},
-       {ev_float, "tag_index"},
-       {ev_float, "Version"},
-       {ev_float, "viewzoom"},
-       {ev_vector, "color"},
-       {ev_vector, "colormod"},
-       {ev_vector, "cursor_screen"},
-       {ev_vector, "cursor_trace_endpos"},
-       {ev_vector, "cursor_trace_start"},
-       {ev_vector, "movement"},
-       {ev_vector, "punchvector"},
-       {ev_string, "playermodel"},
-       {ev_string, "playerskin"},
-       {ev_function, "SendEntity"},
-       {ev_function, "customizeentityforclient"},
-       // DRESK - Support for Entity Contents Transition Event
-       {ev_function, "contentstransition"},
-};
-
-void SV_VM_Setup(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;
@@ -2640,8 +2617,7 @@ void SV_VM_Setup(void)
        // OP_STATE is always supported on server (due to entvars_t)
        prog->flag |= PRVM_OP_STATE;
 
-       VM_AutoSentStats_Clear();//[515]: csqc
-       EntityFrameCSQC_ClearVersions();//[515]: csqc
+       VM_CustomStats_Clear();//[515]: csqc
 
        PRVM_End;