mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-07-01 15:36:10 +00:00
start implementing ipv6 support, of course it all sucks
so time to rewrite 90% of it fucking kill me
This commit is contained in:
parent
188a31c69e
commit
f65fbf60fe
@ -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")
|
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(httplib CONFIG REQUIRED)
|
||||||
find_package(nlohmann_json CONFIG REQUIRED)
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
|
find_package(asio CONFIG REQUIRED)
|
||||||
|
find_package(fmt 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")
|
||||||
@ -23,15 +25,15 @@ if (WIN32)
|
|||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
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)
|
elseif (LINUX)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto)
|
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto asio::asio fmt::fmt)
|
||||||
else(WIN32) #MINGW
|
elseif (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")
|
||||||
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)
|
endif(WIN32)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE "include")
|
target_include_directories(${PROJECT_NAME} PRIVATE "include")
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Helpers.h"
|
#include "Helpers.h"
|
||||||
|
#include "asio/io_context.hpp"
|
||||||
|
#include "asio/ip/address.hpp"
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -15,10 +17,14 @@
|
|||||||
#include "linuxfixes.h"
|
#include "linuxfixes.h"
|
||||||
#include <bits/types/siginfo_t.h>
|
#include <bits/types/siginfo_t.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
|
#include <vector>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
extern asio::io_context io;
|
||||||
|
|
||||||
void NetReset();
|
void NetReset();
|
||||||
extern bool Dev;
|
extern bool Dev;
|
||||||
extern int ping;
|
extern int ping;
|
||||||
@ -30,8 +36,8 @@ extern int LastPort;
|
|||||||
extern bool ModLoaded;
|
extern bool ModLoaded;
|
||||||
extern bool Terminate;
|
extern bool Terminate;
|
||||||
extern int DEFAULT_PORT;
|
extern int DEFAULT_PORT;
|
||||||
extern uint64_t UDPSock;
|
extern std::shared_ptr<asio::ip::udp::socket> UDPSock;
|
||||||
extern uint64_t TCPSock;
|
extern std::shared_ptr<asio::ip::tcp::socket> TCPSock;
|
||||||
extern std::string Branch;
|
extern std::string Branch;
|
||||||
extern bool TCPTerminate;
|
extern bool TCPTerminate;
|
||||||
extern std::string LastIP;
|
extern std::string LastIP;
|
||||||
@ -40,18 +46,21 @@ extern std::string UlStatus;
|
|||||||
extern std::string PublicKey;
|
extern std::string PublicKey;
|
||||||
extern std::string PrivateKey;
|
extern std::string PrivateKey;
|
||||||
extern std::string ListOfMods;
|
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 UUl(const std::string& R);
|
||||||
void UDPSend(const std::vector<char>& Data);
|
void UDPSend(const std::vector<char>& Data);
|
||||||
bool CheckBytes(int32_t Bytes);
|
bool CheckBytes(int32_t Bytes);
|
||||||
void GameSend(std::string_view Data);
|
void GameSend(std::string_view Data);
|
||||||
void SendLarge(const std::vector<char>& Data);
|
void SendLarge(const std::vector<char>& Data);
|
||||||
std::string TCPRcv(uint64_t Sock);
|
std::string TCPRcv(asio::ip::tcp::socket& Sock);
|
||||||
void SyncResources(uint64_t TCPSock);
|
void SyncResources(asio::ip::tcp::socket& TCPSock);
|
||||||
std::string GetAddr(const std::string& IP);
|
std::string GetAddr(const std::string& IP);
|
||||||
void ServerParser(std::string_view Data);
|
void ServerParser(std::string_view Data);
|
||||||
std::string Login(const std::string& fields);
|
std::string Login(const std::string& fields);
|
||||||
void TCPSend(const std::vector<char>& Data, uint64_t Sock);
|
void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock);
|
||||||
void TCPClientMain(const std::string& IP, int Port);
|
void TCPClientMain(asio::ip::tcp::socket&& Socket);
|
||||||
void UDPClientMain(const std::string& IP, int Port);
|
void UDPClientMain(asio::ip::address addr, uint16_t port);
|
||||||
void TCPGameServer(const std::string& IP, int Port);
|
void TCPGameServer(asio::ip::tcp::socket&& Socket);
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(__linux__)
|
#include <asio.hpp>
|
||||||
#include "linuxfixes.h"
|
|
||||||
#else
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#endif
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void ReceiveFromGame(SOCKET socket, std::vector<char>& out_data);
|
void ReceiveFromGame(asio::ip::tcp::socket& socket, std::vector<char>& out_data);
|
||||||
|
@ -9,17 +9,15 @@
|
|||||||
#include "Network/network.hpp"
|
#include "Network/network.hpp"
|
||||||
#include "NetworkHelpers.h"
|
#include "NetworkHelpers.h"
|
||||||
#include "Security/Init.h"
|
#include "Security/Init.h"
|
||||||
|
#include <asio/io_context.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <optional>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#if defined(_WIN32)
|
#if defined(__linux__)
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#elif defined(__linux__)
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <spawn.h>
|
#include <spawn.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -28,6 +26,7 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Startup.h"
|
#include "Startup.h"
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -45,26 +44,58 @@ std::string MStatus;
|
|||||||
bool ModLoaded;
|
bool ModLoaded;
|
||||||
int ping = -1;
|
int ping = -1;
|
||||||
|
|
||||||
void StartSync(const std::string& Data) {
|
asio::io_context io {};
|
||||||
std::string IP = GetAddr(Data.substr(1, Data.find(':') - 1));
|
|
||||||
if (IP.find('.') == -1) {
|
static asio::ip::tcp::socket ResolveAndConnect(const std::string& host_port_string) {
|
||||||
if (IP == "DNS")
|
|
||||||
UlStatus = "UlConnection Failed! (DNS Lookup Failed)";
|
using namespace asio;
|
||||||
else
|
ip::tcp::resolver resolver(io);
|
||||||
UlStatus = "UlConnection Failed! (WSA failed to start)";
|
asio::error_code ec;
|
||||||
ListOfMods = "-";
|
auto port = host_port_string.substr(host_port_string.find_last_of(':') + 1);
|
||||||
Terminate = true;
|
auto host = host_port_string.substr(0, host_port_string.find_last_of(':'));
|
||||||
return;
|
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...";
|
UlStatus = "UlLoading...";
|
||||||
|
|
||||||
|
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;
|
TCPTerminate = false;
|
||||||
Terminate = false;
|
Terminate = false;
|
||||||
ConfList->clear();
|
ConfList->clear();
|
||||||
ping = -1;
|
ping = -1;
|
||||||
std::thread GS(TCPGameServer, IP, std::stoi(Data.substr(Data.find(':') + 1)));
|
std::thread GS(TCPGameServer, std::move(Socket));
|
||||||
GS.detach();
|
GS.detach();
|
||||||
info("Connecting to server");
|
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) {
|
bool IsAllowedLink(const std::string& Link) {
|
||||||
@ -92,7 +123,7 @@ bool IsAllowedLink(const std::string& Link) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parse(std::span<char> InData, SOCKET CSocket) {
|
void Parse(std::span<char> InData, asio::ip::tcp::socket& CSocket) {
|
||||||
std::string OutData;
|
std::string OutData;
|
||||||
char Code = InData[0], SubCode = 0;
|
char Code = InData[0], SubCode = 0;
|
||||||
if (InData.size() > 1)
|
if (InData.size() > 1)
|
||||||
@ -213,18 +244,19 @@ void Parse(std::span<char> InData, SOCKET CSocket) {
|
|||||||
OutData.clear();
|
OutData.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!OutData.empty() && CSocket != -1) {
|
if (!OutData.empty() && CSocket.is_open()) {
|
||||||
uint32_t DataSize = OutData.size();
|
uint32_t DataSize = OutData.size();
|
||||||
std::vector<char> ToSend(sizeof(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(reinterpret_cast<char*>(&DataSize), sizeof(DataSize), ToSend.begin());
|
||||||
std::copy_n(OutData.data(), OutData.size(), ToSend.begin() + sizeof(DataSize));
|
std::copy_n(OutData.data(), OutData.size(), ToSend.begin() + sizeof(DataSize));
|
||||||
int res = send(CSocket, ToSend.data(), int(ToSend.size()), 0);
|
asio::error_code ec;
|
||||||
if (res < 0) {
|
asio::write(CSocket, asio::buffer(ToSend), ec);
|
||||||
debug("(Core) send failed with error: " + std::to_string(WSAGetLastError()));
|
if (ec) {
|
||||||
|
debug(fmt::format("(Core) send failed with error: {}", ec.message()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void GameHandler(SOCKET Client) {
|
void GameHandler(asio::ip::tcp::socket& Client) {
|
||||||
std::vector<char> data {};
|
std::vector<char> data {};
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
@ -250,68 +282,52 @@ void localRes() {
|
|||||||
}
|
}
|
||||||
void CoreMain() {
|
void CoreMain() {
|
||||||
debug("Core Network on start!");
|
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;
|
auto acceptor = asio::ip::tcp::acceptor(io, listen_ep);
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
acceptor.listen(asio::ip::tcp::socket::max_listen_connections, ec);
|
||||||
hints.ai_protocol = IPPROTO_TCP;
|
if (ec) {
|
||||||
hints.ai_flags = AI_PASSIVE;
|
::error(fmt::format("listen() failed, which is needed for the launcher to operate. Error: {}",
|
||||||
iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res);
|
ec.message()));
|
||||||
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;
|
|
||||||
}
|
|
||||||
#if defined(__linux__)
|
|
||||||
int opt = 1;
|
|
||||||
if (setsockopt(LSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
|
||||||
error("setsockopt(SO_REUSEADDR) failed");
|
|
||||||
#endif
|
|
||||||
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();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
CSocket = accept(LSocket, nullptr, nullptr);
|
auto CSocket = acceptor.accept(ec);
|
||||||
if (CSocket == -1) {
|
if (ec) {
|
||||||
error("(Core) accept failed with error: " + std::to_string(WSAGetLastError()));
|
error(fmt::format("(Core) accept failed with error: {}", ec.message()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
localRes();
|
localRes();
|
||||||
info("Game Connected!");
|
info("Game Connected!");
|
||||||
GameHandler(CSocket);
|
GameHandler(CSocket);
|
||||||
warn("Game Reconnecting...");
|
warn("Game Reconnecting...");
|
||||||
} while (CSocket);
|
} while (LSocket.is_open());
|
||||||
KillSocket(LSocket);
|
KillSocket(LSocket);
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "Helpers.h"
|
#include "Helpers.h"
|
||||||
#include "Network/network.hpp"
|
#include "Network/network.hpp"
|
||||||
#include "NetworkHelpers.h"
|
#include "NetworkHelpers.h"
|
||||||
|
#include "asio/socket_base.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -27,6 +28,7 @@
|
|||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -34,20 +36,31 @@
|
|||||||
std::chrono::time_point<std::chrono::high_resolution_clock> PingStart, PingEnd;
|
std::chrono::time_point<std::chrono::high_resolution_clock> PingStart, PingEnd;
|
||||||
bool GConnected = false;
|
bool GConnected = false;
|
||||||
bool CServer = true;
|
bool CServer = true;
|
||||||
SOCKET CSocket = -1;
|
std::shared_ptr<asio::ip::tcp::socket> CSocket = nullptr;
|
||||||
SOCKET GSocket = -1;
|
std::shared_ptr<asio::ip::tcp::socket> GSocket = nullptr;
|
||||||
|
|
||||||
int KillSocket(uint64_t Dead) {
|
void KillSocket(std::shared_ptr<asio::ip::tcp::socket>& Dead) {
|
||||||
if (Dead == (SOCKET)-1) {
|
if (!Dead)
|
||||||
debug("Kill socket got -1 returning...");
|
return;
|
||||||
return 0;
|
asio::error_code ec;
|
||||||
|
Dead->shutdown(asio::socket_base::shutdown_both, ec);
|
||||||
}
|
}
|
||||||
shutdown(Dead, SD_BOTH);
|
|
||||||
int a = closesocket(Dead);
|
void KillSocket(std::shared_ptr<asio::ip::udp::socket>& Dead) {
|
||||||
if (a != 0) {
|
if (!Dead)
|
||||||
warn("Failed to close socket!");
|
return;
|
||||||
|
asio::error_code ec;
|
||||||
|
Dead->shutdown(asio::socket_base::shutdown_both, ec);
|
||||||
}
|
}
|
||||||
return a;
|
|
||||||
|
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) {
|
bool CheckBytes(uint32_t Bytes) {
|
||||||
@ -64,7 +77,7 @@ bool CheckBytes(uint32_t Bytes) {
|
|||||||
void GameSend(std::string_view RawData) {
|
void GameSend(std::string_view RawData) {
|
||||||
static std::mutex Lock;
|
static std::mutex Lock;
|
||||||
std::scoped_lock Guard(Lock);
|
std::scoped_lock Guard(Lock);
|
||||||
if (TCPTerminate || !GConnected || CSocket == -1)
|
if (TCPTerminate || !GConnected || CSocket == nullptr)
|
||||||
return;
|
return;
|
||||||
int32_t Size, Temp, Sent;
|
int32_t Size, Temp, Sent;
|
||||||
uint32_t DataSize = RawData.size();
|
uint32_t DataSize = RawData.size();
|
||||||
@ -73,24 +86,14 @@ void GameSend(std::string_view RawData) {
|
|||||||
std::copy_n(RawData.data(), RawData.size(), Data.begin() + sizeof(DataSize));
|
std::copy_n(RawData.data(), RawData.size(), Data.begin() + sizeof(DataSize));
|
||||||
Size = Data.size();
|
Size = Data.size();
|
||||||
Sent = 0;
|
Sent = 0;
|
||||||
#ifdef DEBUG
|
|
||||||
if (Size > 1000) {
|
asio::error_code ec;
|
||||||
debug("Launcher -> game (" + std::to_string(Size) + ")");
|
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);
|
|
||||||
// send separately to avoid an allocation for += "\n"
|
|
||||||
/*Temp = send(CSocket, "\n", 1, 0);
|
|
||||||
if (!CheckBytes(Temp)) {
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerSend(const std::vector<char>& Data, bool Rel) {
|
void ServerSend(const std::vector<char>& Data, bool Rel) {
|
||||||
@ -110,8 +113,8 @@ void ServerSend(const std::vector<char>& Data, bool Rel) {
|
|||||||
if (Ack || Rel) {
|
if (Ack || Rel) {
|
||||||
if (Ack || DLen > 1000)
|
if (Ack || DLen > 1000)
|
||||||
SendLarge(Data);
|
SendLarge(Data);
|
||||||
else
|
else if (TCPSock)
|
||||||
TCPSend(Data, TCPSock);
|
TCPSend(Data, *TCPSock);
|
||||||
} else
|
} else
|
||||||
UDPSend(Data);
|
UDPSend(Data);
|
||||||
}
|
}
|
||||||
@ -122,78 +125,20 @@ void NetReset() {
|
|||||||
Terminate = false;
|
Terminate = false;
|
||||||
UlStatus = "Ulstart";
|
UlStatus = "Ulstart";
|
||||||
MStatus = " ";
|
MStatus = " ";
|
||||||
if (UDPSock != (SOCKET)(-1)) {
|
if (UDPSock != nullptr) {
|
||||||
debug("Terminating UDP Socket : " + std::to_string(TCPSock));
|
KillSocket(*UDPSock);
|
||||||
KillSocket(UDPSock);
|
|
||||||
}
|
}
|
||||||
UDPSock = -1;
|
UDPSock = nullptr;
|
||||||
if (TCPSock != (SOCKET)(-1)) {
|
if (TCPSock != nullptr) {
|
||||||
debug("Terminating TCP Socket : " + std::to_string(TCPSock));
|
KillSocket(*TCPSock);
|
||||||
KillSocket(TCPSock);
|
|
||||||
}
|
}
|
||||||
TCPSock = -1;
|
TCPSock = nullptr;
|
||||||
if (GSocket != (SOCKET)(-1)) {
|
if (GSocket != nullptr) {
|
||||||
debug("Terminating GTCP Socket : " + std::to_string(GSocket));
|
KillSocket(*GSocket);
|
||||||
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;
|
|
||||||
}
|
|
||||||
#if defined (__linux__)
|
|
||||||
int opt = 1;
|
|
||||||
if (setsockopt(GSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
|
||||||
error("setsockopt(SO_REUSEADDR) failed");
|
|
||||||
#endif
|
|
||||||
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() {
|
void AutoPing() {
|
||||||
while (!Terminate) {
|
while (!Terminate) {
|
||||||
ServerSend(strtovec("p"), false);
|
ServerSend(strtovec("p"), false);
|
||||||
@ -228,17 +173,47 @@ void ParserAsync(std::string_view Data) {
|
|||||||
void ServerParser(std::string_view Data) {
|
void ServerParser(std::string_view Data) {
|
||||||
ParserAsync(Data);
|
ParserAsync(Data);
|
||||||
}
|
}
|
||||||
void NetMain(const std::string& IP, int Port) {
|
void NetMain(asio::ip::address addr, uint16_t port) {
|
||||||
std::thread Ping(AutoPing);
|
std::thread Ping(AutoPing);
|
||||||
Ping.detach();
|
Ping.detach();
|
||||||
UDPClientMain(IP, Port);
|
UDPClientMain(addr, port);
|
||||||
CServer = true;
|
CServer = true;
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
info("Connection Terminated!");
|
info("Connection Terminated!");
|
||||||
}
|
}
|
||||||
void TCPGameServer(const std::string& IP, int Port) {
|
void TCPGameServer(asio::ip::tcp::socket&& Socket) {
|
||||||
GSocket = SetupListener();
|
asio::ip::tcp::endpoint listen_ep(asio::ip::address::from_string("0.0.0.0"), static_cast<uint16_t>(DEFAULT_PORT + 1));
|
||||||
while (!TCPTerminate && GSocket != -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");
|
debug("MAIN LOOP OF GAME SERVER");
|
||||||
GConnected = false;
|
GConnected = false;
|
||||||
if (!CServer) {
|
if (!CServer) {
|
||||||
@ -249,38 +224,35 @@ void TCPGameServer(const std::string& IP, int Port) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (CServer) {
|
if (CServer) {
|
||||||
std::thread Client(TCPClientMain, IP, Port);
|
std::thread Client(TCPClientMain, std::move(Socket));
|
||||||
Client.detach();
|
Client.detach();
|
||||||
}
|
}
|
||||||
CSocket = accept(GSocket, nullptr, nullptr);
|
|
||||||
if (CSocket == -1) {
|
CSocket = std::make_shared<asio::ip::tcp::socket>(acceptor.accept());
|
||||||
debug("(Proxy) accept failed with error: " + std::to_string(WSAGetLastError()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
debug("(Proxy) Game Connected!");
|
debug("(Proxy) Game Connected!");
|
||||||
GConnected = true;
|
GConnected = true;
|
||||||
if (CServer) {
|
if (CServer) {
|
||||||
std::thread t1(NetMain, IP, Port);
|
std::thread t1(NetMain, CSocket->remote_endpoint().address(), CSocket->remote_endpoint().port());
|
||||||
t1.detach();
|
t1.detach();
|
||||||
CServer = false;
|
CServer = false;
|
||||||
}
|
}
|
||||||
std::vector<char> data {};
|
std::vector<char> data {};
|
||||||
|
|
||||||
// Read byte by byte until '>' is rcved then get the size and read based on it
|
// Read byte by byte until '>' is rcved then get the size and read based on it
|
||||||
do {
|
while (!TCPTerminate && !CSocket) {
|
||||||
try {
|
try {
|
||||||
ReceiveFromGame(CSocket, data);
|
ReceiveFromGame(*CSocket, data);
|
||||||
ServerSend(data, false);
|
ServerSend(data, false);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
error(std::string("Error while receiving from game: ") + e.what());
|
error(std::string("Error while receiving from game: ") + e.what());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (!TCPTerminate);
|
}
|
||||||
}
|
}
|
||||||
TCPTerminate = true;
|
TCPTerminate = true;
|
||||||
GConnected = false;
|
GConnected = false;
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
if (CSocket != SOCKET_ERROR)
|
if (CSocket != nullptr)
|
||||||
KillSocket(CSocket);
|
KillSocket(CSocket);
|
||||||
debug("END OF GAME SERVER");
|
debug("END OF GAME SERVER");
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
///
|
///
|
||||||
|
|
||||||
#include "Network/network.hpp"
|
#include "Network/network.hpp"
|
||||||
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
@ -27,6 +28,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <future>
|
#include <future>
|
||||||
|
#include <asio.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -72,7 +74,7 @@ void Abord() {
|
|||||||
info("Terminated!");
|
info("Terminated!");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Auth(SOCKET Sock) {
|
std::string Auth(asio::ip::tcp::socket& Sock) {
|
||||||
TCPSend(strtovec("VC" + GetVer()), Sock);
|
TCPSend(strtovec("VC" + GetVer()), Sock);
|
||||||
|
|
||||||
auto Res = TCPRcv(Sock);
|
auto Res = TCPRcv(Sock);
|
||||||
@ -137,22 +139,18 @@ void AsyncUpdate(uint64_t& Rcv, uint64_t Size, const std::string& Name) {
|
|||||||
} while (!Terminate && Rcv < Size);
|
} while (!Terminate && Rcv < Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) {
|
char* TCPRcvRaw(asio::ip::tcp::socket& Sock, uint64_t& GRcv, uint64_t Size) {
|
||||||
if (Sock == -1) {
|
|
||||||
Terminate = true;
|
|
||||||
UUl("Invalid Socket");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
char* File = new char[Size];
|
char* File = new char[Size];
|
||||||
uint64_t Rcv = 0;
|
uint64_t Rcv = 0;
|
||||||
|
asio::error_code ec;
|
||||||
do {
|
do {
|
||||||
int Len = int(Size - Rcv);
|
int Len = int(Size - Rcv);
|
||||||
if (Len > 1000000)
|
if (Len > 1000000)
|
||||||
Len = 1000000;
|
Len = 1000000;
|
||||||
int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL);
|
int32_t Temp = asio::read(Sock, asio::buffer(&File[Rcv], Len), ec);
|
||||||
if (Temp < 1) {
|
if (ec) {
|
||||||
info(std::to_string(Temp));
|
::error(fmt::format("Failed to receive data from server: {}", ec.message()));
|
||||||
UUl("Socket Closed Code 1");
|
UUl("Failed to receive data from server, connection closed (Code 1)");
|
||||||
KillSocket(Sock);
|
KillSocket(Sock);
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
delete[] File;
|
delete[] File;
|
||||||
@ -163,29 +161,23 @@ char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) {
|
|||||||
} while (Rcv < Size && !Terminate);
|
} while (Rcv < Size && !Terminate);
|
||||||
return File;
|
return File;
|
||||||
}
|
}
|
||||||
void MultiKill(SOCKET Sock, SOCKET Sock1) {
|
void MultiKill(asio::ip::tcp::socket& Sock, asio::ip::tcp::socket& Sock1) {
|
||||||
KillSocket(Sock1);
|
KillSocket(Sock1);
|
||||||
KillSocket(Sock);
|
KillSocket(Sock);
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
}
|
}
|
||||||
SOCKET InitDSock() {
|
std::shared_ptr<asio::ip::tcp::socket> InitDSock(asio::ip::tcp::endpoint ep) {
|
||||||
SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
auto DSock = std::make_shared<asio::ip::tcp::socket>(io);
|
||||||
SOCKADDR_IN ServerAddr;
|
asio::error_code ec;
|
||||||
if (DSock < 1) {
|
DSock->connect(ep, ec);
|
||||||
|
if (ec) {
|
||||||
KillSocket(DSock);
|
KillSocket(DSock);
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
char Code[2] = { 'D', char(ClientID) };
|
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);
|
KillSocket(DSock);
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
return 0;
|
return 0;
|
||||||
@ -193,7 +185,7 @@ SOCKET InitDSock() {
|
|||||||
return DSock;
|
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;
|
uint64_t GRcv = 0, MSize = Size / 2, DSize = Size - MSize;
|
||||||
|
|
||||||
@ -239,7 +231,7 @@ void InvalidResource(const std::string& File) {
|
|||||||
Terminate = true;
|
Terminate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncResources(SOCKET Sock) {
|
void SyncResources(asio::ip::tcp::socket& Sock) {
|
||||||
std::string Ret = Auth(Sock);
|
std::string Ret = Auth(Sock);
|
||||||
if (Ret.empty())
|
if (Ret.empty())
|
||||||
return;
|
return;
|
||||||
@ -278,7 +270,7 @@ void SyncResources(SOCKET Sock) {
|
|||||||
}
|
}
|
||||||
if (!FNames.empty())
|
if (!FNames.empty())
|
||||||
info("Syncing...");
|
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) {
|
for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) {
|
||||||
auto pos = FN->find_last_of('/');
|
auto pos = FN->find_last_of('/');
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
@ -331,7 +323,7 @@ void SyncResources(SOCKET Sock) {
|
|||||||
|
|
||||||
std::string Name = std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName;
|
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)
|
if (Terminate)
|
||||||
break;
|
break;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
///
|
///
|
||||||
#include "Network/network.hpp"
|
#include "Network/network.hpp"
|
||||||
#include "Zlib/Compressor.h"
|
#include "Zlib/Compressor.h"
|
||||||
|
#include "asio/ip/address.hpp"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
@ -23,11 +25,10 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
SOCKET UDPSock = -1;
|
std::shared_ptr<asio::ip::udp::socket> UDPSock = nullptr;
|
||||||
sockaddr_in* ToServer = nullptr;
|
|
||||||
|
|
||||||
void UDPSend(const std::vector<char>& RawData) {
|
void UDPSend(const std::vector<char>& RawData) {
|
||||||
if (ClientID == -1 || UDPSock == -1)
|
if (ClientID == -1 || UDPSock == nullptr)
|
||||||
return;
|
return;
|
||||||
std::string Data;
|
std::string Data;
|
||||||
if (Data.size() > 400) {
|
if (Data.size() > 400) {
|
||||||
@ -37,7 +38,7 @@ void UDPSend(const std::vector<char>& RawData) {
|
|||||||
Data = std::string(RawData.data(), RawData.size());
|
Data = std::string(RawData.data(), RawData.size());
|
||||||
}
|
}
|
||||||
std::string Packet = char(ClientID + 1) + std::string(":") + Data;
|
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)
|
if (sendOk == SOCKET_ERROR)
|
||||||
error("Error Code : " + std::to_string(WSAGetLastError()));
|
error("Error Code : " + std::to_string(WSAGetLastError()));
|
||||||
}
|
}
|
||||||
@ -46,9 +47,17 @@ void SendLarge(const std::vector<char>& Data) {
|
|||||||
if (Data.size() > 400) {
|
if (Data.size() > 400) {
|
||||||
auto res = Comp(Data);
|
auto res = Comp(Data);
|
||||||
res.insert(res.begin(), { 'A', 'B', 'G', ':' });
|
res.insert(res.begin(), { 'A', 'B', 'G', ':' });
|
||||||
TCPSend(res, TCPSock);
|
if (!TCPSock) {
|
||||||
|
::debug("TCPSock is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TCPSend(res, *TCPSock);
|
||||||
} else {
|
} else {
|
||||||
TCPSend(Data, TCPSock);
|
if (!TCPSock) {
|
||||||
|
::debug("TCPSock is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TCPSend(Data, *TCPSock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,39 +72,28 @@ void UDPParser(std::string_view Packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void UDPRcv() {
|
void UDPRcv() {
|
||||||
sockaddr_in FromServer {};
|
|
||||||
#if defined(_WIN32)
|
|
||||||
int clientLength = sizeof(FromServer);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
socklen_t clientLength = sizeof(FromServer);
|
|
||||||
#endif
|
|
||||||
ZeroMemory(&FromServer, clientLength);
|
|
||||||
static thread_local std::array<char, 10240> Ret {};
|
static thread_local std::array<char, 10240> Ret {};
|
||||||
if (UDPSock == -1)
|
if (UDPSock == nullptr) {
|
||||||
|
::debug("UDPSock is null");
|
||||||
return;
|
return;
|
||||||
int32_t Rcv = recvfrom(UDPSock, Ret.data(), Ret.size() - 1, 0, (sockaddr*)&FromServer, &clientLength);
|
}
|
||||||
if (Rcv == SOCKET_ERROR)
|
asio::error_code ec;
|
||||||
|
int32_t Rcv = UDPSock->receive(asio::buffer(Ret.data(), Ret.size() - 1), 0, ec);
|
||||||
|
if (ec)
|
||||||
return;
|
return;
|
||||||
Ret[Rcv] = 0;
|
Ret[Rcv] = 0;
|
||||||
UDPParser(std::string_view(Ret.data(), Rcv));
|
UDPParser(std::string_view(Ret.data(), Rcv));
|
||||||
}
|
}
|
||||||
void UDPClientMain(const std::string& IP, int Port) {
|
void UDPClientMain(asio::ip::address addr, uint16_t port) {
|
||||||
#ifdef _WIN32
|
UDPSock = std::make_shared<asio::ip::udp::socket>(io);
|
||||||
WSADATA data;
|
asio::error_code ec;
|
||||||
if (WSAStartup(514, &data)) {
|
UDPSock->connect(asio::ip::udp::endpoint(addr, port), ec);
|
||||||
error("Can't start Winsock!");
|
if (ec) {
|
||||||
return;
|
::error(fmt::format("Failed to connect UDP to server: {}", ec.message()));
|
||||||
|
Terminate = true;
|
||||||
}
|
}
|
||||||
#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);
|
|
||||||
GameSend("P" + std::to_string(ClientID));
|
GameSend("P" + std::to_string(ClientID));
|
||||||
TCPSend(strtovec("H"), TCPSock);
|
TCPSend(strtovec("H"), *TCPSock);
|
||||||
UDPSend(strtovec("p"));
|
UDPSend(strtovec("p"));
|
||||||
while (!Terminate)
|
while (!Terminate)
|
||||||
UDPRcv();
|
UDPRcv();
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
///
|
///
|
||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
#include <Zlib/Compressor.h>
|
#include <Zlib/Compressor.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -27,7 +28,7 @@
|
|||||||
|
|
||||||
int LastPort;
|
int LastPort;
|
||||||
std::string LastIP;
|
std::string LastIP;
|
||||||
SOCKET TCPSock = -1;
|
std::shared_ptr<asio::ip::tcp::socket> TCPSock = nullptr;
|
||||||
|
|
||||||
bool CheckBytes(int32_t Bytes) {
|
bool CheckBytes(int32_t Bytes) {
|
||||||
if (Bytes == 0) {
|
if (Bytes == 0) {
|
||||||
@ -46,8 +47,8 @@ void UUl(const std::string& R) {
|
|||||||
UlStatus = "UlDisconnected: " + R;
|
UlStatus = "UlDisconnected: " + R;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPSend(const std::vector<char>& Data, uint64_t Sock) {
|
void TCPSend(const std::vector<char>& Data, asio::ip::tcp::socket& Sock) {
|
||||||
if (Sock == -1) {
|
if (!Sock.is_open()) {
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
UUl("Invalid Socket");
|
UUl("Invalid Socket");
|
||||||
return;
|
return;
|
||||||
@ -61,53 +62,33 @@ void TCPSend(const std::vector<char>& Data, uint64_t Sock) {
|
|||||||
// Do not use Size before this point for anything but the header
|
// Do not use Size before this point for anything but the header
|
||||||
Sent = 0;
|
Sent = 0;
|
||||||
Size += 4;
|
Size += 4;
|
||||||
do {
|
asio::error_code ec;
|
||||||
if (size_t(Sent) >= Send.size()) {
|
asio::write(Sock, asio::buffer(Send), ec);
|
||||||
error("string OOB in " + std::string(__func__));
|
if (ec) {
|
||||||
UUl("TCP Send OOB");
|
UUl(fmt::format("Failed to send data: {}", ec.message()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Temp = send(Sock, &Send[Sent], Size - Sent, 0);
|
|
||||||
if (!CheckBytes(Temp)) {
|
|
||||||
UUl("Socket Closed Code 2");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Sent += Temp;
|
|
||||||
} while (Sent < Size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TCPRcv(SOCKET Sock) {
|
std::string TCPRcv(asio::ip::tcp::socket& Sock) {
|
||||||
if (Sock == -1) {
|
if (!Sock.is_open()) {
|
||||||
Terminate = true;
|
Terminate = true;
|
||||||
UUl("Invalid Socket");
|
UUl("Invalid Socket");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
int32_t Header, BytesRcv = 0, Temp;
|
int32_t Header, BytesRcv = 0, Temp;
|
||||||
std::vector<char> Data(sizeof(Header));
|
std::vector<char> Data(sizeof(Header));
|
||||||
do {
|
asio::error_code ec;
|
||||||
Temp = recv(Sock, &Data[BytesRcv], 4 - BytesRcv, 0);
|
asio::read(Sock, asio::buffer(Data), ec);
|
||||||
if (!CheckBytes(Temp)) {
|
if (ec) {
|
||||||
UUl("Socket Closed Code 3");
|
UUl(fmt::format("Failed to receive header: {}", ec.message()));
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
BytesRcv += Temp;
|
|
||||||
} while (BytesRcv < 4);
|
|
||||||
memcpy(&Header, &Data[0], sizeof(Header));
|
memcpy(&Header, &Data[0], sizeof(Header));
|
||||||
|
|
||||||
if (!CheckBytes(BytesRcv)) {
|
|
||||||
UUl("Socket Closed Code 4");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
Data.resize(Header);
|
Data.resize(Header);
|
||||||
BytesRcv = 0;
|
asio::read(Sock, asio::buffer(Data), ec);
|
||||||
do {
|
if (ec) {
|
||||||
Temp = recv(Sock, &Data[BytesRcv], Header - BytesRcv, 0);
|
UUl(fmt::format("Failed to receive data: {}", ec.message()));
|
||||||
if (!CheckBytes(Temp)) {
|
|
||||||
UUl("Socket Closed Code 5");
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
BytesRcv += Temp;
|
|
||||||
} while (BytesRcv < Header);
|
|
||||||
|
|
||||||
std::string Ret(Data.data(), Header);
|
std::string Ret(Data.data(), Header);
|
||||||
|
|
||||||
@ -125,50 +106,24 @@ std::string TCPRcv(SOCKET Sock) {
|
|||||||
return Ret;
|
return Ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPClientMain(const std::string& IP, int Port) {
|
void TCPClientMain(asio::ip::tcp::socket&& socket) {
|
||||||
LastIP = IP;
|
if (!TCPSock) {
|
||||||
LastPort = Port;
|
return;
|
||||||
|
}
|
||||||
|
LastIP = socket.remote_endpoint().address().to_string();
|
||||||
|
LastPort = socket.remote_endpoint().port();
|
||||||
SOCKADDR_IN ServerAddr;
|
SOCKADDR_IN ServerAddr;
|
||||||
int RetCode;
|
int RetCode;
|
||||||
#ifdef _WIN32
|
TCPSock = std::make_shared<asio::ip::tcp::socket>(std::move(socket));
|
||||||
WSADATA wsaData;
|
|
||||||
WSAStartup(514, &wsaData); // 2.2
|
|
||||||
#endif
|
|
||||||
TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
|
|
||||||
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!");
|
info("Connected!");
|
||||||
|
|
||||||
char Code = 'C';
|
char Code = 'C';
|
||||||
send(TCPSock, &Code, 1, 0);
|
asio::write(*TCPSock, asio::buffer(&Code, 1));
|
||||||
SyncResources(TCPSock);
|
SyncResources(*TCPSock);
|
||||||
while (!Terminate) {
|
while (!Terminate && TCPSock) {
|
||||||
ServerParser(TCPRcv(TCPSock));
|
ServerParser(TCPRcv(*TCPSock));
|
||||||
}
|
}
|
||||||
GameSend("T");
|
GameSend("T");
|
||||||
////Game Send Terminate
|
KillSocket(TCPSock);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
@ -4,32 +4,32 @@
|
|||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#if defined(__linux__)
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#else
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static uint32_t RecvHeader(SOCKET socket) {
|
using asio::ip::tcp;
|
||||||
|
|
||||||
|
static uint32_t RecvHeader(tcp::socket& socket) {
|
||||||
std::array<uint8_t, sizeof(uint32_t)> header_buffer {};
|
std::array<uint8_t, sizeof(uint32_t)> header_buffer {};
|
||||||
auto n = recv(socket, reinterpret_cast<char*>(header_buffer.data()), header_buffer.size(), MSG_WAITALL);
|
asio::error_code ec;
|
||||||
if (n < 0) {
|
auto n = asio::read(socket, asio::buffer(header_buffer), ec);
|
||||||
throw std::runtime_error(std::string("recv() of header failed: ") + std::strerror(errno));
|
if (ec) {
|
||||||
} else if (n == 0) {
|
throw std::runtime_error(std::string("recv() of header failed: ") + ec.message());
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
throw std::runtime_error("Game disconnected");
|
throw std::runtime_error("Game disconnected");
|
||||||
}
|
}
|
||||||
return *reinterpret_cast<uint32_t*>(header_buffer.data());
|
return *reinterpret_cast<uint32_t*>(header_buffer.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Throws!!!
|
/// Throws!!!
|
||||||
void ReceiveFromGame(SOCKET socket, std::vector<char>& out_data) {
|
void ReceiveFromGame(tcp::socket& socket, std::vector<char>& out_data) {
|
||||||
auto header = RecvHeader(socket);
|
auto header = RecvHeader(socket);
|
||||||
out_data.resize(header);
|
out_data.resize(header);
|
||||||
auto n = recv(socket, reinterpret_cast<char*>(out_data.data()), out_data.size(), MSG_WAITALL);
|
asio::error_code ec;
|
||||||
if (n < 0) {
|
auto n = asio::read(socket, asio::buffer(out_data), ec);
|
||||||
throw std::runtime_error(std::string("recv() of data failed: ") + std::strerror(errno));
|
if (ec) {
|
||||||
} else if (n == 0) {
|
throw std::runtime_error(std::string("recv() of data failed: ") + ec.message());
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
throw std::runtime_error("Game disconnected");
|
throw std::runtime_error("Game disconnected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
"cpp-httplib",
|
"cpp-httplib",
|
||||||
"nlohmann-json",
|
"nlohmann-json",
|
||||||
"zlib",
|
"zlib",
|
||||||
"openssl"
|
"openssl",
|
||||||
|
"asio",
|
||||||
|
"fmt"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user