]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
new commands: prvm_edictget, prvm_globalget - read values from edicts/globals into...
[xonotic/darkplaces.git] / sv_main.c
index b796428a1c9b7ba43a3875714231b9a251095500..476be2853e5ec2be2ee395e139c2bb0cf3adcd1a 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 static void SV_SaveEntFile_f(void);
 static void SV_StartDownload_f(void);
 static void SV_Download_f(void);
-static void SV_VM_Setup();
+static void SV_VM_Setup(void);
 extern cvar_t net_connecttimeout;
 
 void VM_CustomStats_Clear (void);
@@ -50,6 +50,10 @@ cvar_t sv_aim = {CVAR_SAVE, "sv_aim", "2", "maximum cosine angle for quake's ver
 cvar_t sv_airaccel_qw = {0, "sv_airaccel_qw", "1", "ratio of QW-style air control as opposed to simple acceleration"};
 cvar_t sv_airaccel_sideways_friction = {0, "sv_airaccel_sideways_friction", "", "anti-sideways movement stabilization (reduces speed gain when zigzagging)"};
 cvar_t sv_airaccelerate = {0, "sv_airaccelerate", "-1", "rate at which a player accelerates to sv_maxairspeed while in the air, if less than 0 the sv_accelerate variable is used instead"};
+cvar_t sv_airstopaccelerate = {0, "sv_airstopaccelerate", "0", "when set, replacement for sv_airaccelerate when moving backwards"};
+cvar_t sv_airstrafeaccelerate = {0, "sv_airstrafeaccelerate", "0", "when set, replacement for sv_airaccelerate when just strafing"};
+cvar_t sv_maxairstrafespeed = {0, "sv_maxairstrafespeed", "0", "when set, replacement for sv_maxairspeed when just strafing"};
+cvar_t sv_aircontrol = {0, "sv_aircontrol", "0", "CPMA-style air control"};
 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_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)"};
@@ -59,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", "80", "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!)"};
@@ -89,13 +92,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)"};
@@ -118,6 +123,11 @@ cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to
 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
 cvar_t sv_wateraccelerate = {0, "sv_wateraccelerate", "-1", "rate at which a player accelerates to sv_maxspeed while in the air, if less than 0 the sv_accelerate variable is used instead"};
 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
+cvar_t sv_warsowbunny_airforwardaccel = {0, "sv_warsowbunny_airforwardaccel", "1.00001", "how fast you accelerate until you reach sv_maxspeed"};
+cvar_t sv_warsowbunny_accel = {0, "sv_warsowbunny_accel", "0.1585", "how fast you accelerate until after reaching sv_maxspeed (it gets harder as you near sv_warsowbunny_topspeed)"};
+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 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)"};
@@ -329,6 +339,10 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_airaccel_qw);
        Cvar_RegisterVariable (&sv_airaccel_sideways_friction);
        Cvar_RegisterVariable (&sv_airaccelerate);
+       Cvar_RegisterVariable (&sv_airstopaccelerate);
+       Cvar_RegisterVariable (&sv_airstrafeaccelerate);
+       Cvar_RegisterVariable (&sv_maxairstrafespeed);
+       Cvar_RegisterVariable (&sv_aircontrol);
        Cvar_RegisterVariable (&sv_allowdownloads);
        Cvar_RegisterVariable (&sv_allowdownloads_archive);
        Cvar_RegisterVariable (&sv_allowdownloads_config);
@@ -337,7 +351,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);
@@ -369,12 +382,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);
@@ -397,6 +412,11 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_wallfriction);
        Cvar_RegisterVariable (&sv_wateraccelerate);
        Cvar_RegisterVariable (&sv_waterfriction);
+       Cvar_RegisterVariable (&sv_warsowbunny_airforwardaccel);
+       Cvar_RegisterVariable (&sv_warsowbunny_accel);
+       Cvar_RegisterVariable (&sv_warsowbunny_topspeed);
+       Cvar_RegisterVariable (&sv_warsowbunny_turnaccel);
+       Cvar_RegisterVariable (&sv_warsowbunny_backtosideratio);
        Cvar_RegisterVariable (&sys_ticrate);
        Cvar_RegisterVariable (&teamplay);
        Cvar_RegisterVariable (&timelimit);
@@ -444,18 +464,32 @@ void SV_Init (void)
        Cvar_RegisterVariable (&halflifebsp);
 
        // any special defaults for gamemodes go here
+       if (gamemode == GAME_NEHAHRA)
+       {
+               // Nehahra pushable crates malfunction in some levels if this is on
+               Cvar_SetValueQuick (&sv_gameplayfix_upwardvelocityclearsongroundflag, 0);
+               // Nehahra NPC AI is confused by this feature
+               Cvar_SetValueQuick (&sv_gameplayfix_blowupfallenzombies, 0);
+       }
        if (gamemode == GAME_HIPNOTIC)
        {
                // hipnotic mission pack has issues in their 'friendly monster' ai, which seem to attempt to attack themselves for some reason when findradius() returns non-solid entities.
                Cvar_SetValueQuick (&sv_gameplayfix_blowupfallenzombies, 0);
                // hipnotic mission pack has issues with bobbing water entities 'jittering' between different heights on alternate frames at the default 0.0138889 ticrate, 0.02 avoids this issue
                Cvar_SetValueQuick (&sys_ticrate, 0.02);
+               // hipnotic mission pack has issues in their proximity mine sticking code, which causes them to bounce off.                                                                                     
+               Cvar_SetValueQuick (&sv_gameplayfix_slidemoveprojectiles, 0);                                                                                                                                   
        }
        if (gamemode == GAME_ROGUE)
        {
                // 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);
 }
@@ -1315,7 +1349,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                        ed = PRVM_EDICT_NUM(s->number);
 
                        // if not touching a visible leaf
-                       if (sv_cullentities_pvs.integer && sv.writeentitiestoclient_pvsbytes)
+                       if (sv_cullentities_pvs.integer && !r_novis.integer && sv.writeentitiestoclient_pvsbytes)
                        {
                                if (ed->priv.server->pvs_numclusters < 0)
                                {
@@ -1418,6 +1452,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        int i, numsendstates;
        entity_state_t *s;
        prvm_edict_t *camera;
+       qboolean success;
 
        // if there isn't enough space to accomplish anything, skip it
        if (msg->cursize + 25 > maxsize)
@@ -1466,23 +1501,31 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        else
                EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, 0);
 
+       if(client->num_skippedentityframes >= 10)
+               need_empty = true; // force every 10th frame to be not empty (or cl_movement replay takes too long)
+
        if (client->entitydatabase5)
-               EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty);
+               success = EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty);
        else if (client->entitydatabase4)
        {
-               EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
+               success = EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
                Protocol_WriteStatsReliable();
        }
        else if (client->entitydatabase)
        {
-               EntityFrame_WriteFrame(msg, maxsize, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1);
+               success = EntityFrame_WriteFrame(msg, maxsize, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1);
                Protocol_WriteStatsReliable();
        }
        else
        {
-               EntityFrameQuake_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates);
+               success = EntityFrameQuake_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates);
                Protocol_WriteStatsReliable();
        }
+
+       if(success)
+               client->num_skippedentityframes = 0;
+       else
+               ++client->num_skippedentityframes;
 }
 
 /*
@@ -1630,6 +1673,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;
@@ -1649,6 +1695,15 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_airaccel_sideways_friction.value;
        statsf[STAT_MOVEVARS_FRICTION] = sv_friction.value;
        statsf[STAT_MOVEVARS_WATERFRICTION] = sv_waterfriction.value >= 0 ? sv_waterfriction.value : sv_friction.value;
+       statsf[STAT_MOVEVARS_AIRSTOPACCELERATE] = sv_airstopaccelerate.value;
+       statsf[STAT_MOVEVARS_AIRSTRAFEACCELERATE] = sv_airstrafeaccelerate.value;
+       statsf[STAT_MOVEVARS_MAXAIRSTRAFESPEED] = sv_maxairstrafespeed.value;
+       statsf[STAT_MOVEVARS_AIRCONTROL] = sv_aircontrol.value;
+       statsf[STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL] = sv_warsowbunny_airforwardaccel.value;
+       statsf[STAT_MOVEVARS_WARSOWBUNNY_ACCEL] = sv_warsowbunny_accel.value;
+       statsf[STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED] = sv_warsowbunny_topspeed.value;
+       statsf[STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL] = sv_warsowbunny_turnaccel.value;
+       statsf[STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO] = sv_warsowbunny_backtosideratio.value;
        statsf[STAT_FRAGLIMIT] = fraglimit.value;
        statsf[STAT_TIMELIMIT] = timelimit.value;
 
@@ -1784,7 +1839,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;
@@ -1793,34 +1848,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;
 }
 
 /*
@@ -1926,7 +1975,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);
@@ -2238,7 +2287,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);
@@ -2672,8 +2721,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");
        }
 }
 
@@ -2874,11 +2928,13 @@ void SV_SpawnServer (const char *server)
 
        strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
        strlcpy(sv.model_precache[1], sv.modelname, sizeof(sv.model_precache[1]));
-       for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
+       for (i = 1;i < sv.worldmodel->brush.numsubmodels && i+1 < MAX_MODELS;i++)
        {
                dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
                sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, sv.modelname);
        }
+       if(i < sv.worldmodel->brush.numsubmodels)
+               Con_Printf("Too many submodels (MAX_MODELS is %i)\n", MAX_MODELS);
 
 //
 // load the rest of the entities