Merge pull request #82 from BeamMP/rc-v3.0.1

Release Candidate v3.0.1
This commit is contained in:
Lion
2022-02-04 04:37:09 +01:00
committed by GitHub
14 changed files with 128 additions and 80 deletions
+2
View File
@@ -8,6 +8,8 @@ project(BeamMP-Server
HOMEPAGE_URL https://beammp.com HOMEPAGE_URL https://beammp.com
LANGUAGES CXX C) LANGUAGES CXX C)
set(HTTPLIB_REQUIRE_OPENSSL ON)
include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include") include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include")
include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include") include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include")
include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp") include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp")
+6
View File
@@ -1,3 +1,9 @@
# v3.0.1
- ADDED Backup URLs to UpdateCheck (will fail less often now)
- FIXED a bug where, when run with --working-directory, the Server.log would still be in the original directory
- FIXED a bug which could cause the plugin reload thread to spin at 100% if the reloaded plugin's didn't terminate
# v3.0.0 # v3.0.0
- CHANGED entire plugin Lua implementation (rewrite) - CHANGED entire plugin Lua implementation (rewrite)
+9 -4
View File
@@ -74,10 +74,15 @@ public:
static TSettings Settings; static TSettings Settings;
static std::vector<std::string> GetBackendUrlsInOrder() {
return {
"backend.beammp.com",
"backup1.beammp.com",
"backup2.beammp.com"
};
}
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; } static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
static std::string GetBackendHostname() { return "backend.beammp.com"; }
static std::string GetBackup1Hostname() { return "backup1.beammp.com"; }
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; }
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; } static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
static void CheckForUpdates(); static void CheckForUpdates();
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str); static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
@@ -114,7 +119,7 @@ private:
static inline std::mutex mShutdownHandlersMutex {}; static inline std::mutex mShutdownHandlersMutex {};
static inline std::deque<TShutdownHandler> mShutdownHandlers {}; static inline std::deque<TShutdownHandler> mShutdownHandlers {};
static inline Version mVersion { 3, 0, 0 }; static inline Version mVersion { 3, 0, 1 };
}; };
std::string ThreadName(bool DebugModeOverride = false); std::string ThreadName(bool DebugModeOverride = false);
+1 -1
View File
@@ -25,7 +25,7 @@ constexpr size_t RSA_DEFAULT_KEYLENGTH { 2048 };
namespace Http { namespace Http {
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr); std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr); std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {});
namespace Status { namespace Status {
std::string ToString(int code); std::string ToString(int code);
} }
+1
View File
@@ -149,6 +149,7 @@ public:
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId); void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
sol::state_view GetStateForPlugin(const fs::path& PluginPath); sol::state_view GetStateForPlugin(const fs::path& PluginPath);
TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath); TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath);
void AddResultToCheck(const std::shared_ptr<TLuaResult>& Result);
static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND";
+8 -2
View File
@@ -99,7 +99,8 @@ void Application::CheckForUpdates() {
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting); Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
// checks current version against latest version // checks current version against latest version
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s"); for (const auto& url : GetBackendUrlsInOrder()) {
auto Response = Http::GET(GetBackendUrlsInOrder().at(0), 443, "/v/s");
bool Matches = std::regex_match(Response, VersionRegex); bool Matches = std::regex_match(Response, VersionRegex);
if (Matches) { if (Matches) {
auto MyVersion = ServerVersion(); auto MyVersion = ServerVersion();
@@ -111,8 +112,9 @@ void Application::CheckForUpdates() {
beammp_info("Server up-to-date!"); beammp_info("Server up-to-date!");
} }
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good); Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good);
break;
} else { } else {
beammp_warn("Unable to fetch version from backend."); beammp_debug("Failed to fetch version from: " + url);
beammp_trace("got " + Response); beammp_trace("got " + Response);
auto Lock = Sentry.CreateExclusiveContext(); auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("get-response", { { "response", Response } }); Sentry.SetContext("get-response", { { "response", Response } });
@@ -120,6 +122,10 @@ void Application::CheckForUpdates() {
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad); Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad);
} }
} }
if (Application::GetSubsystemStatuses().at("UpdateCheck") == Application::Status::Bad) {
beammp_warn("Unable to fetch version info from backend.");
}
}
// thread name stuff // thread name stuff
+6 -2
View File
@@ -4,6 +4,7 @@
#include "Common.h" #include "Common.h"
#include "CustomAssert.h" #include "CustomAssert.h"
#include "LuaAPI.h" #include "LuaAPI.h"
#include "httplib.h"
#include <map> #include <map>
#include <random> #include <random>
@@ -33,17 +34,20 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ
} }
} }
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status) { std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status, const httplib::Headers& headers) {
httplib::SSLClient client(host, port); httplib::SSLClient client(host, port);
client.set_read_timeout(std::chrono::seconds(10));
beammp_assert(client.is_valid());
client.enable_server_certificate_verification(false); client.enable_server_certificate_verification(false);
client.set_address_family(AF_INET); client.set_address_family(AF_INET);
auto res = client.Post(target.c_str(), body.c_str(), body.size(), ContentType.c_str()); auto res = client.Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str());
if (res) { if (res) {
if (status) { if (status) {
*status = res->status; *status = res->status;
} }
return res->body; return res->body;
} else { } else {
beammp_debug("POST failed: " + httplib::to_string(res.error()));
return Http::ErrorString; return Http::ErrorString;
} }
} }
+1 -1
View File
@@ -386,7 +386,7 @@ TConsole::TConsole() {
} else { } else {
auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared<std::string>(cmd), "", "" }); auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared<std::string>(cmd), "", "" });
while (!Future->Ready) { while (!Future->Ready) {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // TODO: Add a timeout std::this_thread::yield(); // TODO: Add a timeout
} }
if (Future->Error) { if (Future->Error) {
beammp_lua_error(Future->ErrorMessage); beammp_lua_error(Future->ErrorMessage);
+54 -24
View File
@@ -54,41 +54,71 @@ void THeartbeatThread::operator()() {
auto Target = "/heartbeat"; auto Target = "/heartbeat";
unsigned int ResponseCode = 0; unsigned int ResponseCode = 0;
T = Http::POST(Application::GetBackendHostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { json::Document Doc;
beammp_trace("got " + T + " from backend"); bool Ok = false;
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); for (const auto& Url : Application::GetBackendUrlsInOrder()) {
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } });
std::this_thread::sleep_for(std::chrono::milliseconds(500)); beammp_trace(T);
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); Doc.Parse(T.data(), T.size());
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { if (Doc.HasParseError() || !Doc.IsObject()) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); beammp_error("Backend response failed to parse as valid json");
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); beammp_debug("Response was: `" + T + "`");
std::this_thread::sleep_for(std::chrono::milliseconds(500)); Sentry.SetContext("JSON Response", { { "reponse", T } });
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); SentryReportError(Url + Target, ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { } else if (ResponseCode != 200) {
beammp_warn("Backend system refused server! Server will not show in the public server list."); SentryReportError(Url + Target, ResponseCode);
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
isAuth = false;
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
} else { } else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); // all ok
Ok = true;
break;
} }
} else { std::this_thread::sleep_for(std::chrono::milliseconds(500));
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
} }
std::string Status {};
std::string Code {};
std::string Message {};
const auto StatusKey = "status";
const auto CodeKey = "code";
const auto MessageKey = "msg";
if (Ok) {
if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) {
Status = Doc[StatusKey].GetString();
} else { } else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); Sentry.SetContext("JSON Response", { { StatusKey, "invalid string / missing" } });
Ok = false;
}
if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) {
Code = Doc[CodeKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { CodeKey, "invalid string / missing" } });
Ok = false;
}
if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) {
Message = Doc[MessageKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { MessageKey, "invalid string / missing" } });
Ok = false;
}
if (!Ok) {
beammp_error("Missing/invalid json members in backend response");
Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__));
}
} }
if (!isAuth) { if (Ok && !isAuth) {
if (T == "2000") { if (Status == "2000") {
beammp_info(("Authenticated!")); beammp_info(("Authenticated!"));
isAuth = true; isAuth = true;
} else if (T == "200") { } else if (Status == "200") {
beammp_info(("Resumed authenticated session!")); beammp_info(("Resumed authenticated session!"));
isAuth = true; isAuth = true;
} else {
if (Message.empty()) {
Message = "Backend didn't provide a reason";
}
beammp_error("Backend REFUSED the auth key. " + Message);
} }
} }
} }
+11 -17
View File
@@ -54,25 +54,17 @@ void TLuaEngine::operator()() {
auto ResultCheckThread = std::thread([&] { auto ResultCheckThread = std::thread([&] {
RegisterThread("ResultCheckThread"); RegisterThread("ResultCheckThread");
while (!mShutdown) { while (!mShutdown) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::unique_lock Lock(mResultsToCheckMutex); std::unique_lock Lock(mResultsToCheckMutex);
if (!mResultsToCheck.empty()) { if (!mResultsToCheck.empty()) {
auto Res = mResultsToCheck.front(); auto Res = mResultsToCheck.front();
mResultsToCheck.pop(); mResultsToCheck.pop();
Lock.unlock(); Lock.unlock();
size_t Waited = 0; if (!Res->Ready) {
while (!Res->Ready) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
Waited++;
if (Waited > 250) {
// FIXME: This should *eventually* timeout.
// beammp_lua_error(Res->Function + " in " + Res->StateId + " took >1s to respond, not printing possible errors");
Lock.lock(); Lock.lock();
mResultsToCheck.push(Res); mResultsToCheck.push(Res);
Lock.unlock(); Lock.unlock();
break;
}
} }
if (Res->Error) { if (Res->Error) {
if (Res->ErrorMessage != BeamMPFnNotFoundError) { if (Res->ErrorMessage != BeamMPFnNotFoundError) {
@@ -80,13 +72,14 @@ void TLuaEngine::operator()() {
} }
} }
} }
std::this_thread::yield();
} }
}); });
// event loop // event loop
auto Before = std::chrono::high_resolution_clock::now(); auto Before = std::chrono::high_resolution_clock::now();
while (!mShutdown) { while (!mShutdown) {
if (mLuaStates.size() == 0) { if (mLuaStates.size() == 0) {
std::this_thread::sleep_for(std::chrono::seconds(500)); std::this_thread::sleep_for(std::chrono::seconds(100));
} }
{ // Timed Events Scope { // Timed Events Scope
std::unique_lock Lock(mTimedEventsMutex); std::unique_lock Lock(mTimedEventsMutex);
@@ -149,6 +142,11 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) {
return ""; return "";
} }
void TLuaEngine::AddResultToCheck(const std::shared_ptr<TLuaResult>& Result) {
std::unique_lock Lock(mResultsToCheckMutex);
mResultsToCheck.push(Result);
}
void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, const std::optional<std::chrono::high_resolution_clock::duration>& Max) { void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, const std::optional<std::chrono::high_resolution_clock::duration>& Max) {
for (const auto& Result : Results) { for (const auto& Result : Results) {
bool Cancelled = false; bool Cancelled = false;
@@ -705,6 +703,7 @@ void TLuaEngine::StateThreadData::AddPath(const fs::path& Path) {
void TLuaResult::WaitUntilReady() { void TLuaResult::WaitUntilReady() {
while (!Ready) { while (!Ready) {
std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
} }
@@ -762,12 +761,7 @@ void TPluginMonitor::operator()() {
auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path()); auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path());
auto Res = mEngine.EnqueueScript(StateID, Chunk); auto Res = mEngine.EnqueueScript(StateID, Chunk);
// TODO: call onInit // TODO: call onInit
while (!Res->Ready) { mEngine.AddResultToCheck(Res);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (Res->Error) {
beammp_lua_error(Res->ErrorMessage);
}
} else { } else {
// TODO: trigger onFileChanged event // TODO: trigger onFileChanged event
beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet"); beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet");
+2 -2
View File
@@ -21,8 +21,8 @@ TPPSMonitor::TPPSMonitor(TServer& Server)
void TPPSMonitor::operator()() { void TPPSMonitor::operator()() {
RegisterThread("PPSMonitor"); RegisterThread("PPSMonitor");
while (!mNetwork) { while (!mNetwork) {
// hard spi // hard(-ish) spin
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::yield();
} }
beammp_debug("PPSMonitor starting"); beammp_debug("PPSMonitor starting");
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good); Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
+5 -5
View File
@@ -77,11 +77,6 @@ int main(int argc, char** argv) {
int BeamMPServerMain(MainArguments Arguments) { int BeamMPServerMain(MainArguments Arguments) {
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
Application::InitializeConsole();
Application::SetSubsystemStatus("Main", Application::Status::Starting);
SetupSignalHandlers();
ArgsParser Parser; ArgsParser Parser;
Parser.RegisterArgument({ "help" }, ArgsParser::NONE); Parser.RegisterArgument({ "help" }, ArgsParser::NONE);
Parser.RegisterArgument({ "version" }, ArgsParser::NONE); Parser.RegisterArgument({ "version" }, ArgsParser::NONE);
@@ -122,6 +117,11 @@ int BeamMPServerMain(MainArguments Arguments) {
} }
} }
Application::InitializeConsole();
Application::SetSubsystemStatus("Main", Application::Status::Starting);
SetupSignalHandlers();
bool Shutdown = false; bool Shutdown = false;
Application::RegisterShutdownHandler([&Shutdown] { Application::RegisterShutdownHandler([&Shutdown] {
Application::SetSubsystemStatus("Main", Application::Status::ShuttingDown); Application::SetSubsystemStatus("Main", Application::Status::ShuttingDown);