//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net ) // //This program is free software; you can redistribute it and/or //modify it under the terms of the GNU General Public License //as published by the Free Software Foundation; either //version 2 of the License, or (at your option) any later version. // //This program is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. // //You should have received a copy of the GNU General Public License //along with this program; if not, write to the Free Software //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "StdAfx.h" #include "downloadqueue.h" #include "updownclient.h" #include "partfile.h" #include "ed2klink.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif CDownloadQueue::CDownloadQueue(CPreferences* in_prefs,CSharedFileList* in_sharedfilelist){ app_prefs = in_prefs; sharedfilelist = in_sharedfilelist; filesrdy = 0; cur_udpserver = 0; lastfile = 0; lastudpsearchtime = 0; lastudpstattime = 0; udcounter = 0; // Maella => -Accurate measure of bandwidth: IP, TCP or UDP, eDonkey protocol, etc- m_nDownDataOverheadSourceExchange = 0; m_nDownDataOverheadFileRequest = 0; m_nDownDataOverheadOther = 0; m_nDownDataOverheadServer = 0; m_nDownDataOverheadSourceExchangePackets = 0; m_nDownDataOverheadFileRequestPackets = 0; m_nDownDataOverheadOtherPackets = 0; m_nDownDataOverheadServerPackets = 0; m_nDownDatarate = 0; // Maella end // Maella => -Overhead compensation (pseudo full download rate control)- m_lastProcessTime = ::GetTickCount(); m_lastOverallReceivedBytes = theApp.stat_overallReceivedBytes; m_lastReceivedBytes = theApp.stat_receivedBytes; m_nDownloadSlopeControl = 0; // Maella end // Maella => -Code Improvement- m_lastRefreshedDLDisplay = 0; // Maella end } void CDownloadQueue::UpdateDisplayedInfo(boolean force) { DWORD curTick = ::GetTickCount(); if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE+(uint32)(rand()/(RAND_MAX/1000))) { theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this); m_lastRefreshedDLDisplay = curTick; } } void CDownloadQueue::AddPartFilesToShare(){ for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); if (cur_file->GetStatus(true) == PS_READY) sharedfilelist->SafeAddKFile(cur_file,true); } } void CDownloadQueue::Init(){ // find all part files, read & hash them if needed and store into a list CFileFind ff; int count = 0; //char* searchpath = new char[strlen(app_prefs->GetTempDir())+MAX_PATH]; CString searchPath(app_prefs->GetTempDir()); searchPath += "\\*.part.met"; //sprintf(searchpath,"%s\\*.part.met",app_prefs->GetTempDir()); //delete[] searchpath; //check all part.met files bool end = !ff.FindFile(searchPath, 0); while (!end){ end = !ff.FindNextFile(); if (ff.IsDirectory()) continue; CPartFile* toadd = new CPartFile(); if (toadd->LoadPartFile(app_prefs->GetTempDir(),ff.GetFileName().GetBuffer())){ count++; filelist.AddTail(toadd); // to downloadqueue if (toadd->GetStatus(true) == PS_READY) sharedfilelist->SafeAddKFile(toadd); // part files are always shared files theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(toadd);// show in downloadwindow } else delete toadd; } ff.Close(); //try recovering any part.met files searchPath += ".backup"; end = !ff.FindFile(searchPath, 0); while (!end){ end = !ff.FindNextFile(); if (ff.IsDirectory()) continue; CPartFile* toadd = new CPartFile(); if (toadd->LoadPartFile(app_prefs->GetTempDir(),ff.GetFileName().GetBuffer())){ toadd->SavePartFile(); // resave backup count++; filelist.AddTail(toadd); // to downloadqueue if (toadd->GetStatus(true) == PS_READY) sharedfilelist->SafeAddKFile(toadd); // part files are always shared files theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(toadd);// show in downloadwindow theApp.emuledlg->AddLogLine(false, GetResString(IDS_RECOVERED_PARTMET), toadd->GetFileName()); } else { delete toadd; } } ff.Close(); if(count == 0) { theApp.emuledlg->AddLogLine(false,GetResString(IDS_NOPARTSFOUND)); } else { theApp.emuledlg->AddLogLine(false,GetResString(IDS_FOUNDPARTS),count); SortByPriority(); } } CDownloadQueue::~CDownloadQueue(){ // if (!theApp.emuledlg->m_app_state== APP_STATE_SHUTINGDOWN ) // SavePartFiles(true); // changed by InterCeptor for (POSITION pos =filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ //filelist.GetAt(pos)->m_hpartfile.Flush() ; // Flush before close app [Tarod] //filelist.GetAt(pos)->SavePartFile(); delete filelist.GetAt(pos); } } // [InterCeptor] void CDownloadQueue::SavePartFiles(bool del /*= false*/) { for (POSITION pos =filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)) { filelist.GetAt(pos)->m_hpartfile.Flush(); filelist.GetAt(pos)->SavePartFile(); if (del) delete filelist.GetAt(pos); } } void CDownloadQueue::AddSearchToDownload(CSearchFile* toadd){ if (IsFileExisting(toadd->GetFileHash())) return; CPartFile* newfile = new CPartFile(toadd); if (newfile->GetStatus() == PS_ERROR){ delete newfile; return; } AddDownload(newfile); } void CDownloadQueue::AddSearchToDownload(CString link){ CPartFile* newfile = new CPartFile(link); if (newfile->GetStatus() == PS_ERROR){ delete newfile; return; } AddDownload(newfile); } void CDownloadQueue::StartNextFile(){ if( !theApp.glob_prefs->StartNextFile() ) return; CPartFile* pfile = NULL; for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); if (cur_file->GetStatus() == PS_PAUSED){ if (!pfile){ pfile = cur_file; if (pfile->GetPriority() == PR_HIGH) break; } else{ if (cur_file->GetPriority() > pfile->GetPriority()){ pfile = cur_file; if (pfile->GetPriority() == PR_HIGH) break; } } } } if (pfile) pfile->ResumeFile(); } void CDownloadQueue::AddFileLinkToDownload(CED2KFileLink* pLink) { CPartFile* newfile = new CPartFile(pLink); if (newfile->GetStatus() == PS_ERROR){ delete newfile; newfile=NULL; } else AddDownload(newfile); if(pLink->HasValidSources()) { if (newfile) newfile->AddClientSources(pLink->SourcesList); else{ CPartFile* partfile = GetFileByID((uchar*)pLink->GetHashKey()); if (partfile) partfile->AddClientSources(pLink->SourcesList); } } } void CDownloadQueue::AddDownload(CPartFile* newfile) { // Barry - Add in paused mode if required if (theApp.glob_prefs->AddNewFilesPaused()) newfile->PauseFile(); filelist.AddTail(newfile); SortByPriority(); theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(newfile); theApp.emuledlg->AddLogLine(true,GetResString(IDS_NEWDOWNLOAD),newfile->GetFileName()); CString msgTemp; msgTemp.Format(GetResString(IDS_NEWDOWNLOAD)+"\n",newfile->GetFileName()); theApp.emuledlg->ShowNotifier(msgTemp, TBN_DLOAD); } bool CDownloadQueue::IsFileExisting(uchar* fileid){ if (CKnownFile* file = sharedfilelist->GetFileByID((uchar*)fileid)){ if (file->IsPartFile()) theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName() ); else theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADED), file->GetFileName() ); return true; } else if ( file = this->GetFileByID((uchar*)fileid)){ theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName() ); return true; } return false; } // Maella => -New bandwidth control- void CDownloadQueue::Process(){ bool isLimited = (app_prefs->GetMaxDownload() < UNLIMITED); // Elapsed time (TIMER_PERIOD not accurate) uint32 deltaTime = ::GetTickCount() - m_lastProcessTime; m_lastProcessTime += deltaTime; if(isLimited == false){ m_nDownloadSlopeControl = 0; // No limit } else { // Bandwidth control const sint32 slopeCredit = (sint32)(app_prefs->GetMaxDownload() * 1.024f * (float)deltaTime); // Correct the slope with the bytes sent since to last processing if(theApp.glob_prefs->GetCompensateOverhead() == true){ m_nDownloadSlopeControl += slopeCredit; // [bytes/period] m_nDownloadSlopeControl -= (sint32)(theApp.stat_overallReceivedBytes - m_lastOverallReceivedBytes); // Trunk negative value => possible when Overhead compensation activated if(m_nDownloadSlopeControl > slopeCredit){ m_nDownloadSlopeControl = slopeCredit; } else if(m_nDownloadSlopeControl < -1 * MAXFRAGSIZE){ m_nDownloadSlopeControl = -1 * MAXFRAGSIZE; } } else { // The Bandwitch control should be valid for an AVERAGE value m_nDownloadSlopeControl = slopeCredit; // [bytes/period] // Compensate the missing received bytes (e.g. control packet) const sint32 deltaByte = (sint32)(theApp.stat_receivedBytes - m_lastReceivedBytes); m_nDownloadSlopeControl += (deltaByte >= slopeCredit) ? 0 : (slopeCredit - deltaByte); // Allow instant pick of 20% const sint32 slopeCreditPick = 120 * slopeCredit / 100; if(m_nDownloadSlopeControl > slopeCreditPick){ m_nDownloadSlopeControl = slopeCreditPick; } } } // Keep current value for next processing m_lastOverallReceivedBytes = theApp.stat_overallReceivedBytes; m_lastReceivedBytes = theApp.stat_receivedBytes; // PR_HIGH, PR_NORMAL, PR_LOW || PR_AUTO sint32 nDownloadSlopeControl = m_nDownloadSlopeControl; for(int priority = 0; priority < 3; priority++){ // Try to receive the blocks POSITION next_pos = filelist.GetHeadPosition(); for(int i=0; i<filelist.GetCount(); i++){ POSITION cur_pos = next_pos; CPartFile* cur_file = filelist.GetNext(next_pos); // Already point to the next element if(cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY){ if((priority == 0 && cur_file->GetPriority() == PR_HIGH) || (priority == 1 && cur_file->GetPriority() == PR_NORMAL) || (priority == 2 && (cur_file->GetPriority() == PR_LOW || cur_file->GetPriority() == PR_AUTO))){ // The method must be called regardless of nDownloadSlopeControl // The method returns the size of the received blocks if the download rate is limited (otherwise zero) uint32 maxAmmount = (nDownloadSlopeControl <= 0) ? 0 : (uint32)nDownloadSlopeControl; uint32 receivedBlock = cur_file->Process(maxAmmount, isLimited); if(receivedBlock > 0){ ASSERT(nDownloadSlopeControl >= (sint32)receivedBlock); nDownloadSlopeControl -= receivedBlock; // Try to 'balance' the download between sources (=> clients). // Move the 'uploaded' at the end of the list. filelist.AddTail(cur_file); filelist.RemoveAt(cur_pos); } } } } } // Server statistic + UDP socket udcounter++; if (udcounter == (500/TIMER_PERIOD)) { // Maella => -Small latency- every 0.5 second if((!lastudpstattime) || (::GetTickCount() - lastudpstattime) > UDPSERVERSTATTIME){ lastudpstattime = ::GetTickCount(); theApp.serverlist->ServerStats(); } } if (udcounter >= (1000/TIMER_PERIOD)) { // Maella => -Small latency- every 1 second udcounter = 0; if ((!lastudpsearchtime) || (::GetTickCount() - lastudpsearchtime) > UDPSERVERREASKTIME) SendNextUDPPacket(); } } // Maella end // Maella => -Accurate measure of bandwidth: IP, TCP or UDP, eDonkey protocol, etc- void CDownloadQueue::CompDownDataRate(){ m_nDownDatarate = 0; for (POSITION pos = filelist.GetHeadPosition(); pos != 0; ){ CPartFile* cur_file = filelist.GetNext(pos); cur_file->CompDownDataRate(); m_nDownDatarate += cur_file->GetDownDatarate(); // [bytes/s] } } // Maella end CPartFile* CDownloadQueue::GetFileByID(uchar* filehash){ for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ if (!memcmp(filehash,filelist.GetAt(pos)->GetFileHash(),16)) return filelist.GetAt(pos); } return 0; } bool CDownloadQueue::IsPartFile(void* totest){ for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)) if (totest == filelist.GetAt(pos)) return true; return false; } void CDownloadQueue::CheckAndAddSource(CPartFile* sender,CUpDownClient* source){ // if we block loopbacks at this point it should prevent us from connecting to ourself if(!source->HasLowID() && (source->GetUserID() & 0xFF) == 0x7F) { delete source; return; } // uses this only for temp. clients for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty()) for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){ if (cur_file->srclists[sl].GetAt(pos2)->Compare(source)){ if (cur_file == sender){ // this file has already this source delete source; return; } // set request for this source if (cur_file->srclists[sl].GetAt(pos2)->AddRequestForAnotherFile(sender)){ // add it to uploadlistctrl theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,cur_file->srclists[sl].GetAt(pos2),true); delete source; return; } else{ delete source; return; } } } } //our new source is real new but maybe it is already uploading to us? //if yes the known client will be attached to the var "source" //and the old sourceclient will be deleted if (theApp.clientlist->AttachToAlreadyKnown(&source,0)) source->reqfile = sender; else theApp.clientlist->AddClient(source,true); sender->srclists[source->sourcesslot].AddTail(source); theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,false); //theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this); UpdateDisplayedInfo(); } void CDownloadQueue::CheckAndAddKnownSource(CPartFile* sender,CUpDownClient* source){ if(!source->HasLowID() && (source->GetUserID() & 0xFF) == 0x7F) return; // use this for client which are already know (downloading for example) for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty()) if (cur_file->srclists[sl].Find(source)){ if (cur_file == sender) return; if (source->AddRequestForAnotherFile(sender)) theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,true); return; } } source->reqfile = sender; sender->srclists[source->sourcesslot].AddTail(source); theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,false); UpdateDisplayedInfo(); } bool CDownloadQueue::RemoveSource(CUpDownClient* toremove, bool updatewindow){ bool removed = false; for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty()) for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){ if (toremove == cur_file->srclists[sl].GetAt(pos2)){ cur_file->srclists[sl].RemoveAt(pos2); cur_file->NewSrcPartsInfo(); removed = true; // Maella -Downloading source list- // Remark: always try to remove to be sure cur_file->RemoveDownloadingSource(toremove); // Maella end break; } } cur_file->UpdateAvailablePartsCount(); } if (updatewindow){ toremove->SetDownloadState(DS_NONE); theApp.emuledlg->transferwnd.downloadlistctrl.RemoveSource(toremove,0); } toremove->reqfile = 0; return removed; } void CDownloadQueue::RemoveFile(CPartFile* toremove){ for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ if (toremove == filelist.GetAt(pos)){ filelist.RemoveAt(pos); return; } } } void CDownloadQueue::DeleteAll(){ POSITION pos; for (pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty()) cur_file->srclists[sl].RemoveAll(); // Barry - Should also remove all requested blocks // Don't worry about deleting the blocks, that gets handled // when CUpDownClient is deleted in CClientList::DeleteAll() cur_file->RemoveAllRequestedBlocks(); } } bool CDownloadQueue::SendNextUDPPacket(){ if (filelist.IsEmpty() || !theApp.serverconnect->IsConnected()) return false; if (!cur_udpserver) if (!(cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver))){ TRACE("ERROR:SendNextUDPPacket() no server found"); StopUDPRequests(); }; // get nextfile CPartFile* nextfile = 0; while (!(nextfile && (nextfile->GetStatus() == PS_READY ||nextfile->GetStatus() == PS_EMPTY))){ if (lastfile == 0){ nextfile = filelist.GetHead(); lastfile = nextfile; } else{ POSITION pos = filelist.Find(lastfile); if (!pos){ TRACE("Error: CDownloadQueue::SendNextUDPPacket()"); nextfile = filelist.GetHead(); lastfile = nextfile; } else{ filelist.GetNext(pos); if (pos == 0){ cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver); if (cur_udpserver == 0){ //TRACE("finished"); lastudpsearchtime = ::GetTickCount(); lastfile = 0; return false; // finished (processed all file & all servers) } nextfile = filelist.GetHead(); lastfile = nextfile; } else{ nextfile = filelist.GetAt(pos); lastfile = nextfile; } } } } if( (theApp.glob_prefs->GetMaxSourcePerFileUDP()) < nextfile->GetSourceCount()) //<<-- return true; Packet packet(OP_GLOBGETSOURCES,16); memcpy(packet.pBuffer,nextfile->GetFileHash(),16); if (cur_udpserver != theApp.serverlist->GetServerByAddress(theApp.serverconnect->GetCurrentServer()->GetAddress(),theApp.serverconnect->GetCurrentServer()->GetPort())) theApp.serverconnect->SendUDPPacket(&packet,cur_udpserver,false); return true; } void CDownloadQueue::StopUDPRequests(){ cur_udpserver = 0; lastudpsearchtime = ::GetTickCount(); lastfile = 0; } void CDownloadQueue::SortByPriority(){ POSITION pos1, pos2; uint16 i = 0; for( pos1 = filelist.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){ filelist.GetNext(pos1); CPartFile* cur_file = filelist.GetAt(pos2); if (cur_file->GetPriority() == PR_HIGH){ filelist.AddHead(cur_file); filelist.RemoveAt(pos2); } else if (cur_file->GetPriority() == PR_LOW){ filelist.AddTail(cur_file); filelist.RemoveAt(pos2); } i++; if (i == filelist.GetCount()) break; } } void CDownloadQueue::GetDownloadStats(int results[]) { results[0]=0; results[1]=0; for (POSITION pos =theApp.downloadqueue->filelist.GetHeadPosition();pos != 0;theApp.downloadqueue->filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); results[0]+=cur_file->GetSourceCount(); results[1]+=cur_file->GetTransferingSrcCount(); } } CUpDownClient* CDownloadQueue::GetDownloadClientByIP(uint32 dwIP){ for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){ CPartFile* cur_file = filelist.GetAt(pos); for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty()) for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){ if (dwIP == cur_file->srclists[sl].GetAt(pos2)->GetIP()){ return cur_file->srclists[sl].GetAt(pos2); } } } return NULL; }