]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
added sv_gameplayfix_consistentplayerprethink which defaults on - this
[xonotic/darkplaces.git] / sv_main.c
index 74bef76b39122ed27614da8051b95232812c1200..02c4690e83e5335b0e984ea4359186810b592ff9 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -63,7 +63,6 @@ cvar_t sv_areagrid_mingridsize = {CVAR_NOTIFY, "sv_areagrid_mingridsize", "64",
 cvar_t sv_checkforpacketsduringsleep = {0, "sv_checkforpacketsduringsleep", "0", "uses select() function to wait between frames which can be interrupted by packets being received, instead of Sleep()/usleep()/SDL_Sleep() functions which do not check for packets"};
 cvar_t sv_clmovement_enable = {0, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"};
 cvar_t sv_clmovement_minping = {0, "sv_clmovement_minping", "0", "if client ping is below this time in milliseconds, then their ability to use cl_movement prediction is disabled for a while (as they don't need it)"};
-cvar_t sv_clmovement_maxnetfps = {0, "sv_clmovement_maxnetfps", "0", "max amount of movement packets to accept per second"};
 cvar_t sv_clmovement_minping_disabletime = {0, "sv_clmovement_minping_disabletime", "1000", "when client falls below minping, disable their prediction for this many milliseconds (should be at least 1000 or else their prediction may turn on/off frequently)"};
 cvar_t sv_clmovement_inputtimeout = {0, "sv_clmovement_inputtimeout", "0.2", "when a client does not send input for this many seconds, force them to move anyway (unlike QuakeWorld)"};
 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!)"};
@@ -85,6 +84,7 @@ cvar_t sv_fixedframeratesingleplayer = {0, "sv_fixedframeratesingleplayer", "1",
 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
 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_consistentplayerprethink = {0, "sv_gameplayfix_consistentplayerprethink", "1", "improves fairness in multiplayer by running all PlayerPreThink functions (which fire weapons) before performing physics, then runing all PlayerPostThink functions"};
 cvar_t sv_gameplayfix_delayprojectiles = {0, "sv_gameplayfix_delayprojectiles", "1", "causes entities to not move on the same frame they are spawned, meaning that projectiles wait until the next frame to perform their first move, giving proper interpolation and rocket trails, but making weapons harder to use at low framerates"};
 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_gameplayfix_droptofloorstartsolid_nudgetocorrect = {0, "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect", "1", "tries to nudge stuck items and monsters out of walls before droptofloor is performed"};
@@ -93,13 +93,15 @@ cvar_t sv_gameplayfix_findradiusdistancetobox = {0, "sv_gameplayfix_findradiusdi
 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_multiplethinksperframe = {0, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
 cvar_t sv_gameplayfix_slidemoveprojectiles = {0, "sv_gameplayfix_slidemoveprojectiles", "1", "allows MOVETYPE_FLY/FLYMISSILE/TOSS/BOUNCE/BOUNCEMISSILE entities to finish their move in a frame even if they hit something, fixes 'gravity accumulation' bug for grenades on steep slopes"};
-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_noairborncorpse = {0, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
+cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {0, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"};
 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_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_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_gravityunaffectedbyticrate = {0, "sv_gameplayfix_gravityunaffectedbyticrate", "0", "fix some ticrate issues in physics."};
+cvar_t sv_gameplayfix_q2airaccelerate = {0, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
 cvar_t sv_idealpitchscale = {0, "sv_idealpitchscale","0.8", "how much to look up/down slopes and stairs when not using freelook"};
 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
@@ -127,6 +129,7 @@ cvar_t sv_warsowbunny_accel = {0, "sv_warsowbunny_accel", "0.1585", "how fast yo
 cvar_t sv_warsowbunny_topspeed = {0, "sv_warsowbunny_topspeed", "925", "soft speed limit (can get faster with rjs and on ramps)"};
 cvar_t sv_warsowbunny_turnaccel = {0, "sv_warsowbunny_turnaccel", "0", "max sharpness of turns (also master switch for the sv_warsowbunny_* mode; set this to 9 to enable)"};
 cvar_t sv_warsowbunny_backtosideratio = {0, "sv_warsowbunny_backtosideratio", "0.8", "lower values make it easier to change direction without losing speed; the drawback is \"understeering\" in sharp turns"};
+cvar_t sv_onlycsqcnetworking = {0, "sv_onlycsqcnetworking", "0", "disables legacy entity networking code for higher performance (except on clients, which can still be legacy)"};
 cvar_t sys_ticrate = {CVAR_SAVE, "sys_ticrate","0.0138889", "how long a server frame is in seconds, 0.05 is 20fps server rate, 0.1 is 10fps (can not be set higher than 0.1), 0 runs as many server frames as possible (makes games against bots a little smoother, overwhelms network players), 0.0138889 matches QuakeWorld physics"};
 cvar_t teamplay = {CVAR_NOTIFY, "teamplay","0", "teamplay mode, values depend on mod but typically 0 = no teams, 1 = no team damage no self damage, 2 = team damage and self damage, some mods support 3 = no team damage but can damage self"};
 cvar_t timelimit = {CVAR_NOTIFY, "timelimit","0", "ends level at this time (in minutes)"};
@@ -288,6 +291,7 @@ prvm_required_field_t reqfields[] =
        {ev_vector, "cursor_screen"},
        {ev_vector, "cursor_trace_endpos"},
        {ev_vector, "cursor_trace_start"},
+       {ev_vector, "glowmod"},
        {ev_vector, "movement"},
        {ev_vector, "punchvector"},
 };
@@ -350,7 +354,6 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_areagrid_mingridsize);
        Cvar_RegisterVariable (&sv_checkforpacketsduringsleep);
        Cvar_RegisterVariable (&sv_clmovement_enable);
-       Cvar_RegisterVariable (&sv_clmovement_maxnetfps);
        Cvar_RegisterVariable (&sv_clmovement_minping);
        Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
        Cvar_RegisterVariable (&sv_clmovement_inputtimeout);
@@ -373,6 +376,7 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_freezenonclients);
        Cvar_RegisterVariable (&sv_friction);
        Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
+       Cvar_RegisterVariable (&sv_gameplayfix_consistentplayerprethink);
        Cvar_RegisterVariable (&sv_gameplayfix_delayprojectiles);
        Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
        Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid_nudgetocorrect);
@@ -382,12 +386,14 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_gameplayfix_multiplethinksperframe);
        Cvar_RegisterVariable (&sv_gameplayfix_slidemoveprojectiles);
        Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse);
+       Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse_allowsuspendeditems);
        Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
        Cvar_RegisterVariable (&sv_gameplayfix_stepdown);
        Cvar_RegisterVariable (&sv_gameplayfix_stepwhilejumping);
        Cvar_RegisterVariable (&sv_gameplayfix_swiminbmodels);
        Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
        Cvar_RegisterVariable (&sv_gameplayfix_gravityunaffectedbyticrate);
+       Cvar_RegisterVariable (&sv_gameplayfix_q2airaccelerate);
        Cvar_RegisterVariable (&sv_gravity);
        Cvar_RegisterVariable (&sv_idealpitchscale);
        Cvar_RegisterVariable (&sv_jumpstep);
@@ -415,6 +421,7 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_warsowbunny_topspeed);
        Cvar_RegisterVariable (&sv_warsowbunny_turnaccel);
        Cvar_RegisterVariable (&sv_warsowbunny_backtosideratio);
+       Cvar_RegisterVariable (&sv_onlycsqcnetworking);
        Cvar_RegisterVariable (&sys_ticrate);
        Cvar_RegisterVariable (&teamplay);
        Cvar_RegisterVariable (&timelimit);
@@ -483,6 +490,11 @@ void SV_Init (void)
                // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
                Cvar_SetValueQuick (&sv_gameplayfix_findradiusdistancetobox, 0);
        }
+       if (gamemode == GAME_NEXUIZ)
+       {
+               // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
+               Cvar_SetValueQuick (&sv_gameplayfix_q2airaccelerate, 1);
+       }
 
        sv_mempool = Mem_AllocPool("server", 0, NULL);
 }
@@ -1003,11 +1015,18 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
        unsigned int version;
        unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
        unsigned int customizeentityforclient;
+       unsigned int sendentity;
        float f;
        vec3_t cullmins, cullmaxs;
        dp_model_t *model;
        prvm_eval_t *val, *val2;
 
+       // fast path for games that do not use legacy entity networking
+       // note: still networks clients even if they are legacy
+       sendentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendEntity)->function;
+       if (sv_onlycsqcnetworking.integer && !sendentity && enumber > svs.maxclients)
+               return false;
+
        // this 2 billion unit check is actually to detect NAN origins
        // (we really don't want to send those)
        if (!(VectorLength2(ent->fields.server->origin) < 2000000000.0*2000000000.0))
@@ -1096,7 +1115,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
                return false;
 
        *cs = defaultstate;
-       cs->active = true;
+       cs->active = ACTIVE_NETWORK;
        cs->number = enumber;
        VectorCopy(ent->fields.server->origin, cs->origin);
        VectorCopy(ent->fields.server->angles, cs->angles);
@@ -1125,6 +1144,16 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
                i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
        }
 
+       // don't need to init cs->glowmod because the defaultstate did that for us
+       //cs->glowmod[0] = cs->glowmod[1] = cs->glowmod[2] = 32;
+       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glowmod);
+       if (val->vector[0] || val->vector[1] || val->vector[2])
+       {
+               i = (int)(val->vector[0] * 32.0f);cs->glowmod[0] = bound(0, i, 255);
+               i = (int)(val->vector[1] * 32.0f);cs->glowmod[1] = bound(0, i, 255);
+               i = (int)(val->vector[2] * 32.0f);cs->glowmod[2] = bound(0, i, 255);
+       }
+
        cs->modelindex = modelindex;
 
        cs->alpha = 255;
@@ -1242,8 +1271,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
        // we need to do some csqc entity upkeep here
        // get self.SendFlags and clear them
        // (to let the QC know that they've been read)
-       val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendEntity);
-       if (val->function)
+       if (sendentity)
        {
                val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendFlags);
                sendflags = (unsigned int)val->_float;
@@ -1261,6 +1289,8 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
                if (sendflags)
                        for (i = 0;i < svs.maxclients;i++)
                                svs.clients[i].csqcentitysendflags[enumber] |= sendflags;
+               // mark it as inactive for non-csqc networking
+               cs->active = ACTIVE_SHARED;
        }
 
        return true;
@@ -1442,7 +1472,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize)
 {
        qboolean need_empty = false;
-       int i, numsendstates;
+       int i, numsendstates, numcsqcsendstates;
        entity_state_t *s;
        prvm_edict_t *camera;
        qboolean success;
@@ -1475,14 +1505,22 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
                SV_MarkWriteEntityStateToClient(sv.sendentities + i);
 
        numsendstates = 0;
+       numcsqcsendstates = 0;
        for (i = 0;i < sv.numsendentities;i++)
        {
                if (sv.sententities[sv.sendentities[i].number] == sv.sententitiesmark)
                {
-                       s = &sv.writeentitiestoclient_sendstates[numsendstates++];
-                       *s = sv.sendentities[i];
-                       if (s->exteriormodelforclient && s->exteriormodelforclient == sv.writeentitiestoclient_cliententitynumber)
-                               s->flags |= RENDER_EXTERIORMODEL;
+                       if(sv.sendentities[i].active == ACTIVE_NETWORK)
+                       {
+                               s = &sv.writeentitiestoclient_sendstates[numsendstates++];
+                               *s = sv.sendentities[i];
+                               if (s->exteriormodelforclient && s->exteriormodelforclient == sv.writeentitiestoclient_cliententitynumber)
+                                       s->flags |= RENDER_EXTERIORMODEL;
+                       }
+                       else if(sv.sendentities[i].active == ACTIVE_SHARED)
+                               sv.writeentitiestoclient_csqcsendstates[numcsqcsendstates++] = sv.sendentities[i].number;
+                       else
+                               Con_Printf("entity %d is in sv.sendentities and marked, but not active, please breakpoint me\n", sv.sendentities[i].number);
                }
        }
 
@@ -1490,9 +1528,9 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
                Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv.writeentitiestoclient_stats_totalentities, sv.writeentitiestoclient_stats_visibleentities, sv.writeentitiestoclient_stats_culled_pvs + sv.writeentitiestoclient_stats_culled_trace, sv.writeentitiestoclient_stats_culled_pvs, sv.writeentitiestoclient_stats_culled_trace);
 
        if(client->entitydatabase5)
-               need_empty = EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, client->entitydatabase5->latestframenum + 1);
+               need_empty = EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, client->entitydatabase5->latestframenum + 1);
        else
-               EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, 0);
+               EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, 0);
 
        if(client->num_skippedentityframes >= 10)
                need_empty = true; // force every 10th frame to be not empty (or cl_movement replay takes too long)
@@ -1666,6 +1704,9 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
 
        // movement settings for prediction
        // note: these are not sent in protocols with lower MAX_CL_STATS limits
+       stats[STAT_MOVEFLAGS] = MOVEFLAG_VALID
+               | (sv_gameplayfix_q2airaccelerate.integer ? MOVEFLAG_Q2AIRACCELERATE : 0)
+       ;
        statsf[STAT_MOVEVARS_TICRATE] = sys_ticrate.value;
        statsf[STAT_MOVEVARS_TIMESCALE] = slowmo.value;
        statsf[STAT_MOVEVARS_GRAVITY] = sv_gravity.value;
@@ -1829,7 +1870,7 @@ void SV_FlushBroadcastMessages(void)
        SZ_Clear(&sv.datagram);
 }
 
-static void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg, int maxsize)
+static void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg, int maxsize, int maxsize2)
 {
        // scan the splitpoints to find out how many we can fit in
        int numsegments, j, split;
@@ -1838,34 +1879,28 @@ static void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg, int max
        // always accept the first one if it's within 1024 bytes, this ensures
        // that very big datagrams which are over the rate limit still get
        // through, just to keep it working
-       j = msg->cursize + client->unreliablemsg_splitpoint[0];
-       if (maxsize < 1024 && j > maxsize && j <= 1024)
-       {
-               numsegments = 1;
-               maxsize = 1024;
-       }
-       else
-               for (numsegments = 0;numsegments < client->unreliablemsg_splitpoints;numsegments++)
-                       if (msg->cursize + client->unreliablemsg_splitpoint[numsegments] > 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 <= 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;
-       }
+       for (numsegments = 1;numsegments < client->unreliablemsg_splitpoints;numsegments++)
+               if (msg->cursize + client->unreliablemsg_splitpoint[numsegments] > maxsize)
+                       break;
+       // the first segment gets an exemption from the rate limiting, otherwise
+       // it could get dropped consistently due to a low rate limit
+       if (numsegments == 1)
+               maxsize = maxsize2;
+       // 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 <= 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;
 }
 
 /*
@@ -1971,7 +2006,7 @@ static void SV_SendClientDatagram (client_t *client)
                // add as many queued unreliable messages (effects) as we can fit
                // limit effects to half of the remaining space
                if (client->unreliablemsg.cursize)
-                       SV_WriteUnreliableMessages (client, &msg, (msg.cursize + maxsize) / 2);
+                       SV_WriteUnreliableMessages (client, &msg, maxsize/2, maxsize2);
 
                // now write as many entities as we can fit, and also sends stats
                SV_WriteEntitiesToClient (client, client->edict, &msg, maxsize);
@@ -2283,7 +2318,7 @@ static void SV_Download_f(void)
                
                Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
 
-               if(host_client->download_deflate)
+               if(host_client->download_deflate && svs.csqc_progdata_deflated)
                        host_client->download_file = FS_FileFromData(svs.csqc_progdata_deflated, svs.csqc_progsize_deflated, true);
                else
                        host_client->download_file = FS_FileFromData(svs.csqc_progdata, sv.csqc_progsize, true);
@@ -2717,8 +2752,13 @@ void SV_Prepare_CSQC(void)
                //unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool);
                svs.csqc_progdata_deflated = FS_Deflate(svs.csqc_progdata, progsize, &deflated_size, -1, sv_mempool);
                svs.csqc_progsize_deflated = (int)deflated_size;
-               Con_Printf("Deflated: %g%%\n", 100.0 - 100.0 * (deflated_size / (float)progsize));
-               Con_DPrintf("Uncompressed: %u\nCompressed:   %u\n", (unsigned)sv.csqc_progsize, (unsigned)svs.csqc_progsize_deflated);
+               if(svs.csqc_progdata_deflated)
+               {
+                       Con_Printf("Deflated: %g%%\n", 100.0 - 100.0 * (deflated_size / (float)progsize));
+                       Con_DPrintf("Uncompressed: %u\nCompressed:   %u\n", (unsigned)sv.csqc_progsize, (unsigned)svs.csqc_progsize_deflated);
+               }
+               else
+                       Con_Printf("Cannot compress - need zlib for this. Using uncompressed progs only.\n");
        }
 }
 
@@ -3053,7 +3093,7 @@ static void SV_VM_CB_EndIncreaseEdicts(void)
        // 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);
+                       SV_LinkEdict(ent);
 }
 
 static void SV_VM_CB_InitEdict(prvm_edict_t *e)
@@ -3213,6 +3253,7 @@ static void SV_VM_Setup(void)
        prog->init_cmd = VM_SV_Cmd_Init;
        prog->reset_cmd = VM_SV_Cmd_Reset;
        prog->error_cmd = Host_Error;
+       prog->ExecuteProgram = SVVM_ExecuteProgram;
 
        // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
        PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );