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. 560
      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/plPreloaderMsg.h"
#include "plMessage/plNetCommMsgs.h" #include "plMessage/plNetCommMsgs.h"
#include "plMessage/plAgeLoadedMsg.h" #include "plMessage/plAgeLoadedMsg.h"
#include "plMessage/plResPatcherMsg.h"
#include "pfConsoleCore/pfConsoleEngine.h" #include "pfConsoleCore/pfConsoleEngine.h"
#include "pfConsole/pfConsole.h" #include "pfConsole/pfConsole.h"
@ -199,8 +200,7 @@ plClient::plClient()
fHoldLoadRequests(false), fHoldLoadRequests(false),
fNumLoadingRooms(0), fNumLoadingRooms(0),
fNumPostLoadMsgs(0), fNumPostLoadMsgs(0),
fPostLoadMsgInc(0.f), fPostLoadMsgInc(0.f)
fPatchGlobalAges(false)
{ {
#ifndef PLASMA_EXTERNAL_RELEASE #ifndef PLASMA_EXTERNAL_RELEASE
bPythonDebugConnected = false; bPythonDebugConnected = false;
@ -863,6 +863,14 @@ hsBool plClient::MsgReceive(plMessage* msg)
return true; return true;
} }
//============================================================================
// plResPatcherMsg
//============================================================================
if (plResPatcherMsg * resMsg = plResPatcherMsg::ConvertNoRef(msg)) {
plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey());
IOnAsyncInitComplete();
}
return hsKeyedObject::MsgReceive(msg); return hsKeyedObject::MsgReceive(msg);
} }
@ -1578,26 +1586,19 @@ hsBool plClient::StartInit()
//============================================================================ //============================================================================
void plClient::IPatchGlobalAgeFiles( void ) void plClient::IPatchGlobalAgeFiles( void )
{ {
const char * ageFiles[] = { plResPatcher* patcher = plResPatcher::GetInstance();
"GlobalAnimations", if (!gDataServerLocal)
"GlobalAvatars", {
"GlobalClothing", patcher->RequestManifest(L"CustomAvatars");
"GlobalMarkers", patcher->RequestManifest(L"GlobalAnimations");
"GUI", patcher->RequestManifest(L"GlobalAvatars");
"CustomAvatars" patcher->RequestManifest(L"GlobalClothing");
}; patcher->RequestManifest(L"GlobalMarkers");
patcher->RequestManifest(L"GUI");
for (unsigned i = 0; i < arrsize(ageFiles); ++i) {
plResPatcher myPatcher(ageFiles[i], true);
if (gDataServerLocal)
break;
if (!myPatcher.Update()) {
SetDone(true);
break;
}
} }
plgDispatch::Dispatch()->RegisterForExactType(plResPatcherMsg::Index(), GetKey());
patcher->Start();
} }
void plClient::InitDLLs() void plClient::InitDLLs()
@ -1801,15 +1802,6 @@ hsBool plClient::IUpdate()
plgDispatch::MsgSend(cameras); plgDispatch::MsgSend(cameras);
plProfile_EndTiming(CameraMsg); plProfile_EndTiming(CameraMsg);
if (fPatchGlobalAges)
{
// Download or patch our global ages, if necessary
IPatchGlobalAgeFiles();
IOnAsyncInitComplete();
fPatchGlobalAges = false;
}
return false; return false;
} }
@ -2530,7 +2522,7 @@ void plClient::IHandlePreloaderMsg (plPreloaderMsg * msg) {
return; return;
} }
fPatchGlobalAges = true; IPatchGlobalAgeFiles();
} }
//============================================================================ //============================================================================

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

@ -152,8 +152,6 @@ protected:
hsBool fQuitIntro; hsBool fQuitIntro;
hsTArray<plBinkPlayer*> fMovies; hsTArray<plBinkPlayer*> fMovies;
hsBool fPatchGlobalAges;
plMessagePumpProc fMessagePumpProc; plMessagePumpProc fMessagePumpProc;
#ifndef PLASMA_EXTERNAL_RELEASE #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 "plResMgr/plKeyFinder.h"
#include "plAgeDescription/plAgeDescription.h" #include "plAgeDescription/plAgeDescription.h"
#include "plSDL/plSDL.h" #include "plSDL/plSDL.h"
#include "plMessage/plResPatcherMsg.h"
#include "plNetClient/plNetClientMgr.h" #include "plNetClient/plNetClientMgr.h"
#include "plResMgr/plRegistryHelpers.h" #include "plResMgr/plRegistryHelpers.h"
#include "plResMgr/plRegistryNode.h" #include "plResMgr/plRegistryNode.h"
@ -110,7 +111,7 @@ plAgeLoader::~plAgeLoader()
void plAgeLoader::Shutdown() void plAgeLoader::Shutdown()
{ {
plResPatcher::GetInstance()->Shutdown();
} }
void plAgeLoader::Init() 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)
// We have to send this msg ourselves since we're not actually updating
if (!gDataServerLocal) plgDispatch::Dispatch()->MsgSend(new plResPatcherMsg);
else
{ {
plResPatcher myPatcher(ageName); wchar_t* wideAgeName = hsStringToWString(ageName);
result = myPatcher.Update(); 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); hsBool MsgReceive(plMessage* msg);
bool LoadAge(const char ageName[]); bool LoadAge(const char ageName[]);
bool UnloadAge() { return IUnloadAge(); } bool UnloadAge() { return IUnloadAge(); }
bool UpdateAge(const char ageName[]); void UpdateAge(const char ageName[]);
void NotifyAgeLoaded( bool loaded ); void NotifyAgeLoaded( bool loaded );
const plKeyVec& PendingPageOuts() const { return fPendingPageOuts; } const plKeyVec& PendingPageOuts() const { return fPendingPageOuts; }

560
Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp

@ -39,455 +39,245 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
Mead, WA 99021 Mead, WA 99021
*==LICENSE==*/ *==LICENSE==*/
#include "plResPatcher.h"
#include "hsResMgr.h" #include "plResPatcher.h"
#include "plAgeDescription/plAgeManifest.h" #include "plAgeLoader/plAgeLoader.h"
#include "plResMgr/plResManager.h"
#include "plFile/plFileUtils.h"
#include "plFile/plEncryptedStream.h"
#include "plCompression/plZlibStream.h" #include "plCompression/plZlibStream.h"
#include "plAudioCore/plAudioFileReader.h" #include "plEncryption/plChecksum.h"
#include "plProgressMgr/plProgressMgr.h" #include "plFile/plFileUtils.h"
#include "plMessage/plResPatcherMsg.h"
#include "pnAsyncCore/pnAsyncCore.h" #include "pnNetBase/pnNbError.h"
#include "pnNetCli/pnNetCli.h"
#include "plNetGameLib/plNetGameLib.h" #include "plNetGameLib/plNetGameLib.h"
#include "plProgressMgr/plProgressMgr.h"
#include "pnDispatch/plDispatch.h"
#include "plStatusLog/plStatusLog.h" #include "plStatusLog/plStatusLog.h"
static const unsigned kMaxDownloadTries = 10; /////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
class plDownloadStream : public plZlibStream class plResDownloadStream : public plZlibStream
{ {
private:
plOperationProgress* fProgress; plOperationProgress* fProgress;
unsigned fBytesReceived; bool fIsZipped;
public:
plDownloadStream(plOperationProgress* progress) : fProgress(progress), fBytesReceived(0), plZlibStream() {}
virtual ~plDownloadStream() {}
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; 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: case kNetSuccess:
writer->Close(); PatcherLog(kStatus, " Download Complete: %s", name);
patcher->DoneWithFile(true); patcher->IssueRequest();
break; delete[] name;
return;
case kNetErrFileNotFound: case kNetErrFileNotFound:
case kNetErrRemoteShutdown: PatcherLog(kError, " Download Failed: %s not found", name);
writer->Close(); break;
patcher->DoneWithFile(false);
break;
default: default:
writer->Rewind(); char* error = hsWStringToString(NetErrorToString(result));
NetCliFileDownloadRequest( PatcherLog(kError, " Download Failed: %s", error);
filename, delete[] error;
writer, break;
DownloadFileCallback,
param
);
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; plResPatcher* patcher = (plResPatcher*)param;
patcher->DoneWithManifest(result == kNetSuccess, manifest, entryCount); char* name = hsWStringToString(group);
} if (IS_NET_SUCCESS(result))
PatcherLog(kInfo, " Downloaded manifest %s", name);
//// Constructor/Destructor ////////////////////////////////////////////////// else {
PatcherLog(kError, " Failed to download manifest %s", name);
plResPatcher::plResPatcher(const char* ageToPatch, bool showAgeName) patcher->Finish(false);
{ delete[] name;
fAgeToPatch = ageToPatch; return;
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;
} }
fMfsVec.clear();
}
UInt32 plResPatcher::IGetDownloadSize()
{
if (!IGetAgeManifest())
return 0;
#ifdef PLASMA_EXTERNAL_RELEASE for (UInt32 i = 0; i < entryCount; ++i)
bool showAgeName = fAlwaysShowAgeName;
#else
bool showAgeName = true;
#endif
char msg[128];
if (!fAgeToPatch.empty())
{ {
if (showAgeName) const NetCliFileManifestEntry mfs = manifest[i];
sprintf(msg, "Checking age %s...", fAgeToPatch.c_str()); char* fileName = hsWStringToString(mfs.clientName);
else
strcpy(msg, "Checking age...");
}
else
sprintf(msg, "Checking...");
plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOperation((hsScalar)(fMfsVec.size()), msg, plProgressMgr::kNone, false, true); // See if the files are the same
// 1. Check file size before we do time consuming md5 operations
UInt32 downloadSize = 0; // 2. Do wasteful md5. We should consider implementing a CRC instead.
UInt32 downloadFiles = 0; if (plFileUtils::GetFileSize(fileName) == mfs.fileSize)
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
{
plManifestFile* mfsFile = (*i);
if (!mfsFile->IsLocalUpToDate())
{ {
downloadFiles++; plMD5Checksum cliMD5(fileName);
downloadSize += mfsFile->GetDownloadSize(); 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; patcher->IssueRequest();
delete[] name;
PatcherLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize);
return downloadSize;
} }
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) static char* sLastError = nil;
{ plResPatcher* plResPatcher::fInstance = nil;
PatcherLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded);
return false;
}
}
#endif // HS_BUILD_FOR_WIN32
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(); // Better not call this while we're patching
delete fInstance;
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;
// Make sure we have enough free space /////////////////////////////////////////////////////////////////////////////
if (!CheckFreeSpace(size))
return false;
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit)) plResPatcher::plResPatcher()
plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite); : fPatching(false), fProgress(nil) { }
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo))
plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite);
}
return true; plResPatcher::~plResPatcher()
{
if (fProgress)
delete fProgress;
} }
bool plResPatcher::Update() void plResPatcher::IssueRequest()
{ {
UInt32 downloadSize = IGetDownloadSize(); if (!fPatching) return;
// if download size is 0, nothing to download, but we still need to tell the res manager about the files if (fRequests.empty())
// Wheee!
plFileUtils::CreateDir("dat"); Finish();
plFileUtils::CreateDir("sfx"); else {
Request req = fRequests.front();
if (!CheckFreeSpace(downloadSize)) fRequests.pop();
return false;
std::wstring title;
#ifdef PLASMA_EXTERNAL_RELEASE if (req.fType == kManifest)
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 (!IDecompressSound(mfsFile, true)) char* eapSucksString = hsWStringToString(req.fFile.c_str());
{ PatcherLog(kMajorStatus, " Downloading manifest... %s", eapSucksString);
char text[MAX_PATH]; xtl::format(title, L"Checking %s for updates...", req.fFile.c_str());
StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName()); NetCliFileManifestRequest(ManifestDownloaded, this, req.fFile.c_str());
PatcherLog(kInfo, text ); delete[] eapSucksString;
hsAssert(false, text); } else if (req.fType == kFile) {
result = false; 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) char* hack = hsWStringToString(title.c_str());
{ fProgress->SetTitle(hack);
resMgr->AddSinglePage(mfsFile->GetName()); 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; fPatching = false;
wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath()); if (success)
int numTries = 0; 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) delete[] sLastError;
{ sLastError = nil;
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;
} }
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) fRequests.push(Request(srvName, kFile, cliName));
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;
} }
void plResPatcher::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount) void plResPatcher::RequestManifest(const wchar_t* age)
{ {
PatcherLog(kStatus, "New age manifest received. Reading..."); fRequests.push(Request(age, kManifest));
}
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;
}
}
fDoneWithFile = true; void plResPatcher::Start()
fSuccess = success; {
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, ...) void PatcherLog(PatcherLogType type, const char* format, ...)
{ {
UInt32 color = 0; UInt32 color = 0;
@ -512,7 +302,13 @@ void PatcherLog(PatcherLogType type, const char* format, ...)
va_list args; va_list args;
va_start(args, format); 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); 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 #ifndef plResPatcher_h_inc
#define 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; class plOperationProgress;
struct NetCliFileManifestEntry;
class plResPatcher class plResPatcher
{ {
protected: enum { kManifest, kFile };
enum FileType {kFail, kPrp, kOther}; struct Request
std::string fAgeToPatch; {
std::wstring fFile;
typedef std::vector<plManifestFile*> MfsFileVec; std::wstring fFriendlyName;
MfsFileVec fMfsVec; uint8_t fType;
bool fDoneWithFile; Request(const wchar_t* file, uint8_t type, const wchar_t* friendly = nil)
bool fSuccess; : fFile(file), fType(type)
bool fAlwaysShowAgeName; {
if (friendly)
void IInit(); fFriendlyName = std::wstring(friendly);
static void ILog(UInt32 type, const char* format, ...); }
};
FileType IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar);
bool IGetAgeManifest(); static plResPatcher* fInstance;
std::queue<Request> fRequests;
UInt32 IGetDownloadSize(); plOperationProgress* fProgress;
bool fPatching;
bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false);
plResPatcher();
public:
plResPatcher(const char* ageToPatch, bool showAgeName = false);
~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 Finish(bool success = true);
void DoneWithFile(bool success) {fDoneWithFile = true; fSuccess = success;} void IssueRequest();
void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount); void RequestFile(const wchar_t* file, const wchar_t* friendlyName);
void RequestManifest(const wchar_t* age);
void Start();
}; };
enum PatcherLogType enum PatcherLogType
@ -99,5 +95,4 @@ enum PatcherLogType
kError, kError,
}; };
void PatcherLog(PatcherLogType type, const char* format, ...); void PatcherLog(PatcherLogType type, const char* format, ...);
#endif // _plResPatcher_h #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/plAgeLoadedMsg.h"
#include "plMessage/plInputIfaceMgrMsg.h" #include "plMessage/plInputIfaceMgrMsg.h"
#include "plMessage/plNetClientMgrMsg.h" #include "plMessage/plNetClientMgrMsg.h"
#include "plMessage/plResPatcherMsg.h"
#include "plProgressMgr/plProgressMgr.h" #include "plProgressMgr/plProgressMgr.h"
#include "pnDispatch/plDispatch.h" #include "pnDispatch/plDispatch.h"
@ -223,13 +224,6 @@ void plNCAgeJoiner::Start () {
plAgeLoader* al = plAgeLoader::GetInstance(); plAgeLoader* al = plAgeLoader::GetInstance();
al->UpdateAge(age.ageDatasetName); al->UpdateAge(age.ageDatasetName);
nc->ResetServerTimeOffset();
NetCommLinkToAge(
age,
this
);
} }
//============================================================================ //============================================================================
@ -373,6 +367,24 @@ bool plNCAgeJoiner::MsgReceive (plMessage * msg) {
plAvatarMgr * am = plAvatarMgr::GetInstance(); plAvatarMgr * am = plAvatarMgr::GetInstance();
plAgeLoader * al = plAgeLoader::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 // 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/plNetVoiceListMsg.h"
#include "plMessage/plNetCommMsgs.h" #include "plMessage/plNetCommMsgs.h"
#include "plMessage/plNetClientMgrMsg.h" #include "plMessage/plNetClientMgrMsg.h"
#include "plMessage/plResPatcherMsg.h"
#include "plMessage/plVaultNotifyMsg.h" #include "plMessage/plVaultNotifyMsg.h"
#include "plResMgr/plKeyFinder.h" #include "plResMgr/plKeyFinder.h"
#include "plResMgr/plPageInfo.h" #include "plResMgr/plPageInfo.h"
@ -355,6 +356,7 @@ int plNetClientMgr::Init()
// We need plVaultNotifyMsgs for the NetLinkingMgr // We need plVaultNotifyMsgs for the NetLinkingMgr
plgDispatch::Dispatch()->RegisterForType(plVaultNotifyMsg::Index(), GetKey()); plgDispatch::Dispatch()->RegisterForType(plVaultNotifyMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plResPatcherMsg::Index(), GetKey());
IInitNetClientComm(); IInitNetClientComm();

Loading…
Cancel
Save