mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
300 lines
11 KiB
C++
300 lines
11 KiB
C++
//===========================================================================//
|
|
//
|
|
// Purpose: LiveAPI WebSocket implementation
|
|
//
|
|
//===========================================================================//
|
|
#include "liveapi.h"
|
|
#include "protobuf/util/json_util.h"
|
|
|
|
#include "DirtySDK/dirtysock.h"
|
|
#include "DirtySDK/dirtysock/netconn.h"
|
|
#include "DirtySDK/proto/protossl.h"
|
|
#include "DirtySDK/proto/protowebsocket.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// change callbacks
|
|
//-----------------------------------------------------------------------------
|
|
static void LiveAPI_EnabledChangedCallback(IConVar* var, const char* pOldValue)
|
|
{
|
|
LiveAPISystem()->ToggleInit();
|
|
}
|
|
static void LiveAPI_WebSocketEnabledChangedCallback(IConVar* var, const char* pOldValue)
|
|
{
|
|
LiveAPISystem()->ToggleInitWebSocket();
|
|
}
|
|
static void LiveAPI_ParamsChangedCallback(IConVar* var, const char* pOldValue)
|
|
{
|
|
LiveAPISystem()->UpdateParams();
|
|
}
|
|
static void LiveAPI_AddressChangedCallback(IConVar* var, const char* pOldValue)
|
|
{
|
|
LiveAPISystem()->RebootWebSocket();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// console variables
|
|
//-----------------------------------------------------------------------------
|
|
ConVar liveapi_enabled("liveapi_enabled", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Enable LiveAPI functionality", &LiveAPI_EnabledChangedCallback);
|
|
ConVar liveapi_session_name("liveapi_session_name", "liveapi_session", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "LiveAPI session name to identify this connection");
|
|
ConVar liveapi_truncate_hash_fields("liveapi_truncate_hash_fields", "1", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to truncate hash fields in LiveAPI events to save on I/O");
|
|
|
|
// WebSocket core
|
|
static ConVar liveapi_websocket_enabled("liveapi_websocket_enabled", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to use WebSocket to transmit LiveAPI events", &LiveAPI_WebSocketEnabledChangedCallback);
|
|
static ConVar liveapi_servers("liveapi_servers", "", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Comma separated list of addresses to connect to", &LiveAPI_AddressChangedCallback, "ws://domain.suffix:port");
|
|
|
|
// WebSocket connection base parameters
|
|
static ConVar liveapi_retry_count("liveapi_retry_count", "5", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Amount of times to retry connecting before marking the connection as unavailable", &LiveAPI_ParamsChangedCallback);
|
|
static ConVar liveapi_retry_time("liveapi_retry_time", "30", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Amount of time between each retry", &LiveAPI_ParamsChangedCallback);
|
|
|
|
// WebSocket connection context parameters
|
|
static ConVar liveapi_timeout("liveapi_timeout", "300", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "WebSocket connection timeout in seconds", &LiveAPI_ParamsChangedCallback);
|
|
static ConVar liveapi_keepalive("liveapi_keepalive", "30", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Interval of time to send Pong to any connected server", &LiveAPI_ParamsChangedCallback);
|
|
static ConVar liveapi_lax_ssl("liveapi_lax_ssl", "1", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Skip SSL certificate validation for all WSS connections (allows the use of self-signed certificates)", &LiveAPI_ParamsChangedCallback);
|
|
|
|
// Print core
|
|
static ConVar liveapi_print_enabled("liveapi_print_enabled", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to enable the printing of all events to a LiveAPI JSON file");
|
|
|
|
// Print parameters
|
|
static ConVar liveapi_print_pretty("liveapi_print_pretty", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to print events in a formatted manner to the LiveAPI JSON file");
|
|
static ConVar liveapi_print_primitive("liveapi_print_primitive", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to print primitive event fields to the LiveAPI JSON file");
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructors/destructors
|
|
//-----------------------------------------------------------------------------
|
|
LiveAPI::LiveAPI()
|
|
{
|
|
matchLogCount = 0;
|
|
initialLog = false;
|
|
initialized = false;
|
|
}
|
|
LiveAPI::~LiveAPI()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initialization of the LiveAPI system
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::Init()
|
|
{
|
|
if (!liveapi_enabled.GetBool())
|
|
return;
|
|
|
|
InitWebSocket();
|
|
initialized = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Shutdown of the LiveAPI system
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::Shutdown()
|
|
{
|
|
webSocketSystem.Shutdown();
|
|
DestroyLogger();
|
|
initialized = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Toggle between init or deinit depending on current init state and the value
|
|
// of the cvar 'liveapi_enabled'
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::ToggleInit()
|
|
{
|
|
const bool enabled = liveapi_enabled.GetBool();
|
|
|
|
if (enabled && !initialized)
|
|
Init();
|
|
else if (!enabled && initialized)
|
|
Shutdown();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Populate the connection params structure
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::CreateParams(CWebSocket::ConnParams_s& params)
|
|
{
|
|
params.bufSize = LIVE_API_MAX_FRAME_BUFFER_SIZE;
|
|
|
|
params.retryTime = liveapi_retry_time.GetFloat();
|
|
params.maxRetries = liveapi_retry_count.GetInt();
|
|
|
|
params.timeOut = liveapi_timeout.GetInt();
|
|
params.keepAlive = liveapi_keepalive.GetInt();
|
|
params.laxSSL = liveapi_lax_ssl.GetInt();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Update the websocket parameters and apply them on all connections
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::UpdateParams()
|
|
{
|
|
CWebSocket::ConnParams_s connParams;
|
|
CreateParams(connParams);
|
|
|
|
webSocketSystem.UpdateParams(connParams);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initialize the websocket system
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::InitWebSocket()
|
|
{
|
|
if (!liveapi_websocket_enabled.GetBool())
|
|
return;
|
|
|
|
CWebSocket::ConnParams_s connParams;
|
|
CreateParams(connParams);
|
|
|
|
const char* initError = nullptr;
|
|
|
|
if (!webSocketSystem.Init(liveapi_servers.GetString(), connParams, initError))
|
|
{
|
|
Error(eDLL_T::RTECH, 0, "LiveAPI: WebSocket initialization failed! [%s]\n", initError);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Shutdown the websocket system
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::ShutdownWebSocket()
|
|
{
|
|
webSocketSystem.Shutdown();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Toggle between init or deinit depending on current init state and the value
|
|
// of the cvar 'liveapi_websocket_enabled'
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::ToggleInitWebSocket()
|
|
{
|
|
const bool enabled = liveapi_websocket_enabled.GetBool();
|
|
|
|
if (enabled && !WebSocketInitialized())
|
|
InitWebSocket();
|
|
else if (!enabled && WebSocketInitialized())
|
|
ShutdownWebSocket();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Reboot the websocket system and reconnect to addresses specified in cvar
|
|
// 'liveapi_servers'
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::RebootWebSocket()
|
|
{
|
|
ShutdownWebSocket();
|
|
InitWebSocket();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create the file logger
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::CreateLogger()
|
|
{
|
|
// Its possible that one was already created but never closed, this is
|
|
// possible if the game scripts crashed or something along those lines.
|
|
DestroyLogger();
|
|
|
|
if (!liveapi_print_enabled.GetBool())
|
|
return; // Logging is disabled
|
|
|
|
matchLogger = spdlog::basic_logger_mt("match_logger",
|
|
Format("platform/liveapi/logs/%s/match_%d.json", g_LogSessionUUID.c_str(), matchLogCount++));
|
|
|
|
matchLogger.get()->set_pattern("%v");
|
|
matchLogger.get()->info("[\n");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Destroy the file logger
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::DestroyLogger()
|
|
{
|
|
if (initialLog)
|
|
initialLog = false;
|
|
|
|
if (!matchLogger)
|
|
return; // Nothing to drop
|
|
|
|
matchLogger.get()->info("\n]\n");
|
|
matchLogger.reset();
|
|
|
|
spdlog::drop("match_logger");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// LiveAPI state machine
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::RunFrame()
|
|
{
|
|
if (!IsEnabled())
|
|
return;
|
|
|
|
if (WebSocketInitialized())
|
|
webSocketSystem.Update();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Send an event to all sockets
|
|
//-----------------------------------------------------------------------------
|
|
void LiveAPI::LogEvent(const google::protobuf::Message* const toTransmit, const google::protobuf::Message* toPrint)
|
|
{
|
|
if (!IsEnabled())
|
|
return;
|
|
|
|
if (WebSocketInitialized())
|
|
{
|
|
const string data = toTransmit->SerializeAsString();
|
|
webSocketSystem.SendData(data.c_str(), (int)data.size());
|
|
}
|
|
|
|
// NOTE: we don't check on the cvar 'liveapi_print_enabled' here because if
|
|
// this cvar gets disabled on the fly and we check it here, the output will
|
|
// be truncated and thus invalid! Log for as long as the SpdLog instance is
|
|
// valid.
|
|
if (matchLogger)
|
|
{
|
|
std::string jsonStr(initialLog ? ",\n" : "");
|
|
google::protobuf::util::JsonPrintOptions options;
|
|
|
|
options.add_whitespace = liveapi_print_pretty.GetBool();
|
|
options.always_print_primitive_fields = liveapi_print_primitive.GetBool();
|
|
|
|
google::protobuf::util::MessageToJsonString(*toPrint, &jsonStr, options);
|
|
|
|
// Remove the trailing newline character
|
|
if (options.add_whitespace && !jsonStr.empty())
|
|
jsonStr.pop_back();
|
|
|
|
matchLogger.get()->info(jsonStr);
|
|
|
|
if (!initialLog)
|
|
initialLog = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns whether the system is enabled
|
|
//-----------------------------------------------------------------------------
|
|
bool LiveAPI::IsEnabled() const
|
|
{
|
|
return liveapi_enabled.GetBool();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns whether the system is able to run
|
|
//-----------------------------------------------------------------------------
|
|
bool LiveAPI::IsValidToRun() const
|
|
{
|
|
return (IsEnabled() && (WebSocketInitialized() || FileLoggerInitialized()));
|
|
}
|
|
|
|
static LiveAPI s_liveApi;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton accessor
|
|
//-----------------------------------------------------------------------------
|
|
LiveAPI* LiveAPISystem()
|
|
{
|
|
return &s_liveApi;
|
|
}
|