2 Commits

Author SHA1 Message Date
Lion Kortlepel
dd3622170e add debug print and dont try new format if the packet looks invalid 2024-10-06 15:26:02 +02:00
Lion Kortlepel
885061f73d add more info to new mod list receive error 2024-10-06 15:22:45 +02:00
18 changed files with 130 additions and 292 deletions

1
.gitignore vendored
View File

@@ -12,4 +12,3 @@ Resources/
bin/ bin/
compile_commands.json compile_commands.json
key key
out/

View File

@@ -19,3 +19,9 @@ In the root directory of the project,
2. `cmake --build bin --parallel` 2. `cmake --build bin --parallel`
Remember to change `C:/vcpkg` to wherever you have vcpkg installed. Remember to change `C:/vcpkg` to wherever you have vcpkg installed.
Copyright (c) 2019-present Anonymous275.
BeamMP Launcher code is not in the public domain and is not free software.
One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries,
the only permission that has been granted is to use the software in its compiled form as distributed from the BeamMP.com website.
Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.

View File

@@ -17,4 +17,5 @@ public:
static void StartProxy(); static void StartProxy();
public: public:
static bool isDownload; static bool isDownload;
static inline bool SkipSslVerify = false;
}; };

View File

@@ -26,6 +26,7 @@ extern int ClientID;
extern int LastPort; extern int LastPort;
extern bool ModLoaded; extern bool ModLoaded;
extern bool Terminate; extern bool Terminate;
extern int DEFAULT_PORT;
extern uint64_t UDPSock; extern uint64_t UDPSock;
extern uint64_t TCPSock; extern uint64_t TCPSock;
extern std::string Branch; extern std::string Branch;

View File

@@ -1,24 +0,0 @@
#pragma once
#include <string>
struct Options {
#if defined(_WIN32)
std::string executable_name = "BeamMP-Launcher.exe";
#elif defined(__linux__)
std::string executable_name = "BeamMP-Launcher";
#endif
unsigned int port = 4444;
bool verbose = false;
bool no_download = false;
bool no_update = false;
bool no_launch = false;
const char **game_arguments = nullptr;
int game_arguments_length = 0;
const char** argv = nullptr;
int argc = 0;
};
void InitOptions(int argc, const char *argv[], Options &options);
extern Options options;

View File

@@ -10,10 +10,12 @@
#include <string> #include <string>
#include <vector> #include <vector>
void InitLauncher(); void InitLauncher(int argc, char* argv[]);
std::string GetEP(const char* P = nullptr); std::string GetEP(char* P = nullptr);
std::string GetGamePath(); std::string GetGamePath();
std::string GetVer(); std::string GetVer();
std::string GetPatch(); std::string GetPatch();
std::string GetEN(); std::string GetEN();
void ConfigInit(); void ConfigInit();
extern bool Dev;

View File

@@ -25,7 +25,7 @@ std::vector<char> Comp(std::span<const char> input) {
reinterpret_cast<const Bytef*>(input.data()), reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())); static_cast<uLongf>(input.size()));
if (res != Z_OK) { if (res != Z_OK) {
error("zlib compress() failed (code: " + std::to_string(res) + ", message: " + zError(res) + ")"); error("zlib compress() failed: " + std::to_string(res));
throw std::runtime_error("zlib compress() failed"); throw std::runtime_error("zlib compress() failed");
} }
debug("zlib compressed " + std::to_string(input.size()) + " B to " + std::to_string(output_size) + " B"); debug("zlib compressed " + std::to_string(input.size()) + " B to " + std::to_string(output_size) + " B");
@@ -52,7 +52,7 @@ std::vector<char> DeComp(std::span<const char> input) {
output_buffer.resize(output_buffer.size() * 2); output_buffer.resize(output_buffer.size() * 2);
output_size = output_buffer.size(); output_size = output_buffer.size();
} else if (res != Z_OK) { } else if (res != Z_OK) {
error("zlib uncompress() failed (code: " + std::to_string(res) + ", message: " + zError(res) + ")"); error("zlib uncompress() failed: " + std::to_string(res));
throw std::runtime_error("zlib uncompress() failed"); throw std::runtime_error("zlib uncompress() failed");
} else if (res == Z_OK) { } else if (res == Z_OK) {
break; break;

View File

@@ -8,7 +8,6 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "Options.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::string Branch; std::string Branch;
@@ -16,7 +15,7 @@ std::string CachingDirectory = "./Resources";
void ParseConfig(const nlohmann::json& d) { void ParseConfig(const nlohmann::json& d) {
if (d["Port"].is_number()) { if (d["Port"].is_number()) {
options.port = d["Port"].get<int>(); DEFAULT_PORT = d["Port"].get<int>();
} }
// Default -1 // Default -1
// Release 1 // Release 1
@@ -32,14 +31,6 @@ void ParseConfig(const nlohmann::json& d) {
CachingDirectory = d["CachingDirectory"].get<std::string>(); CachingDirectory = d["CachingDirectory"].get<std::string>();
info("Mod caching directory: " + CachingDirectory); info("Mod caching directory: " + CachingDirectory);
} }
if (d.contains("Dev") && d["Dev"].is_boolean()) {
bool dev = d["Dev"].get<bool>();
options.verbose = dev;
options.no_download = dev;
options.no_launch = dev;
options.no_update = dev;
}
} }
void ConfigInit() { void ConfigInit() {

View File

@@ -23,7 +23,6 @@
#include <Security/Init.h> #include <Security/Init.h>
#include <filesystem> #include <filesystem>
#include <thread> #include <thread>
#include "Options.h"
unsigned long GamePID = 0; unsigned long GamePID = 0;
#if defined(_WIN32) #if defined(_WIN32)
@@ -84,14 +83,7 @@ void StartGame(std::string Dir) {
std::string BaseDir = Dir; //+"\\Bin64"; std::string BaseDir = Dir; //+"\\Bin64";
// Dir += R"(\Bin64\BeamNG.drive.x64.exe)"; // Dir += R"(\Bin64\BeamNG.drive.x64.exe)";
Dir += "\\BeamNG.drive.exe"; Dir += "\\BeamNG.drive.exe";
std::string gameArgs = ""; bSuccess = CreateProcessA(Dir.c_str(), nullptr, nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi);
for (int i = 0; i < options.game_arguments_length; i++) {
gameArgs += " ";
gameArgs += options.game_arguments[i];
}
bSuccess = CreateProcessA(nullptr, (LPSTR)(Dir + gameArgs).c_str(), nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi);
if (bSuccess) { if (bSuccess) {
info("Game Launched!"); info("Game Launched!");
GamePID = pi.dwProcessId; GamePID = pi.dwProcessId;
@@ -107,19 +99,13 @@ void StartGame(std::string Dir) {
void StartGame(std::string Dir) { void StartGame(std::string Dir) {
int status; int status;
std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64"); std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64");
std::vector<const char*> argv; char* argv[] = { filename.data(), NULL };
argv.push_back(filename.data());
for (int i = 0; i < options.game_arguments_length; i++) {
argv.push_back(options.game_arguments[i]);
}
argv.push_back(nullptr);
pid_t pid; pid_t pid;
posix_spawn_file_actions_t spawn_actions; posix_spawn_file_actions_t spawn_actions;
posix_spawn_file_actions_init(&spawn_actions); posix_spawn_file_actions_init(&spawn_actions);
posix_spawn_file_actions_addclose(&spawn_actions, STDOUT_FILENO); posix_spawn_file_actions_addclose(&spawn_actions, STDOUT_FILENO);
posix_spawn_file_actions_addclose(&spawn_actions, STDERR_FILENO); posix_spawn_file_actions_addclose(&spawn_actions, STDERR_FILENO);
int result = posix_spawn(&pid, filename.c_str(), &spawn_actions, nullptr, const_cast<char**>(argv.data()), environ); int result = posix_spawn(&pid, filename.c_str(), &spawn_actions, nullptr, argv, environ);
if (result != 0) { if (result != 0) {
error("Failed to Launch the game! launcher closing soon"); error("Failed to Launch the game! launcher closing soon");
@@ -135,7 +121,7 @@ void StartGame(std::string Dir) {
#endif #endif
void InitGame(const std::string& Dir) { void InitGame(const std::string& Dir) {
if (!options.no_launch) { if (!Dev) {
std::thread Game(StartGame, Dir); std::thread Game(StartGame, Dir);
Game.detach(); Game.detach();
} }

View File

@@ -12,7 +12,6 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <thread> #include <thread>
#include "Options.h"
std::string getDate() { std::string getDate() {
time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
@@ -56,7 +55,7 @@ void info(const std::string& toPrint) {
} }
void debug(const std::string& toPrint) { void debug(const std::string& toPrint) {
std::string Print = getDate() + "[DEBUG] " + toPrint + "\n"; std::string Print = getDate() + "[DEBUG] " + toPrint + "\n";
if (options.verbose) { if (Dev) {
std::cout << Print; std::cout << Print;
} }
addToLog(Print); addToLog(Print);

View File

@@ -30,14 +30,11 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <set> #include <set>
#include <thread> #include <thread>
#include <mutex>
#include "Options.h"
#include <future>
extern int TraceBack; extern int TraceBack;
std::set<std::string>* ConfList = nullptr; std::set<std::string>* ConfList = nullptr;
bool TCPTerminate = false; bool TCPTerminate = false;
int DEFAULT_PORT = 4444;
bool Terminate = false; bool Terminate = false;
bool LoginAuth = false; bool LoginAuth = false;
std::string Username = ""; std::string Username = "";
@@ -90,11 +87,7 @@ void StartSync(const std::string& Data) {
info("Connecting to server"); info("Connecting to server");
} }
std::mutex sendMutex;
void CoreSend(std::string data) { void CoreSend(std::string data) {
std::lock_guard lock(sendMutex);
if (CoreSocket != -1) { if (CoreSocket != -1) {
int res = send(CoreSocket, (data + "\n").c_str(), int(data.size()) + 1, 0); int res = send(CoreSocket, (data + "\n").c_str(), int(data.size()) + 1, 0);
if (res < 0) { if (res < 0) {
@@ -104,7 +97,7 @@ void CoreSend(std::string data) {
} }
bool IsAllowedLink(const std::string& Link) { bool IsAllowedLink(const std::string& Link) {
std::regex link_pattern(R"(https:\/\/(?:\w+)?(?:\.)?(?:beammp\.com|beammp\.gg|github\.com\/BeamMP\/|discord\.gg|patreon\.com\/BeamMP))"); std::regex link_pattern(R"(https:\/\/(?:\w+)?(?:\.)?(?:beammp\.com|discord\.gg|patreon\.com\/BeamMP))");
std::smatch link_match; std::smatch link_match;
return std::regex_search(Link, link_match, link_pattern) && link_match.position() == 0; return std::regex_search(Link, link_match, link_pattern) && link_match.position() == 0;
} }
@@ -117,15 +110,11 @@ void Parse(std::string Data, SOCKET CSocket) {
case 'A': case 'A':
Data = Data.substr(0, 1); Data = Data.substr(0, 1);
break; break;
case 'B': { case 'B':
NetReset(); NetReset();
Terminate = true; Terminate = true;
TCPTerminate = true; TCPTerminate = true;
Data.clear(); Data = Code + HTTP::Get("https://backend.beammp.com/servers-info");
auto future = std::async(std::launch::async, []() {
CoreSend("B" + HTTP::Get("https://backend.beammp.com/servers-info"));
});
}
break; break;
case 'C': case 'C':
StartSync(Data); StartSync(Data);
@@ -221,10 +210,7 @@ void Parse(std::string Data, SOCKET CSocket) {
} }
Data = "N" + Auth.dump(); Data = "N" + Auth.dump();
} else { } else {
auto future = std::async(std::launch::async, [data = std::move(Data)]() { Data = "N" + Login(Data.substr(Data.find(':') + 1));
CoreSend("N" + Login(data.substr(data.find(':') + 1)));
});
Data.clear();
} }
break; break;
case 'W': case 'W':
@@ -240,8 +226,12 @@ void Parse(std::string Data, SOCKET CSocket) {
Data.clear(); Data.clear();
break; break;
} }
if (!Data.empty()) if (!Data.empty() && CSocket != -1) {
CoreSend(Data); int res = send(CSocket, (Data + "\n").c_str(), int(Data.size()) + 1, 0);
if (res < 0) {
debug("(Core) send failed with error: " + std::to_string(WSAGetLastError()));
}
}
} }
void GameHandler(SOCKET Client) { void GameHandler(SOCKET Client) {
CoreSocket = Client; CoreSocket = Client;
@@ -298,7 +288,7 @@ void localRes() {
ConfList = new std::set<std::string>; ConfList = new std::set<std::string>;
} }
void CoreMain() { void CoreMain() {
debug("Core Network on start! port: " + std::to_string(options.port)); debug("Core Network on start!");
SOCKET LSocket, CSocket; SOCKET LSocket, CSocket;
struct addrinfo* res = nullptr; struct addrinfo* res = nullptr;
struct addrinfo hints { }; struct addrinfo hints { };
@@ -316,7 +306,7 @@ void CoreMain() {
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
iRes = getaddrinfo("127.0.0.1", std::to_string(options.port).c_str(), &hints, &res); iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res);
if (iRes) { if (iRes) {
debug("(Core) addr info failed with error: " + std::to_string(iRes)); debug("(Core) addr info failed with error: " + std::to_string(iRes));
WSACleanup(); WSACleanup();

View File

@@ -26,7 +26,6 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include "Options.h"
std::chrono::time_point<std::chrono::high_resolution_clock> PingStart, PingEnd; std::chrono::time_point<std::chrono::high_resolution_clock> PingStart, PingEnd;
bool GConnected = false; bool GConnected = false;
@@ -162,7 +161,7 @@ SOCKET SetupListener() {
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
iRes = getaddrinfo(nullptr, std::to_string(options.port + 1).c_str(), &hints, &result); iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT + 1).c_str(), &hints, &result);
if (iRes != 0) { if (iRes != 0) {
error("(Proxy) info failed with error: " + std::to_string(iRes)); error("(Proxy) info failed with error: " + std::to_string(iRes));
WSACleanup(); WSACleanup();

View File

@@ -77,8 +77,7 @@ std::string HTTP::Get(const std::string& IP) {
curl_easy_setopt(curl, CURLOPT_URL, IP.c_str()); curl_easy_setopt(curl, CURLOPT_URL, IP.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); // seconds
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
res = curl_easy_perform(curl); res = curl_easy_perform(curl);
if (res != CURLE_OK) { if (res != CURLE_OK) {
error("GET to " + IP + " failed: " + std::string(curl_easy_strerror(res))); error("GET to " + IP + " failed: " + std::string(curl_easy_strerror(res)));
@@ -105,8 +104,7 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
struct curl_slist* list = nullptr; struct curl_slist* list = nullptr;
list = curl_slist_append(list, "Content-Type: application/json"); list = curl_slist_append(list, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); // seconds
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
res = curl_easy_perform(curl); res = curl_easy_perform(curl);
curl_slist_free_all(list); curl_slist_free_all(list);
if (res != CURLE_OK) { if (res != CURLE_OK) {
@@ -136,6 +134,7 @@ bool HTTP::Download(const std::string& IP, const std::string& Path) {
if (File.is_open()) { if (File.is_open()) {
File << Ret; File << Ret;
File.close(); File.close();
std::cout << "\n";
info("Download Complete!"); info("Download Complete!");
} else { } else {
error("Failed to open file directory: " + Path); error("Failed to open file directory: " + Path);
@@ -261,7 +260,7 @@ void HTTP::StartProxy() {
handle_request(req, res); handle_request(req, res);
}); });
ProxyPort = HTTPProxy.bind_to_any_port("127.0.0.1"); ProxyPort = HTTPProxy.bind_to_any_port("0.0.0.0");
debug("HTTP Proxy listening on port " + std::to_string(ProxyPort)); debug("HTTP Proxy listening on port " + std::to_string(ProxyPort));
HTTPProxy.listen_after_bind(); HTTPProxy.listen_after_bind();
}); });

View File

@@ -362,15 +362,10 @@ std::string GetSha256HashReallyFast(const std::string& filename) {
} }
struct ModInfo { struct ModInfo {
static std::pair<bool, std::vector<ModInfo>> ParseModInfosFromPacket(const std::string& packet) { static std::vector<ModInfo> ParseModInfosFromPacket(const std::string& packet) {
bool success = false;
std::vector<ModInfo> modInfos; std::vector<ModInfo> modInfos;
try { try {
auto json = nlohmann::json::parse(packet); auto json = nlohmann::json::parse(packet);
if (json.empty()) {
return std::make_pair(true, modInfos);
}
for (const auto& entry : json) { for (const auto& entry : json) {
ModInfo modInfo { ModInfo modInfo {
.FileName = entry["file_name"], .FileName = entry["file_name"],
@@ -379,13 +374,13 @@ struct ModInfo {
.HashAlgorithm = entry["hash_algorithm"], .HashAlgorithm = entry["hash_algorithm"],
}; };
modInfos.push_back(modInfo); modInfos.push_back(modInfo);
success = true;
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
debug(std::string("Failed to receive mod list: ") + e.what()); debug(std::string("Failed to receive mod list: ") + e.what());
warn("Failed to receive new mod list format! This server may be outdated, but everything should still work as expected."); warn("Failed to receive new mod list format! This server may be outdated, but everything should still work as expected.");
return {};
} }
return std::make_pair(success, modInfos); return modInfos;
} }
std::string FileName; std::string FileName;
size_t FileSize; size_t FileSize;
@@ -394,13 +389,6 @@ struct ModInfo {
}; };
void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<ModInfo> ModInfos) { void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<ModInfo> ModInfos) {
if (ModInfos.empty()) {
CoreSend("L");
TCPSend("Done", Sock);
info("Done!");
return;
}
if (!SecurityWarning()) if (!SecurityWarning())
return; return;
@@ -523,15 +511,13 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
void SyncResources(SOCKET Sock) { void SyncResources(SOCKET Sock) {
std::string Ret = Auth(Sock); std::string Ret = Auth(Sock);
debug("Mod info: " + Ret);
if (Ret.starts_with("R")) { if (Ret.starts_with("R")) {
debug("This server is likely outdated, not trying to parse new mod info format"); debug("This server is likely outdated, not trying to parse new mod info format");
} else { } else {
auto [success, modInfo] = ModInfo::ParseModInfosFromPacket(Ret); auto ModInfos = ModInfo::ParseModInfosFromPacket(Ret);
if (success) { if (!ModInfos.empty()) {
NewSyncResources(Sock, Ret, modInfo); NewSyncResources(Sock, Ret, ModInfos);
return; return;
} }
} }

View File

@@ -1,107 +0,0 @@
#include "Options.h"
#include "Logger.h"
#include <cstdlib>
#include <filesystem>
void InitOptions(int argc, const char *argv[], Options &options) {
int i = 1;
options.argc = argc;
options.argv = argv;
std::string AllOptions;
for (int i = 0; i < argc; ++i) {
AllOptions += std::string(argv[i]);
if (i + 1 < argc) {
AllOptions += " ";
}
}
debug("Launcher was invoked as: '" + AllOptions + "'");
if (argc > 2) {
if (std::string(argv[1]) == "0" && std::string(argv[2]) == "0") {
options.verbose = true;
options.no_download = true;
options.no_launch = true;
options.no_update = true;
warn("You are using deprecated commandline arguments, please use --dev instead");
return;
}
}
options.executable_name = std::string(argv[0]);
while (i < argc) {
std::string argument(argv[i]);
if (argument == "-p" || argument == "--port") {
if (i + 1 >= argc) {
std::string error_message =
"No port specified, resorting to default (";
error_message += std::to_string(options.port);
error_message += ")";
error(error_message);
i++;
continue;
}
int port = options.port;
try {
port = std::stoi(argv[i + 1]);
} catch (std::exception& e) {
error("Invalid port specified: " + std::string(argv[i + 1]) + " " + std::string(e.what()));
}
if (port <= 0) {
std::string error_message =
"Port invalid, must be a non-zero positive "
"integer, resorting to default (";
error_message += options.port;
error_message += ")";
error(error_message);
i++;
continue;
}
options.port = port;
i++;
} else if (argument == "-v" || argument == "--verbose") {
options.verbose = true;
} else if (argument == "--no-download") {
options.no_download = true;
} else if (argument == "--no-update") {
options.no_update = true;
} else if (argument == "--no-launch") {
options.no_launch = true;
} else if (argument == "--dev") {
options.verbose = true;
options.no_download = true;
options.no_launch = true;
options.no_update = true;
} else if (argument == "--" || argument == "--game") {
options.game_arguments = &argv[i + 1];
options.game_arguments_length = argc - i - 1;
break;
} else if (argument == "--help" || argument == "-h" || argument == "/?") {
std::cout << "USAGE:\n"
"\t" + std::filesystem::path(options.executable_name).filename().string() + " [OPTIONS] [-- <GAME ARGS>...]\n"
"\n"
"OPTIONS:\n"
"\t--port <port> -p Change the default listen port to <port>. This must be configured ingame, too\n"
"\t--verbose -v Verbose mode, prints debug messages\n"
"\t--no-download Skip downloading and installing the BeamMP Lua mod\n"
"\t--no-update Skip applying launcher updates (you must update manually)\n"
"\t--no-launch Skip launching the game (you must launch the game manually)\n"
"\t--dev Developer mode, same as --verbose --no-download --no-launch --no-update\n"
"\t--game <args...> Passes ALL following arguments to the game, see also `--`\n"
<< std::flush;
exit(0);
} else {
warn("Unknown option: " + argument);
}
i++;
}
}

View File

@@ -91,7 +91,6 @@ std::string Login(const std::string& fields) {
if (d.contains("message")) { if (d.contains("message")) {
d.erase("private_key"); d.erase("private_key");
d.erase("public_key"); d.erase("public_key");
debug("Authentication result: " + d["message"].get<std::string>());
return d.dump(); return d.dump();
} }
return GetFail("Invalid message parsing!"); return GetFail("Invalid message parsing!");

View File

@@ -8,7 +8,6 @@
#include "zip_file.h" #include "zip_file.h"
#include <charconv> #include <charconv>
#include <cstring>
#include <httplib.h> #include <httplib.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <string> #include <string>
@@ -26,9 +25,9 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include "Options.h"
extern int TraceBack; extern int TraceBack;
bool Dev = false;
int ProxyPort = 0; int ProxyPort = 0;
namespace fs = std::filesystem; namespace fs = std::filesystem;
@@ -82,13 +81,13 @@ std::string GetEN() {
} }
std::string GetVer() { std::string GetVer() {
return "2.3"; return "2.2";
} }
std::string GetPatch() { std::string GetPatch() {
return ".2"; return ".0";
} }
std::string GetEP(const char* P) { std::string GetEP(char* P) {
static std::string Ret = [&]() { static std::string Ret = [&]() {
std::string path(P); std::string path(P);
return path.substr(0, path.find_last_of("\\/") + 1); return path.substr(0, path.find_last_of("\\/") + 1);
@@ -96,11 +95,11 @@ std::string GetEP(const char* P) {
return Ret; return Ret;
} }
#if defined(_WIN32) #if defined(_WIN32)
void ReLaunch() { void ReLaunch(int argc, char* args[]) {
std::string Arg; std::string Arg;
for (int c = 2; c <= options.argc; c++) { for (int c = 2; c <= argc; c++) {
Arg += options.argv[c - 1];
Arg += " "; Arg += " ";
Arg += args[c - 1];
} }
info("Relaunch!"); info("Relaunch!");
system("cls"); system("cls");
@@ -109,11 +108,11 @@ void ReLaunch() {
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1); exit(1);
} }
void URelaunch() { void URelaunch(int argc, char* args[]) {
std::string Arg; std::string Arg;
for (int c = 2; c <= options.argc; c++) { for (int c = 2; c <= argc; c++) {
Arg += options.argv[c - 1];
Arg += " "; Arg += " ";
Arg += args[c - 1];
} }
ShellExecute(nullptr, "open", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL); ShellExecute(nullptr, "open", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL);
ShowWindow(GetConsoleWindow(), 0); ShowWindow(GetConsoleWindow(), 0);
@@ -121,50 +120,47 @@ void URelaunch() {
exit(1); exit(1);
} }
#elif defined(__linux__) #elif defined(__linux__)
void ReLaunch() { void ReLaunch(int argc, char* args[]) {
std::string Arg; std::string Arg;
for (int c = 2; c <= options.argc; c++) { for (int c = 2; c <= argc; c++) {
Arg += options.argv[c - 1];
Arg += " "; Arg += " ";
Arg += args[c - 1];
} }
info("Relaunch!"); info("Relaunch!");
system("clear"); system("clear");
int ret = execv(options.executable_name.c_str(), const_cast<char**>(options.argv)); execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL);
if (ret < 0) {
error(std::string("execv() failed with: ") + strerror(errno) + ". Failed to relaunch");
exit(1);
}
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1); exit(1);
} }
void URelaunch() { void URelaunch(int argc, char* args[]) {
int ret = execv(options.executable_name.c_str(), const_cast<char**>(options.argv)); std::string Arg;
if (ret < 0) { for (int c = 2; c <= argc; c++) {
error(std::string("execv() failed with: ") + strerror(errno) + ". Failed to relaunch"); Arg += " ";
exit(1); Arg += args[c - 1];
} }
execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL);
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1); exit(1);
} }
#endif #endif
void CheckName() { void CheckName(int argc, char* args[]) {
#if defined(_WIN32) #if defined(_WIN32)
std::string DN = GetEN(), CDir = options.executable_name, FN = CDir.substr(CDir.find_last_of('\\') + 1); std::string DN = GetEN(), CDir = args[0], FN = CDir.substr(CDir.find_last_of('\\') + 1);
#elif defined(__linux__) #elif defined(__linux__)
std::string DN = GetEN(), CDir = options.executable_name, FN = CDir.substr(CDir.find_last_of('/') + 1); std::string DN = GetEN(), CDir = args[0], FN = CDir.substr(CDir.find_last_of('/') + 1);
#endif #endif
if (FN != DN) { if (FN != DN) {
if (fs::exists(DN)) if (fs::exists(DN))
remove(DN.c_str()); remove(DN.c_str());
if (fs::exists(DN)) if (fs::exists(DN))
ReLaunch(); ReLaunch(argc, args);
std::rename(FN.c_str(), DN.c_str()); std::rename(FN.c_str(), DN.c_str());
URelaunch(); URelaunch(argc, args);
} }
} }
void CheckForUpdates(const std::string& CV) { void CheckForUpdates(int argc, char* args[], const std::string& CV) {
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey); std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey);
std::string LatestVersion = HTTP::Get( std::string LatestVersion = HTTP::Get(
"https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey); "https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey);
@@ -174,30 +170,39 @@ void CheckForUpdates(const std::string& CV) {
std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP); std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP);
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) { if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion))) && !Dev) {
if (!options.no_update) { info("Launcher update found!");
info("Launcher update found!");
#if defined(__linux__) #if defined(__linux__)
error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches."); error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches.");
#else #else
fs::remove(Back); fs::remove(Back);
fs::rename(EP, Back); fs::rename(EP, Back);
info("Downloading Launcher update " + LatestHash); info("Downloading Launcher update " + LatestHash);
HTTP::Download( HTTP::Download(
"https://backend.beammp.com/builds/launcher?download=true" "https://backend.beammp.com/builds/launcher?download=true"
"&pk=" "&pk="
+ PublicKey + "&branch=" + Branch, + PublicKey + "&branch=" + Branch,
EP); EP);
URelaunch(); URelaunch(argc, args);
#endif #endif
} else {
warn("Launcher update was found, but not updating because --no-update or --dev was specified.");
}
} else } else
info("Launcher version is up to date"); info("Launcher version is up to date");
TraceBack++; TraceBack++;
} }
void CustomPort(int argc, char* argv[]) {
if (argc > 1) {
std::string Port = argv[1];
if (Port.find_first_not_of("0123456789") == std::string::npos) {
if (std::stoi(Port) > 1000) {
DEFAULT_PORT = std::stoi(Port);
warn("Running on custom port : " + std::to_string(DEFAULT_PORT));
}
}
if (argc > 2)
Dev = true;
}
}
#ifdef _WIN32 #ifdef _WIN32
void LinuxPatch() { void LinuxPatch() {
@@ -229,21 +234,25 @@ void LinuxPatch() {
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
void InitLauncher(int argc, char* argv[]) {
void InitLauncher() {
SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str()); SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str());
CheckName(); InitLog();
CheckName(argc, argv);
LinuxPatch(); LinuxPatch();
CheckLocalKey(); CheckLocalKey();
CheckForUpdates(std::string(GetVer()) + GetPatch()); ConfigInit();
CustomPort(argc, argv);
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
} }
#elif defined(__linux__) #elif defined(__linux__)
void InitLauncher(int argc, char* argv[]) {
void InitLauncher() { InitLog();
info("BeamMP Launcher v" + GetVer() + GetPatch()); info("BeamMP Launcher v" + GetVer() + GetPatch());
CheckName(); CheckName(argc, argv);
CheckLocalKey(); CheckLocalKey();
CheckForUpdates(std::string(GetVer()) + GetPatch()); ConfigInit();
CustomPort(argc, argv);
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
} }
#endif #endif
@@ -307,7 +316,7 @@ void PreGame(const std::string& GamePath) {
CheckMP(GetGamePath() + "mods/multiplayer"); CheckMP(GetGamePath() + "mods/multiplayer");
info("Game user path: " + GetGamePath()); info("Game user path: " + GetGamePath());
if (!options.no_download) { if (!Dev) {
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey); std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(), LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(),

View File

@@ -13,9 +13,6 @@
#include <curl/curl.h> #include <curl/curl.h>
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include "Options.h"
Options options;
[[noreturn]] void flush() { [[noreturn]] void flush() {
while (true) { while (true) {
@@ -24,13 +21,7 @@ Options options;
} }
} }
int main(int argc, const char** argv) try { int main(int argc, char** argv) try {
#if defined(_WIN32)
system("cls");
#elif defined(__linux__)
system("clear");
#endif
#ifdef DEBUG #ifdef DEBUG
std::thread th(flush); std::thread th(flush);
th.detach(); th.detach();
@@ -38,12 +29,23 @@ int main(int argc, const char** argv) try {
curl_global_init(CURL_GLOBAL_ALL); curl_global_init(CURL_GLOBAL_ALL);
#if defined(_WIN32)
system("cls");
#elif defined(__linux__)
system("clear");
#endif
GetEP(argv[0]); GetEP(argv[0]);
InitLog(); for (int i = 0; i < argc; ++i) {
ConfigInit(); if (std::string_view(argv[i]) == "--skip-ssl-verify") {
InitOptions(argc, argv, options); info("SSL verification skip enabled");
InitLauncher(); HTTP::SkipSslVerify = true;
}
}
InitLauncher(argc, argv);
info("IMPORTANT: You MUST keep this window open to play BeamMP!"); info("IMPORTANT: You MUST keep this window open to play BeamMP!");