From a1fbc963a9b2786a3c20cd1cef9bdc01d12695e1 Mon Sep 17 00:00:00 2001 From: Guillaume Dupuy Date: Mon, 10 Apr 2017 01:56:44 +0200 Subject: [PATCH 1/5] Avoid rollover in _RyzomDay for low values --HG-- branch : save_deposit_state_v2 --- .../time_weather_season/time_and_season.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/code/ryzom/common/src/game_share/time_weather_season/time_and_season.cpp b/code/ryzom/common/src/game_share/time_weather_season/time_and_season.cpp index a71d71be4..609938f32 100644 --- a/code/ryzom/common/src/game_share/time_weather_season/time_and_season.cpp +++ b/code/ryzom/common/src/game_share/time_weather_season/time_and_season.cpp @@ -85,13 +85,16 @@ namespace WEEKDAY void CRyzomTime::updateRyzomClock(uint32 gameCyle) { - static const uint32 ticksPerDay = (RYZOM_DAY_IN_HOUR * RYZOM_HOURS_IN_TICKS); static const float ticksPerHour = (float)RYZOM_HOURS_IN_TICKS; uint32 totalTicks = gameCyle + _TickOffset; - uint32 days = totalTicks / ticksPerDay; - uint32 dayCycle = totalTicks - (days * ticksPerDay); - days -= RYZOM_START_SPRING; + uint32 days = totalTicks / RYZOM_DAY_IN_TICKS; + uint32 dayCycle = totalTicks - (days * RYZOM_DAY_IN_TICKS); + // Avoid rollover for low amount of days + if(days >= RYZOM_START_SPRING) + days -= RYZOM_START_SPRING; + else + days = 0; float hours = (float)dayCycle / ticksPerHour; _RyzomDay = days; From bd3a27778e29df3a1da151bea6017be87a8e2b53 Mon Sep 17 00:00:00 2001 From: Guillaume Dupuy Date: Wed, 12 Apr 2017 20:02:11 +0200 Subject: [PATCH 2/5] Save deposit state --HG-- branch : save_deposit_state_v2 --- .../src/entities_game_service/deposit.cpp | 25 +++- .../src/entities_game_service/deposit.h | 38 +++++- .../entities_game_service/egs_variables.cpp | 4 + .../src/entities_game_service/egs_variables.h | 3 + .../entities_game_service/zone_manager.cpp | 112 +++++++++++++++++- .../src/entities_game_service/zone_manager.h | 25 ++++ 6 files changed, 200 insertions(+), 7 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/deposit.cpp b/code/ryzom/server/src/entities_game_service/deposit.cpp index 0fbb7cdfc..6f6f7395e 100644 --- a/code/ryzom/server/src/entities_game_service/deposit.cpp +++ b/code/ryzom/server/src/entities_game_service/deposit.cpp @@ -36,6 +36,7 @@ #include "game_share/multi_target.h" #include "phrase_manager/s_effect.h" #include "projectile_stats.h" +#include "primitives_parser.h" using namespace std; using namespace NLMISC; @@ -293,11 +294,10 @@ struct TCompareStaticItemPtrBySheetId : public std::binary_functiongetPropertyByName( "name", name ) ) return malformed( "name", name ); _Name = name; - + // Read alias + if(!CPrimitivesParser::getAlias(zone, _Alias)) + { + nlwarning(" Could not find an alias for deposit %s", _Name.c_str()); + } // Read exact raw material codes to add vector *exactRMCodesS = NULL; if ( ! (zone->getPropertyByName( "exact_mp_item", exactRMCodesS ) && exactRMCodesS) ) return malformed( "exact_mp_item", name ); @@ -469,6 +473,21 @@ bool CDeposit::build( const NLLIGO::CPrimZone* zone ) return true; } +CDepositState CDeposit::currentState() +{ + CDepositState out; + //If we don't have an alias, don't send any information (it's better to be missing information than having wrong information) + if(_Alias == 0) + return out; + // We should never save the state of a spot without quantity constraint, this is just an additional safeguard against it + if(!_QuantityConstraintsPt) + return out; + + out.alias = _Alias; + out.currentQuantity = _QuantityConstraintsPt->getCurrentQuantity(); + out.nextRespawnDay = _QuantityConstraintsPt->NextRespawnDay; + return out; +} /* * Select the raw materials, using the specified filters and _Ecotype diff --git a/code/ryzom/server/src/entities_game_service/deposit.h b/code/ryzom/server/src/entities_game_service/deposit.h index fee33662a..ce810f21c 100644 --- a/code/ryzom/server/src/entities_game_service/deposit.h +++ b/code/ryzom/server/src/entities_game_service/deposit.h @@ -195,6 +195,28 @@ namespace NLMISC class CWordsDictionary; } +struct CDepositState { + uint32 alias; + float currentQuantity; + uint32 nextRespawnDay; + void serial(NLMISC::IStream &f) throw(NLMISC::EStream) + { + if(f.isXML()) + { + f.xmlSerial(alias, "ALIAS"); + f.xmlSerial(currentQuantity, "CURRENT_QUANTITY"); + f.xmlSerial(nextRespawnDay, "NEXT_RESPAWN_DAY"); + } + else + { + f.serial(alias); + f.serial(currentQuantity); + f.serial(nextRespawnDay); + } + } +}; + + /** * \author Nicolas Brigand, Alain Saffray, Olivier Cado * \author Nevrax France @@ -205,7 +227,7 @@ class CDeposit : public NLLIGO::CPrimZone, public NLMISC::CRefCount public: /// Constructor - CDeposit() : _AutoSpawnSourcePt(NULL), _QuantityConstraintsPt(NULL), _Ecotype(ECOSYSTEM::common_ecosystem), _FilterPhase(0), _KamiAnger(0.0f), _MinQuality(-1), _MaxQuality(-1), _SourceFXIndex(0), _CanProspect(false), _Enabled(false), _CurrentNbAutoSpawnedSources(0), _AllowDepletionRisk(true) {} + CDeposit() : _AutoSpawnSourcePt(NULL), _QuantityConstraintsPt(NULL), _Ecotype(ECOSYSTEM::common_ecosystem), _FilterPhase(0), _KamiAnger(0.0f), _MinQuality(-1), _MaxQuality(-1), _SourceFXIndex(0), _CanProspect(false), _Enabled(false), _CurrentNbAutoSpawnedSources(0), _AllowDepletionRisk(true), _Alias(0) {} /// Destructor ~CDeposit(); @@ -214,7 +236,7 @@ public: static void addEcotype( CEcotypeZone *ecotypeZone ) { _EcotypeZones.push_back( ecotypeZone ); } /// Init deposit - bool build( const NLLIGO::CPrimZone* zone ); + bool build(const NLLIGO::CPrimZone* zone ); /// Clear all ecotype information, after having built the deposits static void clearEcotypes(); @@ -319,7 +341,7 @@ public: float getMaxQuantity() { return _QuantityConstraintsPt ? _QuantityConstraintsPt->getCurrentQuantity() : FLT_MAX; } /// Consume. Return the actual consumed quantity (may be lower if there is no more to get) - float consumeQuantity( float requested ) { if ( _QuantityConstraintsPt ) return _QuantityConstraintsPt->consumeQuantity( requested ); else return requested; } + float consumeQuantity( float requested ) { if ( _QuantityConstraintsPt )return _QuantityConstraintsPt->consumeQuantity( requested ); else return requested; } /** * Get a random RM from the neighbourhood of the specified position. @@ -346,6 +368,13 @@ public: // For auto-spawn source minimum number. Internaly used by CHarvestSource only void decreaseAutoSpawnedSources(); void increaseAutoSpawnedSources(); + /// Used to save the deposit + bool needSave() const { return _QuantityConstraintsPt && (_QuantityConstraintsPt->CurrentQuantity != _QuantityConstraintsPt->InitialQuantity || _QuantityConstraintsPt->NextRespawnDay != 0); } + CDepositState currentState(); + uint32 getAlias() const { return _Alias;} + void setCurrentQuantity(uint32 currentQuantity) { _QuantityConstraintsPt->CurrentQuantity = currentQuantity; } + void setNextRespawnDay(uint32 nextRespawnDay) { _QuantityConstraintsPt->NextRespawnDay = nextRespawnDay; } + protected: @@ -429,6 +458,9 @@ private: /// Current Number of AutoSpawned Sources in this deposit uint32 _CurrentNbAutoSpawnedSources; + + /// CPrimAlias, needed to identify a deposit to restore its state between shutdown of the EGS + uint32 _Alias; }; diff --git a/code/ryzom/server/src/entities_game_service/egs_variables.cpp b/code/ryzom/server/src/entities_game_service/egs_variables.cpp index a96452aca..1891025c9 100644 --- a/code/ryzom/server/src/entities_game_service/egs_variables.cpp +++ b/code/ryzom/server/src/entities_game_service/egs_variables.cpp @@ -329,6 +329,10 @@ CVariable DodgeFactorForForageSkills("egs","DodgeFactorForForageSkills" CVariable ForageExtractionTimeMinGC( "egs", "ForageExtractionTimeMinGC", "Minimum time of extraction in ticks", 230.0f, 0, true ); CVariable ForageExtractionTimeSlopeGC( "egs", "ForageExtractionTimeSlopeGC", "Slope of base extraction time curve", 2.0f, 0, true ); +CVariable RefillDepositOnStartup("egs", "RefillDepositOnStartup", "Ignore saved CurrentQuantity / NextRespawnDay in deposits", false, 0, true); +CVariable DepositSaveInterval("egs", "DepositSaveInterval", "time *in tick* between two saves of a deposit", 10 * 60 * 10, 0, true); +CVariable DepositStateUseXml("egs", "DepositStateUseXml", "Use xml instead of binary file to save deposit state", true, 0, true); + CVariable ForageQuantityBaseRate( "egs", "ForageQuantityBaseRate", "Base of extraction rate", 0.23f, 0, true ); // 0.23 doubles the previous minimum setting diff --git a/code/ryzom/server/src/entities_game_service/egs_variables.h b/code/ryzom/server/src/entities_game_service/egs_variables.h index 661564987..1b2b39d95 100644 --- a/code/ryzom/server/src/entities_game_service/egs_variables.h +++ b/code/ryzom/server/src/entities_game_service/egs_variables.h @@ -280,6 +280,9 @@ extern NLMISC::CVariable ForageQuantityXPDeltaLevelBonusRate; extern NLMISC::CVariable ForageExtractionTimeMinGC; extern NLMISC::CVariable ForageExtractionTimeSlopeGC; +extern NLMISC::CVariable RefillDepositOnStartup; +extern NLMISC::CVariable DepositSaveInterval; +extern NLMISC::CVariable DepositStateUseXml; // QUARTERING extern NLMISC::CVariable QuarteringQuantityAverageForCraftHerbivore; diff --git a/code/ryzom/server/src/entities_game_service/zone_manager.cpp b/code/ryzom/server/src/entities_game_service/zone_manager.cpp index 400a00586..de2ede76d 100644 --- a/code/ryzom/server/src/entities_game_service/zone_manager.cpp +++ b/code/ryzom/server/src/entities_game_service/zone_manager.cpp @@ -530,8 +530,9 @@ void CZoneManager::release() void CZoneManager::initInstance() { _NextDepositIndexUpdated = 0; + _NextDepositSave = CTickEventHandler::getGameCycle() + DepositSaveInterval; _SpreadUpdateLoopBeginTick = CTickEventHandler::getGameCycle(); - + // get the loaded primitives const CPrimitivesParser::TPrimitivesList & primsList = CPrimitivesParser::getInstance().getPrimitives(); @@ -589,6 +590,8 @@ void CZoneManager::initInstance() nlwarning(" Error while building the zones"); } } + // Ask Bsi for saved deposit states + Bsi.requestFile(DepositStateFileName, new CDepositCallback()); // Don't keep ecotypes in memory, the information is already in the deposits CDeposit::clearEcotypes(); @@ -1417,6 +1420,59 @@ bool CZoneManager::parsePVPSafeZones( const NLLIGO::IPrimitive * prim ) return result; } // CZoneManager parsePVPSafeZones +//----------------------------------------------- +// CZoneManager receivedDepositState +//----------------------------------------------- +void CZoneManager::receivedDepositState(const CFileDescription& fileDescription, NLMISC::IStream& dataStream) +{ + if(fileDescription.FileName.empty()) + { + nlwarning("Got no deposit state file from backup service, probably because no deposit need to be saved"); + return; + } + NLMISC::CMemStream& memStream = dynamic_cast(dataStream); + if (&memStream != NULL) + { + try + { + std::vector states; + if(DepositStateUseXml) + { + CIXml xml; + xml.init(memStream); + xml.serialCont(states); + } + else + { + memStream.serialCont(states); + } + nldebug("Got %d CDepositState from backup service", states.size()); + //If we don't want to use state, stop here + if(RefillDepositOnStartup) return; + for(int i=0; i < states.size(); i++) + { + //Find the matching deposit + for(int j = 0 ; j < _Deposits.size(); j++) + { + CDeposit* dep = _Deposits[j]; + if(dep->getAlias() == states[i].alias) + { + dep->setCurrentQuantity(states[i].currentQuantity); + dep->setNextRespawnDay(states[i].nextRespawnDay); + } + } + nldebug("CDepositState for alias %d : currentQuantity=%f, nextRespawn=%d", states[i].alias, states[i].currentQuantity, states[i].nextRespawnDay); + } + + } + catch (const Exception& e) + { + nlwarning("Could not parse deposit state file, reason : %s", e.what()); + } + } +}// CZoneManager receivedDepositState + + //----------------------------------------------- // CZoneManager getContinent //----------------------------------------------- @@ -2021,9 +2077,63 @@ void CZoneManager::tickUpdate() _DepositNeedingAutoSpawnUpdate.erase(itDeposit); itDeposit= itNext; } + // Save the deposits + if(_NextDepositSave < CTickEventHandler::getGameCycle()) + { + _NextDepositSave = CTickEventHandler::getGameCycle() + DepositSaveInterval; + saveDeposits(); + } }// CZoneManager tickUpdate + +//----------------------------------------------- +// CZoneManager saveDeposits +//----------------------------------------------- +void CZoneManager::saveDeposits() +{ + std::vector toSave; + for(int i=0; i < _Deposits.size(); i++) + { + if (!_Deposits[i]->needSave()) continue; + CDepositState tmp = _Deposits[i]->currentState(); + //Don't save if for some reason we couldn't get an alias for the deposit + if(tmp.alias == 0) continue; + toSave.push_back(tmp); + } + // No need to save if we have 0 states + if(toSave.size() == 0) return; + CMemStream stream; + try + { + if(DepositStateUseXml) + { + COXml output; + if (!output.init(&stream)) + { + nlwarning(" cannot init XML output for file %s", DepositStateFileName.c_str()); + return; + } + output.serialCont(toSave); + output.flush(); + } + else + { + stream.serialCont(toSave); + } + } + catch (const Exception & e) + { + nlwarning(" cannot save file %s : %s", DepositStateFileName.c_str(), e.what()); + } + + nldebug(": sending %d states to BIS (total of %d deposits available).", toSave.size(), _Deposits.size()); + CBackupMsgSaveFile msg( DepositStateFileName, CBackupMsgSaveFile::SaveFile, Bsi ); + msg.DataMsg.serialBuffer((uint8*)stream.buffer(), stream.length()); + Bsi.sendFile( msg ); + +}// CZoneManager saveDeposits + //----------------------------------------------- // CZoneManager dumpWorld //----------------------------------------------- diff --git a/code/ryzom/server/src/entities_game_service/zone_manager.h b/code/ryzom/server/src/entities_game_service/zone_manager.h index 8390f2387..4134e0cb7 100644 --- a/code/ryzom/server/src/entities_game_service/zone_manager.h +++ b/code/ryzom/server/src/entities_game_service/zone_manager.h @@ -31,6 +31,7 @@ #include "game_share/string_manager_sender.h" #include "mission_manager/ai_alias_translator.h" #include "deposit.h" +#include "game_share/backup_service_interface.h" class CCharacter; extern NLMISC::CRandom RandomGenerator; @@ -39,6 +40,7 @@ static const uint16 InvalidSpawnZoneId = 0xFFFF; static const uint16 InvalidPlaceId = 0xFFFF; +static const std::string DepositStateFileName = "deposits_state"; /** * A teleport destination zone * \author Nicolas Brigand @@ -539,6 +541,11 @@ public: */ void clearEcotypes(); + // Save deposit to primitive file, if needed + void saveDeposits(); + /// Callback for deposit state + void receivedDepositState(CFileDescription const &fileDescription, NLMISC::IStream &dataStream); + private: /** @@ -640,6 +647,24 @@ private: /// The ecotype zones static CEcotypeZones _EcotypeZones; + + /// Next cycle where we'll save deposits + NLMISC::TGameCycle _NextDepositSave; +}; + +class CDepositCallback : public IBackupFileReceiveCallback +{ +public: + CDepositCallback() : processed(false) {} + bool processed; + void callback(const CFileDescription& fileDescription, NLMISC::IStream& dataStream) + { + if (!processed) + { + CZoneManager::getInstance().receivedDepositState(fileDescription, dataStream); + processed = true; + } + } }; From 4952eeb58553cd480475cad059627933f00dadee Mon Sep 17 00:00:00 2001 From: Guillaume Dupuy Date: Wed, 12 Apr 2017 22:15:23 +0200 Subject: [PATCH 3/5] Add default constructor for CDepositState --HG-- branch : save_deposit_state_v2 --- code/ryzom/server/src/entities_game_service/deposit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ryzom/server/src/entities_game_service/deposit.h b/code/ryzom/server/src/entities_game_service/deposit.h index ce810f21c..24ca5830d 100644 --- a/code/ryzom/server/src/entities_game_service/deposit.h +++ b/code/ryzom/server/src/entities_game_service/deposit.h @@ -199,6 +199,7 @@ struct CDepositState { uint32 alias; float currentQuantity; uint32 nextRespawnDay; + CDepositState() : alias(0), currentQuantity(0.f), nextRespawnDay(0) {} void serial(NLMISC::IStream &f) throw(NLMISC::EStream) { if(f.isXML()) From 9e7258d96ed2a95b50dd46cc2be9ee5948b80c62 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sun, 16 Apr 2017 21:56:12 +0300 Subject: [PATCH 4/5] Fixed: Crash on webig debug log --HG-- branch : develop --- code/nel/src/gui/group_html.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/src/gui/group_html.cpp b/code/nel/src/gui/group_html.cpp index a3cfd514f..c5c4bb532 100644 --- a/code/nel/src/gui/group_html.cpp +++ b/code/nel/src/gui/group_html.cpp @@ -4985,7 +4985,7 @@ namespace NLGUI } #if LOG_DL - nlwarning("(%s) browse local file '%s'", filename.c_str()); + nlwarning("browse local file '%s'", filename.c_str()); #endif _TrustedDomain = true; From 20c216ecda7a08e73e6005b201b7e56078df3763 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sun, 16 Apr 2017 22:01:44 +0300 Subject: [PATCH 5/5] Added: Basic queue for curl downloads --HG-- branch : develop --- code/nel/include/nel/gui/group_html.h | 5 +- code/nel/src/gui/group_html.cpp | 193 ++++++++++++++++---------- 2 files changed, 122 insertions(+), 76 deletions(-) diff --git a/code/nel/include/nel/gui/group_html.h b/code/nel/include/nel/gui/group_html.h index 479fc7384..2dc5e80bc 100644 --- a/code/nel/include/nel/gui/group_html.h +++ b/code/nel/include/nel/gui/group_html.h @@ -820,7 +820,8 @@ namespace NLGUI struct CDataDownload { public: - CDataDownload(CURL *c, const std::string &u, const std::string &d, FILE *f, TDataType t, CViewBase *i, const std::string &s, const std::string &m, const CStyleParams &style = CStyleParams()) : curl(c), url(u), dest(d), luaScript(s), md5sum(m), type(t), fp(f) + CDataDownload(const std::string &u, const std::string &d, TDataType t, CViewBase *i, const std::string &s, const std::string &m, const CStyleParams &style = CStyleParams()) + : curl(NULL), fp(NULL), url(u), dest(d), type(t), luaScript(s), md5sum(m) { if (t == ImgType) imgs.push_back(CDataImageDownload(i, style)); } @@ -840,6 +841,8 @@ namespace NLGUI CURLM *MultiCurl; int RunningCurls; + bool startCurlDownload(CDataDownload &download); + void initImageDownload(); void checkImageDownload(); void addImageDownload(const std::string &url, CViewBase *img, const CStyleParams &style = CStyleParams()); diff --git a/code/nel/src/gui/group_html.cpp b/code/nel/src/gui/group_html.cpp index c5c4bb532..418704262 100644 --- a/code/nel/src/gui/group_html.cpp +++ b/code/nel/src/gui/group_html.cpp @@ -265,6 +265,61 @@ namespace NLGUI return dest; } + // Add url to MultiCurl queue and return cURL handle + bool CGroupHTML::startCurlDownload(CDataDownload &download) + { + if (!MultiCurl) + { + nlwarning("Invalid MultiCurl handle, unable to download '%s'", download.url.c_str()); + return false; + } + + string tmpdest = download.dest + ".tmp"; + + // erase the tmp file if exists + if (CFile::fileExists(tmpdest)) + CFile::deleteFile(tmpdest); + + FILE *fp = nlfopen (tmpdest, "wb"); + if (fp == NULL) + { + nlwarning("Can't open file '%s' for writing: code=%d '%s'", tmpdest.c_str (), errno, strerror(errno)); + return false; + } + + CURL *curl = curl_easy_init(); + if (!curl) + { + fclose(fp); + CFile::deleteFile(tmpdest); + + nlwarning("Creating cURL handle failed, unable to download '%s'", download.url.c_str()); + return false; + } + + download.curl = curl; + download.fp = fp; + + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true); + curl_easy_setopt(curl, CURLOPT_URL, download.url.c_str()); + + // limit curl to HTTP and HTTPS protocols only + curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + + std::string userAgent = options.appName + "/" + options.appVersion; + curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str()); + + sendCookies(curl, _DocumentDomain, _TrustedDomain); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + + curl_multi_add_handle(MultiCurl, curl); + + return true; + } + // Add a image download request in the multi_curl void CGroupHTML::addImageDownload(const string &url, CViewBase *img, const CStyleParams &style) { @@ -285,55 +340,29 @@ namespace NLGUI // use requested url for local name string dest = localImageName(url); - string tmpdest = localImageName(url)+".tmp"; #ifdef LOG_DL nlwarning("add to download '%s' dest '%s' img %p", finalUrl.c_str(), dest.c_str(), img); #endif - // erase the tmp file if exists - if (NLMISC::CFile::fileExists(tmpdest)) - NLMISC::CFile::deleteFile(tmpdest); - if (!NLMISC::CFile::fileExists(dest)) { - if (!MultiCurl) - { - nlwarning("Invalid MultiCurl handle, unable to download '%s'", finalUrl.c_str()); - return; - } + Curls.push_back(CDataDownload(finalUrl, dest, ImgType, img, "", "", style)); + if (Curls.size() < options.curlMaxConnections) { + if (!startCurlDownload(Curls.back())) + { + Curls.pop_back(); + return; + } - CURL *curl = curl_easy_init(); - if (!curl) - { - nlwarning("Creating cURL handle failed, unable to download '%s'", finalUrl.c_str()); - return; - } - - FILE *fp = nlfopen(tmpdest, "wb"); - if (fp == NULL) - { - curl_easy_cleanup(curl); - - nlwarning("Can't open file '%s' for writing: code=%d '%s'", tmpdest.c_str (), errno, strerror(errno)); - return; - } - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true); - curl_easy_setopt(curl, CURLOPT_URL, finalUrl.c_str()); - - std::string userAgent = options.appName + "/" + options.appVersion; - curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str()); - - sendCookies(curl, _DocumentDomain, _TrustedDomain); - - curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - - curl_multi_add_handle(MultiCurl, curl); - Curls.push_back(CDataDownload(curl, finalUrl, dest, fp, ImgType, img, "", "", style)); + RunningCurls++; #ifdef LOG_DL - nlwarning("adding handle %x, %d curls", curl, Curls.size()); + nlwarning("(%s) adding handle %x, %d curls", _Id.c_str(), Curls.back().curl, Curls.size()); + } + else + { + nlwarning("(%s) download queued, %d curls", _Id.c_str(), Curls.size()); #endif - RunningCurls++; + } } else { @@ -378,14 +407,9 @@ namespace NLGUI } string dest = localBnpName(url); - string tmpdest = localBnpName(url)+".tmp"; #ifdef LOG_DL nlwarning("add to download '%s' dest '%s'", url.c_str(), dest.c_str()); #endif - - // erase the tmp file if exists - if (NLMISC::CFile::fileExists(tmpdest)) - NLMISC::CFile::deleteFile(tmpdest); // create/delete the local file if (NLMISC::CFile::fileExists(dest)) @@ -402,39 +426,23 @@ namespace NLGUI } if (action != "delete") { - if (!MultiCurl) + Curls.push_back(CDataDownload(url, dest, BnpType, NULL, script, md5sum)); + if (Curls.size() < options.curlMaxConnections) { - nlwarning("Invalid MultiCurl handle, unable to download '%s'", url.c_str()); - return false; - } - - CURL *curl = curl_easy_init(); - if (!curl) - { - nlwarning("Creating cURL handle failed, unable to download '%s'", url.c_str()); - return false; - } - - FILE *fp = nlfopen (tmpdest, "wb"); - if (fp == NULL) - { - curl_easy_cleanup(curl); - nlwarning("Can't open file '%s' for writing: code=%d '%s'", tmpdest.c_str (), errno, strerror(errno)); - return false; - } - - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - - curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - - curl_multi_add_handle(MultiCurl, curl); - Curls.push_back(CDataDownload(curl, url, dest, fp, BnpType, NULL, script, md5sum)); + if (!startCurlDownload(Curls.back())) + { + Curls.pop_back(); + return false; + } + RunningCurls++; #ifdef LOG_DL - nlwarning("adding handle %x, %d curls", curl, Curls.size()); + nlwarning("(%s) adding handle %x, %d curls", _Id.c_str(), Curls.back().curl, Curls.size()); + } + else + { + nlwarning("(%s) download queued, %d curls", _Id.c_str(), Curls.size()); #endif - RunningCurls++; + } } else return true; @@ -631,7 +639,30 @@ namespace NLGUI } } } + RunningCurls = NewRunningCurls; + + if (RunningCurls < options.curlMaxConnections) + { + for (vector::iterator it=Curls.begin(); itcurl == NULL) { + #ifdef LOG_DL + nlwarning("(%s) starting new download '%s'", _Id.c_str(), it->url.c_str()); + #endif + if (!startCurlDownload(*it)) + { + Curls.erase(it); + break; + } + + RunningCurls++; + if (RunningCurls >= options.curlMaxConnections) + break; + } + } + } + #ifdef LOG_DL if (RunningCurls > 0 || !Curls.empty()) nlwarning("(%s) RunningCurls %d, _Curls %d", _Id.c_str(), RunningCurls, Curls.size()); @@ -4531,6 +4562,18 @@ namespace NLGUI Curls[i].imgs.clear(); } + // remove download that are still queued + for (vector::iterator it=Curls.begin(); itcurl == NULL) { + #ifdef LOG_DL + nlwarning("Remove waiting curl download (%s)", it->url.c_str()); + #endif + it = Curls.erase(it); + } else { + ++it; + } + } } // ***************************************************************************