Browse Source

Added support for RCON protocol 4

Added event: 'RenX_OnFullyConnected'
Added RenX.ServerList plugin
Updated Jupiter
pull/3/head
Jessica James 9 years ago
parent
commit
367a00ffd9
  1. 16
      Config.ini
  2. 2
      Jupiter
  3. 11
      Jupiter Bot.sln
  4. BIN
      Release/Bot.lib
  5. BIN
      Release/Plugins/RenX.Core.lib
  6. 1
      RenX.Core/RenX.Core.vcxproj
  7. 3
      RenX.Core/RenX.Core.vcxproj.filters
  8. 92
      RenX.Core/RenX_Functions.cpp
  9. 18
      RenX.Core/RenX_Functions.h
  10. 1
      RenX.Core/RenX_PlayerInfo.h
  11. 5
      RenX.Core/RenX_Plugin.cpp
  12. 1
      RenX.Core/RenX_Plugin.h
  13. 181
      RenX.Core/RenX_Server.cpp
  14. 43
      RenX.Core/RenX_Server.h
  15. 63
      RenX.Core/RenX_TeamInfo.h
  16. 7
      RenX.ModSystem/RenX_ModSystem.cpp
  17. 86
      RenX.ServerList/RenX.ServerList.vcxproj
  18. 41
      RenX.ServerList/RenX.ServerList.vcxproj.filters
  19. 487
      RenX.ServerList/RenX_ServerList.cpp
  20. 57
      RenX.ServerList/RenX_ServerList.h

16
Config.ini

@ -655,24 +655,20 @@ Part.FormatNoReason={NAME} has left {CHAN}!
[RenX.MinPlayers]
; [SetJoins]
; Join messages are stored here.
; Note: Usage of the "setjoin" command syncs the memory-stored
; file to the drive, which will destroy all comments in the process.
;
; String(User)=String(SetJoin)
; [RenX.SetJoin]
; SetJoinFile=String(Default: RenX.SetJoin.ini)
;
[SetJoins]
[RenX.SetJoin]
; [RenX.SetJoin]
; Renenegade-X game join messages are stored here.
; [SetJoins]
; Join messages are stored here.
; Note: Usage of the "setjoin" command syncs the memory-stored
; file to the drive, which will destroy all comments in the process.
;
; String(User)=String(SetJoin)
;
[RenX.SetJoin]
[SetJoins]
;EOF

2
Jupiter

@ -1 +1 @@
Subproject commit e656b6f6ba3614b57bc19cb4c4835801e33daf8e
Subproject commit 87f1b47149f7974ab5faa81b8b9d3c4b26b68838

11
Jupiter Bot.sln

@ -210,6 +210,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenX.Ladder.Daily", "RenX.L
{9103DF3D-8B4A-48E5-A6B3-CBE2554630E2} = {9103DF3D-8B4A-48E5-A6B3-CBE2554630E2}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenX.ServerList", "RenX.ServerList\RenX.ServerList.vcxproj", "{6B0D59BA-B153-4DE8-8DD4-FBE5D810B033}"
ProjectSection(ProjectDependencies) = postProject
{C188871B-5F32-4946-B301-24CA2EBB275D} = {C188871B-5F32-4946-B301-24CA2EBB275D}
{9103DF3D-8B4A-48E5-A6B3-CBE2554630E2} = {9103DF3D-8B4A-48E5-A6B3-CBE2554630E2}
{BB048D6F-F001-4E9B-95F4-886081E0807A} = {BB048D6F-F001-4E9B-95F4-886081E0807A}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@ -360,6 +367,10 @@ Global
{73F0EEF6-CE5E-46EB-80B4-2B319AE2B258}.Debug|Win32.Build.0 = Debug|Win32
{73F0EEF6-CE5E-46EB-80B4-2B319AE2B258}.Release|Win32.ActiveCfg = Release|Win32
{73F0EEF6-CE5E-46EB-80B4-2B319AE2B258}.Release|Win32.Build.0 = Release|Win32
{6B0D59BA-B153-4DE8-8DD4-FBE5D810B033}.Debug|Win32.ActiveCfg = Debug|Win32
{6B0D59BA-B153-4DE8-8DD4-FBE5D810B033}.Debug|Win32.Build.0 = Debug|Win32
{6B0D59BA-B153-4DE8-8DD4-FBE5D810B033}.Release|Win32.ActiveCfg = Release|Win32
{6B0D59BA-B153-4DE8-8DD4-FBE5D810B033}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

BIN
Release/Bot.lib

Binary file not shown.

BIN
Release/Plugins/RenX.Core.lib

Binary file not shown.

1
RenX.Core/RenX.Core.vcxproj

@ -83,6 +83,7 @@
<ClInclude Include="RenX_Plugin.h" />
<ClInclude Include="RenX_Server.h" />
<ClInclude Include="RenX_Tags.h" />
<ClInclude Include="RenX_TeamInfo.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="RenX_BanDatabase.cpp" />

3
RenX.Core/RenX.Core.vcxproj.filters

@ -62,6 +62,9 @@
<ClInclude Include="RenX_LadderDatabase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RenX_TeamInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="RenX_Plugin.cpp">

92
RenX.Core/RenX_Functions.cpp

@ -44,7 +44,8 @@ Jupiter::ReferenceString GDILongName = "Global Defense Initiative";
Jupiter::ReferenceString OtherLongName = "Unknown";
/** RenegadeX RCON protocol message deliminator */
const char RenX::DelimC = '\xA0';
const char RenX::DelimC = '\x02';
const char RenX::DelimC3 = '\xA0';
const Jupiter::ReferenceString RenX::DevBotName = "DevBot"_jrs;
/** WinType translations */
@ -877,16 +878,6 @@ Jupiter::StringS RenX::formatGUID(const RenX::Map &map)
return Jupiter::StringS::Format("%.16llX%.16llX", map.guid[0], map.guid[1]);
}
void RenX::sanitizeString(Jupiter::StringType &str)
{
if (str.isNotEmpty())
{
str.replace('|', '/');
if (str.get(str.size() - 1) == '\\')
str.set(str.size() - 1, '/');
}
}
std::chrono::milliseconds RenX::getServerTime(const RenX::PlayerInfo *player)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - player->joinTime);
@ -911,4 +902,81 @@ double RenX::getHeadshotKillRatio(const RenX::PlayerInfo *player)
{
if (player->kills == 0) return 0;
return ((double)player->headshots) / ((double)player->kills);
}
}
Jupiter::String RenX::escapifyRCON(const Jupiter::ReadableString &str)
{
const char *ptr = str.ptr();
size_t length = str.size();
Jupiter::String result(str.size() + 32);
uint16_t value;
while (length != 0)
{
if ((*ptr & 0x80) != 0) // UTF-8 sequence
{
if (length < 2)
break;
if ((*ptr & 0x40) != 0) // validity check
{
// get codepoint value
if ((*ptr & 0x20) != 0)
{
if (length < 3)
break;
if ((*ptr & 0x10) != 0) // This is a 4 byte sequence, which we can not fit into a 16-bit codepoint. ignore it.
{
if (length < 4)
break;
ptr += 4;
length -= 4;
continue;
}
else
{
// this is a 3 byte sequence
value = (*ptr & 0x0F) << 12;
value += (*++ptr & 0x3F) << 6;
value += *++ptr & 0x3F;
length -= 3;
}
}
else
{
// This is a 2 byte sequence
value = (*ptr & 0x1F) << 6;
value += *++ptr & 0x3F;
length -= 2;
}
// write escape sequence
result += '\\';
result += 'u';
result += Jupiter_asHex_upper(value >> 8);
result += Jupiter_asHex_upper(value & 0x00FF);
printf(ENDL ENDL ENDL "\\u%x%x" ENDL ENDL ENDL ENDL, value >> 8, value & 0x00FF);
}
// else // This is an invalid 1 byte sequence
}
else if (*ptr == '\\') // backslash, which is used for escape sequencing
{
result += '\\';
result += '\\';
--length;
}
else // an ordinary character
{
result += *ptr;
--length;
}
++ptr;
}
return result;
}

18
RenX.Core/RenX_Functions.h

@ -145,15 +145,6 @@ namespace RenX
*/
RENX_API Jupiter::StringS formatGUID(const RenX::Map &map);
/**
* @brief Sanitizes a string into a RCON-ready state by replacing special
* characters with HTML-style character codes.
* Note: This resolves the pipe character ('|') exploit.
*
* @brief str String to sanitize.
*/
RENX_API void sanitizeString(Jupiter::StringType &str);
/**
* @brief Calculates for how many seconds a player has been in the server.
*
@ -190,8 +181,17 @@ namespace RenX
*/
RENX_API double getHeadshotKillRatio(const RenX::PlayerInfo *player);
/**
* @brief Escapifies a string so that it can be safely transmitted over RCON.
*
* @param str String to escapify
* @return Escapified version of str.
*/
RENX_API Jupiter::String escapifyRCON(const Jupiter::ReadableString &str);
/** Constant variables */
RENX_API extern const char DelimC; /** RCON message deliminator */
RENX_API extern const char DelimC3; /** RCON message deliminator for RCON version number 003 */
RENX_API extern const Jupiter::ReferenceString DevBotName;
}

1
RenX.Core/RenX_PlayerInfo.h

@ -56,6 +56,7 @@ namespace RenX
Jupiter::StringS character;
Jupiter::StringS vehicle;
Jupiter::StringS rdns;
Jupiter::StringS hwid;
std::mutex rdns_mutex;
uint64_t steamid = 0;
uint32_t ip32 = 0;

5
RenX.Core/RenX_Plugin.cpp

@ -71,6 +71,11 @@ void RenX::Plugin::RenX_OnServerCreate(Server *)
return;
}
void RenX::Plugin::RenX_OnServerFullyConnected(Server *)
{
return;
}
void RenX::Plugin::RenX_OnServerDisconnect(Server *, RenX::DisconnectReason)
{
return;

1
RenX.Core/RenX_Plugin.h

@ -50,6 +50,7 @@ namespace RenX
virtual void RenX_OnPlayerUUIDChange(Server *server, const PlayerInfo *player, const Jupiter::ReadableString &newUUID);
virtual void RenX_OnPlayerRDNS(Server *server, const PlayerInfo *player);
virtual void RenX_OnServerCreate(Server *server);
virtual void RenX_OnServerFullyConnected(Server *server);
virtual void RenX_OnServerDisconnect(Server *server, RenX::DisconnectReason reason);
virtual bool RenX_OnBan(Server *server, const PlayerInfo *player, Jupiter::StringType &data);

181
RenX.Core/RenX_Server.cpp

@ -187,6 +187,16 @@ bool RenX::Server::isConnected() const
return RenX::Server::connected;
}
bool RenX::Server::isSubscribed() const
{
return RenX::Server::subscribed;
}
bool RenX::Server::isFullyConnected() const
{
return RenX::Server::fully_connected;
}
bool RenX::Server::hasSeenStart() const
{
return RenX::Server::seenStart;
@ -239,29 +249,28 @@ bool RenX::Server::isPure() const
int RenX::Server::send(const Jupiter::ReadableString &command)
{
return RenX::Server::sock.send("c"_jrs + command + '\n');
return RenX::Server::sock.send("c"_jrs + RenX::escapifyRCON(command) + '\n');
}
int RenX::Server::sendMessage(const Jupiter::ReadableString &message)
{
Jupiter::String msg = RenX::escapifyRCON(message);
if (RenX::Server::neverSay)
{
int r = 0;
if (RenX::Server::players.size() != 0)
for (Jupiter::DLList<RenX::PlayerInfo>::Node *node = RenX::Server::players.getNode(0); node != nullptr; node = node->next)
if (node->data->isBot == false)
r += RenX::Server::sock.send(Jupiter::StringS::Format("chostprivatesay pid%d %.*s\n", node->data->id, message.size(), message.ptr()));
r += RenX::Server::sock.send(Jupiter::StringS::Format("chostprivatesay pid%d %.*s\n", node->data->id, msg.size(), msg.ptr()));
return r;
}
else
return RenX::Server::sock.send("chostsay "_jrs + message + '\n');
return RenX::Server::sock.send("chostsay "_jrs + msg + '\n');
}
int RenX::Server::sendMessage(const RenX::PlayerInfo *player, const Jupiter::ReadableString &message)
{
auto cmd = "chostprivatesay pid"_jrs + Jupiter::StringS::Format("%d ", player->id) + message + '\n';
RenX::sanitizeString(cmd);
return RenX::Server::sock.send(cmd);
return RenX::Server::sock.send("chostprivatesay pid"_jrs + Jupiter::StringS::Format("%d ", player->id) + RenX::escapifyRCON(message) + '\n');
}
int RenX::Server::sendData(const Jupiter::ReadableString &data)
@ -418,8 +427,10 @@ Jupiter::StringS RenX::Server::formatSteamID(uint64_t id) const
}
}
void RenX::Server::kickPlayer(int id, const Jupiter::ReadableString &reason)
void RenX::Server::kickPlayer(int id, const Jupiter::ReadableString &in_reason)
{
Jupiter::String reason = RenX::escapifyRCON(in_reason);
if (reason.isEmpty())
RenX::Server::sock.send(Jupiter::StringS::Format("ckick pid%d\n", id));
else
@ -432,8 +443,10 @@ void RenX::Server::kickPlayer(const RenX::PlayerInfo *player, const Jupiter::Rea
RenX::Server::kickPlayer(player->id, reason);
}
void RenX::Server::forceKickPlayer(int id, const Jupiter::ReadableString &reason)
void RenX::Server::forceKickPlayer(int id, const Jupiter::ReadableString &in_reason)
{
Jupiter::String reason = RenX::escapifyRCON(in_reason);
if (reason.isEmpty())
RenX::Server::sock.send(Jupiter::StringS::Format("cfkick pid%d You were kicked from the server.\n", id));
else
@ -611,7 +624,10 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player)
void RenX::Server::banPlayer(int id, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason)
{
if (RenX::Server::rconBan)
RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", id, reason.size(), reason.ptr()));
{
Jupiter::String out_reason = RenX::escapifyRCON(reason);
RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", id, out_reason.size(), out_reason.ptr()));
}
else
{
RenX::PlayerInfo *player = RenX::Server::getPlayer(id);
@ -630,7 +646,10 @@ void RenX::Server::banPlayer(const RenX::PlayerInfo *player, const Jupiter::Read
if (length == std::chrono::seconds::zero())
{
if (RenX::Server::rconBan)
RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", player->id, reason.size(), reason.ptr()));
{
Jupiter::String out_reason = RenX::escapifyRCON(reason);
RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", player->id, out_reason.size(), out_reason.ptr()));
}
else if (banner.isNotEmpty())
RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are permanently banned from %.*s by %.*s for: %.*s", RenX::Server::ban_from_str.size(), RenX::Server::ban_from_str.ptr(), banner.size(), banner.ptr(), reason.size(), reason.ptr()));
else
@ -680,8 +699,12 @@ bool RenX::Server::removePlayer(RenX::PlayerInfo *player)
bool RenX::Server::fetchClientList()
{
RenX::Server::lastClientListUpdate = std::chrono::steady_clock::now();
return RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cclientvarlist KILLS\xA0""DEATHS\xA0""SCORE\xA0""CREDITS\xA0""CHARACTER\xA0""VEHICLE\xA0""PING\xA0""ADMIN\xA0""STEAM\xA0""IP\xA0""PLAYERLOG\n")) > 0
&& RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cbotvarlist KILLS\xA0""DEATHS\xA0""SCORE\xA0""CREDITS\xA0""CHARACTER\xA0""VEHICLE\xA0""PLAYERLOG\n")) > 0;
if (this->rconVersion >= 4)
return RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cclientvarlist KILLS DEATHS SCORE CREDITS CHARACTER VEHICLE PING ADMIN STEAM IP PLAYERLOG\n")) > 0
&& RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cbotvarlist KILLS DEATHS SCORE CREDITS CHARACTER VEHICLE PLAYERLOG\n")) > 0;
else
return RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cclientvarlist KILLS\xA0""DEATHS\xA0""SCORE\xA0""CREDITS\xA0""CHARACTER\xA0""VEHICLE\xA0""PING\xA0""ADMIN\xA0""STEAM\xA0""IP\xA0""PLAYERLOG\n")) > 0
&& RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cbotvarlist KILLS\xA0""DEATHS\xA0""SCORE\xA0""CREDITS\xA0""CHARACTER\xA0""VEHICLE\xA0""PLAYERLOG\n")) > 0;
}
bool RenX::Server::updateClientList()
@ -690,10 +713,20 @@ bool RenX::Server::updateClientList()
int r = 0;
if (RenX::Server::players.size() != RenX::Server::bot_count)
r = RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cclientvarlist ID\xA0""SCORE\xA0""CREDITS\xA0""PING\n")) > 0;
{
if (this->rconVersion >= 4)
r = RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cclientvarlist ID SCORE CREDITS PING\n")) > 0;
else
r = RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cclientvarlist ID\xA0""SCORE\xA0""CREDITS\xA0""PING\n")) > 0;
}
if (RenX::Server::bot_count != 0)
r |= RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cbotvarlist ID\xA0""SCORE\xA0""CREDITS\n")) > 0;
{
if (this->rconVersion >= 4)
r |= RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cbotvarlist ID SCORE CREDITS\n")) > 0;
else
r |= RenX::Server::sock.send(STRING_LITERAL_AS_REFERENCE("cbotvarlist ID\xA0""SCORE\xA0""CREDITS\n")) > 0;
}
return r != 0;
}
@ -920,11 +953,61 @@ std::chrono::milliseconds RenX::Server::getDelay() const
return RenX::Server::delay;
}
int RenX::Server::getMineLimit() const
{
return RenX::Server::mineLimit;
}
int RenX::Server::getPlayerLimit() const
{
return RenX::Server::playerLimit;
}
int RenX::Server::getVehicleLimit() const
{
return RenX::Server::vehicleLimit;
}
int RenX::Server::getTimeLimit() const
{
return RenX::Server::timeLimit;
}
double RenX::Server::getCrateRespawnDelay() const
{
return RenX::Server::crateRespawnAfterPickup;
}
bool RenX::Server::isSteamRequired() const
{
return RenX::Server::steamRequired;
}
bool RenX::Server::isPrivateMessageTeamOnly() const
{
return RenX::Server::privateMessageTeamOnly;
}
bool RenX::Server::isPrivateMessagingEnabled() const
{
return RenX::Server::allowPrivateMessaging;
}
bool RenX::Server::isPassworded() const
{
return RenX::Server::passworded;
}
bool RenX::Server::isAutoBalanceEnabled() const
{
return RenX::Server::autoBalanceTeams;
}
bool RenX::Server::isCratesEnabled() const
{
return RenX::Server::spawnCrates;
}
const Jupiter::ReadableString &RenX::Server::getPassword() const
{
return RenX::Server::pass;
@ -1212,7 +1295,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
return;
Jupiter::ArrayList<RenX::Plugin> &xPlugins = *RenX::getCore()->getPlugins();
Jupiter::ReadableString::TokenizeResult<Jupiter::String_Strict> tokens = Jupiter::StringS::tokenize(line, RenX::DelimC);
Jupiter::ReadableString::TokenizeResult<Jupiter::String_Strict> tokens = Jupiter::StringS::tokenize(line, this->rconVersion >= 4 ? RenX::DelimC : RenX::DelimC3);
for (size_t index = 0; index != tokens.token_count; ++index)
tokens.tokens[index].processEscapeSequences();
@ -1398,9 +1481,16 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
size_t offset = index;
while (index != 0)
offset += tokens.tokens[--index].size();
offset += tokens.tokens[--index].size() + 1;
return Jupiter::ReferenceString::substring(line, offset + 1);
return Jupiter::ReferenceString::substring(line, offset);
};
auto finished_connecting = [this, &xPlugins]()
{
this->fully_connected = true;
for (size_t index = 0; index < xPlugins.size(); ++index)
xPlugins.get(index)->RenX_OnServerFullyConnected(this);
};
if (tokens.tokens[0].isNotEmpty())
@ -1743,7 +1833,12 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
}
}
else if (this->lastCommand.equalsi("ping"))
RenX::Server::awaitingPong = false;
{
if (tokens.getToken(1).equals("srv_init_done"_jrs))
finished_connecting();
else
RenX::Server::awaitingPong = false;
}
else if (this->lastCommand.equalsi("map"))
{
// Map | Guid
@ -2419,10 +2514,26 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
{
PARSE_PLAYER_DATA_P(tokens.getToken(2));
uint64_t steamid = 0;
if (tokens.getToken(5).equals("steamid"))
steamid = tokens.getToken(6).asUnsignedLongLong();
RenX::PlayerInfo *player;
if (tokens.getToken(5).equals("hwid"))
{
// New format
if (tokens.getToken(7).equals("steamid"))
steamid = tokens.getToken(8).asUnsignedLongLong();
player = getPlayerOrAdd(name, id, team, isBot, steamid, tokens.getToken(4));
player->hwid = tokens.getToken(6);
}
else
{
// Old format
if (tokens.getToken(5).equals("steamid"))
steamid = tokens.getToken(6).asUnsignedLongLong();
player = getPlayerOrAdd(name, id, team, isBot, steamid, tokens.getToken(4));
}
RenX::PlayerInfo *player = getPlayerOrAdd(name, id, team, isBot, steamid, tokens.getToken(4));
if (steamid != 0ULL && default_ladder_database != nullptr && (player->ban_flags & RenX::BanDatabase::Entry::FLAG_TYPE_LADDER) == 0)
{
RenX::LadderDatabase::Entry *itr = RenX::default_ladder_database->getHead();
@ -2434,7 +2545,10 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
if (this->devBot)
{
player->global_rank = itr->rank;
this->sendData(Jupiter::StringS::Format("xset_rank %d\n", player->id));
if (this->rconVersion >= 4)
this->sendData(Jupiter::StringS::Format("xset_rank %d %d\n", player->id, player->global_rank));
else
this->sendData(Jupiter::StringS::Format("xset_rank%c%d%c%d\n", RenX::DelimC3, player->id, RenX::DelimC3, player->global_rank));
}
break;
}
@ -2500,7 +2614,12 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
if (player->isBot == false)
this->banCheck(player);
if (this->devBot && player->global_rank != 0U)
this->sendData(Jupiter::StringS::Format("xset_rank %d\n", player->id));
{
if (this->rconVersion >= 4)
this->sendData(Jupiter::StringS::Format("xset_rank %d %d\n", player->id, player->global_rank));
else
this->sendData(Jupiter::StringS::Format("xset_rank%c%d%c%d\n", RenX::DelimC, player->id, RenX::DelimC, player->global_rank));
}
for (size_t i = 0; i < xPlugins.size(); i++)
xPlugins.get(i)->RenX_OnIDChange(this, player, oldID);
}
@ -2580,6 +2699,10 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
{
// User
Jupiter::ReferenceString user = tokens.getToken(2);
if (user.equals(this->rconUser))
this->subscribed = true;
for (size_t i = 0; i < xPlugins.size(); i++)
xPlugins.get(i)->RenX_OnSubscribe(this, user);
}
@ -2587,6 +2710,10 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
{
// User
Jupiter::ReferenceString user = tokens.getToken(2);
if (user.equals(this->rconUser))
this->subscribed = false;
for (size_t i = 0; i < xPlugins.size(); i++)
xPlugins.get(i)->RenX_OnUnsubscribe(this, user);
}
@ -3045,6 +3172,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
RenX::Server::send("rotation"_jrs);
RenX::Server::fetchClientList();
RenX::Server::updateBuildingList();
RenX::Server::send("ping srv_init_done"_jrs);
RenX::Server::gameStart = std::chrono::steady_clock::now();
this->seenStart = false;
@ -3087,13 +3215,14 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
void RenX::Server::disconnect(RenX::DisconnectReason reason)
{
Jupiter::ArrayList<RenX::Plugin> xPlugins;
RenX::Server::connected = false;
Jupiter::ArrayList<RenX::Plugin> &xPlugins = *RenX::getCore()->getPlugins();
for (size_t i = 0; i < xPlugins.size(); i++)
xPlugins.get(i)->RenX_OnServerDisconnect(this, reason);
RenX::Server::sock.close();
RenX::Server::wipeData();
RenX::Server::connected = false;
}
bool RenX::Server::connect()
@ -3137,6 +3266,8 @@ void RenX::Server::wipeData()
delete player;
}
RenX::Server::subscribed = false;
RenX::Server::fully_connected = false;
RenX::Server::bot_count = 0;
RenX::Server::player_rdns_resolutions_pending = 0;
RenX::Server::buildings.emptyAndDelete();

43
RenX.Core/RenX_Server.h

@ -97,6 +97,21 @@ namespace RenX
*/
bool isConnected() const;
/**
* @brief Checks if the server is currently subscribed to.
*
* @return True if the server is subscribed, false otherwise.
*/
bool isSubscribed() const;
/**
* @brief Checks if the server has been fully connected to.
* Note: This is true after all of the initial commands have received a response, after isSubscribed() is true.
*
* @return True if the server is fully connected, false otherwise.
*/
bool isFullyConnected() const;
/**
* @brief Checks if a map start event has fired.
*
@ -699,12 +714,38 @@ namespace RenX
*/
std::chrono::milliseconds getDelay() const;
/**
server->getMineLimit(),
json_bool_as_cstring(server->isSteamRequired()),
json_bool_as_cstring(server->isPrivateMessageTeamOnly()),
json_bool_as_cstring(server->isPassworded()),
json_bool_as_cstring(server->isPrivateMessagingEnabled()),
server->getPlayerLimit(),
server->getVehicleLimit(),
json_bool_as_cstring(server->isAutoBalanceTeams()),
json_bool_as_cstring(server->isCratesEnabled()),
server->getCrateRespawnDelay(),
server->getTimeLimit(),
*/
int getMineLimit() const;
int getPlayerLimit() const;
int getVehicleLimit() const;
int getTimeLimit() const;
double getCrateRespawnDelay() const;
bool isSteamRequired() const;
bool isPrivateMessageTeamOnly() const;
bool isPrivateMessagingEnabled() const;
/**
* @brief Checks if the server has a game password.
*
* @return True if the game is passworded, false otherwise.
*/
bool isPassworded() const;
bool isAutoBalanceEnabled() const;
bool isCratesEnabled() const;
/**
* @brief Fetches the RCON password of a server.
@ -952,6 +993,8 @@ namespace RenX
bool gameover_pending = false;
bool pure = false;
bool connected = false;
bool subscribed = false;
bool fully_connected = false;
bool seamless = false;
bool needsCList = false;
bool silenceParts = false;

63
RenX.Core/RenX_TeamInfo.h

@ -0,0 +1,63 @@
/**
* Copyright (C) 2016 Jessica James.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Written by Jessica James <jessica.aj@outlook.com>
*/
#if !defined _RENX_TEAMINFO_H_HEADER
#define _RENX_TEAMINFO_H_HEADER
/**
* @file RenX_BuildingInfo.h
* @brief Defines the BuildingInfo structure.
*/
#include "Jupiter/String.h"
#include "Jupiter/INIFile.h"
#include "RenX.h"
/** DLL Linkage Nagging */
#if defined _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4251)
#endif
namespace RenX
{
/**
* @brief Includes all of the tracked information about a team.
*/
struct RENX_API TeamInfo
{
uint8_t id;
int32_t score;
int32_t kills;
int32_t deaths;
int32_t mine_count;
int32_t mine_limit;
int32_t vehicle_count;
int32_t vehicle_limit;
Jupiter::StringS name;
};
}
/** Re-enable warnings */
#if defined _MSC_VER
#pragma warning(pop)
#endif
#endif // _RENX_TEAMINFO_H_HEADER

7
RenX.ModSystem/RenX_ModSystem.cpp

@ -176,7 +176,12 @@ int RenX_ModSystemPlugin::auth(RenX::Server *server, const RenX::PlayerInfo *pla
{
server->sendMessage(player, Jupiter::StringS::Format("You are now authenticated with access level %d; group: %.*s.", player->access, group->name.size(), group->name.ptr()));
if (server->isDevBot())
server->sendData(Jupiter::StringS::Format("d%d\n", player->id));
{
if (server->getVersion() >= 4)
server->sendData(Jupiter::StringS::Format("xset_dev %d\n", player->id));
else
server->sendData(Jupiter::StringS::Format("xset_dev%c%d\n", RenX::DelimC, player->id));
}
}
Jupiter::String playerName = RenX::getFormattedPlayerName(player);
server->sendLogChan(IRCCOLOR "03[Authentication] " IRCBOLD "%.*s" IRCBOLD IRCCOLOR " is now authenticated with access level %d; group: %.*s.", playerName.size(), playerName.ptr(), player->access, group->name.size(), group->name.ptr());

86
RenX.ServerList/RenX.ServerList.vcxproj

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6B0D59BA-B153-4DE8-8DD4-FBE5D810B033}</ProjectGuid>
<RootNamespace>RenX.ServerList</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\Plugins\</OutDir>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>../Bot;../Jupiter;../RenX.Core;../HTTPServer</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="RenX_ServerList.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="RenX_ServerList.cpp" />
</ItemGroup>
<ItemGroup>
<Library Include="..\Jupiter\Release\Jupiter.lib" />
<Library Include="..\Release\Bot.lib" />
<Library Include="..\Release\Plugins\HTTPServer.lib" />
<Library Include="..\Release\Plugins\RenX.Core.lib" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

41
RenX.ServerList/RenX.ServerList.vcxproj.filters

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="RenX_ServerList.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="RenX_ServerList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Library Include="..\Release\Plugins\RenX.Core.lib">
<Filter>Resource Files</Filter>
</Library>
<Library Include="..\Release\Bot.lib">
<Filter>Resource Files</Filter>
</Library>
<Library Include="..\Jupiter\Release\Jupiter.lib">
<Filter>Resource Files</Filter>
</Library>
<Library Include="..\Release\Plugins\HTTPServer.lib">
<Filter>Resource Files</Filter>
</Library>
</ItemGroup>
</Project>

487
RenX.ServerList/RenX_ServerList.cpp

@ -0,0 +1,487 @@
/**
* Copyright (C) 2016 Jessica James.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Written by Jessica James <jessica.aj@outlook.com>
*/
#include "Jupiter/IRC_Client.h"
#include "Jupiter/INIFile.h"
#include "Jupiter/HTTP.h"
#include "Jupiter/HTTP_QueryString.h"
#include "HTTPServer.h"
#include "RenX_Core.h"
#include "RenX_Server.h"
#include "RenX_ServerList.h"
#include "RenX_Functions.h"
using namespace Jupiter::literals;
static STRING_LITERAL_AS_NAMED_REFERENCE(CONTENT_TYPE_APPLICATION_JSON, "application/json");
Jupiter::String jsonify(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 += Jupiter::StringS::Format("\\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;
}
RenX_ServerListPlugin::RenX_ServerListPlugin()
{
RenX_ServerListPlugin::web_hostname = Jupiter::IRC::Client::Config->get(this->name, "Hostname"_jrs, ""_jrs);
RenX_ServerListPlugin::web_path = Jupiter::IRC::Client::Config->get(this->name, "Path"_jrs, "/"_jrs);
RenX_ServerListPlugin::server_list_page_name = Jupiter::IRC::Client::Config->get(this->name, "ServersPageName"_jrs, "servers.json"_jrs);
RenX_ServerListPlugin::server_page_name = Jupiter::IRC::Client::Config->get(this->name, "ServerPageName"_jrs, "server.json"_jrs);
/** Initialize content */
Jupiter::HTTP::Server &server = getHTTPServer();
// Server list page
Jupiter::HTTP::Server::Content *content = new Jupiter::HTTP::Server::Content(RenX_ServerListPlugin::server_list_page_name, handle_server_list_page);
content->language = &Jupiter::HTTP::Content::Language::ENGLISH;
content->type = &CONTENT_TYPE_APPLICATION_JSON;
content->charset = &Jupiter::HTTP::Content::Type::Text::Charset::UTF8;
content->free_result = false;
server.hook(RenX_ServerListPlugin::web_hostname, RenX_ServerListPlugin::web_path, content);
// Server page (GUIDs)
content = new Jupiter::HTTP::Server::Content(RenX_ServerListPlugin::server_page_name, handle_server_page);
content->language = &Jupiter::HTTP::Content::Language::ENGLISH;
content->type = &CONTENT_TYPE_APPLICATION_JSON;
content->charset = &Jupiter::HTTP::Content::Type::Text::Charset::UTF8;
content->free_result = true;
server.hook(RenX_ServerListPlugin::web_hostname, RenX_ServerListPlugin::web_path, content);
this->updateServerList();
}
RenX_ServerListPlugin::~RenX_ServerListPlugin()
{
Jupiter::HTTP::Server &server = getHTTPServer();
server.remove(RenX_ServerListPlugin::web_hostname, RenX_ServerListPlugin::web_path, RenX_ServerListPlugin::server_list_page_name);
server.remove(RenX_ServerListPlugin::web_hostname, RenX_ServerListPlugin::web_path, RenX_ServerListPlugin::server_page_name);
}
Jupiter::ReadableString *RenX_ServerListPlugin::getServerListJSON()
{
return std::addressof(RenX_ServerListPlugin::server_list_json);
}
const char *json_bool_as_cstring(bool in)
{
if (in)
return "true";
return "false";
}
Jupiter::StringS server_as_json(const RenX::Server *server)
{
Jupiter::String server_json_block(128);
Jupiter::String server_name = jsonify(server->getName());
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_name.size(), server_name.ptr(),
server_map.size(), server_map.ptr(),
server->getBotCount(),
server->players.size() - server->getBotCount(),
server_version.size(), server_version.ptr(),
server->getMineLimit(),
json_bool_as_cstring(server->isSteamRequired()),
json_bool_as_cstring(server->isPrivateMessageTeamOnly()),
json_bool_as_cstring(server->isPassworded()),
json_bool_as_cstring(server->isPrivateMessagingEnabled()),
server->getPlayerLimit(),
server->getVehicleLimit(),
json_bool_as_cstring(server->isAutoBalanceEnabled()),
json_bool_as_cstring(server->isCratesEnabled()),
server->getCrateRespawnDelay(),
server->getTimeLimit(),
server->getPort(),
server->getSocketHostname().size(), server->getSocketHostname().ptr());
// Level Rotation
/*if (server->maps.size() != 0)
{
server_json_block += ",\"Levels\":["_jrs;
server_json_block += "{\"Name\":\""_jrs;
server_json_block += server->maps.get(0)->name;
server_json_block += "\",\"GUID\":\""_jrs;
server_json_block += RenX::formatGUID(*server->maps.get(0));
server_json_block += "\"}"_jrs;
for (size_t index = 1; index != server->maps.size(); ++index)
{
server_json_block += ",{\"Name\":\""_jrs;
server_json_block += server->maps.get(index)->name;
server_json_block += "\",\"GUID\":\""_jrs;
server_json_block += RenX::formatGUID(*server->maps.get(index));
server_json_block += "\"}"_jrs;
}
server_json_block += "]"_jrs;
}
// Mutators
if (server->mutators.size() != 0)
{
server_json_block += ",\"Mutators\": ["_jrs;
server_json_block += "{\"Name\":\""_jrs;
server_json_block += *server->mutators.get(0);
server_json_block += "\"}"_jrs;
for (size_t index = 1; index != server->mutators.size(); ++index)
{
server_json_block += ",{\"Name\":\""_jrs;
server_json_block += *server->mutators.get(index);
server_json_block += "\"}"_jrs;
}
server_json_block += "]"_jrs;
}*/
server_json_block += "}"_jrs;
return server_json_block;
}
Jupiter::StringS server_as_hr_json(const RenX::Server *server)
{
Jupiter::String server_json_block(128);
Jupiter::String server_name = jsonify(server->getName());
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_name.size(), server_name.ptr(),
server_map.size(), server_map.ptr(),
server->getBotCount(),
server->players.size() - server->getBotCount(),
server_version.size(), server_version.ptr(),
server->getMineLimit(),
json_bool_as_cstring(server->isSteamRequired()),
json_bool_as_cstring(server->isPrivateMessageTeamOnly()),
json_bool_as_cstring(server->isPassworded()),
json_bool_as_cstring(server->isPrivateMessagingEnabled()),
server->getPlayerLimit(),
server->getVehicleLimit(),
json_bool_as_cstring(server->isAutoBalanceEnabled()),
json_bool_as_cstring(server->isCratesEnabled()),
server->getCrateRespawnDelay(),
server->getTimeLimit(),
server->getPort(),
server->getSocketHostname().size(), server->getSocketHostname().ptr());
// Level Rotation
if (server->maps.size() != 0)
{
server_json_block += ",\n\t\t\"Levels\": ["_jrs;
server_json_block += "\n\t\t\t{\n\t\t\t\t\"Name\": \""_jrs;
server_json_block += server->maps.get(0)->name;
server_json_block += "\",\n\t\t\t\t\"GUID\": \""_jrs;
server_json_block += RenX::formatGUID(*server->maps.get(0));
server_json_block += "\"\n\t\t\t}"_jrs;
for (size_t index = 1; index != server->maps.size(); ++index)
{
server_json_block += ",\n\t\t\t{\n\t\t\t\t\"Name\": \""_jrs;
server_json_block += server->maps.get(index)->name;
server_json_block += "\",\n\t\t\t\t\"GUID\": \""_jrs;
server_json_block += RenX::formatGUID(*server->maps.get(index));
server_json_block += "\"\n\t\t\t}"_jrs;
}
server_json_block += "\n\t\t]"_jrs;
}
// Mutators
if (server->mutators.size() != 0)
{
server_json_block += ",\n\t\t\"Mutators\": ["_jrs;
server_json_block += "\n\t\t\t{\n\t\t\t\t\"Name\": \""_jrs;
server_json_block += *server->mutators.get(0);
server_json_block += "\"\n\t\t\t}"_jrs;
for (size_t index = 1; index != server->mutators.size(); ++index)
{
server_json_block += ",\n\t\t\t{\n\t\t\t\t\"Name\": \""_jrs;
server_json_block += *server->mutators.get(index);
server_json_block += "\"\n\t\t\t}"_jrs;
}
server_json_block += "\n\t\t]"_jrs;
}
server_json_block += "\n\t}"_jrs;
return server_json_block;
}
void RenX_ServerListPlugin::addServerToServerList(const RenX::Server *server)
{
// append to server_list_json
if (RenX_ServerListPlugin::server_list_json.isEmpty())
{
RenX_ServerListPlugin::server_list_json = '[';
RenX_ServerListPlugin::server_list_json += server_as_json(server);
RenX_ServerListPlugin::server_list_json += ']';
}
else
{
RenX_ServerListPlugin::server_list_json.truncate(1); // remove trailing ']'.
RenX_ServerListPlugin::server_list_json += ',';
RenX_ServerListPlugin::server_list_json += server_as_json(server);
RenX_ServerListPlugin::server_list_json += ']';
}
}
void RenX_ServerListPlugin::updateServerList()
{
Jupiter::ArrayList<RenX::Server> servers = RenX::getCore()->getServers();
size_t index = 0;
RenX::Server *server;
// regenerate server_list_json
RenX_ServerListPlugin::server_list_json = '[';
while (index != servers.size())
{
server = servers.get(index);
if (server->isConnected() && server->isFullyConnected())
{
RenX_ServerListPlugin::server_list_json += server_as_json(server);
++index;
break;
}
++index;
}
while (index != servers.size())
{
server = servers.get(index);
if (server->isConnected() && server->isFullyConnected())
{
RenX_ServerListPlugin::server_list_json += ',';
RenX_ServerListPlugin::server_list_json += server_as_json(server);
}
++index;
}
RenX_ServerListPlugin::server_list_json += ']';
}
void RenX_ServerListPlugin::RenX_OnServerFullyConnected(RenX::Server *server)
{
Jupiter::String server_json_block(256);
this->addServerToServerList(server);
// add to individual listing
server_json_block = '{';
if (server->maps.size() != 0)
{
server_json_block += ",\"Levels\":["_jrs;
server_json_block += "{\"Name\":\""_jrs;
server_json_block += jsonify(server->maps.get(0)->name);
server_json_block += "\",\"GUID\":\""_jrs;
server_json_block += RenX::formatGUID(*server->maps.get(0));
server_json_block += "\"}"_jrs;
for (size_t index = 1; index != server->maps.size(); ++index)
{
server_json_block += ",{\"Name\":\""_jrs;
server_json_block += jsonify(server->maps.get(index)->name);
server_json_block += "\",\"GUID\":\""_jrs;
server_json_block += RenX::formatGUID(*server->maps.get(index));
server_json_block += "\"}"_jrs;
}
server_json_block += ']';
}
// Mutators
if (server->mutators.size() != 0)
{
server_json_block += ",\"Mutators\":["_jrs;
server_json_block += "{\"Name\":\""_jrs;
server_json_block += jsonify(*server->mutators.get(0));
server_json_block += "\"}"_jrs;
for (size_t index = 1; index != server->mutators.size(); ++index)
{
server_json_block += ",{\"Name\":\""_jrs;
server_json_block += jsonify(*server->mutators.get(index));
server_json_block += "\"}"_jrs;
}
server_json_block += ']';
}
server_json_block += '}';
server->varData.set(this->name, "j"_jrs, server_json_block);
}
void RenX_ServerListPlugin::RenX_OnServerDisconnect(RenX::Server *server, RenX::DisconnectReason)
{
this->updateServerList();
// remove from individual listing
server->varData.remove(this->name, "j"_jrs);
}
void RenX_ServerListPlugin::RenX_OnJoin(RenX::Server *, const RenX::PlayerInfo *)
{
this->updateServerList();
}
void RenX_ServerListPlugin::RenX_OnPart(RenX::Server *, const RenX::PlayerInfo *)
{
this->updateServerList();
}
// Plugin instantiation and entry point.
RenX_ServerListPlugin pluginInstance;
Jupiter::ReadableString *handle_server_list_page(const Jupiter::ReadableString &)
{
return pluginInstance.getServerListJSON();
}
Jupiter::ReadableString *handle_server_page(const Jupiter::ReadableString &query_string)
{
Jupiter::HTTP::HTMLFormResponse html_form_response(query_string);
Jupiter::ReferenceString address;
int port = 0;
RenX::Server *server;
// parse form data
if (html_form_response.table.size() < 2)
return new Jupiter::ReferenceString();
if (html_form_response.table.size() != 0)
{
address = html_form_response.table.get("ip"_jrs, address);
port = html_form_response.table.getInt("port"_jrs, port);
}
// search for server
Jupiter::ArrayList<RenX::Server> servers = RenX::getCore()->getServers();
size_t index = 0;
while (true)
{
if (index == servers.size())
return new Jupiter::ReferenceString();
server = servers.get(index);
if (server->getSocketHostname().equals(address) && server->getPort() == port)
break;
++index;
}
// return server data
return new Jupiter::ReferenceString(server->varData.get(pluginInstance.getName(), "j"_jrs));
}
extern "C" __declspec(dllexport) Jupiter::Plugin *getPlugin()
{
return &pluginInstance;
}

57
RenX.ServerList/RenX_ServerList.h

@ -0,0 +1,57 @@
/**
* Copyright (C) 2016 Jessica James.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Written by Jessica James <jessica.aj@outlook.com>
*/
#if !defined _RENX_SERVERLIST_H_HEADER
#define _RENX_SERVERLIST_H_HEADER
#include "Jupiter/Plugin.h"
#include "Jupiter/Reference_String.h"
#include "RenX_Plugin.h"
class RenX_ServerListPlugin : public RenX::Plugin
{
public: // RenX_ServerListPlugin
Jupiter::ReadableString *getServerListJSON();
void addServerToServerList(const RenX::Server *server);
void updateServerList();
RenX_ServerListPlugin();
~RenX_ServerListPlugin();
public: // RenX::Plugin
void RenX_OnServerFullyConnected(RenX::Server *server) override;
void RenX_OnServerDisconnect(RenX::Server *server, RenX::DisconnectReason reason) override;
void RenX_OnJoin(RenX::Server *server, const RenX::PlayerInfo *player) override;
void RenX_OnPart(RenX::Server *server, const RenX::PlayerInfo *player) override;
public: // Jupiter::Plugin
const Jupiter::ReadableString &getName() override { return name; }
private:
STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX.ServerList");
Jupiter::StringS server_list_json;
Jupiter::StringS web_hostname, web_path, server_list_page_name, server_page_name;
};
Jupiter::ReadableString *handle_server_list_page(const Jupiter::ReadableString &);
Jupiter::ReadableString *handle_server_page(const Jupiter::ReadableString &query_string);
#endif // _RENX_SERVERLIST_H_HEADER
Loading…
Cancel
Save