diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 39b5b5a4..24e802be 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -78,6 +78,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plPreloaderMsg.h" #include "plMessage/plNetCommMsgs.h" #include "plMessage/plAgeLoadedMsg.h" +#include "plMessage/plResPatcherMsg.h" #include "pfConsoleCore/pfConsoleEngine.h" #include "pfConsole/pfConsole.h" @@ -199,8 +200,7 @@ plClient::plClient() fHoldLoadRequests(false), fNumLoadingRooms(0), fNumPostLoadMsgs(0), - fPostLoadMsgInc(0.f), - fPatchGlobalAges(false) + fPostLoadMsgInc(0.f) { #ifndef PLASMA_EXTERNAL_RELEASE bPythonDebugConnected = false; @@ -863,6 +863,14 @@ hsBool plClient::MsgReceive(plMessage* msg) return true; } + //============================================================================ + // plResPatcherMsg + //============================================================================ + if (plResPatcherMsg * resMsg = plResPatcherMsg::ConvertNoRef(msg)) { + plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey()); + IOnAsyncInitComplete(); + } + return hsKeyedObject::MsgReceive(msg); } @@ -1578,26 +1586,19 @@ hsBool plClient::StartInit() //============================================================================ void plClient::IPatchGlobalAgeFiles( void ) { - const char * ageFiles[] = { - "GlobalAnimations", - "GlobalAvatars", - "GlobalClothing", - "GlobalMarkers", - "GUI", - "CustomAvatars" - }; - - for (unsigned i = 0; i < arrsize(ageFiles); ++i) { - plResPatcher myPatcher(ageFiles[i], true); - - if (gDataServerLocal) - break; - - if (!myPatcher.Update()) { - SetDone(true); - break; - } + plResPatcher* patcher = plResPatcher::GetInstance(); + if (!gDataServerLocal) + { + patcher->RequestManifest(L"CustomAvatars"); + patcher->RequestManifest(L"GlobalAnimations"); + patcher->RequestManifest(L"GlobalAvatars"); + patcher->RequestManifest(L"GlobalClothing"); + patcher->RequestManifest(L"GlobalMarkers"); + patcher->RequestManifest(L"GUI"); } + + plgDispatch::Dispatch()->RegisterForExactType(plResPatcherMsg::Index(), GetKey()); + patcher->Start(); } void plClient::InitDLLs() @@ -1801,15 +1802,6 @@ hsBool plClient::IUpdate() plgDispatch::MsgSend(cameras); plProfile_EndTiming(CameraMsg); - if (fPatchGlobalAges) - { - // Download or patch our global ages, if necessary - IPatchGlobalAgeFiles(); - IOnAsyncInitComplete(); - - fPatchGlobalAges = false; - } - return false; } @@ -2530,7 +2522,7 @@ void plClient::IHandlePreloaderMsg (plPreloaderMsg * msg) { return; } - fPatchGlobalAges = true; + IPatchGlobalAgeFiles(); } //============================================================================ diff --git a/Sources/Plasma/Apps/plClient/plClient.h b/Sources/Plasma/Apps/plClient/plClient.h index 72f4b6ba..b99b4efd 100644 --- a/Sources/Plasma/Apps/plClient/plClient.h +++ b/Sources/Plasma/Apps/plClient/plClient.h @@ -152,8 +152,6 @@ protected: hsBool fQuitIntro; hsTArray fMovies; - hsBool fPatchGlobalAges; - plMessagePumpProc fMessagePumpProc; #ifndef PLASMA_EXTERNAL_RELEASE diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp b/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp index 1eb32b84..fdbe432c 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp @@ -63,6 +63,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plResMgr/plKeyFinder.h" #include "plAgeDescription/plAgeDescription.h" #include "plSDL/plSDL.h" +#include "plMessage/plResPatcherMsg.h" #include "plNetClient/plNetClientMgr.h" #include "plResMgr/plRegistryHelpers.h" #include "plResMgr/plRegistryNode.h" @@ -110,7 +111,7 @@ plAgeLoader::~plAgeLoader() void plAgeLoader::Shutdown() { - + plResPatcher::GetInstance()->Shutdown(); } void plAgeLoader::Init() @@ -170,17 +171,18 @@ bool plAgeLoader::LoadAge(const char ageName[]) } //============================================================================ -bool plAgeLoader::UpdateAge(const char ageName[]) +void plAgeLoader::UpdateAge(const char ageName[]) { - bool result = true; - - if (!gDataServerLocal) + if (gDataServerLocal) + // We have to send this msg ourselves since we're not actually updating + plgDispatch::Dispatch()->MsgSend(new plResPatcherMsg); + else { - plResPatcher myPatcher(ageName); - result = myPatcher.Update(); + wchar_t* wideAgeName = hsStringToWString(ageName); + plResPatcher::GetInstance()->RequestManifest(wideAgeName); + plResPatcher::GetInstance()->Start(); + delete[] wideAgeName; } - - return result; } //============================================================================ diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h b/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h index 22a6599c..7ec3baff 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h @@ -110,7 +110,7 @@ public: hsBool MsgReceive(plMessage* msg); bool LoadAge(const char ageName[]); bool UnloadAge() { return IUnloadAge(); } - bool UpdateAge(const char ageName[]); + void UpdateAge(const char ageName[]); void NotifyAgeLoaded( bool loaded ); const plKeyVec& PendingPageOuts() const { return fPendingPageOuts; } diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp index ad5d0cff..57154f9a 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp @@ -39,455 +39,245 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plResPatcher.h" -#include "hsResMgr.h" +#include "plResPatcher.h" -#include "plAgeDescription/plAgeManifest.h" -#include "plResMgr/plResManager.h" -#include "plFile/plFileUtils.h" -#include "plFile/plEncryptedStream.h" +#include "plAgeLoader/plAgeLoader.h" #include "plCompression/plZlibStream.h" -#include "plAudioCore/plAudioFileReader.h" -#include "plProgressMgr/plProgressMgr.h" - -#include "pnAsyncCore/pnAsyncCore.h" -#include "pnNetCli/pnNetCli.h" +#include "plEncryption/plChecksum.h" +#include "plFile/plFileUtils.h" +#include "plMessage/plResPatcherMsg.h" +#include "pnNetBase/pnNbError.h" #include "plNetGameLib/plNetGameLib.h" - -#include "pnDispatch/plDispatch.h" +#include "plProgressMgr/plProgressMgr.h" #include "plStatusLog/plStatusLog.h" -static const unsigned kMaxDownloadTries = 10; - -////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// -class plDownloadStream : public plZlibStream +class plResDownloadStream : public plZlibStream { -private: plOperationProgress* fProgress; - unsigned fBytesReceived; -public: - plDownloadStream(plOperationProgress* progress) : fProgress(progress), fBytesReceived(0), plZlibStream() {} - virtual ~plDownloadStream() {} + bool fIsZipped; - virtual UInt32 Write(UInt32 byteCount, const void* buffer); +public: + plResDownloadStream(plOperationProgress* prog, const wchar_t* reqFile) + : fProgress(prog) + { + fIsZipped = wcscmp(plFileUtils::GetFileExt(reqFile), L"gz") == 0; + } - void RewindProgress() {fProgress->Increment(-(hsScalar)fBytesReceived);} // rewind the progress bar by as far as we got + UInt32 Write(UInt32 count, const void* buf) + { + fProgress->Increment((hsScalar)count); + if (fIsZipped) + return plZlibStream::Write(count, buf); + else + return fOutput->Write(count, buf); + } }; -UInt32 plDownloadStream::Write(UInt32 byteCount, const void* buffer) -{ - fProgress->Increment((hsScalar)byteCount); - fBytesReceived += byteCount; +///////////////////////////////////////////////////////////////////////////// - return plZlibStream::Write(byteCount, buffer); -} - -////////////////////////////////////////////////////////////////////////////// - -static void DownloadFileCallback(ENetError result, void* param, const wchar filename[], hsStream* writer) +static void FileDownloaded( + ENetError result, + void* param, + const wchar_t filename[], + hsStream* writer) { plResPatcher* patcher = (plResPatcher*)param; + char* name = hsWStringToString(filename); + writer->Close(); + delete writer; - // Retry download unless shutting down or file not found - switch (result) { + switch (result) + { case kNetSuccess: - writer->Close(); - patcher->DoneWithFile(true); - break; - + PatcherLog(kStatus, " Download Complete: %s", name); + patcher->IssueRequest(); + delete[] name; + return; case kNetErrFileNotFound: - case kNetErrRemoteShutdown: - writer->Close(); - patcher->DoneWithFile(false); - break; - + PatcherLog(kError, " Download Failed: %s not found", name); + break; default: - writer->Rewind(); - NetCliFileDownloadRequest( - filename, - writer, - DownloadFileCallback, - param - ); - break; + char* error = hsWStringToString(NetErrorToString(result)); + PatcherLog(kError, " Download Failed: %s", error); + delete[] error; + break; } + // Failure case + patcher->Finish(false); + delete[] name; } -static void ManifestCallback(ENetError result, void* param, const wchar group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +static void ManifestDownloaded( + ENetError result, + void* param, + const wchar_t group[], + const NetCliFileManifestEntry manifest[], + UInt32 entryCount) { plResPatcher* patcher = (plResPatcher*)param; - patcher->DoneWithManifest(result == kNetSuccess, manifest, entryCount); -} - -//// Constructor/Destructor ////////////////////////////////////////////////// - -plResPatcher::plResPatcher(const char* ageToPatch, bool showAgeName) -{ - fAgeToPatch = ageToPatch; - fAlwaysShowAgeName = showAgeName; - IInit(); -} - -void plResPatcher::IInit() -{ - PatcherLog(kHeader, "--- Starting patch process for %s ---", fAgeToPatch.c_str()); -} - -plResPatcher::~plResPatcher() -{ - PatcherLog(kHeader, "--- Patch process done for %s ---", fAgeToPatch.c_str()); - - for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) - { - plManifestFile* file = (*i); - delete file; + char* name = hsWStringToString(group); + if (IS_NET_SUCCESS(result)) + PatcherLog(kInfo, " Downloaded manifest %s", name); + else { + PatcherLog(kError, " Failed to download manifest %s", name); + patcher->Finish(false); + delete[] name; + return; } - fMfsVec.clear(); -} - -UInt32 plResPatcher::IGetDownloadSize() -{ - if (!IGetAgeManifest()) - return 0; - -#ifdef PLASMA_EXTERNAL_RELEASE - bool showAgeName = fAlwaysShowAgeName; -#else - bool showAgeName = true; -#endif - char msg[128]; - if (!fAgeToPatch.empty()) + for (UInt32 i = 0; i < entryCount; ++i) { - if (showAgeName) - sprintf(msg, "Checking age %s...", fAgeToPatch.c_str()); - else - strcpy(msg, "Checking age..."); - } - else - sprintf(msg, "Checking..."); - - plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOperation((hsScalar)(fMfsVec.size()), msg, plProgressMgr::kNone, false, true); + const NetCliFileManifestEntry mfs = manifest[i]; + char* fileName = hsWStringToString(mfs.clientName); - UInt32 downloadSize = 0; - UInt32 downloadFiles = 0; - for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) - { - plManifestFile* mfsFile = (*i); - - if (!mfsFile->IsLocalUpToDate()) + // See if the files are the same + // 1. Check file size before we do time consuming md5 operations + // 2. Do wasteful md5. We should consider implementing a CRC instead. + if (plFileUtils::GetFileSize(fileName) == mfs.fileSize) { - downloadFiles++; - downloadSize += mfsFile->GetDownloadSize(); - } + plMD5Checksum cliMD5(fileName); + plMD5Checksum srvMD5; + char* eapSucksString = hsWStringToString(mfs.md5); + srvMD5.SetFromHexString(eapSucksString); + delete[] eapSucksString; - progress->Increment(1.f); + if (cliMD5 == srvMD5) + { + delete[] fileName; + continue; + } else + PatcherLog(kInfo, " Enqueueing %s: MD5 Checksums Differ", fileName); + } else + PatcherLog(kInfo, " Enqueueing %s: File Sizes Differ", fileName); + + // If we're still here, then we need to update the file. + patcher->GetProgress()->SetLength((hsScalar)mfs.fileSize + patcher->GetProgress()->GetMax()); + patcher->RequestFile(mfs.downloadName, mfs.clientName); } - delete progress; - - PatcherLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize); - - return downloadSize; + patcher->IssueRequest(); + delete[] name; } -bool plResPatcher::CheckFreeSpace(UInt32 bytesNeeded) -{ -#ifdef HS_BUILD_FOR_WIN32 - ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes; - if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL)) - { - neededBytes.HighPart = 0; - neededBytes.LowPart = bytesNeeded; +///////////////////////////////////////////////////////////////////////////// - if (neededBytes.QuadPart > freeBytesAvailable.QuadPart) - { - PatcherLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded); - return false; - } - } -#endif // HS_BUILD_FOR_WIN32 +static char* sLastError = nil; +plResPatcher* plResPatcher::fInstance = nil; - return true; +plResPatcher* plResPatcher::GetInstance() +{ + if (!fInstance) + fInstance = new plResPatcher; + return fInstance; } -bool plResPatcher::IDecompressSound(plManifestFile* mfsFile, bool noOverwrite) +void plResPatcher::Shutdown() { - UInt32 flags = mfsFile->GetFlags(); - - if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "ogg") == 0) - { - plAudioFileReader* reader = plAudioFileReader::CreateReader(mfsFile->GetName(), plAudioCore::kAll, plAudioFileReader::kStreamNative); - if (!reader) - { - PatcherLog(kInfo, "Unable to create audio file reader for %s", mfsFile->GetName()); - return false; - } - - UInt32 size = reader->GetDataSize(); - delete reader; + // Better not call this while we're patching + delete fInstance; +} - // Make sure we have enough free space - if (!CheckFreeSpace(size)) - return false; +///////////////////////////////////////////////////////////////////////////// - if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit)) - plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite); - if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) - plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite); - } +plResPatcher::plResPatcher() + : fPatching(false), fProgress(nil) { } - return true; +plResPatcher::~plResPatcher() +{ + if (fProgress) + delete fProgress; } -bool plResPatcher::Update() +void plResPatcher::IssueRequest() { - UInt32 downloadSize = IGetDownloadSize(); - // if download size is 0, nothing to download, but we still need to tell the res manager about the files - - plFileUtils::CreateDir("dat"); - plFileUtils::CreateDir("sfx"); - - if (!CheckFreeSpace(downloadSize)) - return false; - -#ifdef PLASMA_EXTERNAL_RELEASE - bool showAgeName = fAlwaysShowAgeName; -#else - bool showAgeName = true; -#endif - - char msg[128]; - if (!fAgeToPatch.empty()) - { - if (showAgeName) - sprintf(msg, "Downloading %s data...", fAgeToPatch.c_str()); - else - strcpy(msg, "Downloading age data..."); - } - else - sprintf(msg, "Downloading..."); - - plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOverallOperation((hsScalar)downloadSize, msg, plProgressMgr::kUpdateText, true); - - bool result = true; - plResManager* resMgr = ((plResManager*)hsgResMgr::ResMgr()); - - for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) - { - plManifestFile* mfsFile = (*i); - - if (!mfsFile->IsLocalUpToDate()) - { - FileType type = IGetFile(mfsFile, progress); - if (type == kPrp) - { - // Checks for existence before attempting to remove - resMgr->RemoveSinglePage(mfsFile->GetName()); - } - else if (type == kOther) - { - if (!IDecompressSound(mfsFile, false)) - { - char text[MAX_PATH]; - StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName()); - PatcherLog(kInfo, text ); - hsAssert(false, text); - result = false; - } - } - else - { - char text[MAX_PATH]; - StrPrintf(text, arrsize(text), "Failed downloading file: %s", mfsFile->GetName()); - PatcherLog(kInfo, text ); - hsAssert(false, text); - result = false; - } - } - else + if (!fPatching) return; + if (fRequests.empty()) + // Wheee! + Finish(); + else { + Request req = fRequests.front(); + fRequests.pop(); + + std::wstring title; + if (req.fType == kManifest) { - if (!IDecompressSound(mfsFile, true)) - { - char text[MAX_PATH]; - StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName()); - PatcherLog(kInfo, text ); - hsAssert(false, text); - result = false; + char* eapSucksString = hsWStringToString(req.fFile.c_str()); + PatcherLog(kMajorStatus, " Downloading manifest... %s", eapSucksString); + xtl::format(title, L"Checking %s for updates...", req.fFile.c_str()); + NetCliFileManifestRequest(ManifestDownloaded, this, req.fFile.c_str()); + delete[] eapSucksString; + } else if (req.fType == kFile) { + char* eapSucksString = hsWStringToString(req.fFriendlyName.c_str()); + PatcherLog(kMajorStatus, " Downloading file... %s", eapSucksString); + xtl::format(title, L"Downloading... %s", plFileUtils::GetFileName(req.fFriendlyName.c_str())); + + plFileUtils::EnsureFilePathExists(req.fFriendlyName.c_str()); + plResDownloadStream* stream = new plResDownloadStream(fProgress, req.fFile.c_str()); + if(stream->Open(eapSucksString, "wb")) + NetCliFileDownloadRequest(req.fFile.c_str(), stream, FileDownloaded, this); + else { + PatcherLog(kError, " Unable to create file %s", eapSucksString); + Finish(false); } + delete[] eapSucksString; } - if (!resMgr->FindSinglePage(mfsFile->GetName()) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0) - { - resMgr->AddSinglePage(mfsFile->GetName()); - } + char* hack = hsWStringToString(title.c_str()); + fProgress->SetTitle(hack); + delete[] hack; } - - PatcherLog(kMajorStatus, "Cleaning up patcher..." ); - delete progress; - - return result; } -plResPatcher::FileType plResPatcher::IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar) +void plResPatcher::Finish(bool success) { - PatcherLog(kInfo, " Setting up to download file %s", mfsFile->GetName()); + while (fRequests.size()) + fRequests.pop(); + if (fProgress) { + delete fProgress; + fProgress = nil; + } - bool downloadDone = false; - wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath()); - int numTries = 0; + fPatching = false; + if (success) + PatcherLog(kHeader, "--- Patch Completed Successfully ---"); + else + PatcherLog(kHeader, "--- Patch Killed by Error ---"); - while (!downloadDone) + plResPatcherMsg* pMsg = new plResPatcherMsg(success, sLastError); + pMsg->Send(); // whoosh... off it goes + if (sLastError) { - if (numTries >= kMaxDownloadTries) - { - PatcherLog(kInfo, " Max download tries exceeded (%d). Aborting download...", kMaxDownloadTries); - return kFail; - } - - plDownloadStream downloadStream(progressBar); - if (!downloadStream.Open(mfsFile->GetName(), "wb")) - { - PatcherLog(kInfo, " Unable to create file. Aborting download..."); - return kFail; - } - - PatcherLog(kInfo, " Downloading file %s...", mfsFile->GetName()); - - fSuccess = false; - fDoneWithFile = false; - NetCliFileDownloadRequest( - wServerPath, - &downloadStream, - DownloadFileCallback, - this - ); - - while (!fDoneWithFile) { - NetClientUpdate(); - plgDispatch::Dispatch()->MsgQueueProcess(); - AsyncSleep(10); - } - - if (!fSuccess) { - // remove partial file and die (server didn't have the file or server is shutting down) - downloadStream.RewindProgress(); - plFileUtils::RemoveFile(mfsFile->GetName(), true); - PatcherLog(kError, " File %s failed to download.", mfsFile->GetName()); - downloadDone = true; - } - else { - if (downloadStream.DecompressedOk()) { - PatcherLog(kInfo, " Decompress successful." ); - // download and decompress successful, do a md5 check on the resulting file - plMD5Checksum localMD5(mfsFile->GetName()); - if (localMD5 != mfsFile->GetChecksum()) { - downloadStream.RewindProgress(); - downloadStream.Close(); - plFileUtils::RemoveFile(mfsFile->GetName(), true); - PatcherLog(kError, " File %s MD5 check FAILED.", mfsFile->GetName()); - // don't set downloadDone so we attempt to re-download from the server - } - else { - downloadStream.Close(); - PatcherLog(kInfo, " MD5 check succeeded."); - downloadDone = true; - } - } - else { - downloadStream.RewindProgress(); - downloadStream.Close(); - plFileUtils::RemoveFile(mfsFile->GetName(), true); - PatcherLog(kError, " File %s failed to decompress.", mfsFile->GetName()); - // don't set downloadDone so we attempt to re-download from the server - } - } - ++numTries; + delete[] sLastError; + sLastError = nil; } - FREE(wServerPath); - - if (!fSuccess) - return kFail; - - if (stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0) - return kPrp; - - return kOther; } -bool plResPatcher::IGetAgeManifest() +void plResPatcher::RequestFile(const wchar_t* srvName, const wchar_t* cliName) { - if (fMfsVec.size() > 0) - return true; - - PatcherLog(kMajorStatus, "Downloading new manifest from data server..." ); - - fSuccess = false; - wchar* group = hsStringToWString(fAgeToPatch.c_str()); - unsigned numTries = 0; - while (!fSuccess) - { - numTries++; - fDoneWithFile = false; - NetCliFileManifestRequest(ManifestCallback, this, group); - while (!fDoneWithFile) - { - NetClientUpdate(); - plgDispatch::Dispatch()->MsgQueueProcess(); - AsyncSleep(10); - } - - if (!fSuccess) - { - fMfsVec.clear(); // clear out any bad data - if (numTries > kMaxDownloadTries) - break; // abort - } - } - delete [] group; - - if (fSuccess) - PatcherLog(kStatus, "New age manifest read; number of files: %d", fMfsVec.size() ); - else - PatcherLog(kStatus, "Failed to download manifest after trying %d times", kMaxDownloadTries); - - return fSuccess; + fRequests.push(Request(srvName, kFile, cliName)); } -void plResPatcher::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount) +void plResPatcher::RequestManifest(const wchar_t* age) { - PatcherLog(kStatus, "New age manifest received. Reading..."); - - if (success) - { - for (unsigned i = 0; i < entryCount; i++) - { - char* name = hsWStringToString(manifestEntires[i].clientName); - char* serverPath = hsWStringToString(manifestEntires[i].downloadName); - char* md5Str = hsWStringToString(manifestEntires[i].md5); - int size = manifestEntires[i].fileSize; - int zipsize = manifestEntires[i].zipSize; - int flags = manifestEntires[i].flags; - if (stricmp(plFileUtils::GetFileExt(name), "gz")) - flags |= plManifestFile::kFlagZipped; // add zipped flag if necessary - - plMD5Checksum sum; - sum.SetFromHexString(md5Str); - fMfsVec.push_back(TRACKED_NEW plManifestFile(name, serverPath, sum, size, zipsize, flags)); - - delete [] name; - delete [] serverPath; - delete [] md5Str; - } - } + fRequests.push(Request(age, kManifest)); +} - fDoneWithFile = true; - fSuccess = success; +void plResPatcher::Start() +{ + hsAssert(!fPatching, "Too many calls to plResPatcher::Start"); + fPatching = true; + PatcherLog(kHeader, "--- Patch Started (%i requests) ---", fRequests.size()); + fProgress = plProgressMgr::GetInstance()->RegisterOperation(0.0, "Checking for updates...", + plProgressMgr::kUpdateText, false, true); + IssueRequest(); } +///////////////////////////////////////////////////////////////////////////// + void PatcherLog(PatcherLogType type, const char* format, ...) { UInt32 color = 0; @@ -512,7 +302,13 @@ void PatcherLog(PatcherLogType type, const char* format, ...) va_list args; va_start(args, format); - gStatusLog->AddLineV(color, format, args); + if (type == kError) + { + sLastError = new char[1024]; // Deleted by Finish(false) + vsprintf(sLastError, format, args); + gStatusLog->AddLine(sLastError, color); + } else + gStatusLog->AddLineV(color, format, args); va_end(args); -} \ No newline at end of file +} diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h index cec09cc6..0a4d9c75 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h @@ -42,52 +42,48 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef plResPatcher_h_inc #define plResPatcher_h_inc -#include "hsStlUtils.h" +#include "HeadSpin.h" +#include +#include -#include "pnUtils/pnUtils.h" -#include "pnNetBase/pnNetBase.h" -#include "plEncryption/plChecksum.h" - - -class plManifest; -class plManifestFile; class plOperationProgress; -struct NetCliFileManifestEntry; class plResPatcher { -protected: - enum FileType {kFail, kPrp, kOther}; - std::string fAgeToPatch; - - typedef std::vector MfsFileVec; - MfsFileVec fMfsVec; - - bool fDoneWithFile; - bool fSuccess; - bool fAlwaysShowAgeName; - - void IInit(); - static void ILog(UInt32 type, const char* format, ...); - - FileType IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar); - bool IGetAgeManifest(); - - UInt32 IGetDownloadSize(); - - bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false); - -public: - plResPatcher(const char* ageToPatch, bool showAgeName = false); + enum { kManifest, kFile }; + struct Request + { + std::wstring fFile; + std::wstring fFriendlyName; + uint8_t fType; + + Request(const wchar_t* file, uint8_t type, const wchar_t* friendly = nil) + : fFile(file), fType(type) + { + if (friendly) + fFriendlyName = std::wstring(friendly); + } + }; + + static plResPatcher* fInstance; + std::queue fRequests; + plOperationProgress* fProgress; + bool fPatching; + + plResPatcher(); ~plResPatcher(); - bool Update(); +public: + static plResPatcher* GetInstance(); + static void Shutdown(); - static bool CheckFreeSpace(UInt32 bytesNeeded); + plOperationProgress* GetProgress() { return fProgress; } - // called by download callbacks to tell it we are done with the current file - void DoneWithFile(bool success) {fDoneWithFile = true; fSuccess = success;} - void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount); + void Finish(bool success = true); + void IssueRequest(); + void RequestFile(const wchar_t* file, const wchar_t* friendlyName); + void RequestManifest(const wchar_t* age); + void Start(); }; enum PatcherLogType @@ -99,5 +95,4 @@ enum PatcherLogType kError, }; void PatcherLog(PatcherLogType type, const char* format, ...); - #endif // _plResPatcher_h diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp index a917f177..f5955522 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp @@ -68,6 +68,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plAgeLoadedMsg.h" #include "plMessage/plInputIfaceMgrMsg.h" #include "plMessage/plNetClientMgrMsg.h" +#include "plMessage/plResPatcherMsg.h" #include "plProgressMgr/plProgressMgr.h" #include "pnDispatch/plDispatch.h" @@ -223,13 +224,6 @@ void plNCAgeJoiner::Start () { plAgeLoader* al = plAgeLoader::GetInstance(); al->UpdateAge(age.ageDatasetName); - - nc->ResetServerTimeOffset(); - - NetCommLinkToAge( - age, - this - ); } //============================================================================ @@ -373,6 +367,24 @@ bool plNCAgeJoiner::MsgReceive (plMessage * msg) { plAvatarMgr * am = plAvatarMgr::GetInstance(); plAgeLoader * al = plAgeLoader::GetInstance(); + //======================================================================== + // Finished updating the age from FileSrv + //======================================================================== + if (plResPatcherMsg * resMsg = plResPatcherMsg::ConvertNoRef(msg)) { + + if (resMsg->Success()) + { + nc->ResetServerTimeOffset(); + NetCommLinkToAge( + age, + this + ); + LogMsg(kLogPerf, L"AgeJoiner: Next:kNoOp (age updated)"); + } else + Complete(false, resMsg->GetError()); + return true; + } + //======================================================================== // Connected to age instance //======================================================================== diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp index d6ed4c98..f8462da7 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp @@ -84,6 +84,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plNetVoiceListMsg.h" #include "plMessage/plNetCommMsgs.h" #include "plMessage/plNetClientMgrMsg.h" +#include "plMessage/plResPatcherMsg.h" #include "plMessage/plVaultNotifyMsg.h" #include "plResMgr/plKeyFinder.h" #include "plResMgr/plPageInfo.h" @@ -355,6 +356,7 @@ int plNetClientMgr::Init() // We need plVaultNotifyMsgs for the NetLinkingMgr plgDispatch::Dispatch()->RegisterForType(plVaultNotifyMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plResPatcherMsg::Index(), GetKey()); IInitNetClientComm();