mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-14 02:27:40 -04:00
Merge pull request #353 from Hoikas/new-launcher
Unified Patcher (Part 2: Nuke eap)
This commit is contained in:
@ -6,7 +6,6 @@ if(PLASMA_BUILD_LAUNCHER)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PLASMA_BUILD_TOOLS)
|
if(PLASMA_BUILD_TOOLS)
|
||||||
add_subdirectory(plClientPatcher)
|
|
||||||
add_subdirectory(plPythonPack)
|
add_subdirectory(plPythonPack)
|
||||||
add_subdirectory(plFileSecure)
|
add_subdirectory(plFileSecure)
|
||||||
add_subdirectory(plFileEncrypt)
|
add_subdirectory(plFileEncrypt)
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
};
|
|
@ -9,14 +9,12 @@ include_directories(${OPENSSL_INCLUDE_DIR})
|
|||||||
include_directories(${CURL_INCLUDE_DIR})
|
include_directories(${CURL_INCLUDE_DIR})
|
||||||
|
|
||||||
set(plUruLauncher_HEADERS
|
set(plUruLauncher_HEADERS
|
||||||
Intern.h
|
plClientLauncher.h
|
||||||
Pch.h
|
|
||||||
plLauncherInfo.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(plUruLauncher_SOURCES
|
set(plUruLauncher_SOURCES
|
||||||
Main.cpp
|
plClientLauncher.cpp
|
||||||
SelfPatcher.cpp
|
winmain.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(plUruLauncher_RESOURCES
|
set(plUruLauncher_RESOURCES
|
||||||
@ -34,12 +32,10 @@ if(PLASMA_EXTERNAL_RELEASE)
|
|||||||
endif(PLASMA_EXTERNAL_RELEASE)
|
endif(PLASMA_EXTERNAL_RELEASE)
|
||||||
target_link_libraries(plUruLauncher CoreLib)
|
target_link_libraries(plUruLauncher CoreLib)
|
||||||
target_link_libraries(plUruLauncher pfConsoleCore)
|
target_link_libraries(plUruLauncher pfConsoleCore)
|
||||||
|
target_link_libraries(plUruLauncher pfPatcher)
|
||||||
target_link_libraries(plUruLauncher plAudioCore)
|
target_link_libraries(plUruLauncher plAudioCore)
|
||||||
target_link_libraries(plUruLauncher plClientPatcher)
|
|
||||||
target_link_libraries(plUruLauncher plCompression)
|
target_link_libraries(plUruLauncher plCompression)
|
||||||
target_link_libraries(plUruLauncher plFile)
|
target_link_libraries(plUruLauncher plFile)
|
||||||
target_link_libraries(plUruLauncher plNetClient)
|
|
||||||
target_link_libraries(plUruLauncher plNetClientComm)
|
|
||||||
target_link_libraries(plUruLauncher plNetGameLib)
|
target_link_libraries(plUruLauncher plNetGameLib)
|
||||||
target_link_libraries(plUruLauncher plNetMessage)
|
target_link_libraries(plUruLauncher plNetMessage)
|
||||||
target_link_libraries(plUruLauncher plNetTransport)
|
target_link_libraries(plUruLauncher plNetTransport)
|
||||||
|
@ -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='*'\"")
|
|
468
Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp
Normal file
468
Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp
Normal file
@ -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;
|
||||||
|
}
|
165
Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h
Normal file
165
Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h
Normal file
@ -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);
|
|
@ -4,7 +4,16 @@
|
|||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#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
|
// English (U.S.) resources
|
||||||
@ -32,6 +41,12 @@ BEGIN
|
|||||||
"\0"
|
"\0"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
3 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
#endif // APSTUDIO_INVOKED
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +55,7 @@ END
|
|||||||
// Dialog
|
// Dialog
|
||||||
//
|
//
|
||||||
|
|
||||||
IDD_DIALOG DIALOGEX 0, 0, 301, 180
|
IDD_DIALOG DIALOGEX 0, 0, 301, 135
|
||||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS |
|
STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS |
|
||||||
DS_CENTER | WS_POPUP | WS_VISIBLE | WS_SYSMENU
|
DS_CENTER | WS_POPUP | WS_VISIBLE | WS_SYSMENU
|
||||||
EXSTYLE WS_EX_APPWINDOW
|
EXSTYLE WS_EX_APPWINDOW
|
||||||
@ -48,17 +63,16 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
|||||||
BEGIN
|
BEGIN
|
||||||
CONTROL 109,IDB_BITMAP,"Static",SS_BITMAP | SS_SUNKEN,7,7,288,36
|
CONTROL 109,IDB_BITMAP,"Static",SS_BITMAP | SS_SUNKEN,7,7,288,36
|
||||||
CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER | 0x1,7,
|
CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER | 0x1,7,
|
||||||
162,234,11
|
111,234,11
|
||||||
LTEXT "Static",IDC_TEXT,10,152,266,8
|
CONTROL "", IDC_MARQUEE, "msctls_progress32", WS_BORDER | 0x8, 7,
|
||||||
PUSHBUTTON "Cancel",IDCANCEL,243,162,51,11
|
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
|
LTEXT "Welcome to URU",IDC_STATUS_TEXT,19,57,266,17
|
||||||
GROUPBOX "",IDC_STATIC,7,46,287,49
|
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
|
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
|
END
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +89,7 @@ BEGIN
|
|||||||
LEFTMARGIN, 7
|
LEFTMARGIN, 7
|
||||||
RIGHTMARGIN, 294
|
RIGHTMARGIN, 294
|
||||||
TOPMARGIN, 7
|
TOPMARGIN, 7
|
||||||
BOTTOMMARGIN, 173
|
BOTTOMMARGIN, 135
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
#endif // APSTUDIO_INVOKED
|
#endif // APSTUDIO_INVOKED
|
||||||
|
@ -6,12 +6,15 @@
|
|||||||
#define IDB_BITMAP 109
|
#define IDB_BITMAP 109
|
||||||
#define IDI_ICON1 111
|
#define IDI_ICON1 111
|
||||||
#define IDC_PROGRESS 1003
|
#define IDC_PROGRESS 1003
|
||||||
|
#define IDC_MARQUEE 1004
|
||||||
#define IDC_TEXT 1006
|
#define IDC_TEXT 1006
|
||||||
#define IDC_STATUS_TEXT 1009
|
#define IDC_STATUS_TEXT 1009
|
||||||
#define IDC_TIMEREMAINING 1010
|
#define IDC_TIMEREMAINING 1010
|
||||||
#define IDC_BYTESREMAINING 1011
|
#define IDC_BYTESREMAINING 1011
|
||||||
#define IDC_PRODUCTSTRING 1012
|
#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
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
@ -19,7 +22,7 @@
|
|||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 112
|
#define _APS_NEXT_RESOURCE_VALUE 112
|
||||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1014
|
#define _APS_NEXT_CONTROL_VALUE 1016
|
||||||
#define _APS_NEXT_SYMED_VALUE 101
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
428
Sources/Plasma/Apps/plUruLauncher/winmain.cpp
Normal file
428
Sources/Plasma/Apps/plUruLauncher/winmain.cpp
Normal file
@ -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='*'\"")
|
@ -89,6 +89,13 @@ struct pfPatcherWorker : public hsThread
|
|||||||
|
|
||||||
// Any file
|
// Any file
|
||||||
kFlagZipped = 1<<3,
|
kFlagZipped = 1<<3,
|
||||||
|
|
||||||
|
// Executable flags
|
||||||
|
kRedistUpdate = 1<<4,
|
||||||
|
|
||||||
|
// Begin internal flags
|
||||||
|
kLastManifestFlag = 1<<5,
|
||||||
|
kSelfPatch = 1<<6,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::deque<Request> fRequests;
|
std::deque<Request> fRequests;
|
||||||
@ -100,9 +107,12 @@ struct pfPatcherWorker : public hsThread
|
|||||||
|
|
||||||
pfPatcher::CompletionFunc fOnComplete;
|
pfPatcher::CompletionFunc fOnComplete;
|
||||||
pfPatcher::FileDownloadFunc fFileBeginDownload;
|
pfPatcher::FileDownloadFunc fFileBeginDownload;
|
||||||
|
pfPatcher::FileDesiredFunc fFileDownloadDesired;
|
||||||
pfPatcher::FileDownloadFunc fFileDownloaded;
|
pfPatcher::FileDownloadFunc fFileDownloaded;
|
||||||
pfPatcher::GameCodeDiscoverFunc fGameCodeDiscovered;
|
pfPatcher::GameCodeDiscoverFunc fGameCodeDiscovered;
|
||||||
pfPatcher::ProgressTickFunc fProgressTick;
|
pfPatcher::ProgressTickFunc fProgressTick;
|
||||||
|
pfPatcher::FileDownloadFunc fRedistUpdateDownloaded;
|
||||||
|
pfPatcher::FileDownloadFunc fSelfPatch;
|
||||||
|
|
||||||
pfPatcher* fParent;
|
pfPatcher* fParent;
|
||||||
volatile bool fStarted;
|
volatile bool fStarted;
|
||||||
@ -199,6 +209,8 @@ public:
|
|||||||
|
|
||||||
void Begin() { fDLStartTime = hsTimer::GetSysSeconds(); }
|
void Begin() { fDLStartTime = hsTimer::GetSysSeconds(); }
|
||||||
plFileName GetFileName() const { return fFilename; }
|
plFileName GetFileName() const { return fFilename; }
|
||||||
|
bool IsRedistUpdate() const { return hsCheckBits(fFlags, pfPatcherWorker::kRedistUpdate); }
|
||||||
|
bool IsSelfPatch() const { return hsCheckBits(fFlags, pfPatcherWorker::kSelfPatch); }
|
||||||
void Unlink() const { plFileSystem::Unlink(fFilename); }
|
void Unlink() const { plFileSystem::Unlink(fFilename); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -306,6 +318,10 @@ static void IFileThingDownloadCB(ENetError result, void* param, const plFileName
|
|||||||
if (IS_NET_SUCCESS(result)) {
|
if (IS_NET_SUCCESS(result)) {
|
||||||
PatcherLogGreen("\tDownloaded File '%s'", stream->GetFileName().AsString().c_str());
|
PatcherLogGreen("\tDownloaded File '%s'", stream->GetFileName().AsString().c_str());
|
||||||
patcher->WhitelistFile(stream->GetFileName(), true);
|
patcher->WhitelistFile(stream->GetFileName(), true);
|
||||||
|
if (patcher->fSelfPatch && stream->IsSelfPatch())
|
||||||
|
patcher->fSelfPatch(stream->GetFileName());
|
||||||
|
if (patcher->fRedistUpdateDownloaded && stream->IsRedistUpdate())
|
||||||
|
patcher->fRedistUpdateDownloaded(stream->GetFileName());
|
||||||
patcher->IssueRequest();
|
patcher->IssueRequest();
|
||||||
} else {
|
} else {
|
||||||
PatcherLogRed("\tDownloaded Failed: File '%s'", stream->GetFileName().AsString().c_str());
|
PatcherLogRed("\tDownloaded Failed: File '%s'", stream->GetFileName().AsString().c_str());
|
||||||
@ -454,7 +470,7 @@ hsError pfPatcherWorker::Run()
|
|||||||
void pfPatcherWorker::ProcessFile()
|
void pfPatcherWorker::ProcessFile()
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
const NetCliFileManifestEntry& entry = fQueuedFiles.front();
|
NetCliFileManifestEntry& entry = fQueuedFiles.front();
|
||||||
|
|
||||||
// eap sucks
|
// eap sucks
|
||||||
plFileName clName = plString::FromWchar(entry.clientName);
|
plFileName clName = plString::FromWchar(entry.clientName);
|
||||||
@ -474,10 +490,28 @@ void pfPatcherWorker::ProcessFile()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you got here, they're different.
|
// It's different... but do we want it?
|
||||||
|
if (fFileDownloadDesired) {
|
||||||
|
if (!fFileDownloadDesired(clName)) {
|
||||||
|
PatcherLogRed("\tDeclined '%S'", entry.clientName);
|
||||||
|
fQueuedFiles.pop_front();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you got here, they're different and we want it.
|
||||||
PatcherLogYellow("\tEnqueuing '%S'", entry.clientName);
|
PatcherLogYellow("\tEnqueuing '%S'", entry.clientName);
|
||||||
plFileSystem::CreateDir(plFileName(clName).StripFileName());
|
plFileSystem::CreateDir(plFileName(clName).StripFileName());
|
||||||
|
|
||||||
|
// If someone registered for SelfPatch notifications, then we should probably
|
||||||
|
// let them handle the gruntwork... Otherwise, go nuts!
|
||||||
|
if (fSelfPatch) {
|
||||||
|
if (clName == plFileSystem::GetCurrentAppPath().GetFileName()) {
|
||||||
|
clName += ".tmp"; // don't overwrite myself!
|
||||||
|
entry.flags |= kSelfPatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pfPatcherStream* s = new pfPatcherStream(this, dlName, entry);
|
pfPatcherStream* s = new pfPatcherStream(this, dlName, entry);
|
||||||
s->Open(clName, "wb");
|
s->Open(clName, "wb");
|
||||||
|
|
||||||
@ -547,6 +581,11 @@ void pfPatcher::OnFileDownloadBegin(FileDownloadFunc cb)
|
|||||||
fWorker->fFileBeginDownload = cb;
|
fWorker->fFileBeginDownload = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pfPatcher::OnFileDownloadDesired(FileDesiredFunc cb)
|
||||||
|
{
|
||||||
|
fWorker->fFileDownloadDesired = cb;
|
||||||
|
}
|
||||||
|
|
||||||
void pfPatcher::OnFileDownloaded(FileDownloadFunc cb)
|
void pfPatcher::OnFileDownloaded(FileDownloadFunc cb)
|
||||||
{
|
{
|
||||||
fWorker->fFileDownloaded = cb;
|
fWorker->fFileDownloaded = cb;
|
||||||
@ -562,6 +601,16 @@ void pfPatcher::OnProgressTick(ProgressTickFunc cb)
|
|||||||
fWorker->fProgressTick = cb;
|
fWorker->fProgressTick = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pfPatcher::OnRedistUpdate(FileDownloadFunc cb)
|
||||||
|
{
|
||||||
|
fWorker->fRedistUpdateDownloaded = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pfPatcher::OnSelfPatch(FileDownloadFunc cb)
|
||||||
|
{
|
||||||
|
fWorker->fSelfPatch = cb;
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================
|
// ===================================================
|
||||||
|
|
||||||
void pfPatcher::RequestGameCode()
|
void pfPatcher::RequestGameCode()
|
||||||
|
@ -70,6 +70,9 @@ public:
|
|||||||
/** Represents a function that takes the status and an optional message on completion. */
|
/** Represents a function that takes the status and an optional message on completion. */
|
||||||
typedef std::function<void(ENetError, const plString&)> CompletionFunc;
|
typedef std::function<void(ENetError, const plString&)> CompletionFunc;
|
||||||
|
|
||||||
|
/** Represents a function that takes (const plFileName&) and approves it. */
|
||||||
|
typedef std::function<bool(const plFileName&)> FileDesiredFunc;
|
||||||
|
|
||||||
/** Represents a function that takes (const plFileName&) on an interesting file operation. */
|
/** Represents a function that takes (const plFileName&) on an interesting file operation. */
|
||||||
typedef std::function<void(const plFileName&)> FileDownloadFunc;
|
typedef std::function<void(const plFileName&)> FileDownloadFunc;
|
||||||
|
|
||||||
@ -95,6 +98,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void OnFileDownloadBegin(FileDownloadFunc cb);
|
void OnFileDownloadBegin(FileDownloadFunc cb);
|
||||||
|
|
||||||
|
/** Set a callback that will be fired when the patcher wants to download a file. You are
|
||||||
|
* given the ability to approve or veto the download. With great power comes great responsibility...
|
||||||
|
* \remarks This will be called from the patcher thread.
|
||||||
|
*/
|
||||||
|
void OnFileDownloadDesired(FileDesiredFunc cb);
|
||||||
|
|
||||||
/** Set a callback that will be fired when the patcher has finished downloading a file from the server.
|
/** Set a callback that will be fired when the patcher has finished downloading a file from the server.
|
||||||
* \remarks This will be called from the network thread.
|
* \remarks This will be called from the network thread.
|
||||||
*/
|
*/
|
||||||
@ -112,6 +121,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
void OnProgressTick(ProgressTickFunc cb);
|
void OnProgressTick(ProgressTickFunc cb);
|
||||||
|
|
||||||
|
/** Set a callback that will be fired when the patcher downloads an updated redistributable. Such as
|
||||||
|
* the Visual C++ runtime (vcredist_x86.exe). You are responsible for installing it.
|
||||||
|
* \remarks This will be called from the network thread.
|
||||||
|
*/
|
||||||
|
void OnRedistUpdate(FileDownloadFunc cb);
|
||||||
|
|
||||||
|
/** This is called when the current application has been updated. */
|
||||||
|
void OnSelfPatch(FileDownloadFunc cb);
|
||||||
|
|
||||||
void RequestGameCode();
|
void RequestGameCode();
|
||||||
void RequestManifest(const plString& mfs);
|
void RequestManifest(const plString& mfs);
|
||||||
void RequestManifest(const std::vector<plString>& mfs);
|
void RequestManifest(const std::vector<plString>& mfs);
|
||||||
|
Reference in New Issue
Block a user