30 Commits

Author SHA1 Message Date
Tixx
dff2f2712b Bump version 2024-11-07 22:12:20 +01:00
Lion
3effe0d4de log zlib error message and regex fix (#146) 2024-11-07 22:05:40 +01:00
Tixx
d58ff960ec Fix github regex 2024-11-07 21:39:18 +01:00
Tixx
f67f8573e0 Log zlib error messages 2024-11-07 21:36:38 +01:00
Lion
8a8e0be1a1 Print message from auth (#141) 2024-11-05 10:32:39 +01:00
Tixx
e0041666ca Clarify and change auth message log 2024-11-05 10:26:10 +01:00
Tixx
ed686333ec Print message from auth 2024-11-05 10:15:10 +01:00
Lion
8938fd84ea Add beammp.gg to the list of allowed links (#143) 2024-11-02 23:29:40 +01:00
Tixx
bd4c9c34a9 Add BeamMP github to the list of allowed links 2024-11-02 22:33:26 +01:00
Tixx
8519e279a7 Add beammp.gg to the list of allowed links 2024-11-02 22:18:46 +01:00
Lion Kortlepel
54895eb1b0 bump version 2024-11-01 12:53:55 +01:00
Lion
1423c1193b Speed up response times by waiting for http requests on another thread. (#137)
If, for example, the client requests the serverlist multiple times and
then tries to login the launcher will first wait for those requests to
finish. Thereby putting the other core communication (such as login) on
hold.
2024-11-01 12:13:21 +01:00
Lion
288e76594d Fix port cli argument (#142)
Fixes --port and -p by proccessing the config file before the cli
arguments, before it would first set --port because it was passed and
then overwrite it with the value from the config. Also removed some
useless code related to cli args.
2024-11-01 12:11:26 +01:00
Tixx
4fdc3c4031 Fix --port 2024-10-20 16:59:00 +02:00
Tixx
708da44fec Remove unused code 2024-10-20 16:57:47 +02:00
Tixx
6b6e304cfd Switch to std::async 2024-10-18 19:23:53 +02:00
Tixx
06cb366bb5 Add mutex to CoreSend 2024-10-16 23:12:02 +02:00
Tixx
0b35f0484f put blocking http requests on another thread 2024-10-16 23:12:02 +02:00
Lion
9dbbd8298d Switch to only timeout on connection (#140) 2024-10-15 19:32:55 +02:00
Tixx
ca9dd1ae75 Switch to only timeout on connection 2024-10-14 20:29:19 +02:00
Lion
9ebd218856 Fix empty modlist (#136)
This PR fixes the launcher getting confused when the server sends an
empty mod list using the new downloading system.
Related server PR: https://github.com/BeamMP/BeamMP-Server/pull/377
2024-10-12 22:10:47 +02:00
Tixx
d9874ce70e Make return from parsemodinfo look better 2024-10-12 21:12:12 +02:00
Tixx
423519f31e Only listen on localhost ipv4 (#134)
This avoids the firewall popup on windows.
2024-10-12 20:58:29 +02:00
Tixx
3f12bb757a Mod info logs and check for old format 2024-10-10 21:35:27 +02:00
Lion Kortlepel
7d52e44434 only listen on localhost ipv4 2024-10-10 16:14:16 +02:00
Tixx
4fbd25b551 Handle new modlist being empty but still valid 2024-10-09 19:41:38 +02:00
Tixx
3cf1a2e51b Add mod info debug log 2024-10-09 19:39:27 +02:00
Lion Kortlepel
49874fd633 Revert "remove 'D' socket initialization code"
This reverts commit 6a23518eff.
2024-10-09 18:00:43 +02:00
Lion Kortlepel
6a23518eff remove 'D' socket initialization code 2024-10-09 17:36:54 +02:00
Lion Kortlepel
3297b3e62e fix not recognizing empty mod lists on new mod list 2024-10-09 17:35:50 +02:00
9 changed files with 60 additions and 47 deletions

1
.gitignore vendored
View File

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

View File

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

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: " + std::to_string(res)); error("zlib compress() failed (code: " + std::to_string(res) + ", message: " + zError(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: " + std::to_string(res)); error("zlib uncompress() failed (code: " + std::to_string(res) + ", message: " + zError(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

@@ -30,8 +30,11 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <set> #include <set>
#include <thread> #include <thread>
#include <mutex>
#include "Options.h" #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;
@@ -87,7 +90,11 @@ 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) {
@@ -97,7 +104,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|discord\.gg|patreon\.com\/BeamMP))"); std::regex link_pattern(R"(https:\/\/(?:\w+)?(?:\.)?(?:beammp\.com|beammp\.gg|github\.com\/BeamMP\/|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;
} }
@@ -110,11 +117,15 @@ 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 = Code + HTTP::Get("https://backend.beammp.com/servers-info"); Data.clear();
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);
@@ -210,7 +221,10 @@ void Parse(std::string Data, SOCKET CSocket) {
} }
Data = "N" + Auth.dump(); Data = "N" + Auth.dump();
} else { } else {
Data = "N" + Login(Data.substr(Data.find(':') + 1)); auto future = std::async(std::launch::async, [data = std::move(Data)]() {
CoreSend("N" + Login(data.substr(data.find(':') + 1)));
});
Data.clear();
} }
break; break;
case 'W': case 'W':
@@ -226,12 +240,8 @@ void Parse(std::string Data, SOCKET CSocket) {
Data.clear(); Data.clear();
break; break;
} }
if (!Data.empty() && CSocket != -1) { if (!Data.empty())
int res = send(CSocket, (Data + "\n").c_str(), int(Data.size()) + 1, 0); CoreSend(Data);
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;
@@ -306,7 +316,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(nullptr, std::to_string(options.port).c_str(), &hints, &res); iRes = getaddrinfo("127.0.0.1", std::to_string(options.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

@@ -77,7 +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_TIMEOUT, 10); // seconds curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 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) {
@@ -105,7 +105,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_TIMEOUT, 10); // seconds curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 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);
@@ -261,7 +261,7 @@ void HTTP::StartProxy() {
handle_request(req, res); handle_request(req, res);
}); });
ProxyPort = HTTPProxy.bind_to_any_port("0.0.0.0"); ProxyPort = HTTPProxy.bind_to_any_port("127.0.0.1");
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,10 +362,15 @@ std::string GetSha256HashReallyFast(const std::string& filename) {
} }
struct ModInfo { struct ModInfo {
static std::vector<ModInfo> ParseModInfosFromPacket(const std::string& packet) { static std::pair<bool, 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"],
@@ -374,13 +379,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());
error("Failed to receive mod list!"); warn("Failed to receive new mod list format! This server may be outdated, but everything should still work as expected.");
// TODO: Cry and die
} }
return modInfos; return std::make_pair(success, modInfos);
} }
std::string FileName; std::string FileName;
size_t FileSize; size_t FileSize;
@@ -389,6 +394,13 @@ 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;
@@ -511,11 +523,17 @@ 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);
auto ModInfos = ModInfo::ParseModInfosFromPacket(Ret); debug("Mod info: " + Ret);
if (!ModInfos.empty()) { if (Ret.starts_with("R")) {
NewSyncResources(Sock, Ret, ModInfos); debug("This server is likely outdated, not trying to parse new mod info format");
return; } else {
auto [success, modInfo] = ModInfo::ParseModInfosFromPacket(Ret);
if (success) {
NewSyncResources(Sock, Ret, modInfo);
return;
}
} }
if (Ret.empty()) if (Ret.empty())

View File

@@ -91,6 +91,7 @@ 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

@@ -82,10 +82,10 @@ std::string GetEN() {
} }
std::string GetVer() { std::string GetVer() {
return "2.2"; return "2.3";
} }
std::string GetPatch() { std::string GetPatch() {
return ".0"; return ".2";
} }
std::string GetEP(const char* P) { std::string GetEP(const char* P) {
@@ -232,11 +232,9 @@ void LinuxPatch() {
void InitLauncher() { void InitLauncher() {
SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str()); SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str());
InitLog();
CheckName(); CheckName();
LinuxPatch(); LinuxPatch();
CheckLocalKey(); CheckLocalKey();
ConfigInit();
CheckForUpdates(std::string(GetVer()) + GetPatch()); CheckForUpdates(std::string(GetVer()) + GetPatch());
} }
#elif defined(__linux__) #elif defined(__linux__)
@@ -245,7 +243,6 @@ void InitLauncher() {
info("BeamMP Launcher v" + GetVer() + GetPatch()); info("BeamMP Launcher v" + GetVer() + GetPatch());
CheckName(); CheckName();
CheckLocalKey(); CheckLocalKey();
ConfigInit();
CheckForUpdates(std::string(GetVer()) + GetPatch()); CheckForUpdates(std::string(GetVer()) + GetPatch());
} }
#endif #endif

View File

@@ -38,23 +38,10 @@ 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]);
for (int i = 0; i < argc; ++i) {
if (std::string_view(argv[i]) == "--skip-ssl-verify") {
info("SSL verification skip enabled");
HTTP::SkipSslVerify = true;
}
}
InitLog(); InitLog();
ConfigInit();
InitOptions(argc, argv, options); InitOptions(argc, argv, options);
InitLauncher(); InitLauncher();