Browse Source

Implement a new, asynchronous plResPatcher singleton

Adam Johnson 13 years ago
parent
commit
032a577111
  1. 54
      Sources/Plasma/Apps/plClient/plClient.cpp
  2. 2
      Sources/Plasma/Apps/plClient/plClient.h
  3. 20
      Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp
  4. 2
      Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h
  5. 562
      Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp
  6. 71
      Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h
  7. 26
      Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp
  8. 2
      Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp

54
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();
}
//============================================================================

2
Sources/Plasma/Apps/plClient/plClient.h

@ -152,8 +152,6 @@ protected:
hsBool fQuitIntro;
hsTArray<plBinkPlayer*> fMovies;
hsBool fPatchGlobalAges;
plMessagePumpProc fMessagePumpProc;
#ifndef PLASMA_EXTERNAL_RELEASE

20
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;
}
//============================================================================

2
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; }

562
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);
}
}

71
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 <queue>
#include <string>
#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<plManifestFile*> 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<Request> 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

26
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
//========================================================================

2
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();

Loading…
Cancel
Save