/** * @brief After receiving a valid game state, we validate the cgame and * local zip files here and determine if we need to download them */ void Com_InitDownloads(void) { char missingfiles[1024]; // init some of the www dl data dld.bWWWDl = qfalse; dld.bWWWDlAborting = qfalse; dld.bWWWDlDisconnected = qfalse; Com_ClearStaticDownload(); if (!Com_InitUpdateDownloads()) { // whatever auto download configuration, store missing files in a cvar, use later in the ui maybe if (FS_ComparePaks(missingfiles, sizeof(missingfiles), qfalse)) { Cvar_Set("com_missingFiles", missingfiles); } else { Cvar_Set("com_missingFiles", ""); } // reset the redirect checksum tracking dld.redirectedList[0] = '\0'; #ifdef DEDICATED Com_NextDownload(); #else if (cl_allowDownload->integer && FS_ComparePaks(dld.downloadList, sizeof(dld.downloadList), qtrue)) { // this gets printed to UI, i18n Com_Printf(CL_TranslateStringBuf("Need paks: %s\n"), dld.downloadList); if (*dld.downloadList) { // if autodownloading is not enabled on the server cls.state = CA_CONNECTED; Com_NextDownload(); return; } } #endif } else { return; } Com_DownloadsComplete(); }
/* ================== Com_WWWDownload ================== */ void Com_WWWDownload(void) { char *to_ospath; dlStatus_t ret; static qboolean bAbort = qfalse; if (dld.bWWWDlAborting) { if (!bAbort) { Com_DPrintf("Com_WWWDownload: WWWDlAborting\n"); bAbort = qtrue; } return; } if (bAbort) { Com_DPrintf("Com_WWWDownload: WWWDlAborting done\n"); bAbort = qfalse; } ret = DL_DownloadLoop(); if (ret == DL_CONTINUE) { return; } if (ret == DL_DONE) { // taken from CL_ParseDownload // we work with OS paths dld.download = 0; to_ospath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), dld.originalDownloadName, ""); to_ospath[strlen(to_ospath) - 1] = '\0'; if (rename(dld.downloadTempName, to_ospath)) { FS_CopyFile(dld.downloadTempName, to_ospath); remove(dld.downloadTempName); } *dld.downloadTempName = *dld.downloadName = 0; Cvar_Set("cl_downloadName", ""); if (dld.bWWWDlDisconnected) { // for an auto-update in disconnected mode, we'll be spawning the setup in CL_DownloadsComplete if (!autoupdate.updateStarted && !dld.noReconnect) { // reconnect to the server, which might send us to a new disconnected download Cbuf_ExecuteText(EXEC_APPEND, "reconnect\n"); } } else { Com_AddReliableCommand("wwwdl done"); // tracking potential web redirects leading us to wrong checksum - only works in connected mode if (strlen(dld.redirectedList) + strlen(dld.originalDownloadName) + 1 >= sizeof(dld.redirectedList)) { // just to be safe Com_Printf("ERROR: redirectedList overflow (%s)\n", dld.redirectedList); } else { strcat(dld.redirectedList, "@"); strcat(dld.redirectedList, dld.originalDownloadName); } } } else { if (dld.bWWWDlDisconnected) { // in a connected download, we'd tell the server about failure and wait for a reply // but in this case we can't get anything from server // if we just reconnect it's likely we'll get the same disconnected download message, and error out again // this may happen for a regular dl or an auto update const char *error = va("Download failure while getting '%s'\n", dld.downloadName); // get the msg before clearing structs dld.bWWWDlDisconnected = qfalse; // need clearing structs before ERR_DROP, or it goes into endless reload Com_ClearStaticDownload(); Com_Error(ERR_DROP, "%s", error); } else { // see CL_ParseDownload, same abort strategy Com_Printf("Download failure while getting '%s'\n", dld.downloadName); Com_AddReliableCommand("wwwdl fail"); dld.bWWWDlAborting = qtrue; } return; } dld.bWWWDl = qfalse; Com_NextDownload(); }
/** * @brief A download message has been received from the server */ void CL_ParseDownload(msg_t *msg) { int size; unsigned char data[MAX_MSGLEN]; int block; if (!*cls.download.downloadTempName) { Com_Printf("Server sending download, but no download was requested\n"); CL_AddReliableCommand("stopdl"); return; } // read the data block = MSG_ReadShort(msg); // unfortunately DLTYPE_WWW is -1 FIXME: change this someday! //if (block < 0) //{ //Com_Error(ERR_DROP, "CL_ParseDownload: Server sending invalid download data"); //} // www dl, if we haven't acknowledged the download redirect yet if (block == DLTYPE_WWW) { if (!cls.download.bWWWDl) { // server is sending us a www download Q_strncpyz(cls.download.originalDownloadName, cls.download.downloadName, sizeof(cls.download.originalDownloadName)); Q_strncpyz(cls.download.downloadName, MSG_ReadString(msg), sizeof(cls.download.downloadName)); cls.download.downloadSize = MSG_ReadLong(msg); cls.download.downloadFlags = MSG_ReadLong(msg); if (cls.download.downloadFlags & (1 << DL_FLAG_URL)) { Sys_OpenURL(cls.download.downloadName, qtrue); Cbuf_ExecuteText(EXEC_APPEND, "quit\n"); CL_AddReliableCommand("wwwdl bbl8r"); // not sure if that's the right msg cls.download.bWWWDlAborting = qtrue; return; } Cvar_SetValue("cl_downloadSize", cls.download.downloadSize); Com_DPrintf("Server redirected download: %s\n", cls.download.downloadName); cls.download.bWWWDl = qtrue; // activate wwwdl client loop CL_AddReliableCommand("wwwdl ack"); // make sure the server is not trying to redirect us again on a bad checksum if (strstr(cls.download.badChecksumList, va("@%s", cls.download.originalDownloadName))) { Com_Printf("refusing redirect to %s by server (bad checksum)\n", cls.download.downloadName); CL_AddReliableCommand("wwwdl fail"); cls.download.bWWWDlAborting = qtrue; return; } // make downloadTempName an OS path Q_strncpyz(cls.download.downloadTempName, FS_BuildOSPath(Cvar_VariableString("fs_homepath"), cls.download.downloadTempName, ""), sizeof(cls.download.downloadTempName)); cls.download.downloadTempName[strlen(cls.download.downloadTempName) - 1] = '\0'; if (!DL_BeginDownload(cls.download.downloadTempName, cls.download.downloadName)) { // setting bWWWDl to false after sending the wwwdl fail doesn't work // not sure why, but I suspect we have to eat all remaining block -1 that the server has sent us // still leave a flag so that CL_WWWDownload is inactive // we count on server sending us a gamestate to start up clean again CL_AddReliableCommand("wwwdl fail"); cls.download.bWWWDlAborting = qtrue; Com_Printf("Failed to initialize download for '%s'\n", cls.download.downloadName); } // Check for a disconnected download // we'll let the server disconnect us when it gets the bbl8r message if (cls.download.downloadFlags & (1 << DL_FLAG_DISCON)) { CL_AddReliableCommand("wwwdl bbl8r"); cls.download.bWWWDlDisconnected = qtrue; } return; } else { // server keeps sending that message till we acknowledge it, eat and ignore //MSG_ReadLong( msg ); MSG_ReadString(msg); MSG_ReadLong(msg); MSG_ReadLong(msg); return; } } if (!block) { // block zero is special, contains file size cls.download.downloadSize = MSG_ReadLong(msg); Cvar_SetValue("cl_downloadSize", cls.download.downloadSize); if (cls.download.downloadSize < 0) { Com_Error(ERR_DROP, "%s", MSG_ReadString(msg)); return; } } size = MSG_ReadShort(msg); if (size < 0 || size > sizeof(data)) { Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk.", size); return; } MSG_ReadData(msg, data, size); if (cls.download.downloadBlock != block) { Com_DPrintf("CL_ParseDownload: Expected block %d, got %d\n", cls.download.downloadBlock, block); return; } // open the file if not opened yet if (!cls.download.download) { cls.download.download = FS_SV_FOpenFileWrite(cls.download.downloadTempName); if (!cls.download.download) { Com_Printf("Could not create %s\n", cls.download.downloadTempName); CL_AddReliableCommand("stopdl"); Com_NextDownload(); return; } } if (size) { FS_Write(data, size, cls.download.download); } CL_AddReliableCommand(va("nextdl %d", cls.download.downloadBlock)); cls.download.downloadBlock++; cls.download.downloadCount += size; // So UI gets access to it Cvar_SetValue("cl_downloadCount", cls.download.downloadCount); if (!size) // A zero length block means EOF { if (cls.download.download) { FS_FCloseFile(cls.download.download); cls.download.download = 0; // rename the file FS_SV_Rename(cls.download.downloadTempName, cls.download.downloadName); } *cls.download.downloadTempName = *cls.download.downloadName = 0; Cvar_Set("cl_downloadName", ""); // send intentions now // We need this because without it, we would hold the last nextdl and then start // loading right away. If we take a while to load, the server is happily trying // to send us that last block over and over. // Write it twice to help make sure we acknowledge the download CL_WritePacket(); CL_WritePacket(); // get another file if needed Com_NextDownload(); } }