]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
added DP_SV_MODELFLAGS_AS_EFFECTS extension, this adds EF_ROCKET and
[xonotic/darkplaces.git] / sv_main.c
index aaf4163e70fd58b8327b26117c24f3959568e951..7b89111e60d83f81163ef785b0450171ff01248d 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -25,10 +25,10 @@ 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 VM_CustomStats_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_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);
 
 
@@ -39,11 +39,19 @@ cvar_t sv_maxrate = {CVAR_SAVE | CVAR_NOTIFY, "sv_maxrate", "10000", "upper limi
 cvar_t sv_allowdownloads = {0, "sv_allowdownloads", "1", "whether to allow clients to download files from the server (does not affect http downloads)"};
 cvar_t sv_allowdownloads_inarchive = {0, "sv_allowdownloads_inarchive", "0", "whether to allow downloads from archives (pak/pk3)"};
 cvar_t sv_allowdownloads_archive = {0, "sv_allowdownloads_archive", "0", "whether to allow downloads of archives (pak/pk3)"};
+cvar_t sv_allowdownloads_config = {0, "sv_allowdownloads_config", "0", "whether to allow downloads of config files (cfg)"};
+cvar_t sv_allowdownloads_dlcache = {0, "sv_allowdownloads_dlcache", "0", "whether to allow downloads of dlcache files (dlcache/)"};
 
 extern cvar_t sv_random_seed;
 
 static cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"}; // fast but loose
 static cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"}; // tends to get false negatives, uses a timeout to keep entities visible a short time after becoming hidden
+static cvar_t sv_cullentities_trace_samples = {0, "sv_cullentities_trace_samples", "1", "number of samples to test for entity culling"};
+static cvar_t sv_cullentities_trace_samples_extra = {0, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
+static cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
+static cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
+static cvar_t sv_cullentities_trace_prediction = {0, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"};
+static cvar_t sv_cullentities_nevercullbmodels = {0, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"};
 static cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
 static cvar_t sv_entpatch = {0, "sv_entpatch", "1", "enables loading of .ent files to override entities in the bsp (for example Threewave CTF server pack contains .ent patch files enabling play of CTF on id1 maps)"};
 
@@ -75,11 +83,15 @@ mempool_t *sv_mempool = NULL;
 //============================================================================
 
 extern void SV_Phys_Init (void);
-extern void SV_World_Init (void);
 static void SV_SaveEntFile_f(void);
 static void SV_StartDownload_f(void);
 static void SV_Download_f(void);
 
+void SV_AreaStats_f(void)
+{
+       World_PrintAreaStats(&sv.world, "server");
+}
+
 /*
 ===============
 SV_Init
@@ -97,6 +109,7 @@ void SV_Init (void)
        Cvar_RegisterVariable (&csqc_progsize);
 
        Cmd_AddCommand("sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)");
+       Cmd_AddCommand("sv_areastats", SV_AreaStats_f, "prints statistics on entity culling during collision traces");
        Cmd_AddCommand_WithClientCommand("sv_startdownload", NULL, SV_StartDownload_f, "begins sending a file to the client (network protocol use only)");
        Cmd_AddCommand_WithClientCommand("download", NULL, SV_Download_f, "downloads a specified file from the server");
        Cvar_RegisterVariable (&sv_maxvelocity);
@@ -119,6 +132,12 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_nostep);
        Cvar_RegisterVariable (&sv_cullentities_pvs);
        Cvar_RegisterVariable (&sv_cullentities_trace);
+       Cvar_RegisterVariable (&sv_cullentities_trace_samples);
+       Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
+       Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
+       Cvar_RegisterVariable (&sv_cullentities_trace_delay);
+       Cvar_RegisterVariable (&sv_cullentities_trace_prediction);
+       Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
        Cvar_RegisterVariable (&sv_cullentities_stats);
        Cvar_RegisterVariable (&sv_entpatch);
        Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
@@ -138,11 +157,12 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_allowdownloads);
        Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
        Cvar_RegisterVariable (&sv_allowdownloads_archive);
+       Cvar_RegisterVariable (&sv_allowdownloads_config);
+       Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
        Cvar_RegisterVariable (&sv_progs);
 
        SV_VM_Init();
        SV_Phys_Init();
-       SV_World_Init();
 
        sv_mempool = Mem_AllocPool("server", 0, NULL);
 }
@@ -189,6 +209,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();
 }
 
 /*
@@ -226,6 +247,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();
 }
 
 /*
@@ -305,6 +327,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();
 }
 
 /*
@@ -347,6 +370,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)
@@ -367,14 +393,14 @@ void SV_SendServerinfo (client_t *client)
        {
                prvm_eval_t *val;
                Con_DPrintf("sending csqc info to client (\"%s\" with size %i and crc %i)\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
-               //[515]: init stufftext string (it is sent before svc_serverinfo)
-               val = PRVM_GETGLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset("SV_InitCmd"));
                MSG_WriteByte (&client->netconnection->message, svc_stufftext);
                MSG_WriteString (&client->netconnection->message, va("csqc_progname %s\n", sv.csqc_progname));
                MSG_WriteByte (&client->netconnection->message, svc_stufftext);
                MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize));
                MSG_WriteByte (&client->netconnection->message, svc_stufftext);
                MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
+               //[515]: init stufftext string (it is sent before svc_serverinfo)
+               val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd);
                if (val)
                {
                        MSG_WriteByte (&client->netconnection->message, svc_stufftext);
@@ -388,6 +414,15 @@ void SV_SendServerinfo (client_t *client)
                MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1");
        }
 
+       // send at this time so it's guaranteed to get executed at the right time
+       {
+               client_t *save;
+               save = host_client;
+               host_client = client;
+               Curl_SendRequirements();
+               host_client = save;
+       }
+
        MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
        MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
        MSG_WriteByte (&client->netconnection->message, svs.maxclients);
@@ -419,15 +454,17 @@ void SV_SendServerinfo (client_t *client)
        MSG_WriteByte (&client->netconnection->message, svc_signonnum);
        MSG_WriteByte (&client->netconnection->message, 1);
 
-       {
-               client_t *save;
-               save = host_client;
-               host_client = client;
-               Curl_SendRequirements();
-               host_client = save;
-       }
-
        client->spawned = false;                // need prespawn, spawn, etc
+
+       // clear movement info until client enters the new level properly
+       memset(&client->cmd, 0, sizeof(client->cmd));
+       client->movesequence = 0;
+#ifdef NUM_PING_TIMES
+       for (i = 0;i < NUM_PING_TIMES;i++)
+               client->ping_times[i] = 0;
+       client->num_pings = 0;
+#endif
+       client->ping = 0;
 }
 
 /*
@@ -464,6 +501,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
@@ -503,17 +543,6 @@ FRAME UPDATES
 ===============================================================================
 */
 
-/*
-==================
-SV_ClearDatagram
-
-==================
-*/
-void SV_ClearDatagram (void)
-{
-       SZ_Clear (&sv.datagram);
-}
-
 /*
 =============================================================================
 
@@ -532,6 +561,18 @@ static int numsendentities;
 static entity_state_t sendentities[MAX_EDICTS];
 static entity_state_t *sendentitiesindex[MAX_EDICTS];
 
+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;
+
 qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
 {
        int i;
@@ -553,21 +594,21 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
        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);
+       i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
        glowsize = (unsigned char)bound(0, i, 255);
-       if (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_trail)->_float)
+       if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
                flags |= RENDER_GLOWTRAIL;
 
-       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[0]*256;
+       f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
        light[0] = (unsigned short)bound(0, f, 65535);
-       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[1]*256;
+       f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
        light[1] = (unsigned short)bound(0, f, 65535);
-       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[2]*256;
+       f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
        light[2] = (unsigned short)bound(0, f, 65535);
-       f = PRVM_GETEDICTFIELDVALUE(ent, eval_light_lev)->_float;
+       f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.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;
+       lightstyle = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
+       lightpflags = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
 
        if (gamemode == GAME_TENEBRAE)
        {
@@ -618,7 +659,7 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
 
        // early culling checks
        // (final culling is done by SV_MarkWriteEntityStateToClient)
-       customizeentityforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_customizeentityforclient)->function;
+       customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
        if (!customizeentityforclient)
        {
                if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
@@ -641,18 +682,18 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
        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->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_GETEDICTFIELDVALUE(ent, eval_tag_entity)->edict;
-       cs->tagindex = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_tag_index)->_float;
+       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_GETEDICTFIELDVALUE(ent, eval_colormod);
+       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);
@@ -663,14 +704,14 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
        cs->modelindex = modelindex;
 
        cs->alpha = 255;
-       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_alpha)->_float * 255.0f);
+       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_GETEDICTFIELDVALUE(ent, eval_renderamt)->_float);
+       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
        if (f)
        {
                i = (int)f;
@@ -678,7 +719,7 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
        }
 
        cs->scale = 16;
-       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_scale)->_float * 16.0f);
+       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
        if (f)
        {
                i = (int)f;
@@ -686,16 +727,20 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
        }
 
        cs->glowcolor = 254;
-       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_color)->_float);
+       f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
        if (f)
                cs->glowcolor = (int)f;
 
-       if (PRVM_GETEDICTFIELDVALUE(ent, eval_fullbright)->_float)
+       if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
                cs->effects |= EF_FULLBRIGHT;
 
+       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.modelflags);
+       if (val && val->_float)
+               cs->effects |= ((unsigned int)val->_float & 0xff) << 24;
+
        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)
+       if (cs->number != sv_writeentitiestoclient_clentnum && (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;
@@ -748,10 +793,17 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
                cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
                cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
        }
+       // calculate center of bbox for network prioritization purposes
+       VectorMAM(0.5f, cullmins, 0.5f, cullmaxs, cs->netcenter);
+       // if culling box has moved, update pvs cluster links
        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);
+               // a value of -1 for pvs_numclusters indicates that the links are not
+               // cached, and should be re-tested each time, this is the case if the
+               // culling box touches too many pvs clusters to store, or if the world
+               // model does not support FindBoxClusters
                ent->priv.server->pvs_numclusters = -1;
                if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
                {
@@ -782,25 +834,11 @@ void SV_PrepareEntitiesForSending(void)
        }
 }
 
-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;
-       vec3_t testorigin;
        model_t *model;
        prvm_edict_t *ed;
-       trace_t trace;
        if (sententitiesconsideration[s->number] == sententitiesmark)
                return;
        sententitiesconsideration[s->number] = sententitiesmark;
@@ -829,6 +867,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                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)
                {
@@ -846,7 +885,8 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                }
                // 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)))
+               // 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))
                {
                        // entity has survived every check so far, check if visible
                        ed = PRVM_EDICT_NUM(s->number);
@@ -881,36 +921,45 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                        // or not seen by random tracelines
                        if (sv_cullentities_trace.integer && !isbmodel)
                        {
-                               // LordHavoc: test center first
-                               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, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
-                               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
+                               int samples = s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer;
+                               float enlarge = sv_cullentities_trace_enlarge.value;
+
+                               qboolean visible = TRUE;
+
+                               do
                                {
-                                       // LordHavoc: test random offsets, to maximize chance of detection
-                                       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, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
-                                       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(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv_writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                               break; // directly visible from the server's view
+
+                                       if(sv_cullentities_trace_prediction.integer)
                                        {
-                                               if (s->specialvisibilityradius)
+                                               vec3_t predeye;
+
+                                               // get player velocity
+                                               float predtime = bound(0, host_client->ping, 0.2); // / 2
+                                                       // sorry, no wallhacking by high ping please, and at 200ms
+                                                       // ping a FPS is annoying to play anyway and a player is
+                                                       // likely to have changed his direction
+                                               VectorMA(sv_writeentitiestoclient_testeye, predtime, host_client->edict->fields.server->velocity, predeye);
+                                               if(sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, predeye)) // must be able to go there...
                                                {
-                                                       // LordHavoc: test random offsets, to maximize chance of detection
-                                                       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, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
-                                                       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;
+                                                       if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                                               break; // directly visible from the predicted view
+                                               }
+                                               else
+                                               {
+                                                       //Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n");
                                                }
                                        }
+
+                                       // when we get here, we can't see the entity
+                                       visible = FALSE;
                                }
+                               while(0);
+
+                               if(visible)
+                                       sv_writeentitiestoclient_client->visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
+
                                if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
                                {
                                        sv_writeentitiestoclient_culled_trace++;
@@ -928,9 +977,9 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
 }
 
 entity_state_t sendstates[MAX_EDICTS];
-extern int csqc_clent;
+extern int csqc_clientnum;
 
-void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
+void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg)
 {
        int i, numsendstates;
        entity_state_t *s;
@@ -953,7 +1002,8 @@ 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));
 
-       csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
+       sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
+       csqc_clientnum = sv_writeentitiestoclient_clentnum - 1;
 
        sententitiesmark++;
 
@@ -978,13 +1028,22 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
 
        if (client->entitydatabase5)
-               EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
+               EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, client->movesequence);
        else if (client->entitydatabase4)
+       {
                EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
+               Protocol_WriteStatsReliable();
+       }
        else if (client->entitydatabase)
+       {
                EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
+               Protocol_WriteStatsReliable();
+       }
        else
+       {
                EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
+               Protocol_WriteStatsReliable();
+       }
 }
 
 /*
@@ -1042,24 +1101,35 @@ 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);
-               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
        // mix in items2
-       val = PRVM_GETEDICTFIELDVALUE(ent, eval_items2);
+       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
        if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
                items = (int)ent->fields.server->items | ((int)val->_float << 23);
        else
                items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
 
        VectorClear(punchvector);
-       if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_punchvector)))
+       if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
                VectorCopy(val->vector, punchvector);
 
        // cache weapon model name and index in client struct to save time
@@ -1072,7 +1142,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        }
 
        viewzoom = 255;
-       if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_viewzoom)))
+       if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
                viewzoom = (int)(val->_float * 255.0f);
        if (viewzoom == 0)
                viewzoom = 255;
@@ -1229,6 +1299,60 @@ 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);
+}
+
+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
@@ -1237,36 +1361,42 @@ SV_SendClientDatagram
 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
 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];
 
+       // 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(100, rate, 1400);
+               maxsize = (int)(clientrate * sys_ticrate.value);
+               maxsize = bound(100, maxsize, 1400);
                maxsize2 = 1400;
        }
 
@@ -1279,24 +1409,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)
        {
@@ -1333,7 +1476,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);
 }
 
 /*
@@ -1366,7 +1509,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);
@@ -1376,7 +1519,7 @@ void SV_UpdateToReliableMessages (void)
 
                // DP_SV_CLIENTCOLORS
                // this is always found (since it's added by the progs loader)
-               if ((val = PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_clientcolors)))
+               if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
                        host_client->colors = (int)val->_float;
                if (host_client->old_colors != host_client->colors)
                {
@@ -1388,23 +1531,23 @@ void SV_UpdateToReliableMessages (void)
                }
 
                // NEXUIZ_PLAYERMODEL
-               if( eval_playermodel ) {
-                       model = PRVM_GetString(PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_playermodel)->string);
+               if( prog->fieldoffsets.playermodel >= 0 ) {
+                       model = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string);
                        if (model == NULL)
                                model = "";
                        // always point the string back at host_client->name to keep it safe
                        strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
-                       PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
+                       PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
                }
 
                // NEXUIZ_PLAYERSKIN
-               if( eval_playerskin ) {
-                       skin = PRVM_GetString(PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_playerskin)->string);
+               if( prog->fieldoffsets.playerskin >= 0 ) {
+                       skin = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string);
                        if (skin == NULL)
                                skin = "";
                        // always point the string back at host_client->name to keep it safe
                        strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
-                       PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
+                       PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
                }
 
                // frags
@@ -1439,6 +1582,8 @@ void SV_SendClientMessages (void)
        if (sv.protocol == PROTOCOL_QUAKEWORLD)
                Sys_Error("SV_SendClientMessages: no quakeworld support\n");
 
+       SV_FlushBroadcastMessages();
+
 // update frags, names, etc
        SV_UpdateToReliableMessages();
 
@@ -1513,6 +1658,7 @@ void SV_Download_f(void)
        }
 
        strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
+       extension = FS_FileExtension(host_client->download_name);
 
        // host_client is asking to download a specified file
        if (developer.integer >= 100)
@@ -1547,9 +1693,28 @@ void SV_Download_f(void)
                }
        }
 
+       if (!sv_allowdownloads_config.integer)
+       {
+               if (!strcasecmp(extension, "cfg"))
+               {
+                       SV_ClientPrintf("Download rejected: file \"%s\" is a .cfg file which is forbidden for security reasons\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
+                       Host_ClientCommands("\nstopdownload\n");
+                       return;
+               }
+       }
+
+       if (!sv_allowdownloads_dlcache.integer)
+       {
+               if (!strncasecmp(host_client->download_name, "dlcache/", 8))
+               {
+                       SV_ClientPrintf("Download rejected: file \"%s\" is in the dlcache/ directory which is forbidden for security reasons\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
+                       Host_ClientCommands("\nstopdownload\n");
+                       return;
+               }
+       }
+
        if (!sv_allowdownloads_archive.integer)
        {
-               extension = FS_FileExtension(host_client->download_name);
                if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
                {
                        SV_ClientPrintf("Download rejected: file \"%s\" is an archive\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
@@ -1694,6 +1859,126 @@ 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
+
+================
+*/
+int SV_ParticleEffectIndex(const char *name)
+{
+       int i, argc, linenumber, effectnameindex;
+       fs_offset_t filesize;
+       unsigned char *filedata;
+       const char *text, *textstart, *textend;
+       char argv[16][1024];
+       if (!sv.particleeffectnamesloaded)
+       {
+               sv.particleeffectnamesloaded = true;
+               memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
+               for (i = 0;i < EFFECT_TOTAL;i++)
+                       strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
+               filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
+               if (filedata)
+               {
+                       textstart = (const char *)filedata;
+                       textend = (const char *)filedata + filesize;
+                       text = textstart;
+                       for (linenumber = 1;;linenumber++)
+                       {
+                               argc = 0;
+                               for (;;)
+                               {
+                                       if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
+                                               break;
+                                       if (argc < 16)
+                                       {
+                                               strlcpy(argv[argc], com_token, sizeof(argv[argc]));
+                                               argc++;
+                                       }
+                               }
+                               if (com_token[0] == 0)
+                                       break; // if the loop exited and it's not a \n, it's EOF
+                               if (argc < 1)
+                                       continue;
+                               if (!strcmp(argv[0], "effect"))
+                               {
+                                       if (argc == 2)
+                                       {
+                                               for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
+                                               {
+                                                       if (sv.particleeffectname[effectnameindex][0])
+                                                       {
+                                                               if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
+                                                                       break;
+                                                       }
+                                                       else
+                                                       {
+                                                               strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
+                                                               break;
+                                                       }
+                                               }
+                                               // if we run out of names, abort
+                                               if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
+                                               {
+                                                       Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       Mem_Free(filedata);
+               }
+       }
+       // search for the name
+       for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
+               if (!strcmp(sv.particleeffectname[effectnameindex], name))
+                       return effectnameindex;
+       // return 0 if we couldn't find it
+       return 0;
+}
+
 /*
 ================
 SV_CreateBaseline
@@ -1813,7 +2098,7 @@ void SV_IncreaseEdicts(void)
                        SV_UnlinkEdict(prog->edicts + i);
                memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
        }
-       SV_ClearWorld();
+       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));
@@ -1986,7 +2271,7 @@ void SV_SpawnServer (const char *server)
        prog->allowworldwrites = true;
        sv.paused = false;
 
-       *prog->time = sv.time = 1.0;
+       prog->globals.server->time = sv.time = 1.0;
 
        Mod_ClearUsed();
        worldmodel->used = true;
@@ -1999,7 +2284,9 @@ void SV_SpawnServer (const char *server)
 //
 // clear world interaction links
 //
-       SV_ClearWorld ();
+       VectorCopy(sv.worldmodel->normalmins, sv.world.areagrid_mins);
+       VectorCopy(sv.worldmodel->normalmaxs, sv.world.areagrid_maxs);
+       World_Clear(&sv.world);
 
        strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
 
@@ -2127,10 +2414,10 @@ void SV_VM_CB_BeginIncreaseEdicts(void)
        for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
        {
                if (!ent->priv.server->free)
-                       SV_UnlinkEdict(prog->edicts + i);
+                       World_UnlinkEdict(prog->edicts + i);
                memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
        }
-       SV_ClearWorld();
+       World_Clear(&sv.world);
 }
 
 void SV_VM_CB_EndIncreaseEdicts(void)
@@ -2138,12 +2425,10 @@ void SV_VM_CB_EndIncreaseEdicts(void)
        int i;
        prvm_edict_t *ent;
 
-       for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
-       {
-               // link every entity except world
+       // link every entity except world
+       for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
                if (!ent->priv.server->free)
                        SV_LinkEdict(ent, false);
-       }
 }
 
 void SV_VM_CB_InitEdict(prvm_edict_t *e)
@@ -2163,19 +2448,32 @@ void SV_VM_CB_InitEdict(prvm_edict_t *e)
                // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
                // reset them
                e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
-               if ((val = PRVM_GETEDICTFIELDVALUE(e, eval_clientcolors)))
+               if ((val = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
                        val->_float = svs.clients[num].colors;
                // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
-               if( eval_playermodel )
-                       PRVM_GETEDICTFIELDVALUE(e, eval_playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
-               if( eval_playerskin )
-                       PRVM_GETEDICTFIELDVALUE(e, eval_playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
+               if( prog->fieldoffsets.playermodel >= 0 )
+                       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)
 {
-       SV_UnlinkEdict (ed);            // unlink from world bsp
+       World_UnlinkEdict(ed);          // unlink from world bsp
 
        ed->fields.server->model = 0;
        ed->fields.server->takedamage = 0;
@@ -2314,163 +2612,6 @@ void SV_VM_Init(void)
        Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
 }
 
-// LordHavoc: in an effort to eliminate time wasted on GetEdictFieldValue...  these are defined as externs in progs.h
-int eval_gravity;
-int eval_button3;
-int eval_button4;
-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;
-int eval_glow_trail;
-int eval_glow_color;
-int eval_items2;
-int eval_scale;
-int eval_alpha;
-int eval_renderamt; // HalfLife support
-int eval_rendermode; // HalfLife support
-int eval_fullbright;
-int eval_ammo_shells1;
-int eval_ammo_nails1;
-int eval_ammo_lava_nails;
-int eval_ammo_rockets1;
-int eval_ammo_multi_rockets;
-int eval_ammo_cells1;
-int eval_ammo_plasma;
-int eval_idealpitch;
-int eval_pitch_speed;
-int eval_viewmodelforclient;
-int eval_nodrawtoclient;
-int eval_exteriormodeltoclient;
-int eval_drawonlytoclient;
-int eval_ping;
-int eval_movement;
-int eval_pmodel;
-int eval_punchvector;
-int eval_viewzoom;
-int eval_clientcolors;
-int eval_tag_entity;
-int eval_tag_index;
-int eval_light_lev;
-int eval_color;
-int eval_style;
-int eval_pflags;
-int eval_cursor_active;
-int eval_cursor_screen;
-int eval_cursor_trace_start;
-int eval_cursor_trace_endpos;
-int eval_cursor_trace_ent;
-int eval_colormod;
-int eval_playermodel;
-int eval_playerskin;
-int eval_SendEntity;
-int eval_Version;
-int eval_customizeentityforclient;
-int eval_dphitcontentsmask;
-// DRESK - Support for Entity Contents Transition Event
-int eval_contentstransition;
-
-int gval_trace_dpstartcontents;
-int gval_trace_dphitcontents;
-int gval_trace_dphitq3surfaceflags;
-int gval_trace_dphittexturename;
-
-mfunction_t *SV_PlayerPhysicsQC;
-mfunction_t *EndFrameQC;
-//KrimZon - SERVER COMMANDS IN QUAKEC
-mfunction_t *SV_ParseClientCommandQC;
-
-void SV_VM_FindEdictFieldOffsets(void)
-{
-       eval_gravity = PRVM_ED_FindFieldOffset("gravity");
-       eval_button3 = PRVM_ED_FindFieldOffset("button3");
-       eval_button4 = PRVM_ED_FindFieldOffset("button4");
-       eval_button5 = PRVM_ED_FindFieldOffset("button5");
-       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");
-       eval_glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
-       eval_glow_color = PRVM_ED_FindFieldOffset("glow_color");
-       eval_items2 = PRVM_ED_FindFieldOffset("items2");
-       eval_scale = PRVM_ED_FindFieldOffset("scale");
-       eval_alpha = PRVM_ED_FindFieldOffset("alpha");
-       eval_renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
-       eval_rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
-       eval_fullbright = PRVM_ED_FindFieldOffset("fullbright");
-       eval_ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
-       eval_ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
-       eval_ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
-       eval_ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
-       eval_ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
-       eval_ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
-       eval_ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
-       eval_idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
-       eval_pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
-       eval_viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
-       eval_nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
-       eval_exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
-       eval_drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
-       eval_ping = PRVM_ED_FindFieldOffset("ping");
-       eval_movement = PRVM_ED_FindFieldOffset("movement");
-       eval_pmodel = PRVM_ED_FindFieldOffset("pmodel");
-       eval_punchvector = PRVM_ED_FindFieldOffset("punchvector");
-       eval_viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
-       eval_clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
-       eval_tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
-       eval_tag_index = PRVM_ED_FindFieldOffset("tag_index");
-       eval_light_lev = PRVM_ED_FindFieldOffset("light_lev");
-       eval_color = PRVM_ED_FindFieldOffset("color");
-       eval_style = PRVM_ED_FindFieldOffset("style");
-       eval_pflags = PRVM_ED_FindFieldOffset("pflags");
-       eval_cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
-       eval_cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
-       eval_cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
-       eval_cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
-       eval_cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
-       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");
-       eval_dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
-       // DRESK - Support for Entity Contents Transition Event
-       eval_contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
-
-       // LordHavoc: allowing QuakeC to override the player movement code
-       SV_PlayerPhysicsQC = PRVM_ED_FindFunction ("SV_PlayerPhysics");
-       // LordHavoc: support for endframe
-       EndFrameQC = PRVM_ED_FindFunction ("EndFrame");
-       //KrimZon - SERVER COMMANDS IN QUAKEC
-       SV_ParseClientCommandQC = PRVM_ED_FindFunction ("SV_ParseClientCommand");
-       gval_trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
-       gval_trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
-       gval_trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
-       gval_trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
-}
-
 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
 
 prvm_required_field_t reqfields[] =
@@ -2481,6 +2622,7 @@ prvm_required_field_t reqfields[] =
        {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"},
@@ -2507,6 +2649,7 @@ prvm_required_field_t reqfields[] =
        {ev_float, "buttonuse"},
        {ev_float, "clientcolors"},
        {ev_float, "cursor_active"},
+       {ev_float, "disableclientprediction"},
        {ev_float, "fullbright"},
        {ev_float, "glow_color"},
        {ev_float, "glow_size"},
@@ -2515,6 +2658,7 @@ prvm_required_field_t reqfields[] =
        {ev_float, "idealpitch"},
        {ev_float, "items2"},
        {ev_float, "light_lev"},
+       {ev_float, "modelflags"},
        {ev_float, "pflags"},
        {ev_float, "ping"},
        {ev_float, "pitch_speed"},
@@ -2524,8 +2668,13 @@ prvm_required_field_t reqfields[] =
        {ev_float, "scale"},
        {ev_float, "style"},
        {ev_float, "tag_index"},
-       {ev_float, "Version"},
        {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"},
@@ -2533,12 +2682,6 @@ prvm_required_field_t reqfields[] =
        {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)
@@ -2575,10 +2718,38 @@ void SV_VM_Setup(void)
        prog->error_cmd = Host_Error;
 
        // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
-       PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields );
-       SV_VM_FindEdictFieldOffsets();
-
-       VM_AutoSentStats_Clear();//[515]: csqc
+       PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
+
+       // some mods compiled with scrambling compilers lack certain critical
+       // global names and field names such as "self" and "time" and "nextthink"
+       // so we have to set these offsets manually, matching the entvars_t
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
+       PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
+       PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
+       // OP_STATE is always supported on server (due to entvars_t)
+       prog->flag |= PRVM_OP_STATE;
+
+       VM_CustomStats_Clear();//[515]: csqc
        EntityFrameCSQC_ClearVersions();//[515]: csqc
 
        PRVM_End;
@@ -2599,7 +2770,7 @@ void SV_VM_Begin(void)
        PRVM_Begin;
        PRVM_SetProg( PRVM_SERVERPROG );
 
-       *prog->time = (float) sv.time;
+       prog->globals.server->time = (float) sv.time;
 }
 
 void SV_VM_End(void)