Branan Purvine-Riley
11 years ago
20 changed files with 1164 additions and 2794 deletions
@ -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}) |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 |
||||
* |
||||
***/ |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <process.h> |
||||
#include <ctime> |
||||
#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" |
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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; |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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; |
||||
|
||||
}; |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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); |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <algorithm> |
||||
#pragma hdrstop |
||||
|
||||
|
||||
#include "resource.h" |
||||
#include <commctrl.h> |
||||
#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_t>(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<plFileName> 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); |
||||
} |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <process.h> |
||||
#include <ctime> |
||||
|
||||
#include <curl/curl.h> |
||||
|
||||
#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" |
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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='*'\"") |
@ -0,0 +1,468 @@
|
||||
/*==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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <algorithm> |
||||
#include <curl/curl.h> |
||||
#include <deque> |
||||
|
||||
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_t>(size * nmemb, arrsize(status))); |
||||
status[arrsize(status) - 1] = 0; |
||||
static_cast<plShardStatus*>(thread)->fShardFunc(status); |
||||
return size * nmemb; |
||||
} |
||||
|
||||
hsError plShardStatus::Run() |
||||
{ |
||||
{ |
||||
const char* url = GetServerStatusUrl(); |
||||
|
||||
// initialize CURL
|
||||
std::unique_ptr<CURL, std::function<void(CURL*)>> 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(); |
||||
} |
||||
|
||||
// ===================================================
|
||||
|
||||
class plRedistUpdater : public hsThread |
||||
{ |
||||
bool fSuccess; |
||||
|
||||
public: |
||||
plClientLauncher* fParent; |
||||
plClientLauncher::InstallRedistFunc fInstallProc; |
||||
std::deque<plFileName> fRedistQueue; |
||||
|
||||
plRedistUpdater() |
||||
: fSuccess(true) |
||||
{ } |
||||
|
||||
~plRedistUpdater() |
||||
{ |
||||
// If anything is left in the deque, it was not installed.
|
||||
// We should unlink them so the next launch will redownload and install them.
|
||||
std::for_each(fRedistQueue.begin(), fRedistQueue.end(), |
||||
[] (const plFileName& file) { |
||||
plFileSystem::Unlink(file); |
||||
} |
||||
); |
||||
} |
||||
|
||||
virtual void OnQuit() |
||||
{ |
||||
// If we succeeded, then we should launch the game client...
|
||||
if (fSuccess) |
||||
fParent->LaunchClient(); |
||||
} |
||||
|
||||
virtual hsError Run() |
||||
{ |
||||
while (!fRedistQueue.empty()) { |
||||
if (fInstallProc(fRedistQueue.back())) |
||||
fRedistQueue.pop_back(); |
||||
else { |
||||
s_errorProc(kNetErrInternalError, fRedistQueue.back().AsString()); |
||||
fSuccess = false; |
||||
return hsFail; |
||||
} |
||||
} |
||||
return hsOK; |
||||
} |
||||
|
||||
virtual void Start() |
||||
{ |
||||
if (fRedistQueue.empty()) |
||||
OnQuit(); |
||||
else |
||||
hsThread::Start(); |
||||
} |
||||
}; |
||||
|
||||
// ===================================================
|
||||
|
||||
plClientLauncher::plClientLauncher() : |
||||
fFlags(0), |
||||
fServerIni("server.ini"), |
||||
fPatcherFactory(nullptr), |
||||
fClientExecutable(plManifest::ClientExecutable()), |
||||
fStatusThread(new plShardStatus()), |
||||
fInstallerThread(new plRedistUpdater()) |
||||
{ |
||||
pfPatcher::GetLog()->AddLine(plProduct::ProductString().c_str()); |
||||
} |
||||
|
||||
plClientLauncher::~plClientLauncher() { } |
||||
|
||||
// ===================================================
|
||||
|
||||
plString plClientLauncher::GetAppArgs() const |
||||
{ |
||||
// If -Repair was specified, there are no args for the next call...
|
||||
if (hsCheckBits(fFlags, kRepairGame)) { |
||||
return ""; |
||||
} |
||||
|
||||
plStringStream ss; |
||||
ss << "-ServerIni="; |
||||
ss << fServerIni.AsString(); |
||||
|
||||
// optional args
|
||||
if (hsCheckBits(fFlags, kClientImage)) |
||||
ss << " -Image"; |
||||
|
||||
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 -- update any redistributables, then launch the client.
|
||||
fInstallerThread->fParent = this; |
||||
fInstallerThread->Start(); |
||||
} |
||||
} else if (s_errorProc) |
||||
s_errorProc(result, msg); |
||||
} |
||||
|
||||
bool plClientLauncher::IApproveDownload(const plFileName& file) |
||||
{ |
||||
// So, for a repair, what we want to do is quite simple.
|
||||
// That is: download everything that is NOT in the root directory.
|
||||
plFileName path = file.StripFileName(); |
||||
return !path.AsString().IsEmpty(); |
||||
} |
||||
|
||||
void plClientLauncher::LaunchClient() const |
||||
{ |
||||
if (fStatusFunc) |
||||
fStatusFunc("Launching..."); |
||||
fLaunchClientFunc(fClientExecutable, GetAppArgs()); |
||||
} |
||||
|
||||
void plClientLauncher::PatchClient() |
||||
{ |
||||
if (fStatusFunc) { |
||||
if (hsCheckBits(fFlags, kGameDataOnly)) |
||||
fStatusFunc("Verifying game data..."); |
||||
else |
||||
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; }); |
||||
patcher->OnRedistUpdate([&](const plFileName& file) { fInstallerThread->fRedistQueue.push_back(file); }); |
||||
|
||||
// If this is a repair, we need to approve the downloads...
|
||||
if (hsCheckBits(fFlags, kGameDataOnly)) |
||||
patcher->OnFileDownloadDesired(std::bind(&plClientLauncher::IApproveDownload, this, std::placeholders::_1)); |
||||
|
||||
// Let's get 'er done.
|
||||
if (hsCheckBits(fFlags, kHaveSelfPatched)) { |
||||
if (hsCheckBits(fFlags, kClientImage)) |
||||
patcher->RequestManifest(plManifest::ClientImageManifest()); |
||||
else |
||||
patcher->RequestManifest(plManifest::ClientManifest()); |
||||
} else |
||||
patcher->RequestManifest(plManifest::PatcherManifest()); |
||||
patcher->Start(); |
||||
} |
||||
|
||||
bool plClientLauncher::CompleteSelfPatch(std::function<void(void)> 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<plClientLauncher*>(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() |
||||
{ |
||||
#define APPLY_FLAG(arg, flag) \ |
||||
if (cmdParser.GetBool(arg)) \
|
||||
fFlags |= flag; |
||||
|
||||
enum { kArgServerIni, kArgNoSelfPatch, kArgImage, kArgRepairGame }; |
||||
const CmdArgDef cmdLineArgs[] = { |
||||
{ kCmdArgFlagged | kCmdTypeString, L"ServerIni", kArgServerIni }, |
||||
{ kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch }, |
||||
{ kCmdArgFlagged | kCmdTypeBool, L"Image", kArgImage }, |
||||
{ kCmdArgFlagged | kCmdTypeBool, L"Repair", kArgRepairGame }, |
||||
}; |
||||
|
||||
CCmdParser cmdParser(cmdLineArgs, arrsize(cmdLineArgs)); |
||||
cmdParser.Parse(); |
||||
|
||||
// cache 'em
|
||||
if (cmdParser.IsSpecified(kArgServerIni)) |
||||
fServerIni = plString::FromWchar(cmdParser.GetString(kArgServerIni)); |
||||
APPLY_FLAG(kArgNoSelfPatch, kHaveSelfPatched); |
||||
APPLY_FLAG(kArgImage, kClientImage); |
||||
APPLY_FLAG(kArgRepairGame, kRepairGame); |
||||
|
||||
// last chance setup
|
||||
if (hsCheckBits(fFlags, kRepairGame)) |
||||
fClientExecutable = plManifest::PatcherExecutable(); |
||||
|
||||
#undef APPLY_FLAG |
||||
} |
||||
|
||||
void plClientLauncher::SetErrorProc(ErrorFunc proc) |
||||
{ |
||||
s_errorProc = proc; |
||||
} |
||||
|
||||
void plClientLauncher::SetInstallerProc(InstallRedistFunc proc) |
||||
{ |
||||
fInstallerThread->fInstallProc = proc; |
||||
} |
||||
|
||||
void plClientLauncher::SetShardProc(StatusFunc proc) |
||||
{ |
||||
fStatusThread->fShardFunc = proc; |
||||
} |
@ -0,0 +1,165 @@
|
||||
/*==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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <functional> |
||||
#include <memory> |
||||
|
||||
class plClientLauncher |
||||
{ |
||||
public: |
||||
typedef std::function<class pfPatcher*(void)> CreatePatcherFunc; |
||||
typedef std::function<void(ENetError, const plString&)> ErrorFunc; |
||||
typedef std::function<bool(const plFileName&)> InstallRedistFunc; |
||||
typedef std::function<void(const plFileName&, const plString&)> LaunchClientFunc; |
||||
typedef std::function<void(const plString&)> StatusFunc; |
||||
|
||||
private: |
||||
enum Flags |
||||
{ |
||||
kHaveSelfPatched = 1<<0, |
||||
kClientImage = 1<<1, |
||||
kGameDataOnly = (1<<2), |
||||
|
||||
kRepairGame = kHaveSelfPatched | kClientImage | kGameDataOnly, |
||||
}; |
||||
|
||||
uint32_t fFlags; |
||||
plFileName fServerIni; |
||||
|
||||
plFileName fClientExecutable; |
||||
|
||||
std::unique_ptr<class plShardStatus> fStatusThread; |
||||
std::unique_ptr<class plRedistUpdater> fInstallerThread; |
||||
|
||||
CreatePatcherFunc fPatcherFactory; |
||||
LaunchClientFunc fLaunchClientFunc; |
||||
StatusFunc fStatusFunc; |
||||
|
||||
plString GetAppArgs() const; |
||||
|
||||
void IOnPatchComplete(ENetError result, const plString& msg); |
||||
bool IApproveDownload(const plFileName& file); |
||||
|
||||
public: |
||||
plClientLauncher(); |
||||
~plClientLauncher(); |
||||
|
||||
/** Launch whatever client we think is appropriate. Please note that you should not call this unless you know
|
||||
* absolutely without question what you are doing! |
||||
*/ |
||||
void LaunchClient() const; |
||||
|
||||
/** 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<void(void)> 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 callback that will execute and wait for redistributable installers.
|
||||
* \remarks This will be called from a worker thread. |
||||
*/ |
||||
void SetInstallerProc(InstallRedistFunc 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_
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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); |
@ -0,0 +1,428 @@
|
||||
/*==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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <commctrl.h> |
||||
#include <shellapi.h> |
||||
#include <shlobj.h> |
||||
|
||||
// ===================================================
|
||||
|
||||
#define PLASMA_PHAILURE 1 |
||||
#define PLASMA_OK 0 |
||||
|
||||
static HWND s_dialog; |
||||
static plString s_error; // This is highly unfortunate.
|
||||
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 IShowErrorDialog(const wchar_t* msg) |
||||
{ |
||||
// This bypasses all that hsClientMinimizeGuard crap we have in CoreLib.
|
||||
MessageBoxW(nullptr, msg, L"Error", MB_ICONERROR | MB_OK); |
||||
} |
||||
|
||||
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<WPARAM>(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); |
||||
s_dialog = nullptr; |
||||
break; |
||||
case WM_NCHITTEST: |
||||
SetWindowLongW(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); |
||||
return TRUE; |
||||
case WM_QUIT: |
||||
s_launcher.ShutdownNetCore(); |
||||
DestroyWindow(hwndDlg); |
||||
break; |
||||
} |
||||
|
||||
return DefWindowProcW(hwndDlg, uMsg, wParam, lParam); |
||||
} |
||||
|
||||
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<int32_t>(totalBytes)); |
||||
PostMessageW(progress, PBM_SETPOS, static_cast<int32_t>(curBytes), 0); |
||||
} |
||||
|
||||
// ===================================================
|
||||
|
||||
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 HANDLE ICreateProcess(const plFileName& exe, const plString& args) |
||||
{ |
||||
STARTUPINFOW si; |
||||
PROCESS_INFORMATION pi; |
||||
memset(&si, 0, sizeof(si)); |
||||
memset(&pi, 0, sizeof(pi)); |
||||
si.cb = sizeof(si); |
||||
|
||||
// Create wchar things and stuff :/
|
||||
plString cmd = plString::Format("%s %s", exe.AsString().c_str(), args.c_str()); |
||||
plStringBuffer<wchar_t> file = exe.AsString().ToWchar(); |
||||
plStringBuffer<wchar_t> params = cmd.ToWchar(); |
||||
|
||||
// Guess what? CreateProcess isn't smart enough to throw up an elevation dialog... We need ShellExecute for that.
|
||||
// But guess what? ShellExecute won't run ".exe.tmp" files. GAAAAAAAAHHHHHHHHH!!!!!!!
|
||||
BOOL result = CreateProcessW( |
||||
file, |
||||
const_cast<wchar_t*>(params.GetData()), |
||||
nullptr, |
||||
nullptr, |
||||
FALSE, |
||||
DETACHED_PROCESS, |
||||
nullptr, |
||||
nullptr, |
||||
&si, |
||||
&pi |
||||
); |
||||
|
||||
// So maybe it needs elevation... Or maybe everything arseploded.
|
||||
if (result != FALSE) { |
||||
CloseHandle(pi.hThread); |
||||
return pi.hProcess; |
||||
} else if (GetLastError() == ERROR_ELEVATION_REQUIRED) { |
||||
SHELLEXECUTEINFOW info; |
||||
memset(&info, 0, sizeof(info)); |
||||
info.cbSize = sizeof(info); |
||||
info.lpFile = file.GetData(); |
||||
info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC; |
||||
info.lpParameters = args.ToWchar(); |
||||
hsAssert(ShellExecuteExW(&info), "ShellExecuteExW phailed"); |
||||
|
||||
return info.hProcess; |
||||
} else { |
||||
wchar_t* msg = nullptr; |
||||
FormatMessageW( |
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
||||
nullptr, |
||||
GetLastError(), |
||||
LANG_USER_DEFAULT, |
||||
msg, |
||||
0, |
||||
nullptr |
||||
); |
||||
s_error = plString::FromWchar(msg); |
||||
LocalFree(msg); |
||||
} |
||||
|
||||
return nullptr; |
||||
} |
||||
|
||||
static bool IInstallRedist(const plFileName& exe) |
||||
{ |
||||
ISetDownloadStatus(plString::Format("Installing... %s", exe.AsString().c_str())); |
||||
Sleep(2500); // let's Sleep for a bit so the user can see that we're doing something before the UAC dialog pops up!
|
||||
|
||||
// Try to guess some arguments... Unfortunately, the file manifest format is fairly immutable.
|
||||
plStringStream ss; |
||||
if (exe.AsString().CompareI("oalinst.exe") == 0) |
||||
ss << "/s"; // rarg nonstandard
|
||||
else |
||||
ss << "/q"; |
||||
if (exe.AsString().Find("vcredist", plString::kCaseInsensitive) != -1) |
||||
ss << " /norestart"; // I don't want to image the accusations of viruses and hacking if this happened...
|
||||
|
||||
// Now fire up the process...
|
||||
HANDLE process = ICreateProcess(exe, ss.GetString()); |
||||
if (process) { |
||||
WaitForSingleObject(process, INFINITE); |
||||
|
||||
// Get the exit code so we can indicate success/failure to the redist thread
|
||||
DWORD code = PLASMA_OK; |
||||
hsAssert(GetExitCodeProcess(process, &code), "failed to get redist exit code"); |
||||
CloseHandle(process); |
||||
|
||||
return code != PLASMA_PHAILURE; |
||||
} |
||||
return PLASMA_PHAILURE; |
||||
} |
||||
|
||||
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()) { |
||||
HANDLE hEvent = CreateEventW(nullptr, TRUE, FALSE, L"UruPatcherEvent"); |
||||
HANDLE process = ICreateProcess(exe, args); |
||||
|
||||
// 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(process, 1000); |
||||
WaitForSingleObject(hEvent, INFINITE); |
||||
} |
||||
|
||||
CloseHandle(process); |
||||
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); |
||||
|
||||
s_error = plString::Format("Error: %S\r\n%s", NetErrorAsString(result), msg.c_str()); |
||||
IQuit(PLASMA_PHAILURE); |
||||
} |
||||
|
||||
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.SetInstallerProc(IInstallRedist); |
||||
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()) { |
||||
IShowErrorDialog(L"No server.ini file found. Please check your URU installation."); |
||||
return PLASMA_PHAILURE; |
||||
} |
||||
|
||||
// Ensure there is only ever one patcher running...
|
||||
if (IsPatcherRunning()) { |
||||
plString text = plString::Format("%s is already running", plProduct::LongName().c_str()); |
||||
IShowErrorDialog(text.ToWchar()); |
||||
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(); |
||||
|
||||
// So there appears to be some sort of issue with calling MessageBox once we've set up our dialog...
|
||||
// WTF?!?! So, to hack around that, we'll wait until everything shuts down to display any error.
|
||||
if (!s_error.IsEmpty()) |
||||
IShowErrorDialog(s_error.ToWchar()); |
||||
|
||||
// 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 s_error.IsEmpty() ? PLASMA_OK : PLASMA_PHAILURE; |
||||
} |
||||
|
||||
/* 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='*'\"") |
Loading…
Reference in new issue