mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2026-04-03 14:26:15 +00:00
Compare commits
22 Commits
fix-little
...
ipv6-suppo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f65fbf60fe | ||
|
|
188a31c69e | ||
|
|
caab92375d | ||
|
|
b76930b0bd | ||
|
|
96d579f64b | ||
|
|
ba35d039ae | ||
|
|
3535923f40 | ||
|
|
3ca6e7fd3d | ||
|
|
7c24020124 | ||
|
|
88a9d4a1b1 | ||
|
|
4a5728d421 | ||
|
|
137d9dd1e2 | ||
|
|
7b733bf8eb | ||
|
|
b034072027 | ||
|
|
f94b9adf7a | ||
|
|
c95178ea59 | ||
|
|
1f7c498bd9 | ||
|
|
e46d4b2f0e | ||
|
|
d32da036bc | ||
|
|
8b0f4f99f6 | ||
|
|
17e887442c | ||
|
|
fc454cd11e |
@@ -15,6 +15,8 @@ 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" "include/*.hpp" "include/*/*.hpp" "include/*/*/*.hpp")
|
||||
find_package(httplib CONFIG REQUIRED)
|
||||
find_package(nlohmann_json CONFIG REQUIRED)
|
||||
find_package(asio CONFIG REQUIRED)
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${source_files})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher")
|
||||
@@ -23,15 +25,15 @@ if (WIN32)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json)
|
||||
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json asio::asio fmt::fmt)
|
||||
elseif (LINUX)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto)
|
||||
else(WIN32) #MINGW
|
||||
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto asio::asio fmt::fmt)
|
||||
elseif (WIN32) #MINGW
|
||||
add_definitions("-D_WIN32_WINNT=0x0600")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static")
|
||||
target_link_libraries(${PROJECT_NAME} ssl crypto ws2_32 ssp crypt32 z)
|
||||
target_link_libraries(${PROJECT_NAME} ssl crypto ws2_32 ssp crypt32 z asio::asio fmt::fmt)
|
||||
endif(WIN32)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "include")
|
||||
|
||||
11
include/Helpers.h
Normal file
11
include/Helpers.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#pragma once
|
||||
|
||||
using ByteSpan = std::span<const char>;
|
||||
|
||||
std::string bytespan_to_string(ByteSpan span);
|
||||
|
||||
std::vector<char> strtovec(std::string_view str);
|
||||
@@ -7,6 +7,10 @@
|
||||
///
|
||||
|
||||
#pragma once
|
||||
#include "Helpers.h"
|
||||
#include "asio/io_context.hpp"
|
||||
#include "asio/ip/address.hpp"
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -14,8 +18,13 @@
|
||||
#include <bits/types/siginfo_t.h>
|
||||
#include <cstdint>
|
||||
#include <sys/ucontext.h>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
extern asio::io_context io;
|
||||
|
||||
void NetReset();
|
||||
extern bool Dev;
|
||||
extern int ping;
|
||||
@@ -27,8 +36,8 @@ extern int LastPort;
|
||||
extern bool ModLoaded;
|
||||
extern bool Terminate;
|
||||
extern int DEFAULT_PORT;
|
||||
extern uint64_t UDPSock;
|
||||
extern uint64_t TCPSock;
|
||||
extern std::shared_ptr<asio::ip::udp::socket> UDPSock;
|
||||
extern std::shared_ptr<asio::ip::tcp::socket> TCPSock;
|
||||
extern std::string Branch;
|
||||
extern bool TCPTerminate;
|
||||
extern std::string LastIP;
|
||||
@@ -37,18 +46,21 @@ extern std::string UlStatus;
|
||||
extern std::string PublicKey;
|
||||
extern std::string PrivateKey;
|
||||
extern std::string ListOfMods;
|
||||
int KillSocket(uint64_t Dead);
|
||||
void KillSocket(std::shared_ptr<asio::ip::tcp::socket>& Dead);
|
||||
void KillSocket(std::shared_ptr<asio::ip::udp::socket>& Dead);
|
||||
void KillSocket(asio::ip::tcp::socket& Dead);
|
||||
void KillSocket(asio::ip::udp::socket& Dead);
|
||||
void UUl(const std::string& R);
|
||||
void UDPSend(std::string Data);
|
||||
void UDPSend(const std::vector<char>& Data);
|
||||
bool CheckBytes(int32_t Bytes);
|
||||
void GameSend(std::string Data);
|
||||
void SendLarge(std::string Data);
|
||||
std::string TCPRcv(uint64_t Sock);
|
||||
void SyncResources(uint64_t TCPSock);
|
||||
void GameSend(std::string_view Data);
|
||||
void SendLarge(const std::vector<char>& Data);
|
||||
std::string TCPRcv(asio::ip::tcp::socket& Sock);
|
||||
void SyncResources(asio::ip::tcp::socket& TCPSock);
|
||||
std::string GetAddr(const std::string& IP);
|
||||
void ServerParser(const std::string& Data);
|
||||
void ServerParser(std::string_view Data);
|
||||
std::string Login(const std::string& fields);
|
||||
void TCPSend(const std::string& Data, uint64_t Sock);
|
||||
void TCPClientMain(const std::string& IP, int Port);
|
||||
void UDPClientMain(const std::string& IP, int Port);
|
||||
void TCPGameServer(const std::string& IP, int Port);
|
||||
void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock);
|
||||
void TCPClientMain(asio::ip::tcp::socket&& Socket);
|
||||
void UDPClientMain(asio::ip::address addr, uint16_t port);
|
||||
void TCPGameServer(asio::ip::tcp::socket&& Socket);
|
||||
|
||||
6
include/NetworkHelpers.h
Normal file
6
include/NetworkHelpers.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <vector>
|
||||
|
||||
void ReceiveFromGame(asio::ip::tcp::socket& socket, std::vector<char>& out_data);
|
||||
@@ -19,10 +19,3 @@ void StartProxy();
|
||||
void ConfigInit();
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
std::vector<char> Comp(std::span<char> input);
|
||||
std::vector<char> DeComp(std::span<char> input);
|
||||
std::vector<char> Comp(std::span<const char> input);
|
||||
std::vector<char> DeComp(std::span<const char> input);
|
||||
|
||||
@@ -15,15 +15,16 @@
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
std::vector<char> Comp(std::span<char> input) {
|
||||
std::vector<char> Comp(std::span<const char> input) {
|
||||
auto max_size = compressBound(input.size());
|
||||
std::vector<char> output(max_size);
|
||||
uLongf output_size = output.size();
|
||||
int res = compress(
|
||||
int res = compress2(
|
||||
reinterpret_cast<Bytef*>(output.data()),
|
||||
&output_size,
|
||||
reinterpret_cast<Bytef*>(input.data()),
|
||||
static_cast<uLongf>(input.size()));
|
||||
reinterpret_cast<const Bytef*>(input.data()),
|
||||
static_cast<uLongf>(input.size()),
|
||||
3);
|
||||
if (res != Z_OK) {
|
||||
error("zlib compress() failed: " + std::to_string(res));
|
||||
throw std::runtime_error("zlib compress() failed");
|
||||
@@ -33,7 +34,7 @@ std::vector<char> Comp(std::span<char> input) {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<char> DeComp(std::span<char> input) {
|
||||
std::vector<char> DeComp(std::span<const char> input) {
|
||||
std::vector<char> output_buffer(std::min<size_t>(input.size() * 5, 15 * 1024 * 1024));
|
||||
|
||||
uLongf output_size = output_buffer.size();
|
||||
|
||||
@@ -48,8 +48,8 @@ void ConfigInit() {
|
||||
if (cfg.is_open()) {
|
||||
cfg <<
|
||||
R"({
|
||||
"Port": 4444,
|
||||
"Build": "Default"
|
||||
"Port": 4444,
|
||||
"Build": "Default"
|
||||
})";
|
||||
cfg.close();
|
||||
} else {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__)
|
||||
#include "vdf_parser.hpp"
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <pwd.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/types.h>
|
||||
@@ -90,11 +92,27 @@ void StartGame(std::string Dir) {
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
void StartGame(std::string Dir) {
|
||||
int status;
|
||||
std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64");
|
||||
char* argv[] = { filename.data(), NULL };
|
||||
pid_t pid;
|
||||
int result = posix_spawn(&pid, filename.c_str(), NULL, NULL, argv, 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, argv, environ);
|
||||
|
||||
if (result != 0) {
|
||||
error("Failed to Launch the game! launcher closing soon");
|
||||
@@ -104,6 +122,11 @@ void StartGame(std::string Dir) {
|
||||
error("Game Closed! launcher closing soon");
|
||||
}
|
||||
|
||||
status = posix_spawn_file_actions_destroy(&file_actions);
|
||||
if (status != 0) {
|
||||
warn(std::string("posix_spawn_file_actions_destroy failed: ") + std::strerror(errno));
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
9
src/Helpers.cpp
Normal file
9
src/Helpers.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "Helpers.h"
|
||||
|
||||
std::string bytespan_to_string(ByteSpan span) {
|
||||
return std::string(span.data(), span.size());
|
||||
}
|
||||
|
||||
std::vector<char> strtovec(std::string_view str) {
|
||||
return std::vector<char>(str.begin(), str.end());
|
||||
}
|
||||
@@ -50,35 +50,35 @@ void addToLog(const std::string& Line) {
|
||||
}
|
||||
void info(const std::string& toPrint) {
|
||||
std::string Print = getDate() + "[INFO] " + toPrint + "\n";
|
||||
std::cout << Print;
|
||||
std::cout << Print << std::flush;
|
||||
addToLog(Print);
|
||||
}
|
||||
void debug(const std::string& toPrint) {
|
||||
if (!Dev)
|
||||
return;
|
||||
std::string Print = getDate() + "[DEBUG] " + toPrint + "\n";
|
||||
std::cout << Print;
|
||||
std::cout << Print << std::flush;
|
||||
addToLog(Print);
|
||||
}
|
||||
void warn(const std::string& toPrint) {
|
||||
std::string Print = getDate() + "[WARN] " + toPrint + "\n";
|
||||
std::cout << Print;
|
||||
std::cout << Print << std::flush;
|
||||
addToLog(Print);
|
||||
}
|
||||
void error(const std::string& toPrint) {
|
||||
std::string Print = getDate() + "[ERROR] " + toPrint + "\n";
|
||||
std::cout << Print;
|
||||
std::cout << Print << std::flush;
|
||||
addToLog(Print);
|
||||
}
|
||||
void fatal(const std::string& toPrint) {
|
||||
std::string Print = getDate() + "[FATAL] " + toPrint + "\n";
|
||||
std::cout << Print;
|
||||
std::cout << Print << std::flush;
|
||||
addToLog(Print);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
_Exit(-1);
|
||||
}
|
||||
void except(const std::string& toPrint) {
|
||||
std::string Print = getDate() + "[EXCEP] " + toPrint + "\n";
|
||||
std::cout << Print;
|
||||
std::cout << Print << std::flush;
|
||||
addToLog(Print);
|
||||
}
|
||||
|
||||
@@ -7,18 +7,17 @@
|
||||
///
|
||||
#include "Http.h"
|
||||
#include "Network/network.hpp"
|
||||
#include "NetworkHelpers.h"
|
||||
#include "Security/Init.h"
|
||||
#include <asio/io_context.hpp>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#if defined(_WIN32)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#elif defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
@@ -27,6 +26,7 @@
|
||||
#include "Logger.h"
|
||||
#include "Startup.h"
|
||||
#include <charconv>
|
||||
#include <fmt/format.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
@@ -44,65 +44,117 @@ std::string MStatus;
|
||||
bool ModLoaded;
|
||||
int ping = -1;
|
||||
|
||||
void StartSync(const std::string& Data) {
|
||||
std::string IP = GetAddr(Data.substr(1, Data.find(':') - 1));
|
||||
if (IP.find('.') == -1) {
|
||||
if (IP == "DNS")
|
||||
UlStatus = "UlConnection Failed! (DNS Lookup Failed)";
|
||||
else
|
||||
UlStatus = "UlConnection Failed! (WSA failed to start)";
|
||||
ListOfMods = "-";
|
||||
Terminate = true;
|
||||
return;
|
||||
asio::io_context io {};
|
||||
|
||||
static asio::ip::tcp::socket ResolveAndConnect(const std::string& host_port_string) {
|
||||
|
||||
using namespace asio;
|
||||
ip::tcp::resolver resolver(io);
|
||||
asio::error_code ec;
|
||||
auto port = host_port_string.substr(host_port_string.find_last_of(':') + 1);
|
||||
auto host = host_port_string.substr(0, host_port_string.find_last_of(':'));
|
||||
auto resolved = resolver.resolve(host, port, ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to resolve '[{}]:{}': {}", host, port, ec.message()));
|
||||
throw std::runtime_error(fmt::format("Failed to resolve '{}': {}", host_port_string, ec.message()));
|
||||
}
|
||||
CheckLocalKey();
|
||||
bool connected = false;
|
||||
|
||||
UlStatus = "UlLoading...";
|
||||
TCPTerminate = false;
|
||||
Terminate = false;
|
||||
ConfList->clear();
|
||||
ping = -1;
|
||||
std::thread GS(TCPGameServer, IP, std::stoi(Data.substr(Data.find(':') + 1)));
|
||||
GS.detach();
|
||||
info("Connecting to server");
|
||||
|
||||
for (const auto& addr : resolved) {
|
||||
try {
|
||||
info(fmt::format("Resolved and connected to '[{}]:{}'",
|
||||
addr.endpoint().address().to_string(),
|
||||
addr.endpoint().port()));
|
||||
ip::tcp::socket socket(io);
|
||||
socket.connect(addr);
|
||||
// done, connected fine
|
||||
return socket;
|
||||
} catch (...) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(fmt::format("Failed to connect to '{}'; connection refused", host_port_string));
|
||||
}
|
||||
|
||||
void StartSync(const std::string& Data) {
|
||||
try {
|
||||
auto Socket = ResolveAndConnect(Data.substr(1));
|
||||
ListOfMods = "-";
|
||||
CheckLocalKey();
|
||||
TCPTerminate = false;
|
||||
Terminate = false;
|
||||
ConfList->clear();
|
||||
ping = -1;
|
||||
std::thread GS(TCPGameServer, std::move(Socket));
|
||||
GS.detach();
|
||||
info("Connecting to server");
|
||||
} catch (const std::exception& e) {
|
||||
UlStatus = "UlConnection Failed!";
|
||||
error(fmt::format("Client: connect failed! Error: {}", e.what()));
|
||||
WSACleanup();
|
||||
Terminate = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
std::vector<std::string> allowed_links = {
|
||||
R"(patreon\.com\/beammp$)",
|
||||
R"(discord\.gg\/beammp$)",
|
||||
R"(forum\.beammp\.com$)",
|
||||
R"(beammp\.com$)",
|
||||
R"(patreon\.com\/beammp\/$)",
|
||||
R"(discord\.gg\/beammp\/$)",
|
||||
R"(forum\.beammp\.com\/$)",
|
||||
R"(beammp\.com\/$)",
|
||||
R"(docs\.beammp\.com$)",
|
||||
R"(wiki\.beammp\.com$)",
|
||||
R"(docs\.beammp\.com\/$)",
|
||||
R"(wiki\.beammp\.com\/$)",
|
||||
R"(docs\.beammp\.com\/.*$)",
|
||||
R"(wiki\.beammp\.com\/.*$)",
|
||||
};
|
||||
for (const auto& allowed_link : allowed_links) {
|
||||
if (std::regex_match(Link, std::regex(std::string(R"(^http(s)?:\/\/)") + allowed_link))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Parse(std::string Data, SOCKET CSocket) {
|
||||
char Code = Data.at(0), SubCode = 0;
|
||||
if (Data.length() > 1)
|
||||
SubCode = Data.at(1);
|
||||
void Parse(std::span<char> InData, asio::ip::tcp::socket& CSocket) {
|
||||
std::string OutData;
|
||||
char Code = InData[0], SubCode = 0;
|
||||
if (InData.size() > 1)
|
||||
SubCode = InData[1];
|
||||
switch (Code) {
|
||||
case 'A':
|
||||
Data = Data.substr(0, 1);
|
||||
OutData = "A";
|
||||
break;
|
||||
case 'B':
|
||||
NetReset();
|
||||
Terminate = true;
|
||||
TCPTerminate = true;
|
||||
Data = Code + HTTP::Get("https://backend.beammp.com/servers-info");
|
||||
OutData = Code + HTTP::Get("https://backend.beammp.com/servers-info");
|
||||
break;
|
||||
case 'C':
|
||||
ListOfMods.clear();
|
||||
StartSync(Data);
|
||||
StartSync(std::string(InData.data(), InData.size()));
|
||||
while (ListOfMods.empty() && !Terminate) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
if (ListOfMods == "-")
|
||||
Data = "L";
|
||||
OutData = "L";
|
||||
else
|
||||
Data = "L" + ListOfMods;
|
||||
OutData = "L" + ListOfMods;
|
||||
break;
|
||||
case 'O': // open default browser with URL
|
||||
if (IsAllowedLink(Data.substr(1))) {
|
||||
if (IsAllowedLink(bytespan_to_string(InData.subspan(1)))) {
|
||||
#if defined(__linux)
|
||||
if (char* browser = getenv("BROWSER"); browser != nullptr && !std::string_view(browser).empty()) {
|
||||
pid_t pid;
|
||||
auto arg = Data.substr(1);
|
||||
auto arg = bytespan_to_string(InData.subspan(1));
|
||||
char* argv[] = { browser, arg.data() };
|
||||
auto status = posix_spawn(&pid, browser, nullptr, nullptr, argv, environ);
|
||||
if (status == 0) {
|
||||
@@ -114,27 +166,27 @@ void Parse(std::string Data, SOCKET CSocket) {
|
||||
error(std::string("posix_spawn: ") + strerror(status));
|
||||
}
|
||||
} else {
|
||||
error("Failed to open the following link in the browser because the $BROWSER environment variable is not set: " + Data.substr(1));
|
||||
error("Failed to open the following link in the browser because the $BROWSER environment variable is not set: " + bytespan_to_string(InData.subspan(1)));
|
||||
}
|
||||
#elif defined(WIN32)
|
||||
ShellExecuteA(nullptr, "open", Data.substr(1).c_str(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port
|
||||
ShellExecuteA(nullptr, "open", InData.subspan(1).data(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port
|
||||
#endif
|
||||
|
||||
info("Opening Link \"" + Data.substr(1) + "\"");
|
||||
info("Opening Link \"" + bytespan_to_string(InData.subspan(1)) + "\"");
|
||||
}
|
||||
Data.clear();
|
||||
OutData.clear();
|
||||
break;
|
||||
case 'P':
|
||||
Data = Code + std::to_string(ProxyPort);
|
||||
OutData = Code + std::to_string(ProxyPort);
|
||||
break;
|
||||
case 'U':
|
||||
if (SubCode == 'l')
|
||||
Data = UlStatus;
|
||||
OutData = UlStatus;
|
||||
if (SubCode == 'p') {
|
||||
if (ping > 800) {
|
||||
Data = "Up-2";
|
||||
OutData = "Up-2";
|
||||
} else
|
||||
Data = "Up" + std::to_string(ping);
|
||||
OutData = "Up" + std::to_string(ping);
|
||||
}
|
||||
if (!SubCode) {
|
||||
std::string Ping;
|
||||
@@ -142,11 +194,11 @@ void Parse(std::string Data, SOCKET CSocket) {
|
||||
Ping = "-2";
|
||||
else
|
||||
Ping = std::to_string(ping);
|
||||
Data = std::string(UlStatus) + "\n" + "Up" + Ping;
|
||||
OutData = std::string(UlStatus) + "\n" + "Up" + Ping;
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
Data = MStatus;
|
||||
OutData = MStatus;
|
||||
break;
|
||||
case 'Q':
|
||||
if (SubCode == 'S') {
|
||||
@@ -157,17 +209,19 @@ void Parse(std::string Data, SOCKET CSocket) {
|
||||
}
|
||||
if (SubCode == 'G')
|
||||
exit(2);
|
||||
Data.clear();
|
||||
OutData.clear();
|
||||
break;
|
||||
case 'R': // will send mod name
|
||||
if (ConfList->find(Data) == ConfList->end()) {
|
||||
ConfList->insert(Data);
|
||||
{
|
||||
auto str = bytespan_to_string(InData);
|
||||
if (ConfList->find(str) == ConfList->end()) {
|
||||
ConfList->insert(str);
|
||||
ModLoaded = true;
|
||||
}
|
||||
Data.clear();
|
||||
break;
|
||||
OutData.clear();
|
||||
} break;
|
||||
case 'Z':
|
||||
Data = "Z" + GetVer();
|
||||
OutData = "Z" + GetVer();
|
||||
break;
|
||||
case 'N':
|
||||
if (SubCode == 'c') {
|
||||
@@ -180,64 +234,39 @@ void Parse(std::string Data, SOCKET CSocket) {
|
||||
if (!UserRole.empty()) {
|
||||
Auth["role"] = UserRole;
|
||||
}
|
||||
Data = "N" + Auth.dump();
|
||||
OutData = "N" + Auth.dump();
|
||||
} else {
|
||||
Data = "N" + Login(Data.substr(Data.find(':') + 1));
|
||||
auto indata_str = bytespan_to_string(InData);
|
||||
OutData = "N" + Login(indata_str.substr(indata_str.find(':') + 1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Data.clear();
|
||||
OutData.clear();
|
||||
break;
|
||||
}
|
||||
if (!Data.empty() && CSocket != -1) {
|
||||
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()));
|
||||
if (!OutData.empty() && CSocket.is_open()) {
|
||||
uint32_t DataSize = OutData.size();
|
||||
std::vector<char> ToSend(sizeof(DataSize) + OutData.size());
|
||||
std::copy_n(reinterpret_cast<char*>(&DataSize), sizeof(DataSize), ToSend.begin());
|
||||
std::copy_n(OutData.data(), OutData.size(), ToSend.begin() + sizeof(DataSize));
|
||||
asio::error_code ec;
|
||||
asio::write(CSocket, asio::buffer(ToSend), ec);
|
||||
if (ec) {
|
||||
debug(fmt::format("(Core) send failed with error: {}", ec.message()));
|
||||
}
|
||||
}
|
||||
}
|
||||
void GameHandler(SOCKET Client) {
|
||||
|
||||
int32_t Size, Temp, Rcv;
|
||||
char Header[10] = { 0 };
|
||||
void GameHandler(asio::ip::tcp::socket& Client) {
|
||||
std::vector<char> data {};
|
||||
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 {
|
||||
ReceiveFromGame(Client, data);
|
||||
Parse(data, Client);
|
||||
} catch (const std::exception& e) {
|
||||
error(std::string("Error while receiving from game: ") + 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;
|
||||
|
||||
std::thread Respond(Parse, Ret, Client);
|
||||
Respond.detach();
|
||||
} while (Temp > 0);
|
||||
if (Temp == 0) {
|
||||
debug("(Core) Connection closing");
|
||||
} else {
|
||||
debug("(Core) recv failed with error: " + std::to_string(WSAGetLastError()));
|
||||
}
|
||||
} while (true);
|
||||
NetReset();
|
||||
KillSocket(Client);
|
||||
}
|
||||
@@ -253,63 +282,52 @@ void localRes() {
|
||||
}
|
||||
void CoreMain() {
|
||||
debug("Core Network on start!");
|
||||
SOCKET LSocket, CSocket;
|
||||
struct addrinfo* res = nullptr;
|
||||
struct addrinfo hints { };
|
||||
int iRes;
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
iRes = WSAStartup(514, &wsaData); // 2.2
|
||||
if (iRes)
|
||||
debug("WSAStartup failed with error: " + std::to_string(iRes));
|
||||
#endif
|
||||
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast<uint16_t>(DEFAULT_PORT));
|
||||
asio::ip::tcp::socket LSocket(io);
|
||||
asio::error_code ec;
|
||||
LSocket.open(listen_ep.protocol(), ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to open core socket: {}", ec.message()));
|
||||
return;
|
||||
}
|
||||
asio::ip::tcp::socket::linger linger_opt {};
|
||||
linger_opt.enabled(false);
|
||||
LSocket.set_option(linger_opt, ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to set up listening core socket to not linger / reuse address. "
|
||||
"This may cause the core socket to refuse to bind(). Error: {}",
|
||||
ec.message()));
|
||||
return;
|
||||
}
|
||||
asio::ip::tcp::socket::reuse_address reuse_opt { true };
|
||||
LSocket.set_option(reuse_opt, ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to set up listening core socket to not linger / reuse address. "
|
||||
"This may cause the core socket to refuse to bind(). Error: {}",
|
||||
ec.message()));
|
||||
return;
|
||||
}
|
||||
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res);
|
||||
if (iRes) {
|
||||
debug("(Core) addr info failed with error: " + std::to_string(iRes));
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
LSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (LSocket == -1) {
|
||||
debug("(Core) socket failed with error: " + std::to_string(WSAGetLastError()));
|
||||
freeaddrinfo(res);
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
iRes = bind(LSocket, res->ai_addr, int(res->ai_addrlen));
|
||||
if (iRes == SOCKET_ERROR) {
|
||||
error("(Core) bind failed with error: " + std::to_string(WSAGetLastError()));
|
||||
freeaddrinfo(res);
|
||||
KillSocket(LSocket);
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
iRes = listen(LSocket, SOMAXCONN);
|
||||
if (iRes == SOCKET_ERROR) {
|
||||
debug("(Core) listen failed with error: " + std::to_string(WSAGetLastError()));
|
||||
freeaddrinfo(res);
|
||||
KillSocket(LSocket);
|
||||
WSACleanup();
|
||||
auto acceptor = asio::ip::tcp::acceptor(io, listen_ep);
|
||||
acceptor.listen(asio::ip::tcp::socket::max_listen_connections, ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("listen() failed, which is needed for the launcher to operate. Error: {}",
|
||||
ec.message()));
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
CSocket = accept(LSocket, nullptr, nullptr);
|
||||
if (CSocket == -1) {
|
||||
error("(Core) accept failed with error: " + std::to_string(WSAGetLastError()));
|
||||
auto CSocket = acceptor.accept(ec);
|
||||
if (ec) {
|
||||
error(fmt::format("(Core) accept failed with error: {}", ec.message()));
|
||||
continue;
|
||||
}
|
||||
localRes();
|
||||
info("Game Connected!");
|
||||
GameHandler(CSocket);
|
||||
warn("Game Reconnecting...");
|
||||
} while (CSocket);
|
||||
} while (LSocket.is_open());
|
||||
KillSocket(LSocket);
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
///
|
||||
/// Created by Anonymous275 on 7/25/2020
|
||||
///
|
||||
#include "Helpers.h"
|
||||
#include "Network/network.hpp"
|
||||
#include "NetworkHelpers.h"
|
||||
#include "asio/socket_base.hpp"
|
||||
#include <algorithm>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
#if defined(_WIN32)
|
||||
#include <winsock2.h>
|
||||
@@ -22,6 +28,7 @@
|
||||
|
||||
#include "Logger.h"
|
||||
#include <charconv>
|
||||
#include <fmt/format.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -29,20 +36,31 @@
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> PingStart, PingEnd;
|
||||
bool GConnected = false;
|
||||
bool CServer = true;
|
||||
SOCKET CSocket = -1;
|
||||
SOCKET GSocket = -1;
|
||||
std::shared_ptr<asio::ip::tcp::socket> CSocket = nullptr;
|
||||
std::shared_ptr<asio::ip::tcp::socket> GSocket = nullptr;
|
||||
|
||||
int KillSocket(uint64_t Dead) {
|
||||
if (Dead == (SOCKET)-1) {
|
||||
debug("Kill socket got -1 returning...");
|
||||
return 0;
|
||||
}
|
||||
shutdown(Dead, SD_BOTH);
|
||||
int a = closesocket(Dead);
|
||||
if (a != 0) {
|
||||
warn("Failed to close socket!");
|
||||
}
|
||||
return a;
|
||||
void KillSocket(std::shared_ptr<asio::ip::tcp::socket>& Dead) {
|
||||
if (!Dead)
|
||||
return;
|
||||
asio::error_code ec;
|
||||
Dead->shutdown(asio::socket_base::shutdown_both, ec);
|
||||
}
|
||||
|
||||
void KillSocket(std::shared_ptr<asio::ip::udp::socket>& Dead) {
|
||||
if (!Dead)
|
||||
return;
|
||||
asio::error_code ec;
|
||||
Dead->shutdown(asio::socket_base::shutdown_both, ec);
|
||||
}
|
||||
|
||||
void KillSocket(asio::ip::tcp::socket& Dead) {
|
||||
asio::error_code ec;
|
||||
Dead.shutdown(asio::socket_base::shutdown_both, ec);
|
||||
}
|
||||
|
||||
void KillSocket(asio::ip::udp::socket& Dead) {
|
||||
asio::error_code ec;
|
||||
Dead.shutdown(asio::socket_base::shutdown_both, ec);
|
||||
}
|
||||
|
||||
bool CheckBytes(uint32_t Bytes) {
|
||||
@@ -56,38 +74,34 @@ bool CheckBytes(uint32_t Bytes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameSend(std::string Data) {
|
||||
void GameSend(std::string_view RawData) {
|
||||
static std::mutex Lock;
|
||||
std::scoped_lock Guard(Lock);
|
||||
if (TCPTerminate || !GConnected || CSocket == -1)
|
||||
if (TCPTerminate || !GConnected || CSocket == nullptr)
|
||||
return;
|
||||
int32_t Size, Temp, Sent;
|
||||
Data += '\n';
|
||||
Size = int32_t(Data.size());
|
||||
uint32_t DataSize = RawData.size();
|
||||
std::vector<char> Data(sizeof(DataSize) + RawData.size());
|
||||
std::copy_n(reinterpret_cast<char*>(&DataSize), sizeof(DataSize), Data.begin());
|
||||
std::copy_n(RawData.data(), RawData.size(), Data.begin() + sizeof(DataSize));
|
||||
Size = Data.size();
|
||||
Sent = 0;
|
||||
#ifdef DEBUG
|
||||
if (Size > 1000) {
|
||||
debug("Launcher -> game (" + std::to_string(Size) + ")");
|
||||
|
||||
asio::error_code ec;
|
||||
asio::write(*CSocket, asio::buffer(Data), ec);
|
||||
if (ec) {
|
||||
debug(fmt::format("(TCP CB) recv failed with error: {}", ec.message()));
|
||||
KillSocket(TCPSock);
|
||||
Terminate = true;
|
||||
}
|
||||
#endif
|
||||
do {
|
||||
if (Sent > -1) {
|
||||
Temp = send(CSocket, &Data[Sent], Size - Sent, 0);
|
||||
}
|
||||
if (!CheckBytes(Temp))
|
||||
return;
|
||||
Sent += Temp;
|
||||
} while (Sent < Size);
|
||||
}
|
||||
void ServerSend(std::string Data, bool Rel) {
|
||||
|
||||
void ServerSend(const std::vector<char>& Data, bool Rel) {
|
||||
if (Terminate || Data.empty())
|
||||
return;
|
||||
if (Data.find("Zp") != std::string::npos && Data.size() > 500) {
|
||||
abort();
|
||||
}
|
||||
char C = 0;
|
||||
bool Ack = false;
|
||||
int DLen = int(Data.length());
|
||||
int DLen = int(Data.size());
|
||||
if (DLen > 3)
|
||||
C = Data.at(0);
|
||||
if (C == 'O' || C == 'T')
|
||||
@@ -99,18 +113,10 @@ void ServerSend(std::string Data, bool Rel) {
|
||||
if (Ack || Rel) {
|
||||
if (Ack || DLen > 1000)
|
||||
SendLarge(Data);
|
||||
else
|
||||
TCPSend(Data, TCPSock);
|
||||
else if (TCPSock)
|
||||
TCPSend(Data, *TCPSock);
|
||||
} else
|
||||
UDPSend(Data);
|
||||
|
||||
if (DLen > 1000) {
|
||||
debug("(Launcher->Server) Bytes sent: " + std::to_string(Data.length()) + " : "
|
||||
+ Data.substr(0, 10)
|
||||
+ Data.substr(Data.length() - 10));
|
||||
} else if (C == 'Z') {
|
||||
// debug("(Game->Launcher) : " + Data);
|
||||
}
|
||||
}
|
||||
|
||||
void NetReset() {
|
||||
@@ -119,82 +125,29 @@ void NetReset() {
|
||||
Terminate = false;
|
||||
UlStatus = "Ulstart";
|
||||
MStatus = " ";
|
||||
if (UDPSock != (SOCKET)(-1)) {
|
||||
debug("Terminating UDP Socket : " + std::to_string(TCPSock));
|
||||
KillSocket(UDPSock);
|
||||
if (UDPSock != nullptr) {
|
||||
KillSocket(*UDPSock);
|
||||
}
|
||||
UDPSock = -1;
|
||||
if (TCPSock != (SOCKET)(-1)) {
|
||||
debug("Terminating TCP Socket : " + std::to_string(TCPSock));
|
||||
KillSocket(TCPSock);
|
||||
UDPSock = nullptr;
|
||||
if (TCPSock != nullptr) {
|
||||
KillSocket(*TCPSock);
|
||||
}
|
||||
TCPSock = -1;
|
||||
if (GSocket != (SOCKET)(-1)) {
|
||||
debug("Terminating GTCP Socket : " + std::to_string(GSocket));
|
||||
KillSocket(GSocket);
|
||||
TCPSock = nullptr;
|
||||
if (GSocket != nullptr) {
|
||||
KillSocket(*GSocket);
|
||||
}
|
||||
GSocket = -1;
|
||||
GSocket = nullptr;
|
||||
}
|
||||
|
||||
SOCKET SetupListener() {
|
||||
if (GSocket != -1)
|
||||
return GSocket;
|
||||
struct addrinfo* result = nullptr;
|
||||
struct addrinfo hints { };
|
||||
int iRes;
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
iRes = WSAStartup(514, &wsaData); // 2.2
|
||||
if (iRes != 0) {
|
||||
error("(Proxy) WSAStartup failed with error: " + std::to_string(iRes));
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT + 1).c_str(), &hints, &result);
|
||||
if (iRes != 0) {
|
||||
error("(Proxy) info failed with error: " + std::to_string(iRes));
|
||||
WSACleanup();
|
||||
}
|
||||
GSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||
if (GSocket == -1) {
|
||||
error("(Proxy) socket failed with error: " + std::to_string(WSAGetLastError()));
|
||||
freeaddrinfo(result);
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
iRes = bind(GSocket, result->ai_addr, (int)result->ai_addrlen);
|
||||
if (iRes == SOCKET_ERROR) {
|
||||
error("(Proxy) bind failed with error: " + std::to_string(WSAGetLastError()));
|
||||
freeaddrinfo(result);
|
||||
KillSocket(GSocket);
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
iRes = listen(GSocket, SOMAXCONN);
|
||||
if (iRes == SOCKET_ERROR) {
|
||||
error("(Proxy) listen failed with error: " + std::to_string(WSAGetLastError()));
|
||||
KillSocket(GSocket);
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
return GSocket;
|
||||
}
|
||||
void AutoPing() {
|
||||
while (!Terminate) {
|
||||
ServerSend("p", false);
|
||||
ServerSend(strtovec("p"), false);
|
||||
PingStart = std::chrono::high_resolution_clock::now();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
int ClientID = -1;
|
||||
void ParserAsync(const std::string& Data) {
|
||||
void ParserAsync(std::string_view Data) {
|
||||
if (Data.empty())
|
||||
return;
|
||||
char Code = Data.at(0), SubCode = 0;
|
||||
@@ -217,20 +170,50 @@ void ParserAsync(const std::string& Data) {
|
||||
}
|
||||
GameSend(Data);
|
||||
}
|
||||
void ServerParser(const std::string& Data) {
|
||||
void ServerParser(std::string_view Data) {
|
||||
ParserAsync(Data);
|
||||
}
|
||||
void NetMain(const std::string& IP, int Port) {
|
||||
void NetMain(asio::ip::address addr, uint16_t port) {
|
||||
std::thread Ping(AutoPing);
|
||||
Ping.detach();
|
||||
UDPClientMain(IP, Port);
|
||||
UDPClientMain(addr, port);
|
||||
CServer = true;
|
||||
Terminate = true;
|
||||
info("Connection Terminated!");
|
||||
}
|
||||
void TCPGameServer(const std::string& IP, int Port) {
|
||||
GSocket = SetupListener();
|
||||
while (!TCPTerminate && GSocket != -1) {
|
||||
void TCPGameServer(asio::ip::tcp::socket&& Socket) {
|
||||
asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast<uint16_t>(DEFAULT_PORT + 1));
|
||||
asio::ip::tcp::socket listener(io);
|
||||
asio::error_code ec;
|
||||
listener.open(listen_ep.protocol(), ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to open game socket: {}", ec.message()));
|
||||
return;
|
||||
}
|
||||
asio::ip::tcp::socket::linger linger_opt {};
|
||||
linger_opt.enabled(false);
|
||||
listener.set_option(linger_opt, ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to set up listening game socket to not linger / reuse address. "
|
||||
"This may cause the game socket to refuse to bind(). Error: {}",
|
||||
ec.message()));
|
||||
}
|
||||
asio::ip::tcp::socket::reuse_address reuse_opt { true };
|
||||
listener.set_option(reuse_opt, ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to set up listening core socket to not linger / reuse address. "
|
||||
"This may cause the core socket to refuse to bind(). Error: {}",
|
||||
ec.message()));
|
||||
return;
|
||||
}
|
||||
auto acceptor = asio::ip::tcp::acceptor(io, listen_ep);
|
||||
acceptor.listen(asio::ip::tcp::socket::max_listen_connections, ec);
|
||||
if (ec) {
|
||||
debug(fmt::format("Proxy accept failed: {}", ec.message()));
|
||||
TCPTerminate = true; // skip the loop
|
||||
}
|
||||
debug(fmt::format("Game server listening on {}:{}", acceptor.local_endpoint().address().to_string(), acceptor.local_endpoint().port()));
|
||||
while (!TCPTerminate && acceptor.is_open()) {
|
||||
debug("MAIN LOOP OF GAME SERVER");
|
||||
GConnected = false;
|
||||
if (!CServer) {
|
||||
@@ -241,62 +224,35 @@ void TCPGameServer(const std::string& IP, int Port) {
|
||||
break;
|
||||
}
|
||||
if (CServer) {
|
||||
std::thread Client(TCPClientMain, IP, Port);
|
||||
std::thread Client(TCPClientMain, std::move(Socket));
|
||||
Client.detach();
|
||||
}
|
||||
CSocket = accept(GSocket, nullptr, nullptr);
|
||||
if (CSocket == -1) {
|
||||
debug("(Proxy) accept failed with error: " + std::to_string(WSAGetLastError()));
|
||||
break;
|
||||
}
|
||||
|
||||
CSocket = std::make_shared<asio::ip::tcp::socket>(acceptor.accept());
|
||||
debug("(Proxy) Game Connected!");
|
||||
GConnected = true;
|
||||
if (CServer) {
|
||||
std::thread t1(NetMain, IP, Port);
|
||||
std::thread t1(NetMain, CSocket->remote_endpoint().address(), CSocket->remote_endpoint().port());
|
||||
t1.detach();
|
||||
CServer = false;
|
||||
}
|
||||
int32_t Size, Temp, Rcv;
|
||||
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));
|
||||
while (!TCPTerminate && !CSocket) {
|
||||
try {
|
||||
ReceiveFromGame(*CSocket, data);
|
||||
ServerSend(data, false);
|
||||
} catch (const std::exception& e) {
|
||||
error(std::string("Error while receiving from game: ") + 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()));
|
||||
}
|
||||
}
|
||||
TCPTerminate = true;
|
||||
GConnected = false;
|
||||
Terminate = true;
|
||||
if (CSocket != SOCKET_ERROR)
|
||||
if (CSocket != nullptr)
|
||||
KillSocket(CSocket);
|
||||
debug("END OF GAME SERVER");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
///
|
||||
|
||||
#include "Network/network.hpp"
|
||||
#include "fmt/core.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <ws2tcpip.h>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <asio.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -72,8 +74,8 @@ void Abord() {
|
||||
info("Terminated!");
|
||||
}
|
||||
|
||||
std::string Auth(SOCKET Sock) {
|
||||
TCPSend("VC" + GetVer(), Sock);
|
||||
std::string Auth(asio::ip::tcp::socket& Sock) {
|
||||
TCPSend(strtovec("VC" + GetVer()), Sock);
|
||||
|
||||
auto Res = TCPRcv(Sock);
|
||||
|
||||
@@ -82,7 +84,7 @@ std::string Auth(SOCKET Sock) {
|
||||
return "";
|
||||
}
|
||||
|
||||
TCPSend(PublicKey, Sock);
|
||||
TCPSend(strtovec(PublicKey), Sock);
|
||||
if (Terminate)
|
||||
return "";
|
||||
|
||||
@@ -100,7 +102,7 @@ std::string Auth(SOCKET Sock) {
|
||||
UUl("Authentication failed!");
|
||||
return "";
|
||||
}
|
||||
TCPSend("SR", Sock);
|
||||
TCPSend(strtovec("SR"), Sock);
|
||||
if (Terminate)
|
||||
return "";
|
||||
|
||||
@@ -114,7 +116,7 @@ std::string Auth(SOCKET Sock) {
|
||||
if (Res.empty() || Res == "-") {
|
||||
info("Didn't Receive any mods...");
|
||||
ListOfMods = "-";
|
||||
TCPSend("Done", Sock);
|
||||
TCPSend(strtovec("Done"), Sock);
|
||||
info("Done!");
|
||||
return "";
|
||||
}
|
||||
@@ -137,22 +139,18 @@ void AsyncUpdate(uint64_t& Rcv, uint64_t Size, const std::string& Name) {
|
||||
} while (!Terminate && Rcv < Size);
|
||||
}
|
||||
|
||||
char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) {
|
||||
if (Sock == -1) {
|
||||
Terminate = true;
|
||||
UUl("Invalid Socket");
|
||||
return nullptr;
|
||||
}
|
||||
char* TCPRcvRaw(asio::ip::tcp::socket& Sock, uint64_t& GRcv, uint64_t Size) {
|
||||
char* File = new char[Size];
|
||||
uint64_t Rcv = 0;
|
||||
asio::error_code ec;
|
||||
do {
|
||||
int Len = int(Size - Rcv);
|
||||
if (Len > 1000000)
|
||||
Len = 1000000;
|
||||
int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL);
|
||||
if (Temp < 1) {
|
||||
info(std::to_string(Temp));
|
||||
UUl("Socket Closed Code 1");
|
||||
int32_t Temp = asio::read(Sock, asio::buffer(&File[Rcv], Len), ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to receive data from server: {}", ec.message()));
|
||||
UUl("Failed to receive data from server, connection closed (Code 1)");
|
||||
KillSocket(Sock);
|
||||
Terminate = true;
|
||||
delete[] File;
|
||||
@@ -163,29 +161,23 @@ char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) {
|
||||
} while (Rcv < Size && !Terminate);
|
||||
return File;
|
||||
}
|
||||
void MultiKill(SOCKET Sock, SOCKET Sock1) {
|
||||
void MultiKill(asio::ip::tcp::socket& Sock, asio::ip::tcp::socket& Sock1) {
|
||||
KillSocket(Sock1);
|
||||
KillSocket(Sock);
|
||||
Terminate = true;
|
||||
}
|
||||
SOCKET InitDSock() {
|
||||
SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
SOCKADDR_IN ServerAddr;
|
||||
if (DSock < 1) {
|
||||
std::shared_ptr<asio::ip::tcp::socket> InitDSock(asio::ip::tcp::endpoint ep) {
|
||||
auto DSock = std::make_shared<asio::ip::tcp::socket>(io);
|
||||
asio::error_code ec;
|
||||
DSock->connect(ep, ec);
|
||||
if (ec) {
|
||||
KillSocket(DSock);
|
||||
Terminate = true;
|
||||
return 0;
|
||||
}
|
||||
ServerAddr.sin_family = AF_INET;
|
||||
ServerAddr.sin_port = htons(LastPort);
|
||||
inet_pton(AF_INET, LastIP.c_str(), &ServerAddr.sin_addr);
|
||||
if (connect(DSock, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) != 0) {
|
||||
KillSocket(DSock);
|
||||
Terminate = true;
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
char Code[2] = { 'D', char(ClientID) };
|
||||
if (send(DSock, Code, 2, 0) != 2) {
|
||||
asio::write(*DSock, asio::buffer(Code, 2), ec);
|
||||
if (ec) {
|
||||
KillSocket(DSock);
|
||||
Terminate = true;
|
||||
return 0;
|
||||
@@ -193,7 +185,7 @@ SOCKET InitDSock() {
|
||||
return DSock;
|
||||
}
|
||||
|
||||
std::string MultiDownload(SOCKET MSock, SOCKET DSock, uint64_t Size, const std::string& Name) {
|
||||
std::string MultiDownload(asio::ip::tcp::socket& MSock, asio::ip::tcp::socket& DSock, uint64_t Size, const std::string& Name) {
|
||||
|
||||
uint64_t GRcv = 0, MSize = Size / 2, DSize = Size - MSize;
|
||||
|
||||
@@ -239,7 +231,7 @@ void InvalidResource(const std::string& File) {
|
||||
Terminate = true;
|
||||
}
|
||||
|
||||
void SyncResources(SOCKET Sock) {
|
||||
void SyncResources(asio::ip::tcp::socket& Sock) {
|
||||
std::string Ret = Auth(Sock);
|
||||
if (Ret.empty())
|
||||
return;
|
||||
@@ -278,7 +270,7 @@ void SyncResources(SOCKET Sock) {
|
||||
}
|
||||
if (!FNames.empty())
|
||||
info("Syncing...");
|
||||
SOCKET DSock = InitDSock();
|
||||
auto DSock = InitDSock(Sock.remote_endpoint());
|
||||
for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) {
|
||||
auto pos = FN->find_last_of('/');
|
||||
if (pos != std::string::npos) {
|
||||
@@ -296,13 +288,14 @@ void SyncResources(SOCKET Sock) {
|
||||
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
|
||||
fs::create_directories(GetGamePath() + "mods/multiplayer");
|
||||
}
|
||||
auto name = GetGamePath() + "mods/multiplayer" + a.substr(a.find_last_of('/'));
|
||||
auto modname = a.substr(a.find_last_of('/'));
|
||||
#if defined(__linux__)
|
||||
// Linux version of the game doesnt support uppercase letters in mod names
|
||||
for (char& c : name) {
|
||||
for (char& c : modname) {
|
||||
c = ::tolower(c);
|
||||
}
|
||||
#endif
|
||||
auto name = GetGamePath() + "mods/multiplayer" + modname;
|
||||
auto tmp_name = name + ".tmp";
|
||||
fs::copy_file(a, tmp_name, fs::copy_options::overwrite_existing);
|
||||
fs::rename(tmp_name, name);
|
||||
@@ -319,7 +312,7 @@ void SyncResources(SOCKET Sock) {
|
||||
CheckForDir();
|
||||
std::string FName = a.substr(a.find_last_of('/'));
|
||||
do {
|
||||
TCPSend("f" + *FN, Sock);
|
||||
TCPSend(strtovec("f" + *FN), Sock);
|
||||
|
||||
std::string Data = TCPRcv(Sock);
|
||||
if (Data == "CO" || Terminate) {
|
||||
@@ -330,7 +323,7 @@ void SyncResources(SOCKET Sock) {
|
||||
|
||||
std::string Name = std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName;
|
||||
|
||||
Data = MultiDownload(Sock, DSock, std::stoull(*FS), Name);
|
||||
Data = MultiDownload(Sock, *DSock, std::stoull(*FS), Name);
|
||||
|
||||
if (Terminate)
|
||||
break;
|
||||
@@ -361,7 +354,7 @@ void SyncResources(SOCKET Sock) {
|
||||
}
|
||||
KillSocket(DSock);
|
||||
if (!Terminate) {
|
||||
TCPSend("Done", Sock);
|
||||
TCPSend(strtovec("Done"), Sock);
|
||||
info("Done!");
|
||||
} else {
|
||||
UlStatus = "Ulstart";
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
///
|
||||
#include "Network/network.hpp"
|
||||
#include "Zlib/Compressor.h"
|
||||
#include "asio/ip/address.hpp"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <ws2tcpip.h>
|
||||
@@ -14,82 +16,85 @@
|
||||
#include "linuxfixes.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include "Logger.h"
|
||||
#include <set>
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
SOCKET UDPSock = -1;
|
||||
sockaddr_in* ToServer = nullptr;
|
||||
std::shared_ptr<asio::ip::udp::socket> UDPSock = nullptr;
|
||||
|
||||
void UDPSend(std::string Data) {
|
||||
if (ClientID == -1 || UDPSock == -1)
|
||||
void UDPSend(const std::vector<char>& RawData) {
|
||||
if (ClientID == -1 || UDPSock == nullptr)
|
||||
return;
|
||||
if (Data.length() > 400) {
|
||||
auto res = Comp(std::span<char>(Data.data(), Data.size()));
|
||||
std::string Data;
|
||||
if (Data.size() > 400) {
|
||||
auto res = Comp(RawData);
|
||||
Data = "ABG:" + std::string(res.data(), res.size());
|
||||
} else {
|
||||
Data = std::string(RawData.data(), RawData.size());
|
||||
}
|
||||
std::string Packet = char(ClientID + 1) + std::string(":") + Data;
|
||||
int sendOk = sendto(UDPSock, Packet.c_str(), int(Packet.size()), 0, (sockaddr*)ToServer, sizeof(*ToServer));
|
||||
int sendOk = UDPSock->send(asio::buffer(Packet));
|
||||
if (sendOk == SOCKET_ERROR)
|
||||
error("Error Code : " + std::to_string(WSAGetLastError()));
|
||||
}
|
||||
|
||||
void SendLarge(std::string Data) {
|
||||
if (Data.length() > 400) {
|
||||
auto res = Comp(std::span<char>(Data.data(), Data.size()));
|
||||
Data = "ABG:" + std::string(res.data(), res.size());
|
||||
void SendLarge(const std::vector<char>& Data) {
|
||||
if (Data.size() > 400) {
|
||||
auto res = Comp(Data);
|
||||
res.insert(res.begin(), { 'A', 'B', 'G', ':' });
|
||||
if (!TCPSock) {
|
||||
::debug("TCPSock is null");
|
||||
return;
|
||||
}
|
||||
TCPSend(res, *TCPSock);
|
||||
} else {
|
||||
if (!TCPSock) {
|
||||
::debug("TCPSock is null");
|
||||
return;
|
||||
}
|
||||
TCPSend(Data, *TCPSock);
|
||||
}
|
||||
TCPSend(Data, TCPSock);
|
||||
}
|
||||
|
||||
void UDPParser(std::string Packet) {
|
||||
void UDPParser(std::string_view Packet) {
|
||||
if (Packet.substr(0, 4) == "ABG:") {
|
||||
auto substr = Packet.substr(4);
|
||||
auto res = DeComp(std::span<char>(substr.data(), substr.size()));
|
||||
Packet = std::string(res.data(), res.size());
|
||||
auto res = DeComp(std::span<const char>(substr.data(), substr.size()));
|
||||
std::string DeCompPacket = std::string(res.data(), res.size());
|
||||
ServerParser(DeCompPacket);
|
||||
} else {
|
||||
ServerParser(Packet);
|
||||
}
|
||||
ServerParser(Packet);
|
||||
}
|
||||
void UDPRcv() {
|
||||
sockaddr_in FromServer {};
|
||||
#if defined(_WIN32)
|
||||
int clientLength = sizeof(FromServer);
|
||||
#elif defined(__linux__)
|
||||
socklen_t clientLength = sizeof(FromServer);
|
||||
#endif
|
||||
ZeroMemory(&FromServer, clientLength);
|
||||
std::string Ret(10240, 0);
|
||||
if (UDPSock == -1)
|
||||
return;
|
||||
int32_t Rcv = recvfrom(UDPSock, &Ret[0], 10240, 0, (sockaddr*)&FromServer, &clientLength);
|
||||
if (Rcv == SOCKET_ERROR)
|
||||
return;
|
||||
UDPParser(Ret.substr(0, Rcv));
|
||||
}
|
||||
void UDPClientMain(const std::string& IP, int Port) {
|
||||
#ifdef _WIN32
|
||||
WSADATA data;
|
||||
if (WSAStartup(514, &data)) {
|
||||
error("Can't start Winsock!");
|
||||
static thread_local std::array<char, 10240> Ret {};
|
||||
if (UDPSock == nullptr) {
|
||||
::debug("UDPSock is null");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
delete ToServer;
|
||||
ToServer = new sockaddr_in;
|
||||
ToServer->sin_family = AF_INET;
|
||||
ToServer->sin_port = htons(Port);
|
||||
inet_pton(AF_INET, IP.c_str(), &ToServer->sin_addr);
|
||||
UDPSock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
asio::error_code ec;
|
||||
int32_t Rcv = UDPSock->receive(asio::buffer(Ret.data(), Ret.size() - 1), 0, ec);
|
||||
if (ec)
|
||||
return;
|
||||
Ret[Rcv] = 0;
|
||||
UDPParser(std::string_view(Ret.data(), Rcv));
|
||||
}
|
||||
void UDPClientMain(asio::ip::address addr, uint16_t port) {
|
||||
UDPSock = std::make_shared<asio::ip::udp::socket>(io);
|
||||
asio::error_code ec;
|
||||
UDPSock->connect(asio::ip::udp::endpoint(addr, port), ec);
|
||||
if (ec) {
|
||||
::error(fmt::format("Failed to connect UDP to server: {}", ec.message()));
|
||||
Terminate = true;
|
||||
}
|
||||
GameSend("P" + std::to_string(ClientID));
|
||||
TCPSend("H", TCPSock);
|
||||
UDPSend("p");
|
||||
TCPSend(strtovec("H"), *TCPSock);
|
||||
UDPSend(strtovec("p"));
|
||||
while (!Terminate)
|
||||
UDPRcv();
|
||||
KillSocket(UDPSock);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
///
|
||||
|
||||
#include "Logger.h"
|
||||
#include "fmt/format.h"
|
||||
#include <Zlib/Compressor.h>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
@@ -27,7 +28,7 @@
|
||||
|
||||
int LastPort;
|
||||
std::string LastIP;
|
||||
SOCKET TCPSock = -1;
|
||||
std::shared_ptr<asio::ip::tcp::socket> TCPSock = nullptr;
|
||||
|
||||
bool CheckBytes(int32_t Bytes) {
|
||||
if (Bytes == 0) {
|
||||
@@ -46,8 +47,8 @@ void UUl(const std::string& R) {
|
||||
UlStatus = "UlDisconnected: " + R;
|
||||
}
|
||||
|
||||
void TCPSend(const std::string& Data, uint64_t Sock) {
|
||||
if (Sock == -1) {
|
||||
void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock) {
|
||||
if (!Sock.is_open()) {
|
||||
Terminate = true;
|
||||
UUl("Invalid Socket");
|
||||
return;
|
||||
@@ -57,63 +58,43 @@ void TCPSend(const std::string& Data, uint64_t Sock) {
|
||||
std::string Send(4, 0);
|
||||
Size = int32_t(Data.size());
|
||||
memcpy(&Send[0], &Size, sizeof(Size));
|
||||
Send += Data;
|
||||
Send += std::string(Data.data(), Data.size());
|
||||
// Do not use Size before this point for anything but the header
|
||||
Sent = 0;
|
||||
Size += 4;
|
||||
do {
|
||||
if (size_t(Sent) >= Send.size()) {
|
||||
error("string OOB in " + std::string(__func__));
|
||||
UUl("TCP Send OOB");
|
||||
return;
|
||||
}
|
||||
Temp = send(Sock, &Send[Sent], Size - Sent, 0);
|
||||
if (!CheckBytes(Temp)) {
|
||||
UUl("Socket Closed Code 2");
|
||||
return;
|
||||
}
|
||||
Sent += Temp;
|
||||
} while (Sent < Size);
|
||||
asio::error_code ec;
|
||||
asio::write(Sock, asio::buffer(Send), ec);
|
||||
if (ec) {
|
||||
UUl(fmt::format("Failed to send data: {}", ec.message()));
|
||||
}
|
||||
}
|
||||
|
||||
std::string TCPRcv(SOCKET Sock) {
|
||||
if (Sock == -1) {
|
||||
std::string TCPRcv(asio::ip::tcp::socket& Sock) {
|
||||
if (!Sock.is_open()) {
|
||||
Terminate = true;
|
||||
UUl("Invalid Socket");
|
||||
return "";
|
||||
}
|
||||
int32_t Header, BytesRcv = 0, Temp;
|
||||
std::vector<char> Data(sizeof(Header));
|
||||
do {
|
||||
Temp = recv(Sock, &Data[BytesRcv], 4 - BytesRcv, 0);
|
||||
if (!CheckBytes(Temp)) {
|
||||
UUl("Socket Closed Code 3");
|
||||
return "";
|
||||
}
|
||||
BytesRcv += Temp;
|
||||
} while (BytesRcv < 4);
|
||||
asio::error_code ec;
|
||||
asio::read(Sock, asio::buffer(Data), ec);
|
||||
if (ec) {
|
||||
UUl(fmt::format("Failed to receive header: {}", ec.message()));
|
||||
}
|
||||
memcpy(&Header, &Data[0], sizeof(Header));
|
||||
|
||||
if (!CheckBytes(BytesRcv)) {
|
||||
UUl("Socket Closed Code 4");
|
||||
return "";
|
||||
}
|
||||
Data.resize(Header);
|
||||
BytesRcv = 0;
|
||||
do {
|
||||
Temp = recv(Sock, &Data[BytesRcv], Header - BytesRcv, 0);
|
||||
if (!CheckBytes(Temp)) {
|
||||
UUl("Socket Closed Code 5");
|
||||
return "";
|
||||
}
|
||||
BytesRcv += Temp;
|
||||
} while (BytesRcv < Header);
|
||||
asio::read(Sock, asio::buffer(Data), ec);
|
||||
if (ec) {
|
||||
UUl(fmt::format("Failed to receive data: {}", ec.message()));
|
||||
}
|
||||
|
||||
std::string Ret(Data.data(), Header);
|
||||
|
||||
if (Ret.substr(0, 4) == "ABG:") {
|
||||
auto substr = Ret.substr(4);
|
||||
auto res = DeComp(std::span<char>(substr.data(), substr.size()));
|
||||
auto res = DeComp(strtovec(substr));
|
||||
Ret = std::string(res.data(), res.size());
|
||||
}
|
||||
|
||||
@@ -125,50 +106,24 @@ std::string TCPRcv(SOCKET Sock) {
|
||||
return Ret;
|
||||
}
|
||||
|
||||
void TCPClientMain(const std::string& IP, int Port) {
|
||||
LastIP = IP;
|
||||
LastPort = Port;
|
||||
void TCPClientMain(asio::ip::tcp::socket&& socket) {
|
||||
if (!TCPSock) {
|
||||
return;
|
||||
}
|
||||
LastIP = socket.remote_endpoint().address().to_string();
|
||||
LastPort = socket.remote_endpoint().port();
|
||||
SOCKADDR_IN ServerAddr;
|
||||
int RetCode;
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
WSAStartup(514, &wsaData); // 2.2
|
||||
#endif
|
||||
TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
TCPSock = std::make_shared<asio::ip::tcp::socket>(std::move(socket));
|
||||
|
||||
if (TCPSock == -1) {
|
||||
printf("Client: socket failed! Error code: %d\n", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
ServerAddr.sin_family = AF_INET;
|
||||
ServerAddr.sin_port = htons(Port);
|
||||
inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr);
|
||||
RetCode = connect(TCPSock, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
|
||||
if (RetCode != 0) {
|
||||
UlStatus = "UlConnection Failed!";
|
||||
error("Client: connect failed! Error code: " + std::to_string(WSAGetLastError()));
|
||||
KillSocket(TCPSock);
|
||||
WSACleanup();
|
||||
Terminate = true;
|
||||
return;
|
||||
}
|
||||
info("Connected!");
|
||||
|
||||
char Code = 'C';
|
||||
send(TCPSock, &Code, 1, 0);
|
||||
SyncResources(TCPSock);
|
||||
while (!Terminate) {
|
||||
ServerParser(TCPRcv(TCPSock));
|
||||
asio::write(*TCPSock, asio::buffer(&Code, 1));
|
||||
SyncResources(*TCPSock);
|
||||
while (!Terminate && TCPSock) {
|
||||
ServerParser(TCPRcv(*TCPSock));
|
||||
}
|
||||
GameSend("T");
|
||||
////Game Send Terminate
|
||||
if (KillSocket(TCPSock) != 0)
|
||||
debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError()));
|
||||
|
||||
#ifdef _WIN32
|
||||
if (WSACleanup() != 0)
|
||||
debug("(TCP) Client: WSACleanup() failed!...");
|
||||
#endif
|
||||
KillSocket(TCPSock);
|
||||
}
|
||||
|
||||
35
src/NetworkHelpers.cpp
Normal file
35
src/NetworkHelpers.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "NetworkHelpers.h"
|
||||
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
static uint32_t RecvHeader(tcp::socket& socket) {
|
||||
std::array<uint8_t, sizeof(uint32_t)> header_buffer {};
|
||||
asio::error_code ec;
|
||||
auto n = asio::read(socket, asio::buffer(header_buffer), ec);
|
||||
if (ec) {
|
||||
throw std::runtime_error(std::string("recv() of header failed: ") + ec.message());
|
||||
}
|
||||
if (n == 0) {
|
||||
throw std::runtime_error("Game disconnected");
|
||||
}
|
||||
return *reinterpret_cast<uint32_t*>(header_buffer.data());
|
||||
}
|
||||
|
||||
/// Throws!!!
|
||||
void ReceiveFromGame(tcp::socket& socket, std::vector<char>& out_data) {
|
||||
auto header = RecvHeader(socket);
|
||||
out_data.resize(header);
|
||||
asio::error_code ec;
|
||||
auto n = asio::read(socket, asio::buffer(out_data), ec);
|
||||
if (ec) {
|
||||
throw std::runtime_error(std::string("recv() of data failed: ") + ec.message());
|
||||
}
|
||||
if (n == 0) {
|
||||
throw std::runtime_error("Game disconnected");
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
///
|
||||
|
||||
#include "zip_file.h"
|
||||
#include <charconv>
|
||||
#include <httplib.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
@@ -31,36 +32,44 @@ int ProxyPort = 0;
|
||||
|
||||
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);
|
||||
struct Version {
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
uint8_t patch;
|
||||
Version(uint8_t major, uint8_t minor, uint8_t patch);
|
||||
Version(const std::array<uint8_t, 3>& v);
|
||||
};
|
||||
|
||||
std::array<uint8_t, 3> VersionStrToInts(const std::string& str) {
|
||||
std::array<uint8_t, 3> Version;
|
||||
std::stringstream ss(str);
|
||||
for (uint8_t& i : Version) {
|
||||
std::string Part;
|
||||
std::getline(ss, Part, '.');
|
||||
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
|
||||
}
|
||||
return Version;
|
||||
}
|
||||
|
||||
bool IsOutdated(const Version& Current, const Version& Newest) {
|
||||
if (Newest.major > Current.major) {
|
||||
return true;
|
||||
} else if (Newest.major == Current.major && Newest.minor > Current.minor) {
|
||||
return true;
|
||||
} else if (Newest.major == Current.major && Newest.minor == Current.minor && Newest.patch > Current.patch) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
|
||||
: major(major)
|
||||
, minor(minor)
|
||||
, patch(patch) { }
|
||||
|
||||
bool VersionParser::operator==(const VersionParser& rhs) const noexcept {
|
||||
return std::is_eq(*this <=> rhs);
|
||||
Version::Version(const std::array<uint8_t, 3>& v)
|
||||
: Version(v[0], v[1], v[2]) {
|
||||
}
|
||||
|
||||
std::string GetEN() {
|
||||
@@ -72,10 +81,10 @@ std::string GetEN() {
|
||||
}
|
||||
|
||||
std::string GetVer() {
|
||||
return "2.0";
|
||||
return "2.1";
|
||||
}
|
||||
std::string GetPatch() {
|
||||
return ".85";
|
||||
return ".0";
|
||||
}
|
||||
|
||||
std::string GetEP(char* P) {
|
||||
@@ -163,7 +172,7 @@ void CheckForUpdates(int argc, char* args[], const std::string& CV) {
|
||||
system("clear");
|
||||
#endif
|
||||
|
||||
if (FileHash != LatestHash && VersionParser(LatestVersion) > VersionParser(GetVer() + GetPatch())) {
|
||||
if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) {
|
||||
info("Launcher update found!");
|
||||
#if defined(__linux__)
|
||||
error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches.");
|
||||
@@ -177,7 +186,7 @@ void CheckForUpdates(int argc, char* args[], const std::string& CV) {
|
||||
+ PublicKey + "&branch=" + Branch,
|
||||
EP);
|
||||
URelaunch(argc, args);
|
||||
#endif
|
||||
#endif
|
||||
} else
|
||||
info("Launcher version is up to date");
|
||||
TraceBack++;
|
||||
@@ -195,6 +204,13 @@ void CustomPort(int argc, char* argv[]) {
|
||||
if (argc > 2)
|
||||
Dev = true;
|
||||
}
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::string_view(argv[i]) == "--dev") {
|
||||
Dev = true;
|
||||
} else if (std::string_view(argv[i]) == "--no-dev") {
|
||||
Dev = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -246,7 +262,15 @@ void InitLauncher(int argc, char* argv[]) {
|
||||
CheckLocalKey();
|
||||
ConfigInit();
|
||||
CustomPort(argc, argv);
|
||||
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
|
||||
bool update = true;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::string_view(argv[i]) == "--no-update") {
|
||||
update = false;
|
||||
}
|
||||
}
|
||||
if (update) {
|
||||
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -309,6 +333,8 @@ void PreGame(const std::string& GamePath) {
|
||||
|
||||
CheckMP(GetGamePath() + "mods/multiplayer");
|
||||
|
||||
info("Game user path: '" + GetGamePath() + "'");
|
||||
|
||||
if (!Dev) {
|
||||
std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey);
|
||||
transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"cpp-httplib",
|
||||
"nlohmann-json",
|
||||
"zlib",
|
||||
"openssl"
|
||||
"openssl",
|
||||
"asio",
|
||||
"fmt"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user