16 Commits

Author SHA1 Message Date
SaltySnail
642dc76a92 Remove backend 2026-03-28 22:23:33 +01:00
Tixx
b1c3b978ff Fix include order for winsock2 2026-01-24 20:34:47 +01:00
Lion Kortlepel
7d00d489e4 remove invalid flag 2026-01-24 20:09:37 +01:00
Lion Kortlepel
fd398ed5ab add new header implementation for game<->launcher communication 2026-01-24 20:09:37 +01:00
Lion Kortlepel
b4f0f0759d flush all prints 2026-01-24 20:09:37 +01:00
Lion Kortlepel
5b2bc2d499 print posix_spawn errors 2026-01-24 20:09:26 +01:00
Tixx
fab9ff5559 Provide more info if the launcher update fails (#214)
Also prevents the launcher from bricking itself (again)

---

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-11-22 20:09:15 +01:00
Tixx
a2789a8524 Bump version to v2.7.0 2025-10-20 22:39:36 +02:00
Tixx
3db98eaf0c Provide more info if the launcher update fails
Also prevents the launcher from bricking itself (again)
2025-10-05 15:13:49 +02:00
Tixx
6f84b56f1b Bump version 2025-09-27 20:47:14 +02:00
Tixx
6f5197217c Workaround bom header (#211)
This PR removes the utf8 bom encoding header which was causing issues.
This PR also adds support for unicode characters in the terminal on
windows.

For context it looks like the BeamNG launcher only encodes it with the
utf8 header, which is what it's currently removing.

---

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-27 20:14:10 +02:00
Tixx
d1d2b3414b Adjust ini parser (#212)
This PR removes mid-line ini comments which were causing issue because
users had `#` and `;` in their file path.

---

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-27 20:08:13 +02:00
Tixx
a669557726 Make logger stdout cross platform again 2025-09-21 21:54:24 +02:00
Tixx
be1f5c04f7 Remove mid-line ini comments 2025-09-21 21:49:40 +02:00
Tixx
1209ff88e2 Support unicode in windows terminal 2025-09-21 21:39:57 +02:00
Tixx
c40af681bf Strip bom encoding 2025-09-21 21:39:38 +02:00
10 changed files with 297 additions and 286 deletions

View File

@@ -38,6 +38,7 @@ extern std::string MStatus;
extern std::string UlStatus;
extern std::string PublicKey;
extern std::string PrivateKey;
extern std::string magic;
int KillSocket(uint64_t Dead);
void UUl(const std::string& R);
void UDPSend(std::string Data);

View File

@@ -13,22 +13,39 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <regex>
#include <cstdint>
#include <cstring>
#include <string>
#include <variant>
#include <vector>
#include <array>
#include <cerrno>
#include <cstring>
#include <stdexcept>
#if defined(__linux__)
#include <sys/socket.h>
#include "linuxfixes.h"
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#ifdef _WIN32
#define beammp_fs_string std::wstring
#define beammp_fs_char wchar_t
#define beammp_wide(str) L##str
#define beammp_stdout std::wcout
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#define beammp_fs_string std::string
#define beammp_fs_char char
#define beammp_wide(str) str
#define beammp_stdout std::cout
#endif
#include "Logger.h"
namespace Utils {
inline std::vector<std::string> Split(const std::string& String, const std::string& delimiter) {
std::vector<std::string> Val;
@@ -43,7 +60,7 @@ namespace Utils {
if (!s.empty())
Val.push_back(s);
return Val;
};
}
inline std::string ExpandEnvVars(const std::string& input) {
std::string result;
std::regex envPattern(R"(%([^%]+)%|\$([A-Za-z_][A-Za-z0-9_]*)|\$\{([^}]+)\})");
@@ -120,13 +137,6 @@ namespace Utils {
if (line.empty() || line[0] == ';' || line[0] == '#')
continue;
for (auto& c : line) {
if (c == '#' || c == ';') {
line = line.substr(0, &c - &line[0]);
break;
}
}
auto invalidLineLog = [&]{
debug("Invalid INI line: " + line);
debug("Surrounding lines: \n" +
@@ -291,4 +301,39 @@ namespace Utils {
return "";
}
}
};
template<typename T>
inline std::vector<char> PrependHeader(const T& data) {
std::vector<char> size_buffer(4);
uint32_t len = data.size();
std::memcpy(size_buffer.data(), &len, 4);
std::vector<char> buffer;
buffer.reserve(size_buffer.size() + data.size());
buffer.insert(buffer.begin(), size_buffer.begin(), size_buffer.end());
buffer.insert(buffer.end(), data.begin(), data.end());
return buffer;
}
inline uint32_t RecvHeader(SOCKET socket) {
std::array<uint8_t, sizeof(uint32_t)> header_buffer {};
auto n = recv(socket, reinterpret_cast<char*>(header_buffer.data()), header_buffer.size(), MSG_WAITALL);
if (n < 0) {
throw std::runtime_error(std::string("recv() of header failed: ") + std::strerror(errno));
} else if (n == 0) {
throw std::runtime_error("Game disconnected");
}
return *reinterpret_cast<uint32_t*>(header_buffer.data());
}
/// Throws!!!
inline void ReceiveFromGame(SOCKET socket, std::vector<char>& out_data) {
auto header = RecvHeader(socket);
out_data.resize(header);
auto n = recv(socket, reinterpret_cast<char*>(out_data.data()), out_data.size(), MSG_WAITALL);
if (n < 0) {
throw std::runtime_error(std::string("recv() of data failed: ") + std::strerror(errno));
} else if (n == 0) {
throw std::runtime_error("Game disconnected");
}
}
};

View File

@@ -4,6 +4,8 @@
SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <cstring>
#include "Utils.h"
#if defined(_WIN32)
#include <shlobj.h>
#elif defined(__linux__)
@@ -18,7 +20,6 @@
#include "Logger.h"
#include "Options.h"
#include "Startup.h"
#include "Utils.h"
#include <Security/Init.h>
#include <filesystem>
#include <thread>
@@ -57,12 +58,11 @@ std::filesystem::path GetGamePath() {
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() && 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());
} else
warn(L"Found custom user folder path (" + userPath + L") in startup.ini but it doesn't exist, skipping");
if (userPath = Utils::ExpandEnvVars(userPath); std::filesystem::exists(userPath)) {
Path = userPath;
debug(L"Using custom user folder path from startup.ini: " + Path.wstring());
} else
warn(L"Found custom user folder path (" + userPath + L") in startup.ini but it doesn't exist, skipping");
}
if (Path.empty()) {
@@ -94,12 +94,11 @@ std::filesystem::path GetGamePath() {
}
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");
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");
}
}
@@ -175,7 +174,6 @@ void StartGame(std::wstring Dir) {
}
#elif defined(__linux__)
void StartGame(std::string Dir) {
int status;
std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64");
std::vector<const char*> argv;
argv.push_back(filename.data());
@@ -185,11 +183,24 @@ void StartGame(std::string Dir) {
argv.push_back(nullptr);
pid_t pid;
posix_spawn_file_actions_t 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, STDERR_FILENO);
int result = posix_spawn(&pid, filename.c_str(), &spawn_actions, nullptr, const_cast<char**>(argv.data()), environ);
posix_spawn_file_actions_t file_actions;
auto status = posix_spawn_file_actions_init(&file_actions);
// disable stdout
if (status != 0) {
error(std::string("posix_spawn_file_actions_init failed: ") + std::strerror(errno));
}
status = posix_spawn_file_actions_addclose(&file_actions, STDOUT_FILENO);
if (status != 0) {
error(std::string("posix_spawn_file_actions_addclose for STDOUT failed: ") + std::strerror(errno));
}
status = posix_spawn_file_actions_addclose(&file_actions, STDERR_FILENO);
if (status != 0) {
error(std::string("posix_spawn_file_actions_addclose for STDERR failed: ") + std::strerror(errno));
}
// launch the game
int result = posix_spawn(&pid, filename.c_str(), &file_actions, NULL, const_cast<char**>(argv.data()), environ);
if (result != 0) {
error("Failed to Launch the game! launcher closing soon");

View File

@@ -57,36 +57,36 @@ void addToLog(const std::wstring& Line) {
}
void info(const std::string& toPrint) {
std::string Print = getDate() + "[INFO] " + toPrint + "\n";
std::cout << Print;
beammp_stdout << Utils::ToWString(Print);
addToLog(Print);
}
void debug(const std::string& toPrint) {
std::string Print = getDate() + "[DEBUG] " + toPrint + "\n";
if (options.verbose) {
std::cout << Print;
beammp_stdout << Utils::ToWString(Print);
}
addToLog(Print);
}
void warn(const std::string& toPrint) {
std::string Print = getDate() + "[WARN] " + toPrint + "\n";
std::cout << Print;
beammp_stdout << Utils::ToWString(Print);
addToLog(Print);
}
void error(const std::string& toPrint) {
std::string Print = getDate() + "[ERROR] " + toPrint + "\n";
std::cout << Print;
beammp_stdout << Utils::ToWString(Print);
addToLog(Print);
}
void fatal(const std::string& toPrint) {
std::string Print = getDate() + "[FATAL] " + toPrint + "\n";
std::cout << Print;
beammp_stdout << Utils::ToWString(Print);
addToLog(Print);
std::this_thread::sleep_for(std::chrono::seconds(5));
std::exit(1);
}
void except(const std::string& toPrint) {
std::string Print = getDate() + "[EXCEP] " + toPrint + "\n";
std::cout << Print;
beammp_stdout << Utils::ToWString(Print);
addToLog(Print);
}

View File

@@ -7,6 +7,7 @@
#include "Http.h"
#include "Network/network.hpp"
#include "Security/Init.h"
#include "Utils.h"
#include <cstdlib>
#include <regex>
#if defined(_WIN32)
@@ -188,7 +189,8 @@ void CoreSend(std::string data) {
std::lock_guard lock(sendMutex);
if (CoreSocket != -1) {
int res = send(CoreSocket, (data + "\n").c_str(), int(data.size()) + 1, 0);
auto ToSend = Utils::PrependHeader(data);
int res = send(CoreSocket, ToSend.data(), ToSend.size(), 0);
if (res < 0) {
debug("(Core) send failed with error: " + std::to_string(WSAGetLastError()));
}
@@ -225,7 +227,7 @@ void Parse(std::string Data, SOCKET CSocket) {
TCPTerminate = true;
Data.clear();
futures.push_back(std::async(std::launch::async, []() {
CoreSend("B" + HTTP::Get("https://backend.beammp.com/servers-info"));
// CoreSend("B" + HTTP::Get("https://backend.beammp.com/servers-info"));
}));
}
break;
@@ -278,7 +280,7 @@ void Parse(std::string Data, SOCKET CSocket) {
Ping = "-2";
else
Ping = std::to_string(ping);
Data = std::string(UlStatus) + "\n" + "Up" + Ping;
Data = "Up" + Ping;
}
break;
case 'M':
@@ -348,51 +350,29 @@ void Parse(std::string Data, SOCKET CSocket) {
Data.clear();
break;
}
if (!Data.empty())
CoreSend(Data);
if (!Data.empty() && CSocket != -1) {
auto ToSend = Utils::PrependHeader(Data);
int res = send(CSocket, ToSend.data(), ToSend.size(), 0);
if (res < 0) {
debug("(Core) send failed with error: " + std::to_string(WSAGetLastError()));
}
}
}
void GameHandler(SOCKET Client) {
CoreSocket = Client;
int32_t Size, Rcv;
std::vector<char> data{};
int Temp;
char Header[10] = { 0 };
do {
Rcv = 0;
do {
Temp = recv(Client, &Header[Rcv], 1, 0);
if (Temp < 1)
break;
if (!isdigit(Header[Rcv]) && Header[Rcv] != '>') {
error("(Core) Invalid lua communication");
KillSocket(Client);
return;
}
} while (Header[Rcv++] != '>');
if (Temp < 1)
break;
if (std::from_chars(Header, &Header[Rcv], Size).ptr[0] != '>') {
debug("(Core) Invalid lua Header -> " + std::string(Header, Rcv));
try {
Utils::ReceiveFromGame(Client, data);
Parse(std::string(data.data(), data.size()), Client);
} catch (const std::exception& e) {
error(std::string("Error while receiving from game on core: ") + e.what());
break;
}
std::string Ret(Size, 0);
Rcv = 0;
do {
Temp = recv(Client, &Ret[Rcv], Size - Rcv, 0);
if (Temp < 1)
break;
Rcv += Temp;
} while (Rcv < Size);
if (Temp < 1)
break;
Parse(Ret, Client);
} while (Temp > 0);
if (Temp == 0) {
debug("(Core) Connection closing");
} else {
debug("(Core) recv failed with error: " + std::to_string(WSAGetLastError()));
}
} while (true);
debug("(Core) Connection closing");
NetReset();
KillSocket(Client);
}

View File

@@ -5,6 +5,7 @@
*/
#include "Network/network.hpp"
#include "Utils.h"
#include <memory>
#include <zlib.h>
#if defined(_WIN32)
@@ -21,6 +22,7 @@
#endif
#include "Logger.h"
#include "Options.h"
#include <charconv>
#include <mutex>
#include <string>
@@ -33,6 +35,7 @@ bool GConnected = false;
bool CServer = true;
SOCKET CSocket = -1;
SOCKET GSocket = -1;
std::string magic;
int KillSocket(uint64_t Dead) {
if (Dead == (SOCKET)-1) {
@@ -61,30 +64,13 @@ bool CheckBytes(uint32_t Bytes) {
void GameSend(std::string_view Data) {
static std::mutex Lock;
std::scoped_lock Guard(Lock);
if (TCPTerminate || !GConnected || CSocket == -1)
return;
int32_t Size, Temp, Sent;
Size = int32_t(Data.size());
Sent = 0;
#ifdef DEBUG
if (Size > 1000) {
debug("Launcher -> game (" + std::to_string(Size) + ")");
}
#endif
do {
if (Sent > -1) {
Temp = send(CSocket, &Data[Sent], Size - Sent, 0);
}
if (!CheckBytes(Temp))
return;
Sent += Temp;
} while (Sent < Size);
// send separately to avoid an allocation for += "\n"
Temp = send(CSocket, "\n", 1, 0);
if (!CheckBytes(Temp)) {
return;
auto ToSend = Utils::PrependHeader<std::string_view>(Data);
auto Result = send(CSocket, ToSend.data(), ToSend.size(), 0);
if (Result < 0) {
error("(Game) send failed with error: " + std::to_string(WSAGetLastError()));
}
}
void ServerSend(std::string Data, bool Rel) {
if (Terminate || Data.empty())
return;
@@ -218,6 +204,8 @@ void ParserAsync(std::string_view Data) {
MStatus = Data;
UlStatus = "Uldone";
return;
case 'U':
magic = Data.substr(1);
default:
break;
}
@@ -265,40 +253,19 @@ void TCPGameServer(const std::string& IP, int Port) {
int32_t Size, Rcv;
int Temp;
char Header[10] = { 0 };
std::vector<char> data {};
// Read byte by byte until '>' is rcved then get the size and read based on it
do {
Rcv = 0;
do {
Temp = recv(CSocket, &Header[Rcv], 1, 0);
if (Temp < 1 || TCPTerminate)
break;
} while (Header[Rcv++] != '>');
if (Temp < 1 || TCPTerminate)
break;
if (std::from_chars(Header, &Header[Rcv], Size).ptr[0] != '>') {
debug("(Game) Invalid lua Header -> " + std::string(Header, Rcv));
try {
Utils::ReceiveFromGame(CSocket, data);
ServerSend(std::string(data.data(), data.size()), false);
} catch (const std::exception& e) {
error(std::string("Error while receiving from game on proxy: ") + e.what());
break;
}
std::string Ret(Size, 0);
Rcv = 0;
do {
Temp = recv(CSocket, &Ret[Rcv], Size - Rcv, 0);
if (Temp < 1)
break;
Rcv += Temp;
} while (Rcv < Size && !TCPTerminate);
if (Temp < 1 || TCPTerminate)
break;
ServerSend(Ret, false);
} while (Temp > 0 && !TCPTerminate);
if (Temp == 0)
debug("(Proxy) Connection closing");
else
debug("(Proxy) recv failed error : " + std::to_string(WSAGetLastError()));
} while (!TCPTerminate);
debug("(Proxy) Connection closing");
}
TCPTerminate = true;
GConnected = false;

View File

@@ -169,118 +169,118 @@ void set_headers(httplib::Response& res) {
}
void HTTP::StartProxy() {
std::thread proxy([&]() {
httplib::Server HTTPProxy;
httplib::Headers headers = {
{ "User-Agent", "BeamMP-Launcher/" + GetVer() + GetPatch() },
{ "Accept", "*/*" }
};
httplib::Client backend("https://backend.beammp.com");
httplib::Client forum("https://forum.beammp.com");
// std::thread proxy([&]() {
// httplib::Server HTTPProxy;
// httplib::Headers headers = {
// { "User-Agent", "BeamMP-Launcher/" + GetVer() + GetPatch() },
// { "Accept", "*/*" }
// };
// httplib::Client backend("https://backend.beammp.com");
// httplib::Client forum("https://forum.beammp.com");
const std::string pattern = ".*";
// const std::string pattern = ".*";
auto handle_request = [&](const httplib::Request& req, httplib::Response& res) {
set_headers(res);
if (req.has_header("X-BMP-Authentication")) {
headers.emplace("X-BMP-Authentication", PrivateKey);
}
if (req.has_header("X-API-Version")) {
headers.emplace("X-API-Version", req.get_header_value("X-API-Version"));
}
// auto handle_request = [&](const httplib::Request& req, httplib::Response& res) {
// set_headers(res);
// if (req.has_header("X-BMP-Authentication")) {
// headers.emplace("X-BMP-Authentication", PrivateKey);
// }
// if (req.has_header("X-API-Version")) {
// headers.emplace("X-API-Version", req.get_header_value("X-API-Version"));
// }
const std::vector<std::string> path = Utils::Split(req.path, "/");
// const std::vector<std::string> path = Utils::Split(req.path, "/");
httplib::Result cli_res;
const std::string method = req.method;
std::string host = "";
// httplib::Result cli_res;
// const std::string method = req.method;
// std::string host = "";
if (!path.empty())
host = path[0];
// if (!path.empty())
// host = path[0];
if (host == "backend") {
std::string remaining_path = req.path.substr(std::strlen("/backend"));
// if (host == "backend") {
// std::string remaining_path = req.path.substr(std::strlen("/backend"));
if (method == "GET")
cli_res = backend.Get(remaining_path, headers);
else if (method == "POST")
cli_res = backend.Post(remaining_path, headers);
// if (method == "GET")
// cli_res = backend.Get(remaining_path, headers);
// else if (method == "POST")
// cli_res = backend.Post(remaining_path, headers);
} else if (host == "avatar") {
bool error = false;
std::string username;
std::string avatar_size = "100";
// } else if (host == "avatar") {
// bool error = false;
// std::string username;
// std::string avatar_size = "100";
if (path.size() > 1) {
username = path[1];
} else {
error = true;
}
// if (path.size() > 1) {
// username = path[1];
// } else {
// error = true;
// }
if (path.size() > 2) {
try {
if (std::stoi(path[2]) > 0)
avatar_size = path[2];
// if (path.size() > 2) {
// try {
// if (std::stoi(path[2]) > 0)
// avatar_size = path[2];
} catch (std::exception&) { }
}
// } catch (std::exception&) { }
// }
httplib::Result summary_res;
// httplib::Result summary_res;
if (!error) {
summary_res = forum.Get("/u/" + username + ".json", headers);
// if (!error) {
// summary_res = forum.Get("/u/" + username + ".json", headers);
if (!summary_res || summary_res->status != 200) {
error = true;
}
}
// if (!summary_res || summary_res->status != 200) {
// error = true;
// }
// }
if (!error) {
try {
nlohmann::json d = nlohmann::json::parse(summary_res->body, nullptr, false); // can fail with parse_error
// if (!error) {
// try {
// nlohmann::json d = nlohmann::json::parse(summary_res->body, nullptr, false); // can fail with parse_error
auto user = d.at("user"); // can fail with out_of_range
auto avatar_link_json = user.at("avatar_template"); // can fail with out_of_range
// auto user = d.at("user"); // can fail with out_of_range
// auto avatar_link_json = user.at("avatar_template"); // can fail with out_of_range
auto avatar_link = avatar_link_json.get<std::string>();
size_t start_pos = avatar_link.find("{size}");
if (start_pos != std::string::npos)
avatar_link.replace(start_pos, std::strlen("{size}"), avatar_size);
// auto avatar_link = avatar_link_json.get<std::string>();
// size_t start_pos = avatar_link.find("{size}");
// if (start_pos != std::string::npos)
// avatar_link.replace(start_pos, std::strlen("{size}"), avatar_size);
cli_res = forum.Get(avatar_link, headers);
// cli_res = forum.Get(avatar_link, headers);
} catch (std::exception&) {
error = true;
}
}
// } catch (std::exception&) {
// error = true;
// }
// }
if (error) {
cli_res = forum.Get("/user_avatar/forum.beammp.com/user/0/0.png", headers);
}
// if (error) {
// cli_res = forum.Get("/user_avatar/forum.beammp.com/user/0/0.png", headers);
// }
} else {
res.set_content("Host not found", "text/plain");
return;
}
// } else {
// res.set_content("Host not found", "text/plain");
// return;
// }
if (cli_res) {
res.set_content(cli_res->body, cli_res->get_header_value("Content-Type"));
} else {
res.set_content(to_string(cli_res.error()), "text/plain");
}
};
// if (cli_res) {
// res.set_content(cli_res->body, cli_res->get_header_value("Content-Type"));
// } else {
// res.set_content(to_string(cli_res.error()), "text/plain");
// }
// };
HTTPProxy.Get(pattern, [&](const httplib::Request& req, httplib::Response& res) {
handle_request(req, res);
});
// HTTPProxy.Get(pattern, [&](const httplib::Request& req, httplib::Response& res) {
// handle_request(req, res);
// });
HTTPProxy.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) {
handle_request(req, res);
});
// HTTPProxy.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) {
// handle_request(req, res);
// });
ProxyPort = HTTPProxy.bind_to_any_port("127.0.0.1");
debug("HTTP Proxy listening on port " + std::to_string(ProxyPort));
HTTPProxy.listen_after_bind();
});
proxy.detach();
// ProxyPort = HTTPProxy.bind_to_any_port("127.0.0.1");
// debug("HTTP Proxy listening on port " + std::to_string(ProxyPort));
// HTTPProxy.listen_after_bind();
// });
// proxy.detach();
}

View File

@@ -93,6 +93,8 @@ void UDPClientMain(const std::string& IP, int Port) {
ToServer->sin_port = htons(Port);
inet_pton(AF_INET, IP.c_str(), &ToServer->sin_addr);
UDPSock = socket(AF_INET, SOCK_DGRAM, 0);
if (!magic.empty())
UDPSend(magic);
GameSend("P" + std::to_string(ClientID));
TCPSend("H", TCPSock);
UDPSend("p");

View File

@@ -6,6 +6,7 @@
#include <filesystem>
#include "Utils.h"
#if defined(_WIN32)
#include <shlobj_core.h>
#elif defined(__linux__)
@@ -15,8 +16,6 @@
#include <vector>
#endif
#include "Logger.h"
#include "Utils.h"
#include <fstream>
#include <string>
#include <thread>
@@ -177,6 +176,10 @@ void LegitimacyCheck() {
std::string contents((std::istreambuf_iterator(beamngIni)), std::istreambuf_iterator<char>());
beamngIni.close();
if (contents.size() >= 3 && (unsigned char)contents[0] == 0xEF && (unsigned char)contents[1] == 0xBB && (unsigned char)contents[2] == 0xBF) {
contents = contents.substr(3);
}
auto ini = Utils::ParseINI(contents);
if (ini.empty())
lowExit(3);

View File

@@ -84,10 +84,10 @@ beammp_fs_string GetEN() {
}
std::string GetVer() {
return "2.6";
return "2.7";
}
std::string GetPatch() {
return ".3";
return ".0";
}
beammp_fs_string GetEP(const beammp_fs_char* P) {
@@ -195,44 +195,44 @@ void CheckName() {
}
void CheckForUpdates(const std::string& CV) {
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey);
std::string LatestVersion = HTTP::Get(
"https://backend.beammp.com/version/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(
// "https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey);
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
beammp_fs_string BP(GetBP() / GetEN()), Back(GetBP() / beammp_wide("BeamMP-Launcher.back"));
// transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
// beammp_fs_string BP(GetBP() / GetEN()), Back(GetBP() / beammp_wide("BeamMP-Launcher.back"));
std::string FileHash = Utils::GetSha256HashReallyFastFile(BP);
// std::string FileHash = Utils::GetSha256HashReallyFastFile(BP);
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
if (!options.no_update) {
info("Launcher update " + LatestVersion + " found!");
#if defined(__linux__)
error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches.");
#else
info("Downloading Launcher update " + LatestHash);
HTTP::Download(
"https://backend.beammp.com/builds/launcher?download=true"
"&pk="
+ PublicKey + "&branch=" + Branch,
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 {
warn("Launcher update was found, but not updating because --no-update or --dev was specified.");
}
} else
info("Launcher version is up to date. Latest version: " + LatestVersion);
TraceBack++;
// if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
// if (!options.no_update) {
// info("Launcher update " + LatestVersion + " found!");
// #if defined(__linux__)
// error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches.");
// #else
// info("Downloading Launcher update " + LatestHash);
// HTTP::Download(
// "https://backend.beammp.com/builds/launcher?download=true"
// "&pk="
// + PublicKey + "&branch=" + Branch,
// 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 {
// warn("Launcher update was found, but not updating because --no-update or --dev was specified.");
// }
// } else
// info("Launcher version is up to date. Latest version: " + LatestVersion);
// TraceBack++;
}
@@ -269,6 +269,8 @@ void LinuxPatch() {
void InitLauncher() {
SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str());
SetConsoleOutputCP(CP_UTF8);
_setmode(_fileno(stdout), _O_U8TEXT);
debug("Launcher Version : " + GetVer() + GetPatch());
CheckName();
LinuxPatch();
@@ -345,42 +347,42 @@ void PreGame(const beammp_fs_string& GamePath) {
CheckMP(GetGamePath() / beammp_wide("mods/multiplayer"));
info(beammp_wide("Game user path: ") + beammp_fs_string(GetGamePath()));
if (!options.no_download) {
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(),
[](auto const& c) -> bool { return !std::isalnum(c); }),
LatestHash.end());
// if (!options.no_download) {
// std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
// transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
// LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(),
// [](auto const& c) -> bool { return !std::isalnum(c); }),
// LatestHash.end());
try {
if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
}
EnableMP();
} catch (std::exception& e) {
fatal(e.what());
}
#if defined(_WIN32)
std::wstring ZipPath(GetGamePath() / LR"(mods\multiplayer\BeamMP.zip)");
#elif defined(__linux__)
// Linux version of the game cant handle mods with uppercase names
std::string ZipPath(GetGamePath() / R"(mods/multiplayer/beammp.zip)");
#endif
// try {
// if (!fs::exists(GetGamePath() / beammp_wide("mods/multiplayer"))) {
// fs::create_directories(GetGamePath() / beammp_wide("mods/multiplayer"));
// }
// EnableMP();
// } catch (std::exception& e) {
// fatal(e.what());
// }
// #if defined(_WIN32)
// std::wstring ZipPath(GetGamePath() / LR"(mods\multiplayer\BeamMP.zip)");
// #elif defined(__linux__)
// // Linux version of the game cant handle mods with uppercase names
// std::string ZipPath(GetGamePath() / R"(mods/multiplayer/beammp.zip)");
// #endif
std::string FileHash = fs::exists(ZipPath) ? Utils::GetSha256HashReallyFastFile(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, LatestHash);
}
// if (FileHash != LatestHash) {
// info("Downloading BeamMP Update " + LatestHash);
// HTTP::Download("https://backend.beammp.com/builds/client?download=true"
// "&pk="
// + PublicKey + "&branch=" + Branch,
// ZipPath, LatestHash);
// }
beammp_fs_string Target(GetGamePath() / beammp_wide("mods/unpacked/beammp"));
// beammp_fs_string Target(GetGamePath() / beammp_wide("mods/unpacked/beammp"));
if (fs::is_directory(Target) && !fs::is_directory(Target + beammp_wide("/.git"))) {
fs::remove_all(Target);
}
}
// if (fs::is_directory(Target) && !fs::is_directory(Target + beammp_wide("/.git"))) {
// fs::remove_all(Target);
// }
// }
}