diff --git a/Sources/Plasma/Apps/CMakeLists.txt b/Sources/Plasma/Apps/CMakeLists.txt index fef53526..f3a1efa7 100644 --- a/Sources/Plasma/Apps/CMakeLists.txt +++ b/Sources/Plasma/Apps/CMakeLists.txt @@ -6,7 +6,6 @@ if(PLASMA_BUILD_LAUNCHER) endif() if(PLASMA_BUILD_TOOLS) - add_subdirectory(plClientPatcher) add_subdirectory(plPythonPack) add_subdirectory(plFileSecure) add_subdirectory(plFileEncrypt) diff --git a/Sources/Plasma/Apps/plClientPatcher/CMakeLists.txt b/Sources/Plasma/Apps/plClientPatcher/CMakeLists.txt deleted file mode 100644 index 10046d95..00000000 --- a/Sources/Plasma/Apps/plClientPatcher/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -add_definitions(-D_LIB) - -include_directories("../../CoreLib") -include_directories("../../NucleusLib/inc") -include_directories("../../NucleusLib") -include_directories("../../PubUtilLib") - -include_directories(${OPENSSL_INCLUDE_DIR}) - -set(plClientPatcher_HEADERS - Intern.h - Pch.h - UruPlayer.h -) - -set(plClientPatcher_SOURCES - UruPlayer.cpp -) - -add_library(plClientPatcher STATIC ${plClientPatcher_HEADERS} ${plClientPatcher_SOURCES}) -target_link_libraries(plClientPatcher CoreLib plAudioCore plStatusLog) - -if(USE_VLD) - target_link_libraries(plClientPatcher ${VLD_LIBRARY}) -endif() - -source_group("Header Files" FILES ${plClientPatcher_HEADERS}) -source_group("Source Files" FILES ${plClientPatcher_SOURCES}) diff --git a/Sources/Plasma/Apps/plClientPatcher/Intern.h b/Sources/Plasma/Apps/plClientPatcher/Intern.h deleted file mode 100644 index 692e602f..00000000 --- a/Sources/Plasma/Apps/plClientPatcher/Intern.h +++ /dev/null @@ -1,58 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h -* -***/ - -#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_INTERN_H -#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h included more than once" -#endif -#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_INTERN_H - - -/***************************************************************************** -* -* SelfPatcher.cpp -* -***/ diff --git a/Sources/Plasma/Apps/plClientPatcher/Pch.h b/Sources/Plasma/Apps/plClientPatcher/Pch.h deleted file mode 100644 index 39eb2515..00000000 --- a/Sources/Plasma/Apps/plClientPatcher/Pch.h +++ /dev/null @@ -1,75 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h -* -***/ - -#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_PCH_H -#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h included more than once" -#endif -#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_PCH_H - -#include "hsWindows.h" -#include -#include -#include "pnUtils/pnUtils.h" -#include "pnNetBase/pnNetBase.h" -#include "pnAsyncCore/pnAsyncCore.h" -#include "plProduct.h" -#include "pnNetCli/pnNetCli.h" -#include "plNetGameLib/plNetGameLib.h" -#include "pnEncryption/plChecksum.h" -#include "plAgeDescription/plAgeManifest.h" -#include "plAudioCore/plAudioFileReader.h" - -#define USES_PROTOCOL_CLI2AUTH -#include "pnNetProtocol/pnNetProtocol.h" - -#include "UruPlayer.h" - -#include "plCompression/plZlibStream.h" -#include "Intern.h" -#include "../plUruLauncher/plLauncherInfo.h" - - diff --git a/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp b/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp deleted file mode 100644 index a5957d6b..00000000 --- a/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp +++ /dev/null @@ -1,1020 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp -* -***/ - -#include "Pch.h" -#include "plStatusLog/plStatusLog.h" -#include -#pragma hdrstop - - -/***************************************************************************** -* -* Private -* -***/ -#ifndef PLASMA_EXTERNAL_RELEASE - static const wchar_t s_manifest[] = L"Internal"; - static const wchar_t s_thinmanifest[] = L"ThinInternal"; -#else - static const wchar_t s_manifest[] = L"External"; - static const wchar_t s_thinmanifest[] = L"ThinExternal"; -#endif - -struct ManifestFile -{ - ManifestFile(const plFileName &clientName, const plFileName &downloadName, const plString &md5val, int flags, plLauncherInfo *info) - { - filename = clientName; - zipName = downloadName; - md5 = md5val; - this->flags = flags; - this->info = info; - md5failed = false; - } - - plFileName filename; - plFileName zipName; - plString md5; - int flags; - bool md5failed; - plLauncherInfo *info; -}; - - -class ProgressStream : public plZlibStream { -public: - virtual uint32_t Write(uint32_t byteCount, const void* buffer); - static plLauncherInfo *info; - static long totalBytes; - static unsigned progress; - - // for updating bytes per second - static uint32_t startTime; -}; - -struct ProcessManifestEntryParam { - struct ManifestResult * mr; - unsigned index; - static long totalSize; - static long progress; - static double startTime; - bool exists; // marked as true if the file exists before MD5 check -}; - -struct ManifestResult { - wchar_t group[MAX_PATH]; - ARRAY(NetCliFileManifestEntry) manifest; - long * indicator; - plLauncherInfo * info; - - hsMutex critsect; - ARRAY(unsigned) indices; -}; - - -static void DownloadCallback ( - ENetError result, - void * param, - const plFileName & filename, - hsStream * writer -); - - -/***************************************************************************** -* -* Private data -* -***/ - -static const unsigned kMaxManifestFileRequests = 5; -static const unsigned kMinThreads = 16; -static const unsigned kMaxThreads = 64; - - -static unsigned s_fileRequests; -static unsigned s_fileListRequests; -static bool s_patchComplete; -static PROCESS_INFORMATION s_pi; -static long s_numFiles; -static plFileName s_workingDir; -static bool s_patchError; -static long s_asyncCoreInitCount; -static long s_numConnectFailures; -static bool s_running; -static std::queue manifestQueue; -//static AsyncThreadTaskList * s_taskList; - -// error strings -static const char s_fileOpenError[] = "Unable to create file. Hard drive may be full."; -static const char s_md5CheckError[] = "Error downloading file from server, exiting..."; - -enum { - kPerfThreadTaskCount, - kNumPerf -}; - -static long s_perf[kNumPerf]; - - -long ProgressStream::totalBytes; -unsigned ProgressStream::progress; -plLauncherInfo * ProgressStream::info; -uint32_t ProgressStream::startTime = 0; -long ProcessManifestEntryParam::progress = 0; -long ProcessManifestEntryParam::totalSize = 0; -double ProcessManifestEntryParam::startTime = 0; - - -/***************************************************************************** -* -* Exported data -* -***/ - -// IMPORTANT: This string may NEVER change. Doing so will break self-patching, -// leaving clients with older patchers "dead in the water", without -// a way to play Uru. -#ifdef PLASMA_EXTERNAL_RELEASE -plFileName kPatcherExeFilename = "UruLauncher.exe"; -#else -plFileName kPatcherExeFilename = "plUruLauncher.exe"; -#endif - - -//============================================================================ -// External client file list -//============================================================================ -#ifdef PLASMA_EXTERNAL_RELEASE -#ifdef HS_DEBUGGING -static wchar_t s_clientExeName[] = L"plClient_dbg.exe"; -#else -static wchar_t s_clientExeName[] = L"UruExplorer.exe"; -#endif // HS_DEBUGGING - -//============================================================================ -// Internal client file list -//============================================================================ -#else -#ifdef HS_DEBUGGING -static wchar_t s_clientExeName[] = L"plClient_dbg.exe"; -#else -static wchar_t s_clientExeName[] = L"plClient.exe"; -#endif // HS_DEBUGGING -#endif // PLASMA_EXTERNAL_RELEASE - - -/***************************************************************************** -* -* Private Functions -* -***/ - -//============================================================================ -static void NetErrorHandler (ENetProtocol protocol, ENetError error) { - - const wchar_t * srv; - switch (protocol) { - case kNetProtocolNil: srv = L"Notify"; break; - case kNetProtocolCli2File: srv = L"File"; break; - case kNetProtocolCli2GateKeeper: srv = L"GateKeeper"; break; - DEFAULT_FATAL(protocol); - } - - switch (error) { - case kNetErrConnectFailed: - case kNetErrTimeout: - ++s_numConnectFailures; - break; - - case kNetErrDisconnected: - s_patchError = true; - break; - - case kNetErrServerBusy: - MessageBox(0, "Due to the high demand, the server is currently busy. Please try again later, or for alternative download options visit: http://www.mystonline.com/play/", "UruLauncher", MB_OK); - s_running = false; - break; - } - - plString msg = plString::Format("NetErr: %S: %S", srv, NetErrorToString(error)); - plStatusLog::AddLineS("patcher.log", msg.c_str()); - - // Notify GameTap something bad happened. - if (!s_patchError) { - MessageBox( - nil, - "Unable to connect to server.", - "Error", - MB_ICONERROR - ); - s_patchError = true; - } - - /*AsyncAppCallback( - kPlayerNotifyFailed, - kCmdResultFailed, - (void *)NetErrorToString(error) - );*/ -} - -/* -//============================================================================ -static void WaitUruExitProc (void * param) { -#ifdef USE_VLD - VLDEnable(); -#endif - - plLauncherInfo *info = (plLauncherInfo *) param; - WaitForSingleObject(s_pi.hProcess, INFINITE); - DWORD exitcode; - GetExitCodeProcess(s_pi.hProcess, &exitcode); - CloseHandle( s_pi.hThread ); - CloseHandle( s_pi.hProcess ); - - if(exitcode == kExitCodeTerminated) { - info->stopCallback(kStatusOk, nil); // notify of succesful stop - } - else { - info->exitCallback(kStatusOk, nil); - } -} -*/ - -//============================================================================ -static bool MD5Check (const plFileName& filename, const char *md5) { - plMD5Checksum existingMD5(filename); - plMD5Checksum latestMD5; - - latestMD5.SetFromHexString(md5); - return (existingMD5 == latestMD5); -} - -//============================================================================ -static void DecompressOgg (ManifestFile *mf) { - unsigned flags = mf->flags; - for(;;) - { - // decompress ogg if necessary - if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) ) - { - plFileName path = plFileName::Join(s_workingDir, mf->filename); - - plAudioFileReader* reader = plAudioFileReader::CreateReader(path, plAudioCore::kAll, plAudioFileReader::kStreamNative); - if (!reader) - { - break; - } - - uint32_t size = reader->GetDataSize(); - delete reader; - - ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes; - if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL)) - { - neededBytes.HighPart = 0; - neededBytes.LowPart = size; - - if (neededBytes.QuadPart > freeBytesAvailable.QuadPart) - { - //PatcherLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded); - break; - } - } - - if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit)) - plAudioFileReader::CacheFile(path, true, true); - if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) - plAudioFileReader::CacheFile(path, false, true); - } - break; - } -} - -//============================================================================ -void Shutdown(plLauncherInfo *info) { - info->SetText("Shutting Down..."); - s_patchError = true; - s_running = false; -} - -//============================================================================ -static void RequestNextManifestFile () { - bool success = true; - - if (!manifestQueue.size()) - return; - - ManifestFile* nextfile = manifestQueue.front(); - manifestQueue.pop(); - - plFileName path = plFileName::Join(s_workingDir, nextfile->filename); - plFileSystem::CreateDir(path.StripFileName(), true); - - ProgressStream *writer = new ProgressStream(); // optimization: dont delete and recreate. Doesn't seem to be working currently, ZLibStream is breaking - if(!writer->Open(path, "wb")) - { - writer->Close(); - delete writer; - success = false; - } - - if(success) - { -#ifndef PLASMA_EXTERNAL_RELEASE - char text[256]; - StrPrintf(text, arrsize(text), "Updating URU... %s", nextfile->filename.AsString().c_str()); - nextfile->info->SetText(text); -#endif - NetCliFileDownloadRequest(nextfile->zipName, writer, DownloadCallback, nextfile, nextfile->info->buildId); - } -} - -//============================================================================ -static void DownloadCallback ( - ENetError result, - void * param, - const plFileName & filename, - hsStream * writer -) { - s_numConnectFailures = 0; - - ManifestFile *mf = (ManifestFile *)param; - if (IS_NET_ERROR(result) && s_running && !s_patchError) { - if (result == kNetErrFileNotFound) { - plString str = plString::Format("File not found on server: %s", filename.AsString().c_str()); - MessageBox(nil, str.c_str(), "URU Launcher", MB_ICONERROR); - s_patchError = true; - } - else if (result == kNetErrRemoteShutdown) { - s_patchError = true; - } - else { - // failed, re-queue the file to be downloaded - // (after rewinding the stream) - writer->Rewind(); - plLauncherInfo *info = mf->info; - NetCliFileDownloadRequest(filename, writer, DownloadCallback, mf, info->buildId); - return; - } - } - - writer->Close(); - delete writer; // delete our stream - - plFileName path = plFileName::Join(s_workingDir, mf->filename); - if (s_running) - { - if (!MD5Check(path, mf->md5.c_str())) { - if (mf->md5failed) - { -#ifdef PLASMA_EXTERNAL_RELEASE - MessageBox(nil, s_md5CheckError, "URU Launcher", MB_ICONERROR); -#else - char str[256]; - StrPrintf(str, arrsize(str), "%s %s ", path.AsString().c_str(), s_md5CheckError); - MessageBox(nil, str, "URU Launcher", MB_ICONERROR); -#endif // PLASMA_EXTERNAL_RELEASE - Shutdown(mf->info); - } - writer = new ProgressStream(); - if (!writer->Open(path, "wb")) { -#ifdef PLASMA_EXTERNAL_RELEASE - MessageBox(nil, s_fileOpenError, "URU Launcher", MB_ICONERROR); -#else - char str[256]; - StrPrintf(str, arrsize(str), "%s %s", s_fileOpenError, path.AsString().c_str()); - MessageBox(nil, str, "URU Launcher", MB_ICONERROR); -#endif // PLASMA_EXTERNAL_RELEASE - Shutdown(mf->info); - } - mf->md5failed = true; - plLauncherInfo *info = mf->info; - NetCliFileDownloadRequest(filename, writer, DownloadCallback, mf, info->buildId); - return; - } - } - - AtomicAdd(&s_numFiles, -1); - - if (s_running) - { - if (!mf->filename.GetFileExt().CompareI("ogg")) - { - DecompressOgg(mf); - } - } - - delete mf; // delete manifest file entry - - // if we are not still running don't request any more file downloads - if(s_running) - { - if(!s_numFiles) { - s_patchComplete = true; - } - else - { - RequestNextManifestFile(); - } - } -} - -//============================================================================ -static void ProcessManifestEntry (void * param, ENetError error) { - ProcessManifestEntryParam * p = (ProcessManifestEntryParam*)param; - -#ifndef PLASMA_EXTERNAL_RELEASE - char text[256]; - StrPrintf(text, arrsize(text), "Checking for updates... %S", p->mr->manifest[p->index].clientName); - p->mr->info->SetText(text); -#endif - plFileName path = plFileName::Join(s_workingDir, plString::FromWchar(p->mr->manifest[p->index].clientName)); - uint32_t start = (uint32_t)(TimeGetTime() / kTimeIntervalsPerMs); - if (!MD5Check(path, plString::FromWchar(p->mr->manifest[p->index].md5, 32).c_str())) { - p->mr->critsect.Lock(); - p->mr->indices.Add(p->index); - p->mr->critsect.Unlock(); - AtomicAdd(&ProgressStream::totalBytes, p->mr->manifest[p->index].zipSize); - } - - // if we have a file that was cached the MD5 check will be really fast throwing off our approx time remaining. - uint32_t t = (uint32_t)(TimeGetTime() / kTimeIntervalsPerMs - start); - if(t < 25) - { - // cached file - AtomicAdd(&ProcessManifestEntryParam::totalSize, -p->mr->manifest[p->index].zipSize); - p->exists = false; - } - - // p->mr->info->SetBytesRemaining(ProcessManifestEntryParam::totalSize); // for testing purposes only - if(p->exists) - { - AtomicAdd(&ProcessManifestEntryParam::progress, p->mr->manifest[p->index].zipSize); - - PatchInfo patchInfo; - patchInfo.stage = 0; - patchInfo.progressStage = 0; - patchInfo.progress = (unsigned)((float)(ProcessManifestEntryParam::progress) / (float)ProcessManifestEntryParam::totalSize * 1000.0f); - p->mr->info->progressCallback(kStatusPending, &patchInfo); - if(ProcessManifestEntryParam::progress > ProcessManifestEntryParam::totalSize) - { - p->mr->info->SetTimeRemaining(0); - } - else - { - if(TimeGetTime() / kTimeIntervalsPerMs != ProcessManifestEntryParam::startTime) - { - double timeElapsed = (TimeGetTime() / kTimeIntervalsPerMs - ProcessManifestEntryParam::startTime) / 1000; - double bytesPerSec = (float)(ProcessManifestEntryParam::progress ) / timeElapsed; - p->mr->info->SetTimeRemaining(bytesPerSec ? (int)((ProcessManifestEntryParam::totalSize - ProcessManifestEntryParam::progress) / bytesPerSec) : 0); - } - } - } -} - -//============================================================================ -static void ProcessManifest (void * param) { -#ifdef USE_VLD - VLDEnable(); -#endif - - AtomicAdd(&s_perf[kPerfThreadTaskCount], 1); - - ManifestResult * mr = (ManifestResult *)param; - - PatchInfo patchInfo; - patchInfo.stage = 0; - patchInfo.progressStage = 0; - patchInfo.progress = 0; - mr->info->progressCallback(kStatusPending, &patchInfo); - - char text[256]; - StrPrintf(text, arrsize(text), "Checking for updates..."); - mr->info->SetText(text); - - unsigned entryCount = mr->manifest.Count(); - NetCliFileManifestEntry * manifest = mr->manifest.Ptr(); - - FILE *fd = nil; - ARRAY(ProcessManifestEntryParam) params; - params.Reserve(mr->manifest.Count()); - for (unsigned i = 0; i < entryCount; ++i) { - ProcessManifestEntryParam * p = params.New(); - p->index = i; - p->mr = mr; - p->exists = false; - plFileName path = plFileName::Join(s_workingDir, plString::FromWchar(mr->manifest[i].clientName)); - fd = plFileSystem::Open(path, "r"); - if (fd) - { - p->exists = true; - p->totalSize += p->mr->manifest[i].zipSize; - fclose(fd); - } - } - - ProcessManifestEntryParam::startTime = (double)(TimeGetTime() / kTimeIntervalsPerMs); - - for (unsigned i = 0; i < entryCount && s_running; ++i){ - ProcessManifestEntry(¶ms[i], kNetSuccess); - } - - if(s_running) - { - PatchInfo patchInfo; - patchInfo.stage = 0; - patchInfo.progressStage = 0; - patchInfo.progress = 1000; - mr->info->progressCallback(kStatusPending, &patchInfo); - - AtomicAdd(&s_numFiles, mr->indices.Count()); - if(!s_numFiles || !s_running) { - s_patchComplete = true; - } - else { - mr->info->SetText("Updating URU..."); - - PatchInfo patchInfo; - patchInfo.stage = 0; - patchInfo.progressStage = 0; - patchInfo.progress = 0; - mr->info->progressCallback(kStatusPending, &patchInfo); - - for (unsigned i = 0; i < mr->indices.Count(); ++i) - { - if(s_running) - { - unsigned index = mr->indices[i]; - plFileName path = plFileName::Join(s_workingDir, plString::FromWchar(manifest[index].clientName)); - plFileSystem::CreateDir(path.StripFileName(), true); - - ManifestFile* mf = new ManifestFile( - plString::FromWchar(manifest[index].clientName), - plString::FromWchar(manifest[index].downloadName), - plString::FromWchar(manifest[index].md5), - manifest[index].flags, - mr->info - ); - - if (i < kMaxManifestFileRequests) { - ProgressStream * stream = new ProgressStream; - if (!stream->Open(path, "wb")) { -#ifdef PLASMA_EXTERNAL_RELEASE - MessageBox(nil, s_fileOpenError, "URU Launcher", MB_ICONERROR); -#else - char str[256]; - StrPrintf(str, arrsize(str), "%s %s", path.AsString().c_str(), s_fileOpenError); - MessageBox(nil, str, "URU Launcher", MB_ICONERROR); -#endif - Shutdown(mr->info); - } -#ifndef PLASMA_EXTERNAL_RELEASE - char text[256]; - StrPrintf(text, arrsize(text), "Updating URU... %S", manifest[i].clientName); - mr->info->SetText(text); -#endif - // fire off our initial requests. The remaining will be added as files are downloaded - NetCliFileDownloadRequest(mf->zipName, stream, DownloadCallback, mf, mr->info->buildId); - } - else { - // queue up this file download - manifestQueue.push(mf); - } - } - } - } - } - delete mr; - AtomicAdd(&s_perf[kPerfThreadTaskCount], -1); -} - -//============================================================================ -static void ManifestCallback ( - ENetError result, - void * param, - const wchar_t group[], - const NetCliFileManifestEntry manifest[], - unsigned entryCount -){ - s_numConnectFailures = 0; - - plLauncherInfo * info = (plLauncherInfo *) param; - - if(!s_running || IS_NET_ERROR(result)) { - if (s_running && !s_patchError) { - switch (result) { - case kNetErrTimeout: - NetCliFileManifestRequest(ManifestCallback, param, group); - break; - - default: { - char str[256]; - StrPrintf(str, arrsize(str), "Failed to download manifest from server"); - MessageBox(nil, str, "URU Launcher", MB_ICONERROR); - s_patchError = true; - } - break; - } - } - return; - } - - ManifestResult * mr = new ManifestResult(); - StrCopy(mr->group, group, arrsize(mr->group)); - mr->manifest.Set(manifest, entryCount); - mr->info = info; - - // sort our requests by size(this must be done for the next step to work) - QSORT( - NetCliFileManifestEntry, - mr->manifest.Ptr(), - mr->manifest.Count(), - elem1.fileSize > elem2.fileSize - ); - - // remove duplicate entries. This can cause some bad problems if not done. It will cause MD5 checks to fail, since it can be writing a file while MD5 checking it. - ARRAY(NetCliFileManifestEntry) noDuplicates; - noDuplicates.Reserve(mr->manifest.Count()); - for(unsigned i = 0; i < entryCount - 1; ++i) - { - if (mr->manifest[i].clientName != mr->manifest[i+1].clientName) - { - noDuplicates.Add(mr->manifest[i]); - } - } - noDuplicates.Add(mr->manifest[entryCount - 1]); - - // adjust our array and set data - mr->manifest.ShrinkBy(mr->manifest.Count() - noDuplicates.Count()); - mr->manifest.Set(noDuplicates.Ptr(), noDuplicates.Count()); - - (void)_beginthread(ProcessManifest, 0, mr); -} - -//============================================================================ -static void ThinManifestCallback ( - ENetError result, - void * param, - const wchar_t group[], - const NetCliFileManifestEntry manifest[], - unsigned entryCount -){ - s_numConnectFailures = 0; - - plLauncherInfo * info = (plLauncherInfo *) param; - char text[256]; - StrPrintf(text, arrsize(text), "Checking for updates..."); - info->SetText(text); - - if(!s_running || IS_NET_ERROR(result)) { - if (s_running && !s_patchError) { - switch (result) { - case kNetErrTimeout: - NetCliFileManifestRequest(ManifestCallback, param, group); - break; - - default: { - char str[256]; - StrPrintf(str, arrsize(str), "Failed to download manifest from server"); - MessageBox(nil, str, "URU Launcher", MB_ICONERROR); - s_patchError = true; - } - break; - } - } - return; - } - s_patchComplete = true; - for (unsigned i = 0; i < entryCount; ++i) { - if (!s_running) - return; - - plFileName path = plFileName::Join(s_workingDir, plString::FromWchar(manifest[i].clientName)); - if (!MD5Check(path, plString::FromWchar(manifest[i].md5, 32).c_str())) { - s_patchComplete = false; - NetCliFileManifestRequest(ManifestCallback, info, s_manifest, info->buildId); - break; - } - PatchInfo patchInfo; - patchInfo.stage = 0; - patchInfo.progressStage = 0; - patchInfo.progress = (unsigned)((float)i / (float)entryCount * 1000.0f); - info->progressCallback(kStatusPending, &patchInfo); -#ifndef PLASMA_EXTERNAL_RELEASE - char text[256]; - StrPrintf(text, arrsize(text), "Checking for updates... %S", manifest[i].clientName); - info->SetText(text); -#endif - } -} - - -/***************************************************************************** -* -* ProgressStream Functions -* -***/ - -//============================================================================ -uint32_t ProgressStream::Write(uint32_t byteCount, const void* buffer) { - if(!s_running || s_patchError) - return 0; - if(!startTime) { - startTime = TimeGetSecondsSince2001Utc(); - } - progress += byteCount; - float p = (float)progress / (float)totalBytes * 1000; // progress - - PatchInfo patchInfo; - patchInfo.stage = 1; - patchInfo.progress = (unsigned) p; - patchInfo.progressStage = 50; - info->progressCallback(kStatusPending, (void *)&patchInfo); - - // there seems to, sometimes, be a slight discrepency in progress and totalBytes. - if(progress > totalBytes) - { - info->SetBytesRemaining(0); - info->SetTimeRemaining(0); - } - else - { - info->SetBytesRemaining(totalBytes - progress); - if(TimeGetSecondsSince2001Utc() != startTime) - { - uint32_t bytesPerSec = (progress ) / (TimeGetSecondsSince2001Utc() - startTime); - info->SetTimeRemaining(bytesPerSec ? (totalBytes - progress) / bytesPerSec : 0); - } - } - return plZlibStream::Write(byteCount, buffer); -} - - -//============================================================================ -static void FileSrvIpAddressCallback ( - ENetError result, - void * param, - const wchar_t addr[] -) { - NetCliGateKeeperDisconnect(); - - if (IS_NET_ERROR(result)) { - plString msg = plString::Format("FileSrvIpAddressRequest failed: %S", NetErrorToString(result)); - plStatusLog::AddLineS("patcher.log", msg.c_str()); - - s_patchError = true; - return; - } - - plLauncherInfo *info = (plLauncherInfo *) param; - - // Start connecting to the server - const char* caddr = hsWStringToString(addr); - NetCliFileStartConnect(&caddr, 1, true); - delete[] caddr; - - NetCliFileManifestRequest(ThinManifestCallback, info, s_thinmanifest, info->buildId); - - ProgressStream::info = info; - PatchInfo patchInfo; - patchInfo.stage = 0; - patchInfo.progressStage = 0; - patchInfo.progress = 0; - info->progressCallback(kStatusPending, &patchInfo); -} - - -/***************************************************************************** -* -* Public Functions -* -***/ - -//============================================================================ -void InitAsyncCore () { - if(AtomicAdd(&s_asyncCoreInitCount, 1) > 0) - return; - AsyncCoreInitialize(); - - plStatusLog::AddLineS("patcher.log", plProduct::ProductString().c_str()); -} - -//============================================================================ -void ShutdownAsyncCore () { - if(AtomicAdd(&s_asyncCoreInitCount, -1) > 1) - return; - ASSERT(s_asyncCoreInitCount >= 0); - - while (s_perf[kPerfThreadTaskCount]) - AsyncSleep(10); - - AsyncCoreDestroy(30 * 1000); -} - -//============================================================================ -// param = URU_PreparationRequest -void UruPrepProc (void * param) { -#ifdef USE_VLD - VLDEnable(); -#endif - - s_running = true; - - plLauncherInfo *info = (plLauncherInfo *) param; - s_workingDir = plString::FromWchar(info->path); - - InitAsyncCore(); - NetClientInitialize(); - NetClientSetErrorHandler(NetErrorHandler); - NetClientSetTransTimeoutMs(5 * 60 * 1000); // five minute timeout - - s_patchComplete = false; - s_patchError = false; - - const char** addrs; - unsigned count; - - count = GetGateKeeperSrvHostnames(&addrs); - - // Start connecting to the server - NetCliGateKeeperStartConnect(addrs, count); - - // request a file server ip address - NetCliGateKeeperFileSrvIpAddressRequest(FileSrvIpAddressCallback, param, true); - - do { - NetClientUpdate(); - AsyncSleep(10); - } while ((!s_patchComplete && !s_patchError && s_running) || s_perf[kPerfThreadTaskCount]); - - while (manifestQueue.size()) - { - ManifestFile* mf = manifestQueue.front(); - manifestQueue.pop(); - delete mf; - } - // If s_patchError, we don't wait around for s_numFiles - // to drop to zero because it never does for reasons - // I'm not willing to debug at the moment, so we just - // bail on them. This causes a race condition with - // the outstanding file object cancel/deletion and - // subsequently a memory leak. -eap - - if (s_patchError) { - info->SetText("Exiting..."); - } - else { - PatchInfo patchInfo; - patchInfo.stage = 2; - patchInfo.progressStage = 100; - patchInfo.progress = 1000; - info->progressCallback(kStatusOk, &patchInfo); - } - - ProgressStream::info = nil; - - NetCliFileDisconnect (); - NetClientUpdate(); - - // Shutdown the client/server networking subsystem - NetClientDestroy(); - - info->prepCallback(s_patchError ? kStatusError : kStatusOk, nil); -} - -//============================================================================ -void PlayerStopProc (void * param) { -#ifdef USE_VLD - VLDEnable(); -#endif - - s_running = false; - plLauncherInfo *info = (plLauncherInfo *) param; - //TerminateProcess(s_pi.hProcess, kExitCodeTerminated); - info->stopCallback(kStatusOk, nil); -} - -//============================================================================ -void PlayerTerminateProc (void * param) { -#ifdef USE_VLD - VLDEnable(); -#endif - - s_running = false; - plLauncherInfo *info = (plLauncherInfo *) param; - ShutdownAsyncCore(); - info->terminateCallback(kStatusOk, nil); -} - -//============================================================================ -void UruStartProc (void * param) { -#ifdef USE_VLD - VLDEnable(); -#endif - - if(!s_running) - return; - - plLauncherInfo *info = (plLauncherInfo *) param; - - wchar_t workDir[MAX_PATH]; - StrPrintf(workDir, arrsize(workDir), L"%s", info->path); - //fprintf(stderr, "URUPlayer StartProc gamePath is:%ws\n", workDir); - - wchar_t cmdLine[MAX_PATH]; - StrPrintf(cmdLine, arrsize(cmdLine), L"%s\\%s %s", workDir, s_clientExeName, info->cmdLine); - - // Create the named event so the client won't restart us (Windows will clean it up when we exit) - HANDLE hPatcherEvent = CreateEventW(nil, TRUE, FALSE, L"UruPatcherEvent"); - if (hPatcherEvent == NULL) { - info->startCallback(kStatusError, nil); - return; - } - - fprintf(stderr, "URUPlayer StartProc, running game process at dir:%ws, cmd:%ws for application:%ws\n", workDir, cmdLine, s_clientExeName); - - STARTUPINFOW si; - memset(&si, 0, sizeof(si)); - memset(&s_pi, 0, sizeof(s_pi)); - si.cb = sizeof(si); - - info->SetText("Launching URU..."); - BOOL success = CreateProcessW( - NULL, - cmdLine, - NULL, // plProcessAttributes - NULL, // plThreadAttributes - FALSE, // bInheritHandles - 0, // dwCreationFlags - NULL, // lpEnvironment - workDir, // lpCurrentDirectory - &si, - &s_pi - ); - - if (success) - { - fprintf(stderr, "%d", GetLastError()); - info->returnCode = s_pi.dwProcessId; - CloseHandle( s_pi.hThread ); - CloseHandle( s_pi.hProcess ); - // This may smooth the visual transition from GameTap to Uru, or it may make it worse. - WaitForInputIdle(s_pi.hProcess, INFINITE); - //_beginthread(WaitUruExitProc, 0, param); - - // wait for the event to signal (give the client 10 seconds to start up, then die) - DWORD wait = WaitForSingleObject(hPatcherEvent, 10000); - if (wait == WAIT_TIMEOUT) - info->startCallback(kStatusOk, nil); - else - info->startCallback(kStatusOk, nil); - } - else - { - info->startCallback(kStatusError, nil); - } -} diff --git a/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h b/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h deleted file mode 100644 index b769ed53..00000000 --- a/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h +++ /dev/null @@ -1,67 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h -* -***/ - -#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_URUPLAYER_H -#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h included more than once" -#endif -#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_URUPLAYER_H - - -/***************************************************************************** -* -* UruPlayer.cpp -* -***/ - -void InitAsyncCore (); -void ShutdownAsyncCore () ; -void UruPrepProc (void * param); -void UruStartProc (void * param); -void PlayerTerminateProc (void * param); -void PlayerStopProc (void * param); - -extern plFileName kPatcherExeFilename; diff --git a/Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h b/Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h deleted file mode 100644 index e58275a7..00000000 --- a/Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h +++ /dev/null @@ -1,65 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h -* -***/ - -#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H -#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h included more than once" -#endif -#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H - -enum EStatus { - kStatusOk, -}; - -typedef void (*launcherCallback)(int status, void *param); -struct plLauncherCallback { - launcherCallback prepCallback; - launcherCallback initCallback; - launcherCallback startCallback; - launcherCallback stopCallback; - launcherCallback terminateCallback; - -}; \ No newline at end of file diff --git a/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt b/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt index 0ee292ed..40609b31 100644 --- a/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt +++ b/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt @@ -9,14 +9,12 @@ include_directories(${OPENSSL_INCLUDE_DIR}) include_directories(${CURL_INCLUDE_DIR}) set(plUruLauncher_HEADERS - Intern.h - Pch.h - plLauncherInfo.h + plClientLauncher.h ) set(plUruLauncher_SOURCES - Main.cpp - SelfPatcher.cpp + plClientLauncher.cpp + winmain.cpp ) set(plUruLauncher_RESOURCES @@ -34,12 +32,10 @@ if(PLASMA_EXTERNAL_RELEASE) endif(PLASMA_EXTERNAL_RELEASE) target_link_libraries(plUruLauncher CoreLib) target_link_libraries(plUruLauncher pfConsoleCore) +target_link_libraries(plUruLauncher pfPatcher) target_link_libraries(plUruLauncher plAudioCore) -target_link_libraries(plUruLauncher plClientPatcher) target_link_libraries(plUruLauncher plCompression) target_link_libraries(plUruLauncher plFile) -target_link_libraries(plUruLauncher plNetClient) -target_link_libraries(plUruLauncher plNetClientComm) target_link_libraries(plUruLauncher plNetGameLib) target_link_libraries(plUruLauncher plNetMessage) target_link_libraries(plUruLauncher plNetTransport) diff --git a/Sources/Plasma/Apps/plUruLauncher/Intern.h b/Sources/Plasma/Apps/plUruLauncher/Intern.h deleted file mode 100644 index bdd81d6c..00000000 --- a/Sources/Plasma/Apps/plUruLauncher/Intern.h +++ /dev/null @@ -1,61 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h -* -***/ - -#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_INTERN_H -#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h included more than once" -#endif -#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_INTERN_H - - - -/***************************************************************************** -* -* SelfPatcher.cpp -* -***/ -bool SelfPatch (bool noSelfPatch, bool * abort, ENetError * result, plLauncherInfo *info); -void SetReturnCode (DWORD retCode); \ No newline at end of file diff --git a/Sources/Plasma/Apps/plUruLauncher/Main.cpp b/Sources/Plasma/Apps/plUruLauncher/Main.cpp deleted file mode 100644 index 5578633e..00000000 --- a/Sources/Plasma/Apps/plUruLauncher/Main.cpp +++ /dev/null @@ -1,884 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Main.cpp -* -***/ - -#include "Pch.h" -#include "hsThread.h" -#include -#pragma hdrstop - - -#include "resource.h" -#include -#define WIN32_LEAN_AND_MEAN -#define WHITESPACE L" \"\t\r\n\x1A" -#define UPDATE_STATUSMSG_SECONDS 30 // Must be an int - - -/***************************************************************************** -* -* Private -* -***/ - -enum ELogSev { - kLogInfo, - kLogErr, - kNumLogSev -}; - -enum { - kEventTimer = 1, -}; - -enum EEventType { - kEventSetProgress, - kEventSetText, - kEventSetStatusText, - kEventSetTimeRemaining, - kEventSetBytesRemaining, -}; - -// base window event -struct WndEvent { - LINK(WndEvent) link; - EEventType type; -}; - -struct SetProgressEvent : WndEvent { - int progress; -}; - -struct SetTextEvent : WndEvent { - char text[MAX_PATH]; -}; - -struct SetStatusTextEvent : WndEvent { - char text[MAX_PATH]; -}; - -struct SetTimeRemainingEvent : WndEvent { - unsigned seconds; -}; - -struct SetBytesRemainingEvent : WndEvent { - unsigned bytes; -}; - - -/***************************************************************************** -* -* Private data -* -***/ - -static bool s_shutdown; -static bool s_prepared; -static int s_retCode = 1; -static long s_terminationIssued; -static bool s_terminated; -static plLauncherInfo s_launcherInfo; -static HANDLE s_thread; -static HANDLE s_event; -static HINSTANCE s_hInstance; -static HWND s_dialog; -static hsSemaphore s_dialogCreateEvent(0); -static hsMutex s_critsect; -static LISTDECL(WndEvent, link) s_eventQ; -static hsSemaphore s_shutdownEvent(0); -static plFileName s_workingDir; -static hsSemaphore s_statusEvent(0); -static char s_curlError[CURL_ERROR_SIZE]; - - -/***************************************************************************** -* -* Local functions -* -***/ - -//============================================================================ -static void Abort () { - s_retCode = 0; - s_shutdown = true; -} - -//============================================================================ -static void PostEvent (WndEvent *event) { - s_critsect.Lock(); - s_eventQ.Link(event); - s_critsect.Unlock(); -} - -//============================================================================ -static void LogV (ELogSev sev, const wchar_t fmt[], va_list args) { - static struct { FILE * file; const wchar_t * pre; } s_log[] = { - { stdout, L"Inf" }, - { stderr, L"Err" }, - }; - static_assert(arrsize(s_log) == kNumLogSev, "Log severity array and enum have different sizes"); - - fwprintf (s_log[sev].file, L"%s: ", s_log[sev].pre); - vfwprintf(s_log[sev].file, fmt, args); - fwprintf (s_log[sev].file, L"\n"); - - if (sev >= kLogErr) - Abort(); -} - -//============================================================================ -static void Log (ELogSev sev, const wchar_t fmt[], ...) { - va_list args; - va_start(args, fmt); - LogV(sev, fmt, args); - va_end(args); -} - -//============================================================================ -// NOTE: Must use LocalFree() on the return value of this function when finished with the string -static wchar_t *TranslateErrorCode(DWORD errorCode) { - LPVOID lpMsgBuf; - - FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (wchar_t *) &lpMsgBuf, - 0, - NULL - ); - return (wchar_t *)lpMsgBuf; -} - -//============================================================================ -static BOOL WINAPI CtrlHandler (DWORD) { - static unsigned s_ctrlCount; - if (++s_ctrlCount == 3) - _exit(1); // exit process immediately upon 3rd Ctrl-C. - Abort(); - return TRUE; -} - -//============================================================================ -static void PrepareGame () { - SetText("Connecting to server..."); - (void)_beginthread(UruPrepProc, 0, (void *) &s_launcherInfo); -} - -//============================================================================ -static void InitGame () { - s_launcherInfo.initCallback(kStatusOk, nil); -} - -//============================================================================ -static void StartGame () { - (void)_beginthread(UruStartProc, 0, (void *) &s_launcherInfo); -} - -//============================================================================ -static void StopGame () { - (void)_beginthread(PlayerStopProc, 0, (void *) &s_launcherInfo); -} - -//============================================================================ -static void TerminateGame () { - if (!AtomicSet(&s_terminationIssued, 1)) - _beginthread(PlayerTerminateProc, 0, (void *) &s_launcherInfo); -} - -//============================================================================ -static void Recv_SetProgress (HWND hwnd, const SetProgressEvent &event) { - SendMessage(GetDlgItem(s_dialog, IDC_PROGRESS), PBM_SETPOS, event.progress, NULL); -} - -//============================================================================ -static void Recv_SetText (HWND hwnd, const SetTextEvent &event) { - bool b = SendMessage(GetDlgItem(s_dialog, IDC_TEXT), WM_SETTEXT, 0, (LPARAM) event.text); -} - -//============================================================================ -static void Recv_SetStatusText (HWND hwnd, const SetStatusTextEvent &event) { - bool b = SendMessage(GetDlgItem(s_dialog, IDC_STATUS_TEXT), WM_SETTEXT, 0, (LPARAM) event.text); -} - -//============================================================================ -static void Recv_SetTimeRemaining (HWND hwnd, const SetTimeRemainingEvent &event) { - unsigned days; - unsigned hours; - unsigned minutes; - unsigned seconds; - - if(event.seconds == 0xffffffff) - { - SendMessage(GetDlgItem(s_dialog, IDC_TIMEREMAINING), WM_SETTEXT, 0, (LPARAM) "estimating..."); - return; - } - - seconds = event.seconds; - - days = seconds / (60 * 60 * 24); - seconds -= (days * 60 * 60 * 24); - hours = seconds / (60 * 60); - seconds -= hours * 60 * 60; - minutes = seconds / 60; - seconds -= minutes * 60; - seconds = seconds; - - char text[64] = {0}; - if(days) - { - if(days > 1) - StrPrintf(text, arrsize(text), "%d days ", days); - else - StrPrintf(text, arrsize(text), "%d day ", days); - } - if(hours) - { - if(hours > 1) - StrPrintf(text, arrsize(text), "%s%d hours ", text, hours); - else - StrPrintf(text, arrsize(text), "%s%d hour ", text, hours); - } - if(minutes) - StrPrintf(text, arrsize(text), "%s%d min ", text, minutes); - if( seconds || !text[0]) - StrPrintf(text, arrsize(text), "%s%d sec", text, seconds); - bool b = SendMessage(GetDlgItem(s_dialog, IDC_TIMEREMAINING), WM_SETTEXT, 0, (LPARAM) text); -} - -//============================================================================ -static void Recv_SetBytesRemaining (HWND hwnd, const SetBytesRemainingEvent &event) { - char text[32]; - unsigned MB; - unsigned decimal; - unsigned bytes = event.bytes; - - unsigned GB = bytes / 1000000000; - if(GB) - { - bytes -= GB * 1000000000; - decimal = bytes / 100000000; // to two decimal places - StrPrintf(text, arrsize(text), "%d.%d GB", GB, decimal); - } - else - { - MB = bytes / 1000000; - bytes -= MB * 1000000; - decimal = bytes / 100000; // to one decimal place - StrPrintf(text, arrsize(text), "%d.%d MB", MB, decimal); - } - bool b = SendMessage(GetDlgItem(s_dialog, IDC_BYTESREMAINING), WM_SETTEXT, 0, (LPARAM) text); -} - -//============================================================================ -static void DispatchEvents (HWND hwnd) { - LISTDECL(WndEvent, link) eventQ; - - s_critsect.Lock(); - { - eventQ.Link(&s_eventQ); - } - s_critsect.Unlock(); - -#define DISPATCH(a) case kEvent##a: Recv_##a(hwnd, *(const a##Event *) event); break - while (WndEvent *event = eventQ.Head()) { - switch (event->type) { - DISPATCH(SetProgress); - DISPATCH(SetText); - DISPATCH(SetStatusText); - DISPATCH(SetTimeRemaining); - DISPATCH(SetBytesRemaining); - DEFAULT_FATAL(event->type); - } - delete event; // unlinks from list - } -#undef DISPATCH -} - -//============================================================================ -static void OnTimer(HWND hwnd, unsigned int timerId) { - if(s_shutdown) return; - switch (timerId) { - case kEventTimer: - DispatchEvents(hwnd); - break; - - DEFAULT_FATAL(timerId); - } -} - -//=========================================================================== -static void MessagePump (HWND hwnd) { - for (;;) { - // wait for a message or the shutdown event - const DWORD result = MsgWaitForMultipleObjects( - 1, - &s_event, - false, - INFINITE, - QS_ALLEVENTS - ); - if (result == WAIT_OBJECT_0) - return; - - // process windows messages - MSG msg; - - while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { - if (!IsDialogMessage(s_dialog, &msg)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - if (msg.message == WM_QUIT) { - return; - } - } - } -} - -//============================================================================ -BOOL CALLBACK SplashDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) -{ - switch( uMsg ) - { - case WM_INITDIALOG: - { - PostMessage( GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETRANGE, 0, MAKELPARAM(0, 1000)); - } - break; - - case WM_COMMAND: - if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) { - // we dont shutdown the window here, but instead let the patcher know it needs to shutdown, and display our shutting down message. - // setting s_shutdown also wont allow any more Set text messages. - if(!s_shutdown) - { - s_shutdown = true; - SendMessage(GetDlgItem(s_dialog, IDC_TEXT), WM_SETTEXT, 0, (LPARAM) "Shutting Down..."); - EnableWindow(GetDlgItem(s_dialog, IDCANCEL), false); - } - } - break; - - case WM_KEYDOWN: - break; - - case WM_NCHITTEST: - SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); - return TRUE; - - case WM_TIMER: - OnTimer(hwndDlg, wParam); - break; - - case WM_QUIT: - ::DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - PostQuitMessage(0); - break; - - default: - return DefWindowProc(hwndDlg, uMsg, wParam, lParam); - } - return TRUE; -} - -//============================================================================ -static void WindowThreadProc(void *) { -#ifdef USE_VLD - VLDEnable(); -#endif - - InitCommonControls(); - s_event = CreateEvent( - (LPSECURITY_ATTRIBUTES) 0, - false, // auto reset - false, // initial state off - (LPCTSTR) 0 // name - ); - - s_dialog = ::CreateDialog( s_hInstance, MAKEINTRESOURCE( IDD_DIALOG ), NULL, SplashDialogProc ); - SetWindowText(s_dialog, "URU Launcher"); - - - ::SetDlgItemText( s_dialog, IDC_TEXT, "Initializing patcher..."); - SetTimer(s_dialog, kEventTimer, 250, 0); - - SendMessage(GetDlgItem(s_dialog, IDC_PRODUCTSTRING), WM_SETTEXT, 0, - (LPARAM)plProduct::ProductString().c_str()); - - s_dialogCreateEvent.Signal(); - - MessagePump(s_dialog); - - s_dialog = 0; - s_shutdown = true; - s_shutdownEvent.Signal(); -} - -//============================================================================ -static size_t CurlCallback(void *buffer, size_t size, size_t nmemb, void *) -{ - static char status[256]; - - strncpy(status, (const char *)buffer, std::min(size * nmemb, 256)); - status[255] = 0; - SetStatusText(status); - return size * nmemb; -} - -//============================================================================ -static void StatusCallback(void *) -{ -#ifdef USE_VLD - VLDEnable(); -#endif - - const char *serverUrl = GetServerStatusUrl(); - - CURL * hCurl = curl_easy_init(); - curl_easy_setopt(hCurl, CURLOPT_ERRORBUFFER, s_curlError); - - // update while we are running - while(!s_shutdown) - { - curl_easy_setopt(hCurl, CURLOPT_USERAGENT, "UruClient/1.0"); - curl_easy_setopt(hCurl, CURLOPT_URL, serverUrl); - curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, &CurlCallback); - - if (serverUrl[0] && curl_easy_perform(hCurl) != 0) // only perform request if there's actually a URL set - SetStatusText(s_curlError); - - for(unsigned i = 0; i < UPDATE_STATUSMSG_SECONDS && !s_shutdown; ++i) - { - Sleep(1000); - } - } - - curl_easy_cleanup(hCurl); - - s_statusEvent.Signal(); -} - - -/***************************************************************************** -* -* Exports -* -***/ - -//============================================================================ -void PrepCallback (int id, void *param) { - s_prepared = true; - if (id) - s_shutdown = true; - - if (!s_shutdown) - InitGame(); -} - -//============================================================================ -void InitCallback (int id, void *param) { - if (id) - s_shutdown = true; - if (!s_shutdown) - StartGame(); -} - -//============================================================================= -void StartCallback( int id, void *param) { - if(id == kStatusError) { - MessageBox(nil, "Failed to launch URU", "URU Launcher", MB_ICONERROR); - } - StopGame(); -} - -//============================================================================ -void StopCallback (int id, void *param) { - s_shutdown = true; - TerminateGame(); -} - -//============================================================================ -void TerminateCallback (int id, void *param) { - s_shutdown = true; - s_terminated = true; -} - -//============================================================================ -void ExitCallback (int id, void *param) { - TerminateGame(); -} - -//============================================================================ -void ProgressCallback (int id, void *param) { - PatchInfo *patchInfo = (PatchInfo *)param; - SetProgress(patchInfo->progress); -} - -//============================================================================ -void SetTextCallback (const char text[]) { - SetText(text); -} - -//============================================================================ -void SetStatusTextCallback (const char text[]) { - SetStatusText(text); -} - -//============================================================================ -void SetTimeRemainingCallback (unsigned seconds) { - SetTimeRemaining(seconds); -} - -//============================================================================ -void SetBytesRemainingCallback (unsigned bytes) { - SetBytesRemaining(bytes); -} - - -enum { - kArgServerIni, - kArgNoSelfPatch, - kArgBuildId, - kArgCwd, -}; - -static const CmdArgDef s_cmdLineArgs[] = { - { kCmdArgFlagged | kCmdTypeString, L"ServerIni", kArgServerIni }, - { kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch }, - { kCmdArgFlagged | kCmdTypeInt, L"BuildId", kArgBuildId }, - { kCmdArgFlagged | kCmdTypeBool, L"Cwd", kArgCwd }, -}; - -#include "pfConsoleCore/pfConsoleEngine.h" -PF_CONSOLE_LINK_FILE(Core) - -//============================================================================ -int __stdcall WinMain ( - HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPSTR lpCmdLine, - int nCmdShow -){ - PF_CONSOLE_INITIALIZE(Core) - - wchar_t token[256]; - const wchar_t *appCmdLine = AppGetCommandLine(); - StrTokenize(&appCmdLine, token, arrsize(token), WHITESPACE); - while(!StrStr(token, L".exe") && !StrStr(token, L".tmp")) - { - StrTokenize(&appCmdLine, token, arrsize(token), WHITESPACE); - } - while (*appCmdLine == L' ') - ++appCmdLine; - - bool isTempPatcher = false; - - plFileName curPatcherFile = plFileSystem::GetCurrentAppPath(); - plFileName newPatcherFile = plFileName::Join(curPatcherFile.StripFileName(), kPatcherExeFilename); - - // If our exe name doesn't match the "real" patcher exe name, then we are a newly - // downloaded patcher that needs to be copied over to the "real" exe.. so do that, - // exec it, and exit. - if (0 != curPatcherFile.AsString().CompareI(newPatcherFile.AsString())) { - isTempPatcher = true; - } - - CCmdParser cmdParser(s_cmdLineArgs, arrsize(s_cmdLineArgs)); - cmdParser.Parse(); - - if (!cmdParser.IsSpecified(kArgCwd)) - s_workingDir = plFileSystem::GetCurrentAppPath().StripFileName(); - - s_hInstance = hInstance; - memset(&s_launcherInfo, 0, sizeof(s_launcherInfo)); - StrPrintf(s_launcherInfo.cmdLine, arrsize(s_launcherInfo.cmdLine), appCmdLine); - s_launcherInfo.returnCode = 0; - - curl_global_init(CURL_GLOBAL_ALL); - - plFileName serverIni = "server.ini"; - if (cmdParser.IsSpecified(kArgServerIni)) - serverIni = plString::FromWchar(cmdParser.GetString(kArgServerIni)); - - // Load the server.ini so we know what to connect to - FILE *serverini = plFileSystem::Open(serverIni, "rb"); - if (serverini) - { - fclose(serverini); - pfConsoleEngine tempConsole; - tempConsole.ExecuteFile(serverIni); - } - else - { - hsMessageBox("No server.ini file found. Please check your URU installation.", "Error", hsMessageBoxNormal); - return 1; - } - - if(!isTempPatcher) - { - // create window thread - s_thread = (HANDLE)_beginthread( - WindowThreadProc, - 0, - nil - ); - if(cmdParser.IsSpecified(kArgBuildId)) - s_launcherInfo.buildId = cmdParser.GetInt(kArgBuildId); - - // Wait for the dialog to be created - s_dialogCreateEvent.Wait(); - _beginthread(StatusCallback, 0, nil); // get status - } - - for (;;) { - // Wait for previous process to exit. This will happen if we just patched. - HANDLE mutex = CreateMutexW(NULL, TRUE, kPatcherExeFilename.AsString().ToWchar()); - DWORD wait = WaitForSingleObject(mutex, 0); - while(!s_shutdown && wait != WAIT_OBJECT_0) - wait = WaitForSingleObject(mutex, 100); - - // User canceled - if (s_shutdown) - break; - - // If our exe name doesn't match the "real" patcher exe name, then we are a newly - // downloaded patcher that needs to be copied over to the "real" exe.. so do that, - // exec it, and exit. - if (isTempPatcher) { -// MessageBox(nil, "Replacing patcher file", "Msg", MB_OK); - - // Wait for the other process to exit - Sleep(1000); - - if (!plFileSystem::Unlink(newPatcherFile)) { - wchar_t error[256]; - DWORD errorCode = GetLastError(); - wchar_t *msg = TranslateErrorCode(errorCode); - - StrPrintf(error, arrsize(error), L"Failed to delete old patcher executable. %s", msg); - MessageBoxW(GetTopWindow(nil), error, L"Error", MB_OK); - LocalFree(msg); - break; - } - if (!plFileSystem::Move(curPatcherFile, newPatcherFile)) { - wchar_t error[256]; - DWORD errorCode = GetLastError(); - wchar_t *msg = TranslateErrorCode(errorCode); - - StrPrintf(error, arrsize(error), L"Failed to replace old patcher executable. %s", msg); - MessageBoxW(GetTopWindow(nil), error, L"Error", MB_OK); - // attempt to clean up this tmp file - plFileSystem::Unlink(curPatcherFile); - LocalFree(msg); - break; - } - - // launch new patcher - STARTUPINFOW si; - PROCESS_INFORMATION pi; - memset(&si, 0, sizeof(si)); - memset(&pi, 0, sizeof(pi)); - si.cb = sizeof(si); - - wchar_t cmdline[MAX_PATH]; - StrPrintf(cmdline, arrsize(cmdline), L"%S %s", newPatcherFile.AsString().c_str(), s_launcherInfo.cmdLine); - - // we have only successfully patched if we actually launch the new version of the patcher - (void)CreateProcessW( - NULL, - cmdline, - NULL, - NULL, - FALSE, - DETACHED_PROCESS, - NULL, - NULL, - &si, - &pi - ); - - SetReturnCode( pi.dwProcessId ); - CloseHandle( pi.hThread ); - CloseHandle( pi.hProcess ); - - // We're done. - break; - } - - // Clean up old temp files - plFileName fileSpec = plFileSystem::GetCurrentAppPath().StripFileName(); - std::vector tmpFiles = plFileSystem::ListDir(fileSpec, "*.tmp"); - std::for_each(tmpFiles.begin(), tmpFiles.end(), [](const plFileName &tmp) { - plFileSystem::Unlink(tmp); - }); - - SetConsoleCtrlHandler(CtrlHandler, TRUE); - InitAsyncCore(); // must do this before self patch, since it needs to connect to the file server - - // check to see if the patcher needs to be updated, and do it if so. - ENetError selfPatchResult; - if (false == (SelfPatch(cmdParser.IsSpecified(kArgNoSelfPatch), &s_shutdown, &selfPatchResult, &s_launcherInfo)) && IS_NET_SUCCESS(selfPatchResult)) { - // We didn't self-patch, so check for client updates and download them, then exec the client - StrCopy(s_launcherInfo.path, s_workingDir.AsString().ToWchar(), arrsize(s_launcherInfo.path)); - s_launcherInfo.prepCallback = PrepCallback; - s_launcherInfo.initCallback = InitCallback; - s_launcherInfo.startCallback = StartCallback; - s_launcherInfo.stopCallback = StopCallback; - s_launcherInfo.terminateCallback = TerminateCallback; - s_launcherInfo.progressCallback = ProgressCallback; - s_launcherInfo.exitCallback = ExitCallback; - s_launcherInfo.SetText = SetTextCallback; - s_launcherInfo.SetStatusText = SetStatusTextCallback; - s_launcherInfo.SetTimeRemaining = SetTimeRemainingCallback; - s_launcherInfo.SetBytesRemaining = SetBytesRemainingCallback; - PrepareGame(); - - while (!s_shutdown) // wait for window to be closed - AsyncSleep(10); - - StopGame(); - - // Wait for the PrepareGame thread to exit - while (!s_prepared) - AsyncSleep(10); - - // Wait for the StopGame thread to exit - while (!s_terminated) - Sleep(10); - } - else if (IS_NET_ERROR(selfPatchResult)) { - // Self-patch failed - SetText("Self-patch failed. Exiting..."); - if (!s_shutdown) { - wchar_t str[256]; - StrPrintf(str, arrsize(str), L"Patcher update failed. Error %u, %s", selfPatchResult, NetErrorToString(selfPatchResult)); - MessageBoxW(GetTopWindow(nil), str, L"Error", MB_OK); - } - } - else { - // We self-patched, so just exit (self-patcher already launched the new patcher. - // it is now waiting for our process to shutdown and release the shared mutex). - SetText("Patcher updated. Restarting..."); - s_shutdown = true; - } - - ShutdownAsyncCore(); - s_statusEvent.Wait(); - - PostMessage(s_dialog, WM_QUIT, 0, 0); // tell our window to shutdown - s_shutdownEvent.Wait(); // wait for our window to shutdown - - SetConsoleCtrlHandler(CtrlHandler, FALSE); - - if (s_event) - CloseHandle(s_event); - - s_eventQ.Clear(); - break; - } - - curl_global_cleanup(); - - return s_launcherInfo.returnCode; -} - -//============================================================================ -void SetReturnCode (DWORD retCode) { - s_launcherInfo.returnCode = retCode; -} - - -/***************************************************************************** -* -* Window Events -* -***/ - -//============================================================================ -void SetProgress (unsigned progress) { - SetProgressEvent *event = new SetProgressEvent(); - event->type = kEventSetProgress; - event->progress = progress; - PostEvent(event); -} - -//============================================================================ -void SetText (const char text[]) { - SetTextEvent *event = new SetTextEvent(); - event->type = kEventSetText; - StrCopy(event->text, text, arrsize(event->text)); - PostEvent(event); -} - -//============================================================================ -void SetStatusText (const char text[]) { - SetTextEvent *event = new SetTextEvent(); - event->type = kEventSetStatusText; - StrCopy(event->text, text, arrsize(event->text)); - PostEvent(event); -} - -//============================================================================ -void SetTimeRemaining (unsigned seconds) { - SetTimeRemainingEvent *event = new SetTimeRemainingEvent; - event->type = kEventSetTimeRemaining; - event->seconds = seconds; - PostEvent(event); -} - -//============================================================================ -void SetBytesRemaining (unsigned bytes) { - SetBytesRemainingEvent *event = new SetBytesRemainingEvent; - event->type = kEventSetBytesRemaining; - event->bytes = bytes; - PostEvent(event); -} diff --git a/Sources/Plasma/Apps/plUruLauncher/Pch.h b/Sources/Plasma/Apps/plUruLauncher/Pch.h deleted file mode 100644 index 262b5071..00000000 --- a/Sources/Plasma/Apps/plUruLauncher/Pch.h +++ /dev/null @@ -1,72 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h -* -***/ - -#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PCH_H -#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h included more than once" -#endif -#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PCH_H - -#include "hsWindows.h" -#include -#include - -#include - -#include "pnUtils/pnUtils.h" -#include "pnNetBase/pnNetBase.h" -#include "pnAsyncCore/pnAsyncCore.h" -#include "plProduct.h" -#include "pnNetCli/pnNetCli.h" -#include "plNetGameLib/plNetGameLib.h" -#include "pnEncryption/plChecksum.h" - -#include "plCompression/plZlibStream.h" -#include "plClientPatcher/UruPlayer.h" - -#include "plLauncherInfo.h" -#include "Intern.h" - diff --git a/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp b/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp deleted file mode 100644 index 1755b29c..00000000 --- a/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp -* -***/ - -#include "Pch.h" -#include "plStatusLog/plStatusLog.h" -#pragma hdrstop - - -/***************************************************************************** -* -* Private Data -* -***/ - -#ifndef PLASMA_EXTERNAL_RELEASE - static const wchar_t s_manifest[] = L"InternalPatcher"; -#else - static const wchar_t s_manifest[] = L"ExternalPatcher"; -#endif - -class SelfPatcherStream : public plZlibStream { - public: - virtual uint32_t Write(uint32_t byteCount, const void* buffer); - static plLauncherInfo *info; - static unsigned totalBytes; - static unsigned progress; -}; - -unsigned SelfPatcherStream::totalBytes = 0; -unsigned SelfPatcherStream::progress = 0; - -static bool s_downloadComplete; -static long s_numFiles; -static ENetError s_patchResult; -static bool s_updated; -static plFileName s_newPatcherFile; - - -/***************************************************************************** -* -* Private Functions -* -***/ - -//============================================================================ -static void NetErrorHandler (ENetProtocol protocol, ENetError error) { - plString msg = plString::Format("NetErr: %S", NetErrorToString(error)); - plStatusLog::AddLineS("patcher.log", msg.c_str()); - - if (IS_NET_SUCCESS(s_patchResult)) - s_patchResult = error; - s_downloadComplete = true; - - switch(error) { - case kNetErrServerBusy: - MessageBox(0, "Due to the high demand, the server is currently busy. Please try again later, or for alternative download options visit: http://www.mystonline.com/play/", "UruLauncher", MB_OK); - s_patchResult = kNetErrServerBusy; - s_downloadComplete = true; - break; - } -} - -//============================================================================ -static void DownloadCallback ( - ENetError result, - void * param, - const plFileName & filename, - hsStream * writer -) { - if(IS_NET_ERROR(result)) { - switch (result) { - case kNetErrTimeout: - writer->Rewind(); - NetCliFileDownloadRequest(filename, writer, DownloadCallback, param); - break; - - default: - plString msg = plString::Format("Error getting patcher file: %S", NetErrorToString(result)); - plStatusLog::AddLineS("patcher.log", msg.c_str()); - - if (IS_NET_SUCCESS(s_patchResult)) - s_patchResult = result; - break; - } - return; - } - - writer->Close(); - delete writer; - AtomicAdd(&s_numFiles, -1); - - if(!s_numFiles) { - s_downloadComplete = true; - s_updated = true; - } -} - -//============================================================================ -static bool MD5Check (const plFileName &filename, const char *md5) { - // Do md5 check - plMD5Checksum existingMD5(filename); - plMD5Checksum latestMD5; - - latestMD5.SetFromHexString(md5); - return (existingMD5 == latestMD5); -} - -//============================================================================ -static void ManifestCallback ( - ENetError result, - void * param, - const wchar_t group[], - const NetCliFileManifestEntry manifest[], - unsigned entryCount -) { - if(IS_NET_ERROR(result)) { - switch (result) { - case kNetErrTimeout: - NetCliFileManifestRequest(ManifestCallback, nil, s_manifest); - break; - - default: - plString msg = plString::Format("Error getting patcher manifest: %S", NetErrorToString(result)); - plStatusLog::AddLineS("patcher.log", msg.c_str()); - - if (IS_NET_SUCCESS(s_patchResult)) - s_patchResult = result; - break; - } - return; - } - -#ifndef PLASMA_EXTERNAL_RELEASE - if (entryCount == 0) { // dataserver does not contain a patcher - s_downloadComplete = true; - return; - } -#endif - - // MD5 check current patcher against value in manifest - ASSERT(entryCount == 1); - plFileName curPatcherFile = plFileSystem::GetCurrentAppPath(); - if (!MD5Check(curPatcherFile, plString::FromWchar(manifest[0].md5, 32).c_str())) { -// MessageBox(GetTopWindow(nil), "MD5 failed", "Msg", MB_OK); - SelfPatcherStream::totalBytes += manifest[0].zipSize; - - AtomicAdd(&s_numFiles, 1); - SetText("Downloading new patcher..."); - - SelfPatcherStream * stream = new SelfPatcherStream; - if (!stream->Open(s_newPatcherFile, "wb")) - ErrorAssert(__LINE__, __FILE__, "Failed to create file: %s, errno: %u", s_newPatcherFile.AsString().c_str(), errno); - - NetCliFileDownloadRequest(plString::FromWchar(manifest[0].downloadName), stream, DownloadCallback, nil); - } - else { - s_downloadComplete = true; - } -} - -//============================================================================ -static void FileSrvIpAddressCallback ( - ENetError result, - void * param, - const wchar_t addr[] -) { - NetCliGateKeeperDisconnect(); - - if (IS_NET_ERROR(result)) { - plString msg = plString::Format("FileSrvIpAddressRequest failed: %S", NetErrorToString(result)); - plStatusLog::AddLineS("patcher.log", msg.c_str()); - - s_patchResult = result; - s_downloadComplete = true; - } - - // Start connecting to the server - const char* caddr = hsWStringToString(addr); - NetCliFileStartConnect(&caddr, 1, true); - delete[] caddr; - - s_newPatcherFile = plFileSystem::GetCurrentAppPath().StripFileName(); - s_newPatcherFile = plFileSystem::GetTempFilename(kPatcherExeFilename.AsString().c_str(), s_newPatcherFile); - plFileSystem::Unlink(s_newPatcherFile); - - NetCliFileManifestRequest(ManifestCallback, nil, s_manifest); -} - -//============================================================================ -static bool SelfPatcherProc (bool * abort, plLauncherInfo *info) { - - bool patched = false; - s_downloadComplete = false; - s_patchResult = kNetSuccess; - - NetClientInitialize(); - NetClientSetErrorHandler(NetErrorHandler); - - const char** addrs; - unsigned count; - - count = GetGateKeeperSrvHostnames(&addrs); - - // Start connecting to the server - NetCliGateKeeperStartConnect(addrs, count); - - // request a file server ip address - NetCliGateKeeperFileSrvIpAddressRequest(FileSrvIpAddressCallback, nil, true); - - while(!s_downloadComplete && !*abort) { - NetClientUpdate(); - AsyncSleep(10); - } - - NetCliFileDisconnect(); - NetClientUpdate(); - - // Shutdown the client/server networking subsystem - NetClientDestroy(); - - if (s_downloadComplete && !*abort && s_updated && IS_NET_SUCCESS(s_patchResult)) { - - // launch new patcher - STARTUPINFOW si; - PROCESS_INFORMATION pi; - memset(&si, 0, sizeof(si)); - memset(&pi, 0, sizeof(pi)); - si.cb = sizeof(si); - - wchar_t cmdline[MAX_PATH]; - StrPrintf(cmdline, arrsize(cmdline), L"%s %s", - s_newPatcherFile.AsString().ToWchar().GetData(), info->cmdLine); - - // we have only successfully patched if we actually launch the new version of the patcher - patched = CreateProcessW( - NULL, - cmdline, - NULL, - NULL, - FALSE, - DETACHED_PROCESS, - NULL, - NULL, - &si, - &pi - ); - SetReturnCode(pi.dwProcessId); - CloseHandle( pi.hThread ); - CloseHandle( pi.hProcess ); - ASSERT(patched); - } - - return patched; -} - - -/***************************************************************************** -* -* ProgressStream Functions -* -***/ - -//============================================================================ -uint32_t SelfPatcherStream::Write(uint32_t byteCount, const void* buffer) { - progress += byteCount; - float p = (float)progress / (float)totalBytes * 100; // progress - SetProgress( (int)p ); - return plZlibStream::Write(byteCount, buffer); -} - - -/***************************************************************************** -* -* Protected Functions -* -***/ - -//============================================================================ -// if return value is true, there was an update and the patcher should be shutdown, so the new patcher can take over -bool SelfPatch (bool noSelfPatch, bool * abort, ENetError * result, plLauncherInfo *info) { - bool patched = false; - if (!noSelfPatch) { - SetText("Checking for patcher update..."); - patched = SelfPatcherProc(abort, info); - } - *result = s_patchResult; - return patched; -} - - -/* Enable themes in Windows XP and later */ -#pragma comment(linker,"\"/manifestdependency:type='win32' \ -name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ -processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") diff --git a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp new file mode 100644 index 00000000..4a8b015b --- /dev/null +++ b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp @@ -0,0 +1,351 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com +or by snail mail at: +Cyan Worlds, Inc. +14617 N Newport Hwy +Mead, WA 99021 + +*==LICENSE==*/ + +#include "HeadSpin.h" +#include "plClientLauncher.h" +#include "plFileSystem.h" +#include "plProduct.h" +#include "hsThread.h" +#include "hsTimer.h" + +#include "pnUtils/pnUtils.h" // for CCmdParser +#include "pnAsyncCore/pnAsyncCore.h" +#include "plNetGameLib/plNetGameLib.h" +#include "plStatusLog/plStatusLog.h" + +#include "pfPatcher/plManifests.h" +#include "pfPatcher/pfPatcher.h" + +#include "pfConsoleCore/pfConsoleEngine.h" +PF_CONSOLE_LINK_FILE(Core) + +#include + +plClientLauncher::ErrorFunc s_errorProc = nullptr; // don't even ask, cause I'm not happy about this. + +const int kNetTransTimeout = 5 * 60 * 1000; // 5m +const int kShardStatusUpdateTime = 5; // 5s +const int kAsyncCoreShutdownTime = 2 * 1000; // 2s +const int kNetCoreUpdateSleepTime = 10; // 10ms + +// =================================================== + +class plShardStatus : public hsThread +{ + double fLastUpdate; + volatile bool fRunning; + hsEvent fUpdateEvent; + char fCurlError[CURL_ERROR_SIZE]; + +public: + plClientLauncher::StatusFunc fShardFunc; + + plShardStatus() : + fRunning(true), fLastUpdate(0) + { } + + virtual hsError Run(); + void Shutdown(); + void Update(); +}; + +static size_t ICurlCallback(void* buffer, size_t size, size_t nmemb, void* thread) +{ + static char status[256]; + + strncpy(status, (const char *)buffer, std::min(size * nmemb, arrsize(status))); + status[arrsize(status) - 1] = 0; + static_cast(thread)->fShardFunc(status); + return size * nmemb; +} + +hsError plShardStatus::Run() +{ + { + const char* url = GetServerStatusUrl(); + + // initialize CURL + std::unique_ptr> curl(curl_easy_init(), curl_easy_cleanup); + curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, fCurlError); + curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "UruClient/1.0"); + curl_easy_setopt(curl.get(), CURLOPT_URL, url); + curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, this); + curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, ICurlCallback); + + // we want to go ahead and run once + fUpdateEvent.Signal(); + + // loop until we die! + do + { + fUpdateEvent.Wait(); + if (!fRunning) + break; + + if (url[0] && curl_easy_perform(curl.get())) + fShardFunc(fCurlError); + fLastUpdate = hsTimer::GetSysSeconds(); + } while (fRunning); + } + + return hsOK; +} + +void plShardStatus::Shutdown() +{ + fRunning = false; + fUpdateEvent.Signal(); +} + +void plShardStatus::Update() +{ + double now = hsTimer::GetSysSeconds(); + if ((now - fLastUpdate) >= kShardStatusUpdateTime) + fUpdateEvent.Signal(); +} + +// =================================================== + +plClientLauncher::plClientLauncher() : + fFlags(0), + fServerIni("server.ini"), + fPatcherFactory(nullptr), + fClientExecutable(plManifest::ClientExecutable()), + fStatusThread(new plShardStatus()) +{ + pfPatcher::GetLog()->AddLine(plProduct::ProductString().c_str()); +} + +plClientLauncher::~plClientLauncher() { } + +// =================================================== + +plString plClientLauncher::GetAppArgs() const +{ + plStringStream ss; + ss << "-ServerIni="; + ss << fServerIni.AsString(); + return ss.GetString(); +} + +void plClientLauncher::IOnPatchComplete(ENetError result, const plString& msg) +{ + if (IS_NET_SUCCESS(result)) { + // a couple of options + // 1. we self-patched and didn't update anything. patch the main client. + // 2. we self-patched and did things and stuff... re-run myself. + // 3. we patched the client... run it. + if (!hsCheckBits(fFlags, kHaveSelfPatched) && (fClientExecutable == plManifest::ClientExecutable())) { + // case 1 + hsSetBits(fFlags, kHaveSelfPatched); + PatchClient(); + } else + // cases 2 & 3 + fLaunchClientFunc(fClientExecutable, GetAppArgs()); + } else if (s_errorProc) + s_errorProc(result, msg); +} + +void plClientLauncher::PatchClient() +{ + if (fStatusFunc) + fStatusFunc("Checking for updates..."); + hsAssert(fPatcherFactory, "why is the patcher factory nil?"); + + pfPatcher* patcher = fPatcherFactory(); + patcher->OnCompletion(std::bind(&plClientLauncher::IOnPatchComplete, this, std::placeholders::_1, std::placeholders::_2)); + patcher->OnSelfPatch([&](const plFileName& file) { fClientExecutable = file; }); + + if (hsCheckBits(fFlags, kHaveSelfPatched)) + patcher->RequestManifest(plManifest::ClientManifest()); + else + patcher->RequestManifest(plManifest::PatcherManifest()); + patcher->Start(); +} + +bool plClientLauncher::CompleteSelfPatch(std::function waitProc) const +{ + if (hsCheckBits(fFlags, kHaveSelfPatched)) + return false; + + plString myExe = plFileSystem::GetCurrentAppPath().GetFileName(); + if (myExe.CompareI(plManifest::PatcherExecutable().AsString()) != 0) { + waitProc(); + + // so now we need to unlink the old patcher, and move ME into that fool's place... + // then we can continue on our merry way! + if (!plFileSystem::Unlink(plManifest::PatcherExecutable())) { + hsMessageBox("Failed to delete old patcher executable!", "Error", hsMessageBoxNormal, hsMessageBoxIconError); + return true; + } + if (!plFileSystem::Move(plFileSystem::GetCurrentAppPath(), plManifest::PatcherExecutable())) { + hsMessageBox("Failed to move patcher executable!", "Error", hsMessageBoxNormal, hsMessageBoxIconError); + return true; + } + + // Now, execute the new patcher... + fLaunchClientFunc(plManifest::PatcherExecutable(), GetAppArgs() + " -NoSelfPatch"); + return true; + } + return false; +} + +// =================================================== + +static void IGotFileServIPs(ENetError result, void* param, const wchar_t* addr) +{ + plClientLauncher* launcher = static_cast(param); + NetCliGateKeeperDisconnect(); + + if (IS_NET_SUCCESS(result)) { + // bah... why do I even bother + plString eapSucks = plString::FromWchar(addr); + const char* eapReallySucks[] = { eapSucks.c_str() }; + NetCliFileStartConnect(eapReallySucks, 1, true); + + // Who knows if we will actually connect. So let's start updating. + launcher->PatchClient(); + } else if (s_errorProc) + s_errorProc(result, "Failed to get FileServ addresses"); +} + +static void IEapSucksErrorProc(ENetProtocol protocol, ENetError error) +{ + if (s_errorProc) { + plString msg = plString::Format("Protocol: %S", NetProtocolToString(protocol)); + s_errorProc(error, msg); + } +} + +void plClientLauncher::InitializeNetCore() +{ + // initialize shard status + hsTimer::SetRealTime(true); + fStatusThread->Start(); + + // init eap... + AsyncCoreInitialize(); + + NetClientInitialize(); + NetClientSetErrorHandler(IEapSucksErrorProc); + NetClientSetTransTimeoutMs(kNetTransTimeout); + + // Gotta grab the filesrvs from the gate + const char** addrs; + uint32_t num = GetGateKeeperSrvHostnames(&addrs); + + NetCliGateKeeperStartConnect(addrs, num); + NetCliGateKeeperFileSrvIpAddressRequest(IGotFileServIPs, this, true); +} + +// =================================================== + +void plClientLauncher::PumpNetCore() const +{ + // this ain't net core, but it needs to be pumped :( + hsTimer::IncSysSeconds(); + + // pump eap + NetClientUpdate(); + + // pump shard status + fStatusThread->Update(); + + // don't nom all the CPU... kthx + hsSleep::Sleep(kNetCoreUpdateSleepTime); +} + +void plClientLauncher::ShutdownNetCore() const +{ + // shutdown shard status + fStatusThread->Shutdown(); + + // unhook the neterr callback at this point because all transactions + // will fail when we call NetClientDestroy + s_errorProc = nullptr; + + // shutdown eap + NetCliGateKeeperDisconnect(); + NetCliFileDisconnect(); + NetClientDestroy(); + + // shutdown eap (part deux) + AsyncCoreDestroy(kAsyncCoreShutdownTime); +} + +// =================================================== + +bool plClientLauncher::LoadServerIni() const +{ + PF_CONSOLE_INITIALIZE(Core); + + pfConsoleEngine console; + return console.ExecuteFile(fServerIni); +} + +void plClientLauncher::ParseArguments() +{ + enum { kArgServerIni, kArgNoSelfPatch }; + const CmdArgDef cmdLineArgs[] = { + { kCmdArgFlagged | kCmdTypeString, L"ServerIni", kArgServerIni }, + { kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch }, + }; + + CCmdParser cmdParser(cmdLineArgs, arrsize(cmdLineArgs)); + cmdParser.Parse(); + + // cache 'em + if (cmdParser.GetBool(kArgNoSelfPatch)) + hsSetBits(fFlags, kHaveSelfPatched); + if (cmdParser.IsSpecified(kArgServerIni)) + fServerIni = plString::FromWchar(cmdParser.GetString(kArgServerIni)); +} + +void plClientLauncher::SetErrorProc(ErrorFunc proc) +{ + s_errorProc = proc; +} + +void plClientLauncher::SetShardProc(StatusFunc proc) +{ + fStatusThread->fShardFunc = proc; +} diff --git a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h new file mode 100644 index 00000000..13f20a3f --- /dev/null +++ b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h @@ -0,0 +1,147 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com +or by snail mail at: +Cyan Worlds, Inc. +14617 N Newport Hwy +Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _plClientLauncher_inc_ +#define _plClientLauncher_inc_ + +#include "plFileSystem.h" +#include "pnNetBase/pnNbError.h" + +#include +#include + +class plClientLauncher +{ +public: + typedef std::function CreatePatcherFunc; + typedef std::function ErrorFunc; + typedef std::function LaunchClientFunc; + typedef std::function StatusFunc; + +private: + enum Flags + { + kHaveSelfPatched = 1<<0, + }; + + uint32_t fFlags; + plFileName fServerIni; + + plFileName fClientExecutable; + std::unique_ptr fStatusThread; + + CreatePatcherFunc fPatcherFactory; + LaunchClientFunc fLaunchClientFunc; + StatusFunc fStatusFunc; + + plString GetAppArgs() const; + + void IOnPatchComplete(ENetError result, const plString& msg); + +public: + plClientLauncher(); + ~plClientLauncher(); + + /** Begin the next logical patch operation. We are internally tracking if this is a self patch or a client patch. + * All you need to do is make certain the doggone callbacks are set so that your UI will update. In theory, you + * should never call this from your UI code since we manage this state for you. + */ + void PatchClient(); + + /** Attempt to complete a self-patch left in progress by an older launcher. Specifically, we want to rename + * the launcher to something sane (UruLauncher.exe.tmp -> UruLauncher.exe). If we complete a self-patch in + * here, then we need to relaunch ourselves so that the game client will look like what the server expects. + * \returns True if a self-patch was completed. False if not. + */ + bool CompleteSelfPatch(std::function waitProc) const; + + /** Start eap's weird network subsystem and the shard status pinger. + * \remarks Please note that this will also enqueue the first patch. + */ + void InitializeNetCore(); + + /** This pumps eap's network subsystem and runs any queued transaction completion callbacks. + * The thread that you call this from will be the thread that all your UI updates come from. + * So be certain that you've thought that through! + * \remarks This method will cause the thread to sleep so that we don't hog the CPU. + */ + void PumpNetCore() const; + + /** Shutdown eap's netcore and purge any other crap that needs to happen while the app is + * visible. In other words, tear down evil threaded crap. + */ + void ShutdownNetCore() const; + + /** Load the server configuration file. Note that you MUST parse the command + * arguments before calling this function! + */ + bool LoadServerIni() const; + + /** Parse the command line options. */ + void ParseArguments(); + + /** Set a callback function that is called on a network error. + * \remarks This will be called from the network thread. + */ + void SetErrorProc(ErrorFunc proc); + + /** Set a patcher factory. */ + void SetPatcherFactory(CreatePatcherFunc factory) { fPatcherFactory = factory; } + + /** Set a callback that launches an arbitrary executable. + * \remarks This will be called from an arbitrary thread. + */ + void SetLaunchClientProc(LaunchClientFunc proc) { fLaunchClientFunc = proc; } + + /** Set a callback that displays the shard status. + * \remarks This will be called from a worker thread. + */ + void SetShardProc(StatusFunc proc); + + /** Set a callback that displays the patcher status. + * \remarks This will be called from the network thread. Note that any time + * this is called, you should consider it a state reset (so undo your progress bars). + */ + void SetStatusProc(StatusFunc proc) { fStatusFunc = proc; } +}; + +#endif // _plClientLauncher_inc_ diff --git a/Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h b/Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h deleted file mode 100644 index 5cc995bc..00000000 --- a/Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h +++ /dev/null @@ -1,102 +0,0 @@ -/*==LICENSE==* - -CyanWorlds.com Engine - MMOG client, server and tools -Copyright (C) 2011 Cyan Worlds, Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Additional permissions under GNU GPL version 3 section 7 - -If you modify this Program, or any covered work, by linking or -combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, -NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent -JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK -(or a modified version of those libraries), -containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, -PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG -JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the -licensors of this Program grant you additional -permission to convey the resulting work. Corresponding Source for a -non-source form of such a combination shall include the source code for -the parts of OpenSSL and IJG JPEG Library used as well as that of the covered -work. - -You can contact Cyan Worlds, Inc. by email legal@cyan.com - or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 - -*==LICENSE==*/ -/***************************************************************************** -* -* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h -* -***/ - -#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H -#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h included more than once" -#endif -#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H - -enum EStatus { - kStatusOk, - kStatusError, - kStatusPending, -}; - -struct PatchInfo { - unsigned progress; - unsigned stage; - unsigned progressStage; -}; - -typedef void (*launcherCallback)(int status, void *param); -typedef void (*setTextCallback)(const char text[]); -typedef void (*setStatusTextCallback)(const char text[]); -typedef void (*setTimeRemainingCallback)(unsigned seconds); -typedef void (*setBytesRemainingCallback)(unsigned bytes); - -struct plLauncherInfo { - wchar_t path[MAX_PATH]; - wchar_t cmdLine[512]; - unsigned buildId; // buildId override - launcherCallback prepCallback; - launcherCallback initCallback; - launcherCallback startCallback; - launcherCallback stopCallback; - launcherCallback terminateCallback; - launcherCallback progressCallback; - launcherCallback exitCallback; - setTextCallback SetText; - setStatusTextCallback SetStatusText; - setTimeRemainingCallback SetTimeRemaining; - setBytesRemainingCallback SetBytesRemaining; - - PatchInfo patchInfo; - DWORD returnCode; // used so we can pass a new process id back to gametap. That way gametap wont think uru has exited when the patcher quits. -}; - - -/***************************************************************************** -* -* Main.cpp -* -***/ - -void SetProgress (unsigned progress) ; -void SetText (const char text[]); -void SetStatusText (const char text[]); -void SetTimeRemaining(unsigned seconds); -void SetBytesRemaining(unsigned bytes); \ No newline at end of file diff --git a/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc b/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc index e6261ee9..0a6e552a 100644 --- a/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc +++ b/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc @@ -4,7 +4,16 @@ #define WIN32_LEAN_AND_MEAN #include -#define IDC_STATIC (-1) // all static controls + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources @@ -32,6 +41,12 @@ BEGIN "\0" END +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + #endif // APSTUDIO_INVOKED @@ -40,7 +55,7 @@ END // Dialog // -IDD_DIALOG DIALOGEX 0, 0, 301, 180 +IDD_DIALOG DIALOGEX 0, 0, 301, 135 STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW @@ -48,17 +63,16 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL 109,IDB_BITMAP,"Static",SS_BITMAP | SS_SUNKEN,7,7,288,36 CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER | 0x1,7, - 162,234,11 - LTEXT "Static",IDC_TEXT,10,152,266,8 - PUSHBUTTON "Cancel",IDCANCEL,243,162,51,11 + 111,234,11 + CONTROL "", IDC_MARQUEE, "msctls_progress32", WS_BORDER | 0x8, 7, + 111, 234, 11 + LTEXT "Static",IDC_TEXT,10,101,266,8 + PUSHBUTTON "Cancel",IDCANCEL,243,111,51,11 LTEXT "Welcome to URU",IDC_STATUS_TEXT,19,57,266,17 GROUPBOX "",IDC_STATIC,7,46,287,49 - GROUPBOX "Status",IDC_STATIC,7,103,287,44 - LTEXT "Approx. Time Remaining:",IDC_STATIC,19,117,88,11 - LTEXT "Update Remaining:",IDC_STATIC,20,130,73,9 - LTEXT "...",IDC_TIMEREMAINING,111,117,165,12 - LTEXT "...",IDC_BYTESREMAINING,111,130,42,12 RTEXT "Product String",IDC_PRODUCTSTRING,19,85,272,8 + LTEXT "6 MiB / 666 MiB",IDC_DLSIZE,11,124,110,8 + LTEXT "128 KiB/s",IDC_DLSPEED,186,124,53,8,0,WS_EX_RIGHT END @@ -75,7 +89,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 294 TOPMARGIN, 7 - BOTTOMMARGIN, 173 + BOTTOMMARGIN, 135 END END #endif // APSTUDIO_INVOKED diff --git a/Sources/Plasma/Apps/plUruLauncher/resource.h b/Sources/Plasma/Apps/plUruLauncher/resource.h index d54baf7a..612a5ffd 100644 --- a/Sources/Plasma/Apps/plUruLauncher/resource.h +++ b/Sources/Plasma/Apps/plUruLauncher/resource.h @@ -6,12 +6,15 @@ #define IDB_BITMAP 109 #define IDI_ICON1 111 #define IDC_PROGRESS 1003 +#define IDC_MARQUEE 1004 #define IDC_TEXT 1006 #define IDC_STATUS_TEXT 1009 #define IDC_TIMEREMAINING 1010 #define IDC_BYTESREMAINING 1011 #define IDC_PRODUCTSTRING 1012 -#define IDC_FILESREMAINING 1013 +#define IDC_DLSIZE 1014 +#define IDC_DLSPEED 1015 +#define IDC_STATIC -1 // Next default values for new objects // @@ -19,7 +22,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 112 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1014 +#define _APS_NEXT_CONTROL_VALUE 1016 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/Sources/Plasma/Apps/plUruLauncher/winmain.cpp b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp new file mode 100644 index 00000000..702018c6 --- /dev/null +++ b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp @@ -0,0 +1,348 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com +or by snail mail at: +Cyan Worlds, Inc. +14617 N Newport Hwy +Mead, WA 99021 + +*==LICENSE==*/ + +#include "HeadSpin.h" +#include "plFileSystem.h" +#include "plProduct.h" + +#include "pfPatcher/plManifests.h" +#include "pfPatcher/pfPatcher.h" + +#include "plClientLauncher.h" + +#include "hsWindows.h" +#include "resource.h" +#include +#include + +// =================================================== + +#define PLASMA_PHAILURE 1 +#define PLASMA_OK 0 + +static HWND s_dialog; +static plClientLauncher s_launcher; +static UINT s_taskbarCreated = RegisterWindowMessageW(L"TaskbarButtonCreated"); +static ITaskbarList3* s_taskbar = nullptr; + +// =================================================== + +/** Create a global patcher mutex that is backwards compatible with eap's */ +static HANDLE CreatePatcherMutex() +{ + return CreateMutexW(nullptr, FALSE, plManifest::PatcherExecutable().AsString().ToWchar()); +} + +static bool IsPatcherRunning() +{ + HANDLE mut = CreatePatcherMutex(); + return WaitForSingleObject(mut, 0) != WAIT_OBJECT_0; +} + +static void WaitForOldPatcher() +{ + HANDLE mut = CreatePatcherMutex(); + WaitForSingleObject(mut, INFINITE); +} + +// =================================================== + +static inline void IQuit(int exitCode=PLASMA_OK) +{ + // hey, guess what? + // PostQuitMessage doesn't work if you're not on the main thread... + PostMessageW(s_dialog, WM_QUIT, exitCode, 0); +} + +static inline void IShowMarquee(bool marquee=true) +{ + // NOTE: This is a HACK to workaround a bug that causes progress bars that were ever + // marquees to reanimate when changing the range or position + ShowWindow(GetDlgItem(s_dialog, IDC_MARQUEE), marquee ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(s_dialog, IDC_PROGRESS), marquee ? SW_HIDE : SW_SHOW); + PostMessageW(GetDlgItem(s_dialog, IDC_MARQUEE), PBM_SETMARQUEE, static_cast(marquee), 0); +} + +BOOL CALLBACK PatcherDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + // NT6 Taskbar Majick + if (uMsg == s_taskbarCreated) { + if (s_taskbar) + s_taskbar->Release(); + HRESULT result = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, IID_ITaskbarList3, (void**)&s_taskbar); + if (FAILED(result)) + s_taskbar = nullptr; + } + + switch (uMsg) { + case WM_COMMAND: + // Did they press cancel? + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) { + EnableWindow(GetDlgItem(s_dialog, IDCANCEL), false); + SetWindowTextW(GetDlgItem(s_dialog, IDC_TEXT), L"Shutting Down..."); + IQuit(); + } + break; + case WM_DESTROY: + if (s_taskbar) + s_taskbar->Release(); + PostQuitMessage(PLASMA_OK); + break; + case WM_NCHITTEST: + SetWindowLongW(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); + return TRUE; + case WM_QUIT: + s_launcher.ShutdownNetCore(); + DestroyWindow(hwndDlg); + break; + default: + return DefWindowProcW(hwndDlg, uMsg, wParam, lParam); + } + + return TRUE; +} + +static void ShowPatcherDialog(HINSTANCE hInstance) +{ + s_dialog = ::CreateDialogW(hInstance, MAKEINTRESOURCEW(IDD_DIALOG), nullptr, PatcherDialogProc); + SetDlgItemTextW(s_dialog, IDC_TEXT, L"Connecting..."); + SetDlgItemTextW(s_dialog, IDC_PRODUCTSTRING, plProduct::ProductString().ToWchar()); + SetDlgItemTextW(s_dialog, IDC_DLSIZE, L""); + SetDlgItemTextW(s_dialog, IDC_DLSPEED, L""); + IShowMarquee(); +} + +static void PumpMessages() +{ + MSG msg; + do { + // Pump all Win32 messages + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) { + if (!IsDialogMessageW(s_dialog, &msg)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + // Now we need to pump the netcore while we have some spare time... + s_launcher.PumpNetCore(); + } while (msg.message != WM_QUIT); +} + +// =================================================== + +static void IOnDownloadBegin(const plFileName& file) +{ + plString msg = plString::Format("Downloading... %s", file.AsString().c_str()); + SetDlgItemTextW(s_dialog, IDC_TEXT, msg.ToWchar()); +} + +static void IOnProgressTick(uint64_t curBytes, uint64_t totalBytes, const plString& status) +{ + // Swap marquee/real progress + IShowMarquee(false); + + // DL size + plString size = plString::Format("%s / %s", plFileSystem::ConvertFileSize(curBytes).c_str(), + plFileSystem::ConvertFileSize(totalBytes).c_str()); + SetDlgItemTextW(s_dialog, IDC_DLSIZE, size.ToWchar()); + + // DL speed + SetDlgItemTextW(s_dialog, IDC_DLSPEED, status.ToWchar()); + HWND progress = GetDlgItem(s_dialog, IDC_PROGRESS); + + // hey look... ULONGLONG. that's exactly what we need >.< + if (s_taskbar) + s_taskbar->SetProgressValue(s_dialog, curBytes, totalBytes); + + // Windows can only do signed 32-bit int progress bars. + // So, chop it into smaller chunks until we get something we can represent. + while (totalBytes > INT32_MAX) { + totalBytes /= 1024; + curBytes /= 1024; + } + + PostMessageW(progress, PBM_SETRANGE32, 0, static_cast(totalBytes)); + PostMessageW(progress, PBM_SETPOS, static_cast(curBytes), 0); +} + +// =================================================== + +static void ILaunchClientExecutable(const plFileName& exe, const plString& args) +{ + // Once we start launching something, we no longer need to trumpet any taskbar status + if (s_taskbar) + s_taskbar->SetProgressState(s_dialog, TBPF_NOPROGRESS); + + // Only launch a client executable if we're given one. If not, that's probably a cue that we're + // done with some service operation and need to go away. + if (!exe.AsString().IsEmpty()) { + STARTUPINFOW si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + si.cb = sizeof(si); + + // This event will prevent the game from restarting the patcher + HANDLE hEvent = CreateEventW(nullptr, TRUE, FALSE, L"UruPatcherEvent"); + + // Fire up ye olde new process + plString cmd = plString::Format("%s %s", exe.AsString().c_str(), args.c_str()); + CreateProcessW( + exe.AsString().ToWchar(), + const_cast(cmd.ToWchar().GetData()), // windows claims that it may modify this... let's hope that doesn't happen. + nullptr, + nullptr, + FALSE, + DETACHED_PROCESS, + nullptr, + plFileSystem::GetCWD().AsString().ToWchar(), + &si, + &pi + ); + + // if this is the real game client, then we need to make sure it gets this event... + if (plManifest::ClientExecutable().AsString().CompareI(exe.AsString()) == 0) { + WaitForInputIdle(pi.hProcess, 1000); + WaitForSingleObject(hEvent, INFINITE); + } + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + CloseHandle(hEvent); + } + + // time to hara-kiri... + IQuit(); +} + +static void IOnNetError(ENetError result, const plString& msg) +{ + if (s_taskbar) + s_taskbar->SetProgressState(s_dialog, TBPF_ERROR); + + plString text = plString::Format("Error: %S\r\n%s", NetErrorAsString(result), msg.c_str()); + hsMessageBox(text.c_str(), "Error", hsMessageBoxNormal); + IQuit(PLASMA_PHAILURE); +} + +static void ISetDownloadStatus(const plString& status) +{ + SetDlgItemTextW(s_dialog, IDC_TEXT, status.ToWchar()); + + // consider this a reset of the download status... + IShowMarquee(); + SetDlgItemTextW(s_dialog, IDC_DLSIZE, L""); + SetDlgItemTextW(s_dialog, IDC_DLSPEED, L""); + + if (s_taskbar) + s_taskbar->SetProgressState(s_dialog, TBPF_INDETERMINATE); +} + +static void ISetShardStatus(const plString& status) +{ + SetDlgItemTextW(s_dialog, IDC_STATUS_TEXT, status.ToWchar()); +} + +static pfPatcher* IPatcherFactory() +{ + pfPatcher* patcher = new pfPatcher(); + patcher->OnFileDownloadBegin(IOnDownloadBegin); + patcher->OnProgressTick(IOnProgressTick); + + return patcher; +} + +// =================================================== + +int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLink, int nCmdShow) +{ + // Let's initialize our plClientLauncher friend + s_launcher.ParseArguments(); + s_launcher.SetErrorProc(IOnNetError); + s_launcher.SetLaunchClientProc(ILaunchClientExecutable); + s_launcher.SetPatcherFactory(IPatcherFactory); + s_launcher.SetShardProc(ISetShardStatus); + s_launcher.SetStatusProc(ISetDownloadStatus); + + // If we're newly updated, then our filename will be something we don't expect! + // Let's go ahead and take care of that nao. + if (s_launcher.CompleteSelfPatch(WaitForOldPatcher)) + return PLASMA_OK; // see you on the other side... + + // Load the doggone server.ini + if (!s_launcher.LoadServerIni()) { + hsMessageBox("No server.ini file found. Please check your URU installation.", "Error", hsMessageBoxNormal); + return PLASMA_PHAILURE; + } + + // Ensure there is only ever one patcher running... + if (IsPatcherRunning()) { + hsMessageBox(plString::Format("%s is already running", plProduct::LongName().c_str()).c_str(), "Error", + hsMessageBoxNormal, hsMessageBoxIconError); + return PLASMA_OK; + } + HANDLE _onePatcherMut = CreatePatcherMutex(); + + // Initialize the network core + s_launcher.InitializeNetCore(); + + // Welp, now that we know we're (basically) sane, let's create our client window + // and pump window messages until we're through. + ShowPatcherDialog(hInstance); + PumpMessages(); + + // Alrighty now we just need to clean up behind ourselves! + // NOTE: We shut down the netcore in the WM_QUIT handler so + // we don't have a windowless, zombie process if that takes + // awhile (it can... dang eap...) + ReleaseMutex(_onePatcherMut); + CloseHandle(_onePatcherMut); + + // kthxbai + return PLASMA_OK; +} + +/* Enable themes in Windows XP and later */ +#pragma comment(linker,"\"/manifestdependency:type='win32' \ + name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ + processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")