diff --git a/Release/Bot.lib b/Release/Bot.lib index 8c36025..1ff74ab 100644 Binary files a/Release/Bot.lib and b/Release/Bot.lib differ diff --git a/Release/Plugins/RenX.Core.lib b/Release/Plugins/RenX.Core.lib index 9d0a105..5eaf3db 100644 Binary files a/Release/Plugins/RenX.Core.lib and b/Release/Plugins/RenX.Core.lib differ diff --git a/RenX.Core/RenX_Core.cpp b/RenX.Core/RenX_Core.cpp index d47bc05..b9c6b99 100644 --- a/RenX.Core/RenX_Core.cpp +++ b/RenX.Core/RenX_Core.cpp @@ -146,463 +146,14 @@ int RenX::Core::addCommand(RenX::GameCommand *command) return RenX::Core::servers.size(); } -// This shouldn't be needed later. -/*#define PARSE_PLAYER_DATA(STRING, OFFSET) \ - char *name = nullptr; int id; char team; \ - const char *p = strstr(buff, STRING); \ - if (p - buff - OFFSET < 12) { } \ - team = *(p - 3); \ - if (*(p - 9) == '<') id = atoi(p - 8); \ - else id = atoi(p - 9); \ - name = charToChar(buff + OFFSET + 1, 0, (int)(p - 10 - (buff + OFFSET + 1)));*/ - -#define PARSE_PLAYER_DATA() \ - Jupiter::ReferenceString name; \ - TeamType team; \ - int id; \ - bool isBot = false; { \ - Jupiter::ReferenceString idToken; \ - if (playerData[0] == ',') { \ - name = playerData.gotoWord(1, ","); \ - idToken = playerData.getWord(0, ","); \ - team = Other; \ - } else { \ - name = playerData.gotoWord(2, ","); \ - idToken = playerData.getWord(1, ","); \ - team = RenX::getTeam(playerData[0]); \ - } \ - if (idToken[0] == 'b') { idToken.shiftRight(1); isBot = true; } \ - id = idToken.asInt(10); } \ - RenX::PlayerInfo *player = getPlayerOrAdd(server, name, id, team, isBot); - -inline RenX::PlayerInfo *getPlayerOrAdd(RenX::Server *server, const Jupiter::ReadableString &name, int id, RenX::TeamType team, bool isBot) -{ - RenX::PlayerInfo *r = server->getPlayer(id); - if (r == nullptr) - { - r = new RenX::PlayerInfo(); - r->id = id; - r->name = name; - r->isBot = isBot; - r->joinTime = time(nullptr); - if (id != 0) server->players.add(r); - } - else if (r->name.size() == 0) r->name = name; - r->team = team; - return r; -} - -inline void onPreGameOver(RenX::Server *server, RenX::WinType winType, RenX::TeamType team, int gScore, int nScore) -{ - RenX::PlayerInfo *player; - - if (server->players.size() != 0) - { - for (Jupiter::DLList::Node *n = server->players.getNode(0); n != nullptr; n = n->next) - { - player = n->data; - if (player != nullptr) - { - if (player->team == team) - player->wins++; - else player->loses++; - } - } - } -} - -inline void onPostGameOver(RenX::Server *server, RenX::WinType winType, RenX::TeamType team, int gScore, int nScore) -{ - RenX::PlayerInfo *player; - - if (server->players.size() != 0) - { - for (Jupiter::DLList::Node *n = server->players.getNode(0); n != nullptr; n = n->next) - { - player = n->data; - if (player != nullptr) - { - player->kills = 0; - player->deaths = 0; - player->suicides = 0; - player->headshots = 0; - player->vehicleKills = 0; - player->buildingKills = 0; - player->defenceKills = 0; - } - } - } -} - -inline void onChat(RenX::Server *server, RenX::PlayerInfo *player, const Jupiter::ReadableString &message, bool isPublic) -{ - const Jupiter::ReadableString &prefix = server->getCommandPrefix(); - if (message.find(prefix) == 0 && message.size() != prefix.size()) - { - Jupiter::ReferenceString command; - Jupiter::ReferenceString parameters; - if (containsSymbol(WHITESPACE, message.get(prefix.size()))) - { - command = Jupiter::ReferenceString::getWord(message, 1, WHITESPACE); - parameters = Jupiter::ReferenceString::gotoWord(message, 2, WHITESPACE); - } - else - { - command = Jupiter::ReferenceString::getWord(message, 0, WHITESPACE); - command.shiftRight(prefix.size()); - parameters = Jupiter::ReferenceString::gotoWord(message, 1, WHITESPACE); - } - server->triggerCommand(command, player, parameters); - } -} - int RenX::Core::think() { - // Change this later to just call server->think(). - RenX::Server *server; - for (size_t a = 0; a < RenX::Core::servers.size(); a++) - { - server = RenX::Core::servers.get(a); - if (server->isConnected() == false) - { - if (time(0) >= server->getLastAttempt() + server->getDelay()) - { - if (server->connect()) server->sendLogChan(IRCCOLOR "03[RenX]" IRCCOLOR " Socket successfully reconnected to Renegade-X server."); - else server->sendLogChan(IRCCOLOR "04[Error]" IRCCOLOR " Failed to reconnect to Renegade-X server."); - } - } - else - { - if (server->sock.recv() > 0) - { - Jupiter::ReferenceString buffer = server->sock.getBuffer(); - unsigned int totalLines = buffer.wordCount(ENDL); - for (unsigned int currentLine = 0; currentLine != totalLines; currentLine++) - { - Jupiter::ReferenceString buff = buffer.getWord(currentLine, ENDL); - Jupiter::ReferenceString header = buff.getWord(0, RenX::DelimS); - Jupiter::ReferenceString playerData = buff.getWord(1, RenX::DelimS); - Jupiter::ReferenceString action = buff.getWord(2, RenX::DelimS); - - //printf("[RenX Dump] %.*s - %.*s - %.*s" ENDL, header.size(), header.ptr(), playerData.size(), playerData.ptr(), action.size(), action.ptr()); + size_t a = 0; + while (a < RenX::Core::servers.size()) + if (RenX::Core::servers.get(a)->think() != 0) + delete RenX::Core::servers.remove(a); + else a++; - if (buff.size() != 0) - { - switch (header[0]) - { - case 'l': - if (header.equals("lGAME:")) - { - if (action.equals("deployed")) - { - PARSE_PLAYER_DATA(); - Jupiter::ReferenceString objectType = buff.getWord(3, RenX::DelimS); - if (objectType.match("*Beacon")) player->beaconPlacements++; - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnDeploy(server, player, objectType); - } - else if (action.equals("suicided by")) - { - PARSE_PLAYER_DATA(); - player->deaths++; - player->suicides++; - Jupiter::ReferenceString damageType = buff.getWord(3, RenX::DelimS); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnSuicide(server, player, damageType); - } - else if (action.equals("killed")) - { - PARSE_PLAYER_DATA(); - Jupiter::ReferenceString victimData = buff.getWord(3, RenX::DelimS); - Jupiter::ReferenceString vname = victimData.getWord(2, ","); - Jupiter::ReferenceString vidToken = victimData.getWord(1, ","); - int vid; - bool visBot = false; - if (vidToken[0] == 'b') - { - vidToken.shiftRight(1); - visBot = true; - } - vid = vidToken.asInt(10); - TeamType vteam = RenX::getTeam(victimData.getWord(0, ",")[0]); - Jupiter::ReferenceString damageType = buff.getWord(5, RenX::DelimS); - RenX::PlayerInfo *victim = getPlayerOrAdd(server, vname, vid, vteam, visBot); - player->kills++; - if (damageType.equals("Rx_DmgType_Headshot")) player->headshots++; - victim->deaths++; - - if (server->needsCList) - { - server->sendData(STRING_LITERAL_AS_REFERENCE("clogclientlist\n")); - server->needsCList = false; - } - - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnKill(server, player, victim, damageType); - } - else if (action.match("died by")) - { - PARSE_PLAYER_DATA(); - player->deaths++; - Jupiter::ReferenceString damageType = buff.getWord(3, RenX::DelimS); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnDie(server, player, damageType); - } - else if (action.match("destroyed*")) - { - PARSE_PLAYER_DATA(); - Jupiter::ReferenceString victim = buff.getWord(3, RenX::DelimS); - Jupiter::ReferenceString damageType = buff.getWord(5, RenX::DelimS); - ObjectType type; - if (action.equals("destroyed building")) - { - type = Building; - player->buildingKills++; - } - else if (victim.match("Rx_Defence_*")) - { - type = Defence; - player->defenceKills++; - } - else - { - type = Vehicle; - player->vehicleKills++; - } - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnDestroy(server, player, victim, damageType, type); - } - else if (playerData.match("??? wins (*)")) - { - TeamType team = RenX::getTeam(playerData[0]); - int gScore = buff.getWord(2, RenX::DelimS).gotoWord(1, "=").asInt(10); - int nScore = buff.getWord(3, RenX::DelimS).gotoWord(1, "=").asInt(10); - Jupiter::ReferenceString winType = Jupiter::ReferenceString::substring(playerData, 10); - winType.truncate(1); - WinType iWinType = Unknown; - if (gScore == nScore) - iWinType = Tie; - else if (winType.equals("TimeLimit")) - iWinType = Score; - else if (winType.equals("Buildings")) - iWinType = Base; - - server->needsCList = true; - onPreGameOver(server, iWinType, team, gScore, nScore); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnGameOver(server, iWinType, team, gScore, nScore); - onPostGameOver(server, iWinType, team, gScore, nScore); - } - else if (playerData.equals("Tie")) - { - int gScore = action.gotoWord(1, "=").asInt(10); - int nScore = buff.getWord(3, RenX::DelimS).gotoWord(1, "=").asInt(10); - - server->needsCList = true; - if (gScore == nScore) - { - onPreGameOver(server, Tie, Other, gScore, nScore); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnGameOver(server, Tie, Other, gScore, nScore); - onPostGameOver(server, Tie, Other, gScore, nScore); - } - else - { - TeamType winTeam = gScore > nScore ? RenX::getTeam('G') : RenX::getTeam('N'); - onPreGameOver(server, Shutdown, winTeam, gScore, nScore); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnGameOver(server, Shutdown, winTeam, gScore, nScore); - onPostGameOver(server, Shutdown, winTeam, gScore, nScore); - } - } - else for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnGame(server, buff.gotoWord(1, RenX::DelimS)); - } - else if (header.equals("lCHAT:")) - { - if (action.equals("teamsay:")) - { - PARSE_PLAYER_DATA(); - Jupiter::ReferenceString message = buff.getWord(3, RenX::DelimS); - onChat(server, player, message, false); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnTeamChat(server, player, message); - } - else if (action.equals("say:")) - { - PARSE_PLAYER_DATA(); - Jupiter::ReferenceString message = buff.getWord(3, RenX::DelimS); - onChat(server, player, message, true); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnChat(server, player, message); - } - } - else if (header.equals("lPLAYER:")) - { - PARSE_PLAYER_DATA(); - if (action.equals("disconnected")) - { - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnPart(server, player); - server->removePlayer(player); - player = nullptr; - } - else if (action.equals("entered from")) - { - player->ip = buff.getWord(3, RenX::DelimS); - if (buff.getWord(4, RenX::DelimS).equals("steamid")) player->steamid = buff.getWord(5, RenX::DelimS); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnJoin(server, player); - } - else if (action.equals("changed name to:")) - { - Jupiter::ReferenceString newName = buff.getWord(3, RenX::DelimS); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnNameChange(server, player, newName); - player->name = newName; - } - } - else if (header.equals("lRCON:")) - { - if (action.equals("executed:")) - { - Jupiter::ReferenceString command = buff.getWord(3, RenX::DelimS); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnExecute(server, playerData, command); - } - else if (action.equals("subscribed")) for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnSubscribe(server, playerData); - else for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnRCON(server, buff.gotoWord(1, RenX::DelimS)); - } - else if (header.equals("lADMIN:")) - { - PARSE_PLAYER_DATA(); - if (action.equals("logged in as")) - { - player->adminType = buff.getWord(3, RenX::DelimS); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnAdminLogin(server, player); - } - else if (action.equals("logged out of")) - { - Jupiter::ReferenceString type = buff.getWord(3, RenX::DelimS); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnAdminLogout(server, player); - player->adminType = ""; - } - else for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnAdmin(server, buff.gotoWord(1, RenX::DelimS)); - } - else if (header.equals("lC-LIST:")) - { - // ID IP SteamID Team Name - if (playerData.isEmpty()) - break; - - int id; - bool isBot = false; - if (playerData.get(0) == 'b') - { - isBot = true; - playerData.shiftRight(1); - id = playerData.asInt(10); - playerData.shiftLeft(1); - } - else id = playerData.asInt(10); - Jupiter::ReferenceString ip = playerData.getWord(1, WHITESPACE); - Jupiter::ReferenceString steamid = playerData.getWord(2, WHITESPACE); - RenX::TeamType team; - Jupiter::ReferenceString name; - if (steamid.equals("-----NO")) // RCONv2-2a - { - steamid = ""; - Jupiter::ReferenceString &teamToken = playerData.getWord(4, WHITESPACE); - if (teamToken.isEmpty()) - break; - team = getTeam(teamToken.get(0)); - name = playerData.gotoWord(5, WHITESPACE); - } - else - { - if (steamid.equals("-----NO-STEAM-----")) // RCONv2-2.5a - steamid = ""; - Jupiter::ReferenceString &teamToken = playerData.getWord(3, WHITESPACE); - if (teamToken.isEmpty()) - break; - team = getTeam(teamToken.get(0)); - name = playerData.gotoWord(4, WHITESPACE); - } - - RenX::PlayerInfo *player = getPlayerOrAdd(server, name, id, team, isBot); - if (player->ip.size() == 0) - { - player->ip = ip; - player->steamid = steamid; - } - } - else - { - buff.shiftRight(1); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnLog(server, buff); - buff.shiftLeft(1); - } - break; - - case 'c': - buff.shiftRight(1); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnCommand(server, buff); - buff.shiftLeft(1); - break; - - case 'e': - buff.shiftRight(1); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnError(server, buff); - buff.shiftLeft(1); - break; - - case 'v': - buff.shiftRight(1); - server->rconVersion = buff.asInt(10); - server->gameVersion = buff.substring(3); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnVersion(server, buff); - buff.shiftLeft(1); - break; - - case 'a': - buff.shiftRight(1); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnAuthorized(server, buff); - buff.shiftLeft(1); - break; - - default: - buff.shiftRight(1); - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnOther(server, header[0], buff); - buff.shiftLeft(1); - break; - } - for (size_t i = 0; i < RenX::Core::plugins.size(); i++) - RenX::Core::plugins.get(i)->RenX_OnRaw(server, buff); - } - } - } - else if (Jupiter::Socket::getLastError() != 10035) // This is a serious error - { - if (server->reconnect()) server->sendLogChan(IRCCOLOR "07[Warning]" IRCCOLOR " Connection lost to Renegade-X server lost. Reconnection attempt in progress."); - else - { - server->wipeData(); - server->sendLogChan(IRCCOLOR "04[Error]" IRCCOLOR " Connection lost to Renegade-X server lost. Reconnection attempt failed."); - } - } - } - } return Jupiter::Plugin::think(); } diff --git a/RenX.Core/RenX_Core.h b/RenX.Core/RenX_Core.h index 67f481d..cdbd674 100644 --- a/RenX.Core/RenX_Core.h +++ b/RenX.Core/RenX_Core.h @@ -51,7 +51,7 @@ namespace RenX public: /** - * @brief Loops through the Renegade-X servers and parses data. + * @brief Loops through the Renegade-X servers. * This should only be called by main(). * * @return Result of Jupiter::Plugin::Think(). diff --git a/RenX.Core/RenX_Server.cpp b/RenX.Core/RenX_Server.cpp index 4ed362f..c09669b 100644 --- a/RenX.Core/RenX_Server.cpp +++ b/RenX.Core/RenX_Server.cpp @@ -24,6 +24,49 @@ #include "RenX_PlayerInfo.h" #include "RenX_GameCommand.h" #include "RenX_Functions.h" +#include "RenX_Plugin.h" + +int RenX::Server::think() +{ + if (RenX::Server::connected == false) + { + if (time(0) >= RenX::Server::lastAttempt + RenX::Server::delay) + { + if (RenX::Server::connect()) + RenX::Server::sendLogChan(IRCCOLOR "03[RenX]" IRCCOLOR " Socket successfully reconnected to Renegade-X server."); + else RenX::Server::sendLogChan(IRCCOLOR "04[Error]" IRCCOLOR " Failed to reconnect to Renegade-X server."); + } + } + else + { + if (RenX::Server::sock.recv() > 0) + { + Jupiter::ReferenceString buffer = RenX::Server::sock.getBuffer(); + unsigned int totalLines = buffer.tokenCount('\n'); + + RenX::Server::lastLine.concat(buffer.getToken(0, '\n')); + if (totalLines != 0) + { + RenX::Server::processLine(RenX::Server::lastLine); + RenX::Server::lastLine = buffer.getToken(totalLines - 1, '\n'); + + for (unsigned int currentLine = 1; currentLine != totalLines - 1; currentLine++) + RenX::Server::processLine(buffer.getToken(currentLine, '\n')); + } + } + else if (Jupiter::Socket::getLastError() != 10035) // This is a serious error + { + if (RenX::Server::reconnect()) + RenX::Server::sendLogChan(IRCCOLOR "07[Warning]" IRCCOLOR " Connection lost to Renegade-X server lost. Reconnection attempt in progress."); + else + { + RenX::Server::wipeData(); + RenX::Server::sendLogChan(IRCCOLOR "04[Error]" IRCCOLOR " Connection lost to Renegade-X server lost. Reconnection attempt failed."); + } + } + } + return 0; +} bool RenX::Server::isConnected() const { @@ -367,6 +410,419 @@ void RenX::Server::sendLogChan(const char *fmt, ...) const } } +#define PARSE_PLAYER_DATA() \ + Jupiter::ReferenceString name; \ + TeamType team; \ + int id; \ + bool isBot = false; { \ + Jupiter::ReferenceString idToken; \ + if (playerData[0] == ',') { \ + name = playerData.gotoWord(1, ","); \ + idToken = playerData.getWord(0, ","); \ + team = Other; \ + } else { \ + name = playerData.gotoWord(2, ","); \ + idToken = playerData.getWord(1, ","); \ + team = RenX::getTeam(playerData[0]); \ + } \ + if (idToken[0] == 'b') { idToken.shiftRight(1); isBot = true; } \ + id = idToken.asInt(10); } \ + RenX::PlayerInfo *player = getPlayerOrAdd(this, name, id, team, isBot); + +inline RenX::PlayerInfo *getPlayerOrAdd(RenX::Server *server, const Jupiter::ReadableString &name, int id, RenX::TeamType team, bool isBot) +{ + RenX::PlayerInfo *r = server->getPlayer(id); + if (r == nullptr) + { + r = new RenX::PlayerInfo(); + r->id = id; + r->name = name; + r->isBot = isBot; + r->joinTime = time(nullptr); + if (id != 0) server->players.add(r); + } + else if (r->name.size() == 0) r->name = name; + r->team = team; + return r; +} + +inline void onPreGameOver(RenX::Server *server, RenX::WinType winType, RenX::TeamType team, int gScore, int nScore) +{ + RenX::PlayerInfo *player; + + if (server->players.size() != 0) + { + for (Jupiter::DLList::Node *n = server->players.getNode(0); n != nullptr; n = n->next) + { + player = n->data; + if (player != nullptr) + { + if (player->team == team) + player->wins++; + else player->loses++; + } + } + } +} + +inline void onPostGameOver(RenX::Server *server, RenX::WinType winType, RenX::TeamType team, int gScore, int nScore) +{ + RenX::PlayerInfo *player; + + if (server->players.size() != 0) + { + for (Jupiter::DLList::Node *n = server->players.getNode(0); n != nullptr; n = n->next) + { + player = n->data; + if (player != nullptr) + { + player->kills = 0; + player->deaths = 0; + player->suicides = 0; + player->headshots = 0; + player->vehicleKills = 0; + player->buildingKills = 0; + player->defenceKills = 0; + } + } + } +} + +inline void onChat(RenX::Server *server, RenX::PlayerInfo *player, const Jupiter::ReadableString &message, bool isPublic) +{ + const Jupiter::ReadableString &prefix = server->getCommandPrefix(); + if (message.find(prefix) == 0 && message.size() != prefix.size()) + { + Jupiter::ReferenceString command; + Jupiter::ReferenceString parameters; + if (containsSymbol(WHITESPACE, message.get(prefix.size()))) + { + command = Jupiter::ReferenceString::getWord(message, 1, WHITESPACE); + parameters = Jupiter::ReferenceString::gotoWord(message, 2, WHITESPACE); + } + else + { + command = Jupiter::ReferenceString::getWord(message, 0, WHITESPACE); + command.shiftRight(prefix.size()); + parameters = Jupiter::ReferenceString::gotoWord(message, 1, WHITESPACE); + } + server->triggerCommand(command, player, parameters); + } +} + +void RenX::Server::processLine(const Jupiter::ReadableString &line) +{ + Jupiter::ReferenceString buff = line; + Jupiter::ArrayList &xPlugins = *RenX::getCore()->getPlugins(); + Jupiter::ReferenceString header = buff.getWord(0, RenX::DelimS); + Jupiter::ReferenceString playerData = buff.getWord(1, RenX::DelimS); + Jupiter::ReferenceString action = buff.getWord(2, RenX::DelimS); + if (buff.size() != 0) + { + switch (header[0]) + { + case 'l': + if (header.equals("lGAME:")) + { + if (action.equals("deployed")) + { + PARSE_PLAYER_DATA(); + Jupiter::ReferenceString objectType = buff.getWord(3, RenX::DelimS); + if (objectType.match("*Beacon")) player->beaconPlacements++; + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnDeploy(this, player, objectType); + } + else if (action.equals("suicided by")) + { + PARSE_PLAYER_DATA(); + player->deaths++; + player->suicides++; + Jupiter::ReferenceString damageType = buff.getWord(3, RenX::DelimS); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnSuicide(this, player, damageType); + } + else if (action.equals("killed")) + { + PARSE_PLAYER_DATA(); + Jupiter::ReferenceString victimData = buff.getWord(3, RenX::DelimS); + Jupiter::ReferenceString vname = victimData.getWord(2, ","); + Jupiter::ReferenceString vidToken = victimData.getWord(1, ","); + int vid; + bool visBot = false; + if (vidToken[0] == 'b') + { + vidToken.shiftRight(1); + visBot = true; + } + vid = vidToken.asInt(10); + TeamType vteam = RenX::getTeam(victimData.getWord(0, ",")[0]); + Jupiter::ReferenceString damageType = buff.getWord(5, RenX::DelimS); + RenX::PlayerInfo *victim = getPlayerOrAdd(this, vname, vid, vteam, visBot); + player->kills++; + if (damageType.equals("Rx_DmgType_Headshot")) player->headshots++; + victim->deaths++; + + if (this->needsCList) + { + this->sendData(STRING_LITERAL_AS_REFERENCE("clogclientlist\n")); + this->needsCList = false; + } + + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnKill(this, player, victim, damageType); + } + else if (action.match("died by")) + { + PARSE_PLAYER_DATA(); + player->deaths++; + Jupiter::ReferenceString damageType = buff.getWord(3, RenX::DelimS); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnDie(this, player, damageType); + } + else if (action.match("destroyed*")) + { + PARSE_PLAYER_DATA(); + Jupiter::ReferenceString victim = buff.getWord(3, RenX::DelimS); + Jupiter::ReferenceString damageType = buff.getWord(5, RenX::DelimS); + ObjectType type; + if (action.equals("destroyed building")) + { + type = Building; + player->buildingKills++; + } + else if (victim.match("Rx_Defence_*")) + { + type = Defence; + player->defenceKills++; + } + else + { + type = Vehicle; + player->vehicleKills++; + } + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnDestroy(this, player, victim, damageType, type); + } + else if (playerData.match("??? wins (*)")) + { + TeamType team = RenX::getTeam(playerData[0]); + int gScore = buff.getWord(2, RenX::DelimS).gotoWord(1, "=").asInt(10); + int nScore = buff.getWord(3, RenX::DelimS).gotoWord(1, "=").asInt(10); + Jupiter::ReferenceString winType = Jupiter::ReferenceString::substring(playerData, 10); + winType.truncate(1); + WinType iWinType = Unknown; + if (gScore == nScore) + iWinType = Tie; + else if (winType.equals("TimeLimit")) + iWinType = Score; + else if (winType.equals("Buildings")) + iWinType = Base; + + this->needsCList = true; + onPreGameOver(this, iWinType, team, gScore, nScore); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnGameOver(this, iWinType, team, gScore, nScore); + onPostGameOver(this, iWinType, team, gScore, nScore); + } + else if (playerData.equals("Tie")) + { + int gScore = action.gotoWord(1, "=").asInt(10); + int nScore = buff.getWord(3, RenX::DelimS).gotoWord(1, "=").asInt(10); + + this->needsCList = true; + if (gScore == nScore) + { + onPreGameOver(this, Tie, Other, gScore, nScore); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnGameOver(this, Tie, Other, gScore, nScore); + onPostGameOver(this, Tie, Other, gScore, nScore); + } + else + { + TeamType winTeam = gScore > nScore ? RenX::getTeam('G') : RenX::getTeam('N'); + onPreGameOver(this, Shutdown, winTeam, gScore, nScore); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnGameOver(this, Shutdown, winTeam, gScore, nScore); + onPostGameOver(this, Shutdown, winTeam, gScore, nScore); + } + } + else for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnGame(this, buff.gotoWord(1, RenX::DelimS)); + } + else if (header.equals("lCHAT:")) + { + if (action.equals("teamsay:")) + { + PARSE_PLAYER_DATA(); + Jupiter::ReferenceString message = buff.getWord(3, RenX::DelimS); + onChat(this, player, message, false); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnTeamChat(this, player, message); + } + else if (action.equals("say:")) + { + PARSE_PLAYER_DATA(); + Jupiter::ReferenceString message = buff.getWord(3, RenX::DelimS); + onChat(this, player, message, true); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnChat(this, player, message); + } + } + else if (header.equals("lPLAYER:")) + { + PARSE_PLAYER_DATA(); + if (action.equals("disconnected")) + { + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnPart(this, player); + this->removePlayer(player); + player = nullptr; + } + else if (action.equals("entered from")) + { + player->ip = buff.getWord(3, RenX::DelimS); + if (buff.getWord(4, RenX::DelimS).equals("steamid")) player->steamid = buff.getWord(5, RenX::DelimS); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnJoin(this, player); + } + else if (action.equals("changed name to:")) + { + Jupiter::ReferenceString newName = buff.getWord(3, RenX::DelimS); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnNameChange(this, player, newName); + player->name = newName; + } + } + else if (header.equals("lRCON:")) + { + if (action.equals("executed:")) + { + Jupiter::ReferenceString command = buff.getWord(3, RenX::DelimS); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnExecute(this, playerData, command); + } + else if (action.equals("subscribed")) for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnSubscribe(this, playerData); + else for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnRCON(this, buff.gotoWord(1, RenX::DelimS)); + } + else if (header.equals("lADMIN:")) + { + PARSE_PLAYER_DATA(); + if (action.equals("logged in as")) + { + player->adminType = buff.getWord(3, RenX::DelimS); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnAdminLogin(this, player); + } + else if (action.equals("logged out of")) + { + Jupiter::ReferenceString type = buff.getWord(3, RenX::DelimS); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnAdminLogout(this, player); + player->adminType = ""; + } + else for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnAdmin(this, buff.gotoWord(1, RenX::DelimS)); + } + else if (header.equals("lC-LIST:")) + { + // ID IP SteamID Team Name + if (playerData.isEmpty()) + break; + + int id; + bool isBot = false; + if (playerData.get(0) == 'b') + { + isBot = true; + playerData.shiftRight(1); + id = playerData.asInt(10); + playerData.shiftLeft(1); + } + else id = playerData.asInt(10); + Jupiter::ReferenceString ip = playerData.getWord(1, WHITESPACE); + Jupiter::ReferenceString steamid = playerData.getWord(2, WHITESPACE); + RenX::TeamType team; + Jupiter::ReferenceString name; + if (steamid.equals("-----NO")) // RCONv2-2a + { + steamid = ""; + Jupiter::ReferenceString &teamToken = playerData.getWord(4, WHITESPACE); + if (teamToken.isEmpty()) + break; + team = getTeam(teamToken.get(0)); + name = playerData.gotoWord(5, WHITESPACE); + } + else + { + if (steamid.equals("-----NO-STEAM-----")) // RCONv2-2.5a + steamid = ""; + Jupiter::ReferenceString &teamToken = playerData.getWord(3, WHITESPACE); + if (teamToken.isEmpty()) + break; + team = getTeam(teamToken.get(0)); + name = playerData.gotoWord(4, WHITESPACE); + } + + RenX::PlayerInfo *player = getPlayerOrAdd(this, name, id, team, isBot); + if (player->ip.size() == 0) + { + player->ip = ip; + player->steamid = steamid; + } + } + else + { + buff.shiftRight(1); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnLog(this, buff); + buff.shiftLeft(1); + } + break; + + case 'c': + buff.shiftRight(1); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnCommand(this, buff); + buff.shiftLeft(1); + break; + + case 'e': + buff.shiftRight(1); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnError(this, buff); + buff.shiftLeft(1); + break; + + case 'v': + buff.shiftRight(1); + this->rconVersion = buff.asInt(10); + this->gameVersion = buff.substring(3); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnVersion(this, buff); + buff.shiftLeft(1); + break; + + case 'a': + buff.shiftRight(1); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnAuthorized(this, buff); + buff.shiftLeft(1); + break; + + default: + buff.shiftRight(1); + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnOther(this, header[0], buff); + buff.shiftLeft(1); + break; + } + for (size_t i = 0; i < xPlugins.size(); i++) + xPlugins.get(i)->RenX_OnRaw(this, buff); + } +} + void RenX::Server::disconnect() { RenX::Server::sock.closeSocket(); @@ -398,7 +854,8 @@ bool RenX::Server::reconnect() void RenX::Server::wipeData() { - while (RenX::Server::players.size() != 0) delete RenX::Server::players.remove(0U); + while (RenX::Server::players.size() != 0) + delete RenX::Server::players.remove(0U); RenX::Server::commands.emptyAndDelete(); } diff --git a/RenX.Core/RenX_Server.h b/RenX.Core/RenX_Server.h index b8bda77..66b604e 100644 --- a/RenX.Core/RenX_Server.h +++ b/RenX.Core/RenX_Server.h @@ -29,6 +29,7 @@ #include "Jupiter/String.h" #include "Jupiter/CString.h" #include "Jupiter/INIFile.h" +#include "Jupiter/Thinker.h" #include "RenX.h" /** DLL Linkage Nagging */ @@ -48,10 +49,18 @@ namespace RenX * @brief Represents a connection to an individiaul Renegade-X server. * There are often more than one of these, such as when communities run multiple servers. */ - class RENX_API Server + class RENX_API Server : public Jupiter::Thinker { - friend class RenX::Core; - public: + public: // Jupiter::Thinker + + /** + * @brief Checks and processes raw socket data. + * + * @return Zero if no error occurs, a non-zero value otherwise. + */ + int think(); + + public: // RenX::Server Jupiter::DLList players; /** A list of players in the server */ Jupiter::INIFile varData; /** This may be replaced later with a more dedicated type. */ @@ -352,6 +361,13 @@ namespace RenX */ void sendLogChan(const char *fmt, ...) const; + /** + * @brief Processes a line of RCON input data. Input data SHOULD NOT include a new-line ('\n') terminator. + * + * @param line Line to process + */ + void processLine(const Jupiter::ReadableString &line); + /** * @brief Disconnects from a server's RCON interface. */ @@ -418,6 +434,7 @@ namespace RenX Jupiter::StringS IRCPrefix; Jupiter::StringS CommandPrefix; Jupiter::StringS gameVersion; + Jupiter::String lastLine; Jupiter::ArrayList commands; };