diff --git a/.gitignore b/.gitignore index 54d1f29..a4945d8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /Release/ /CCEngineLoader/Debug/ /CCEngineLoader/Release/ +/CCEngineLoader/GeneratedFiles/ diff --git a/CCEngine.sln b/CCEngine.sln index db0c499..e41e22c 100644 --- a/CCEngine.sln +++ b/CCEngine.sln @@ -5,14 +5,14 @@ VisualStudioVersion = 15.0.27130.2036 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CCEngine", "CCEngine\CCEngine.vcxproj", "{51637EB3-9942-43AE-9272-9DD85412EFC7}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CCEngineLoader", "CCEngineLoader\CCEngineLoader.vcxproj", "{450E76A9-E271-4349-9C06-3C0540F9B00F}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libLicenseClient", "..\libLicenseClient\libLicenseClient.vcxproj", "{51345E59-83E5-4389-93A9-0131B40522B7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libLicenseClientTest", "..\libLicenseClient\test\libLicenseClientTest.vcxproj", "{E781AE2E-4A02-4D63-9CAC-1AEBACD9CD73}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{44063F88-E25A-4C9D-AC11-9868D4CDD704}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CCEngineLoader", "CCEngineLoader\CCEngineLoader.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -29,14 +29,6 @@ Global {51637EB3-9942-43AE-9272-9DD85412EFC7}.Release|x64.Build.0 = Release|x64 {51637EB3-9942-43AE-9272-9DD85412EFC7}.Release|x86.ActiveCfg = Release|Win32 {51637EB3-9942-43AE-9272-9DD85412EFC7}.Release|x86.Build.0 = Release|Win32 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Debug|x64.ActiveCfg = Debug|x64 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Debug|x64.Build.0 = Debug|x64 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Debug|x86.ActiveCfg = Debug|Win32 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Debug|x86.Build.0 = Debug|Win32 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Release|x64.ActiveCfg = Release|x64 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Release|x64.Build.0 = Release|x64 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Release|x86.ActiveCfg = Release|Win32 - {450E76A9-E271-4349-9C06-3C0540F9B00F}.Release|x86.Build.0 = Release|Win32 {51345E59-83E5-4389-93A9-0131B40522B7}.Debug|x64.ActiveCfg = Debug|x64 {51345E59-83E5-4389-93A9-0131B40522B7}.Debug|x64.Build.0 = Debug|x64 {51345E59-83E5-4389-93A9-0131B40522B7}.Debug|x86.ActiveCfg = Debug|Win32 @@ -53,6 +45,12 @@ Global {E781AE2E-4A02-4D63-9CAC-1AEBACD9CD73}.Release|x64.Build.0 = Release|x64 {E781AE2E-4A02-4D63-9CAC-1AEBACD9CD73}.Release|x86.ActiveCfg = Release|Win32 {E781AE2E-4A02-4D63-9CAC-1AEBACD9CD73}.Release|x86.Build.0 = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x86.ActiveCfg = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x86.Build.0 = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.ActiveCfg = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CCEngine/src/CCServer.cpp b/CCEngine/src/CCServer.cpp index b4d0298..d3ecef8 100644 --- a/CCEngine/src/CCServer.cpp +++ b/CCEngine/src/CCServer.cpp @@ -767,7 +767,7 @@ public: void shutdown() { - std::lock_guard scopeGuard(m_lock); + std::lock_guard scopeGuard(m_mutex); m_ioc.stop(); for (auto& t : m_threads) { @@ -787,7 +787,7 @@ public: } private: - mutable std::mutex m_lock; + mutable std::mutex m_mutex; int m_maxThreads; std::string m_docRoot; etag_cache m_etagCache; diff --git a/CCEngineLoader/CCEngineLoader.cpp b/CCEngineLoader/CCEngineLoader.cpp index bdd6b2a..7936194 100644 Binary files a/CCEngineLoader/CCEngineLoader.cpp and b/CCEngineLoader/CCEngineLoader.cpp differ diff --git a/CCEngineLoader/CCEngineLoader.h b/CCEngineLoader/CCEngineLoader.h new file mode 100644 index 0000000..cabfdef --- /dev/null +++ b/CCEngineLoader/CCEngineLoader.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "ui_CCEngineLoader.h" + +#include "UpdateWorker.h" + +class CCEngineLoader : public QDialog +{ + Q_OBJECT + +public: + CCEngineLoader( + const boost::filesystem::path& appPath, + unsigned callerProcessPid, + QWidget *parent = Q_NULLPTR); + +private slots: + void onProgressChanged(QString info); + void onStartWorker(); + +private: + std::unique_ptr m_updateWorker; + +private: + Ui::CCEngineLoaderClass ui; +}; diff --git a/CCEngineLoader/CCEngineLoader.ico b/CCEngineLoader/CCEngineLoader.ico new file mode 100644 index 0000000..1c4fb80 Binary files /dev/null and b/CCEngineLoader/CCEngineLoader.ico differ diff --git a/CCEngineLoader/CCEngineLoader.qrc b/CCEngineLoader/CCEngineLoader.qrc new file mode 100644 index 0000000..81c2ca7 --- /dev/null +++ b/CCEngineLoader/CCEngineLoader.qrc @@ -0,0 +1,5 @@ + + + Resources/ajax-loader.gif + + diff --git a/CCEngineLoader/CCEngineLoader.rc b/CCEngineLoader/CCEngineLoader.rc new file mode 100644 index 0000000..62030b5 --- /dev/null +++ b/CCEngineLoader/CCEngineLoader.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "CCEngineLoader.ico" + diff --git a/CCEngineLoader/CCEngineLoader.ui b/CCEngineLoader/CCEngineLoader.ui new file mode 100644 index 0000000..fd639a5 --- /dev/null +++ b/CCEngineLoader/CCEngineLoader.ui @@ -0,0 +1,51 @@ + + + CCEngineLoaderClass + + + + 0 + 0 + 600 + 58 + + + + CCEngine Update Installer + + + + + 10 + 10 + 41 + 41 + + + + + + + :/CCEngineLoader/Resources/ajax-loader.gif + + + + + + 60 + 20 + 531 + 21 + + + + TextLabel + + + + + + + + + diff --git a/CCEngineLoader/CCEngineLoader.vcxproj b/CCEngineLoader/CCEngineLoader.vcxproj index d22b103..b74918f 100644 --- a/CCEngineLoader/CCEngineLoader.vcxproj +++ b/CCEngineLoader/CCEngineLoader.vcxproj @@ -1,4 +1,4 @@ - + @@ -9,20 +9,10 @@ Release Win32 - - Debug - x64 - - - Release - x64 - - 15.0 - {450E76A9-E271-4349-9C06-3C0540F9B00F} - Win32Proj - CCEngineLoader + {B12702AD-ABFB-343A-A199-8E24837244A3} + Qt4VSv1.0 10.0.16299.0 @@ -39,24 +29,54 @@ true MultiByte - - Application - true - v141 - MultiByte - - - Application - false - v141 - true - MultiByte - - - - + + + true + WIN32;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) + + MultiThreadedDLL + true + + + Windows + $(OutDir)\$(ProjectName).exe + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + false + qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies) + + + %(FullPath) + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + output + Moc'ing %(Identity)... + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName)\.;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(ProjectDir)..\..\libLicenseClient\api;$(ZLIB_ROOT_DIR)include;$(LIBZIP_ROOT_DIR)include;$(LIBCURL_ROOT_DIR)include;$(CRYPTOPP_ROOT_DIR)include;$(BOOST_ROOT_DIR)\. + CURL_STATICLIB;_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING;_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING;WIN32;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;_MBCS + $(QTDIR) + + + Uic'ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h + + + Rcc'ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp + + + + $(MSBuildProjectDirectory)\QtMsBuild + + + + + + + + + + @@ -73,112 +93,72 @@ - - - - - - - - true - - - true - - - false - - - false - - NotUsing - Level3 + WIN32;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) Disabled - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) + ProgramDatabase + MultiThreadedDebugDLL + true - Console + Windows + $(OutDir)\$(ProjectName).exe + $(QTDIR)\lib;%(AdditionalLibraryDirectories) true + qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;%(AdditionalDependencies) + + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + Moc'ing %(Identity)... + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName)\.;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(ProjectDir)..\..\libLicenseClient\api;$(ZLIB_ROOT_DIR)include;$(LIBZIP_ROOT_DIR)include;$(LIBCURL_ROOT_DIR)include;$(CRYPTOPP_ROOT_DIR)include;$(BOOST_ROOT_DIR)\. + WIN32;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;CURL_STATICLIB;_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING;_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING;_MBCS + + + Uic'ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h + + + Rcc'ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp + - - - NotUsing - Level3 - Disabled - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) - - - Console - true - - - - - NotUsing - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) - - - Console - true - true - true - - - - - NotUsing - Level3 - MaxSpeed - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) - - - Console - true - true - true - - - - - - - - Create - Create - Create - Create - + + + + + + + + + + + + + + {51345e59-83e5-4389-93a9-0131b40522b7} + + + + + + + + + + + \ No newline at end of file diff --git a/CCEngineLoader/CCEngineLoader.vcxproj.filters b/CCEngineLoader/CCEngineLoader.vcxproj.filters index 3720797..f2d42ac 100644 --- a/CCEngineLoader/CCEngineLoader.vcxproj.filters +++ b/CCEngineLoader/CCEngineLoader.vcxproj.filters @@ -10,24 +10,57 @@ h;hh;hpp;hxx;hm;inl;inc;xsd - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + moc;h;cpp + False - - Header Files - - - Header Files - - - - + Source Files Source Files + + Source Files + + + + + Header Files + + + + + Form Files + + + + + Resource Files + + + + + + + + Header Files + \ No newline at end of file diff --git a/CCEngineLoader/Resources/ajax-loader.gif b/CCEngineLoader/Resources/ajax-loader.gif new file mode 100644 index 0000000..e158443 Binary files /dev/null and b/CCEngineLoader/Resources/ajax-loader.gif differ diff --git a/CCEngineLoader/UpdateWorker.cpp b/CCEngineLoader/UpdateWorker.cpp new file mode 100644 index 0000000..af9bf61 --- /dev/null +++ b/CCEngineLoader/UpdateWorker.cpp @@ -0,0 +1,128 @@ +#include "UpdateWorker.h" + +#include "CachedDownloader.h" +#include "HTTPClient.h" +#include "JSONModuleDatabase.h" +#include "ModuleManager.h" + +#include + +#include +#include +#include +#include + + +namespace pt = boost::property_tree; +namespace fs = boost::filesystem; + + +namespace +{ + static const std::string moduleDatabaseFolder = ".inst"; + static const std::string updateCacheFolder = ".upd"; + static const std::string updateTrashFolder = ".upd/.remove"; + + void waitUntilProcessTerminates(unsigned long processID) + { + HANDLE hProcess = OpenProcess(SYNCHRONIZE, true, processID); + + if (NULL == hProcess) + { + WaitForSingleObject(hProcess, INFINITE); + } + } + + void runApp(const std::string& cmdLine, bool waitForProcessToExit) + { + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + // Start the child process. + if (!CreateProcess(NULL, // No module name (use command line) + (LPSTR)cmdLine.c_str(), // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + DETACHED_PROCESS, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi) // Pointer to PROCESS_INFORMATION structure + ) + { + printf("CreateProcess failed (%d).\n", GetLastError()); + return; + } + + if (waitForProcessToExit) + { + // Wait until child process exits. + WaitForSingleObject(pi.hProcess, INFINITE); + } + + // Close process and thread handles. + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + std::vector loadPendingUpdates(const boost::filesystem::path& appDir) + { + std::vector result; + + boost::filesystem::path path(appDir / updateCacheFolder / ".updates.json"); + if (boost::filesystem::is_regular_file(path)) + { + pt::ptree root; + pt::read_json(path.string(), root); + + deserialize(root, result); + } + + return result; + } +} + +UpdateWorkerThread::UpdateWorkerThread(const boost::filesystem::path& appDir, unsigned parentProcessId) + : m_appDir(appDir) + , m_parentProcessId(parentProcessId) +{} + +void UpdateWorkerThread::run() +{ + emit progressChanged(UpdateStatus::loadingUpdateDefinitions, QString("Loading update definitions...")); + std::vector moduleUpdates = loadPendingUpdates(m_appDir); + + HTTPClient httpClient; + CachedDownloader cachedDownloader(m_appDir / updateCacheFolder, httpClient); + JSONModuleDatabase moduleDatabase((m_appDir / moduleDatabaseFolder).string()); + ModuleManager moduleManager(m_appDir.string(), moduleDatabase, cachedDownloader, (m_appDir / updateTrashFolder).string()); + bool isRestartRequired = false; + for (const auto& update : moduleUpdates) + { + bool restartRequired = update.flag & static_cast(ModuleUpdateFlags::restartRequired); + isRestartRequired = isRestartRequired || restartRequired; + if (restartRequired && m_parentProcessId) + { + std::cout << "RestartRequired" << std::endl; + emit progressChanged(UpdateStatus::waitingForAppToExit, QString("Waiting for application to exit")); + waitUntilProcessTerminates(m_parentProcessId); + m_parentProcessId = 0; + } + emit progressChanged(UpdateStatus::installingUpdates, QString("Installing update %1...").arg(update.moduleId.c_str())); + moduleManager.applyUpdate(update.moduleId, update); + std::cout << "UpdateApplied " << update.moduleId << " " << update.version << std::endl; + } + + std::cout << "Done" << std::endl; + emit progressChanged(UpdateStatus::updatesInstalled, QString("Updated successfully")); + + if (isRestartRequired) + { + runApp((m_appDir / "CCEngine.exe").string() + " --updated", false); + } +} diff --git a/CCEngineLoader/UpdateWorker.h b/CCEngineLoader/UpdateWorker.h new file mode 100644 index 0000000..fd9db32 --- /dev/null +++ b/CCEngineLoader/UpdateWorker.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +enum class UpdateStatus +{ + loadingUpdateDefinitions, + installingUpdates, + waitingForAppToExit, + updatesInstalled, +}; + +class UpdateWorkerThread : public QThread +{ + Q_OBJECT + +public: + UpdateWorkerThread(const boost::filesystem::path& appDir, unsigned parentProcessId); + + void run() override; + +public: signals: + void progressChanged(UpdateStatus updateStatus, QString info); + +private: + boost::filesystem::path m_appDir; + unsigned m_parentProcessId; + +}; + diff --git a/CCEngineLoader/main.cpp b/CCEngineLoader/main.cpp new file mode 100644 index 0000000..7d757b2 --- /dev/null +++ b/CCEngineLoader/main.cpp @@ -0,0 +1,22 @@ +#include "CCEngineLoader.h" +#include + +#include +namespace fs = boost::filesystem; + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + auto appDir = fs::system_complete(a.arguments().at(0).toStdString()).parent_path(); + + unsigned long parentProcessId = 0; + if (a.arguments().size() >= 2) + { + parentProcessId = a.arguments().at(1).toInt(); + } + + CCEngineLoader w(appDir, parentProcessId); + w.show(); + return a.exec(); +}