mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-08-17 16:57:05 +00:00
add socketio, http post & get
This commit is contained in:
parent
4cda6e8bc3
commit
ef5db013b3
17
.gitmodules
vendored
17
.gitmodules
vendored
@ -1,6 +1,15 @@
|
|||||||
[submodule "commandline"]
|
|
||||||
path = commandline
|
|
||||||
url = https://github.com/lionkor/commandline
|
|
||||||
[submodule "include/commandline"]
|
[submodule "include/commandline"]
|
||||||
path = include/commandline
|
path = include/commandline
|
||||||
url = https://github.com/lionkor/commandline
|
url = https://github.com/lionkor/commandline
|
||||||
|
[submodule "socket.io-client-cpp"]
|
||||||
|
path = socket.io-client-cpp
|
||||||
|
url = https://github.com/socketio/socket.io-client-cpp
|
||||||
|
[submodule "asio"]
|
||||||
|
path = asio
|
||||||
|
url = https://github.com/chriskohlhoff/asio
|
||||||
|
[submodule "rapidjson"]
|
||||||
|
path = rapidjson
|
||||||
|
url = https://github.com/Tencent/rapidjson
|
||||||
|
[submodule "websocketpp"]
|
||||||
|
path = websocketpp
|
||||||
|
url = https://github.com/zaphoyd/websocketpp
|
||||||
|
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@ -2,5 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/include/commandline" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -2,14 +2,24 @@ cmake_minimum_required(VERSION 3.13)
|
|||||||
project(Server)
|
project(Server)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
|
||||||
|
|
||||||
|
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
||||||
|
include_directories("asio/asio/include")
|
||||||
|
include_directories("rapidjson/include")
|
||||||
|
include_directories("websocketpp")
|
||||||
|
add_subdirectory("socket.io-client-cpp")
|
||||||
add_subdirectory("include/commandline")
|
add_subdirectory("include/commandline")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -g")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -g")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
|
||||||
|
if (SANITIZE)
|
||||||
|
message(STATUS "sanitize is ON")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread")
|
||||||
|
endif (SANITIZE)
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
|
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
|
||||||
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
|
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
|
||||||
@ -20,22 +30,25 @@ endif ()
|
|||||||
find_package(Boost REQUIRED COMPONENTS system thread)
|
find_package(Boost REQUIRED COMPONENTS system thread)
|
||||||
|
|
||||||
add_executable(BeamMP-Server
|
add_executable(BeamMP-Server
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/TConsole.cpp
|
include/TConsole.h src/TConsole.cpp
|
||||||
src/TServer.cpp
|
include/TServer.h src/TServer.cpp
|
||||||
src/Compat.cpp
|
include/Compat.h src/Compat.cpp
|
||||||
src/Common.cpp
|
include/Common.h src/Common.cpp
|
||||||
src/Client.cpp
|
include/Client.h src/Client.cpp
|
||||||
src/VehicleData.cpp
|
include/VehicleData.h src/VehicleData.cpp
|
||||||
src/TConfig.cpp
|
include/TConfig.h src/TConfig.cpp
|
||||||
src/TLuaEngine.cpp
|
include/TLuaEngine.h src/TLuaEngine.cpp
|
||||||
src/TLuaFile.cpp
|
include/TLuaFile.h src/TLuaFile.cpp
|
||||||
)
|
include/TResourceManager.h src/TResourceManager.cpp
|
||||||
|
include/THeartbeatThread.h src/THeartbeatThread.cpp
|
||||||
|
include/Http.h src/Http.cpp
|
||||||
|
include/SocketIO.h src/SocketIO.cpp)
|
||||||
|
|
||||||
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
||||||
|
|
||||||
find_package(Lua REQUIRED)
|
find_package(Lua REQUIRED)
|
||||||
target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR})
|
target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src")
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
|
9
include/Http.h
Normal file
9
include/Http.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Http {
|
||||||
|
std::string GET(const std::string& host, int port, const std::string& target);
|
||||||
|
std::string POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json);
|
||||||
|
}
|
69
include/SocketIO.h
Normal file
69
include/SocketIO.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <deque>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sio_client.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We send relevant server events over socket.io to the backend.
|
||||||
|
*
|
||||||
|
* We send all events to `backend.beammp.com`, to the room `/key`
|
||||||
|
* where `key` is the currently active auth-key.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum class SocketIOEvent {
|
||||||
|
ConsoleOut,
|
||||||
|
CPUUsage,
|
||||||
|
MemoryUsage,
|
||||||
|
NetworkUsage,
|
||||||
|
PlayerList,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SocketIORoom {
|
||||||
|
None,
|
||||||
|
Stats,
|
||||||
|
Player,
|
||||||
|
Info,
|
||||||
|
Console,
|
||||||
|
};
|
||||||
|
|
||||||
|
class SocketIO final {
|
||||||
|
private:
|
||||||
|
struct Event;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class EventType {
|
||||||
|
};
|
||||||
|
|
||||||
|
// Singleton pattern
|
||||||
|
static SocketIO& Get();
|
||||||
|
|
||||||
|
void Emit(SocketIORoom Room, SocketIOEvent Event, const std::string& Data);
|
||||||
|
|
||||||
|
~SocketIO();
|
||||||
|
|
||||||
|
void SetAuthenticated(bool auth) { _Authenticated = auth; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SocketIO();
|
||||||
|
|
||||||
|
void ThreadMain();
|
||||||
|
|
||||||
|
struct Event {
|
||||||
|
std::string Room;
|
||||||
|
std::string Name;
|
||||||
|
std::string Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool _Authenticated { false };
|
||||||
|
sio::client _Client;
|
||||||
|
std::thread _Thread;
|
||||||
|
std::atomic_bool _CloseThread { false };
|
||||||
|
std::mutex _QueueMutex;
|
||||||
|
std::deque<Event> _Queue;
|
||||||
|
|
||||||
|
friend std::unique_ptr<SocketIO> std::make_unique<SocketIO>();
|
||||||
|
};
|
||||||
|
|
10
include/THeartbeatThread.h
Normal file
10
include/THeartbeatThread.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IThreaded.h"
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
class THeartbeatThread : public IThreaded {
|
||||||
|
public:
|
||||||
|
THeartbeatThread();
|
||||||
|
void operator()() override;
|
||||||
|
};
|
19
include/TResourceManager.h
Normal file
19
include/TResourceManager.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
class TResourceManager {
|
||||||
|
public:
|
||||||
|
TResourceManager();
|
||||||
|
|
||||||
|
[[nodiscard]] uint64_t MaxModSize() const { return mMaxModSize; }
|
||||||
|
[[nodiscard]] std::string FileList() const { return mFileList; }
|
||||||
|
[[nodiscard]] std::string FileSizes() const { return mFileSizes; }
|
||||||
|
[[nodiscard]] int ModsLoaded() const { return mModsLoaded; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t mMaxModSize = 0;
|
||||||
|
std::string mFileSizes;
|
||||||
|
std::string mFileList;
|
||||||
|
int mModsLoaded = 0;
|
||||||
|
};
|
143
src/Http.cpp
Normal file
143
src/Http.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#include "Http.h"
|
||||||
|
|
||||||
|
#include "CustomAssert.h"
|
||||||
|
#include <boost/asio/connect.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
|
|
||||||
|
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||||
|
namespace http = beast::http; // from <boost/beast/http.hpp>
|
||||||
|
namespace net = boost::asio; // from <boost/asio.hpp>
|
||||||
|
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
||||||
|
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
||||||
|
|
||||||
|
std::string Http::GET(const std::string& host, int port, const std::string& target) {
|
||||||
|
// FIXME: doesn't support https
|
||||||
|
// if it causes issues, yell at me and I'll fix it asap. - Lion
|
||||||
|
try {
|
||||||
|
net::io_context io;
|
||||||
|
tcp::resolver resolver(io);
|
||||||
|
beast::tcp_stream stream(io);
|
||||||
|
auto const results = resolver.resolve(host, std::to_string(port));
|
||||||
|
stream.connect(results);
|
||||||
|
|
||||||
|
http::request<http::string_body> req { http::verb::get, target, 11 /* http 1.1 */ };
|
||||||
|
|
||||||
|
req.set(http::field::host, host);
|
||||||
|
// tell the server what we are (boost beast)
|
||||||
|
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||||
|
|
||||||
|
http::write(stream, req);
|
||||||
|
|
||||||
|
// used for reading
|
||||||
|
beast::flat_buffer buffer;
|
||||||
|
http::response<http::string_body> response;
|
||||||
|
|
||||||
|
http::read(stream, buffer, response);
|
||||||
|
|
||||||
|
std::string result(response.body());
|
||||||
|
|
||||||
|
beast::error_code ec;
|
||||||
|
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
|
||||||
|
if (ec && ec != beast::errc::not_connected) {
|
||||||
|
throw beast::system_error { ec }; // goes down to `return "-1"` anyways
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error(e.what());
|
||||||
|
return "-1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Http::POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json) {
|
||||||
|
try {
|
||||||
|
net::io_context io;
|
||||||
|
|
||||||
|
// The SSL context is required, and holds certificates
|
||||||
|
ssl::context ctx(ssl::context::tlsv13);
|
||||||
|
|
||||||
|
ctx.set_verify_mode(ssl::verify_none);
|
||||||
|
|
||||||
|
tcp::resolver resolver(io);
|
||||||
|
beast::ssl_stream<beast::tcp_stream> stream(io, ctx);
|
||||||
|
decltype(resolver)::results_type results;
|
||||||
|
auto try_connect_with_protocol = [&](tcp protocol) {
|
||||||
|
try {
|
||||||
|
results = resolver.resolve(protocol, host, std::to_string(443));
|
||||||
|
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
||||||
|
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
|
||||||
|
// FIXME: we could throw and crash, if we like
|
||||||
|
// throw boost::system::system_error { ec };
|
||||||
|
debug("POST " + host + target + " failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
beast::get_lowest_layer(stream).connect(results);
|
||||||
|
} catch (const boost::system::system_error&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
//bool ok = try_connect_with_protocol(tcp::v6());
|
||||||
|
//if (!ok) {
|
||||||
|
//debug("IPv6 connect failed, trying IPv4");
|
||||||
|
bool ok = try_connect_with_protocol(tcp::v4());
|
||||||
|
if (!ok) {
|
||||||
|
error("failed to resolve or connect in POST " + host + target);
|
||||||
|
return "-1";
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
stream.handshake(ssl::stream_base::client);
|
||||||
|
http::request<http::string_body> req { http::verb::post, target, 11 /* http 1.1 */ };
|
||||||
|
|
||||||
|
req.set(http::field::host, host);
|
||||||
|
if (!body.empty()) {
|
||||||
|
if (json) {
|
||||||
|
// FIXME: json is untested.
|
||||||
|
req.set(http::field::content_type, "application/json");
|
||||||
|
} else {
|
||||||
|
req.set(http::field::content_type, "application/x-www-form-urlencoded");
|
||||||
|
}
|
||||||
|
req.set(http::field::content_length, std::to_string(body.size()));
|
||||||
|
req.body() = body;
|
||||||
|
// info("body is " + body + " (" + req.body() + ")");
|
||||||
|
// info("content size is " + std::to_string(body.size()) + " (" + boost::lexical_cast<std::string>(body.size()) + ")");
|
||||||
|
}
|
||||||
|
for (const auto& pair : fields) {
|
||||||
|
// info("setting " + pair.first + " to " + pair.second);
|
||||||
|
req.set(pair.first, pair.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream oss;
|
||||||
|
oss << req;
|
||||||
|
|
||||||
|
beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(5));
|
||||||
|
|
||||||
|
http::write(stream, req);
|
||||||
|
|
||||||
|
// used for reading
|
||||||
|
beast::flat_buffer buffer;
|
||||||
|
http::response<http::string_body> response;
|
||||||
|
|
||||||
|
http::read(stream, buffer, response);
|
||||||
|
|
||||||
|
std::stringstream result;
|
||||||
|
result << response;
|
||||||
|
|
||||||
|
beast::error_code ec;
|
||||||
|
stream.shutdown(ec);
|
||||||
|
// IGNORING ec
|
||||||
|
|
||||||
|
// info(result.str());
|
||||||
|
std::string debug_response_str;
|
||||||
|
std::getline(result, debug_response_str);
|
||||||
|
debug("POST " + host + target + ": " + debug_response_str);
|
||||||
|
return std::string(response.body());
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error(e.what());
|
||||||
|
return "-1";
|
||||||
|
}
|
||||||
|
}
|
109
src/SocketIO.cpp
Normal file
109
src/SocketIO.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include "SocketIO.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
|
||||||
|
static std::unique_ptr<SocketIO> SocketIOInstance = std::make_unique<SocketIO>();
|
||||||
|
|
||||||
|
SocketIO& SocketIO::Get() {
|
||||||
|
return *SocketIOInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketIO::SocketIO()
|
||||||
|
: _Thread([this] { ThreadMain(); }) {
|
||||||
|
_Client.socket("/")->on("Hello", [&](sio::event&) {
|
||||||
|
DebugPrintTIDInternal("Hello-handler");
|
||||||
|
info("Got 'Hello' from backend socket-io!");
|
||||||
|
});
|
||||||
|
_Client.connect("https://backend.beammp.com");
|
||||||
|
_Client.set_logs_quiet();
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketIO::~SocketIO() {
|
||||||
|
_CloseThread.store(true);
|
||||||
|
_Thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto RoomNameFromEnum(SocketIORoom Room) {
|
||||||
|
switch (Room) {
|
||||||
|
case SocketIORoom::None:
|
||||||
|
return "";
|
||||||
|
case SocketIORoom::Console:
|
||||||
|
return "console";
|
||||||
|
case SocketIORoom::Info:
|
||||||
|
return "info";
|
||||||
|
case SocketIORoom::Player:
|
||||||
|
return "player";
|
||||||
|
case SocketIORoom::Stats:
|
||||||
|
return "stats";
|
||||||
|
default:
|
||||||
|
error("unreachable code reached (developer error)");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto EventNameFromEnum(SocketIOEvent Event) {
|
||||||
|
switch (Event) {
|
||||||
|
case SocketIOEvent::CPUUsage:
|
||||||
|
return "cpu usage";
|
||||||
|
case SocketIOEvent::MemoryUsage:
|
||||||
|
return "memory usage";
|
||||||
|
case SocketIOEvent::ConsoleOut:
|
||||||
|
return "console out";
|
||||||
|
case SocketIOEvent::NetworkUsage:
|
||||||
|
return "network usage";
|
||||||
|
case SocketIOEvent::PlayerList:
|
||||||
|
return "player list";
|
||||||
|
default:
|
||||||
|
error("unreachable code reached (developer error)");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketIO::Emit(SocketIORoom Room, SocketIOEvent Event, const std::string& Data) {
|
||||||
|
if (!_Authenticated) {
|
||||||
|
debug("trying to emit a socket.io event when not yet authenticated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string RoomName = RoomNameFromEnum(Room);
|
||||||
|
std::string EventName = EventNameFromEnum(Event);
|
||||||
|
debug("emitting event \"" + EventName + "\" with data: \"" + Data + "\" in room \"/key/" + RoomName + "\"");
|
||||||
|
std::unique_lock Lock(_QueueMutex);
|
||||||
|
_Queue.push_back({ RoomName, EventName, Data });
|
||||||
|
debug("queue now has " + std::to_string(_Queue.size()) + " events");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketIO::ThreadMain() {
|
||||||
|
bool FirstTime = true;
|
||||||
|
while (!_CloseThread.load()) {
|
||||||
|
if (_Authenticated && FirstTime) {
|
||||||
|
FirstTime = false;
|
||||||
|
DebugPrintTID();
|
||||||
|
}
|
||||||
|
bool empty = false;
|
||||||
|
{ // queue lock scope
|
||||||
|
std::unique_lock Lock(_QueueMutex);
|
||||||
|
empty = _Queue.empty();
|
||||||
|
} // end queue lock scope
|
||||||
|
if (empty) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Event TheEvent;
|
||||||
|
{ // queue lock scope
|
||||||
|
std::unique_lock Lock(_QueueMutex);
|
||||||
|
TheEvent = _Queue.front();
|
||||||
|
_Queue.pop_front();
|
||||||
|
} // end queue lock scope
|
||||||
|
debug("sending \"" + TheEvent.Name + "\" event");
|
||||||
|
auto Room = "/" + TheEvent.Room;
|
||||||
|
_Client.socket("/")->emit(TheEvent.Name, TheEvent.Data);
|
||||||
|
debug("sent \"" + TheEvent.Name + "\" event");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "closing " + std::string(__func__) << std::endl;
|
||||||
|
|
||||||
|
_Client.sync_close();
|
||||||
|
_Client.clear_con_listeners();
|
||||||
|
std::cout << "closed" << std::endl;
|
||||||
|
|
||||||
|
}
|
57
src/THeartbeatThread.cpp
Normal file
57
src/THeartbeatThread.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#include "THeartbeatThread.h"
|
||||||
|
|
||||||
|
#include "Http.h"
|
||||||
|
#include "SocketIO.h"
|
||||||
|
|
||||||
|
THeartbeatThread::THeartbeatThread() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void THeartbeatThread::operator()() {
|
||||||
|
std::string Body;
|
||||||
|
std::string T;
|
||||||
|
|
||||||
|
// these are "hot-change" related variables
|
||||||
|
static std::string Last = "";
|
||||||
|
|
||||||
|
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
||||||
|
bool isAuth = false;
|
||||||
|
while (true) {
|
||||||
|
Body = GenerateCall();
|
||||||
|
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
||||||
|
auto Now = std::chrono::high_resolution_clock::now();
|
||||||
|
if (Last == Body && (Now - LastNormalUpdateTime) < std::chrono::seconds(30)) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Last = Body;
|
||||||
|
LastNormalUpdateTime = Now;
|
||||||
|
if (!Application::Settings.CustomIP.empty())
|
||||||
|
Body += "&ip=" + Application::Settings.CustomIP;
|
||||||
|
|
||||||
|
T = Http::POST("backend.beammp.com", "/heartbeat", {}, Body, false);
|
||||||
|
|
||||||
|
if (T.substr(0, 2) != "20") {
|
||||||
|
//Backend system refused server startup!
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
|
T = Http::POST("backend.beammp.com", "/heartbeat", {}, Body, false);
|
||||||
|
// TODO backup2 + HTTP flag (no TSL)
|
||||||
|
if (T.substr(0, 2) != "20") {
|
||||||
|
warn("Backend system refused server! Server might not show in the public list");
|
||||||
|
debug("server returned \"" + T + "\"");
|
||||||
|
isAuth = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAuth) {
|
||||||
|
if (T == "2000") {
|
||||||
|
info(("Authenticated!"));
|
||||||
|
isAuth = true;
|
||||||
|
} else if (T == "200") {
|
||||||
|
info(("Resumed authenticated session!"));
|
||||||
|
isAuth = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketIO::Get().SetAuthenticated(isAuth);
|
||||||
|
}
|
||||||
|
}
|
26
src/TResourceManager.cpp
Normal file
26
src/TResourceManager.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "TResourceManager.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
TResourceManager::TResourceManager() {
|
||||||
|
std::string Path = Application::Settings.Resource + "/Client";
|
||||||
|
if (!fs::exists(Path))
|
||||||
|
fs::create_directory(Path);
|
||||||
|
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||||
|
auto pos = entry.path().string().find(".zip");
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
if (entry.path().string().length() - pos == 4) {
|
||||||
|
mFileList += entry.path().string() + ";";
|
||||||
|
mFileSizes += std::to_string(uint64_t(fs::file_size(entry.path()))) + ";";
|
||||||
|
mMaxModSize += uint64_t(fs::file_size(entry.path()));
|
||||||
|
mModsLoaded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::replace(mFileList.begin(), mFileList.end(), '\\', '/');
|
||||||
|
if (mModsLoaded)
|
||||||
|
info("Loaded " + std::to_string(mModsLoaded) + " Mods");
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
#include "TConfig.h"
|
#include "TConfig.h"
|
||||||
#include "TConsole.h"
|
#include "TConsole.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
|
#include "TResourceManager.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -44,6 +45,8 @@ int main(int argc, char** argv) {
|
|||||||
TServer Server(argc, argv);
|
TServer Server(argc, argv);
|
||||||
TConfig Config("Server.cfg");
|
TConfig Config("Server.cfg");
|
||||||
TLuaEngine LuaEngine(Server);
|
TLuaEngine LuaEngine(Server);
|
||||||
|
TResourceManager ResourceManager;
|
||||||
|
THeartbeatThread Heartbeat;
|
||||||
|
|
||||||
// TODO: replace
|
// TODO: replace
|
||||||
bool Shutdown = false;
|
bool Shutdown = false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user