Skip to content

Commit

Permalink
Add version check and automatic update
Browse files Browse the repository at this point in the history
  • Loading branch information
SirLynix committed Jan 23, 2024
1 parent 86a797e commit 632b4f9
Show file tree
Hide file tree
Showing 9 changed files with 506 additions and 6 deletions.
2 changes: 1 addition & 1 deletion include/CommonLib/Version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace tsom
TSOM_COMMONLIB_API extern std::uint32_t GameMinorVersion;
TSOM_COMMONLIB_API extern std::uint32_t GamePatchVersion;

TSOM_COMMONLIB_API extern std::string_view BuildConfig;
TSOM_COMMONLIB_API extern std::string_view BuildSystem;
TSOM_COMMONLIB_API extern std::string_view BuildBranch;
TSOM_COMMONLIB_API extern std::string_view BuildCommit;
Expand All @@ -35,7 +36,6 @@ namespace tsom
minorVersion = (version >> 12) & 0x3FF;
patchVersion = (version >> 0) & 0xFFF;
}

}

#include <CommonLib/Version.inl>
Expand Down
138 changes: 138 additions & 0 deletions src/Game/States/MenuState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,47 @@
#include <Game/States/MenuState.hpp>
#include <Game/States/ConnectionState.hpp>
#include <Game/States/GameState.hpp>
#include <Game/States/UpdateState.hpp>
#include <CommonLib/GameConstants.hpp>
#include <CommonLib/Version.hpp>
#include <Nazara/Core/ApplicationBase.hpp>
#include <Nazara/Core/StateMachine.hpp>
#include <Nazara/Network/Algorithm.hpp>
#include <Nazara/Network/IpAddress.hpp>
#include <Nazara/Network/Network.hpp>
#include <Nazara/Network/WebRequest.hpp>
#include <Nazara/Network/WebService.hpp>
#include <Nazara/Utility/SimpleTextDrawer.hpp>
#include <Nazara/Widgets.hpp>
#include <fmt/color.h>
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include <semver.hpp>
#include <optional>

namespace nlohmann
{
template<>
struct adl_serializer<tsom::UpdateInfo::DownloadInfo>
{
static void from_json(const nlohmann::json& json, tsom::UpdateInfo::DownloadInfo& downloadInfo)
{
downloadInfo.downloadUrl = json["download_url"];
downloadInfo.size = json["size"];
downloadInfo.sha256 = json.value("sha256", "");
}
};

template<>
struct adl_serializer<semver::version>
{
static void from_json(const nlohmann::json& json, semver::version& downloadInfo)
{
std::string_view versionStr = json;
downloadInfo.from_string(versionStr);
}
};
}

namespace tsom
{
Expand Down Expand Up @@ -52,30 +84,127 @@ namespace tsom

m_connectButton = m_layout->Add<Nz::ButtonWidget>();
m_connectButton->UpdateText(Nz::SimpleTextDrawer::Draw("Connect", 36, Nz::TextStyle_Regular, Nz::Color::sRGBToLinear(Nz::Color(0.13f))));
m_connectButton->SetMaximumWidth(m_connectButton->GetPreferredWidth() * 1.5f);
m_connectButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*)
{
OnConnectPressed();
});

m_updateLayout = CreateWidget<Nz::BoxLayout>(Nz::BoxLayoutOrientation::TopToBottom);

m_updateLabel = m_updateLayout->Add<Nz::LabelWidget>();
m_updateLabel->UpdateText(Nz::SimpleTextDrawer::Draw("A new version is available!", 18));

m_updateButton = m_updateLayout->Add<Nz::ButtonWidget>();
m_updateButton->UpdateText(Nz::SimpleTextDrawer::Draw("Update game", 18, Nz::TextStyle_Regular, Nz::Color::sRGBToLinear(Nz::Color(0.13f))));
m_updateButton->SetMaximumWidth(m_updateButton->GetPreferredWidth());
m_updateButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*)
{
OnUpdatePressed();
});

m_autoConnect = cmdParams.HasFlag("auto-connect");
}

void MenuState::Enter(Nz::StateMachine& fsm)
{
WidgetState::Enter(fsm);

CheckVersion();
}

bool MenuState::Update(Nz::StateMachine& fsm, Nz::Time elapsedTime)
{
if (m_nextState)
{
fsm.ChangeState(std::move(m_nextState));
return true;
}

if (m_autoConnect)
{
OnConnectPressed();
m_autoConnect = false;
}

if (m_webService)
m_webService->Poll();

return true;
}

void MenuState::CheckVersion()
{
if (!m_webService)
m_webService = Nz::Network::Instance()->InstantiateWebService();

std::string versionUrl = fmt::format("http://tsom-api.digitalpulse.software/game_version?platform={}", BuildConfig);

m_updateLayout->Hide();
m_newVersionInfo.reset();

auto request = m_webService->CreateGetRequest(versionUrl, [this](Nz::WebRequestResult&& result)
{
if (!result.HasSucceeded() || result.GetStatusCode() != 200)
{
fmt::print(fg(fmt::color::red), "failed to get version update\n");
return;
}

semver::version newAssetVersion;
semver::version newGameVersion;
UpdateInfo::DownloadInfo assetInfo;
UpdateInfo::DownloadInfo gameBinariesInfo;
UpdateInfo::DownloadInfo updaterInfo;

try
{
nlohmann::json json = nlohmann::json::parse(result.GetBody());

newAssetVersion = json["assets_version"];
newGameVersion = json["version"];
assetInfo = json["assets"];
gameBinariesInfo = json["binaries"];
updaterInfo = json["updater"];
}
catch (const std::exception& e)
{
fmt::print(fg(fmt::color::red), "failed to parse version data: {}\n", e.what());
return;
}

semver::version currentGameVersion(GameMajorVersion, GameMinorVersion, GamePatchVersion);

if (newGameVersion > currentGameVersion)
{
m_newVersionInfo.emplace(UpdateInfo{
.assets = std::move(assetInfo),
.version = newGameVersion.to_string(),
.binaries = std::move(gameBinariesInfo),
.updater = std::move(updaterInfo)
});

fmt::print(fg(fmt::color::yellow), "new version available: {}\n", m_newVersionInfo->version);
m_updateButton->UpdateText(Nz::SimpleTextDrawer::Draw("Update game to " + m_newVersionInfo->version, 18, Nz::TextStyle_Regular, Nz::Color::sRGBToLinear(Nz::Color(0.13f))));
m_updateButton->SetMaximumWidth(m_updateButton->GetPreferredWidth());

m_updateLayout->Show();
}
});

request->SetServiceName("TSOM Version Check");

m_webService->QueueRequest(std::move(request));
}

void MenuState::LayoutWidgets(const Nz::Vector2f& newSize)
{
m_layout->Resize({ newSize.x * 0.2f, m_layout->GetPreferredHeight() });
m_layout->CenterHorizontal();
m_layout->SetPosition(m_layout->GetPosition().x, newSize.y * 0.2f - m_layout->GetSize().y / 2.f);

m_updateLayout->Resize({ std::max(m_updateLabel->GetPreferredWidth(), m_updateButton->GetPreferredWidth()), m_updateLabel->GetPreferredHeight() * 2.f + m_updateButton->GetPreferredHeight() });
m_updateLayout->SetPosition(newSize * Nz::Vector2f(0.9f, 0.1f) - m_updateButton->GetSize() * 0.5f);
}

void MenuState::OnConnectPressed()
Expand Down Expand Up @@ -108,4 +237,13 @@ namespace tsom
if (auto connectionState = m_connectionState.lock())
connectionState->Connect(serverAddress, m_loginArea->GetText(), shared_from_this());
}

void MenuState::OnUpdatePressed()
{
if (!m_newVersionInfo)
return;

m_nextState = std::make_shared<UpdateState>(GetStateDataPtr(), shared_from_this(), m_webService, std::move(*m_newVersionInfo));
m_newVersionInfo.reset();
}
}
13 changes: 13 additions & 0 deletions src/Game/States/MenuState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
#define TSOM_CLIENT_STATES_MENUSTATE_HPP

#include <Game/States/WidgetState.hpp>
#include <Game/States/UpdateInfo.hpp>
#include <string>

namespace Nz
{
class BoxLayout;
class ButtonWidget;
class LabelWidget;
class TextAreaWidget;
class WebService;
}

namespace tsom
Expand All @@ -26,15 +30,24 @@ namespace tsom
MenuState(std::shared_ptr<StateData> stateData, std::shared_ptr<ConnectionState> connectionState);
~MenuState() = default;

void Enter(Nz::StateMachine& fsm) override;
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;

private:
void CheckVersion();
void LayoutWidgets(const Nz::Vector2f& newSize) override;
void OnConnectPressed();
void OnUpdatePressed();

std::optional<UpdateInfo> m_newVersionInfo;
std::shared_ptr<Nz::State> m_nextState;
std::shared_ptr<Nz::WebService> m_webService;
std::weak_ptr<ConnectionState> m_connectionState;
Nz::BoxLayout* m_layout;
Nz::BoxLayout* m_updateLayout;
Nz::ButtonWidget* m_connectButton;
Nz::ButtonWidget* m_updateButton;
Nz::LabelWidget* m_updateLabel;
Nz::TextAreaWidget* m_serverAddressArea;
Nz::TextAreaWidget* m_loginArea;
bool m_autoConnect;
Expand Down
31 changes: 31 additions & 0 deletions src/Game/States/UpdateInfo.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "This Space Of Mine" project
// For conditions of distribution and use, see copyright notice in Config.hpp

#pragma once

#ifndef TSOM_CLIENT_STATES_UPDATEINFO_HPP
#define TSOM_CLIENT_STATES_UPDATEINFO_HPP

#include <optional>
#include <string>

namespace tsom
{
struct UpdateInfo
{
struct DownloadInfo
{
std::string downloadUrl;
std::string sha256;
Nz::UInt64 size;
};

std::optional<DownloadInfo> assets;
std::string version;
DownloadInfo binaries;
DownloadInfo updater;
};
}

#endif // TSOM_CLIENT_STATES_UPDATEINFO_HPP
Loading

0 comments on commit 632b4f9

Please sign in to comment.