diff --git a/Release/Plugins/RenX.Core.lib b/Release/Plugins/RenX.Core.lib index ccebe6c..7ad58b0 100644 Binary files a/Release/Plugins/RenX.Core.lib and b/Release/Plugins/RenX.Core.lib differ diff --git a/RenX.Commands/RenX_Commands.cpp b/RenX.Commands/RenX_Commands.cpp index f9d76f3..c627f60 100644 --- a/RenX.Commands/RenX_Commands.cpp +++ b/RenX.Commands/RenX_Commands.cpp @@ -716,12 +716,143 @@ void BuildingInfoIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableStr const Jupiter::ReadableString &BuildingInfoIRCCommand::getHelp(const Jupiter::ReadableString &) { - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Gets information about a player. Syntax: PlayerInfo "); + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Provides a list of buildings, and the status of each one. Syntax: BuildingInfo"); return defaultHelp; } IRC_COMMAND_INIT(BuildingInfoIRCCommand) +// Mutators IRC Command + +void MutatorsIRCCommand::create() +{ + this->addTrigger("mutators"_jrs); + this->addTrigger("mutator"_jrs); +} + +void MutatorsIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); + if (chan != nullptr) + { + int type = chan->getType(); + Jupiter::String list; + size_t index = 0; + for (unsigned int i = 0; i != RenX::getCore()->getServerCount(); i++) + { + RenX::Server *server = RenX::getCore()->getServer(i); + if (server->isLogChanType(type)) + { + list = STRING_LITERAL_AS_REFERENCE(IRCCOLOR "03[Mutators]" IRCNORMAL); + for (index = 0; index != server->mutators.size(); ++index) + list += " "_jrs + *server->mutators.get(index); + if (index == 0) + source->sendMessage(channel, "No mutators loaded"_jrs); + else + source->sendMessage(channel, list); + } + } + if (list.isEmpty()) + source->sendMessage(channel, "Error: Channel not attached to any connected Renegade X servers."_jrs); + } +} + +const Jupiter::ReadableString &MutatorsIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Provides a list of mutators being used. Syntax: Mutators"); + return defaultHelp; +} + +IRC_COMMAND_INIT(MutatorsIRCCommand) + +// Rotation IRC Command + +void RotationIRCCommand::create() +{ + this->addTrigger("rotation"_jrs); + this->addTrigger("maprotation"_jrs); + this->addTrigger("maps"_jrs); + this->addTrigger("rot"_jrs); +} + +void RotationIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); + if (chan != nullptr) + { + const Jupiter::ReadableString *map; + int type = chan->getType(); + Jupiter::String list; + size_t index = 0; + for (unsigned int i = 0; i != RenX::getCore()->getServerCount(); i++) + { + RenX::Server *server = RenX::getCore()->getServer(i); + if (server->isLogChanType(type)) + { + list = STRING_LITERAL_AS_REFERENCE(IRCCOLOR "03[Rotation]" IRCNORMAL); + for (index = 0; index != server->maps.size(); ++index) + { + map = server->maps.get(index); + if (server->getMap().equalsi(*map)) + list += STRING_LITERAL_AS_REFERENCE(" " IRCBOLD "[") + *server->maps.get(index) + STRING_LITERAL_AS_REFERENCE("]" IRCBOLD); + else + list += " "_jrs + *server->maps.get(index); + } + if (index == 0) + source->sendMessage(channel, "No maps in rotation"_jrs); + else + source->sendMessage(channel, list); + } + } + if (list.isEmpty()) + source->sendMessage(channel, "Error: Channel not attached to any connected Renegade X servers."_jrs); + } +} + +const Jupiter::ReadableString &RotationIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Provides a list of maps in the server rotation. Syntax: Rotation"); + return defaultHelp; +} + +IRC_COMMAND_INIT(RotationIRCCommand) + +// Map IRC Command + +void MapIRCCommand::create() +{ + this->addTrigger("map"_jrs); +} + +void MapIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); + if (chan != nullptr) + { + int type = chan->getType(); + bool match = false; + for (unsigned int i = 0; i != RenX::getCore()->getServerCount(); i++) + { + RenX::Server *server = RenX::getCore()->getServer(i); + if (server->isLogChanType(type)) + { + match = true; + source->sendMessage(channel, "Current Map: "_jrs + server->getMap()); + } + } + if (match == false) + source->sendMessage(channel, "Error: Channel not attached to any connected Renegade X servers."_jrs); + } +} + +const Jupiter::ReadableString &MapIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Fetches the current map. Syntax: Map"); + return defaultHelp; +} + +IRC_COMMAND_INIT(MapIRCCommand) + // Steam IRC Command void SteamIRCCommand::create() @@ -1269,19 +1400,21 @@ void SetMapIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &c Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); if (chan != nullptr) { + const Jupiter::ReadableString *map_name = nullptr; int type = chan->getType(); - bool match = false; for (unsigned int i = 0; i != RenX::getCore()->getServerCount(); i++) { RenX::Server *server = RenX::getCore()->getServer(i); if (server->isLogChanType(type)) { - match = true; - if (server->setMap(parameters) == false) - source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Server does not support setmap.")); + map_name = server->getMapName(parameters); + if (map_name == nullptr) + source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Map not in rotation.")); + else if (server->setMap(*map_name) == false) + source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Transmission error.")); } } - if (match == false) + if (map_name == nullptr) source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Channel not attached to any connected Renegade X servers.")); } } diff --git a/RenX.Commands/RenX_Commands.h b/RenX.Commands/RenX_Commands.h index 5979730..b0bbc2d 100644 --- a/RenX.Commands/RenX_Commands.h +++ b/RenX.Commands/RenX_Commands.h @@ -62,6 +62,9 @@ GENERIC_IRC_COMMAND(PlayersIRCCommand) GENERIC_IRC_COMMAND(PlayerTableIRCCommand) GENERIC_IRC_COMMAND(PlayerInfoIRCCommand) GENERIC_IRC_COMMAND(BuildingInfoIRCCommand) +GENERIC_IRC_COMMAND(MutatorsIRCCommand) +GENERIC_IRC_COMMAND(RotationIRCCommand) +GENERIC_IRC_COMMAND(MapIRCCommand) GENERIC_IRC_COMMAND(SteamIRCCommand) GENERIC_IRC_COMMAND(KillDeathRatioIRCCommand) GENERIC_IRC_COMMAND(ShowModsIRCCommand) diff --git a/RenX.Core/RenX_Server.cpp b/RenX.Core/RenX_Server.cpp index cd7efd7..7e7452f 100644 --- a/RenX.Core/RenX_Server.cpp +++ b/RenX.Core/RenX_Server.cpp @@ -37,7 +37,7 @@ int RenX::Server::think() { if (RenX::Server::maxAttempts < 0 || RenX::Server::attempts < RenX::Server::maxAttempts) { - if (time(0) >= RenX::Server::lastAttempt + RenX::Server::delay) + if (std::chrono::steady_clock::now() >= RenX::Server::lastAttempt + RenX::Server::delay) { if (RenX::Server::connect()) RenX::Server::sendLogChan(IRCCOLOR "03[RenX]" IRCCOLOR " Socket successfully reconnected to Renegade-X server."); @@ -243,6 +243,28 @@ RenX::BuildingInfo *RenX::Server::getBuildingByName(const Jupiter::ReadableStrin return nullptr; } +bool RenX::Server::hasMapInRotation(const Jupiter::ReadableString &name) const +{ + size_t index = RenX::Server::maps.size(); + while (index != 0) + if (RenX::Server::maps.get(--index)->equalsi(name)) + return true; + return false; +} + +const Jupiter::ReadableString *RenX::Server::getMapName(const Jupiter::ReadableString &name) const +{ + size_t index = RenX::Server::maps.size(); + const Jupiter::ReadableString *map_name; + while (index != 0) + { + map_name = RenX::Server::maps.get(--index); + if (map_name->findi(name) != Jupiter::INVALID_INDEX) + return map_name; + } + return nullptr; +} + const Jupiter::ReadableString &RenX::Server::getCurrentRCONCommand() const { return RenX::Server::lastCommand; @@ -621,12 +643,12 @@ unsigned short RenX::Server::getSocketPort() const return RenX::Server::sock.getPort(); } -time_t RenX::Server::getLastAttempt() const +std::chrono::steady_clock::time_point RenX::Server::getLastAttempt() const { return RenX::Server::lastAttempt; } -time_t RenX::Server::getDelay() const +std::chrono::milliseconds RenX::Server::getDelay() const { return RenX::Server::delay; } @@ -924,7 +946,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) } } }; - auto onPostGameOver = [this](RenX::WinType winType, RenX::TeamType team, int gScore, int nScore) + auto onMapChange = [this]() { this->firstAction = false; this->firstKill = false; @@ -1440,15 +1462,31 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) { if (this->lastCommandParams.isEmpty()) { - // "Port" | Port | "Name" | Name | "Passworded" | "True"/"False" | "Level" | Level + // "Port" | Port | "Name" | Name | "Level" | Level | "Players" | Players | "Bots" | Bots buff.shiftRight(1); this->port = static_cast(buff.getToken(1, RenX::DelimC).asUnsignedInt(10)); this->serverName = buff.getToken(3, RenX::DelimC); - this->passworded = buff.getToken(5, RenX::DelimC).asBool(); - this->map = buff.getToken(7, RenX::DelimC); + this->map = buff.getToken(5, RenX::DelimC); buff.shiftLeft(1); } } + else if (this->lastCommand.equalsi("gameinfo"_jrs)) + { + // "PlayerLimit" | PlayerLimit | "VehicleLimit" | VehicleLimit | "MineLimit" | MineLimit | "TimeLimit" | TimeLimit | "bPassworded" | bPassworded | "bSteamRequired" | bSteamRequired | "bPrivateMessageTeamOnly" | bPrivateMessageTeamOnly | "bAllowPrivateMessaging" | bAllowPrivateMessaging | "bAutoBalanceTeams" | bAutoBalanceTeams | "bSpawnCrates" | bSpawnCrates | "CrateRespawnAfterPickup" | CrateRespawnAfterPickup + buff.shiftRight(1); + this->playerLimit = buff.getToken(1, RenX::DelimC).asInt(); + this->vehicleLimit = buff.getToken(3, RenX::DelimC).asInt(); + this->mineLimit = buff.getToken(5, RenX::DelimC).asInt(); + this->timeLimit = buff.getToken(7, RenX::DelimC).asInt(); + this->passworded = buff.getToken(9, RenX::DelimC).asBool(); + this->steamRequired = buff.getToken(11, RenX::DelimC).asBool(); + this->privateMessageTeamOnly = buff.getToken(13, RenX::DelimC).asBool(); + this->allowPrivateMessaging = buff.getToken(15, RenX::DelimC).asBool(); + this->autoBalanceTeams = buff.getToken(17, RenX::DelimC).asBool(); + this->spawnCrates = buff.getToken(19, RenX::DelimC).asBool(); + this->crateRespawnAfterPickup = buff.getToken(21, RenX::DelimC).asDouble(); + buff.shiftLeft(1); + } else if (this->lastCommand.equalsi("mutatorlist"_jrs)) { // "The following mutators are loaded:" [ | Mutator [ | Mutator [ ... ] ] ] @@ -1466,6 +1504,14 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) } buff.shiftLeft(1); } + else if (this->lastCommand.equalsi("rotation"_jrs)) + { + // Map + buff.shiftRight(1); + if (this->hasMapInRotation(buff) == false) + this->maps.add(new Jupiter::StringS(buff)); + buff.shiftLeft(1); + } else if (this->lastCommand.equalsi("changename")) { buff.shiftRight(1); @@ -1475,6 +1521,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) for (size_t i = 0; i < xPlugins.size(); i++) xPlugins.get(i)->RenX_OnNameChange(this, player, newName); player->name = newName; + buff.shiftLeft(1); } break; case 'l': @@ -1939,7 +1986,6 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) onPreGameOver(winType, team, gScore, nScore); for (size_t i = 0; i < xPlugins.size(); i++) xPlugins.get(i)->RenX_OnGameOver(this, winType, team, gScore, nScore); - onPostGameOver(winType, team, gScore, nScore); } else if (winTieToken.equals("tie")) { @@ -1947,7 +1993,6 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) int nScore = buff.getToken(5, RenX::DelimC).getToken(1, '=').asInt(); for (size_t i = 0; i < xPlugins.size(); i++) xPlugins.get(i)->RenX_OnGameOver(this, RenX::WinType::Tie, RenX::TeamType::None, gScore, nScore); - onPostGameOver(WinType::Tie, RenX::TeamType::None, gScore, nScore); } } else @@ -2365,6 +2410,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) for (size_t i = 0; i < xPlugins.size(); i++) xPlugins.get(i)->RenX_OnMapChange(this, map, seamless); this->map = map; + onMapChange(); } else if (subHeader.equals("Loaded;")) { @@ -2466,7 +2512,9 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) { RenX::Server::sock.send("s\n"_jrs); RenX::Server::send("serverinfo"_jrs); + RenX::Server::send("gameinfo"_jrs); RenX::Server::send("mutatorlist"_jrs); + RenX::Server::send("rotation"_jrs); RenX::Server::fetchClientList(); RenX::Server::updateBuildingList(); @@ -2518,7 +2566,7 @@ void RenX::Server::disconnect(RenX::DisconnectReason reason) bool RenX::Server::connect() { - RenX::Server::lastAttempt = time(0); + RenX::Server::lastAttempt = std::chrono::steady_clock::now(); if (RenX::Server::sock.connect(RenX::Server::hostname.c_str(), RenX::Server::port, RenX::Server::clientHostname.isEmpty() ? nullptr : RenX::Server::clientHostname.c_str())) { RenX::Server::sock.setBlocking(false); @@ -2550,6 +2598,9 @@ void RenX::Server::wipeData() xPlugins.get(index)->RenX_OnPlayerDelete(this, player); delete player; } + RenX::Server::buildings.emptyAndDelete(); + RenX::Server::mutators.emptyAndDelete(); + RenX::Server::maps.emptyAndDelete(); RenX::Server::awaitingPong = false; RenX::Server::rconVersion = 0; RenX::Server::rconUser.truncate(RenX::Server::rconUser.size()); @@ -2603,7 +2654,7 @@ void RenX::Server::init() RenX::Server::setPrefix(Jupiter::IRC::Client::Config->get(RenX::Server::configSection, STRING_LITERAL_AS_REFERENCE("IRCPrefix"))); RenX::Server::rules = Jupiter::IRC::Client::Config->get(RenX::Server::configSection, STRING_LITERAL_AS_REFERENCE("Rules"), STRING_LITERAL_AS_REFERENCE("Anarchy!")); - RenX::Server::delay = Jupiter::IRC::Client::Config->getInt(RenX::Server::configSection, STRING_LITERAL_AS_REFERENCE("ReconnectDelay"), 10); + RenX::Server::delay = std::chrono::milliseconds(Jupiter::IRC::Client::Config->getInt(RenX::Server::configSection, STRING_LITERAL_AS_REFERENCE("ReconnectDelay"), 10000)); RenX::Server::maxAttempts = Jupiter::IRC::Client::Config->getInt(RenX::Server::configSection, STRING_LITERAL_AS_REFERENCE("MaxReconnectAttempts"), -1); RenX::Server::rconBan = Jupiter::IRC::Client::Config->getBool(RenX::Server::configSection, STRING_LITERAL_AS_REFERENCE("RCONBan"), false); RenX::Server::localSteamBan = Jupiter::IRC::Client::Config->getBool(RenX::Server::configSection, STRING_LITERAL_AS_REFERENCE("LocalSteamBan"), true); diff --git a/RenX.Core/RenX_Server.h b/RenX.Core/RenX_Server.h index 9c0415d..37ffcff 100644 --- a/RenX.Core/RenX_Server.h +++ b/RenX.Core/RenX_Server.h @@ -85,6 +85,7 @@ namespace RenX Jupiter::DLList players; /** A list of players in the server */ Jupiter::ArrayList buildings; /** A list of buildings in the server */ Jupiter::ArrayList mutators; /** A list of buildings the server is running */ + Jupiter::ArrayList maps; /** A list of maps in the server's rotation */ Jupiter::INIFile varData; /** This may be replaced later with a more dedicated type. */ /** @@ -208,6 +209,22 @@ namespace RenX */ RenX::BuildingInfo *getBuildingByName(const Jupiter::ReadableString &name) const; + /** + * @brief Checks if a map name is in the rotation. + * + * @param name Name of map to search for + * @return True if the map exists, false otherwise. + */ + bool hasMapInRotation(const Jupiter::ReadableString &name) const; + + /** + * @brief Searches for a map based on a part of its name. + * + * @param name Part of the map's name to search for + * @return A map's full name if it exists, nullptr otherwise. + */ + const Jupiter::ReadableString *getMapName(const Jupiter::ReadableString &name) const; + /** * @brief Fetches the RCON command currently being processed. * @@ -609,14 +626,14 @@ namespace RenX * * @return Time of the last connection attempt. */ - time_t getLastAttempt() const; + std::chrono::steady_clock::time_point getLastAttempt() const; /** * @brief Fetches the time delay between connection attempts. * * @return Time delay between connection attempts. */ - time_t getDelay() const; + std::chrono::milliseconds getDelay() const; /** * @brief Checks if the server has a game password. @@ -856,7 +873,6 @@ namespace RenX bool pure = false; bool connected = false; bool seamless = false; - bool passworded = false; bool needsCList = false; bool silenceParts = false; bool silenceJoins = false; @@ -865,9 +881,20 @@ namespace RenX bool firstDeath = false; bool firstAction = false; bool awaitingPong = false; - unsigned int rconVersion = 0; - time_t lastAttempt = 0; + bool passworded = false; + bool steamRequired = false; + bool privateMessageTeamOnly = false; + bool allowPrivateMessaging = true; + bool autoBalanceTeams = true; + bool spawnCrates = true; int attempts = 0; + int playerLimit = 0; + int vehicleLimit = 0; + int mineLimit = 0; + int timeLimit = 0; + unsigned int rconVersion = 0; + double crateRespawnAfterPickup = 0.0; + std::chrono::steady_clock::time_point lastAttempt = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point gameStart = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point lastClientListUpdate = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point lastBuildingListUpdate = std::chrono::steady_clock::now(); @@ -881,8 +908,8 @@ namespace RenX unsigned short port; int logChanType; int adminLogChanType; - time_t delay; int maxAttempts; + std::chrono::milliseconds delay; std::chrono::milliseconds clientUpdateRate; std::chrono::milliseconds buildingUpdateRate; std::chrono::milliseconds pingRate; diff --git a/RenX.HybridUUID/RenX_HybridUUID.h b/RenX.HybridUUID/RenX_HybridUUID.h index 16b49ea..805e4f2 100644 --- a/RenX.HybridUUID/RenX_HybridUUID.h +++ b/RenX.HybridUUID/RenX_HybridUUID.h @@ -34,7 +34,7 @@ public: // Jupiter::Plugin ~RenX_HybridUUIDPlugin(); private: - STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX_TemplatePlugin"); + STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX.HybridUUID"); }; #endif // _RENX_HYBRIDUUID_H_HEADER \ No newline at end of file diff --git a/RenX.NicknameUUID/RenX_NicknameUUID.h b/RenX.NicknameUUID/RenX_NicknameUUID.h index 6a7d7d8..729b27d 100644 --- a/RenX.NicknameUUID/RenX_NicknameUUID.h +++ b/RenX.NicknameUUID/RenX_NicknameUUID.h @@ -34,7 +34,7 @@ public: // Jupiter::Plugin ~RenX_NicknameUUIDPlugin(); private: - STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX_TemplatePlugin"); + STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX.NicknameUUID"); }; #endif // _RENX_NICKNAMEUUID_H_HEADER \ No newline at end of file