feat: add updater

This commit is contained in:
2021-11-13 16:06:48 +01:00
parent 67a4ec511e
commit fc0cab54d2
7 changed files with 289 additions and 0 deletions

37
include/misc/Updater.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include "misc/DataBuffer.h"
namespace td {
namespace utils {
class Updater {
private:
float m_Progress;
bool m_DownloadComplete;
bool m_FileWrited;
bool m_CancelDownload;
DataBuffer m_FileBuffer;
public:
Updater() : m_Progress(0), m_DownloadComplete(false), m_FileWrited(false), m_CancelDownload(false) {}
bool checkUpdate();
void downloadUpdate();
void cancelDownload() { m_CancelDownload = true; m_Progress = 0.0f; m_DownloadComplete = false; }
bool writeFile();
void clearCache() { m_FileBuffer.Clear(); }
float getDownloadProgress() { return m_Progress; }
bool isDownloadComplete() { return m_DownloadComplete; }
bool isFileWrited() { return m_FileWrited; }
static std::string getLocalFilePath();
static void removeOldFile();
private:
std::string getDownloadFileURL();
std::uint32_t getLocalFileSize();
};
} // namespace utils
} // namespace td

View File

@@ -41,6 +41,7 @@ private:
std::unique_ptr<td::gui::MainMenu> m_MainMenu; std::unique_ptr<td::gui::MainMenu> m_MainMenu;
std::unique_ptr<td::gui::GameMenu> m_GameMenu; std::unique_ptr<td::gui::GameMenu> m_GameMenu;
std::unique_ptr<td::gui::FrameMenu> m_FrameMenu; std::unique_ptr<td::gui::FrameMenu> m_FrameMenu;
std::unique_ptr<td::gui::UpdateMenu> m_UpdateMenu;
bool m_DemoOpened = false; bool m_DemoOpened = false;
public: public:

View File

@@ -0,0 +1,29 @@
#pragma once
#include "GuiWidget.h"
#include "misc/Updater.h"
#include <future>
namespace td {
namespace gui {
class UpdateMenu : public GuiWidget {
private:
bool m_Opened;
std::string m_Error;
utils::Updater m_Updater;
std::shared_future<bool> m_UpdateAvailable;
public:
UpdateMenu(client::Client* client);
virtual void render();
private:
void checkUpdates();
bool isUpdateChecked();
void renderErrorPopup();
};
} // namespace gui
} // namespace td

View File

@@ -7,12 +7,15 @@
//============================================================================ //============================================================================
#include "window/Display.h" #include "window/Display.h"
#include "misc/Updater.h"
#ifdef __ANDROID__ #ifdef __ANDROID__
extern "C" extern "C"
#endif #endif
int main(int argc, const char* args[]) { int main(int argc, const char* args[]) {
td::utils::Updater::removeOldFile();
Display::create(); Display::create();
while (!Display::isCloseRequested()) { while (!Display::isCloseRequested()) {
Display::pollEvents(); Display::pollEvents();

133
src/misc/Updater.cpp Normal file
View File

@@ -0,0 +1,133 @@
#include "misc/Updater.h"
#include "misc/Platform.h"
#include "misc/DataBuffer.h"
#include "network/HttpLib.h"
#ifdef _WIN32
#include <windows.h>
#include <cstdio>
#endif
namespace td {
namespace utils {
bool Updater::checkUpdate() {
std::string newFileUrl = getDownloadFileURL();
if (newFileUrl.empty()) return false;
httplib::Client client("thesims.freeboxos.fr", 30000);
if (auto res = client.Head(newFileUrl.c_str())) {
std::uint32_t distantFileSize = std::stol(res.value().get_header_value("Content-Length"));
std::uint32_t localFileSize = getLocalFileSize();
if (distantFileSize != localFileSize) {
return true;
}
return false;
} else {
std::cerr << "Error : " << res.error() << std::endl;
return false;
}
}
std::uint32_t Updater::getLocalFileSize() {
std::string localFile = getLocalFilePath();
if (localFile.empty()) return 0;
std::ifstream in(localFile, std::ifstream::ate | std::ifstream::binary);
return in.tellg();
}
std::string Updater::getDownloadFileURL() {
Os systemOs = getSystemOs();
Architecture systemArch = getSystemArchitecture();
switch (systemOs) {
case Os::Windows: {
switch (systemArch) {
case Architecture::x86_64:
return "/Tower%20Defense/Tower%20Defense%20Windows%20(x86_64).exe";
case Architecture::x86:
return "/Tower%20Defense/Tower%20Defense%20Windows%20(x86).exe";
default:
return "";
}
}
default: // not supported on Android and Linux (package managers are better)
return "";
}
}
std::string Updater::getLocalFilePath() {
#ifdef _WIN32
char filePathBuffer[1024];
GetModuleFileName(nullptr, filePathBuffer, sizeof(filePathBuffer));
std::string filePath = filePathBuffer;
std::cout << filePath << std::endl;
return filePath;
#endif
return "";
}
void Updater::downloadUpdate() {
if (m_DownloadComplete) return;
m_CancelDownload = false;
std::string newFileUrl = getDownloadFileURL();
httplib::Client client("thesims.freeboxos.fr", 30000);
httplib::ContentReceiver reciever = [&](const char* data, size_t data_length) {
std::string fileData(data, data_length);
m_FileBuffer << fileData;
return true;
};
httplib::Progress progress = [&](uint64_t len, uint64_t total) {
m_Progress = (float)len * 100.0f / total;
return !m_CancelDownload;
};
auto res = client.Get(newFileUrl.c_str(), reciever, progress);
if (!m_CancelDownload) {
m_DownloadComplete = true;
}
}
void Updater::removeOldFile(){
std::remove(std::string(getLocalFilePath() + "_old").c_str());
}
bool Updater::writeFile() {
if (m_FileWrited || !m_DownloadComplete) return false;
std::cout << m_FileBuffer.GetSize() << std::endl;
if(!m_FileBuffer.WriteFile(getLocalFilePath() + "_recent"))
return false;
#ifdef _WIN32
std::rename(getLocalFilePath().c_str(), std::string(getLocalFilePath() + "_old").c_str());
std::rename(std::string(getLocalFilePath() + "_recent").c_str(), getLocalFilePath().c_str());
#endif
m_FileWrited = true;
m_DownloadComplete = false;
clearCache();
return true;
}
} // namespace utils
} // namespace td

View File

@@ -11,6 +11,7 @@
#include "render/gui/MainMenu.h" #include "render/gui/MainMenu.h"
#include "render/gui/GameMenu.h" #include "render/gui/GameMenu.h"
#include "render/gui/FrameMenu.h" #include "render/gui/FrameMenu.h"
#include "render/gui/UpdateMenu.h"
#include "imgui/imgui_impl_opengl3.h" #include "imgui/imgui_impl_opengl3.h"
#include "imgui/imgui_impl_sdl.h" #include "imgui/imgui_impl_sdl.h"
@@ -24,6 +25,7 @@ void TowerGui::initWidgets() {
m_MainMenu = std::make_unique<td::gui::MainMenu>(m_Client.get()); m_MainMenu = std::make_unique<td::gui::MainMenu>(m_Client.get());
m_GameMenu = std::make_unique<td::gui::GameMenu>(m_Client.get()); m_GameMenu = std::make_unique<td::gui::GameMenu>(m_Client.get());
m_FrameMenu = std::make_unique<td::gui::FrameMenu>(m_Client.get()); m_FrameMenu = std::make_unique<td::gui::FrameMenu>(m_Client.get());
m_UpdateMenu = std::make_unique<td::gui::UpdateMenu>(m_Client.get());
} }
TowerGui::TowerGui(SDL_Window* sdl_window, SDL_GLContext glContext, td::render::Renderer* renderer) : m_Window(sdl_window), TowerGui::TowerGui(SDL_Window* sdl_window, SDL_GLContext glContext, td::render::Renderer* renderer) : m_Window(sdl_window),
@@ -74,7 +76,9 @@ void TowerGui::render() {
if (m_DemoOpened) if (m_DemoOpened)
ImGui::ShowDemoWindow(&m_DemoOpened); ImGui::ShowDemoWindow(&m_DemoOpened);
m_FrameMenu->render(); m_FrameMenu->render();
m_UpdateMenu->render();
endFrame(); endFrame();
} }

View File

@@ -0,0 +1,82 @@
#include "render/gui/UpdateMenu.h"
#include "render/gui/imgui/imgui.h"
#include <chrono>
namespace td {
namespace gui {
UpdateMenu::UpdateMenu(client::Client* client) : GuiWidget(client), m_Opened(true) {
checkUpdates();
}
void UpdateMenu::render() {
renderErrorPopup();
if (m_Opened) {
ImGui::Begin("Updater", &m_Opened);
if (isUpdateChecked()) {
bool updateAvailable = m_UpdateAvailable.get();
if (updateAvailable) {
if(m_Updater.isFileWrited()){
ImGui::Text("The update is now installed");
ImGui::Text("The game needs to be restarted");
}else if (m_Updater.isDownloadComplete()) {
ImGui::Text("Download done!");
if(ImGui::Button("Install")){
if(!m_Updater.writeFile()){
m_Error = "Failed to write file !\n";
ImGui::OpenPopup("UpdateError");
}
}
if(ImGui::Button("Cancel")){
m_Updater.cancelDownload();
m_Updater.clearCache();
}
} else {
if (m_Updater.getDownloadProgress() > 0) {
ImGui::Text("Downloading ...");
ImGui::ProgressBar(m_Updater.getDownloadProgress());
if (ImGui::Button("Cancel")) {
m_Updater.cancelDownload();
}
} else {
ImGui::Text("An update is available!");
if (ImGui::Button("Download")) {
m_Updater.downloadUpdate();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
m_Opened = false;
}
}
}
} else {
ImGui::Text("No update available!");
}
} else {
ImGui::Text("Checking updates ...");
}
ImGui::End();
}
}
void UpdateMenu::renderErrorPopup(){
if(ImGui::BeginPopup("UpdateError")){
ImGui::Text("Error : %s", m_Error.c_str());
ImGui::EndPopup();
}
}
bool UpdateMenu::isUpdateChecked() {
return m_UpdateAvailable.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}
void UpdateMenu::checkUpdates() {
m_UpdateAvailable = std::async(std::launch::async, [&]() { return m_Updater.checkUpdate();});
}
} // namespace gui
} // namespace td