mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2026-04-03 06:16:15 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e93fa35fa | ||
|
|
8373a70c4b | ||
|
|
d52a791dd9 | ||
|
|
08a6f9a093 | ||
|
|
e5e40e186b | ||
|
|
bfbff52cb1 | ||
|
|
8d4ba6f158 | ||
|
|
a5d450b680 | ||
|
|
f4e985976f | ||
|
|
db9ec53a6e | ||
|
|
f9b2edd410 | ||
|
|
333a95262b | ||
|
|
e53885a8a8 | ||
|
|
c22ea1e85d | ||
|
|
f8ea9bd8a3 | ||
|
|
ad8eab3d66 | ||
|
|
e880da5cf9 | ||
|
|
d14b64c652 | ||
|
|
649514ca1a | ||
|
|
7149075d53 | ||
|
|
c485fba26b | ||
|
|
d35567dd47 |
@@ -49,7 +49,7 @@ In the root directory of the project,
|
||||
|
||||
## Running out of RAM while building
|
||||
|
||||
Should you run out of RAM while building, you can ommit the `--parallel` intruction, it will then use less RAM due to building only on one CPU thread.
|
||||
Should you run out of RAM while building, you can ommit the `--parallel` instruction, it will then use less RAM due to building only on one CPU thread.
|
||||
|
||||
You can also specify a number of threads to use, for example `--parallel 4` will use four CPU threads, but due to the small project size, you may be faster just omitting `--parallel` instead of trying to find the highest possible multithread number
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <bits/types/siginfo_t.h>
|
||||
#include <cstdint>
|
||||
#include <sys/ucontext.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
void NetReset();
|
||||
|
||||
@@ -89,6 +89,98 @@ void StartSync(const std::string& Data) {
|
||||
info("Connecting to server");
|
||||
}
|
||||
|
||||
void GetServerInfo(std::string Data) {
|
||||
debug("Fetching server info of " + Data.substr(1));
|
||||
|
||||
std::string IP = GetAddr(Data.substr(1, Data.find(':') - 1));
|
||||
if (IP.find('.') == -1) {
|
||||
if (IP == "DNS")
|
||||
warn("Connection Failed! (DNS Lookup Failed) for " + Data);
|
||||
else
|
||||
warn("Connection Failed! (WSA failed to start) for " + Data);
|
||||
CoreSend("I" + Data + ";");
|
||||
return;
|
||||
}
|
||||
|
||||
SOCKET ISock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
SOCKADDR_IN ServerAddr;
|
||||
if (ISock < 1) {
|
||||
debug("Socket creation failed with error: " + std::to_string(WSAGetLastError()));
|
||||
KillSocket(ISock);
|
||||
CoreSend("I" + Data + ";");
|
||||
return;
|
||||
}
|
||||
ServerAddr.sin_family = AF_INET;
|
||||
|
||||
int port = std::stoi(Data.substr(Data.find(':') + 1));
|
||||
|
||||
if (port < 1 || port > 65535) {
|
||||
debug("Invalid port number: " + std::to_string(port));
|
||||
KillSocket(ISock);
|
||||
CoreSend("I" + Data + ";");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerAddr.sin_port = htons(port);
|
||||
inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr);
|
||||
if (connect(ISock, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) != 0) {
|
||||
debug("Connection to server failed with error: " + std::to_string(WSAGetLastError()));
|
||||
KillSocket(ISock);
|
||||
CoreSend("I" + Data + ";");
|
||||
return;
|
||||
}
|
||||
|
||||
char Code[1] = { 'I' };
|
||||
if (send(ISock, Code, 1, 0) != 1) {
|
||||
debug("Sending data to server failed with error: " + std::to_string(WSAGetLastError()));
|
||||
KillSocket(ISock);
|
||||
CoreSend("I" + Data + ";");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string buffer = ([&]() -> std::string {
|
||||
int32_t Header;
|
||||
std::vector<char> data(sizeof(Header));
|
||||
int32_t Temp = recv(ISock, data.data(), sizeof(Header), MSG_WAITALL);
|
||||
|
||||
auto checkBytes = ([&](const int32_t bytes) -> bool {
|
||||
if (bytes == 0) {
|
||||
return false;
|
||||
} else if (bytes < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!checkBytes(Temp)) {
|
||||
return "";
|
||||
}
|
||||
memcpy(&Header, data.data(), sizeof(Header));
|
||||
|
||||
if (!checkBytes(Temp)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
data.resize(Header, 0);
|
||||
Temp = recv(ISock, data.data(), Header, MSG_WAITALL);
|
||||
if (!checkBytes(Temp)) {
|
||||
return "";
|
||||
}
|
||||
return std::string(data.data(), Header);
|
||||
})();
|
||||
|
||||
if (!buffer.empty()) {
|
||||
debug("Server Info: " + buffer);
|
||||
|
||||
CoreSend("I" + Data + ";" + buffer);
|
||||
} else {
|
||||
debug("Receiving data from server failed with error: " + std::to_string(WSAGetLastError()));
|
||||
debug("Failed to receive server info from " + Data);
|
||||
CoreSend("I" + Data + ";");
|
||||
}
|
||||
|
||||
KillSocket(ISock);
|
||||
}
|
||||
std::mutex sendMutex;
|
||||
|
||||
void CoreSend(std::string data) {
|
||||
@@ -235,6 +327,12 @@ void Parse(std::string Data, SOCKET CSocket) {
|
||||
|
||||
Data.clear();
|
||||
break;
|
||||
case 'I': {
|
||||
auto future = std::async(std::launch::async, [data = std::move(Data)]() {
|
||||
GetServerInfo(data);
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Data.clear();
|
||||
break;
|
||||
|
||||
@@ -73,14 +73,18 @@ std::string HTTP::Get(const std::string& IP) {
|
||||
static thread_local CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
CURLcode res;
|
||||
char errbuf[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(curl, CURLOPT_URL, IP.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 120); // seconds
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
|
||||
errbuf[0] = 0;
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
error("GET to " + IP + " failed: " + std::string(curl_easy_strerror(res)));
|
||||
error("Curl error: " + std::string(errbuf));
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
@@ -95,6 +99,7 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
|
||||
static thread_local CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
CURLcode res;
|
||||
char errbuf[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(curl, CURLOPT_URL, IP.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
||||
@@ -104,12 +109,15 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
|
||||
struct curl_slist* list = nullptr;
|
||||
list = curl_slist_append(list, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 120); // seconds
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
|
||||
errbuf[0] = 0;
|
||||
res = curl_easy_perform(curl);
|
||||
curl_slist_free_all(list);
|
||||
if (res != CURLE_OK) {
|
||||
error("POST to " + IP + " failed: " + std::string(curl_easy_strerror(res)));
|
||||
error("Curl error: " + std::string(errbuf));
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -382,7 +382,7 @@ struct ModInfo {
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
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.");
|
||||
debug("Failed to receive new mod list format! This server may be outdated, but everything should still work as expected.");
|
||||
}
|
||||
return std::make_pair(success, modInfos);
|
||||
}
|
||||
@@ -392,6 +392,52 @@ struct ModInfo {
|
||||
std::string HashAlgorithm;
|
||||
};
|
||||
|
||||
nlohmann::json modUsage = {};
|
||||
|
||||
void UpdateModUsage(const std::string& fileName) {
|
||||
try {
|
||||
fs::path usageFile = fs::path(CachingDirectory) / "mods.json";
|
||||
|
||||
if (!fs::exists(usageFile)) {
|
||||
if (std::ofstream file(usageFile); !file.is_open()) {
|
||||
error("Failed to create mods.json");
|
||||
return;
|
||||
} else {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
std::fstream file(usageFile, std::ios::in | std::ios::out);
|
||||
if (!file.is_open()) {
|
||||
error("Failed to open or create mods.json");
|
||||
return;
|
||||
}
|
||||
|
||||
if (modUsage.empty()) {
|
||||
auto Size = fs::file_size(fs::path(CachingDirectory) / "mods.json");
|
||||
std::string modsJson(Size, 0);
|
||||
file.read(&modsJson[0], Size);
|
||||
|
||||
if (!modsJson.empty()) {
|
||||
auto parsedModJson = nlohmann::json::parse(modsJson, nullptr, false);
|
||||
|
||||
if (parsedModJson.is_object())
|
||||
modUsage = parsedModJson;
|
||||
}
|
||||
}
|
||||
|
||||
modUsage[fileName] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
file.clear();
|
||||
file.seekp(0, std::ios::beg);
|
||||
file << modUsage.dump();
|
||||
file.close();
|
||||
} catch (std::exception& e) {
|
||||
error("Failed to update mods.json: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<ModInfo> ModInfos) {
|
||||
if (ModInfos.empty()) {
|
||||
CoreSend("L");
|
||||
@@ -451,6 +497,42 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
||||
|
||||
fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
|
||||
fs::rename(tmp_name, name);
|
||||
UpdateModUsage(FileName);
|
||||
} catch (std::exception& e) {
|
||||
error("Failed copy to the mods folder! " + std::string(e.what()));
|
||||
Terminate = true;
|
||||
continue;
|
||||
}
|
||||
WaitForConfirm();
|
||||
continue;
|
||||
} else if (auto OldCachedPath = fs::path(CachingDirectory) / std::filesystem::path(ModInfoIter->FileName).filename();
|
||||
fs::exists(OldCachedPath) && GetSha256HashReallyFast(OldCachedPath.string()) == ModInfoIter->Hash) {
|
||||
debug("Mod '" + FileName + "' found in old cache, copying it to the new cache");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
try {
|
||||
fs::copy_file(OldCachedPath, PathToSaveTo, fs::copy_options::overwrite_existing);
|
||||
|
||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
||||
}
|
||||
|
||||
auto modname = ModInfoIter->FileName;
|
||||
|
||||
#if defined(__linux__)
|
||||
// Linux version of the game doesnt support uppercase letters in mod names
|
||||
for (char& c : modname) {
|
||||
c = ::tolower(c);
|
||||
}
|
||||
#endif
|
||||
|
||||
debug("Mod name: " + modname);
|
||||
auto name = std::filesystem::path(GetGamePath()) / "mods/multiplayer" / modname;
|
||||
std::string tmp_name = name.string();
|
||||
tmp_name += ".tmp";
|
||||
|
||||
fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
|
||||
fs::rename(tmp_name, name);
|
||||
UpdateModUsage(FileName);
|
||||
} catch (std::exception& e) {
|
||||
error("Failed copy to the mods folder! " + std::string(e.what()));
|
||||
Terminate = true;
|
||||
@@ -505,6 +587,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
|
||||
#endif
|
||||
|
||||
fs::copy_file(PathToSaveTo, std::filesystem::path(GetGamePath()) / "mods/multiplayer" / FName, fs::copy_options::overwrite_existing);
|
||||
UpdateModUsage(FName);
|
||||
}
|
||||
WaitForConfirm();
|
||||
++ModNo;
|
||||
@@ -606,6 +689,7 @@ void SyncResources(SOCKET Sock) {
|
||||
auto tmp_name = name + ".tmp";
|
||||
fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
|
||||
fs::rename(tmp_name, name);
|
||||
UpdateModUsage(modname);
|
||||
} catch (std::exception& e) {
|
||||
error("Failed copy to the mods folder! " + std::string(e.what()));
|
||||
Terminate = true;
|
||||
@@ -661,6 +745,7 @@ void SyncResources(SOCKET Sock) {
|
||||
#endif
|
||||
|
||||
fs::copy_file(PathToSaveTo, GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing);
|
||||
UpdateModUsage(FN->substr(pos));
|
||||
}
|
||||
WaitForConfirm();
|
||||
}
|
||||
|
||||
@@ -81,10 +81,10 @@ std::string GetEN() {
|
||||
}
|
||||
|
||||
std::string GetVer() {
|
||||
return "2.3";
|
||||
return "2.4";
|
||||
}
|
||||
std::string GetPatch() {
|
||||
return ".2";
|
||||
return ".0";
|
||||
}
|
||||
|
||||
std::string GetEP(const char* P) {
|
||||
@@ -175,7 +175,7 @@ void CheckForUpdates(const std::string& CV) {
|
||||
|
||||
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
|
||||
if (!options.no_update) {
|
||||
info("Launcher update found!");
|
||||
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
|
||||
@@ -193,7 +193,7 @@ void CheckForUpdates(const std::string& CV) {
|
||||
warn("Launcher update was found, but not updating because --no-update or --dev was specified.");
|
||||
}
|
||||
} else
|
||||
info("Launcher version is up to date");
|
||||
info("Launcher version is up to date. Latest version: " + LatestVersion);
|
||||
TraceBack++;
|
||||
}
|
||||
|
||||
@@ -231,6 +231,7 @@ void LinuxPatch() {
|
||||
|
||||
void InitLauncher() {
|
||||
SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str());
|
||||
debug("Launcher Version : " + GetVer() + GetPatch());
|
||||
CheckName();
|
||||
LinuxPatch();
|
||||
CheckLocalKey();
|
||||
|
||||
Reference in New Issue
Block a user