|
|
@ -30,6 +30,9 @@ using namespace Jupiter::literals; |
|
|
|
|
|
|
|
static STRING_LITERAL_AS_NAMED_REFERENCE(CONTENT_TYPE_APPLICATION_JSON, "application/json"); |
|
|
|
|
|
|
|
const Jupiter::ReferenceString server_list_game_header = "<html><body>"_jrs; |
|
|
|
const Jupiter::ReferenceString server_list_game_footer = "\n</body></html>"_jrs; |
|
|
|
|
|
|
|
Jupiter::String jsonify(const Jupiter::ReadableString &in_str) |
|
|
|
{ |
|
|
|
const char *ptr = in_str.ptr(); |
|
|
@ -49,7 +52,65 @@ Jupiter::String jsonify(const Jupiter::ReadableString &in_str) |
|
|
|
result += '\"'; |
|
|
|
} |
|
|
|
else if (*ptr < 0x20) // control characters
|
|
|
|
result += Jupiter::StringS::Format("\\u00%x", *ptr); |
|
|
|
result.aformat("\\u00%x", *ptr); |
|
|
|
else if ((*ptr & 0x80) != 0) // UTF-8 sequence; copy to bypass above processing |
|
|
|
{ |
|
|
|
result += *ptr; |
|
|
|
|
|
|
|
if ((*ptr & 0x40) != 0) |
|
|
|
{ |
|
|
|
// this is a 2+ byte sequence
|
|
|
|
|
|
|
|
if ((*ptr & 0x20) != 0) |
|
|
|
{ |
|
|
|
// this is a 3+ byte sequence
|
|
|
|
|
|
|
|
if ((*ptr & 0x10) != 0) |
|
|
|
{ |
|
|
|
// this is a 4 byte sequnce
|
|
|
|
result += *++ptr; |
|
|
|
} |
|
|
|
|
|
|
|
result += *++ptr; |
|
|
|
} |
|
|
|
|
|
|
|
result += *++ptr; |
|
|
|
} |
|
|
|
} |
|
|
|
else // Character in standard ASCII table
|
|
|
|
result += *ptr; |
|
|
|
|
|
|
|
++ptr; |
|
|
|
--str_length; |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
Jupiter::String sanitize_game(const Jupiter::ReadableString &in_str) |
|
|
|
{ |
|
|
|
const char *ptr = in_str.ptr(); |
|
|
|
size_t str_length = in_str.size(); |
|
|
|
Jupiter::String result(str_length); |
|
|
|
|
|
|
|
while (str_length != 0) |
|
|
|
{ |
|
|
|
if (*ptr == '\\') // backslash
|
|
|
|
{ |
|
|
|
result += '\\'; |
|
|
|
result += '\\'; |
|
|
|
} |
|
|
|
else if (*ptr == '\"') // quotation
|
|
|
|
{ |
|
|
|
result += '\\'; |
|
|
|
result += '\"'; |
|
|
|
} |
|
|
|
else if (*ptr < 0x20) // control characters
|
|
|
|
result.aformat("\\u00%x", *ptr); |
|
|
|
else if (*ptr == '~') // Game server list control character
|
|
|
|
result += "\\u007E"_jrs; |
|
|
|
else if (*ptr == ';') // Game server list control character
|
|
|
|
result += "\\u003B"_jrs; |
|
|
|
else if ((*ptr & 0x80) != 0) // UTF-8 sequence; copy to bypass above processing |
|
|
|
{ |
|
|
|
result += *ptr; |
|
|
@ -91,6 +152,7 @@ bool RenX_ServerListPlugin::initialize() |
|
|
|
RenX_ServerListPlugin::server_list_page_name = this->config.get(Jupiter::ReferenceString::empty, "ServersPageName"_jrs, "servers.jsp"_jrs); |
|
|
|
RenX_ServerListPlugin::server_list_long_page_name = this->config.get(Jupiter::ReferenceString::empty, "HumanServersPageName"_jrs, "servers_long.jsp"_jrs); |
|
|
|
RenX_ServerListPlugin::server_page_name = this->config.get(Jupiter::ReferenceString::empty, "ServerPageName"_jrs, "server.jsp"_jrs); |
|
|
|
RenX_ServerListPlugin::game_server_list_page_name = this->config.get(Jupiter::ReferenceString::empty, "ServersGamePageName"_jrs, "browser.jsp"_jrs); |
|
|
|
|
|
|
|
/** Initialize content */ |
|
|
|
Jupiter::HTTP::Server &server = getHTTPServer(); |
|
|
@ -119,6 +181,14 @@ bool RenX_ServerListPlugin::initialize() |
|
|
|
content->free_result = true; |
|
|
|
server.hook(RenX_ServerListPlugin::web_hostname, RenX_ServerListPlugin::web_path, content); |
|
|
|
|
|
|
|
// Game server list page
|
|
|
|
content = new Jupiter::HTTP::Server::Content(RenX_ServerListPlugin::game_server_list_page_name, handle_game_server_list_page); |
|
|
|
content->language = &Jupiter::HTTP::Content::Language::ENGLISH; |
|
|
|
content->type = &Jupiter::HTTP::Content::Type::Text::HTML; |
|
|
|
content->charset = &Jupiter::HTTP::Content::Type::Text::Charset::ASCII; |
|
|
|
content->free_result = false; |
|
|
|
server.hook(RenX_ServerListPlugin::web_hostname, RenX_ServerListPlugin::web_path, content); |
|
|
|
|
|
|
|
this->updateServerList(); |
|
|
|
return true; |
|
|
|
} |
|
|
@ -135,11 +205,14 @@ Jupiter::ReadableString *RenX_ServerListPlugin::getServerListJSON() |
|
|
|
return std::addressof(RenX_ServerListPlugin::server_list_json); |
|
|
|
} |
|
|
|
|
|
|
|
const char *json_bool_as_cstring(bool in) |
|
|
|
Jupiter::ReadableString *RenX_ServerListPlugin::getServerListGame() |
|
|
|
{ |
|
|
|
return std::addressof(RenX_ServerListPlugin::server_list_game); |
|
|
|
} |
|
|
|
|
|
|
|
constexpr const char *json_bool_as_cstring(bool in) |
|
|
|
{ |
|
|
|
if (in) |
|
|
|
return "true"; |
|
|
|
return "false"; |
|
|
|
return in ? "true" : "false"; |
|
|
|
} |
|
|
|
|
|
|
|
Jupiter::StringS server_as_json(const RenX::Server *server) |
|
|
@ -150,7 +223,7 @@ Jupiter::StringS server_as_json(const RenX::Server *server) |
|
|
|
Jupiter::String server_map = jsonify(server->getMap().name); |
|
|
|
Jupiter::String server_version = jsonify(server->getGameVersion()); |
|
|
|
|
|
|
|
server_json_block.format(R"json({"Name":"%.*s","Current Map":"%.*s","Bots":%u,"Players":%u,"Game Version":"%.*s","Variables":{"Mine Limit":%d,"bSteamRequired":%s,"bPrivateMessageTeamOnly":%s,"bPassworded":%s,"bAllowPrivateMessaging":%s,"Player Limit":%d,"Vehicle Limit":%d,"bAutoBalanceTeams":%s,"bSpawnCrates":%s,"CrateRespawnAfterPickup":%f,"Time Limit":%d},"Port":%u,"IP":"%.*s")json", |
|
|
|
server_json_block.format(R"json({"Name":"%.*s","Current Map":"%.*s","Bots":%u,"Players":%u,"Game Version":"%.*s","Variables":{"Mine Limit":%d,"bSteamRequired":%s,"bPrivateMessageTeamOnly":%s,"bPassworded":%s,"bAllowPrivateMessaging":%s,"Player Limit":%d,"Vehicle Limit":%d,"bAutoBalanceTeams":%s,"Team Mode":%d,"bSpawnCrates":%s,"CrateRespawnAfterPickup":%f,"Time Limit":%d},"Port":%u,"IP":"%.*s")json", |
|
|
|
server_name.size(), server_name.ptr(), |
|
|
|
server_map.size(), server_map.ptr(), |
|
|
|
server->getBotCount(), |
|
|
@ -163,7 +236,8 @@ Jupiter::StringS server_as_json(const RenX::Server *server) |
|
|
|
json_bool_as_cstring(server->isPrivateMessagingEnabled()), |
|
|
|
server->getPlayerLimit(), |
|
|
|
server->getVehicleLimit(), |
|
|
|
json_bool_as_cstring(server->isAutoBalanceEnabled()), |
|
|
|
json_bool_as_cstring(server->getTeamMode() == 3), |
|
|
|
server->getTeamMode(), |
|
|
|
json_bool_as_cstring(server->isCratesEnabled()), |
|
|
|
server->getCrateRespawnDelay(), |
|
|
|
server->getTimeLimit(), |
|
|
@ -175,6 +249,42 @@ Jupiter::StringS server_as_json(const RenX::Server *server) |
|
|
|
return server_json_block; |
|
|
|
} |
|
|
|
|
|
|
|
Jupiter::StringS server_as_game(const RenX::Server *server) |
|
|
|
{ |
|
|
|
Jupiter::String server_game_block(128); |
|
|
|
|
|
|
|
Jupiter::String server_name = sanitize_game(server->getName()); |
|
|
|
Jupiter::String server_map = sanitize_game(server->getMap().name); |
|
|
|
Jupiter::String server_version = sanitize_game(server->getGameVersion()); |
|
|
|
|
|
|
|
server_game_block.format("\n<@>%.*s~%.*s~%u~%s~%.*s~" "%d;%d;%d;%s;%d;%d;%d;%s;%s;%s;%.*s;%s" "~%u~%d~%s~%s", |
|
|
|
server_name.size(), server_name.ptr(), |
|
|
|
server->getSocketHostname().size(), server->getSocketHostname().ptr(), |
|
|
|
server->getPort(), |
|
|
|
json_bool_as_cstring(server->isPassworded()), |
|
|
|
server_map.size(), server_map.ptr(), |
|
|
|
//START OPTIONS
|
|
|
|
server->getPlayerLimit(), |
|
|
|
server->getVehicleLimit(), |
|
|
|
server->getMineLimit(), |
|
|
|
json_bool_as_cstring(server->isCratesEnabled()), |
|
|
|
server->getGameType(), |
|
|
|
server->getTeamMode(), |
|
|
|
server->getTimeLimit(), |
|
|
|
json_bool_as_cstring(server->isPrivateMessagingEnabled()), |
|
|
|
json_bool_as_cstring(server->isPrivateMessageTeamOnly()), |
|
|
|
json_bool_as_cstring(server->isSteamRequired()), |
|
|
|
server_version.size(), server_version.ptr(), |
|
|
|
json_bool_as_cstring(server->isBotsEnabled()), |
|
|
|
//END OPTIONS
|
|
|
|
server->players.size() - server->getBotCount(), |
|
|
|
server->getPlayerLimit(), |
|
|
|
json_bool_as_cstring(server->isRanked()), // json_bool_as_cstring(plugin != nullptr && (reinterpret_cast<RenX_LadderPlugin *>(plugin)->isOnlyPure() == false || server->isPure())),
|
|
|
|
json_bool_as_cstring(server->isMatchInProgress())); |
|
|
|
|
|
|
|
return server_game_block; |
|
|
|
} |
|
|
|
|
|
|
|
Jupiter::StringS server_as_long_json(const RenX::Server *server) |
|
|
|
{ |
|
|
|
Jupiter::String server_json_block(128); |
|
|
@ -198,6 +308,7 @@ Jupiter::StringS server_as_long_json(const RenX::Server *server) |
|
|
|
"Player Limit": %d, |
|
|
|
"Vehicle Limit": %d, |
|
|
|
"bAutoBalanceTeams": %s, |
|
|
|
"Team Mode": %d |
|
|
|
"bSpawnCrates": %s, |
|
|
|
"CrateRespawnAfterPickup": %f, |
|
|
|
"Time Limit": %d |
|
|
@ -218,7 +329,8 @@ Jupiter::StringS server_as_long_json(const RenX::Server *server) |
|
|
|
json_bool_as_cstring(server->isPrivateMessagingEnabled()), |
|
|
|
server->getPlayerLimit(), |
|
|
|
server->getVehicleLimit(), |
|
|
|
json_bool_as_cstring(server->isAutoBalanceEnabled()), |
|
|
|
json_bool_as_cstring(server->getTeamMode() == 3), |
|
|
|
server->getTeamMode(), |
|
|
|
json_bool_as_cstring(server->isCratesEnabled()), |
|
|
|
server->getCrateRespawnDelay(), |
|
|
|
server->getTimeLimit(), |
|
|
@ -294,6 +406,21 @@ void RenX_ServerListPlugin::addServerToServerList(RenX::Server *server) |
|
|
|
RenX_ServerListPlugin::server_list_json += ']'; |
|
|
|
} |
|
|
|
|
|
|
|
// append to server_list_game
|
|
|
|
|
|
|
|
if (RenX_ServerListPlugin::server_list_game.isEmpty()) |
|
|
|
{ |
|
|
|
RenX_ServerListPlugin::server_list_game = server_list_game_header; |
|
|
|
RenX_ServerListPlugin::server_list_game += server_as_game(server); |
|
|
|
RenX_ServerListPlugin::server_list_game += server_list_game_footer; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
RenX_ServerListPlugin::server_list_game.truncate(server_list_game_footer.size()); // remove trailing "</body></html>"
|
|
|
|
RenX_ServerListPlugin::server_list_game += server_as_game(server); |
|
|
|
RenX_ServerListPlugin::server_list_game += server_list_game_footer; |
|
|
|
} |
|
|
|
|
|
|
|
// add to individual listing
|
|
|
|
|
|
|
|
server_json_block = '{'; |
|
|
@ -352,9 +479,10 @@ void RenX_ServerListPlugin::updateServerList() |
|
|
|
size_t index = 0; |
|
|
|
RenX::Server *server; |
|
|
|
|
|
|
|
// regenerate server_list_json
|
|
|
|
// regenerate server_list_json and server_list_Game
|
|
|
|
|
|
|
|
RenX_ServerListPlugin::server_list_json = '['; |
|
|
|
RenX_ServerListPlugin::server_list_game = server_list_game_header; |
|
|
|
|
|
|
|
while (index != servers.size()) |
|
|
|
{ |
|
|
@ -362,6 +490,8 @@ void RenX_ServerListPlugin::updateServerList() |
|
|
|
if (server->isConnected() && server->isFullyConnected()) |
|
|
|
{ |
|
|
|
RenX_ServerListPlugin::server_list_json += server_as_json(server); |
|
|
|
RenX_ServerListPlugin::server_list_game += server_as_game(server); |
|
|
|
|
|
|
|
++index; |
|
|
|
break; |
|
|
|
} |
|
|
@ -374,11 +504,14 @@ void RenX_ServerListPlugin::updateServerList() |
|
|
|
{ |
|
|
|
RenX_ServerListPlugin::server_list_json += ','; |
|
|
|
RenX_ServerListPlugin::server_list_json += server_as_json(server); |
|
|
|
|
|
|
|
RenX_ServerListPlugin::server_list_game += server_as_game(server); |
|
|
|
} |
|
|
|
++index; |
|
|
|
} |
|
|
|
|
|
|
|
RenX_ServerListPlugin::server_list_json += ']'; |
|
|
|
RenX_ServerListPlugin::server_list_game += server_list_game_footer; |
|
|
|
} |
|
|
|
|
|
|
|
void RenX_ServerListPlugin::RenX_OnServerFullyConnected(RenX::Server *server) |
|
|
@ -495,6 +628,11 @@ Jupiter::ReadableString *handle_server_page(const Jupiter::ReadableString &query |
|
|
|
return new Jupiter::ReferenceString(server->varData.get(pluginInstance.getName(), "j"_jrs)); |
|
|
|
} |
|
|
|
|
|
|
|
Jupiter::ReadableString *handle_game_server_list_page(const Jupiter::ReadableString &query_string) |
|
|
|
{ |
|
|
|
return pluginInstance.getServerListGame(); |
|
|
|
} |
|
|
|
|
|
|
|
extern "C" __declspec(dllexport) Jupiter::Plugin *getPlugin() |
|
|
|
{ |
|
|
|
return &pluginInstance; |
|
|
|