mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-08-15 15:58:04 +00:00
Refine updating (#201)
This PR makes the launcher update more reliable by downloading the update to a "new_BeamMP-Launcher.exe" file, instead of the launcher renaming itself to a backup (effectively bricking itself and the shortcut) and then hoping the download doesn't fail. --- By creating this pull request, I understand that code that is AI generated or otherwise automatically generated may be rejected without further discussion. I declare that I fully understand all code I pushed into this PR, and wrote all this code myself and own the rights to this code.
This commit is contained in:
commit
b6b0e4ba3e
@ -11,7 +11,7 @@
|
||||
#include <string>
|
||||
class HTTP {
|
||||
public:
|
||||
static bool Download(const std::string& IP, const beammp_fs_string& Path);
|
||||
static bool Download(const std::string& IP, const beammp_fs_string& Path, const std::string& Hash);
|
||||
static std::string Post(const std::string& IP, const std::string& Fields);
|
||||
static std::string Get(const std::string& IP);
|
||||
static bool ProgressBar(size_t c, size_t t);
|
||||
|
@ -29,7 +29,6 @@
|
||||
#endif
|
||||
|
||||
namespace Utils {
|
||||
|
||||
inline std::vector<std::string> Split(const std::string& String, const std::string& delimiter) {
|
||||
std::vector<std::string> Val;
|
||||
size_t pos;
|
||||
@ -166,7 +165,7 @@ namespace Utils {
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
inline std::wstring ToWString(const std::string& s) {
|
||||
inline std::wstring ToWString(const std::string& s) {
|
||||
if (s.empty()) return std::wstring();
|
||||
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0);
|
||||
@ -185,7 +184,7 @@ inline std::wstring ToWString(const std::string& s) {
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
inline std::string GetSha256HashReallyFast(const beammp_fs_string& filename) {
|
||||
inline std::string GetSha256HashReallyFastFile(const beammp_fs_string& filename) {
|
||||
try {
|
||||
EVP_MD_CTX* mdctx;
|
||||
const EVP_MD* md;
|
||||
@ -237,7 +236,51 @@ inline std::wstring ToWString(const std::string& s) {
|
||||
for (size_t i = 0; i < sha256_len; i++) {
|
||||
char buf[3];
|
||||
sprintf(buf, "%02x", sha256_value[i]);
|
||||
buf[2] = 0;
|
||||
buf[2] = NULL;
|
||||
result += buf;
|
||||
}
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
error(beammp_wide("Sha256 hashing of '") + filename + beammp_wide("' failed: ") + ToWString(e.what()));
|
||||
return "";
|
||||
}
|
||||
}
|
||||
inline std::string GetSha256HashReallyFast(const std::string& text, const beammp_fs_string& filename) {
|
||||
try {
|
||||
EVP_MD_CTX* mdctx;
|
||||
const EVP_MD* md;
|
||||
uint8_t sha256_value[EVP_MAX_MD_SIZE];
|
||||
md = EVP_sha256();
|
||||
if (md == nullptr) {
|
||||
throw std::runtime_error("EVP_sha256() failed");
|
||||
}
|
||||
|
||||
mdctx = EVP_MD_CTX_new();
|
||||
if (mdctx == nullptr) {
|
||||
throw std::runtime_error("EVP_MD_CTX_new() failed");
|
||||
}
|
||||
if (!EVP_DigestInit_ex2(mdctx, md, NULL)) {
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("EVP_DigestInit_ex2() failed");
|
||||
}
|
||||
|
||||
if (!EVP_DigestUpdate(mdctx, text.data(), text.size())) {
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("EVP_DigestUpdate() failed");
|
||||
}
|
||||
|
||||
unsigned int sha256_len = 0;
|
||||
if (!EVP_DigestFinal_ex(mdctx, sha256_value, &sha256_len)) {
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("EVP_DigestFinal_ex() failed");
|
||||
}
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
|
||||
std::string result;
|
||||
for (size_t i = 0; i < sha256_len; i++) {
|
||||
char buf[3];
|
||||
sprintf(buf, "%02x", sha256_value[i]);
|
||||
buf[2] = NULL;
|
||||
result += buf;
|
||||
}
|
||||
return result;
|
||||
|
@ -127,7 +127,7 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
|
||||
return Ret;
|
||||
}
|
||||
|
||||
bool HTTP::Download(const std::string& IP, const beammp_fs_string& Path) {
|
||||
bool HTTP::Download(const std::string& IP, const beammp_fs_string& Path, const std::string& Hash) {
|
||||
static std::mutex Lock;
|
||||
std::scoped_lock Guard(Lock);
|
||||
|
||||
@ -139,6 +139,16 @@ bool HTTP::Download(const std::string& IP, const beammp_fs_string& Path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string RetHash = Utils::GetSha256HashReallyFast(Ret, Path);
|
||||
|
||||
debug("Return hash: " + RetHash);
|
||||
debug("Expected hash: " + Hash);
|
||||
|
||||
if (RetHash != Hash) {
|
||||
error("Downloaded file hash does not match expected hash");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ofstream File(Path, std::ios::binary);
|
||||
if (File.is_open()) {
|
||||
File << Ret;
|
||||
|
@ -445,7 +445,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
||||
}
|
||||
auto FileName = std::filesystem::path(ModInfoIter->FileName).stem().string() + "-" + ModInfoIter->Hash.substr(0, 8) + std::filesystem::path(ModInfoIter->FileName).extension().string();
|
||||
auto PathToSaveTo = (CachingDirectory / FileName);
|
||||
if (fs::exists(PathToSaveTo) && Utils::GetSha256HashReallyFast(PathToSaveTo) == ModInfoIter->Hash) {
|
||||
if (fs::exists(PathToSaveTo) && Utils::GetSha256HashReallyFastFile(PathToSaveTo) == ModInfoIter->Hash) {
|
||||
debug("Mod '" + FileName + "' found in cache");
|
||||
UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
@ -476,7 +476,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
||||
WaitForConfirm();
|
||||
continue;
|
||||
} else if (auto OldCachedPath = CachingDirectory / std::filesystem::path(ModInfoIter->FileName).filename();
|
||||
fs::exists(OldCachedPath) && Utils::GetSha256HashReallyFast(OldCachedPath) == ModInfoIter->Hash) {
|
||||
fs::exists(OldCachedPath) && Utils::GetSha256HashReallyFastFile(OldCachedPath) == ModInfoIter->Hash) {
|
||||
debug("Mod '" + FileName + "' found in old cache, copying it to the new cache");
|
||||
UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
@ -562,7 +562,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
||||
Terminate = true;
|
||||
}
|
||||
|
||||
if (Utils::GetSha256HashReallyFast(PathToSaveTo) != ModInfoIter->Hash) {
|
||||
if (Utils::GetSha256HashReallyFastFile(PathToSaveTo) != ModInfoIter->Hash) {
|
||||
error(beammp_wide("Failed to write or download the entire file '") + beammp_fs_string(PathToSaveTo) + beammp_wide("' correctly (hash mismatch)"));
|
||||
Terminate = true;
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ void CheckForUpdates(const std::string& CV) {
|
||||
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
|
||||
beammp_fs_string EP(GetEP() + GetEN()), Back(GetEP() + beammp_wide("BeamMP-Launcher.back"));
|
||||
|
||||
std::string FileHash = Utils::GetSha256HashReallyFast(EP);
|
||||
std::string FileHash = Utils::GetSha256HashReallyFastFile(EP);
|
||||
|
||||
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
|
||||
if (!options.no_update) {
|
||||
@ -179,14 +179,21 @@ void CheckForUpdates(const std::string& CV) {
|
||||
#if defined(__linux__)
|
||||
error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches.");
|
||||
#else
|
||||
fs::remove(Back);
|
||||
fs::rename(EP, Back);
|
||||
info("Downloading Launcher update " + LatestHash);
|
||||
HTTP::Download(
|
||||
"https://backend.beammp.com/builds/launcher?download=true"
|
||||
"&pk="
|
||||
+ PublicKey + "&branch=" + Branch,
|
||||
EP);
|
||||
beammp_wide("new_") + EP, LatestHash);
|
||||
std::error_code ec;
|
||||
fs::remove(Back, ec);
|
||||
if (ec == std::errc::permission_denied) {
|
||||
error("Failed to remove old backup file: " + ec.message() + ". Using alternative name.");
|
||||
fs::rename(EP, Back + beammp_wide(".") + Utils::ToWString(FileHash.substr(0, 8)));
|
||||
} else {
|
||||
fs::rename(EP, Back);
|
||||
}
|
||||
fs::rename(beammp_wide("new_") + EP, EP);
|
||||
URelaunch();
|
||||
#endif
|
||||
} else {
|
||||
@ -329,14 +336,14 @@ void PreGame(const beammp_fs_string& GamePath) {
|
||||
std::string ZipPath(GetGamePath() / R"(mods/multiplayer/beammp.zip)");
|
||||
#endif
|
||||
|
||||
std::string FileHash = fs::exists(ZipPath) ? Utils::GetSha256HashReallyFast(ZipPath) : "";
|
||||
std::string FileHash = fs::exists(ZipPath) ? Utils::GetSha256HashReallyFastFile(ZipPath) : "";
|
||||
|
||||
if (FileHash != LatestHash) {
|
||||
info("Downloading BeamMP Update " + LatestHash);
|
||||
HTTP::Download("https://backend.beammp.com/builds/client?download=true"
|
||||
"&pk="
|
||||
+ PublicKey + "&branch=" + Branch,
|
||||
ZipPath);
|
||||
ZipPath, LatestHash);
|
||||
}
|
||||
|
||||
beammp_fs_string Target(GetGamePath() / beammp_wide("mods/unpacked/beammp"));
|
||||
|
Loading…
x
Reference in New Issue
Block a user