]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_parse.c
added an error message stating if an image file was loaded but decoding failed
[xonotic/darkplaces.git] / cl_parse.c
index 06595e9a3a3d16edb322628579dfdfbade3297e9..b33fd7e71b7fb84fc44b0bdf114670cc829f5973 100644 (file)
@@ -166,6 +166,7 @@ cvar_t cl_sound_ric3 = {0, "cl_sound_ric3", "weapons/ric3.wav", "sound to play w
 cvar_t cl_sound_r_exp3 = {0, "cl_sound_r_exp3", "weapons/r_exp3.wav", "sound to play during TE_EXPLOSION and related effects (empty cvar disables sound)"};
 cvar_t cl_serverextension_download = {0, "cl_serverextension_download", "0", "indicates whether the server supports the download command"};
 cvar_t cl_joinbeforedownloadsfinish = {0, "cl_joinbeforedownloadsfinish", "1", "if non-zero the game will begin after the map is loaded before other downloads finish"};
+cvar_t cl_nettimesyncmode = {0, "cl_nettimesyncmode", "2", "selects method of time synchronization in client with regard to server packets, values are: 0 = no sync, 1 = exact sync (reset timing each packet), 2 = loose sync (reset timing only if it is out of bounds), 3 = tight sync and bounding"};
 
 static qboolean QW_CL_CheckOrDownloadFile(const char *filename);
 static void QW_CL_RequestNextDownload(void);
@@ -897,6 +898,21 @@ void CL_BeginDownloads(qboolean aborteddownload)
 
        // TODO: this would be a good place to do curl downloads
 
+       if (cl.downloadcsqc)
+       {
+               size_t progsize;
+               cl.downloadcsqc = false;
+               if (cls.netcon
+                && !sv.active
+                && csqc_progname.string
+                && csqc_progname.string[0]
+                && csqc_progcrc.integer >= 0
+                && cl_serverextension_download.integer
+                && (FS_CRCFile(csqc_progname.string, &progsize) != csqc_progcrc.integer || ((int)progsize != csqc_progsize.integer && csqc_progsize.integer != -1))
+                && !FS_FileExists(va("dlcache/%s.%i.%i", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer)))
+                       Cmd_ForwardStringToServer(va("download %s", csqc_progname.string));
+       }
+
        if (cl.loadmodel_current < cl.loadmodel_total)
        {
                // loading models
@@ -1072,17 +1088,37 @@ void CL_StopDownload(int size, int crc)
 {
        if (cls.qw_downloadmemory && cls.qw_downloadmemorycursize == size && CRC_Block(cls.qw_downloadmemory, size) == crc)
        {
+               int existingcrc;
+               size_t existingsize;
+               const char *extension;
+
                // finished file
                // save to disk only if we don't already have it
                // (this is mainly for playing back demos)
-               if (!FS_FileExists(cls.qw_downloadname))
+               existingcrc = FS_CRCFile(cls.qw_downloadname, &existingsize);
+               if (existingsize)
                {
-                       const char *extension;
-
+                       if ((int)existingsize != size || existingcrc != crc)
+                       {
+                               // we have a mismatching file, pick another name for it
+                               char name[MAX_QPATH*2];
+                               dpsnprintf(name, sizeof(name), "dlcache/%s.%i.%i", cls.qw_downloadname, size, crc);
+                               if (!FS_FileExists(name))
+                               {
+                                       Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", name, size, crc);
+                                       FS_WriteFile(name, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
+                               }
+                       }
+               }
+               else
+               {
+                       // we either don't have it or have a mismatching file...
+                       // so it's time to accept the file
+                       // but if we already have a mismatching file we need to rename
+                       // this new one, and if we already have this file in renamed form,
+                       // we do nothing
                        Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", cls.qw_downloadname, size, crc);
-
                        FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
-
                        extension = FS_FileExtension(cls.qw_downloadname);
                        if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
                                FS_Rescan();
@@ -1446,6 +1482,7 @@ void CL_ParseServerInfo (void)
                cl.loadsound_current = 1;
                cl.downloadsound_current = 1;
                cl.loadsound_total = numsounds;
+               cl.downloadcsqc = true;
                cl.loadfinished = false;
        }
 
@@ -1460,7 +1497,7 @@ void CL_ValidateState(entity_state_t *s)
        if (!s->active)
                return;
 
-       if (s->modelindex >= MAX_MODELS && (65536-s->modelindex) >= MAX_MODELS)
+       if (s->modelindex >= MAX_MODELS)
                Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)\n", s->modelindex, MAX_MODELS);
 
        // colormap is client index + 1
@@ -1579,7 +1616,6 @@ void CL_ParseBaseline (entity_t *ent, int large)
                ent->state_baseline.origin[i] = MSG_ReadCoord(cls.protocol);
                ent->state_baseline.angles[i] = MSG_ReadAngle(cls.protocol);
        }
-       CL_ValidateState(&ent->state_baseline);
        ent->state_previous = ent->state_current = ent->state_baseline;
 }
 
@@ -1702,9 +1738,6 @@ void CL_ParseClientdata (void)
 
        // viewzoom interpolation
        cl.mviewzoom[0] = (float) max(cl.stats[STAT_VIEWZOOM], 2) * (1.0f / 255.0f);
-
-       // force a recalculation of the player prediction
-       cl.movement_replay = true;
 }
 
 /*
@@ -2195,7 +2228,7 @@ void CL_ParseTempEntity(void)
                        color[2] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
                        CL_ParticleExplosion(pos);
                        Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-                       CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
                        S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
                        break;
 
@@ -2208,7 +2241,7 @@ void CL_ParseTempEntity(void)
                        color[1] = MSG_ReadByte() * (2.0f / 255.0f);
                        color[2] = MSG_ReadByte() * (2.0f / 255.0f);
                        Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-                       CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
                        S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
                        break;
 
@@ -2235,7 +2268,7 @@ void CL_ParseTempEntity(void)
                        color[1] = MSG_ReadByte() * (2.0f / 255.0f);
                        color[2] = MSG_ReadByte() * (2.0f / 255.0f);
                        Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-                       CL_AllocDlight(NULL, &tempmatrix, radius, color[0], color[1], color[2], radius / velspeed, velspeed, 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_AllocLightFlash(NULL, &tempmatrix, radius, color[0], color[1], color[2], radius / velspeed, velspeed, 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
                        break;
 
                case TE_FLAMEJET:
@@ -2294,7 +2327,7 @@ void CL_ParseTempEntity(void)
                        color[1] = tempcolor[1] * (2.0f / 255.0f);
                        color[2] = tempcolor[2] * (2.0f / 255.0f);
                        Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-                       CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
                        S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
                        break;
 
@@ -2334,6 +2367,33 @@ void CL_ParseTempEntity(void)
        }
 }
 
+void CL_ParseTrailParticles(void)
+{
+       int entityindex;
+       int effectindex;
+       vec3_t start, end;
+       entityindex = (unsigned short)MSG_ReadShort();
+       if (entityindex >= MAX_EDICTS)
+               entityindex = 0;
+       if (entityindex >= cl.max_entities)
+               CL_ExpandEntities(entityindex);
+       effectindex = (unsigned short)MSG_ReadShort();
+       MSG_ReadVector(start, cls.protocol);
+       MSG_ReadVector(end, cls.protocol);
+       CL_ParticleEffect(effectindex, VectorDistance(start, end), start, end, vec3_origin, vec3_origin, entityindex > 0 ? cl.entities + entityindex : NULL, 0);
+}
+
+void CL_ParsePointParticles(void)
+{
+       int effectindex, count;
+       vec3_t origin, velocity;
+       effectindex = (unsigned short)MSG_ReadShort();
+       MSG_ReadVector(origin, cls.protocol);
+       MSG_ReadVector(velocity, cls.protocol);
+       count = (unsigned short)MSG_ReadShort();
+       CL_ParticleEffect(effectindex, count, origin, origin, velocity, velocity, NULL, 0);
+}
+
 // look for anything interesting like player IP addresses or ping reports
 qboolean CL_ExaminePrintString(const char *text)
 {
@@ -2463,6 +2523,38 @@ qboolean CL_ExaminePrintString(const char *text)
        return true;
 }
 
+static void CL_NetworkTimeReceived(double newtime)
+{
+       if (cl_nolerp.integer || cls.timedemo || (cl.islocalgame && !sv_fixedframeratesingleplayer.integer))
+               cl.mtime[1] = cl.mtime[0] = newtime;
+       else
+       {
+               cl.mtime[1] = max(cl.mtime[0], newtime - 0.1);
+               cl.mtime[0] = newtime;
+       }
+       if (cl_nettimesyncmode.integer == 3)
+               cl.time = cl.mtime[1];
+       if (cl_nettimesyncmode.integer == 2)
+       {
+               if (cl.time < cl.mtime[1] || cl.time > cl.mtime[0])
+                       cl.time = cl.mtime[1];
+       }
+       else if (cl_nettimesyncmode.integer == 1)
+               cl.time = cl.mtime[1];
+       // this packet probably contains a player entity update, so we will need
+       // to update the prediction
+       cl.movement_needupdate = true;
+       // this may get updated later in parsing by svc_clientdata
+       cl.onground = false;
+       // if true the cl.viewangles are interpolated from cl.mviewangles[]
+       // during this frame
+       // (makes spectating players much smoother and prevents mouse movement from turning)
+       cl.fixangle[1] = cl.fixangle[0];
+       cl.fixangle[0] = false;
+       if (!cls.demoplayback)
+               VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
+}
+
 #define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf("%3i:%s\n", msg_readcount-1, x);
 
 //[515]: csqc
@@ -2513,20 +2605,7 @@ void CL_ParseServerMessage(void)
 
        if (cls.protocol == PROTOCOL_QUAKEWORLD)
        {
-               cl.mtime[1] = cl.mtime[0];
-               cl.mtime[0] = realtime; // qw has no clock
-               cl.movement_needupdate = true;
-               cl.onground = false; // since there's no clientdata parsing, clear the onground flag here
-               // if true the cl.viewangles are interpolated from cl.mviewangles[]
-               // during this frame
-               // (makes spectating players much smoother and prevents mouse movement from turning)
-               cl.fixangle[1] = cl.fixangle[0];
-               cl.fixangle[0] = false;
-               if (!cls.demoplayback)
-                       VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
-
-               // force a recalculation of the player prediction
-               cl.movement_replay = true;
+               CL_NetworkTimeReceived(realtime); // qw has no clock
 
                // slightly kill qw player entities each frame
                for (i = 1;i < cl.maxclients;i++)
@@ -2937,16 +3016,7 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case svc_time:
-                               cl.mtime[1] = cl.mtime[0];
-                               cl.mtime[0] = MSG_ReadFloat ();
-                               cl.movement_needupdate = true;
-                               // if true the cl.viewangles are interpolated from cl.mviewangles[]
-                               // during this frame
-                               // (makes spectating players much smoother and prevents mouse movement from turning)
-                               cl.fixangle[1] = cl.fixangle[0];
-                               cl.fixangle[0] = false;
-                               if (!cls.demoplayback)
-                                       VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
+                               CL_NetworkTimeReceived(MSG_ReadFloat());
                                break;
 
                        case svc_clientdata:
@@ -3270,6 +3340,12 @@ void CL_ParseServerMessage(void)
                        case svc_downloaddata:
                                CL_ParseDownload();
                                break;
+                       case svc_trailparticles:
+                               CL_ParseTrailParticles();
+                               break;
+                       case svc_pointparticles:
+                               CL_ParsePointParticles();
+                               break;
                        }
                }
        }
@@ -3318,6 +3394,8 @@ void CL_Parse_Init(void)
        // server extension cvars set by commands issued from the server during connect
        Cvar_RegisterVariable(&cl_serverextension_download);
 
+       Cvar_RegisterVariable(&cl_nettimesyncmode);
+
        Cmd_AddCommand("nextul", QW_CL_NextUpload, "sends next fragment of current upload buffer (screenshot for example)");
        Cmd_AddCommand("stopul", QW_CL_StopUpload, "aborts current upload (screenshot for example)");
        Cmd_AddCommand("skins", QW_CL_Skins_f, "downloads missing qw skins from server");