feat: add updater
This commit is contained in:
37
include/misc/Updater.h
Normal file
37
include/misc/Updater.h
Normal 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
|
||||||
@@ -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:
|
||||||
|
|||||||
29
include/render/gui/UpdateMenu.h
Normal file
29
include/render/gui/UpdateMenu.h
Normal 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
|
||||||
@@ -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
133
src/misc/Updater.cpp
Normal 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
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/render/gui/UpdateMenu.cpp
Normal file
82
src/render/gui/UpdateMenu.cpp
Normal 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
|
||||||
Reference in New Issue
Block a user