/* * CL_DownloadDone */ void CL_DownloadDone( void ) { qboolean requestnext; Mem_ZoneFree( cls.download.requestname ); cls.download.requestname = NULL; requestnext = cls.download.requestnext; cls.download.requestnext = qfalse; cls.download.requestpak = qfalse; cls.download.timeout = 0; cls.download.timestart = 0; cls.download.offset = cls.download.baseoffset = 0; cls.download.web = qfalse; cls.download.filenum = 0; cls.download.cancelled = qfalse; // the server has changed map during the download if( cls.download.pending_reconnect ) { cls.download.pending_reconnect = qfalse; CL_FreeDownloadList(); CL_ServerReconnect_f(); return; } if( requestnext && cls.state > CA_DISCONNECTED ) CL_RequestNextDownload(); }
// Fatal HTTP error occured, remove any special entries from // queue and fall back to UDP downloading. static void abort_downloads (void) { dlqueue_t *q; HTTP_CleanupDownloads(); cls.download.current = NULL; cls.download.percent = 0; FOR_EACH_DLQ (q) { if (q->state != DL_DONE && q->type >= DL_LIST) CL_FinishDownload (q); else if (q->state == DL_RUNNING) q->state = DL_PENDING; } CL_RequestNextDownload (); CL_StartNextDownload (); }
/* * The server will send this command right * before allowing the client into the server */ void CL_Precache_f(void) { /* Yet another hack to let old demos work */ if (Cmd_Argc() < 2) { unsigned map_checksum; /* for detecting cheater maps */ CM_LoadMap(cl.configstrings[CS_MODELS + 1], true, &map_checksum); CL_RegisterSounds(); CL_PrepRefresh(); return; } precache_check = CS_MODELS; precache_spawncount = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); precache_model = 0; precache_model_skin = 0; CL_RequestNextDownload(); }
void CL_ParseDownload (qboolean dataIsCompressed) { int size, percent; char name[MAX_OSPATH]; // read the data size = MSG_ReadShort (&net_message); percent = MSG_ReadByte (&net_message); if (size < 0) { if (size == -1) Com_Printf ("Server does not have this file.\n", LOG_CLIENT); else Com_Printf ("Bad download data from server.\n", LOG_CLIENT); //r1: nuke the temp filename cls.downloadtempname[0] = 0; cls.downloadname[0] = 0; cls.failed_download = true; if (cls.download) { // if here, we tried to resume a file but the server said no fclose (cls.download); cls.download = NULL; } cls.downloadpending = false; CL_RequestNextDownload (); return; } // open the file if not opened yet if (!cls.download) { if (!cls.downloadtempname[0]) { Com_Printf ("Received download packet without request. Ignored.\n", LOG_CLIENT); net_message.readcount += size; return; } CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); FS_CreatePath (name); cls.download = fopen (name, "wb"); if (!cls.download) { net_message.readcount += size; Com_Printf ("Failed to open %s\n", LOG_CLIENT, cls.downloadtempname); cls.downloadpending = false; CL_RequestNextDownload (); return; } } //r1: downloading something, drop to console to show status bar SCR_EndLoadingPlaque(); //r1: if we're stuck with udp, may as well make best use of the bandwidth... if (dataIsCompressed) { #ifndef NO_ZLIB uint16 uncompressedLen; byte uncompressed[0xFFFF]; uncompressedLen = (uint16)MSG_ReadShort (&net_message); if (!uncompressedLen) Com_Error (ERR_DROP, "uncompressedLen == 0"); ZLibDecompress (net_message_buffer + net_message.readcount, size, uncompressed, uncompressedLen, -15); fwrite (uncompressed, 1, uncompressedLen, cls.download); Com_DPrintf ("svc_zdownload(%s): %d -> %d\n", cls.downloadname, size, uncompressedLen); #else Com_Error (ERR_DROP, "Received a unrequested compressed download"); #endif } else { fwrite (net_message_buffer + net_message.readcount, 1, size, cls.download); } net_message.readcount += size; if (percent != 100) { cls.downloadpercent = percent; MSG_WriteByte (clc_stringcmd); MSG_Print ("nextdl"); MSG_EndWriting (&cls.netchan.message); send_packet_now = true; } else { CL_FinishDownload (); // get another file if needed CL_RequestNextDownload (); } }
/* =============== CL_FinishHTTPDownload A download finished, find out what it was, whether there were any errors and if so, how severe. If none, rename file and other such stuff. =============== */ static void CL_FinishHTTPDownload (void) { size_t i; int msgs_in_queue; CURLMsg *msg; CURLcode result; dlhandle_t *dl; CURL *curl; long responseCode; double timeTaken; double fileSize; char tempName[MAX_OSPATH]; qboolean isFile; do { msg = curl_multi_info_read (multi, &msgs_in_queue); if (!msg) { Com_Printf ("CL_FinishHTTPDownload: Odd, no message for us...\n"); return; } if (msg->msg != CURLMSG_DONE) { Com_Printf ("CL_FinishHTTPDownload: Got some weird message...\n"); continue; } curl = msg->easy_handle; // curl doesn't provide reverse-lookup of the void * ptr, so search for it for (i = 0; i < 4; i++) { if (cls.HTTPHandles[i].curl == curl) { dl = &cls.HTTPHandles[i]; break; } } if (i == 4) Com_Error (ERR_DROP, "CL_FinishHTTPDownload: Handle not found"); // we mark everything as done even if it errored to prevent multiple // attempts. dl->queueEntry->state = DLQ_STATE_DONE; //filelist processing is done on read if (dl->file) isFile = true; else isFile = false; if (isFile) { fclose (dl->file); dl->file = NULL; } //might be aborted if (pendingCount) pendingCount--; handleCount--; //Com_Printf ("finished dl: hc = %d\n", LOG_GENERAL, handleCount); cls.downloadname[0] = 0; cls.downloadposition = 0; result = msg->data.result; switch (result) { // for some reason curl returns CURLE_OK for a 404... case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode == 404) { i = strlen (dl->queueEntry->quakePath); if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak") || !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") ) downloading_pak = false; if (isFile) remove (dl->filePath); Com_Printf ("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->quakePath, pendingCount); curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); if (fileSize > 512) { // ick isFile = false; result = CURLE_FILESIZE_EXCEEDED; Com_Printf ("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize); } else { curl_multi_remove_handle (multi, dl->curl); // Knightmare- fall back to UDP download for this map if failure on .bsp if ( !strncmp(dl->queueEntry->quakePath, "maps/", 5) && !strcmp(dl->queueEntry->quakePath + i - 4, ".bsp") ) { Com_Printf ("HTTP: failed to download %s, falling back to UDP until next map.\n", dl->queueEntry->quakePath); thisMapAbort = true; CL_CancelHTTPDownloads (false); CL_ResetPrecacheCheck (); } // end Knightmare continue; } } else if (responseCode == 200) { if (!isFile && !abortDownloads) CL_ParseFileList (dl); break; } //every other code is treated as fatal, fallthrough here Com_Printf ("Bad HTTP response code %d for %s, aborting HTTP downloading.\n", responseCode, dl->queueEntry->quakePath); //fatal error, disable http case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_PROXY: if (isFile) remove (dl->filePath); // Com_Printf ("Fatal HTTP error: %s\n", curl_easy_strerror (result)); Com_Printf ("Fatal HTTP error: %s\n", CURL_ERROR(result)); curl_multi_remove_handle (multi, dl->curl); if (abortDownloads) continue; CL_CancelHTTPDownloads (true); continue; default: i = strlen (dl->queueEntry->quakePath); if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak") || !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") ) downloading_pak = false; if (isFile) remove (dl->filePath); // Com_Printf ("HTTP download failed: %s\n", curl_easy_strerror (result)); Com_Printf ("HTTP download failed: %s\n", CURL_ERROR(result)); curl_multi_remove_handle (multi, dl->curl); continue; } if (isFile) { //rename the temp file Com_sprintf (tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->quakePath); if (rename (dl->filePath, tempName)) Com_Printf ("Failed to rename %s for some odd reason...", dl->filePath); //a pak file is very special... i = strlen (tempName); if ( !strcmp (tempName + i - 4, ".pak") || !strcmp (tempName + i - 4, ".pk3") ) { // FS_FlushCache (); // FS_ReloadPAKs (); // Knightmare- just add the pk3/ pak file if (!strcmp (tempName + i - 4, ".pk3")) FS_AddPK3File (tempName); else FS_AddPAKFile (tempName); CL_ReVerifyHTTPQueue (); downloading_pak = false; } } //show some stats curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &timeTaken); curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); //FIXME: //technically i shouldn't need to do this as curl will auto reuse the //existing handle when you change the URL. however, the handleCount goes //all weird when reusing a download slot in this way. if you can figure //out why, please let me know. curl_multi_remove_handle (multi, dl->curl); Com_Printf ("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n", dl->queueEntry->quakePath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount); } while (msgs_in_queue > 0); // FS_FlushCache (); if (handleCount == 0) { if (abortDownloads == HTTPDL_ABORT_SOFT) abortDownloads = HTTPDL_ABORT_NONE; else if (abortDownloads == HTTPDL_ABORT_HARD) { // FS: Added because Whale's Weapons HTTP server rejects you after a lot of 404s. Then you lose HTTP until a hard reconnect. Q_strncpyz(cls.downloadServerRetry, cls.downloadServer, sizeof(cls.downloadServerRetry)); cls.downloadServer[0] = 0; } } // done current batch, see if we have more to dl - maybe a .bsp needs downloaded if (cls.state == ca_connected && !CL_PendingHTTPDownloads()) CL_RequestNextDownload (); }
/* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload(void) { int size, percent; char name[MAX_OSPATH]; int r; // read the data size = MSG_ReadShort(&net_message); percent = MSG_ReadByte(&net_message); if (size == -1) { Com_Printf("Server does not have this file.\n"); if (cls.download) { // if here, we tried to resume a file but the server said no fclose(cls.download); cls.download = NULL; } CL_RequestNextDownload(); return; } // open the file if not opened yet if (!cls.download) { CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); FS_CreatePath(name); cls.download = fopen(name, "wb"); if (!cls.download) { net_message.readcount += size; Com_Printf("Failed to open %s\n", cls.downloadtempname); CL_RequestNextDownload(); return; } } fwrite(net_message.data + net_message.readcount, 1, size, cls.download); net_message.readcount += size; if (percent != 100) { // request next block // change display routines by zoid #if 0 Com_Printf ("."); if (10*(percent/10) != cls.downloadpercent) { cls.downloadpercent = 10*(percent/10); Com_Printf ("%i%%", cls.downloadpercent); } #endif cls.downloadpercent = percent; MSG_WriteByte(&cls.netchan.message, clc_stringcmd); SZ_Print(&cls.netchan.message, "nextdl"); } else { char oldn[MAX_OSPATH]; char newn[MAX_OSPATH]; // Com_Printf ("100%%\n"); fclose(cls.download); // rename the temp file to it's final name CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname); CL_DownloadFileName(newn, sizeof(newn), cls.downloadname); r = rename(oldn, newn); if (r) Com_Printf("failed to rename.\n"); cls.download = NULL; cls.downloadpercent = 0; // get another file if needed CL_RequestNextDownload(); } }
/** * @brief A download finished, find out what it was, whether there were any errors and * if so, how severe. If none, rename file and other such stuff. */ static void CL_FinishHTTPDownload (void) { int messagesInQueue, i; CURLcode result; CURL *curl; long responseCode; double timeTaken, fileSize; char tempName[MAX_OSPATH]; bool isFile; do { CURLMsg *msg = curl_multi_info_read(multi, &messagesInQueue); dlhandle_t *dl = NULL; if (!msg) { Com_Printf("CL_FinishHTTPDownload: Odd, no message for us...\n"); return; } if (msg->msg != CURLMSG_DONE) { Com_Printf("CL_FinishHTTPDownload: Got some weird message...\n"); continue; } curl = msg->easy_handle; /* curl doesn't provide reverse-lookup of the void * ptr, so search for it */ for (i = 0; i < 4; i++) { if (cls.HTTPHandles[i].curl == curl) { dl = &cls.HTTPHandles[i]; break; } } if (!dl) Com_Error(ERR_DROP, "CL_FinishHTTPDownload: Handle not found"); /* we mark everything as done even if it errored to prevent multiple attempts. */ dl->queueEntry->state = DLQ_STATE_DONE; /* filelist processing is done on read */ if (dl->file) isFile = true; else isFile = false; if (isFile) { fclose(dl->file); dl->file = NULL; } /* might be aborted */ if (pendingCount) pendingCount--; handleCount--; /* Com_Printf("finished dl: hc = %d\n", handleCount); */ cls.downloadName[0] = 0; cls.downloadPosition = 0; result = msg->data.result; switch (result) { /* for some reason curl returns CURLE_OK for a 404... */ case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode == 404) { const char *extension = Com_GetExtension(dl->queueEntry->ufoPath); if (extension != NULL && Q_streq(extension, "pk3")) downloadingPK3 = false; if (isFile) FS_RemoveFile(dl->filePath); Com_Printf("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->ufoPath, pendingCount); curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); if (fileSize > 512) { /* ick */ isFile = false; result = CURLE_FILESIZE_EXCEEDED; Com_Printf("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize); } else { curl_multi_remove_handle(multi, dl->curl); continue; } } else if (responseCode == 200) { if (!isFile && !abortDownloads) CL_ParseFileList(dl); break; } /* every other code is treated as fatal, fallthrough here */ /* fatal error, disable http */ case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_PROXY: if (isFile) FS_RemoveFile(dl->filePath); Com_Printf("Fatal HTTP error: %s\n", curl_easy_strerror(result)); curl_multi_remove_handle(multi, dl->curl); if (abortDownloads) continue; CL_CancelHTTPDownloads(true); continue; default: i = strlen(dl->queueEntry->ufoPath); if (Q_streq(dl->queueEntry->ufoPath + i - 4, ".pk3")) downloadingPK3 = false; if (isFile) FS_RemoveFile(dl->filePath); Com_Printf("HTTP download failed: %s\n", curl_easy_strerror(result)); curl_multi_remove_handle(multi, dl->curl); continue; } if (isFile) { /* rename the temp file */ Com_sprintf(tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->ufoPath); if (!FS_RenameFile(dl->filePath, tempName, false)) Com_Printf("Failed to rename %s for some odd reason...", dl->filePath); /* a pk3 file is very special... */ i = strlen(tempName); if (Q_streq(tempName + i - 4, ".pk3")) { FS_RestartFilesystem(NULL); CL_ReVerifyHTTPQueue(); downloadingPK3 = false; } } /* show some stats */ curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &timeTaken); curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); /** @todo * technically i shouldn't need to do this as curl will auto reuse the * existing handle when you change the URL. however, the handleCount goes * all weird when reusing a download slot in this way. if you can figure * out why, please let me know. */ curl_multi_remove_handle(multi, dl->curl); Com_Printf("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n", dl->queueEntry->ufoPath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount); } while (messagesInQueue > 0); if (handleCount == 0) { if (abortDownloads == HTTPDL_ABORT_SOFT) abortDownloads = HTTPDL_ABORT_NONE; else if (abortDownloads == HTTPDL_ABORT_HARD) cls.downloadServer[0] = 0; } /* done current batch, see if we have more to dl - maybe a .bsp needs downloaded */ if (cls.state == ca_connected && !CL_PendingHTTPDownloads()) CL_RequestNextDownload(); }
// A download finished, find out what it was, whether there were any errors and // if so, how severe. If none, rename file and other such stuff. static qboolean finish_download(void) { int msgs_in_queue; CURLMsg *msg; CURLcode result; dlhandle_t *dl; CURL *curl; long response; double sec, bytes; char size[16], speed[16]; char temp[MAX_OSPATH]; qboolean fatal_error = qfalse; const char *err; print_type_t level; do { msg = curl_multi_info_read(curl_multi, &msgs_in_queue); if (!msg) break; if (msg->msg != CURLMSG_DONE) continue; curl = msg->easy_handle; dl = find_handle(curl); cls.download.current = NULL; cls.download.percent = 0; //filelist processing is done on read if (dl->file) { fclose(dl->file); dl->file = NULL; } curl_handles--; result = msg->data.result; switch (result) { //for some reason curl returns CURLE_OK for a 404... case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); if (result == CURLE_OK && response == 200) { //success break; } err = http_strerror(response); //404 is non-fatal if (response == 404) { level = PRINT_ALL; goto fail1; } //every other code is treated as fatal //not marking download as done since //we are falling back to UDP level = PRINT_ERROR; fatal_error = qtrue; goto fail2; case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_PROXY: //connection problems are fatal err = curl_easy_strerror(result); level = PRINT_ERROR; fatal_error = qtrue; goto fail2; default: err = curl_easy_strerror(result); level = PRINT_WARNING; fail1: //we mark download as done even if it errored //to prevent multiple attempts. CL_FinishDownload(dl->queue); fail2: Com_LPrintf(level, "[HTTP] %s [%s] [%d remaining file%s]\n", dl->queue->path, err, cls.download.pending, cls.download.pending == 1 ? "" : "s"); if (dl->path[0]) { remove(dl->path); dl->path[0] = 0; } if (dl->buffer) { Z_Free(dl->buffer); dl->buffer = NULL; } curl_multi_remove_handle(curl_multi, curl); continue; } //mark as done CL_FinishDownload(dl->queue); //show some stats curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &sec); curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &bytes); if (sec < 0.001) sec = 0.001; Com_FormatSizeLong(size, sizeof(size), bytes); Com_FormatSizeLong(speed, sizeof(speed), bytes / sec); //FIXME: //technically i shouldn't need to do this as curl will auto reuse the //existing handle when you change the url. however, the curl_handles goes //all weird when reusing a download slot in this way. if you can figure //out why, please let me know. curl_multi_remove_handle(curl_multi, curl); Com_Printf("[HTTP] %s [%s, %s/sec] [%d remaining file%s]\n", dl->queue->path, size, speed, cls.download.pending, cls.download.pending == 1 ? "" : "s"); if (dl->path[0]) { //rename the temp file Q_snprintf(temp, sizeof(temp), "%s/%s", fs_gamedir, dl->queue->path); if (rename(dl->path, temp)) Com_EPrintf("[HTTP] Failed to rename '%s' to '%s': %s\n", dl->path, dl->queue->path, strerror(errno)); dl->path[0] = 0; //a pak file is very special... if (dl->queue->type == DL_PAK) { CL_RestartFilesystem(qfalse); rescan_queue(); } } else if (!fatal_error) { parse_file_list(dl); } } while (msgs_in_queue > 0); //fatal error occured, disable HTTP if (fatal_error) { abort_downloads(); return qfalse; } // see if we have more to dl CL_RequestNextDownload(); return qtrue; }
// Actually starts a download by adding it to the curl multi handle. static void start_download(dlqueue_t *entry, dlhandle_t *dl) { size_t len; char temp[MAX_QPATH]; char escaped[MAX_QPATH * 4]; CURLMcode ret; qerror_t err; //yet another hack to accomodate filelists, how i wish i could push :( //NULL file handle indicates filelist. if (entry->type == DL_LIST) { dl->file = NULL; dl->path[0] = 0; //filelist paths are absolute escape_path(entry->path, escaped); } else { len = Q_snprintf(dl->path, sizeof(dl->path), "%s/%s.tmp", fs_gamedir, entry->path); if (len >= sizeof(dl->path)) { Com_EPrintf("[HTTP] Refusing oversize temporary file path.\n"); goto fail; } //prepend quake path with gamedir len = Q_snprintf(temp, sizeof(temp), "%s/%s", http_gamedir(), entry->path); if (len >= sizeof(temp)) { Com_EPrintf("[HTTP] Refusing oversize server file path.\n"); goto fail; } escape_path(temp, escaped); err = FS_CreatePath(dl->path); if (err < 0) { Com_EPrintf("[HTTP] Couldn't create path to '%s': %s\n", dl->path, Q_ErrorString(err)); goto fail; } //don't bother with http resume... too annoying if server doesn't support it. dl->file = fopen(dl->path, "wb"); if (!dl->file) { Com_EPrintf("[HTTP] Couldn't open '%s' for writing: %s\n", dl->path, strerror(errno)); goto fail; } } len = Q_snprintf(dl->url, sizeof(dl->url), "%s%s", download_server, escaped); if (len >= sizeof(dl->url)) { Com_EPrintf("[HTTP] Refusing oversize download URL.\n"); goto fail; } dl->buffer = NULL; dl->size = 0; dl->position = 0; dl->queue = entry; if (!dl->curl) dl->curl = curl_easy_init(); curl_easy_setopt(dl->curl, CURLOPT_ENCODING, ""); #ifdef _DEBUG if (cl_http_debug->integer) { curl_easy_setopt(dl->curl, CURLOPT_DEBUGFUNCTION, debug_func); curl_easy_setopt(dl->curl, CURLOPT_VERBOSE, 1); } #endif curl_easy_setopt(dl->curl, CURLOPT_NOPROGRESS, 0); if (dl->file) { curl_easy_setopt(dl->curl, CURLOPT_WRITEDATA, dl->file); curl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, NULL); } else { curl_easy_setopt(dl->curl, CURLOPT_WRITEDATA, dl); curl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, recv_func); } curl_easy_setopt(dl->curl, CURLOPT_FAILONERROR, 1); curl_easy_setopt(dl->curl, CURLOPT_PROXY, cl_http_proxy->string); curl_easy_setopt(dl->curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(dl->curl, CURLOPT_MAXREDIRS, 5); curl_easy_setopt(dl->curl, CURLOPT_PROGRESSFUNCTION, progress_func); curl_easy_setopt(dl->curl, CURLOPT_PROGRESSDATA, dl); curl_easy_setopt(dl->curl, CURLOPT_USERAGENT, com_version->string); curl_easy_setopt(dl->curl, CURLOPT_REFERER, download_referer); curl_easy_setopt(dl->curl, CURLOPT_URL, dl->url); ret = curl_multi_add_handle(curl_multi, dl->curl); if (ret != CURLM_OK) { Com_EPrintf("[HTTP] Failed to add download handle: %s\n", curl_multi_strerror(ret)); fail: CL_FinishDownload(entry); // see if we have more to dl CL_RequestNextDownload(); return; } Com_DPrintf("[HTTP] Fetching %s...\n", dl->url); entry->state = DL_RUNNING; curl_handles++; }
/* * A download message has been received from the server */ void CL_ParseDownload (void) { int size, percent; char name[MAX_OSPATH]; int r; /* read the data */ size = MSG_ReadShort (&net_message); percent = MSG_ReadByte (&net_message); if (size == -1) { Com_Printf ("Server does not have this file.\n"); if (cls.download) { /* if here, we tried to resume a file but the server said no */ fclose (cls.download); cls.download = NULL; } CL_RequestNextDownload (); return; } /* open the file if not opened yet */ if (!cls.download) { CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); FS_CreatePath (name); cls.download = fopen (name, "wb"); if (!cls.download) { net_message.readcount += size; Com_Printf ("Failed to open %s\n", cls.downloadtempname); CL_RequestNextDownload (); return; } } fwrite (net_message.data + net_message.readcount, 1, size, cls.download); net_message.readcount += size; if (percent != 100) { /* request next block */ cls.downloadpercent = percent; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, "nextdl"); } else { char oldn[MAX_OSPATH]; char newn[MAX_OSPATH]; fclose (cls.download); /* rename the temp file to it's final name */ CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname); CL_DownloadFileName(newn, sizeof(newn), cls.downloadname); r = rename (oldn, newn); if (r) Com_Printf ("failed to rename.\n"); cls.download = NULL; cls.downloadpercent = 0; /* get another file if needed */ CL_RequestNextDownload (); } }
/* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload (sizebuf_t *msg, qboolean dataIsCompressed) { int size, percent; char name[MAX_OSPATH]; // int r; // read the data size = MSG_ReadShort (msg); percent = MSG_ReadByte (msg); if (size < 0) { if (size == -1) Com_Printf ("Server does not have this file.\n"); else Com_Printf ("Bad download data from server.\n"); cls.downloadtempname[0] = 0; cls.downloadname[0] = 0; cls.failed_download = true; if (cls.download) { // if here, we tried to resume a file but the server said no fclose (cls.download); cls.download = NULL; } CL_RequestNextDownload (); return; } // open the file if not opened yet if (!cls.download) { if (!cls.downloadtempname[0]) { Com_Printf ("Received download packet without request. Ignored.\n"); msg->readcount += size; return; } CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); FS_CreatePath (name); cls.download = fopen (name, "wb"); if (!cls.download) { msg->readcount += size; Com_Printf ("Failed to open %s\n", cls.downloadtempname); CL_RequestNextDownload (); return; } } if (dataIsCompressed) { uint16 uncompressedLen; byte uncompressed[0xFFFF]; uncompressedLen = MSG_ReadShort (msg); if (!uncompressedLen) Com_Error (ERR_DROP, "uncompressedLen == 0"); ZLibDecompress (msg->data + msg->readcount, size, uncompressed, uncompressedLen, -15); fwrite (uncompressed, 1, uncompressedLen, cls.download); Com_DPrintf ("svc_zdownload(%s): %d -> %d\n", cls.downloadname, size, uncompressedLen); } else { fwrite (msg->data + msg->readcount, 1, size, cls.download); } //fwrite (net_message.data + net_message.readcount, 1, size, cls.download); msg->readcount += size; if (percent != 100) { // request next block // change display routines by zoid cls.downloadpercent = percent; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, "nextdl"); } else { CL_FinishDownload(); // get another file if needed CL_RequestNextDownload (); } }
/** * @brief The server will send this command right before allowing the client into the server * @sa CL_StartGame * @sa SV_Configstrings_f */ static void CL_Precache_f (void) { cls.downloadMaps = true; CL_RequestNextDownload(); }