18 Commits

Author SHA1 Message Date
Starystars67
7217cb1cab Update Startup.cpp 2023-12-16 03:01:28 +00:00
Anonymous275
c2f260a86c v2.0.84
- remove comment
2023-12-15 19:39:55 +00:00
Anonymous275
2781179b4b v2.0.84
- proxy tweaking
2023-12-15 19:38:47 +00:00
Anonymous275
3b479abf64 v2.0.84
- add hash check
- new routes for updates
- use C++ 20
2023-12-15 18:16:12 +00:00
Anonymous275
c731718f50 v2.0.84
- HTTP Proxy for backend.beammp.com
- Fix Attempt for mod loading, game detecting partial zip file
- Use nlohmann JSON
- Update vcpkg parameters and commit ID
- Add ability to open URL using default browser with filter
2023-12-15 17:38:47 +00:00
Anonymous-275
0fd0a9fe7e Merge remote-tracking branch 'origin/master' 2023-10-26 00:09:04 +01:00
Anonymous-275
302582bfe1 v2.0.83
- removed code that is no longer needed
2023-10-26 00:08:56 +01:00
Anonymous275
839bb48cd6 Merge pull request #58 from WhiteHusky/patch-1
Emit a useful message if cleaning the mods folder fails
2023-08-04 20:30:36 +01:00
Carlen White
c4ebdda1a4 Emit a useful message if cleaning the mods folder fails
The launcher complains if it can not delete a file when it's trying to clean the mods folder out. However the message it emits is unhelpful and does not offer a suggestion to what is going on.

This commit addresses this issue by telling the user what the launcher is trying to do and suggests checking if something is currently open in that directory. I figure not having to go into detail in *where* the folder is not necessary and (primarily) only happens if the user is inspecting the folder themselves and already knows where the folder is.
2023-07-02 23:02:14 -04:00
Anonymous275
d6b494c6c4 - release v2.0.82 2022-12-18 14:30:21 +00:00
Anonymous275
a80d4f5147 - quick fix for launcher crash 2022-12-18 14:29:38 +00:00
Anonymous275
811b04485c - try fix for github workflow 2022-12-18 13:20:20 +00:00
Simon Abed El Sater
a64fead653 update 2.0.81 2022-12-18 14:39:54 +02:00
Simon Abed El Sater
399461d1b1 remove version check 2022-12-18 14:39:28 +02:00
Anonymous275
ec5e8ed5b3 v2.0.80 2022-09-25 20:39:27 +03:00
Anonymous275
5655164e60 bump 2.0.80 2022-09-25 20:37:46 +03:00
Simon Abed El Sater
3d9b7c2d67 Merge pull request #49 from snepsnepsnep/master
Fix kick message for server 3.0+
2022-09-25 20:32:50 +03:00
snepsnepsnep
764e3ab5c1 Fix kick message for server 3.0+ 2022-09-25 19:26:46 +02:00
20 changed files with 4882 additions and 7746 deletions

View File

@@ -18,9 +18,9 @@ jobs:
uses: lukka/run-vcpkg@v7 uses: lukka/run-vcpkg@v7
id: runvcpkg id: runvcpkg
with: with:
vcpkgArguments: 'discord-rpc zlib rapidjson openssl' vcpkgArguments: 'discord-rpc zlib nlohmann-json openssl cpp-httplib[openssl]'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '86ff75c6d8232b54d3ebd0e71525b4634dcd9523' vcpkgGitCommitId: '16ee2ecb31788c336ace8bb14c21801efb6836e4'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment

View File

@@ -42,9 +42,9 @@ jobs:
uses: lukka/run-vcpkg@main uses: lukka/run-vcpkg@main
id: runvcpkg id: runvcpkg
with: with:
vcpkgArguments: 'discord-rpc zlib rapidjson openssl' vcpkgArguments: 'discord-rpc zlib nlohmann-json openssl cpp-httplib[openssl]'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '86ff75c6d8232b54d3ebd0e71525b4634dcd9523' vcpkgGitCommitId: '16ee2ecb31788c336ace8bb14c21801efb6836e4'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment

View File

@@ -7,15 +7,18 @@ if (WIN32)
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
endif(WIN32) endif(WIN32)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h") file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h")
find_package(httplib CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
add_executable(${PROJECT_NAME} ${source_files}) add_executable(${PROJECT_NAME} ${source_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher") set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher")
if (WIN32) if (WIN32)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
@@ -23,8 +26,14 @@ if (WIN32)
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}) set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
include_directories(${VcpkgRoot}/include) include_directories(${VcpkgRoot}/include)
link_directories(${VcpkgRoot}/lib) link_directories(${VcpkgRoot}/lib)
target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/lib/discord-rpc.lib target_link_libraries(${PROJECT_NAME} PRIVATE
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32) ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/lib/discord-rpc.lib)
else ()
target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/debug/lib/discord-rpc.lib)
endif()
else(WIN32) #MINGW else(WIN32) #MINGW
add_definitions("-D_WIN32_WINNT=0x0600") add_definitions("-D_WIN32_WINNT=0x0600")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static")

View File

@@ -16,5 +16,4 @@ public:
static bool ProgressBar(size_t c, size_t t); static bool ProgressBar(size_t c, size_t t);
public: public:
static bool isDownload; static bool isDownload;
static std::string Codes_[];
}; };

View File

@@ -1,12 +0,0 @@
// 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.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 11/27/2020
///
#pragma once
#include "rapidjson/stringbuffer.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
namespace json = rapidjson;

View File

@@ -14,6 +14,7 @@ extern bool Dev;
extern int ping; extern int ping;
[[noreturn]] void CoreNetwork(); [[noreturn]] void CoreNetwork();
extern int ProxyPort;
extern int ClientID; extern int ClientID;
extern int LastPort; extern int LastPort;
extern bool ModLoaded; extern bool ModLoaded;
@@ -27,6 +28,7 @@ extern std::string LastIP;
extern std::string MStatus; extern std::string MStatus;
extern std::string UlStatus; extern std::string UlStatus;
extern std::string PublicKey; extern std::string PublicKey;
extern std::string PrivateKey;
extern std::string ListOfMods; extern std::string ListOfMods;
int KillSocket(uint64_t Dead); int KillSocket(uint64_t Dead);
void UUl(const std::string& R); void UUl(const std::string& R);

View File

@@ -7,10 +7,22 @@
/// ///
#pragma once #pragma once
#include <string> #include <string>
#include <compare>
#include <vector>
void InitLauncher(int argc, char* argv[]); void InitLauncher(int argc, char* argv[]);
std::string GetEP(char*P = nullptr); std::string GetEP(char*P = nullptr);
std::string GetGamePath(); std::string GetGamePath();
std::string GetVer(); std::string GetVer();
std::string GetEN(); std::string GetEN();
void StartProxy();
void ConfigInit(); void ConfigInit();
extern bool Dev; extern bool Dev;
struct VersionParser {
explicit VersionParser(const std::string& from_string);
std::strong_ordering operator<=>(VersionParser const& rhs) const noexcept;
bool operator==(VersionParser const& rhs) const noexcept;
std::vector<std::string> split;
std::vector<size_t> data;
};

4642
include/hashpp.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,18 +2,18 @@
/// Created by Anonymous275 on 2/23/2021 /// Created by Anonymous275 on 2/23/2021
/// ///
#include <nlohmann/json.hpp>
#include "Network/network.h" #include "Network/network.h"
#include <filesystem> #include <filesystem>
#include "Logger.h" #include "Logger.h"
#include <fstream> #include <fstream>
#include "Json.h"
#include <cstdint> #include <cstdint>
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::string Branch; std::string Branch;
void ParseConfig(const json::Document& d){ void ParseConfig(const nlohmann::json& d){
if(d["Port"].IsInt()){ if(d["Port"].is_number()){
DEFAULT_PORT = d["Port"].GetInt(); DEFAULT_PORT = d["Port"].get<int>();
} }
//Default -1 //Default -1
//Release 1 //Release 1
@@ -21,8 +21,8 @@ void ParseConfig(const json::Document& d){
//Dev 3 //Dev 3
//Custom 3 //Custom 3
if(d["Build"].IsString()){ if(d["Build"].is_string()){
Branch = d["Build"].GetString(); Branch = d["Build"].get<std::string>();
for(char& c : Branch)c = char(tolower(c)); for(char& c : Branch)c = char(tolower(c));
} }
} }
@@ -35,10 +35,9 @@ void ConfigInit(){
std::string Buffer(Size, 0); std::string Buffer(Size, 0);
cfg.read(&Buffer[0], Size); cfg.read(&Buffer[0], Size);
cfg.close(); cfg.close();
json::Document d; nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false);
d.Parse(Buffer.c_str()); if(d.is_discarded()){
if(d.HasParseError()){ fatal("Config failed to parse make sure it's valid JSON!");
fatal("Config failed to parse make sure it's valid JSON! Code : " + std::to_string(d.GetParseError()));
} }
ParseConfig(d); ParseConfig(d);
}else fatal("Failed to open Launcher.cfg!"); }else fatal("Failed to open Launcher.cfg!");

View File

@@ -99,8 +99,8 @@ void ErrorAboard(){
exit(6); exit(6);
} }
void Discord_Main(){ void Discord_Main(){
std::thread t1(DMain); /*std::thread t1(DMain);
t1.detach(); t1.detach();*/
/*info("Connecting to discord client..."); /*info("Connecting to discord client...");
int C = 0; int C = 0;
while(DiscordInfo == nullptr && C < 80){ while(DiscordInfo == nullptr && C < 80){

View File

@@ -61,6 +61,7 @@ void StartGame(std::string Dir){
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(5));
exit(2); exit(2);
} }
void InitGame(const std::string& Dir){ void InitGame(const std::string& Dir){
if(!Dev){ if(!Dev){
std::thread Game(StartGame, Dir); std::thread Game(StartGame, Dir);

View File

@@ -5,9 +5,10 @@
/// ///
/// Created by Anonymous275 on 7/20/2020 /// Created by Anonymous275 on 7/20/2020
/// ///
#include "Network/network.h" #include "Network/network.h"
#include "Security/Init.h" #include "Security/Init.h"
#include <regex>
#include "Http.h" #include "Http.h"
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
@@ -47,6 +48,13 @@ void StartSync(const std::string &Data){
GS.detach(); GS.detach();
info("Connecting to server"); info("Connecting to server");
} }
bool IsAllowedLink(const std::string& Link) {
std::regex link_pattern(R"(https:\/\/(?:\w+)?(?:\.)?(?:beammp\.com|discord\.gg))");
std::smatch link_match;
return std::regex_search(Link,link_match, link_pattern) && link_match.position() == 0;
}
void Parse(std::string Data,SOCKET CSocket){ void Parse(std::string Data,SOCKET CSocket){
char Code = Data.at(0), SubCode = 0; char Code = Data.at(0), SubCode = 0;
if(Data.length() > 1)SubCode = Data.at(1); if(Data.length() > 1)SubCode = Data.at(1);
@@ -69,6 +77,16 @@ void Parse(std::string Data,SOCKET CSocket){
if(ListOfMods == "-")Data = "L"; if(ListOfMods == "-")Data = "L";
else Data = "L"+ListOfMods; else Data = "L"+ListOfMods;
break; break;
case 'O': //open default browser with URL
if(IsAllowedLink(Data.substr(1))) {
ShellExecuteA(nullptr, "open", Data.substr(1).c_str(), nullptr, nullptr,SW_SHOW); ///TODO: Look at when working on linux port
info("Opening Link \"" + Data.substr(1) + "\"");
}
Data.clear();
break;
case 'P':
Data = Code + std::to_string(ProxyPort);
break;
case 'U': case 'U':
if(SubCode == 'l')Data = UlStatus; if(SubCode == 'l')Data = UlStatus;
if(SubCode == 'p'){ if(SubCode == 'p'){

View File

@@ -15,13 +15,6 @@
#include <cmath> #include <cmath>
#include <httplib.h> #include <httplib.h>
std::string HTTP::Codes_[] =
{
"Success","Unknown","Connection","BindIPAddress",
"Read","Write","ExceedRedirectCount","Canceled",
"SSLConnection","SSLLoadingCerts","SSLServerVerification",
"UnsupportedMultipartBoundaryChars","Compression"
};
bool HTTP::isDownload = false; bool HTTP::isDownload = false;
std::string HTTP::Get(const std::string &IP) { std::string HTTP::Get(const std::string &IP) {
static std::mutex Lock; static std::mutex Lock;
@@ -35,7 +28,7 @@ std::string HTTP::Get(const std::string &IP) {
auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar); auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar);
std::string Ret; std::string Ret;
if(res.error() == 0){ if(res){
if(res->status == 200){ if(res->status == 200){
Ret = res->body; Ret = res->body;
}else error(res->reason); }else error(res->reason);
@@ -44,7 +37,7 @@ std::string HTTP::Get(const std::string &IP) {
if(isDownload) { if(isDownload) {
std::cout << "\n"; std::cout << "\n";
} }
error("HTTP Get failed on " + Codes_[res.error()]); error("HTTP Get failed on " + to_string(res.error()));
} }
return Ret; return Ret;
@@ -63,23 +56,23 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
if(!Fields.empty()) { if(!Fields.empty()) {
httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json"); httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json");
if(res.error() == 0) { if(res) {
if (res->status != 200) { if (res->status != 200) {
error(res->reason); error(res->reason);
} }
Ret = res->body; Ret = res->body;
}else{ }else{
error("HTTP Post failed on " + Codes_[res.error()]); error("HTTP Post failed on " + to_string(res.error()));
} }
}else{ }else{
httplib::Result res = cli.Post(IP.substr(pos).c_str()); httplib::Result res = cli.Post(IP.substr(pos).c_str());
if(res.error() == 0) { if(res) {
if (res->status != 200) { if (res->status != 200) {
error(res->reason); error(res->reason);
} }
Ret = res->body; Ret = res->body;
}else{ }else{
error("HTTP Post failed on " + Codes_[res.error()]); error("HTTP Post failed on " + to_string(res.error()));
} }
} }

View File

@@ -60,7 +60,7 @@ std::string Auth(SOCKET Sock){
auto Res = TCPRcv(Sock); auto Res = TCPRcv(Sock);
if(Res.empty() || Res[0] == 'E'){ if(Res.empty() || Res[0] == 'E' || Res[0] == 'K'){
Abord(); Abord();
return ""; return "";
} }
@@ -87,7 +87,7 @@ std::string Auth(SOCKET Sock){
Res = TCPRcv(Sock); Res = TCPRcv(Sock);
if(Res[0] == 'E'){ if(Res[0] == 'E' || Res[0] == 'K'){
Abord(); Abord();
return ""; return "";
} }
@@ -102,7 +102,7 @@ std::string Auth(SOCKET Sock){
return Res; return Res;
} }
void UpdateUl(bool D,const std::string&msg){ void UpdateUl(bool D,const std::string& msg){
if(D)UlStatus = "UlDownloading Resource " + msg; if(D)UlStatus = "UlDownloading Resource " + msg;
else UlStatus = "UlLoading Resource " + msg; else UlStatus = "UlLoading Resource " + msg;
} }
@@ -267,8 +267,10 @@ void SyncResources(SOCKET Sock){
if(!fs::exists(GetGamePath() + "mods/multiplayer")){ if(!fs::exists(GetGamePath() + "mods/multiplayer")){
fs::create_directories(GetGamePath() + "mods/multiplayer"); fs::create_directories(GetGamePath() + "mods/multiplayer");
} }
fs::copy_file(a, GetGamePath() + "mods/multiplayer" + a.substr(a.find_last_of('/')), auto name = GetGamePath() + "mods/multiplayer" + a.substr(a.find_last_of('/'));
fs::copy_options::overwrite_existing); auto tmp_name = name + ".tmp";
fs::copy_file(a,tmp_name,fs::copy_options::overwrite_existing);
fs::rename(tmp_name, name);
} catch (std::exception& e) { } catch (std::exception& e) {
error("Failed copy to the mods folder! " + std::string(e.what())); error("Failed copy to the mods folder! " + std::string(e.what()));
Terminate = true; Terminate = true;

View File

@@ -109,7 +109,7 @@ std::string TCPRcv(SOCKET Sock){
#ifdef DEBUG #ifdef DEBUG
//debug("Parsing from server -> " + std::to_string(Ret.size())); //debug("Parsing from server -> " + std::to_string(Ret.size()));
#endif #endif
if(Ret[0] == 'E')UUl(Ret.substr(1)); if(Ret[0] == 'E' || Ret[0] == 'K')UUl(Ret.substr(1));
return Ret; return Ret;
} }

View File

@@ -144,9 +144,9 @@ void FileList(std::vector<std::string>&a,const std::string& Path){
for (const auto &entry : fs::directory_iterator(Path)) { for (const auto &entry : fs::directory_iterator(Path)) {
const auto& DPath = entry.path(); const auto& DPath = entry.path();
if (!entry.is_directory()) { if (!entry.is_directory()) {
a.emplace_back(DPath.u8string()); a.emplace_back(DPath.string());
}else if(NameValid(DPath.filename().u8string())){ }else if(NameValid(DPath.filename().string())){
FileList(a, DPath.u8string()); FileList(a, DPath.string());
} }
} }
} }
@@ -165,7 +165,7 @@ bool Find(const std::string& FName,const std::string& Path){
bool FindHack(const std::string& Path){ bool FindHack(const std::string& Path){
bool s = true; bool s = true;
for (const auto &entry : fs::directory_iterator(Path)) { for (const auto &entry : fs::directory_iterator(Path)) {
std::string Name = entry.path().filename().u8string(); std::string Name = entry.path().filename().string();
for(char&c : Name)c = char(tolower(c)); for(char&c : Name)c = char(tolower(c));
if(Name == "steam.exe")s = false; if(Name == "steam.exe")s = false;
if(Name.find("greenluma") != -1){ if(Name.find("greenluma") != -1){

View File

@@ -6,19 +6,22 @@
/// Created by Anonymous275 on 11/26/2020 /// Created by Anonymous275 on 11/26/2020
/// ///
#include <nlohmann/json.hpp>
#include "Http.h" #include "Http.h"
#include <filesystem> #include <filesystem>
#include "Logger.h" #include "Logger.h"
#include <fstream> #include <fstream>
#include "Json.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::string PublicKey; std::string PublicKey;
std::string PrivateKey;
extern bool LoginAuth; extern bool LoginAuth;
std::string Role; std::string Role;
void UpdateKey(const char* newKey){ void UpdateKey(const char* newKey){
if(newKey && std::isalnum(newKey[0])){ if(newKey && std::isalnum(newKey[0])){
PrivateKey = newKey;
std::ofstream Key("key"); std::ofstream Key("key");
if(Key.is_open()){ if(Key.is_open()){
Key << newKey; Key << newKey;
@@ -48,33 +51,31 @@ std::string Login(const std::string& fields){
} }
info("Attempting to authenticate..."); info("Attempting to authenticate...");
std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields); std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields);
json::Document d;
d.Parse(Buffer.c_str());
if(Buffer == "-1"){ if(Buffer == "-1"){
return GetFail("Failed to communicate with the auth system!"); return GetFail("Failed to communicate with the auth system!");
} }
if (Buffer.at(0) != '{' || d.HasParseError()) { nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false);
if (Buffer.at(0) != '{' || d.is_discarded()) {
error(Buffer); error(Buffer);
return GetFail("Invalid answer from authentication servers, please try again later!"); return GetFail("Invalid answer from authentication servers, please try again later!");
} }
if(!d["success"].IsNull() && d["success"].GetBool()){ if(d.contains("success") && d["success"].get<bool>()){
LoginAuth = true; LoginAuth = true;
if(!d["private_key"].IsNull()){ if(!d.contains("private_key")) {
UpdateKey(d["private_key"].GetString()); UpdateKey(d["private_key"].get<std::string>().c_str());
} }
if(!d["public_key"].IsNull()){ if(!d.contains("public_key")){
PublicKey = d["public_key"].GetString(); PublicKey = d["public_key"].get<std::string>();
} }
info("Authentication successful!"); info("Authentication successful!");
}else info("Authentication failed!"); }else info("Authentication failed!");
if(!d["message"].IsNull()){ if(!d.contains("message")){
d.RemoveMember("private_key"); d.erase("private_key");
d.RemoveMember("public_key"); d.erase("public_key");
rapidjson::StringBuffer buffer; return d.dump();
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
d.Accept(writer);
return buffer.GetString();
} }
return GetFail("Invalid message parsing!"); return GetFail("Invalid message parsing!");
} }
@@ -97,18 +98,18 @@ void CheckLocalKey(){
Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}"); Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}");
json::Document d; nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false);
d.Parse(Buffer.c_str());
if (Buffer == "-1" || Buffer.at(0) != '{' || d.HasParseError()) { if (Buffer == "-1" || Buffer.at(0) != '{' || d.is_discarded()) {
error(Buffer); error(Buffer);
info("Invalid answer from authentication servers."); info("Invalid answer from authentication servers.");
UpdateKey(nullptr); UpdateKey(nullptr);
} }
if(d["success"].GetBool()){ if(d["success"].get<bool>()){
LoginAuth = true; LoginAuth = true;
UpdateKey(d["private_key"].GetString()); UpdateKey(d["private_key"].get<std::string>().c_str());
PublicKey = d["public_key"].GetString(); PublicKey = d["public_key"].get<std::string>();
Role = d["role"].GetString(); Role = d["role"].get<std::string>();
//info(Role); //info(Role);
}else{ }else{
info("Auto-Authentication unsuccessful please re-login!"); info("Auto-Authentication unsuccessful please re-login!");

View File

@@ -5,6 +5,9 @@
/// ///
/// Created by Anonymous275 on 7/16/2020 /// Created by Anonymous275 on 7/16/2020
/// ///
#include <nlohmann/json.hpp>
#include <httplib.h>
#include "zip_file.h" #include "zip_file.h"
#include <windows.h> #include <windows.h>
#include "Discord/discord_info.h" #include "Discord/discord_info.h"
@@ -12,16 +15,46 @@
#include "Security/Init.h" #include "Security/Init.h"
#include <filesystem> #include <filesystem>
#include "Startup.h" #include "Startup.h"
#include "hashpp.h"
#include "Logger.h" #include "Logger.h"
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include "Http.h" #include "Http.h"
#include "Json.h"
extern int TraceBack; extern int TraceBack;
bool Dev = false; bool Dev = false;
int ProxyPort = 0;
namespace fs = std::filesystem; namespace fs = std::filesystem;
VersionParser::VersionParser(const std::string& from_string) {
std::string token;
std::istringstream tokenStream(from_string);
while (std::getline(tokenStream, token, '.')) {
data.emplace_back(std::stol(token));
split.emplace_back(token);
}
}
std::strong_ordering VersionParser::operator<=>(
const VersionParser& rhs) const noexcept {
size_t const fields = std::min(data.size(), rhs.data.size());
for (size_t i = 0; i != fields; ++i) {
if (data[i] == rhs.data[i]) continue;
else if (data[i] < rhs.data[i]) return std::strong_ordering::less;
else return std::strong_ordering::greater;
}
if (data.size() == rhs.data.size()) return std::strong_ordering::equal;
else if (data.size() > rhs.data.size()) return std::strong_ordering::greater;
else return std::strong_ordering::less;
}
bool VersionParser::operator==(const VersionParser& rhs) const noexcept {
return std::is_eq(*this <=> rhs);
}
std::string GetEN(){ std::string GetEN(){
return "BeamMP-Launcher.exe"; return "BeamMP-Launcher.exe";
} }
@@ -29,8 +62,9 @@ std::string GetVer(){
return "2.0"; return "2.0";
} }
std::string GetPatch(){ std::string GetPatch(){
return ".79"; return ".84";
} }
std::string GetEP(char*P){ std::string GetEP(char*P){
static std::string Ret = [&](){ static std::string Ret = [&](){
std::string path(P); std::string path(P);
@@ -71,43 +105,28 @@ void CheckName(int argc,char* args[]){
} }
} }
void CheckForUpdates(int argc,char*args[],const std::string& CV){ void CheckForUpdates(int argc, char* args[], const std::string& CV) {
std::string link; std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey);
std::string HTTP = HTTP::Get("https://beammp.com/builds/launcher?version=true"); std::string LatestVersion = HTTP::Get(
bool fallback = false; "https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey);
if(HTTP.find_first_of("0123456789") == std::string::npos){
HTTP = HTTP::Get("https://backup1.beammp.com/builds/launcher?version=true");
fallback = true;
if(HTTP.find_first_of("0123456789") == std::string::npos) {
fatal("Primary Servers Offline! sorry for the inconvenience!");
}
}
if(fallback){
link = "https://backup1.beammp.com/builds/launcher?download=true";
}else link = "https://beammp.com/builds/launcher?download=true";
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back"); std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back");
if(fs::exists(Back))remove(Back.c_str()); std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP);
if(HTTP > CV){ if (FileHash != LatestHash && VersionParser(LatestVersion) > VersionParser(GetVer()+GetPatch())) {
system("cls"); info("Launcher update found!");
info("Update found!"); fs::remove(Back);
info("Updating..."); fs::rename(EP, Back);
if(std::rename(EP.c_str(), Back.c_str()))error("failed creating a backup!"); info("Downloading Launcher update " + LatestHash);
HTTP::Download(
if(!HTTP::Download(link, EP)){ "https://backend.beammp.com/builds/launcher?download=true"
error("Launcher Update failed! trying again..."); "&pk=" +
std::this_thread::sleep_for(std::chrono::seconds(2)); PublicKey + "&branch=" + Branch,
EP);
if(!HTTP::Download(link, EP)){ URelaunch(argc, args);
error("Launcher Update failed!"); } else info("Launcher version is up to date");
std::this_thread::sleep_for(std::chrono::seconds(5));
ReLaunch(argc,args);
}
}
URelaunch(argc,args);
}else info("Launcher version is up to date");
TraceBack++; TraceBack++;
} }
@@ -147,31 +166,6 @@ void LinuxPatch(){
} }
RegCloseKey(hKey); RegCloseKey(hKey);
std::string Path = R"(Z:\home\)" + std::string(getenv("USER")) + R"(\.steam\steam\Steam.exe)";
if(!fs::exists(Path)) {
std::ofstream ofs(Path);
if (!ofs.is_open()) {
fatal("Failed to create file \"" + Path + "\"");
return;
} else ofs.close();
}
result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Valve\Steam)", 0, KEY_ALL_ACCESS, &hKey);
if (result != ERROR_SUCCESS){
fatal(R"(failed to open HKEY_CURRENT_USER\Software\Valve\Steam)");
return;
}
result = RegSetValueEx(hKey, "SteamExe", 0, REG_SZ, (BYTE*)Path.c_str(), Path.size());
if (result != ERROR_SUCCESS){
fatal(R"(failed to create the value "Name" under HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)");
return;
}
RegCloseKey(hKey);
info("Patched!"); info("Patched!");
} }
@@ -198,13 +192,13 @@ void CheckMP(const std::string& Path) {
try { try {
for (auto& p : fs::directory_iterator(Path)){ for (auto& p : fs::directory_iterator(Path)){
if(p.exists() && !p.is_directory()){ if(p.exists() && !p.is_directory()){
std::string Name = p.path().filename().u8string(); std::string Name = p.path().filename().string();
for(char&Ch : Name)Ch = char(tolower(Ch)); for(char&Ch : Name)Ch = char(tolower(Ch));
if(Name != "beammp.zip")fs::remove(p.path()); if(Name != "beammp.zip")fs::remove(p.path());
} }
} }
} catch (...) { } catch (...) {
fatal("Please close the game, and try again!"); fatal("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?");
} }
} }
@@ -219,20 +213,16 @@ void EnableMP(){
std::string Data(Size, 0); std::string Data(Size, 0);
db.read(&Data[0], Size); db.read(&Data[0], Size);
db.close(); db.close();
json::Document d; nlohmann::json d = nlohmann::json::parse(Data, nullptr, false);
d.Parse(Data.c_str()); if(Data.at(0) != '{' || d.is_discarded()) {
if(Data.at(0) != '{' || d.HasParseError()){
//error("Failed to parse " + File); //TODO illegal formatting //error("Failed to parse " + File); //TODO illegal formatting
return; return;
} }
if(!d["mods"].IsNull() && !d["mods"]["multiplayerbeammp"].IsNull()){ if(d.contains("mods") && d["mods"].contains("multiplayerbeammp")){
d["mods"]["multiplayerbeammp"]["active"] = true; d["mods"]["multiplayerbeammp"]["active"] = true;
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
d.Accept(writer);
std::ofstream ofs(File); std::ofstream ofs(File);
if(ofs.is_open()){ if(ofs.is_open()){
ofs << buffer.GetString(); ofs << d.dump();
ofs.close(); ofs.close();
}else{ }else{
error("Failed to write " + File); error("Failed to write " + File);
@@ -242,18 +232,17 @@ void EnableMP(){
} }
void PreGame(const std::string& GamePath){ void PreGame(const std::string& GamePath){
const std::string CurrVer("0.26.1.0");
std::string GameVer = CheckVer(GamePath); std::string GameVer = CheckVer(GamePath);
info("Game Version : " + GameVer); info("Game Version : " + GameVer);
if(GameVer < CurrVer){
fatal("Game version is old! Please update.");
}else if(GameVer > CurrVer){
warn("Game is newer than recommended, multiplayer may not work as intended!");
}
CheckMP(GetGamePath() + "mods/multiplayer"); CheckMP(GetGamePath() + "mods/multiplayer");
if(!Dev) { if(!Dev) {
info("Downloading mod please wait..."); 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 { try {
if (!fs::exists(GetGamePath() + "mods/multiplayer")) { if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
fs::create_directories(GetGamePath() + "mods/multiplayer"); fs::create_directories(GetGamePath() + "mods/multiplayer");
@@ -262,18 +251,76 @@ void PreGame(const std::string& GamePath){
}catch(std::exception&e){ }catch(std::exception&e){
fatal(e.what()); fatal(e.what());
} }
std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)"); std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)");
HTTP::Download("https://backend.beammp.com/builds/client?download=true" std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, ZipPath);
"&pk=" + PublicKey + "&branch=" + Branch, ZipPath);
if (FileHash != LatestHash) {
info("Downloading BeamMP Update " + LatestHash);
HTTP::Download("https://backend.beammp.com/builds/client?download=true"
"&pk=" + PublicKey + "&branch=" + Branch, ZipPath);
}
std::string Target(GetGamePath() + "mods/unpacked/beammp"); std::string Target(GetGamePath() + "mods/unpacked/beammp");
if(fs::is_directory(Target)) { if(fs::is_directory(Target)) {
fs::remove_all(Target); fs::remove_all(Target);
} }
//HTTP::Download("beammp.com/builds/client", GetGamePath() + R"(mods\multiplayer\BeamMP.zip)");
} }
}
void set_headers(httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "*");
res.set_header("Access-Control-Allow-Headers", "X-API-Version");
res.set_header("Access-Control-Allow-Method", "GET,POST,OPTIONS");
}
void StartProxy() {
std::thread proxy([&](){
httplib::Server HTTPProxy;
httplib::Headers headers = {
{"User-Agent", "BeamMP-Launcher/" + GetVer() + GetPatch()},
{"Accept", "*/*"}
};
std::string pattern = "/:any1";
for (int i = 2; i <= 4; i++) {
HTTPProxy.Get(pattern, [&](const httplib::Request &req, httplib::Response &res) {
httplib::Client cli("https://backend.beammp.com");
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"));
}
if (auto cli_res = cli.Get(req.path, headers); 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.Post(pattern, [&](const httplib::Request &req, httplib::Response &res) {
httplib::Client cli("https://backend.beammp.com");
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"));
}
if (auto cli_res = cli.Post(req.path, headers, req.body,
req.get_header_value("Content-Type")); 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");
}
});
pattern += "/:any" + std::to_string(i);
}
ProxyPort = HTTPProxy.bind_to_any_port("0.0.0.0");
HTTPProxy.listen_after_bind();
});
proxy.detach();
} }

View File

@@ -21,20 +21,21 @@
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
#ifdef DEBUG #ifdef DEBUG
std::thread th(flush); std::thread th(flush);
th.detach(); th.detach();
#endif #endif
GetEP(argv[0]); GetEP(argv[0]);
InitLauncher(argc,argv); InitLauncher(argc, argv);
try { try {
LegitimacyCheck(); LegitimacyCheck();
}catch (std::exception& e){ } catch (std::exception &e) {
fatal("Main 1 : " + std::string(e.what())); fatal("Main 1 : " + std::string(e.what()));
} }
StartProxy();
PreGame(GetGameDir()); PreGame(GetGameDir());
InitGame(GetGameDir()); InitGame(GetGameDir());
CoreNetwork(); CoreNetwork();