mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Launcher UX Improvements
* Fix bug regarding searching for prerelease builds, prerelease was assumed to be always at the top but this isn't true. * Make the prerelease toggle button check for updates instantly. * Automatically launch the game after the update has finished.
This commit is contained in:
parent
633292a820
commit
a7277b47fc
@ -12,6 +12,7 @@
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier2/curlutils.h"
|
||||
#include "zip/src/ZipFile.h"
|
||||
#include "utility/vdf_parser.h"
|
||||
|
||||
#define WINDOW_SIZE_X 400
|
||||
#define WINDOW_SIZE_Y 224
|
||||
@ -29,6 +30,9 @@ CBaseSurface::CBaseSurface()
|
||||
this->SetMaximizeBox(false);
|
||||
this->SetBackColor(Drawing::Color(47, 54, 61));
|
||||
|
||||
this->Load += &OnLoad;
|
||||
this->FormClosing += &OnClose;
|
||||
|
||||
this->m_BaseGroup = new UIX::UIXGroupBox();
|
||||
this->m_ManageButton = new UIX::UIXButton();
|
||||
this->m_RepairButton = new UIX::UIXButton();
|
||||
@ -102,16 +106,13 @@ CBaseSurface::CBaseSurface()
|
||||
this->m_ExperimentalBuildsCheckbox->SetTabIndex(0);
|
||||
this->m_ExperimentalBuildsCheckbox->SetText(XorStr("Check for playtest/event updates"));
|
||||
this->m_ExperimentalBuildsCheckbox->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left);
|
||||
this->m_ExperimentalBuildsCheckbox->Click += OnExperimentalBuildsClick;
|
||||
|
||||
m_BaseGroup->AddControl(this->m_ExperimentalBuildsCheckbox);
|
||||
|
||||
// TODO: Use a toggle item instead; remove this field.
|
||||
m_bPartialInstall = true;
|
||||
m_bUpdateViewToggled = false;
|
||||
|
||||
Threading::Thread([this] {
|
||||
this->Frame();
|
||||
}).Start();
|
||||
}
|
||||
|
||||
void CBaseSurface::ToggleUpdateView(bool bValue)
|
||||
@ -119,7 +120,13 @@ void CBaseSurface::ToggleUpdateView(bool bValue)
|
||||
// Game must be installed before this can be called!!
|
||||
Assert(m_bIsInstalled);
|
||||
|
||||
this->m_ManageButton->SetText(bValue ? XorStr("Update Apex") : XorStr("Install Apex"));
|
||||
// Don't switch it to the same view, else we install the callbacks
|
||||
// twice and thus call them twice.
|
||||
if (bValue == m_bUpdateViewToggled)
|
||||
return;
|
||||
|
||||
m_bUpdateViewToggled = bValue;
|
||||
this->m_ManageButton->SetText(bValue ? XorStr("Update && Launch Apex") : XorStr("Launch Apex"));
|
||||
|
||||
if (bValue)
|
||||
{
|
||||
@ -131,8 +138,23 @@ void CBaseSurface::ToggleUpdateView(bool bValue)
|
||||
this->m_ManageButton->Click -= &OnUpdateClick;
|
||||
this->m_ManageButton->Click += &OnLaunchClick;
|
||||
}
|
||||
}
|
||||
|
||||
m_bUpdateViewToggled = bValue;
|
||||
bool CBaseSurface::CheckForUpdate()
|
||||
{
|
||||
printf("%s: runframe; interval=%f\n", __FUNCTION__, g_flUpdateCheckRate);
|
||||
const bool updateAvailable = SDKLauncher_CheckForUpdate(m_ExperimentalBuildsCheckbox->Checked());
|
||||
|
||||
if (m_bIsInstalled && updateAvailable)
|
||||
{
|
||||
printf("%s: found update; interval=%f\n", __FUNCTION__, g_flUpdateCheckRate);
|
||||
ToggleUpdateView(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ToggleUpdateView(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -142,9 +164,8 @@ void CBaseSurface::Frame()
|
||||
{
|
||||
printf("%s: runframe; interval=%f\n", __FUNCTION__, g_flUpdateCheckRate);
|
||||
|
||||
if (!m_bUpdateViewToggled && m_bIsInstalled && SDKLauncher_CheckForUpdate(m_ExperimentalBuildsCheckbox->Checked()))
|
||||
if (CheckForUpdate())
|
||||
{
|
||||
ToggleUpdateView(true);
|
||||
printf("%s: found update; interval=%f\n", __FUNCTION__, g_flUpdateCheckRate);
|
||||
}
|
||||
|
||||
@ -152,6 +173,124 @@ void CBaseSurface::Frame()
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: load callback
|
||||
// Input : *pSender -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseSurface::OnLoad(Forms::Control* pSender)
|
||||
{
|
||||
CBaseSurface* pBaseSurface = (CBaseSurface*)pSender->FindForm();
|
||||
pBaseSurface->LoadSettings();
|
||||
|
||||
Threading::Thread([pBaseSurface] {
|
||||
pBaseSurface->Frame();
|
||||
}).Start();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: close callback
|
||||
// Input : *pSender -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseSurface::OnClose(const std::unique_ptr<FormClosingEventArgs>& /*pEventArgs*/, Forms::Control* pSender)
|
||||
{
|
||||
((CBaseSurface*)pSender->FindForm())->SaveSettings();
|
||||
}
|
||||
|
||||
void CBaseSurface::LoadSettings()
|
||||
{
|
||||
const fs::path settingsPath(Format("platform/%s/%s", SDK_SYSTEM_CFG_PATH, LAUNCHER_SETTING_FILE));
|
||||
if (!fs::exists(settingsPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool success;
|
||||
std::ifstream fileStream(settingsPath, fstream::in);
|
||||
vdf::object vRoot = vdf::read(fileStream, &success);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
printf("%s: Failed to parse VDF file: '%s'\n", __FUNCTION__,
|
||||
settingsPath.u8string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string& attributeView = vRoot.attribs["version"];
|
||||
|
||||
int settingsVersion = atoi(attributeView.c_str());
|
||||
if (settingsVersion != SDK_LAUNCHER_VERSION)
|
||||
return;
|
||||
|
||||
vdf::object* pSubKey = vRoot.childs["vars"].get();
|
||||
if (!pSubKey)
|
||||
return;
|
||||
|
||||
attributeView = pSubKey->attribs["experimentalBuilds"];
|
||||
this->m_ExperimentalBuildsCheckbox->SetChecked(attributeView != "0");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
printf("%s: Exception while parsing VDF file: %s\n", __FUNCTION__, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: gets the control item value
|
||||
// Input : *pControl -
|
||||
//-----------------------------------------------------------------------------
|
||||
const char* GetControlValue(Forms::Control* pControl)
|
||||
{
|
||||
switch (pControl->GetType())
|
||||
{
|
||||
case Forms::ControlTypes::CheckBox:
|
||||
case Forms::ControlTypes::RadioButton:
|
||||
{
|
||||
return reinterpret_cast<UIX::UIXCheckBox*>(pControl)->Checked() ? "1" : "0";
|
||||
}
|
||||
default:
|
||||
{
|
||||
return pControl->Text().ToCString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CBaseSurface::SaveSettings()
|
||||
{
|
||||
const fs::path settingsPath(Format("platform/%s/%s", SDK_SYSTEM_CFG_PATH, LAUNCHER_SETTING_FILE));
|
||||
const fs::path parentPath = settingsPath.parent_path();
|
||||
|
||||
if (!fs::exists(parentPath) && !fs::create_directories(parentPath))
|
||||
{
|
||||
printf("%s: Failed to create directory: '%s'\n", __FUNCTION__,
|
||||
parentPath.relative_path().u8string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream fileStream(settingsPath, fstream::out);
|
||||
if (!fileStream)
|
||||
{
|
||||
printf("%s: Failed to create VDF file: '%s'\n", __FUNCTION__,
|
||||
settingsPath.u8string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
vdf::object vRoot;
|
||||
vRoot.set_name("LauncherSettings");
|
||||
vRoot.add_attribute("version", std::to_string(SDK_LAUNCHER_VERSION));
|
||||
|
||||
vdf::object* vVars = new vdf::object();
|
||||
vVars->set_name("vars");
|
||||
|
||||
// Game.
|
||||
vVars->add_attribute("experimentalBuilds", GetControlValue(this->m_ExperimentalBuildsCheckbox));
|
||||
vRoot.add_child(std::unique_ptr<vdf::object>(vVars));
|
||||
|
||||
vdf::write(fileStream, vRoot);
|
||||
}
|
||||
|
||||
void CBaseSurface::OnUpdateClick(Forms::Control* Sender)
|
||||
{
|
||||
//CBaseSurface* pSurf = (CBaseSurface*)Sender;
|
||||
@ -283,6 +422,12 @@ void CBaseSurface::OnAdvancedClick(Forms::Control* Sender)
|
||||
pAdvancedSurface->ShowDialog((Forms::Form*)Sender->FindForm());
|
||||
}
|
||||
|
||||
void CBaseSurface::OnExperimentalBuildsClick(Forms::Control* Sender)
|
||||
{
|
||||
CBaseSurface* pBaseSurface = reinterpret_cast<CBaseSurface*>(Sender->FindForm());
|
||||
pBaseSurface->CheckForUpdate();
|
||||
}
|
||||
|
||||
void CBaseSurface::OnSupportClick(Forms::Control* /*Sender*/)
|
||||
{
|
||||
ShellExecute(0, 0, XorStr("https://ko-fi.com/amos0"), 0, 0, SW_SHOW);
|
||||
|
@ -9,14 +9,18 @@ public:
|
||||
{};
|
||||
|
||||
void ToggleUpdateView(bool bValue);
|
||||
bool CheckForUpdate();
|
||||
|
||||
protected:
|
||||
static void OnLoad(Forms::Control* pSender);
|
||||
static void OnClose(const std::unique_ptr<FormClosingEventArgs>& pEventArgs, Forms::Control* pSender);
|
||||
|
||||
static void OnInstallClick(Forms::Control* Sender);
|
||||
static void OnUpdateClick(Forms::Control* Sender);
|
||||
static void OnLaunchClick(Forms::Control* Sender);
|
||||
|
||||
static void OnAdvancedClick(Forms::Control* Sender);
|
||||
|
||||
static void OnExperimentalBuildsClick(Forms::Control* Sender);
|
||||
|
||||
static void OnSupportClick(Forms::Control* Sender);
|
||||
static void OnJoinClick(Forms::Control* Sender);
|
||||
@ -25,6 +29,9 @@ protected:
|
||||
private:
|
||||
void Frame();
|
||||
|
||||
void LoadSettings();
|
||||
void SaveSettings();
|
||||
|
||||
enum class eMode
|
||||
{
|
||||
NONE = -1,
|
||||
|
@ -56,7 +56,12 @@ int CLauncher::HandleCommandLine(int argc, char* argv[])
|
||||
string arg = argv[i];
|
||||
eLaunchMode mode = eLaunchMode::LM_NONE;
|
||||
|
||||
if ((arg == "-developer") || (arg == "-dev"))
|
||||
if ((arg == "-launch"))
|
||||
{
|
||||
LaunchGameDefault();
|
||||
return -2;
|
||||
}
|
||||
else if ((arg == "-developer") || (arg == "-dev"))
|
||||
{
|
||||
mode = eLaunchMode::LM_GAME_DEV;
|
||||
}
|
||||
@ -326,6 +331,26 @@ bool CLauncher::LaunchProcess() const
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Purpose: launches the game with default parameters
|
||||
// Output : true on success, false otherwise
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
bool CLauncher::LaunchGameDefault() const
|
||||
{
|
||||
// Hack: free the console before launching since we only want it to be shown
|
||||
// for the command line interface.
|
||||
FreeConsole();
|
||||
|
||||
// !TODO: parameter building and settings loading should be its own class!!
|
||||
if (g_pLauncher->CreateLaunchContext(eLaunchMode::LM_CLIENT, 0, "", "startup_launcher.cfg"))
|
||||
{
|
||||
g_pLauncher->LaunchProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Purpose: Window enumerator callback.
|
||||
// Input : hwnd -
|
||||
@ -386,6 +411,17 @@ void LauncherLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context,
|
||||
}
|
||||
}
|
||||
|
||||
void RunGUI()
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
FreeConsole();
|
||||
#endif // NDEBUG
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
g_CoreMsgVCallback = LauncherLoggerSink;
|
||||
g_pLauncher->RunSurface();
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EntryPoint.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -400,21 +436,23 @@ int main(int argc, char* argv[]/*, char* envp[]*/)
|
||||
g_pLauncher->InitLogger();
|
||||
if (argc < 2)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
FreeConsole();
|
||||
#endif // NDEBUG
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
g_CoreMsgVCallback = LauncherLoggerSink;
|
||||
g_pLauncher->RunSurface();
|
||||
curl_global_cleanup();
|
||||
RunGUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
int results = g_pLauncher->HandleCommandLine(argc, argv);
|
||||
if (results != -1)
|
||||
if (results == -1)
|
||||
{
|
||||
return g_pLauncher->HandleInput();
|
||||
}
|
||||
else if (results == -2)
|
||||
{
|
||||
RunGUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
return results;
|
||||
|
||||
return g_pLauncher->HandleInput();
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -50,6 +50,7 @@ public:
|
||||
bool CreateLaunchContext(eLaunchMode lMode, uint64_t nProcessorAffinity = NULL, const char* szCommandLine = nullptr, const char* szConfig = nullptr);
|
||||
void SetupLaunchContext(const char* szConfig, const char* szGameDll, const char* szCommandLine);
|
||||
bool LaunchProcess() const;
|
||||
bool LaunchGameDefault() const;
|
||||
|
||||
CBaseSurface* GetMainSurface() const { return m_pSurface; }
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
#include "sdklauncher_utils.h"
|
||||
#include "windows/window.h"
|
||||
#include "tier0/binstream.h"
|
||||
#include "tier1/xorstr.h"
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier2/curlutils.h"
|
||||
#include "zip/src/ZipFile.h"
|
||||
#include <public/tier0/binstream.h>
|
||||
|
||||
bool g_bPartialInstall = false;
|
||||
//bool g_bExperimentalBuilds = false;
|
||||
@ -331,12 +331,25 @@ bool SDKLauncher_AcquireReleaseManifest(const char* url, string& responseMessage
|
||||
{
|
||||
auto& release = responseJson[i];
|
||||
|
||||
if (preRelease && release["prerelease"])
|
||||
if (preRelease)
|
||||
{
|
||||
outManifest = release;
|
||||
break;
|
||||
if (i == responseJson.size() - 1 && outManifest.empty())
|
||||
{
|
||||
// TODO: we probably do not want to take the first one, as it might not be ordered
|
||||
// needs to be confirmed!
|
||||
outManifest = responseJson[0]; // Just take the first one then.
|
||||
break;
|
||||
}
|
||||
|
||||
const bool isPreRelease = release["prerelease"].get<bool>();
|
||||
|
||||
if (isPreRelease)
|
||||
{
|
||||
outManifest = release;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!release["prerelease"])
|
||||
else
|
||||
{
|
||||
outManifest = release;
|
||||
break;
|
||||
|
@ -122,17 +122,28 @@ void MoveFiles(const char* pSourceDir, const char* pDestDir)
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: print version numbers, warnings, etc
|
||||
//-----------------------------------------------------------------------------
|
||||
void PrintHeader()
|
||||
{
|
||||
printf("********************************************************************************\n");
|
||||
printf("R5 updater [Version %s]\n", UPDATER_VERSION);
|
||||
printf("!!! DO NOT INTERRUPT THE APPLICATION WHILE THE UPDATE IS IN PROGRESS !!!\n");
|
||||
printf("********************************************************************************\n");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: entry point
|
||||
//-----------------------------------------------------------------------------
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("R5 updater [Version %s]\n", UPDATER_VERSION);
|
||||
PrintHeader();
|
||||
|
||||
// Make sure the caller passed in a process id.
|
||||
if (argc < 1)
|
||||
{
|
||||
printf("%s: Updater must be invoked with a ProcessID of the parent process!\n", "Error");
|
||||
printf("%s: Updater must be invoked with a Process ID of the parent process!\n", "Error");
|
||||
Sleep(UPDATER_SLEEP_TIME_BEFORE_EXIT);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
@ -199,7 +210,7 @@ int main(int argc, char** argv)
|
||||
|
||||
BOOL createResult = CreateProcessA(
|
||||
"launcher.exe", // lpApplicationName
|
||||
NULL, // lpCommandLine
|
||||
(LPSTR)"-launch", // lpCommandLine
|
||||
NULL, // lpProcessAttributes
|
||||
NULL, // lpThreadAttributes
|
||||
FALSE, // bInheritHandles
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef SDKUPDATER_H
|
||||
#define SDKUPDATER_H
|
||||
|
||||
#define UPDATER_VERSION "v0.2a"
|
||||
#define UPDATER_VERSION "v0.3a"
|
||||
#define UPDATER_SLEEP_TIME_BEFORE_EXIT 15000
|
||||
|
||||
#endif // SDKUPDATER_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user