]> 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 ef06333b26eee33ac2d2b016265501b16c4886bb..7b89111e60d83f81163ef785b0450171ff01248d 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -20,14 +20,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // sv_main.c -- server main program
 
 #include "quakedef.h"
+#include "libcurl.h"
 
 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);
 
 
@@ -35,23 +36,45 @@ void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_sta
 cvar_t sv_protocolname = {0, "sv_protocolname", "DP7", "selects network protocol to host for (values include QUAKE, QUAKEDP, NEHAHRAMOVIE, DP1 and up)"};
 cvar_t sv_ratelimitlocalplayer = {0, "sv_ratelimitlocalplayer", "0", "whether to apply rate limiting to the local player in a listen server (only useful for testing)"};
 cvar_t sv_maxrate = {CVAR_SAVE | CVAR_NOTIFY, "sv_maxrate", "10000", "upper limit on client rate cvar, should reflect your network connection quality"};
+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)"};
 
 cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
 cvar_t sv_gameplayfix_noairborncorpse = {0, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
-cvar_t sv_gameplayfix_stepdown = {0, "sv_gameplayfix_stepdown", "1", "attempts to step down stairs, not just up them (prevents the familiar thud..thud..thud.. when running down stairs and slopes)"};
+cvar_t sv_gameplayfix_stepdown = {0, "sv_gameplayfix_stepdown", "0", "attempts to step down stairs, not just up them (prevents the familiar thud..thud..thud.. when running down stairs and slopes)"};
 cvar_t sv_gameplayfix_stepwhilejumping = {0, "sv_gameplayfix_stepwhilejumping", "1", "applies step-up onto a ledge even while airborn, useful if you would otherwise just-miss the floor when running across small areas with gaps (for instance running across the moving platforms in dm2, or jumping to the megahealth and red armor in dm2 rather than using the bridge)"};
 cvar_t sv_gameplayfix_swiminbmodels = {0, "sv_gameplayfix_swiminbmodels", "1", "causes pointcontents (used to determine if you are in a liquid) to check bmodel entities as well as the world model, so you can swim around in (possibly moving) water bmodel entities"};
 cvar_t sv_gameplayfix_setmodelrealbox = {0, "sv_gameplayfix_setmodelrealbox", "1", "fixes a bug in Quake that made setmodel always set the entity box to ('-16 -16 -16', '16 16 16') rather than properly checking the model box, breaks some poorly coded mods"};
 cvar_t sv_gameplayfix_blowupfallenzombies = {0, "sv_gameplayfix_blowupfallenzombies", "1", "causes findradius to detect SOLID_NOT entities such as zombies and corpses on the floor, allowing splash damage to apply to them"};
 cvar_t sv_gameplayfix_findradiusdistancetobox = {0, "sv_gameplayfix_findradiusdistancetobox", "1", "causes findradius to check the distance to the corner of a box rather than the center of the box, makes findradius detect bmodels such as very large doors that would otherwise be unaffected by splash damage"};
+cvar_t sv_gameplayfix_qwplayerphysics = {0, "sv_gameplayfix_qwplayerphysics", "1", "changes water jumping to make it easier to get out of water, and prevents friction on landing when bunnyhopping"};
+cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {0, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"};
+cvar_t sv_gameplayfix_droptofloorstartsolid = {0, "sv_gameplayfix_droptofloorstartsolid", "1", "prevents items and monsters that start in a solid area from falling out of the level (makes droptofloor treat trace_startsolid as an acceptable outcome)"};
 
 cvar_t sv_progs = {0, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
 
+// TODO: move these cvars here
+extern cvar_t sv_clmovement_enable;
+extern cvar_t sv_clmovement_minping;
+extern cvar_t sv_clmovement_minping_disabletime;
+extern cvar_t sv_clmovement_waitforinput;
+
 server_t sv;
 server_static_t svs;
 
@@ -60,8 +83,14 @@ 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");
+}
 
 /*
 ===============
@@ -70,20 +99,45 @@ SV_Init
 */
 void SV_Init (void)
 {
+       // init the csqc progs cvars, since they are updated/used by the server code
+       // TODO: fix this since this is a quick hack to make some of [515]'s broken code run ;) [9/13/2006 Black]
+       extern cvar_t csqc_progname;    //[515]: csqc crc check and right csprogs name according to progs.dat
+       extern cvar_t csqc_progcrc;
+       extern cvar_t csqc_progsize;
+       Cvar_RegisterVariable (&csqc_progname);
+       Cvar_RegisterVariable (&csqc_progcrc);
+       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);
        Cvar_RegisterVariable (&sv_gravity);
        Cvar_RegisterVariable (&sv_friction);
+       Cvar_RegisterVariable (&sv_waterfriction);
        Cvar_RegisterVariable (&sv_edgefriction);
        Cvar_RegisterVariable (&sv_stopspeed);
        Cvar_RegisterVariable (&sv_maxspeed);
        Cvar_RegisterVariable (&sv_maxairspeed);
        Cvar_RegisterVariable (&sv_accelerate);
+       Cvar_RegisterVariable (&sv_airaccelerate);
+       Cvar_RegisterVariable (&sv_wateraccelerate);
+       Cvar_RegisterVariable (&sv_clmovement_enable);
+       Cvar_RegisterVariable (&sv_clmovement_minping);
+       Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
+       Cvar_RegisterVariable (&sv_clmovement_waitforinput);
        Cvar_RegisterVariable (&sv_idealpitchscale);
        Cvar_RegisterVariable (&sv_aim);
        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);
@@ -94,14 +148,21 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
        Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
        Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
+       Cvar_RegisterVariable (&sv_gameplayfix_qwplayerphysics);
+       Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
+       Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
        Cvar_RegisterVariable (&sv_protocolname);
        Cvar_RegisterVariable (&sv_ratelimitlocalplayer);
        Cvar_RegisterVariable (&sv_maxrate);
+       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);
 }
@@ -136,7 +197,7 @@ Make sure the event gets sent to all clients
 */
 void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
 {
-       int             i, v;
+       int i;
 
        if (sv.datagram.cursize > MAX_PACKETFRAGMENT-18)
                return;
@@ -145,16 +206,10 @@ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
        MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
        MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
        for (i=0 ; i<3 ; i++)
-       {
-               v = dir[i]*16;
-               if (v > 127)
-                       v = 127;
-               else if (v < -128)
-                       v = -128;
-               MSG_WriteChar (&sv.datagram, v);
-       }
+               MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
        MSG_WriteByte (&sv.datagram, count);
        MSG_WriteByte (&sv.datagram, color);
+       SV_FlushBroadcastMessages();
 }
 
 /*
@@ -192,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();
 }
 
 /*
@@ -257,7 +313,7 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v
        if (field_mask & SND_VOLUME)
                MSG_WriteByte (&sv.datagram, volume);
        if (field_mask & SND_ATTENUATION)
-               MSG_WriteByte (&sv.datagram, attenuation*64);
+               MSG_WriteByte (&sv.datagram, (int)(attenuation*64));
        if (field_mask & SND_LARGEENTITY)
        {
                MSG_WriteShort (&sv.datagram, ent);
@@ -271,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();
 }
 
 /*
@@ -281,8 +338,6 @@ CLIENT SPAWNING
 ==============================================================================
 */
 
-static const char *SV_InitCmd; //[515]: svprogs able to send cmd to client on connect
-extern qboolean csqc_loaded;
 /*
 ================
 SV_SendServerinfo
@@ -308,20 +363,64 @@ void SV_SendServerinfo (client_t *client)
        // LordHavoc: clear entityframe tracking
        client->latestframenum = 0;
 
+       if (client->entitydatabase)
+               EntityFrame_FreeDatabase(client->entitydatabase);
+       if (client->entitydatabase4)
+               EntityFrame4_FreeDatabase(client->entitydatabase4);
+       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)
+                       client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
+               else if (sv.protocol == PROTOCOL_DARKPLACES4)
+                       client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool);
+               else
+                       client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
+       }
+
        SZ_Clear (&client->netconnection->message);
        MSG_WriteByte (&client->netconnection->message, svc_print);
-       dpsnprintf (message, sizeof (message), "\002\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
+       dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
        MSG_WriteString (&client->netconnection->message,message);
 
-       // FIXME: LordHavoc: this does not work on dedicated servers, needs fixing.
-//[515]: init csprogs according to version of svprogs, check the crc, etc.
-       if(csqc_loaded && (cls.state == ca_dedicated || PRVM_NUM_FOR_EDICT(client->edict) != 1))
+       //[515]: init csprogs according to version of svprogs, check the crc, etc.
+       if (sv.csqc_progname[0])
        {
+               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);
                MSG_WriteByte (&client->netconnection->message, svc_stufftext);
-               if(SV_InitCmd)
-                       MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i;%s\n", csqc_progcrc.integer, SV_InitCmd));
-               else
-                       MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", csqc_progcrc.integer));
+               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);
+                       MSG_WriteString (&client->netconnection->message, va("%s\n", PRVM_GetString(val->string)));
+               }
+       }
+
+       if (sv_allowdownloads.integer)
+       {
+               MSG_WriteByte (&client->netconnection->message, svc_stufftext);
+               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);
@@ -345,8 +444,8 @@ void SV_SendServerinfo (client_t *client)
 
 // send music
        MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
-       MSG_WriteByte (&client->netconnection->message, prog->edicts->fields.server->sounds);
-       MSG_WriteByte (&client->netconnection->message, prog->edicts->fields.server->sounds);
+       MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
+       MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
 
 // set view
        MSG_WriteByte (&client->netconnection->message, svc_setview);
@@ -355,8 +454,17 @@ void SV_SendServerinfo (client_t *client)
        MSG_WriteByte (&client->netconnection->message, svc_signonnum);
        MSG_WriteByte (&client->netconnection->message, 1);
 
-       client->sendsignon = true;
        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;
 }
 
 /*
@@ -387,12 +495,15 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
 
        Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
 
-       strcpy(client->name, "unconnected");
-       strcpy(client->old_name, "unconnected");
+       strlcpy(client->name, "unconnected", sizeof(client->name));
+       strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
        client->spawned = false;
        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
@@ -410,27 +521,15 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
                PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
                for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
                        client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
-       }
 
-       // set up the entity for this client (including .colormap, .team, etc)
-       PRVM_ED_ClearEdict(client->edict);
+               // set up the entity for this client (including .colormap, .team, etc)
+               PRVM_ED_ClearEdict(client->edict);
+       }
 
        // don't call SendServerinfo for a fresh botclient because its fields have
        // not been set up by the qc yet
        if (client->netconnection)
-       {
                SV_SendServerinfo (client);
-               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)
-                               client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
-                       else if (sv.protocol == PROTOCOL_DARKPLACES4)
-                               client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool);
-                       else
-                               client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
-               }
-
-       }
        else
                client->spawned = true;
 }
@@ -444,17 +543,6 @@ FRAME UPDATES
 ===============================================================================
 */
 
-/*
-==================
-SV_ClearDatagram
-
-==================
-*/
-void SV_ClearDatagram (void)
-{
-       SZ_Clear (&sv.datagram);
-}
-
 /*
 =============================================================================
 
@@ -473,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;
@@ -494,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)
        {
@@ -522,9 +622,9 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
                if (effects & 32)
                {
                        effects &= ~32;
-                       light[0] = 0.2;
-                       light[1] = 1;
-                       light[2] = 0.2;
+                       light[0] = (int)(0.2*256);
+                       light[1] = (int)(1.0*256);
+                       light[2] = (int)(0.2*256);
                        light[3] = 200;
                        lightpflags |= PFLAGS_FULLDYNAMIC;
                }
@@ -559,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))
@@ -582,36 +682,36 @@ 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 = val->vector[0] * 32.0f;cs->colormod[0] = bound(0, i, 255);
-               i = val->vector[1] * 32.0f;cs->colormod[1] = bound(0, i, 255);
-               i = val->vector[2] * 32.0f;cs->colormod[2] = bound(0, i, 255);
+               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_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;
@@ -619,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;
@@ -627,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;
@@ -676,8 +780,9 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
        }
        else
        {
-               VectorCopy(cs->origin, cullmins);
-               VectorCopy(cs->origin, cullmaxs);
+               // 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);
        }
        if (specialvisibilityradius)
        {
@@ -688,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)
                {
@@ -722,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;
@@ -769,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)
                {
@@ -786,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);
@@ -821,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, sv_writeentitiestoclient_testeye, testorigin, 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, sv_writeentitiestoclient_testeye, testorigin, 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, sv_writeentitiestoclient_testeye, testorigin, 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++;
@@ -868,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;
@@ -893,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++;
 
@@ -918,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();
+       }
 }
 
 /*
@@ -957,7 +1076,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        int             items;
        prvm_eval_t     *val;
        vec3_t  punchvector;
-       unsigned char   viewzoom;
+       int             viewzoom;
        const char *s;
 
 //
@@ -967,8 +1086,8 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        {
                other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
                MSG_WriteByte (msg, svc_damage);
-               MSG_WriteByte (msg, ent->fields.server->dmg_save);
-               MSG_WriteByte (msg, ent->fields.server->dmg_take);
+               MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
+               MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
                for (i=0 ; i<3 ; i++)
                        MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
 
@@ -982,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
@@ -1012,8 +1142,8 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        }
 
        viewzoom = 255;
-       if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_viewzoom)))
-               viewzoom = val->_float * 255.0f;
+       if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
+               viewzoom = (int)(val->_float * 255.0f);
        if (viewzoom == 0)
                viewzoom = 255;
 
@@ -1038,23 +1168,23 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        }
 
        memset(stats, 0, sizeof(int[MAX_CL_STATS]));
-       stats[STAT_VIEWHEIGHT] = ent->fields.server->view_ofs[2];
+       stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
        stats[STAT_ITEMS] = items;
-       stats[STAT_WEAPONFRAME] = ent->fields.server->weaponframe;
-       stats[STAT_ARMOR] = ent->fields.server->armorvalue;
+       stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
+       stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
        stats[STAT_WEAPON] = client->weaponmodelindex;
-       stats[STAT_HEALTH] = ent->fields.server->health;
-       stats[STAT_AMMO] = ent->fields.server->currentammo;
-       stats[STAT_SHELLS] = ent->fields.server->ammo_shells;
-       stats[STAT_NAILS] = ent->fields.server->ammo_nails;
-       stats[STAT_ROCKETS] = ent->fields.server->ammo_rockets;
-       stats[STAT_CELLS] = ent->fields.server->ammo_cells;
-       stats[STAT_ACTIVEWEAPON] = ent->fields.server->weapon;
+       stats[STAT_HEALTH] = (int)ent->fields.server->health;
+       stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
+       stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
+       stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
+       stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
+       stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
+       stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
        stats[STAT_VIEWZOOM] = viewzoom;
+       stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
+       stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
        // the QC bumps these itself by sending svc_'s, so we have to keep them
        // zero or they'll be corrected by the engine
-       //stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
-       //stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
        //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
        //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
 
@@ -1088,14 +1218,14 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
                MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
 
        if (bits & SU_IDEALPITCH)
-               MSG_WriteChar (msg, ent->fields.server->idealpitch);
+               MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
 
        for (i=0 ; i<3 ; i++)
        {
                if (bits & (SU_PUNCH1<<i))
                {
                        if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
-                               MSG_WriteChar(msg, ent->fields.server->punchangle[i]);
+                               MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
                        else
                                MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
                }
@@ -1109,7 +1239,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
                if (bits & (SU_VELOCITY1<<i))
                {
                        if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
-                               MSG_WriteChar(msg, ent->fields.server->velocity[i] * (1.0f / 16.0f));
+                               MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
                        else
                                MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
                }
@@ -1134,7 +1264,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
                MSG_WriteShort (msg, stats[STAT_CELLS]);
                MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
                if (bits & SU_VIEWZOOM)
-                       MSG_WriteShort (msg, min(stats[STAT_VIEWZOOM], 65535));
+                       MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
        }
        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)
        {
@@ -1162,81 +1292,191 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
                if (bits & SU_VIEWZOOM)
                {
                        if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
-                               MSG_WriteByte (msg, min(stats[STAT_VIEWZOOM], 255));
+                               MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
                        else
-                               MSG_WriteShort (msg, min(stats[STAT_VIEWZOOM], 65535));
+                               MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
                }
        }
 }
 
+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
 =======================
 */
 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
-qboolean SV_SendClientDatagram (client_t *client)
+void SV_SendClientDatagram (client_t *client)
 {
-       int rate, maxrate, maxsize, maxsize2;
+       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 = bound(NET_MINRATE, sv_maxrate.integer, NET_MAXRATE);
-               if (sv_maxrate.integer != maxrate)
-                       Cvar_SetValueQuick(&sv_maxrate, maxrate);
-
-               rate = bound(NET_MINRATE, client->rate, maxrate);
-               rate = (int)(client->rate * sys_ticrate.value);
-               maxsize = bound(100, rate, 1400);
+               // 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!)
+               maxsize = (int)(clientrate * sys_ticrate.value);
+               maxsize = bound(100, maxsize, 1400);
                maxsize2 = 1400;
        }
 
+       // while downloading, limit entity updates to half the packet
+       // (any leftover space will be used for downloading)
+       if (host_client->download_file)
+               maxsize /= 2;
+
        msg.data = sv_sendclientdatagram_buf;
        msg.maxsize = maxsize;
        msg.cursize = 0;
 
-       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);
+       // 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);
+               // 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);
+
+               // add as many queued unreliable messages (effects) as we can fit
+               // limit effects to half of the remaining space
+               msg.maxsize -= (msg.maxsize - msg.cursize) / 2;
+               if (client->unreliablemsg.cursize)
+                       SV_WriteUnreliableMessages (client, &msg);
+
+               msg.maxsize = maxsize;
+
+               // now write as many entities as we can fit, and also sends stats
+               SV_WriteEntitiesToClient (client, client->edict, &msg);
+       }
+       else if (realtime > client->keepalivetime)
+       {
+               // the player isn't totally in the game yet
+               // send small keepalive messages if too much time has passed
+               msg.maxsize = maxsize2;
+               client->keepalivetime = realtime + 5;
+               MSG_WriteChar (&msg, svc_nop);
+       }
 
-       // expand packet size to allow effects to go over the rate limit
-       // (dropping them is FAR too ugly)
        msg.maxsize = maxsize2;
 
-       // 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);
-
-// send the datagram
-       if (NetConn_SendUnreliableMessage (client->netconnection, &msg) == -1)
+       // if a download is active, see if there is room to fit some download data
+       // in this packet
+       downloadsize = maxsize * 2 - msg.cursize - 7;
+       if (host_client->download_file && host_client->download_started && downloadsize > 0)
        {
-               SV_DropClient (true);// if the message couldn't send, kick off
-               return false;
+               fs_offset_t downloadstart;
+               unsigned char data[1400];
+               downloadstart = FS_Tell(host_client->download_file);
+               downloadsize = min(downloadsize, (int)sizeof(data));
+               downloadsize = FS_Read(host_client->download_file, data, downloadsize);
+               // note this sends empty messages if at the end of the file, which is
+               // necessary to keep the packet loss logic working
+               // (the last blocks may be lost and need to be re-sent, and that will
+               //  only occur if the client acks the empty end messages, revealing
+               //  a gap in the download progress, causing the last blocks to be
+               //  sent again)
+               MSG_WriteChar (&msg, svc_downloaddata);
+               MSG_WriteLong (&msg, downloadstart);
+               MSG_WriteShort (&msg, downloadsize);
+               if (downloadsize > 0)
+                       SZ_Write (&msg, data, downloadsize);
        }
 
-       return true;
+// send the datagram
+       NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate);
 }
 
 /*
@@ -1269,8 +1509,8 @@ 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);
-                       strcpy(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);
                        MSG_WriteByte (&sv.reliable_datagram, i);
@@ -1279,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)
                {
@@ -1291,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
@@ -1330,30 +1570,6 @@ void SV_UpdateToReliableMessages (void)
 }
 
 
-/*
-=======================
-SV_SendNop
-
-Send a nop message without trashing or sending the accumulated client
-message buffer
-=======================
-*/
-void SV_SendNop (client_t *client)
-{
-       sizebuf_t       msg;
-       unsigned char           buf[4];
-
-       msg.data = buf;
-       msg.maxsize = sizeof(buf);
-       msg.cursize = 0;
-
-       MSG_WriteChar (&msg, svc_nop);
-
-       if (NetConn_SendUnreliableMessage (client->netconnection, &msg) == -1)
-               SV_DropClient (true);   // if the message couldn't send, kick off
-       client->last_message = realtime;
-}
-
 /*
 =======================
 SV_SendClientMessages
@@ -1363,6 +1579,11 @@ void SV_SendClientMessages (void)
 {
        int i, prepared = false;
 
+       if (sv.protocol == PROTOCOL_QUAKEWORLD)
+               Sys_Error("SV_SendClientMessages: no quakeworld support\n");
+
+       SV_FlushBroadcastMessages();
+
 // update frags, names, etc
        SV_UpdateToReliableMessages();
 
@@ -1380,49 +1601,158 @@ void SV_SendClientMessages (void)
                        continue;
                }
 
-               if (host_client->spawned)
+               if (!prepared)
                {
-                       if (!prepared)
-                       {
-                               prepared = true;
-                               // only prepare entities once per frame
-                               SV_PrepareEntitiesForSending();
-                       }
-                       if (!SV_SendClientDatagram (host_client))
-                               continue;
+                       prepared = true;
+                       // only prepare entities once per frame
+                       SV_PrepareEntitiesForSending();
                }
-               else
+               SV_SendClientDatagram (host_client);
+       }
+
+// clear muzzle flashes
+       SV_CleanupEnts();
+}
+
+void SV_StartDownload_f(void)
+{
+       if (host_client->download_file)
+               host_client->download_started = true;
+}
+
+void SV_Download_f(void)
+{
+       const char *whichpack, *whichpack2, *extension;
+
+       if (Cmd_Argc() != 2)
+       {
+               SV_ClientPrintf("usage: download <filename>\n");
+               return;
+       }
+
+       if (FS_CheckNastyPath(Cmd_Argv(1), false))
+       {
+               SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
+               return;
+       }
+
+       if (host_client->download_file)
+       {
+               // at this point we'll assume the previous download should be aborted
+               Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
+               Host_ClientCommands("\nstopdownload\n");
+
+               // close the file and reset variables
+               FS_Close(host_client->download_file);
+               host_client->download_file = NULL;
+               host_client->download_name[0] = 0;
+               host_client->download_expectedposition = 0;
+               host_client->download_started = false;
+       }
+
+       if (!sv_allowdownloads.integer)
+       {
+               SV_ClientPrintf("Downloads are disabled on this server\n");
+               Host_ClientCommands("\nstopdownload\n");
+               return;
+       }
+
+       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)
+               Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
+
+       if (!FS_FileExists(host_client->download_name))
+       {
+               SV_ClientPrintf("Download rejected: server does not have the file \"%s\"\nYou may need to 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;
+       }
+
+       // check if the user is trying to download part of registered Quake(r)
+       whichpack = FS_WhichPack(host_client->download_name);
+       whichpack2 = FS_WhichPack("gfx/pop.lmp");
+       if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
+       {
+               SV_ClientPrintf("Download rejected: file \"%s\" is part of registered Quake(r)\nYou must purchase Quake(r) from id Software or a retailer to get this file\nPlease go to http://www.idsoftware.com/games/quake/quake/index.php?game_section=buy\n", host_client->download_name);
+               Host_ClientCommands("\nstopdownload\n");
+               return;
+       }
+
+       // check if the server has forbidden archive downloads entirely
+       if (!sv_allowdownloads_inarchive.integer)
+       {
+               whichpack = FS_WhichPack(host_client->download_name);
+               if (whichpack)
                {
-               // the player isn't totally in the game yet
-               // send small keepalive messages if too much time has passed
-               // send a full message when the next signon stage has been requested
-               // some other message data (name changes, etc) may accumulate
-               // between signon stages
-                       if (!host_client->sendsignon)
-                       {
-                               if (realtime - host_client->last_message > 5)
-                                       SV_SendNop (host_client);
-                               continue;       // don't send out non-signon messages
-                       }
+                       SV_ClientPrintf("Download rejected: file \"%s\" is in an archive (\"%s\")\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name, whichpack);
+                       Host_ClientCommands("\nstopdownload\n");
+                       return;
                }
+       }
 
-               if (host_client->netconnection->message.cursize)
+       if (!sv_allowdownloads_config.integer)
+       {
+               if (!strcasecmp(extension, "cfg"))
                {
-                       if (!NetConn_CanSendMessage (host_client->netconnection))
-                               continue;
-
-                       if (NetConn_SendReliableMessage (host_client->netconnection, &host_client->netconnection->message) == -1)
-                               SV_DropClient (true);   // if the message couldn't send, kick off
-                       SZ_Clear (&host_client->netconnection->message);
-                       host_client->last_message = realtime;
-                       host_client->sendsignon = false;
+                       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;
                }
        }
 
-// clear muzzle flashes
-       SV_CleanupEnts();
-}
+       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)
+       {
+               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);
+                       Host_ClientCommands("\nstopdownload\n");
+                       return;
+               }
+       }
+
+       host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
+       if (!host_client->download_file)
+       {
+               SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
+               Host_ClientCommands("\nstopdownload\n");
+               return;
+       }
+
+       if (FS_FileSize(host_client->download_file) > 1<<30)
+       {
+               SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
+               Host_ClientCommands("\nstopdownload\n");
+               FS_Close(host_client->download_file);
+               host_client->download_file = NULL;
+               return;
+       }
+
+       Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
 
+       Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
+
+       host_client->download_expectedposition = 0;
+       host_client->download_started = false;
+
+       // the rest of the download process is handled in SV_SendClientDatagram
+       // and other code dealing with svc_downloaddata and clc_ackdownloaddata
+       //
+       // no svc_downloaddata messages will be sent until sv_startdownload is
+       // sent by the client
+}
 
 /*
 ==============================================================================
@@ -1529,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
@@ -1557,8 +2007,8 @@ void SV_CreateBaseline (void)
                // create entity baseline
                VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
                VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
-               svent->priv.server->baseline.frame = svent->fields.server->frame;
-               svent->priv.server->baseline.skin = svent->fields.server->skin;
+               svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
+               svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
                if (entnum > 0 && entnum <= svs.maxclients)
                {
                        svent->priv.server->baseline.colormap = entnum;
@@ -1567,7 +2017,7 @@ void SV_CreateBaseline (void)
                else
                {
                        svent->priv.server->baseline.colormap = 0;
-                       svent->priv.server->baseline.modelindex = svent->fields.server->modelindex;
+                       svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
                }
 
                large = false;
@@ -1614,7 +2064,7 @@ void SV_SaveSpawnparms (void)
 {
        int             i, j;
 
-       svs.serverflags = prog->globals.server->serverflags;
+       svs.serverflags = (int)prog->globals.server->serverflags;
 
        for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
        {
@@ -1648,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));
@@ -1709,16 +2159,24 @@ void SV_SpawnServer (const char *server)
 
        svs.changelevel_issued = false;         // now safe to issue another
 
+       // make the map a required file for clients
+       Curl_ClearRequirements();
+       Curl_RequireFile(modelname);
+
 //
 // tell all connected clients that we are going to a new level
 //
        if (sv.active)
        {
-               // Tell all the clients that the server is changing levels
-               SV_VM_Begin();
-               MSG_WriteByte(&sv.reliable_datagram, svc_stufftext);
-               MSG_WriteString(&sv.reliable_datagram, "reconnect\n");
-               SV_VM_End();
+               client_t *client;
+               for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
+               {
+                       if (client->netconnection)
+                       {
+                               MSG_WriteByte(&client->netconnection->message, svc_stufftext);
+                               MSG_WriteString(&client->netconnection->message, "reconnect\n");
+                       }
+               }
        }
        else
        {
@@ -1739,9 +2197,16 @@ void SV_SpawnServer (const char *server)
 //
 // set up the new server
 //
-       Host_ClearMemory ();
-
        memset (&sv, 0, sizeof(sv));
+       // if running a local client, make sure it doesn't try to access the last
+       // level's data which is no longer valiud
+       cls.signon = 0;
+
+       if(*sv_random_seed.string)
+       {
+               srand(sv_random_seed.integer);
+               Con_Printf("NOTE: random seed is %d; use for debugging/benchmarking only!\nUnset sv_random_seed to get real random numbers again.\n", sv_random_seed.integer);
+       }
 
        SV_VM_Setup();
 
@@ -1806,20 +2271,22 @@ 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;
 
        strlcpy (sv.name, server, sizeof (sv.name));
-       strcpy(sv.modelname, modelname);
+       strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
        sv.worldmodel = worldmodel;
        sv.models[1] = sv.worldmodel;
 
 //
 // 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]));
 
@@ -1842,6 +2309,10 @@ void SV_SpawnServer (const char *server)
        ent->fields.server->modelindex = 1;             // world model
        ent->fields.server->solid = SOLID_BSP;
        ent->fields.server->movetype = MOVETYPE_PUSH;
+       VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
+       VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
+       VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
+       VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
 
        if (coop.value)
                prog->globals.server->coop = coop.integer;
@@ -1865,10 +2336,7 @@ void SV_SpawnServer (const char *server)
        }
 
        // load replacement entity file if found
-       entities = NULL;
-       if (sv_entpatch.integer)
-               entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL);
-       if (entities)
+       if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL)))
        {
                Con_Printf("Loaded maps/%s.ent\n", sv.name);
                PRVM_ED_LoadFromFile (entities);
@@ -1888,7 +2356,7 @@ void SV_SpawnServer (const char *server)
 // run two frames to allow everything to settle
        for (i = 0;i < 2;i++)
        {
-               sv.frametime = host_frametime = 0.1;
+               sv.frametime = 0.1;
                SV_Physics ();
        }
 
@@ -1946,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)
@@ -1957,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)
@@ -1970,6 +2436,8 @@ void SV_VM_CB_InitEdict(prvm_edict_t *e)
        // LordHavoc: for consistency set these here
        int num = PRVM_NUM_FOR_EDICT(e) - 1;
 
+       e->priv.server->move = false; // don't move on first frame
+
        if (num >= 0 && num < svs.maxclients)
        {
                prvm_eval_t *val;
@@ -1980,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;
@@ -2131,156 +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;
-
-mfunction_t *SV_PlayerPhysicsQC;
-mfunction_t *EndFrameQC;
-//KrimZon - SERVER COMMANDS IN QUAKEC
-mfunction_t *SV_ParseClientCommandQC;
-
-ddef_t *PRVM_ED_FindGlobal(const char *name);
-
-void SV_VM_FindEdictFieldOffsets(void)
-{
-       eval_gravity = PRVM_ED_FindFieldOffset("gravity");
-       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");
-
-       // 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");
-
-       //[515]: init stufftext string (it is sent before svc_serverinfo)
-       if(PRVM_ED_FindGlobal("SV_InitCmd") && PRVM_ED_FindGlobal("SV_InitCmd")->type & ev_string)
-               SV_InitCmd = PRVM_G_STRING(PRVM_ED_FindGlobal("SV_InitCmd")->ofs);
-       else
-               SV_InitCmd = NULL;
-}
-
 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
 
 prvm_required_field_t reqfields[] =
@@ -2291,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"},
@@ -2317,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"},
@@ -2325,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"},
@@ -2334,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"},
@@ -2343,18 +2682,19 @@ 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"},
 };
 
 void SV_VM_Setup(void)
 {
+       extern cvar_t csqc_progname;    //[515]: csqc crc check and right csprogs name according to progs.dat
+       extern cvar_t csqc_progcrc;
+       extern cvar_t csqc_progsize;
+       size_t csprogsdatasize;
        PRVM_Begin;
        PRVM_InitProg( PRVM_SERVERPROG );
 
        // allocate the mempools
+       // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
        prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
        prog->builtins = vm_sv_builtins;
        prog->numbuiltins = vm_sv_numbuiltins;
@@ -2378,13 +2718,51 @@ 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;
+
+       // see if there is a csprogs.dat installed, and if so, set the csqc_progcrc accordingly, this will be sent to connecting clients to tell them to only load a matching csprogs.dat file
+       sv.csqc_progname[0] = 0;
+       sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
+       sv.csqc_progsize = csprogsdatasize;
+       if (sv.csqc_progsize > 0)
+       {
+               strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
+               Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
+       }
 }
 
 void SV_VM_Begin(void)
@@ -2392,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)