Browse Source

Rewrite plUruLauncher

Welcome to the glorious new regime.
Adam Johnson 11 years ago
parent
commit
a231b4db9d
  1. 1
      Sources/Plasma/Apps/CMakeLists.txt
  2. 28
      Sources/Plasma/Apps/plClientPatcher/CMakeLists.txt
  3. 58
      Sources/Plasma/Apps/plClientPatcher/Intern.h
  4. 75
      Sources/Plasma/Apps/plClientPatcher/Pch.h
  5. 1020
      Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp
  6. 67
      Sources/Plasma/Apps/plClientPatcher/UruPlayer.h
  7. 65
      Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h
  8. 12
      Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt
  9. 61
      Sources/Plasma/Apps/plUruLauncher/Intern.h
  10. 884
      Sources/Plasma/Apps/plUruLauncher/Main.cpp
  11. 72
      Sources/Plasma/Apps/plUruLauncher/Pch.h
  12. 338
      Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp
  13. 351
      Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp
  14. 147
      Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h
  15. 102
      Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h
  16. 36
      Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc
  17. 7
      Sources/Plasma/Apps/plUruLauncher/resource.h
  18. 348
      Sources/Plasma/Apps/plUruLauncher/winmain.cpp

1
Sources/Plasma/Apps/CMakeLists.txt

@ -6,7 +6,6 @@ if(PLASMA_BUILD_LAUNCHER)
endif()
if(PLASMA_BUILD_TOOLS)
add_subdirectory(plClientPatcher)
add_subdirectory(plPythonPack)
add_subdirectory(plFileSecure)
add_subdirectory(plFileEncrypt)

28
Sources/Plasma/Apps/plClientPatcher/CMakeLists.txt

@ -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})

58
Sources/Plasma/Apps/plClientPatcher/Intern.h

@ -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
*
***/

75
Sources/Plasma/Apps/plClientPatcher/Pch.h

@ -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"

1020
Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp

File diff suppressed because it is too large Load Diff

67
Sources/Plasma/Apps/plClientPatcher/UruPlayer.h

@ -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;

65
Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h

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

12
Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt

@ -9,14 +9,12 @@ include_directories(${OPENSSL_INCLUDE_DIR})
include_directories(${CURL_INCLUDE_DIR})
set(plUruLauncher_HEADERS
Intern.h
Pch.h
plLauncherInfo.h
plClientLauncher.h
)
set(plUruLauncher_SOURCES
Main.cpp
SelfPatcher.cpp
plClientLauncher.cpp
winmain.cpp
)
set(plUruLauncher_RESOURCES
@ -34,12 +32,10 @@ if(PLASMA_EXTERNAL_RELEASE)
endif(PLASMA_EXTERNAL_RELEASE)
target_link_libraries(plUruLauncher CoreLib)
target_link_libraries(plUruLauncher pfConsoleCore)
target_link_libraries(plUruLauncher pfPatcher)
target_link_libraries(plUruLauncher plAudioCore)
target_link_libraries(plUruLauncher plClientPatcher)
target_link_libraries(plUruLauncher plCompression)
target_link_libraries(plUruLauncher plFile)
target_link_libraries(plUruLauncher plNetClient)
target_link_libraries(plUruLauncher plNetClientComm)
target_link_libraries(plUruLauncher plNetGameLib)
target_link_libraries(plUruLauncher plNetMessage)
target_link_libraries(plUruLauncher plNetTransport)

61
Sources/Plasma/Apps/plUruLauncher/Intern.h

@ -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);

884
Sources/Plasma/Apps/plUruLauncher/Main.cpp

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

72
Sources/Plasma/Apps/plUruLauncher/Pch.h

@ -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"

338
Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp

@ -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='*'\"")

351
Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp

@ -0,0 +1,351 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <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 <curl/curl.h>
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();
}
// ===================================================
plClientLauncher::plClientLauncher() :
fFlags(0),
fServerIni("server.ini"),
fPatcherFactory(nullptr),
fClientExecutable(plManifest::ClientExecutable()),
fStatusThread(new plShardStatus())
{
pfPatcher::GetLog()->AddLine(plProduct::ProductString().c_str());
}
plClientLauncher::~plClientLauncher() { }
// ===================================================
plString plClientLauncher::GetAppArgs() const
{
plStringStream ss;
ss << "-ServerIni=";
ss << fServerIni.AsString();
return ss.GetString();
}
void plClientLauncher::IOnPatchComplete(ENetError result, const plString& msg)
{
if (IS_NET_SUCCESS(result)) {
// a couple of options
// 1. we self-patched and didn't update anything. patch the main client.
// 2. we self-patched and did things and stuff... re-run myself.
// 3. we patched the client... run it.
if (!hsCheckBits(fFlags, kHaveSelfPatched) && (fClientExecutable == plManifest::ClientExecutable())) {
// case 1
hsSetBits(fFlags, kHaveSelfPatched);
PatchClient();
} else
// cases 2 & 3
fLaunchClientFunc(fClientExecutable, GetAppArgs());
} else if (s_errorProc)
s_errorProc(result, msg);
}
void plClientLauncher::PatchClient()
{
if (fStatusFunc)
fStatusFunc("Checking for updates...");
hsAssert(fPatcherFactory, "why is the patcher factory nil?");
pfPatcher* patcher = fPatcherFactory();
patcher->OnCompletion(std::bind(&plClientLauncher::IOnPatchComplete, this, std::placeholders::_1, std::placeholders::_2));
patcher->OnSelfPatch([&](const plFileName& file) { fClientExecutable = file; });
if (hsCheckBits(fFlags, kHaveSelfPatched))
patcher->RequestManifest(plManifest::ClientManifest());
else
patcher->RequestManifest(plManifest::PatcherManifest());
patcher->Start();
}
bool plClientLauncher::CompleteSelfPatch(std::function<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()
{
enum { kArgServerIni, kArgNoSelfPatch };
const CmdArgDef cmdLineArgs[] = {
{ kCmdArgFlagged | kCmdTypeString, L"ServerIni", kArgServerIni },
{ kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch },
};
CCmdParser cmdParser(cmdLineArgs, arrsize(cmdLineArgs));
cmdParser.Parse();
// cache 'em
if (cmdParser.GetBool(kArgNoSelfPatch))
hsSetBits(fFlags, kHaveSelfPatched);
if (cmdParser.IsSpecified(kArgServerIni))
fServerIni = plString::FromWchar(cmdParser.GetString(kArgServerIni));
}
void plClientLauncher::SetErrorProc(ErrorFunc proc)
{
s_errorProc = proc;
}
void plClientLauncher::SetShardProc(StatusFunc proc)
{
fStatusThread->fShardFunc = proc;
}

147
Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h

@ -0,0 +1,147 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <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<void(const plFileName&, const plString&)> LaunchClientFunc;
typedef std::function<void(const plString&)> StatusFunc;
private:
enum Flags
{
kHaveSelfPatched = 1<<0,
};
uint32_t fFlags;
plFileName fServerIni;
plFileName fClientExecutable;
std::unique_ptr<class plShardStatus> fStatusThread;
CreatePatcherFunc fPatcherFactory;
LaunchClientFunc fLaunchClientFunc;
StatusFunc fStatusFunc;
plString GetAppArgs() const;
void IOnPatchComplete(ENetError result, const plString& msg);
public:
plClientLauncher();
~plClientLauncher();
/** Begin the next logical patch operation. We are internally tracking if this is a self patch or a client patch.
* All you need to do is make certain the doggone callbacks are set so that your UI will update. In theory, you
* should never call this from your UI code since we manage this state for you.
*/
void PatchClient();
/** Attempt to complete a self-patch left in progress by an older launcher. Specifically, we want to rename
* the launcher to something sane (UruLauncher.exe.tmp -> UruLauncher.exe). If we complete a self-patch in
* here, then we need to relaunch ourselves so that the game client will look like what the server expects.
* \returns True if a self-patch was completed. False if not.
*/
bool CompleteSelfPatch(std::function<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 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_

102
Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h

@ -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);

36
Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc

@ -4,7 +4,16 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define IDC_STATIC (-1) // all static controls
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
@ -32,6 +41,12 @@ BEGIN
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
@ -40,7 +55,7 @@ END
// Dialog
//
IDD_DIALOG DIALOGEX 0, 0, 301, 180
IDD_DIALOG DIALOGEX 0, 0, 301, 135
STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS |
DS_CENTER | WS_POPUP | WS_VISIBLE | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
@ -48,17 +63,16 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL 109,IDB_BITMAP,"Static",SS_BITMAP | SS_SUNKEN,7,7,288,36
CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER | 0x1,7,
162,234,11
LTEXT "Static",IDC_TEXT,10,152,266,8
PUSHBUTTON "Cancel",IDCANCEL,243,162,51,11
111,234,11
CONTROL "", IDC_MARQUEE, "msctls_progress32", WS_BORDER | 0x8, 7,
111, 234, 11
LTEXT "Static",IDC_TEXT,10,101,266,8
PUSHBUTTON "Cancel",IDCANCEL,243,111,51,11
LTEXT "Welcome to URU",IDC_STATUS_TEXT,19,57,266,17
GROUPBOX "",IDC_STATIC,7,46,287,49
GROUPBOX "Status",IDC_STATIC,7,103,287,44
LTEXT "Approx. Time Remaining:",IDC_STATIC,19,117,88,11
LTEXT "Update Remaining:",IDC_STATIC,20,130,73,9
LTEXT "...",IDC_TIMEREMAINING,111,117,165,12
LTEXT "...",IDC_BYTESREMAINING,111,130,42,12
RTEXT "Product String",IDC_PRODUCTSTRING,19,85,272,8
LTEXT "6 MiB / 666 MiB",IDC_DLSIZE,11,124,110,8
LTEXT "128 KiB/s",IDC_DLSPEED,186,124,53,8,0,WS_EX_RIGHT
END
@ -75,7 +89,7 @@ BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 294
TOPMARGIN, 7
BOTTOMMARGIN, 173
BOTTOMMARGIN, 135
END
END
#endif // APSTUDIO_INVOKED

7
Sources/Plasma/Apps/plUruLauncher/resource.h

@ -6,12 +6,15 @@
#define IDB_BITMAP 109
#define IDI_ICON1 111
#define IDC_PROGRESS 1003
#define IDC_MARQUEE 1004
#define IDC_TEXT 1006
#define IDC_STATUS_TEXT 1009
#define IDC_TIMEREMAINING 1010
#define IDC_BYTESREMAINING 1011
#define IDC_PRODUCTSTRING 1012
#define IDC_FILESREMAINING 1013
#define IDC_DLSIZE 1014
#define IDC_DLSPEED 1015
#define IDC_STATIC -1
// Next default values for new objects
//
@ -19,7 +22,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 112
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1014
#define _APS_NEXT_CONTROL_VALUE 1016
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

348
Sources/Plasma/Apps/plUruLauncher/winmain.cpp

@ -0,0 +1,348 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <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 <shlobj.h>
// ===================================================
#define PLASMA_PHAILURE 1
#define PLASMA_OK 0
static HWND s_dialog;
static plClientLauncher s_launcher;
static UINT s_taskbarCreated = RegisterWindowMessageW(L"TaskbarButtonCreated");
static ITaskbarList3* s_taskbar = nullptr;
// ===================================================
/** Create a global patcher mutex that is backwards compatible with eap's */
static HANDLE CreatePatcherMutex()
{
return CreateMutexW(nullptr, FALSE, plManifest::PatcherExecutable().AsString().ToWchar());
}
static bool IsPatcherRunning()
{
HANDLE mut = CreatePatcherMutex();
return WaitForSingleObject(mut, 0) != WAIT_OBJECT_0;
}
static void WaitForOldPatcher()
{
HANDLE mut = CreatePatcherMutex();
WaitForSingleObject(mut, INFINITE);
}
// ===================================================
static inline void IQuit(int exitCode=PLASMA_OK)
{
// hey, guess what?
// PostQuitMessage doesn't work if you're not on the main thread...
PostMessageW(s_dialog, WM_QUIT, exitCode, 0);
}
static inline void IShowMarquee(bool marquee=true)
{
// NOTE: This is a HACK to workaround a bug that causes progress bars that were ever
// marquees to reanimate when changing the range or position
ShowWindow(GetDlgItem(s_dialog, IDC_MARQUEE), marquee ? SW_SHOW : SW_HIDE);
ShowWindow(GetDlgItem(s_dialog, IDC_PROGRESS), marquee ? SW_HIDE : SW_SHOW);
PostMessageW(GetDlgItem(s_dialog, IDC_MARQUEE), PBM_SETMARQUEE, static_cast<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);
break;
case WM_NCHITTEST:
SetWindowLongW(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION);
return TRUE;
case WM_QUIT:
s_launcher.ShutdownNetCore();
DestroyWindow(hwndDlg);
break;
default:
return DefWindowProcW(hwndDlg, uMsg, wParam, lParam);
}
return TRUE;
}
static void ShowPatcherDialog(HINSTANCE hInstance)
{
s_dialog = ::CreateDialogW(hInstance, MAKEINTRESOURCEW(IDD_DIALOG), nullptr, PatcherDialogProc);
SetDlgItemTextW(s_dialog, IDC_TEXT, L"Connecting...");
SetDlgItemTextW(s_dialog, IDC_PRODUCTSTRING, plProduct::ProductString().ToWchar());
SetDlgItemTextW(s_dialog, IDC_DLSIZE, L"");
SetDlgItemTextW(s_dialog, IDC_DLSPEED, L"");
IShowMarquee();
}
static void PumpMessages()
{
MSG msg;
do {
// Pump all Win32 messages
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) {
if (!IsDialogMessageW(s_dialog, &msg)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
// Now we need to pump the netcore while we have some spare time...
s_launcher.PumpNetCore();
} while (msg.message != WM_QUIT);
}
// ===================================================
static void IOnDownloadBegin(const plFileName& file)
{
plString msg = plString::Format("Downloading... %s", file.AsString().c_str());
SetDlgItemTextW(s_dialog, IDC_TEXT, msg.ToWchar());
}
static void IOnProgressTick(uint64_t curBytes, uint64_t totalBytes, const plString& status)
{
// Swap marquee/real progress
IShowMarquee(false);
// DL size
plString size = plString::Format("%s / %s", plFileSystem::ConvertFileSize(curBytes).c_str(),
plFileSystem::ConvertFileSize(totalBytes).c_str());
SetDlgItemTextW(s_dialog, IDC_DLSIZE, size.ToWchar());
// DL speed
SetDlgItemTextW(s_dialog, IDC_DLSPEED, status.ToWchar());
HWND progress = GetDlgItem(s_dialog, IDC_PROGRESS);
// hey look... ULONGLONG. that's exactly what we need >.<
if (s_taskbar)
s_taskbar->SetProgressValue(s_dialog, curBytes, totalBytes);
// Windows can only do signed 32-bit int progress bars.
// So, chop it into smaller chunks until we get something we can represent.
while (totalBytes > INT32_MAX) {
totalBytes /= 1024;
curBytes /= 1024;
}
PostMessageW(progress, PBM_SETRANGE32, 0, static_cast<int32_t>(totalBytes));
PostMessageW(progress, PBM_SETPOS, static_cast<int32_t>(curBytes), 0);
}
// ===================================================
static void ILaunchClientExecutable(const plFileName& exe, const plString& args)
{
// Once we start launching something, we no longer need to trumpet any taskbar status
if (s_taskbar)
s_taskbar->SetProgressState(s_dialog, TBPF_NOPROGRESS);
// Only launch a client executable if we're given one. If not, that's probably a cue that we're
// done with some service operation and need to go away.
if (!exe.AsString().IsEmpty()) {
STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.cb = sizeof(si);
// This event will prevent the game from restarting the patcher
HANDLE hEvent = CreateEventW(nullptr, TRUE, FALSE, L"UruPatcherEvent");
// Fire up ye olde new process
plString cmd = plString::Format("%s %s", exe.AsString().c_str(), args.c_str());
CreateProcessW(
exe.AsString().ToWchar(),
const_cast<wchar_t*>(cmd.ToWchar().GetData()), // windows claims that it may modify this... let's hope that doesn't happen.
nullptr,
nullptr,
FALSE,
DETACHED_PROCESS,
nullptr,
plFileSystem::GetCWD().AsString().ToWchar(),
&si,
&pi
);
// if this is the real game client, then we need to make sure it gets this event...
if (plManifest::ClientExecutable().AsString().CompareI(exe.AsString()) == 0) {
WaitForInputIdle(pi.hProcess, 1000);
WaitForSingleObject(hEvent, INFINITE);
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hEvent);
}
// time to hara-kiri...
IQuit();
}
static void IOnNetError(ENetError result, const plString& msg)
{
if (s_taskbar)
s_taskbar->SetProgressState(s_dialog, TBPF_ERROR);
plString text = plString::Format("Error: %S\r\n%s", NetErrorAsString(result), msg.c_str());
hsMessageBox(text.c_str(), "Error", hsMessageBoxNormal);
IQuit(PLASMA_PHAILURE);
}
static void ISetDownloadStatus(const plString& status)
{
SetDlgItemTextW(s_dialog, IDC_TEXT, status.ToWchar());
// consider this a reset of the download status...
IShowMarquee();
SetDlgItemTextW(s_dialog, IDC_DLSIZE, L"");
SetDlgItemTextW(s_dialog, IDC_DLSPEED, L"");
if (s_taskbar)
s_taskbar->SetProgressState(s_dialog, TBPF_INDETERMINATE);
}
static void ISetShardStatus(const plString& status)
{
SetDlgItemTextW(s_dialog, IDC_STATUS_TEXT, status.ToWchar());
}
static pfPatcher* IPatcherFactory()
{
pfPatcher* patcher = new pfPatcher();
patcher->OnFileDownloadBegin(IOnDownloadBegin);
patcher->OnProgressTick(IOnProgressTick);
return patcher;
}
// ===================================================
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLink, int nCmdShow)
{
// Let's initialize our plClientLauncher friend
s_launcher.ParseArguments();
s_launcher.SetErrorProc(IOnNetError);
s_launcher.SetLaunchClientProc(ILaunchClientExecutable);
s_launcher.SetPatcherFactory(IPatcherFactory);
s_launcher.SetShardProc(ISetShardStatus);
s_launcher.SetStatusProc(ISetDownloadStatus);
// If we're newly updated, then our filename will be something we don't expect!
// Let's go ahead and take care of that nao.
if (s_launcher.CompleteSelfPatch(WaitForOldPatcher))
return PLASMA_OK; // see you on the other side...
// Load the doggone server.ini
if (!s_launcher.LoadServerIni()) {
hsMessageBox("No server.ini file found. Please check your URU installation.", "Error", hsMessageBoxNormal);
return PLASMA_PHAILURE;
}
// Ensure there is only ever one patcher running...
if (IsPatcherRunning()) {
hsMessageBox(plString::Format("%s is already running", plProduct::LongName().c_str()).c_str(), "Error",
hsMessageBoxNormal, hsMessageBoxIconError);
return PLASMA_OK;
}
HANDLE _onePatcherMut = CreatePatcherMutex();
// Initialize the network core
s_launcher.InitializeNetCore();
// Welp, now that we know we're (basically) sane, let's create our client window
// and pump window messages until we're through.
ShowPatcherDialog(hInstance);
PumpMessages();
// Alrighty now we just need to clean up behind ourselves!
// NOTE: We shut down the netcore in the WM_QUIT handler so
// we don't have a windowless, zombie process if that takes
// awhile (it can... dang eap...)
ReleaseMutex(_onePatcherMut);
CloseHandle(_onePatcherMut);
// kthxbai
return PLASMA_OK;
}
/* Enable themes in Windows XP and later */
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
Loading…
Cancel
Save