949 lines
28 KiB
C++
949 lines
28 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||
// Copyright (C) 2010 Winch Gate Property Limited
|
||
//
|
||
// This program is free software: you can redistribute it and/or modify
|
||
// it under the terms of the GNU Affero General Public License as
|
||
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU Affero General Public License
|
||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
#include "stdpch.h"
|
||
#include "bg_downloader_access.h"
|
||
#include "global.h"
|
||
#include "login_patch.h"
|
||
//
|
||
#include "nel/misc/shared_memory.h"
|
||
#include "nel/misc/i18n.h"
|
||
#include "nel/misc/win_thread.h"
|
||
#include "nel/misc/big_file.h"
|
||
//
|
||
#include "game_share/bg_downloader_msg.h"
|
||
|
||
|
||
#ifdef NL_OS_WINDOWS
|
||
|
||
#include "nel/misc/win_thread.h"
|
||
|
||
using namespace BGDownloader;
|
||
using namespace NLMISC;
|
||
|
||
extern HINSTANCE HInstance;
|
||
|
||
// exception used by the download coroutine to signal an error
|
||
class EDownloadException : public NLMISC::Exception
|
||
{
|
||
public:
|
||
EDownloadException(const std::string &reason) : Exception(reason) {}
|
||
};
|
||
|
||
class EDownloadTerminationRequested : public NLMISC::Exception
|
||
{
|
||
public:
|
||
EDownloadTerminationRequested() : Exception("Download termination requested") {}
|
||
};
|
||
|
||
class EWaitMessageTimeoutException : public NLMISC::Exception
|
||
{
|
||
public:
|
||
EWaitMessageTimeoutException() : Exception("Download timeout message") {}
|
||
};
|
||
|
||
//=====================================================
|
||
CBGDownloaderAccess::CBGDownloaderAccess()
|
||
{
|
||
_DownloadCoTask = new CDownloadCoTask;
|
||
_DownloadCoTask->Parent = this;
|
||
_State = State_Idle;
|
||
_TaskResult = BGDownloader::TaskResult_Unknown;
|
||
_AvailablePatchs = 0;
|
||
clearCurrentMessage();
|
||
_MustLaunchBatFile = false;
|
||
_ShowDownloader = false;
|
||
_DownloadThreadPriority = ThreadPriority_Low;
|
||
_FrozenUI = false;
|
||
_PatchCompletionFlag = false;
|
||
_RyzomInstPIDPtr = NULL;
|
||
_WaitBalloonDisplay = false;
|
||
_CurrentFileProgress = 0.f;
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::clearCurrentMessage()
|
||
{
|
||
_CurrentMessage = ucstring();
|
||
_CurrentFilesToGet = 0;
|
||
_TotalFilesToGet = 0;
|
||
_PatchingSize = 0;
|
||
_TotalSize = 0;
|
||
_CurrentFileProgress = 0.f;
|
||
}
|
||
|
||
//=====================================================
|
||
CBGDownloaderAccess::~CBGDownloaderAccess()
|
||
{
|
||
delete _DownloadCoTask;
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::init()
|
||
{
|
||
release();
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::release()
|
||
{
|
||
_DownloaderMsgQueue.release();
|
||
if (_DownloadCoTask->isStarted())
|
||
{
|
||
_DownloadCoTask->requestTerminate();
|
||
while (_DownloadCoTask->isFinished())
|
||
{
|
||
}
|
||
}
|
||
_State = State_Idle;
|
||
if (_RyzomInstPIDPtr)
|
||
{
|
||
NLMISC::CSharedMemory::closeSharedMemory(_RyzomInstPIDPtr);
|
||
_RyzomInstPIDPtr = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::update()
|
||
{
|
||
/*nlwarning("In msg queue = %d, out msg queue = %d",
|
||
(int) _DownloaderMsgQueue.getReceiveQueueSize(),
|
||
(int) _DownloaderMsgQueue.getSendQueueSize());*/
|
||
switch (_State)
|
||
{
|
||
case State_Idle:
|
||
nlassert(!_DownloadCoTask->isStarted());
|
||
break;
|
||
case State_Finished:
|
||
if (_WaitBalloonDisplay)
|
||
{
|
||
nlassert(_DownloadCoTask->isStarted());
|
||
_DownloadCoTask->resume();
|
||
}
|
||
else
|
||
{
|
||
if (_DownloadCoTask->isStarted())
|
||
{
|
||
resetDownloadTask();
|
||
}
|
||
}
|
||
break;
|
||
case State_Patching:
|
||
// Avoid task collision here : this may happen during the tp main loop, and
|
||
// would lead to a freeze
|
||
if (NLMISC::CCoTask::getCurrentTask() == NULL)
|
||
{
|
||
_DownloadCoTask->resume();
|
||
}
|
||
if (_State == State_Finished && !_WaitBalloonDisplay)
|
||
{
|
||
resetDownloadTask();
|
||
}
|
||
break;
|
||
default:
|
||
nlassert(0);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::resetDownloadTask()
|
||
{
|
||
delete _DownloadCoTask;
|
||
_DownloadCoTask = new CDownloadCoTask;
|
||
_DownloadCoTask->Parent = this;
|
||
nlassert(!_DownloadCoTask->isStarted());
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::startTask(const BGDownloader::CTaskDesc &taskDesc, const std::string &commandLine, bool showDownloader)
|
||
{
|
||
nlassert(_State != State_Patching); // patching already started
|
||
nlassert(!_DownloadCoTask->isStarted());
|
||
_CurrentFilesToGet = 0;
|
||
_TotalFilesToGet = 0;
|
||
_PatchingSize = 0;
|
||
_TotalSize = 0;
|
||
_CommandLine = commandLine;
|
||
_State = State_Patching;
|
||
_TaskResult = BGDownloader::TaskResult_Unknown;
|
||
_WantedTask = taskDesc;
|
||
_ErrorMsg.clear();
|
||
_ShowDownloader = showDownloader;
|
||
}
|
||
|
||
//=====================================================
|
||
bool CBGDownloaderAccess::isTaskEnded(BGDownloader::TTaskResult &result, ucstring &errorMsg) const
|
||
{
|
||
if (_State == State_Finished)
|
||
{
|
||
result = _TaskResult;
|
||
errorMsg = _ErrorMsg;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::resumeBackgroundDownload()
|
||
{
|
||
nlassert(0); // not handled yet
|
||
//_DownloadCoTask->setDownloaderMode(DownloaderMode_Autonomous);
|
||
}
|
||
|
||
////////////////////////
|
||
// DOWNLOAD COROUTINE //
|
||
////////////////////////
|
||
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::run()
|
||
{
|
||
Parent->_ErrorMsg = "";
|
||
try
|
||
{
|
||
// don't restart downloader before
|
||
if (Parent->_WantedTask.State == DLState_GetAndApplyPatch)
|
||
{
|
||
// this is a continuation of the check task, so ensure
|
||
// that the downloader is still running and in slave mode
|
||
if (!isDownloaderProcessRunning() && getDownloaderMode() != DownloaderMode_Slave)
|
||
{
|
||
throw EDownloadException(CI18N::get("uiBGD_DownloaderStopped").toUtf8());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// should be executed only once for the check
|
||
restartDownloader();
|
||
}
|
||
setDownloaderVerbosity(true);
|
||
setDownloaderMode(DownloaderMode_Slave);
|
||
doTask(Parent->_WantedTask);
|
||
setDownloaderVerbosity(false);
|
||
// clear display info, else they would remain for a brief time when arriving on patch screen
|
||
Parent->clearCurrentMessage();
|
||
//
|
||
getTaskResult(Parent->_TaskResult, Parent->_AvailablePatchs, Parent->_MustLaunchBatFile, Parent->_ErrorMsg);
|
||
|
||
Parent->_PatchCompletionFlag = false;
|
||
if (Parent->_TaskResult == BGDownloader::TaskResult_Success)
|
||
{
|
||
if (Parent->_WantedTask == DLState_CheckPatch)
|
||
{
|
||
getDescFile();
|
||
//
|
||
if (!Parent->_AvailablePatchs)
|
||
{
|
||
Parent->_WaitBalloonDisplay = true;
|
||
Parent->_State = State_Finished;
|
||
// because the downloader may pop a notification balloon here, wait some more before shutting it down
|
||
|
||
sleep(2500); // at this time, may last longer because the client starts a loading phase
|
||
Parent->_WaitBalloonDisplay = false;
|
||
// no patch, just stop the downloader
|
||
shutdownDownloader();
|
||
}
|
||
|
||
bool rosPatch = (Parent->_AvailablePatchs & (1 << BGDownloader::DownloadID_RoS)) != 0;
|
||
bool mainlandPatch = (Parent->_AvailablePatchs & (1 << BGDownloader::DownloadID_MainLand)) != 0;
|
||
|
||
if (Parent->_MustLaunchBatFile && !rosPatch) // let be superstitious, should always be true ...
|
||
{
|
||
// no patch but must rebbot ? may happen only if some 'unpacked' file needed by RoS have been deleted
|
||
shutdownDownloader();
|
||
}
|
||
else
|
||
if (mainlandPatch)
|
||
{
|
||
// if mainland patch isn't completed yet, remove the searchpaths
|
||
// to the .bnp that are going to be patched
|
||
|
||
//sint64 startTime = NLMISC::CTime::getLocalTime();
|
||
const CBNPCategorySet &bnpCatSet = Parent->_DescFile.getCategories();
|
||
std::vector<std::string> bigFiles;
|
||
for (uint catIndex = 0; catIndex < bnpCatSet.categoryCount(); ++catIndex)
|
||
{
|
||
const CBNPCategory &cat = bnpCatSet.getCategory(catIndex);
|
||
if (cat.isOptional()) // NB : 'optionnal' flag meaning has changed : it now means 'Mainland Patch'
|
||
// until an enum is added
|
||
{
|
||
for (uint f = 0; f < cat.fileCount(); ++f)
|
||
{
|
||
bigFiles.push_back(cat.getFile(f));
|
||
}
|
||
}
|
||
}
|
||
NLMISC::CPath::removeBigFiles(bigFiles);
|
||
//sint64 endTime = NLMISC::CTime::getLocalTime();
|
||
//nlinfo("%.2f s to remove paths", (endTime - startTime) / 1000.f);
|
||
}
|
||
}
|
||
|
||
if (Parent->_WantedTask.State == DLState_GetAndApplyPatch)
|
||
{
|
||
Parent->_PatchCompletionFlag = true;
|
||
if (Parent->_WantedTask.DownloadBitfield & (1 << BGDownloader::DownloadID_RoS))
|
||
{
|
||
if (Parent->_MustLaunchBatFile) // let be superstitious, should always be true ...
|
||
{
|
||
Parent->_WaitBalloonDisplay = true;
|
||
Parent->_State = State_Finished;
|
||
// because the downloader may pop a notification balloon here, wait some more before shutting it down
|
||
|
||
sleep(5000);
|
||
Parent->_WaitBalloonDisplay = false;
|
||
// because a reboot is required, just stop now ...
|
||
shutdownDownloader();
|
||
}
|
||
}
|
||
else
|
||
if (Parent->_WantedTask.DownloadBitfield & (1 << BGDownloader::DownloadID_MainLand))
|
||
{
|
||
bool memoryCompressed = CPath::isMemoryCompressed();
|
||
if (memoryCompressed)
|
||
{
|
||
CPath::memoryUncompress();
|
||
}
|
||
// and redo 'addSearchPath' on data, because of the new bnp files that have just been added
|
||
NLMISC::CPath::addSearchPath("data/", true, false, NULL);
|
||
if (memoryCompressed)
|
||
{
|
||
CPath::memoryCompress();
|
||
}
|
||
Parent->_WaitBalloonDisplay = true;
|
||
Parent->_State = State_Finished;
|
||
// because the downloader may pop a notification balloon here, wait some more before shutting it down
|
||
sleep(5000);
|
||
Parent->_WaitBalloonDisplay = false;
|
||
// stop after the mainland download (hardcoded, but should suffice for now)
|
||
shutdownDownloader();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch(EDownloadException &e)
|
||
{
|
||
//shutdownDownloader();
|
||
Parent->_TaskResult = TaskResult_Error;
|
||
Parent->_ErrorMsg.fromUtf8(e.what());
|
||
Parent->_DownloadThreadPriority = ThreadPriority_DownloaderError;
|
||
}
|
||
catch(EDownloadTerminationRequested &e)
|
||
{
|
||
shutdownDownloader();
|
||
Parent->_TaskResult = TaskResult_Error;
|
||
Parent->_ErrorMsg = ucstring(e.what());
|
||
Parent->_DownloadThreadPriority = ThreadPriority_DownloaderError;
|
||
}
|
||
catch(NLMISC::EStream &e)
|
||
{
|
||
shutdownDownloader();
|
||
Parent->_TaskResult = TaskResult_Error;
|
||
nlwarning("BG DOWNLOADER PROTOCOL ERROR ! Stream error");
|
||
Parent->_ErrorMsg = CI18N::get("uiBGD_ProtocolError") + ucstring(" : ") + ucstring(e.what());
|
||
Parent->_DownloadThreadPriority = ThreadPriority_DownloaderError;
|
||
}
|
||
catch (EWaitMessageTimeoutException &e)
|
||
{
|
||
shutdownDownloader();
|
||
Parent->_TaskResult = TaskResult_Error;
|
||
nlwarning("BG DOWNLOADER PROTOCOL ERROR ! Message timeout");
|
||
Parent->_ErrorMsg = CI18N::get("uiBGD_ProtocolError") + ucstring(" : ") + ucstring(e.what());
|
||
Parent->_DownloadThreadPriority = ThreadPriority_DownloaderError;
|
||
}
|
||
Parent->_State = State_Finished;
|
||
}
|
||
|
||
//=====================================================
|
||
bool CBGDownloaderAccess::getPatchCompletionFlag(bool clearFlag)
|
||
{
|
||
bool flag = _PatchCompletionFlag;
|
||
if (clearFlag)
|
||
{
|
||
_PatchCompletionFlag = false;
|
||
}
|
||
return flag;
|
||
}
|
||
|
||
//=====================================================
|
||
bool CBGDownloaderAccess::CDownloadCoTask::isDownloaderProcessRunning()
|
||
{
|
||
// the downloader creates a system-wide mutex, so if present, assume that the downloader is running
|
||
//
|
||
HANDLE mutex = CreateMutex (NULL, FALSE, BGDownloader::DownloaderMutexName);
|
||
if (mutex)
|
||
{
|
||
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
||
{
|
||
CloseHandle(mutex);
|
||
return true;
|
||
}
|
||
CloseHandle(mutex);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// launch the process and wait until completion
|
||
#if defined (NL_DEBUG)
|
||
static const char *BGDownloaderName = "client_background_downloader_d.exe";
|
||
#elif defined (NL_RELEASE)
|
||
static const char *BGDownloaderName = "client_background_downloader_r.exe";
|
||
#else
|
||
# error "Unknown bg downloader exe name";
|
||
#endif
|
||
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::createDownloaderProcess()
|
||
{
|
||
bool manualLaunch = false;
|
||
CConfigFile::CVar *manualLaunchVar = ClientCfg.ConfigFile.getVarPtr("BackgroundDownloaderManualLaunch");
|
||
if (manualLaunchVar)
|
||
{
|
||
manualLaunch = (manualLaunchVar->asInt() != 0);
|
||
}
|
||
if (!manualLaunch)
|
||
{
|
||
BOOL ok = NLMISC::launchProgram(BGDownloaderName, Parent->_CommandLine);
|
||
if (!ok)
|
||
{
|
||
throw EDownloadException(CI18N::get("uiBGD_LaunchError").toUtf8());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
nlwarning(Parent->_CommandLine.c_str());
|
||
}
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::restartDownloader()
|
||
{
|
||
// if another ryzom client is running, try stopping it first
|
||
for (;;)
|
||
{
|
||
void *ryzomInstPIDPtr = NLMISC::CSharedMemory::accessSharedMemory(NLMISC::toSharedMemId(RYZOM_PID_SHM_ID));
|
||
if (!ryzomInstPIDPtr) break; // only instance running is us
|
||
// try to terminate the other instance
|
||
uint count = 0;
|
||
while (*(DWORD *) ryzomInstPIDPtr == 0)
|
||
{
|
||
// the other process didn't write its pid into shared mem yet (shared memory is initially filled with zero's)
|
||
// so wait a bit (very unlikely case !!)
|
||
sleep(100);
|
||
++ count;
|
||
if (count == 50)
|
||
{
|
||
nlwarning("CBGDownloaderAccess::CDownloadCoTask : detected shared memory segment, with NULL pid");
|
||
// some problem here ...
|
||
throw EDownloadException(CI18N::get("uiBGD_MultipleRyzomInstance").toUtf8());
|
||
}
|
||
}
|
||
bool ok = NLMISC::CWinProcess::terminateProcess(*(DWORD *) ryzomInstPIDPtr);
|
||
CSharedMemory::closeSharedMemory(ryzomInstPIDPtr);
|
||
if (!ok)
|
||
{
|
||
nlwarning("CBGDownloaderAccess::CDownloadCoTask : detected shared memory segment, with good pid, but couldn't stop the process");
|
||
// couldn't stop the other client ...
|
||
throw EDownloadException(CI18N::get("uiBGD_MultipleRyzomInstance").toUtf8());
|
||
}
|
||
}
|
||
// write our pid into shared mem
|
||
Parent->_RyzomInstPIDPtr = CSharedMemory::createSharedMemory(NLMISC::toSharedMemId(RYZOM_PID_SHM_ID), sizeof(uint32));
|
||
if (!Parent->_RyzomInstPIDPtr)
|
||
{
|
||
// really, really bad luck ...
|
||
throw EDownloadException(CI18N::get("uiBGD_MultipleRyzomInstance").toUtf8());
|
||
}
|
||
*(uint32 *) Parent->_RyzomInstPIDPtr = (uint32) GetCurrentProcessId();
|
||
|
||
HWND hWnd = Driver->getDisplay();
|
||
|
||
// for safety, stop any running downloader
|
||
if (isDownloaderProcessRunning())
|
||
{
|
||
Parent->_DownloaderMsgQueue.init(hWnd, BGDownloader::ClientWndID, BGDownloader::DownloaderWndID);
|
||
sleep(200);
|
||
shutdownDownloader();
|
||
Parent->_DownloaderMsgQueue.release();
|
||
sleep(200);
|
||
}
|
||
uint tryCounter = 1;
|
||
for (;;)
|
||
{
|
||
nlwarning("Launching downloader: try n<>%d", (int) tryCounter++);
|
||
// now we can create the message queue because we are sure that it will reach the good app
|
||
Parent->_DownloaderMsgQueue.init(HInstance, BGDownloader::ClientWndID, BGDownloader::DownloaderWndID);
|
||
sleep(200);
|
||
|
||
Parent->_CurrentMessage = CI18N::get("uiBGD_Launching");
|
||
|
||
#ifndef BG_DOWNLOADER_MANUAL_LAUNCH
|
||
createDownloaderProcess();
|
||
#endif
|
||
while (!Parent->_DownloaderMsgQueue.connected())
|
||
{
|
||
yieldDownload();
|
||
}
|
||
// try probe/ack test for safety
|
||
bool ok = false;
|
||
int tryIndex = 1;
|
||
uint32 waitTime = 500;
|
||
const uint32 totalTries = 7;
|
||
while (waitTime <= 32000)
|
||
{
|
||
Parent->_CurrentMessage.fromUtf8(toString(CI18N::get("uiBGD_HandShaking").toUtf8().c_str(), tryIndex, totalTries));
|
||
|
||
sendSimpleMsg(CL_Probe);
|
||
NLMISC::CMemStream dummyMsg;
|
||
try
|
||
{
|
||
waitMsg(BGD_Ack, dummyMsg, waitTime /* max milliseconds */);
|
||
ok = true;
|
||
break;
|
||
}
|
||
catch (EWaitMessageTimeoutException &)
|
||
{
|
||
// no-op, just continue the loop for another try
|
||
}
|
||
waitTime *= 2;
|
||
++ tryIndex;
|
||
}
|
||
if (ok) break;
|
||
if (isDownloaderProcessRunning())
|
||
{
|
||
shutdownDownloader();
|
||
sleep(200);
|
||
}
|
||
Parent->_DownloaderMsgQueue.release();
|
||
sleep(200);
|
||
}
|
||
nlwarning("Downloader launched successfully");
|
||
if (Parent->_ShowDownloader)
|
||
{
|
||
Parent->showDownloader();
|
||
}
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::yieldDownload()
|
||
{
|
||
yield();
|
||
if (isTerminationRequested())
|
||
{
|
||
throw EDownloadTerminationRequested();
|
||
}
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::sendSimpleMsg(BGDownloader::TMsgType msgType)
|
||
{
|
||
CMemStream outMsg(false /* output stream */);
|
||
outMsg.serialEnum(msgType);
|
||
_DownloaderMsgQueue.sendMessage(outMsg);
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::showDownloader()
|
||
{
|
||
sendSimpleMsg(CL_Show);
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::hideDownloader()
|
||
{
|
||
sendSimpleMsg(CL_Hide);
|
||
}
|
||
|
||
|
||
//=====================================================
|
||
CTaskDesc CBGDownloaderAccess::CDownloadCoTask::getDownloaderState()
|
||
{
|
||
sendSimpleMsg(CL_GetState);
|
||
CMemStream inMsg(true /* input stream */);
|
||
waitMsg(BGD_State, inMsg);
|
||
CTaskDesc result;
|
||
inMsg.serial(result);
|
||
return result;
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::getDescFile()
|
||
{
|
||
sendSimpleMsg(CL_GetDescFile);
|
||
CMemStream inMsg(true /* input stream */);
|
||
waitMsg(BGD_DescFile, inMsg);
|
||
inMsg.serial(Parent->_DescFile);
|
||
}
|
||
|
||
|
||
//=====================================================
|
||
TDownloaderMode CBGDownloaderAccess::CDownloadCoTask::getDownloaderMode()
|
||
{
|
||
sendSimpleMsg(CL_GetMode);
|
||
CMemStream inMsg(true /* input stream */);
|
||
waitMsg(BGD_Mode, inMsg);
|
||
TDownloaderMode mode = DownloaderMode_Unknown;
|
||
inMsg.serialEnum(mode);
|
||
return mode;
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::getTaskResult(TTaskResult &result,
|
||
uint32 &availablePatchs,
|
||
bool &mustLaunchBatFile,
|
||
ucstring &errorMsg
|
||
)
|
||
{
|
||
sendSimpleMsg(CL_GetTaskResult);
|
||
CMemStream inMsg(true /* input stream */);
|
||
waitMsg(BGD_TaskResult, inMsg);
|
||
inMsg.serialEnum(result);
|
||
inMsg.serial(availablePatchs);
|
||
inMsg.serial(mustLaunchBatFile);
|
||
inMsg.serial(errorMsg);
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::doTask(const CTaskDesc &taskDesc)
|
||
{
|
||
CTaskDesc downloaderTask = getDownloaderState();
|
||
if (downloaderTask.State != DLState_Idle && downloaderTask != taskDesc)
|
||
{
|
||
// if not the wanted task, ask to stop current task, and wait until completion
|
||
stopTask();
|
||
startTask(taskDesc);
|
||
}
|
||
else
|
||
if (downloaderTask.State == DLState_Idle)
|
||
{
|
||
startTask(taskDesc);
|
||
}
|
||
// else, good task already started, just wait
|
||
waitIdle();
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::startTask(const CTaskDesc &taskDesc)
|
||
{
|
||
CMemStream outMsg(false /* output stream */);
|
||
TMsgType msgType = CL_StartTask;
|
||
outMsg.serialEnum(msgType);
|
||
CTaskDesc mutableTaskDesc = taskDesc;
|
||
outMsg.serial(mutableTaskDesc);
|
||
sendMsg(outMsg);
|
||
waitIdle();
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::stopTask()
|
||
{
|
||
sendSimpleMsg(CL_Stop);
|
||
waitIdle();
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::shutdownDownloader()
|
||
{
|
||
const uint32 SHUTDOWN_TIMEOUT = 6; // seconds until we consider the downloader is hung, and try to shut it down
|
||
sendSimpleMsg(CL_Shutdown);
|
||
uint32 startTime = NLMISC::CTime::getSecondsSince1970();
|
||
setDownloaderVerbosity(false);
|
||
while (isDownloaderProcessRunning())
|
||
{
|
||
Parent->_CurrentMessage = CI18N::get("uiBGD_ShuttingDown");
|
||
yield();
|
||
if ((NLMISC::CTime::getSecondsSince1970() - startTime) >= SHUTDOWN_TIMEOUT)
|
||
{
|
||
Parent->_CurrentMessage = CI18N::get("uiBGD_ForciblyShutdown");
|
||
yield();
|
||
if (!CWinProcess::terminateProcessFromModuleName(BGDownloaderName))
|
||
{
|
||
Parent->_CurrentMessage = CI18N::get("uiBGD_ShutdownFailed");
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
CWinProcess::terminateProcessFromModuleName(BGDownloaderName); // for safety
|
||
Parent->_CurrentMessage = ucstring();
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::setDownloaderMode(TDownloaderMode mode)
|
||
{
|
||
CMemStream outMsg(false /* output stream */);
|
||
TMsgType msgType = CL_SetMode;
|
||
outMsg.serialEnum(msgType);
|
||
outMsg.serialEnum(mode);
|
||
sendMsg(outMsg);
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::setDownloaderVerbosity(bool verbose)
|
||
{
|
||
CMemStream outMsg(false /* output stream */);
|
||
TMsgType msgType = CL_SetVerbose;
|
||
outMsg.serialEnum(msgType);
|
||
outMsg.serial(verbose);
|
||
sendMsg(outMsg);
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::waitIdle()
|
||
{
|
||
for (;;)
|
||
{
|
||
CTaskDesc downloaderTask = getDownloaderState();
|
||
if (downloaderTask.State == DLState_Idle) break;
|
||
}
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::waitMsg(BGDownloader::TMsgType wantedMsgType, NLMISC::CMemStream &msg, NLMISC::TTime timeOutInMs)
|
||
{
|
||
NLMISC::TTime startTime = NLMISC::CTime::getLocalTime();
|
||
BGDownloader::TMsgType msgType = (BGDownloader::TMsgType) ~0;
|
||
for (;;)
|
||
{
|
||
while (!Parent->_DownloaderMsgQueue.pumpMessage(msg))
|
||
{
|
||
#ifdef NL_DEBUG
|
||
CConfigFile::CVar *manualLaunchVar = ClientCfg.ConfigFile.getVarPtr("BackgroundDownloaderManualLaunch");
|
||
if (!manualLaunchVar || manualLaunchVar->asInt() == 0)
|
||
#endif
|
||
{
|
||
if (NLMISC::CTime::getLocalTime() - startTime > timeOutInMs)
|
||
{
|
||
nlwarning("Time out exceeded while waiting for message of type %d", (int) wantedMsgType);
|
||
#ifdef NL_DEBUG
|
||
//nlassert(0);
|
||
#endif
|
||
throw EWaitMessageTimeoutException();
|
||
}
|
||
}
|
||
checkDownloaderAlive();
|
||
yieldDownload();
|
||
}
|
||
nlassert (msg.isReading());
|
||
msg.serialEnum(msgType);
|
||
if (msgType == wantedMsgType) return;
|
||
if (!defaultMessageHandling(msgType, msg))
|
||
{
|
||
//nlwarning("##CLIENT RCV message of type : %s", toString(msg).c_str());
|
||
break;
|
||
}
|
||
}
|
||
if (msgType != wantedMsgType)
|
||
{
|
||
nlwarning("BG DOWNLOADER PROTOCOL ERROR ! Bad message type received. Expected type is '%d', received type is '%d'", (int) wantedMsgType, (int) msgType);
|
||
throw EDownloadException(CI18N::get("uiBGD_ProtocolError").toUtf8());
|
||
}
|
||
}
|
||
|
||
|
||
//=====================================================
|
||
bool CBGDownloaderAccess::CDownloadCoTask::defaultMessageHandling(BGDownloader::TMsgType msgType, NLMISC::CMemStream &msg)
|
||
{
|
||
nlassert(msg.isReading());
|
||
switch(msgType)
|
||
{
|
||
case BGD_Priority:
|
||
{
|
||
TThreadPriority tp;
|
||
msg.serialEnum(tp);
|
||
if (tp != Parent->_DownloadThreadPriority)
|
||
{
|
||
Parent->_DownloadThreadPriority = tp; // TMP, for debug
|
||
}
|
||
return true;
|
||
}
|
||
break;
|
||
case BGD_UpdateStatusString:
|
||
{
|
||
msg.serial(Parent->_CurrentMessage);
|
||
msg.serial(Parent->_CurrentFilesToGet);
|
||
msg.serial(Parent->_TotalFilesToGet);
|
||
msg.serial(Parent->_PatchingSize);
|
||
msg.serial(Parent->_TotalSize);
|
||
msg.serial(Parent->_CurrentFileProgress);
|
||
//nlinfo(Parent->_CurrentMessage.toString().c_str());
|
||
return true;
|
||
}
|
||
break;
|
||
case BGD_Error:
|
||
{
|
||
Parent->_TaskResult = TaskResult_Error;
|
||
ucstring errorMsg;
|
||
msg.serial(errorMsg);
|
||
throw EDownloadException(errorMsg.toUtf8());
|
||
}
|
||
break;
|
||
case BGD_Ack:
|
||
{
|
||
// NO-OP
|
||
return true;
|
||
}
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::checkDownloaderAlive()
|
||
{
|
||
if (!Parent->_DownloaderMsgQueue.connected() || !isDownloaderProcessRunning())
|
||
{
|
||
throw EDownloadException(CI18N::get("uiBGD_DownloaderDisconnected").toUtf8());
|
||
}
|
||
}
|
||
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::sendMsg(NLMISC::CMemStream &msg)
|
||
{
|
||
Parent->_DownloaderMsgQueue.sendMessage(msg);
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::CDownloadCoTask::sendSimpleMsg(BGDownloader::TMsgType msgType)
|
||
{
|
||
//nlwarning("##CLIENT SEND simple message of type : %s", toString(msgType).c_str());
|
||
Parent->sendSimpleMsg(msgType);
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::reboot()
|
||
{
|
||
CPatchManager *pm = CPatchManager::getInstance();
|
||
pm->createBatchFile(_DescFile);
|
||
pm->executeBatchFile();
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::requestDownloadThreadPriority(BGDownloader::TThreadPriority newPriority, bool freezeUI)
|
||
{
|
||
nlassert((uint) newPriority < BGDownloader::ThreadPriority_Count);
|
||
CMemStream outMsg(false /* output stream */);
|
||
TMsgType msgType = CL_SetPriority;
|
||
outMsg.serialEnum(msgType);
|
||
outMsg.serialEnum(newPriority);
|
||
outMsg.serial(freezeUI);
|
||
_DownloaderMsgQueue.sendMessage(outMsg);
|
||
_FrozenUI = freezeUI;
|
||
}
|
||
|
||
|
||
|
||
#else
|
||
|
||
//=====================================================
|
||
CBGDownloaderAccess::CBGDownloaderAccess()
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
CBGDownloaderAccess::~CBGDownloaderAccess()
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
bool CBGDownloaderAccess::getPatchCompletionFlag(bool clearFlag)
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::startTask(const BGDownloader::CTaskDesc &taskDesc, const std::string &commandLine, bool showDownloader)
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
bool CBGDownloaderAccess::isTaskEnded(BGDownloader::TTaskResult &result, ucstring &errorMsg) const
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::update()
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::release()
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::requestDownloadThreadPriority(BGDownloader::TThreadPriority newPriority, bool freezeUI)
|
||
{
|
||
// TODO for Linux
|
||
}
|
||
|
||
//=====================================================
|
||
void CBGDownloaderAccess::reboot()
|
||
{
|
||
}
|
||
|
||
// not supported on other os
|
||
//#error IMPLEMENT ME PLEASE !!
|
||
|
||
#endif
|
||
|
||
|
||
bool isBGDownloadEnabled()
|
||
{
|
||
return ClientCfg.BackgroundDownloader && !ClientCfg.Local && ClientCfg.PatchWanted;
|
||
}
|
||
|
||
|
||
static BGDownloader::TThreadPriority DownloaderPriorityBackup = BGDownloader::ThreadPriority_Low;
|
||
static bool DownloaderUIFrozen = false;
|
||
static bool DownloaderPaused = false;
|
||
|
||
|
||
// ------------------------------------------------------------------------------------------------
|
||
void pauseBGDownloader()
|
||
{
|
||
if (isBGDownloadEnabled() && !DownloaderPaused)
|
||
{
|
||
CBGDownloaderAccess &downloader = CBGDownloaderAccess::getInstance();
|
||
DownloaderPriorityBackup = downloader.getDownloadThreadPriority();
|
||
DownloaderUIFrozen = downloader.isDownloaderUIFrozen();
|
||
downloader.requestDownloadThreadPriority(BGDownloader::ThreadPriority_Paused, true);
|
||
DownloaderPaused = true;
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------------------------------------------
|
||
void unpauseBGDownloader()
|
||
{
|
||
if (isBGDownloadEnabled() && DownloaderPaused)
|
||
{
|
||
if (DownloaderPriorityBackup != BGDownloader::ThreadPriority_DownloaderError)
|
||
{
|
||
CBGDownloaderAccess::getInstance().requestDownloadThreadPriority(DownloaderPriorityBackup, DownloaderUIFrozen);
|
||
}
|
||
DownloaderPaused = false;
|
||
}
|
||
}
|