23 Commits
v2.5.1 ... 193

Author SHA1 Message Date
Tixx
db1aad37a8 Change and debug CreateProcessW params 2025-09-23 22:40:36 +02:00
Tixx
c03b1d5946 Bump version to v2.6.3 2025-09-17 09:20:21 +02:00
Tixx
5e73c7bce2 Check outdated registry entry for GameDir 2025-09-17 09:19:50 +02:00
Tixx
386f471362 Fix includes for linux build 2025-09-16 20:16:28 +02:00
Tixx
6c3bfda23b Bump version to v2.6.2 2025-09-16 20:09:56 +02:00
Tixx
5737e27bf3 Use ini instead of registry for game dir 2025-09-16 20:07:57 +02:00
Tixx
1860c0aef1 Fix userfolder on linux (hopefully) 2025-09-16 20:07:26 +02:00
Tixx
9d20b678f9 Bump version to v2.6.1 2025-09-16 17:53:29 +02:00
Tixx
be7594039e Fix updating logic 2025-09-16 17:52:30 +02:00
Tixx
77f3375658 Bump version to v2.6.0 2025-09-16 16:38:55 +02:00
Tixx
ae6e5b51bf User folder parsing updates (#208)
This PR adds support for the changes made to the BeamNG userfolder path
location.
1. Registry check for userfolder removed
2. New ini file is checked for userfolder
3. Ini parser now supports global keys

---

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.
2025-09-16 16:36:07 +02:00
Tixx
8a0f87f476 Remove registry check and add support for new ini file 2025-09-16 16:11:26 +02:00
Tixx
33b2030f97 Support global keys in ini 2025-09-16 16:10:16 +02:00
Tixx
5b2eb0a1a0 Change to execution paths to binary paths (#200)
Previously, it used argv[0] for the execution path, which is fine in
most cases, but in my case, it is not.
The thing is, I use NixOS, and I have created a Nix module for BeamMP.
When running BeamMP-Launcher, it tries to check for updates, and fails
because of the SHA hash.
There are other ways around this, such as setting the execution
directory, but I think this is a far clearer approach

---

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.
2025-08-02 19:38:18 +02:00
Vali0004
f27d3c6120 Fix bug with BP and not having a trailing slash by default 2025-08-02 00:21:40 -04:00
Vali0004
7a4b24d616 Change to canonical paths for executable paths 2025-08-02 00:14:37 -04:00
Tixx
b6b0e4ba3e 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.
2025-07-27 01:58:06 +02:00
Tixx
a87aa7230a Make null termination in hex encoding more clear 2025-07-27 01:07:23 +02:00
Tixx
b9cc025083 Check hashes after downloading 2025-07-25 23:50:16 +02:00
Tixx
c0ed056440 Fix for backup failing when it's in use
The backup file may still be in use by older launcher versions (v2.0.71) if they update
2025-07-17 14:33:25 +02:00
Tixx
e6e5bf8327 Fix launcher update to delete backup after download 2025-07-17 14:32:17 +02:00
Tixx
e7cfb6e406 Add Debian legacy Steam installation path (#198)
Current Steam installations seem to have a much cleaner filesystem
layout on Debian while older ones look quite different.

Older Debian installations ~/.steam/root points to ~/.steam while newer
ones point to ~/.steam/debian-installation and hence BeamMP cannot find
integrity.json. 'steamapps' is at ~/.steam/debian-installation in newer
installations.

On the older installations, 'steamapps' is at ~/.steam/steam/steamapps.

---

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.
2025-07-09 11:12:11 +02:00
Markus Ingalsuo
185818d174 Add Debian legacy Steam installation path
Current Steam installations seem to have a much cleaner filesystem
layout on Debian while older ones look quite different.

Older Debian installations ~/.steam/root points to ~/.steam while newer
ones point to ~/.steam/debian-installation and hence BeamMP cannot
find integrity.json. 'steamapps' is at ~/.steam/debian-installation in
newer installations.

On the older installations, 'steamapps' is at ~/.steam/steam/steamapps.

Signed-off-by: Markus Ingalsuo <markus.ingalsuo@gmail.com>
2025-07-09 11:34:45 +03:00
8 changed files with 241 additions and 75 deletions

View File

@@ -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);

View File

@@ -13,6 +13,7 @@
void InitLauncher();
beammp_fs_string GetEP(const beammp_fs_char* P = nullptr);
std::filesystem::path GetBP(const beammp_fs_char* P = nullptr);
std::filesystem::path GetGamePath();
std::string GetVer();
std::string GetPatch();

View File

@@ -14,6 +14,7 @@
#include <openssl/evp.h>
#include <regex>
#include <string>
#include <variant>
#include <vector>
#ifdef _WIN32
@@ -29,7 +30,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;
@@ -109,8 +109,8 @@ namespace Utils {
return result;
}
#endif
inline std::map<std::string, std::map<std::string, std::string>> ParseINI(const std::string& contents) {
std::map<std::string, std::map<std::string, std::string>> ini;
inline std::map<std::string, std::variant<std::map<std::string, std::string>, std::string>> ParseINI(const std::string& contents) {
std::map<std::string, std::variant<std::map<std::string, std::string>, std::string>> ini;
std::string currentSection;
auto sections = Split(contents, "\n");
@@ -137,18 +137,19 @@ namespace Utils {
if (line[0] == '[') {
currentSection = line.substr(1, line.find(']') - 1);
} else {
if (currentSection.empty()) {
invalidLineLog();
continue;
}
std::string key, value;
size_t pos = line.find('=');
if (pos != std::string::npos) {
key = line.substr(0, pos);
key = key.substr(0, key.find_last_not_of(" \t") + 1);
value = line.substr(pos + 1);
ini[currentSection][key] = value;
if (currentSection.empty()) {
ini[key] = value;
} else {
std::get<std::map<std::string, std::string>>(ini[currentSection])[key] = value;
}
} else {
invalidLineLog();
continue;
@@ -158,7 +159,7 @@ namespace Utils {
value.erase(0, value.find_first_not_of(" \t"));
value.erase(value.find_last_not_of(" \t") + 1);
ini[currentSection][key] = value;
std::get<std::map<std::string, std::string>>(ini[currentSection])[key] = value;
}
}
@@ -166,7 +167,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 +186,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 +238,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] = '\0';
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] = '\0';
result += buf;
}
return result;

View File

@@ -54,10 +54,10 @@ std::filesystem::path GetGamePath() {
debug("Successfully parsed startup.ini");
std::wstring userPath;
if (ini.contains("filesystem") && ini["filesystem"].contains("UserPath"))
userPath = Utils::ToWString(ini["filesystem"]["UserPath"]);
if (ini.contains("filesystem") && std::get<std::map<std::string, std::string>>(ini["filesystem"]).contains("UserPath"))
userPath = Utils::ToWString(std::get<std::map<std::string, std::string>>(ini["filesystem"])["UserPath"]);
if (!userPath.empty())
if (!userPath.empty() && Path.empty())
if (userPath = Utils::ExpandEnvVars(userPath); std::filesystem::exists(userPath)) {
Path = userPath;
debug(L"Using custom user folder path from startup.ini: " + Path.wstring());
@@ -66,32 +66,54 @@ std::filesystem::path GetGamePath() {
}
if (Path.empty()) {
HKEY hKey;
LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive";
LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey);
if (openRes != ERROR_SUCCESS) {
fatal("Please launch the game at least once!");
wchar_t* appDataPath = new wchar_t[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath);
if (!SUCCEEDED(result)) {
fatal("Cannot get Local Appdata directory");
}
auto BeamNGAppdataPath = std::filesystem::path(appDataPath) / "BeamNG";
if (const auto beamngIniPath = BeamNGAppdataPath / "BeamNG.Drive.ini"; exists(beamngIniPath)) {
if (std::ifstream beamngIni(beamngIniPath); beamngIni.is_open()) {
std::string contents((std::istreambuf_iterator(beamngIni)), std::istreambuf_iterator<char>());
beamngIni.close();
auto ini = Utils::ParseINI(contents);
if (ini.empty())
warn("Failed to parse BeamNG.Drive.ini");
else
debug("Successfully parsed BeamNG.Drive.ini");
std::wstring userPath;
if (ini.contains("userFolder")) {
userPath = Utils::ToWString(std::get<std::string>(ini["userFolder"]));
userPath.erase(0, userPath.find_first_not_of(L" \t"));
}
if (!userPath.empty() && Path.empty())
if (userPath = std::filesystem::path(Utils::ExpandEnvVars(userPath)); std::filesystem::exists(userPath)) {
Path = userPath;
debug(L"Using custom user folder path from BeamNG.Drive.ini: " + Path.wstring());
} else
warn(L"Found custom user folder path (" + userPath + L") in BeamNG.Drive.ini but it doesn't exist, skipping");
}
}
Path = QueryKey(hKey, 4);
if (Path.empty()) {
wchar_t* appDataPath = new wchar_t[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath);
if (!SUCCEEDED(result)) {
fatal("Cannot get Local Appdata directory");
}
Path = std::filesystem::path(appDataPath) / "BeamNG.drive";
delete[] appDataPath;
Path = BeamNGAppdataPath / "BeamNG.drive";
}
delete[] appDataPath;
}
}
std::string Ver = CheckVer(GetGameDir());
Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1));
Path /= Utils::ToWString(Ver);
Path /= Utils::ToWString("current");
return Path;
}
#elif defined(__linux__)
@@ -100,10 +122,10 @@ std::filesystem::path GetGamePath() {
struct passwd* pw = getpwuid(getuid());
std::string homeDir = pw->pw_dir;
std::string Path = homeDir + "/.local/share/BeamNG.drive/";
std::string Path = homeDir + "/.local/share/BeamNG/BeamNG.drive/";
std::string Ver = CheckVer(GetGameDir());
Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1));
Path += Ver + "/";
Path += "current/";
return Path;
}
#endif
@@ -126,7 +148,13 @@ void StartGame(std::wstring Dir) {
debug(L"BeamNG executable path: " + Dir);
bSuccess = CreateProcessW(nullptr, (wchar_t*)(Dir + gameArgs).c_str(), nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi);
wchar_t* lpApplicationName = Dir.data();
wchar_t* lpCommandLine = (Dir + gameArgs).data();
debug(L"lpApplicationName: " + std::wstring(lpApplicationName));
debug(L"lpCommandLine: " + std::wstring(lpCommandLine));
bSuccess = CreateProcessW(lpApplicationName, lpCommandLine, nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi);
if (bSuccess) {
info("Game Launched!");
GamePID = pi.dwProcessId;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -7,6 +7,7 @@
#include <filesystem>
#if defined(_WIN32)
#include <shlobj_core.h>
#elif defined(__linux__)
#include "vdf_parser.hpp"
#include <pwd.h>
@@ -162,24 +163,66 @@ void FileList(std::vector<std::string>& a, const std::string& Path) {
}
void LegitimacyCheck() {
#if defined(_WIN32)
std::wstring Result;
std::string K3 = R"(Software\BeamNG\BeamNG.drive)";
HKEY hKey;
LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey);
if (dwRegOPenKey == ERROR_SUCCESS) {
Result = QueryKey(hKey, 3);
if (Result.empty()) {
debug("Failed to QUERY key HKEY_CURRENT_USER\\Software\\BeamNG\\BeamNG.drive");
lowExit(3);
}
GameDir = Result;
} else {
debug("Failed to OPEN key HKEY_CURRENT_USER\\Software\\BeamNG\\BeamNG.drive");
lowExit(4);
wchar_t* appDataPath = new wchar_t[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath);
if (!SUCCEEDED(result)) {
fatal("Cannot get Local Appdata directory");
}
K3.clear();
Result.clear();
RegCloseKey(hKey);
auto BeamNGAppdataPath = std::filesystem::path(appDataPath) / "BeamNG";
if (const auto beamngIniPath = BeamNGAppdataPath / "BeamNG.Drive.ini"; exists(beamngIniPath)) {
if (std::ifstream beamngIni(beamngIniPath); beamngIni.is_open()) {
std::string contents((std::istreambuf_iterator(beamngIni)), std::istreambuf_iterator<char>());
beamngIni.close();
auto ini = Utils::ParseINI(contents);
if (ini.empty())
lowExit(3);
else
debug("Successfully parsed BeamNG.Drive.ini");
if (ini.contains("installPath")) {
std::wstring installPath = Utils::ToWString(std::get<std::string>(ini["installPath"]));
installPath.erase(0, installPath.find_first_not_of(L" \t"));
if (installPath = std::filesystem::path(Utils::ExpandEnvVars(installPath)); std::filesystem::exists(installPath)) {
GameDir = installPath;
debug(L"GameDir from BeamNG.Drive.ini: " + installPath);
} else {
lowExit(4);
}
} else {
lowExit(5);
}
}
} else {
std::wstring Result;
std::string K3 = R"(Software\BeamNG\BeamNG.drive)";
HKEY hKey;
LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey);
if (dwRegOPenKey == ERROR_SUCCESS) {
Result = QueryKey(hKey, 3);
if (Result.empty()) {
debug("Failed to QUERY key HKEY_CURRENT_USER\\Software\\BeamNG\\BeamNG.drive");
lowExit(6);
}
GameDir = Result;
debug(L"GameDir from registry: " + Result);
} else {
debug("Failed to OPEN key HKEY_CURRENT_USER\\Software\\BeamNG\\BeamNG.drive");
lowExit(7);
}
K3.clear();
Result.clear();
RegCloseKey(hKey);
}
delete[] appDataPath;
#elif defined(__linux__)
struct passwd* pw = getpwuid(getuid());
std::filesystem::path homeDir = pw->pw_dir;
@@ -187,6 +230,7 @@ void LegitimacyCheck() {
// Right now only steam is supported
std::vector<std::filesystem::path> steamappsCommonPaths = {
".steam/root/steamapps", // default
".steam/steam/steamapps", // Legacy Steam installations
".var/app/com.valvesoftware.Steam/.steam/root/steamapps", // flatpak
"snap/steam/common/.local/share/Steam/steamapps" // snap
};

View File

@@ -14,7 +14,10 @@
#if defined(_WIN32)
#elif defined(__linux__)
#include <unistd.h>
#endif
#elif defined (__APPLE__)
#include <unistd.h>
#include <libproc.h>
#endif // __APPLE__
#include "Http.h"
#include "Logger.h"
#include "Network/network.hpp"
@@ -81,10 +84,10 @@ beammp_fs_string GetEN() {
}
std::string GetVer() {
return "2.5";
return "2.6";
}
std::string GetPatch() {
return ".1";
return ".3";
}
beammp_fs_string GetEP(const beammp_fs_char* P) {
@@ -94,6 +97,34 @@ beammp_fs_string GetEP(const beammp_fs_char* P) {
}();
return Ret;
}
fs::path GetBP(const beammp_fs_char* P) {
fs::path fspath = {};
#if defined(_WIN32)
beammp_fs_char path[256];
GetModuleFileNameW(nullptr, path, sizeof(path));
fspath = path;
#elif defined(__linux__)
fspath = fs::canonical("/proc/self/exe");
#elif defined(__APPLE__)
pid_t pid = getpid();
char path[PROC_PIDPATHINFO_MAXSIZE];
// While this is fine for a raw executable,
// an application bundle is read-only and these files
// should instead be placed in Application Support.
proc_pidpath(pid, path, sizeof(path));
fspath = std::string(path);
#else
fspath = beammp_fs_string(P);
#endif
fspath = fs::weakly_canonical(fspath.string() + "/..");
#if defined(_WIN32)
return fspath.wstring();
#else
return fspath.string();
#endif
}
#if defined(_WIN32)
void ReLaunch() {
std::wstring Arg;
@@ -103,7 +134,7 @@ void ReLaunch() {
}
info("Relaunch!");
system("cls");
ShellExecuteW(nullptr, L"runas", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
ShellExecuteW(nullptr, L"runas", (GetBP() / GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
ShowWindow(GetConsoleWindow(), 0);
std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1);
@@ -114,7 +145,7 @@ void URelaunch() {
Arg += Utils::ToWString(options.argv[c - 1]);
Arg += L" ";
}
ShellExecuteW(nullptr, L"open", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
ShellExecuteW(nullptr, L"open", (GetBP() / GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
ShowWindow(GetConsoleWindow(), 0);
std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1);
@@ -128,7 +159,7 @@ void ReLaunch() {
}
info("Relaunch!");
system("clear");
int ret = execv(options.executable_name.c_str(), const_cast<char**>(options.argv));
int ret = execv((GetBP() / GetEN()).c_str(), const_cast<char**>(options.argv));
if (ret < 0) {
error(std::string("execv() failed with: ") + strerror(errno) + ". Failed to relaunch");
exit(1);
@@ -137,7 +168,7 @@ void ReLaunch() {
exit(1);
}
void URelaunch() {
int ret = execv(options.executable_name.c_str(), const_cast<char**>(options.argv));
int ret = execv((GetBP() / GetEN()).c_str(), const_cast<char**>(options.argv));
if (ret < 0) {
error(std::string("execv() failed with: ") + strerror(errno) + ". Failed to relaunch");
exit(1);
@@ -169,9 +200,9 @@ void CheckForUpdates(const std::string& CV) {
"https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey);
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
beammp_fs_string EP(GetEP() + GetEN()), Back(GetEP() + beammp_wide("BeamMP-Launcher.back"));
beammp_fs_string BP(GetBP() / GetEN()), Back(GetBP() / beammp_wide("BeamMP-Launcher.back"));
std::string FileHash = Utils::GetSha256HashReallyFast(EP);
std::string FileHash = Utils::GetSha256HashReallyFastFile(BP);
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
if (!options.no_update) {
@@ -179,14 +210,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);
GetBP() / (beammp_wide("new_") + GetEN()), 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(BP, Back + beammp_wide(".") + Utils::ToWString(FileHash.substr(0, 8)));
} else {
fs::rename(BP, Back);
}
fs::rename(GetBP() / (beammp_wide("new_") + GetEN()), BP);
URelaunch();
#endif
} else {
@@ -247,8 +285,8 @@ void InitLauncher() {
}
#endif
size_t DirCount(const std::filesystem::path& path) {
return (size_t)std::distance(std::filesystem::directory_iterator { path }, std::filesystem::directory_iterator {});
size_t DirCount(const fs::path& path) {
return (size_t)std::distance(fs::directory_iterator { path }, fs::directory_iterator {});
}
void CheckMP(const beammp_fs_string& Path) {
@@ -329,14 +367,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"));