diff --git a/Bot/Main.cpp b/Bot/Main.cpp index 05983f0..1b4dc01 100644 --- a/Bot/Main.cpp +++ b/Bot/Main.cpp @@ -67,7 +67,7 @@ int main(int argc, const char **args) std::set_terminate(onTerminate); std::thread inputThread(inputLoop); - srand(static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count())); + srand(static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count())); puts(Jupiter::copyright); const char *configFileName = CONFIG_INI; diff --git a/Release/Bot.lib b/Release/Bot.lib index 306934d..3ae9a14 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 c86fad1..72531a6 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 443cd97..dc116d3 100644 --- a/RenX.Commands/RenX_Commands.cpp +++ b/RenX.Commands/RenX_Commands.cpp @@ -27,6 +27,7 @@ #include "RenX_BuildingInfo.h" #include "RenX_Functions.h" #include "RenX_BanDatabase.h" +#include "RenX_ExemptionDatabase.h" #include "RenX_Tags.h" using namespace Jupiter::literals; @@ -65,7 +66,7 @@ void RenX_CommandsPlugin::RenX_OnDie(RenX::Server *server, const RenX::PlayerInf int RenX_CommandsPlugin::OnRehash() { - RenX_CommandsPlugin::_defaultTempBanTime = Jupiter::IRC::Client::Config->getLongLong(RenX_CommandsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("TBanTime"), 86400); + RenX_CommandsPlugin::_defaultTempBanTime = std::chrono::seconds(Jupiter::IRC::Client::Config->getLongLong(RenX_CommandsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("TBanTime"), 86400)); RenX_CommandsPlugin::playerInfoFormat = Jupiter::IRC::Client::Config->get(RenX_CommandsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("PlayerInfoFormat"), STRING_LITERAL_AS_REFERENCE(IRCCOLOR "03[Player Info]" IRCCOLOR "{TCOLOR} Name: " IRCBOLD "{RNAME}" IRCBOLD " - ID: {ID} - Team: " IRCBOLD "{TEAML}" IRCBOLD " - Vehicle Kills: {VEHICLEKILLS} - Building Kills {BUILDINGKILLS} - Kills {KILLS} - Deaths: {DEATHS} - KDR: {KDR} - Access: {ACCESS}")); RenX_CommandsPlugin::adminPlayerInfoFormat = Jupiter::IRC::Client::Config->get(RenX_CommandsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("AdminPlayerInfoFormat"), Jupiter::StringS::Format("%.*s - IP: " IRCBOLD "{IP}" IRCBOLD " - RDNS: " IRCBOLD "{RDNS}" IRCBOLD " - Steam ID: " IRCBOLD "{STEAM}", RenX_CommandsPlugin::playerInfoFormat.size(), RenX_CommandsPlugin::playerInfoFormat.ptr())); RenX_CommandsPlugin::buildingInfoFormat = Jupiter::IRC::Client::Config->get(RenX_CommandsPlugin::getName(), STRING_LITERAL_AS_REFERENCE("BuildingInfoFormat"), STRING_LITERAL_AS_REFERENCE(IRCCOLOR) + RenX::tags->buildingTeamColorTag + RenX::tags->buildingNameTag + STRING_LITERAL_AS_REFERENCE(IRCCOLOR " - " IRCCOLOR "07") + RenX::tags->buildingHealthPercentageTag + STRING_LITERAL_AS_REFERENCE("%")); @@ -76,7 +77,7 @@ int RenX_CommandsPlugin::OnRehash() return 0; } -time_t RenX_CommandsPlugin::getTBanTime() const +std::chrono::seconds RenX_CommandsPlugin::getTBanTime() const { return RenX_CommandsPlugin::_defaultTempBanTime; } @@ -1130,144 +1131,6 @@ const Jupiter::ReadableString &ModsIRCCommand::getHelp(const Jupiter::ReadableSt IRC_COMMAND_INIT(ModsIRCCommand) -// BanSearch IRC Command - -void BanSearchIRCCommand::create() -{ - this->addTrigger(STRING_LITERAL_AS_REFERENCE("bansearch")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("bsearch")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("banfind")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("bfind")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("banlogs")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("blogs")); - this->setAccessLevel(2); -} - -void BanSearchIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) -{ - auto entries = RenX::banDatabase->getEntries(); - if (parameters.isNotEmpty()) - { - if (entries.size() == 0) - source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("The ban database is empty!")); - else - { - RenX::BanDatabase::Entry *entry; - Jupiter::ReferenceString params = Jupiter::ReferenceString::gotoWord(parameters, 1, WHITESPACE); - std::function isMatch = [&](unsigned int type_l) -> bool - { - switch (type_l) - { - default: - case 0: // ANY - return isMatch(1) || isMatch(2) || isMatch(3) || isMatch(4); - case 1: // ALL - return true; - case 2: // IP - return entry->ip == params.asUnsignedInt(); - case 3: // RDNS - return entry->rdns.equals(params); - case 4: // STEAM - return entry->steamid == params.asUnsignedLongLong(); - case 5: // NAME - return entry->name.equalsi(params); - case 6: // BANNER - return entry->varData.get(pluginInstance.getName()).equalsi(params); - case 7: // ACTIVE - return params.asBool() == entry->is_active(); - } - }; - - unsigned int type; - Jupiter::ReferenceString type_str = Jupiter::ReferenceString::getWord(parameters, 0, WHITESPACE); - if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("all")) || type_str.equals('*')) - type = 1; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("ip"))) - type = 2; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("rdns"))) - type = 3; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("steam"))) - type = 4; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("name"))) - type = 5; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("banner"))) - type = 6; - else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("active"))) - type = 7; - else - { - type = 0; - params = parameters; - } - - Jupiter::String out(256); - Jupiter::String types(64); - char timeStr[256]; - for (size_t i = 0; i != entries.size(); i++) - { - entry = entries.get(i); - if (isMatch(type)) - { - Jupiter::StringS &ip_str = Jupiter::Socket::ntop4(entry->ip); - strftime(timeStr, sizeof(timeStr), "%b %d %Y, %H:%M:%S", localtime(&(entry->timestamp))); - - if ((entry->flags & 0x7FFF) == 0) - types = " NULL;"_jrs; - else - { - types.erase(); - if (entry->is_rdns_ban()) - types += " rdns"_jrs; - if (entry->is_type_game()) - types += " game"_jrs; - if (entry->is_type_chat()) - types += " chat"_jrs; - if (entry->is_type_bot()) - types += " bot"_jrs; - if (entry->is_type_vote()) - types += " vote"_jrs; - if (entry->is_type_mine()) - types += " mine"_jrs; - if (entry->is_type_ladder()) - types += " ladder"_jrs; - if (entry->is_type_alert()) - types += " alert"_jrs; - types += ";"_jrs; - } - - out.format("ID: %lu (%sactive); Date: %s; IP: %.*s/%u; Steam: %llu; Types:%.*s Name: %.*s; Banner: %.*s", - i, entry->is_active() ? "" : "in", timeStr, ip_str.size(), ip_str.ptr(), entry->prefix_length, entry->steamid, types.size(), types.ptr(), - entry->name.size(), entry->name.ptr(), entry->banner.size(), entry->banner.ptr()); - - if (entry->rdns.isNotEmpty()) - { - out.concat("; RDNS: "_jrs); - out.concat(entry->rdns); - } - if (entry->reason.isNotEmpty()) - { - out.concat("; Reason: "_jrs); - out.concat(entry->reason); - } - source->sendNotice(nick, out); - } - } - if (out.isEmpty()) - source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("No matches found.")); - } - } - else - source->sendNotice(nick, Jupiter::StringS::Format("There are a total of %u entries in the ban database.", entries.size())); -} - -const Jupiter::ReadableString &BanSearchIRCCommand::getHelp(const Jupiter::ReadableString &) -{ - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Searches the ban database for an entry. Syntax: bsearch [ip/rdns/steam/name/banner/active/any/all = any] "); - return defaultHelp; -} - -IRC_COMMAND_INIT(BanSearchIRCCommand) - // ShowRules IRC Command void ShowRulesIRCCommand::create() @@ -1914,75 +1777,215 @@ const Jupiter::ReadableString &KickIRCCommand::getHelp(const Jupiter::ReadableSt IRC_COMMAND_INIT(KickIRCCommand) -// TempBan IRC Command +/** Ban IRC Commands */ -void TempBanIRCCommand::create() +// BanSearch IRC Command + +void BanSearchIRCCommand::create() { - this->addTrigger(STRING_LITERAL_AS_REFERENCE("tban")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("tb")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("tempban")); - this->setAccessLevel(3); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("bansearch")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("bsearch")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("banfind")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("bfind")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("banlogs")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("blogs")); + this->setAccessLevel(2); } -void TempBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +void BanSearchIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) { + auto entries = RenX::banDatabase->getEntries(); if (parameters.isNotEmpty()) { - Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); - if (chan != nullptr) + if (entries.size() == 0) + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("The ban database is empty!")); + else { - Jupiter::ArrayList servers = RenX::getCore()->getServers(chan->getType()); - if (servers.size() != 0) + RenX::BanDatabase::Entry *entry; + Jupiter::ReferenceString params = Jupiter::ReferenceString::gotoWord(parameters, 1, WHITESPACE); + std::function isMatch = [&](unsigned int type_l) -> bool { - RenX::PlayerInfo *player; - RenX::Server *server; - unsigned int kicks = 0; - Jupiter::ReferenceString name = Jupiter::ReferenceString::getWord(parameters, 0, WHITESPACE); - Jupiter::ReferenceString reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::ReferenceString::gotoWord(parameters, 1, WHITESPACE) : "No reason"_jrs; - Jupiter::String banner(nick.size() + 4); - banner += nick; - banner += "@IRC"; - for (size_t i = 0; i != servers.size(); i++) + switch (type_l) { - server = servers.get(i); - if (server != nullptr) - { - player = server->getPlayerByPartName(name); - if (player != nullptr) - { - server->banPlayer(player, banner, reason, pluginInstance.getTBanTime()); - kicks++; - } - } + default: + case 0: // ANY + return isMatch(1) || isMatch(2) || isMatch(3) || isMatch(4); + case 1: // ALL + return true; + case 2: // IP + return entry->ip == params.asUnsignedInt(); + case 3: // RDNS + return entry->rdns.equals(params); + case 4: // STEAM + return entry->steamid == params.asUnsignedLongLong(); + case 5: // NAME + return entry->name.equalsi(params); + case 6: // BANNER + return entry->banner.equalsi(params); + case 7: // ACTIVE + return params.asBool() == entry->is_active(); } - source->sendMessage(channel, Jupiter::StringS::Format("%u players kicked.", kicks)); - } - else source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Channel not attached to any connected Renegade X servers.")); - } - } - else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: TempBan [Reason]")); -} - -const Jupiter::ReadableString &TempBanIRCCommand::getHelp(const Jupiter::ReadableString &) -{ - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and temporarily bans a player from the game. Syntax: TempBan [Reason]"); - return defaultHelp; -} - -IRC_COMMAND_INIT(TempBanIRCCommand) + }; -// KickBan IRC Command + unsigned int type; + Jupiter::ReferenceString type_str = Jupiter::ReferenceString::getWord(parameters, 0, WHITESPACE); + if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("all")) || type_str.equals('*')) + type = 1; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("ip"))) + type = 2; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("rdns"))) + type = 3; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("steam"))) + type = 4; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("name"))) + type = 5; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("banner"))) + type = 6; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("active"))) + type = 7; + else + { + type = 0; + params = parameters; + } -void KickBanIRCCommand::create() -{ - this->addTrigger(STRING_LITERAL_AS_REFERENCE("kickban")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("kb")); - this->addTrigger(STRING_LITERAL_AS_REFERENCE("ban")); - this->setAccessLevel(4); -} + Jupiter::String out(256); + Jupiter::String types(64); + char timeStr[256]; + for (size_t i = 0; i != entries.size(); i++) + { + entry = entries.get(i); + if (isMatch(type)) + { + Jupiter::StringS &ip_str = Jupiter::Socket::ntop4(entry->ip); + strftime(timeStr, sizeof(timeStr), "%b %d %Y, %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(entry->timestamp)))); -void KickBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) -{ + if ((entry->flags & 0x7FFF) == 0) + types = " NULL;"_jrs; + else + { + types.erase(); + if (entry->is_rdns_ban()) + types += " rdns"_jrs; + if (entry->is_type_game()) + types += " game"_jrs; + if (entry->is_type_chat()) + types += " chat"_jrs; + if (entry->is_type_bot()) + types += " bot"_jrs; + if (entry->is_type_vote()) + types += " vote"_jrs; + if (entry->is_type_mine()) + types += " mine"_jrs; + if (entry->is_type_ladder()) + types += " ladder"_jrs; + if (entry->is_type_alert()) + types += " alert"_jrs; + types += ";"_jrs; + } + + out.format("ID: %lu (%sactive); Date: %s; IP: %.*s/%u; Steam: %llu; Types:%.*s Name: %.*s; Banner: %.*s", + i, entry->is_active() ? "" : "in", timeStr, ip_str.size(), ip_str.ptr(), entry->prefix_length, entry->steamid, types.size(), types.ptr(), + entry->name.size(), entry->name.ptr(), entry->banner.size(), entry->banner.ptr()); + + if (entry->rdns.isNotEmpty()) + { + out.concat("; RDNS: "_jrs); + out.concat(entry->rdns); + } + if (entry->reason.isNotEmpty()) + { + out.concat("; Reason: "_jrs); + out.concat(entry->reason); + } + source->sendNotice(nick, out); + } + } + if (out.isEmpty()) + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("No matches found.")); + } + } + else + source->sendNotice(nick, Jupiter::StringS::Format("There are a total of %u entries in the ban database.", entries.size())); +} + +const Jupiter::ReadableString &BanSearchIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Searches the ban database for an entry. Syntax: bsearch [ip/rdns/steam/name/banner/active/any/all = any] "); + return defaultHelp; +} + +IRC_COMMAND_INIT(BanSearchIRCCommand) + +// TempBan IRC Command + +void TempBanIRCCommand::create() +{ + this->addTrigger(STRING_LITERAL_AS_REFERENCE("tban")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("tb")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("tempban")); + this->setAccessLevel(3); +} + +void TempBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + if (parameters.isNotEmpty()) + { + Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); + if (chan != nullptr) + { + Jupiter::ArrayList servers = RenX::getCore()->getServers(chan->getType()); + if (servers.size() != 0) + { + RenX::PlayerInfo *player; + RenX::Server *server; + unsigned int kicks = 0; + Jupiter::ReferenceString name = Jupiter::ReferenceString::getWord(parameters, 0, WHITESPACE); + Jupiter::ReferenceString reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::ReferenceString::gotoWord(parameters, 1, WHITESPACE) : "No reason"_jrs; + Jupiter::String banner(nick.size() + 4); + banner += nick; + banner += "@IRC"; + for (size_t i = 0; i != servers.size(); i++) + { + server = servers.get(i); + if (server != nullptr) + { + player = server->getPlayerByPartName(name); + if (player != nullptr) + { + server->banPlayer(player, banner, reason, pluginInstance.getTBanTime()); + kicks++; + } + } + } + source->sendMessage(channel, Jupiter::StringS::Format("%u players kicked.", kicks)); + } + else source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Channel not attached to any connected Renegade X servers.")); + } + } + else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: TempBan [Reason]")); +} + +const Jupiter::ReadableString &TempBanIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and temporarily bans a player from the game. Syntax: TempBan [Reason]"); + return defaultHelp; +} + +IRC_COMMAND_INIT(TempBanIRCCommand) + +// KickBan IRC Command + +void KickBanIRCCommand::create() +{ + this->addTrigger(STRING_LITERAL_AS_REFERENCE("kickban")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("kb")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("ban")); + this->setAccessLevel(4); +} + +void KickBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ if (parameters.isNotEmpty()) { Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); @@ -2068,7 +2071,7 @@ void AddBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &c Jupiter::StringS rdns; Jupiter::String banner = nick + "@IRC"_jrs; Jupiter::ReferenceString reason = "No reason"_jrs; - time_t duration = 0; + std::chrono::seconds duration(0); uint16_t flags = 0; Jupiter::ReferenceString word; @@ -2135,7 +2138,7 @@ void AddBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &c return; } - duration = Jupiter::ReferenceString::getWord(parameters, index++, ADDBAN_WHITESPACE).asUnsignedLongLong(); + duration = std::chrono::seconds(Jupiter::ReferenceString::getWord(parameters, index++, ADDBAN_WHITESPACE).asUnsignedLongLong()); } else if (word.equalsi("Game"_jrs)) flags |= RenX::BanDatabase::Entry::FLAG_TYPE_GAME; @@ -2234,6 +2237,406 @@ const Jupiter::ReadableString &UnBanIRCCommand::getHelp(const Jupiter::ReadableS IRC_COMMAND_INIT(UnBanIRCCommand) +/** Exemption IRC Commands */ + +// ExemptionSearch IRC Command + +void ExemptionSearchIRCCommand::create() +{ + this->addTrigger(STRING_LITERAL_AS_REFERENCE("exemptionsearch")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("esearch")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("exemptionfind")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("efind")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("exemptionlogs")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("elogs")); + this->setAccessLevel(2); +} + +void ExemptionSearchIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + auto entries = RenX::exemptionDatabase->getEntries(); + if (parameters.isNotEmpty()) + { + if (entries.size() == 0) + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("The exemption database is empty!")); + else + { + RenX::ExemptionDatabase::Entry *entry; + Jupiter::ReferenceString params = Jupiter::ReferenceString::gotoWord(parameters, 1, WHITESPACE); + std::function isMatch = [&](unsigned int type_l) -> bool + { + switch (type_l) + { + default: + case 0: // ANY + return isMatch(1) || isMatch(2) || isMatch(3) || isMatch(4); + case 1: // ALL + return true; + case 2: // IP + return entry->ip == params.asUnsignedInt(); + case 3: // STEAM + return entry->steamid == params.asUnsignedLongLong(); + case 4: // SETTER + return entry->setter.equalsi(params); + case 5: // ACTIVE + return params.asBool() == entry->is_active(); + } + }; + + unsigned int type; + Jupiter::ReferenceString type_str = Jupiter::ReferenceString::getWord(parameters, 0, WHITESPACE); + if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("all")) || type_str.equals('*')) + type = 1; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("ip"))) + type = 2; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("steam"))) + type = 3; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("setter"))) + type = 4; + else if (type_str.equalsi(STRING_LITERAL_AS_REFERENCE("active"))) + type = 5; + else + { + type = 0; + params = parameters; + } + + Jupiter::String out(256); + Jupiter::String types(64); + char timeStr[256]; + for (size_t i = 0; i != entries.size(); i++) + { + entry = entries.get(i); + if (isMatch(type)) + { + Jupiter::StringS &ip_str = Jupiter::Socket::ntop4(entry->ip); + strftime(timeStr, sizeof(timeStr), "%b %d %Y, %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(entry->timestamp)))); + + if ((entry->flags & 0xFF) == 0) + types = " NULL;"_jrs; + else + { + types.erase(); + if (entry->is_type_kick()) + types += " kick"_jrs; + if (entry->is_type_ban()) + types += " ban"_jrs; + if (entry->is_ip_exemption()) + types += " ip"_jrs; + types += ";"_jrs; + } + + out.format("ID: %lu (%sactive); Date: %s; IP: %.*s/%u; Steam: %llu; Types:%.*s Setter: %.*s", + i, entry->is_active() ? "" : "in", timeStr, ip_str.size(), ip_str.ptr(), entry->prefix_length, entry->steamid, + types.size(), types.ptr(), entry->setter.size(), entry->setter.ptr()); + + source->sendNotice(nick, out); + } + } + if (out.isEmpty()) + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("No matches found.")); + } + } + else + source->sendNotice(nick, Jupiter::StringS::Format("There are a total of %u entries in the exemption database.", entries.size())); +} + +const Jupiter::ReadableString &ExemptionSearchIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Searches the exemption database for an entry. Syntax: esearch [ip/steam/setter/active/any/all = any] "); + return defaultHelp; +} + +IRC_COMMAND_INIT(ExemptionSearchIRCCommand) + +// BanExempt IRC Command + +void BanExemptIRCCommand::create() +{ + this->addTrigger(STRING_LITERAL_AS_REFERENCE("banexempt")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("bexempt")); + this->setAccessLevel(4); +} + +void BanExemptIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + if (parameters.isNotEmpty()) + { + Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); + if (chan != nullptr) + { + Jupiter::ArrayList servers = RenX::getCore()->getServers(chan->getType()); + if (servers.size() != 0) + { + RenX::PlayerInfo *player; + RenX::Server *server; + unsigned int exemptions = 0; + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::String setter(nick.size() + 4); + setter += nick; + setter += "@IRC"; + for (size_t i = 0; i != servers.size(); i++) + { + server = servers.get(i); + if (server != nullptr) + { + player = server->getPlayerByPartName(name); + if (player != nullptr) + { + if (player->steamid != 0LL) + RenX::exemptionDatabase->add(server, player, setter, std::chrono::seconds::zero(), RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN); + else + RenX::exemptionDatabase->add(server, player, setter, std::chrono::seconds::zero(), RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN | RenX::ExemptionDatabase::Entry::FLAG_USE_IP); + ++exemptions; + } + } + } + if (exemptions == 0) + source->sendMessage(channel, "Player \""_jrs + name + "\" not found."_jrs); + else + { + source->sendMessage(channel, Jupiter::StringS::Format("%u players added.", exemptions)); + } + } + else source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Channel not attached to any connected Renegade X servers.")); + } + } + else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: BanExempt [Reason]")); +} + +const Jupiter::ReadableString &BanExemptIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Exempts a player from bans using their SteamID, or their IP address if they have none. Syntax: BanExempt [Reason]"); + return defaultHelp; +} + +IRC_COMMAND_INIT(BanExemptIRCCommand) + +// KickExempt IRC Command + +void KickExemptIRCCommand::create() +{ + this->addTrigger(STRING_LITERAL_AS_REFERENCE("kickexempt")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("kexempt")); + this->setAccessLevel(4); +} + +void KickExemptIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + if (parameters.isNotEmpty()) + { + Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); + if (chan != nullptr) + { + Jupiter::ArrayList servers = RenX::getCore()->getServers(chan->getType()); + if (servers.size() != 0) + { + RenX::PlayerInfo *player; + RenX::Server *server; + unsigned int exemptions = 0; + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::String setter(nick.size() + 4); + setter += nick; + setter += "@IRC"; + for (size_t i = 0; i != servers.size(); i++) + { + server = servers.get(i); + if (server != nullptr) + { + player = server->getPlayerByPartName(name); + if (player != nullptr) + { + if (player->steamid != 0LL) + RenX::exemptionDatabase->add(server, player, setter, std::chrono::seconds::zero(), RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN | RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK); + else + RenX::exemptionDatabase->add(server, player, setter, std::chrono::seconds::zero(), RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN | RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK | RenX::ExemptionDatabase::Entry::FLAG_USE_IP); + ++exemptions; + } + } + } + if (exemptions == 0) + source->sendMessage(channel, "Player \""_jrs + name + "\" not found."_jrs); + else + { + source->sendMessage(channel, Jupiter::StringS::Format("%u players added.", exemptions)); + } + } + else source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Channel not attached to any connected Renegade X servers.")); + } + } + else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: KickExempt [Reason]")); +} + +const Jupiter::ReadableString &KickExemptIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Exempts a player from kicks and bans using their SteamID, or their IP address if they have none. Syntax: KickExempt [Reason]"); + return defaultHelp; +} + +IRC_COMMAND_INIT(KickExemptIRCCommand) + +// AddExemption IRC Command + +#define ADDEXEMPTION_WHITESPACE " \t=" + +void AddExemptionIRCCommand::create() +{ + this->addTrigger("addexemption"_jrs); + this->addTrigger("exemptionadd"_jrs); + this->addTrigger("exempt"_jrs); + this->setAccessLevel(4); +} + +void AddExemptionIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + if (parameters.isNotEmpty()) + { + Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); + if (chan != nullptr) + { + size_t words = parameters.wordCount(ADDEXEMPTION_WHITESPACE); + if (words == 0) + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: BanExempt [Reason]")); + else if (words == 1) + BanExemptIRCCommand_instance.trigger(source, channel, nick, parameters); + else + { + size_t index = 0; + Jupiter::CStringS ip_str; + uint32_t ip = 0U; + uint8_t prefix_length = 32U; + uint64_t steamid = 0U; + Jupiter::String setter = nick + "@IRC"_jrs; + std::chrono::seconds duration = std::chrono::seconds::zero(); + uint8_t flags = 0; + + Jupiter::ReferenceString word; + while (index != words) + { + word = Jupiter::ReferenceString::getWord(parameters, index++, ADDEXEMPTION_WHITESPACE); + + if (word.equalsi("IP"_jrs) || word.equalsi("IPAddress"_jrs) || word.equalsi("Address"_jrs)) + { + if (index == words) + { + source->sendNotice(nick, "ERROR: No value specified for token: "_jrs + word); + return; + } + + ip_str = Jupiter::ReferenceString::getWord(parameters, index++, ADDEXEMPTION_WHITESPACE); + } + else if (word.equalsi("Steam"_jrs) || word.equalsi("SteamID"_jrs)) + { + if (index == words) + { + source->sendNotice(nick, "ERROR: No value specified for token: "_jrs + word); + return; + } + + steamid = Jupiter::ReferenceString::getWord(parameters, index++, ADDEXEMPTION_WHITESPACE).asUnsignedLongLong(); + } + else if (word.equalsi("Duration"_jrs) || word.equalsi("Length"_jrs) || word.equalsi("Time"_jrs)) + { + if (index == words) + { + source->sendNotice(nick, "ERROR: No value specified for token: "_jrs + word); + return; + } + + duration = std::chrono::seconds(Jupiter::ReferenceString::getWord(parameters, index++, ADDEXEMPTION_WHITESPACE).asUnsignedLongLong()); + } + else if (word.equalsi("Ban"_jrs)) + flags |= RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN; + else if (word.equalsi("Kick"_jrs)) + flags |= RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK; + else + { + source->sendNotice(nick, "ERROR: Unknown token: "_jrs + word); + return; + } + } + + // Default to Ban type + if (flags == 0) + flags = RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN; + + if (ip_str.isNotEmpty()) + { + index = ip_str.find('/'); + if (index != JUPITER_INVALID_INDEX) + { + prefix_length = Jupiter::ReferenceString::substring(ip_str, index + 1).asUnsignedInt(); + if (prefix_length == 0) + prefix_length = 32U; + ip_str.set(ip_str.ptr(), index); + } + ip = Jupiter::Socket::pton4(ip_str.c_str()); + + if (ip != 0) + flags |= RenX::ExemptionDatabase::Entry::FLAG_USE_IP; + } + + if ((flags & RenX::ExemptionDatabase::Entry::FLAG_USE_IP) == 0 && steamid == 0ULL) + source->sendNotice(nick, "Pointless exemption detected -- no IP or SteamID specified"_jrs); + else + { + RenX::exemptionDatabase->add(ip, prefix_length, steamid, setter, duration, flags); + source->sendMessage(channel, Jupiter::StringS::Format("Exemption added to the database with ID #%u", RenX::exemptionDatabase->getEntries().size() - 1)); + } + } + } + } + else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: AddExemption [...]")); +} + +const Jupiter::ReadableString &AddExemptionIRCCommand::getHelp(const Jupiter::ReadableString ¶meters) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Adds an exemption entry to the exemption list. Use \"help addexemption keys\" for a list of input keys. Syntax: AddExemption [ ...]"); + static STRING_LITERAL_AS_NAMED_REFERENCE(keyHelp, "Valueless keys (flags): Ban, Kick; Value-paired keys: IP, Steam, Duration"); + if (parameters.isNotEmpty() && parameters.equalsi("keys"_jrs)) + return keyHelp; + return defaultHelp; +} + +IRC_COMMAND_INIT(AddExemptionIRCCommand) + +// UnExempt IRC Command + +void UnExemptIRCCommand::create() +{ + this->addTrigger(STRING_LITERAL_AS_REFERENCE("unexempt")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("deexempt")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("uexempt")); + this->addTrigger(STRING_LITERAL_AS_REFERENCE("dexempt")); + this->setAccessLevel(4); +} + +void UnExemptIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) +{ + if (parameters.isNotEmpty()) + { + size_t index = static_cast(parameters.asUnsignedLongLong()); + if (index < RenX::exemptionDatabase->getEntries().size()) + { + if (RenX::exemptionDatabase->deactivate(index)) + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Exemption deactivated.")); + else + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Exemption not active.")); + } + else + source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Invalid exemption ID; please find the exemption ID using \"esearch\".")); + } + else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: unexempt ")); +} + +const Jupiter::ReadableString &UnExemptIRCCommand::getHelp(const Jupiter::ReadableString &) +{ + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Deactivates an exemption. Syntax: unexempt "); + return defaultHelp; +} + +IRC_COMMAND_INIT(UnExemptIRCCommand) + // AddBots IRC Command void AddBotsIRCCommand::create() diff --git a/RenX.Commands/RenX_Commands.h b/RenX.Commands/RenX_Commands.h index 2d08158..b0fe62c 100644 --- a/RenX.Commands/RenX_Commands.h +++ b/RenX.Commands/RenX_Commands.h @@ -19,6 +19,7 @@ #if !defined _RENX_COMMANDS_H_HEADER #define _RENX_COMMANDS_H_HEADER +#include #include "Console_Command.h" #include "IRC_Command.h" #include "RenX_GameCommand.h" @@ -36,7 +37,7 @@ public: // Jupiter::Plugin int OnRehash() override; public: - time_t getTBanTime() const; + std::chrono::seconds getTBanTime() const; const Jupiter::ReadableString &getPlayerInfoFormat() const; const Jupiter::ReadableString &getAdminPlayerInfoFormat() const; const Jupiter::ReadableString &getBuildingInfoFormat() const; @@ -44,7 +45,7 @@ public: private: STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX.Commands"); - time_t _defaultTempBanTime; + std::chrono::seconds _defaultTempBanTime; Jupiter::StringS playerInfoFormat; Jupiter::StringS adminPlayerInfoFormat; Jupiter::StringS buildingInfoFormat; @@ -69,7 +70,6 @@ GENERIC_IRC_COMMAND(SteamIRCCommand) GENERIC_IRC_COMMAND(KillDeathRatioIRCCommand) GENERIC_IRC_COMMAND(ShowModsIRCCommand) GENERIC_IRC_COMMAND(ModsIRCCommand) -GENERIC_IRC_COMMAND(BanSearchIRCCommand) GENERIC_IRC_COMMAND(ShowRulesIRCCommand) GENERIC_IRC_COMMAND(RulesIRCCommand) GENERIC_IRC_COMMAND(SetRulesIRCCommand) @@ -84,10 +84,16 @@ GENERIC_IRC_COMMAND(DisarmC4IRCCommand) GENERIC_IRC_COMMAND(DisarmBeaconIRCCommand) GENERIC_IRC_COMMAND(MineBanIRCCommand) GENERIC_IRC_COMMAND(KickIRCCommand) +GENERIC_IRC_COMMAND(BanSearchIRCCommand) GENERIC_IRC_COMMAND(TempBanIRCCommand) GENERIC_IRC_COMMAND(KickBanIRCCommand) GENERIC_IRC_COMMAND(AddBanIRCCommand) GENERIC_IRC_COMMAND(UnBanIRCCommand) +GENERIC_IRC_COMMAND(ExemptionSearchIRCCommand) +GENERIC_IRC_COMMAND(BanExemptIRCCommand) +GENERIC_IRC_COMMAND(KickExemptIRCCommand) +GENERIC_IRC_COMMAND(AddExemptionIRCCommand) +GENERIC_IRC_COMMAND(UnExemptIRCCommand) GENERIC_IRC_COMMAND(AddBotsIRCCommand) GENERIC_IRC_COMMAND(KillBotsIRCCommand) GENERIC_IRC_COMMAND(PhaseBotsIRCCommand) diff --git a/RenX.Core/RenX.Core.vcxproj b/RenX.Core/RenX.Core.vcxproj index db0db01..d9de636 100644 --- a/RenX.Core/RenX.Core.vcxproj +++ b/RenX.Core/RenX.Core.vcxproj @@ -74,6 +74,7 @@ + @@ -85,6 +86,7 @@ + diff --git a/RenX.Core/RenX.Core.vcxproj.filters b/RenX.Core/RenX.Core.vcxproj.filters index 1c2ec70..28e0f83 100644 --- a/RenX.Core/RenX.Core.vcxproj.filters +++ b/RenX.Core/RenX.Core.vcxproj.filters @@ -56,6 +56,9 @@ Header Files + + Header Files + @@ -82,5 +85,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/RenX.Core/RenX_BanDatabase.cpp b/RenX.Core/RenX_BanDatabase.cpp index 414f12a..382f5bf 100644 --- a/RenX.Core/RenX_BanDatabase.cpp +++ b/RenX.Core/RenX_BanDatabase.cpp @@ -16,6 +16,7 @@ * Written by Jessica James */ +#include #include #include "Jupiter/IRC_Client.h" #include "Jupiter/INIFile.h" @@ -40,8 +41,16 @@ void RenX::BanDatabase::process_data(Jupiter::DataBuffer &buffer, FILE *file, fp // Read data from buffer to entry entry->flags = buffer.pop(); - entry->timestamp = buffer.pop(); - entry->length = buffer.pop(); + if (RenX::BanDatabase::read_version >= 4U) + { + entry->timestamp = std::chrono::system_clock::time_point(std::chrono::seconds(buffer.pop())); + entry->length = std::chrono::seconds(buffer.pop()); + } + else + { + entry->timestamp = std::chrono::system_clock::from_time_t(buffer.pop()); + entry->length = std::chrono::seconds(static_cast(buffer.pop())); + } entry->steamid = buffer.pop(); entry->ip = buffer.pop(); entry->prefix_length = buffer.pop(); @@ -119,8 +128,8 @@ void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry, FILE *file) // push data from entry to buffer buffer.push(entry->flags); - buffer.push(entry->timestamp); - buffer.push(entry->length); + buffer.push(static_cast(std::chrono::duration_cast(entry->timestamp.time_since_epoch()).count())); + buffer.push(static_cast(entry->length.count())); buffer.push(entry->steamid); buffer.push(entry->ip); buffer.push(entry->prefix_length); @@ -146,12 +155,12 @@ void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry, FILE *file) fgetpos(file, std::addressof(RenX::BanDatabase::eof)); } -void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length, uint16_t flags) +void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, std::chrono::seconds length, uint16_t flags) { Entry *entry = new Entry(); entry->set_active(); entry->flags |= flags; - entry->timestamp = time(0); + entry->timestamp = std::chrono::system_clock::now(); entry->length = length; entry->steamid = player->steamid; entry->ip = player->ip32; @@ -172,12 +181,12 @@ void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player RenX::BanDatabase::write(entry); } -void RenX::BanDatabase::add(const Jupiter::ReadableString &name, uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &rdns, Jupiter::ReadableString &banner, Jupiter::ReadableString &reason, time_t length, uint16_t flags) +void RenX::BanDatabase::add(const Jupiter::ReadableString &name, uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &rdns, const Jupiter::ReadableString &banner, Jupiter::ReadableString &reason, std::chrono::seconds length, uint16_t flags) { Entry *entry = new Entry(); entry->set_active(); entry->flags |= flags; - entry->timestamp = time(0); + entry->timestamp = std::chrono::system_clock::now(); entry->length = length; entry->steamid = steamid; entry->ip = ip; diff --git a/RenX.Core/RenX_BanDatabase.h b/RenX.Core/RenX_BanDatabase.h index 048bc33..c6de7fd 100644 --- a/RenX.Core/RenX_BanDatabase.h +++ b/RenX.Core/RenX_BanDatabase.h @@ -19,12 +19,12 @@ #if !defined _RENX_BANDATABASE_H_HEADER #define _RENX_BANDATABASE_H_HEADER -#include #include #include "Jupiter/Database.h" #include "Jupiter/String.h" #include "Jupiter/CString.h" #include "Jupiter/ArrayList.h" +#include "RenX.h" /** DLL Linkage Nagging */ #if defined _MSC_VER @@ -81,8 +81,8 @@ namespace RenX { fpos_t pos; /** Position of the entry in the database */ uint16_t flags /** Flags affecting this ban entry (See below for flags) */ = 0x00; - time_t timestamp /** Time the ban was created */; - time_t length /** Duration of the ban; 0 if permanent */; + std::chrono::system_clock::time_point timestamp /** Time the ban was created */; + std::chrono::seconds length /** Duration of the ban; 0 if permanent */; uint64_t steamid /** SteamID of the banned player */; uint32_t ip /** IPv4 address of the banned player */; uint8_t prefix_length /** Prefix length for the IPv4 address block */; @@ -101,7 +101,6 @@ namespace RenX static const uint16_t FLAG_TYPE_MINE = 0x0008U; static const uint16_t FLAG_TYPE_LADDER = 0x0004U; static const uint16_t FLAG_TYPE_ALERT = 0x0002U; - inline bool is_active() { return (flags & FLAG_ACTIVE) != 0; }; inline bool is_rdns_ban() { return (flags & FLAG_USE_RDNS) != 0; }; @@ -143,7 +142,7 @@ namespace RenX * @param player Data of the player to be banned * @param length Duration of the ban */ - void add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length, uint16_t flags = RenX::BanDatabase::Entry::FLAG_TYPE_GAME); + void add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, std::chrono::seconds length, uint16_t flags = RenX::BanDatabase::Entry::FLAG_TYPE_GAME); /** * @brief Adds a ban entry for a set of player information and immediately writes it to the database. @@ -156,7 +155,7 @@ namespace RenX * @param reason Reason the player is getting banned * @param length Duration of the ban */ - void add(const Jupiter::ReadableString &name, uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &rdns, Jupiter::ReadableString &banner, Jupiter::ReadableString &reason, time_t length, uint16_t flags = RenX::BanDatabase::Entry::FLAG_TYPE_GAME); + void add(const Jupiter::ReadableString &name, uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &rdns, const Jupiter::ReadableString &banner, Jupiter::ReadableString &reason, std::chrono::seconds length, uint16_t flags = RenX::BanDatabase::Entry::FLAG_TYPE_GAME); /** * @brief Upgrades the ban database to the current write_version. diff --git a/RenX.Core/RenX_ExemptionDatabase.cpp b/RenX.Core/RenX_ExemptionDatabase.cpp new file mode 100644 index 0000000..d55cca2 --- /dev/null +++ b/RenX.Core/RenX_ExemptionDatabase.cpp @@ -0,0 +1,197 @@ +/** +* 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 +*/ + +#include +#include "Jupiter/IRC_Client.h" +#include "Jupiter/INIFile.h" +#include "RenX_PlayerInfo.h" +#include "RenX_ExemptionDatabase.h" +#include "RenX_Core.h" +#include "RenX_Plugin.h" + +using namespace Jupiter::literals; + +RenX::ExemptionDatabase _exemptionDatabase; +RenX::ExemptionDatabase *RenX::exemptionDatabase = &_exemptionDatabase; +RenX::ExemptionDatabase &RenX::defaultExemptionDatabase = _exemptionDatabase; + +void RenX::ExemptionDatabase::process_data(Jupiter::DataBuffer &buffer, FILE *file, fpos_t pos) +{ + RenX::ExemptionDatabase::Entry *entry = new RenX::ExemptionDatabase::Entry(); + entry->pos = pos; + + // Read data from buffer to entry + entry->flags = buffer.pop(); + entry->timestamp = std::chrono::system_clock::time_point(std::chrono::seconds(buffer.pop())); + entry->length = std::chrono::seconds(buffer.pop()); + entry->steamid = buffer.pop(); + entry->ip = buffer.pop(); + entry->prefix_length = buffer.pop(); + entry->setter = buffer.pop(); + + RenX::ExemptionDatabase::entries.add(entry); +} + +void RenX::ExemptionDatabase::process_header(FILE *file) +{ + int chr = fgetc(file); + if (chr != EOF) + RenX::ExemptionDatabase::read_version = chr; +} + +void RenX::ExemptionDatabase::create_header(FILE *file) +{ + fputc(RenX::ExemptionDatabase::write_version, file); +} + +void RenX::ExemptionDatabase::process_file_finish(FILE *file) +{ + fgetpos(file, std::addressof(RenX::ExemptionDatabase::eof)); +} + +void RenX::ExemptionDatabase::upgrade_database() +{ + FILE *file = fopen(RenX::ExemptionDatabase::filename.c_str(), "wb"); + if (file != nullptr) + { + this->create_header(file); + for (size_t index = 0; RenX::ExemptionDatabase::entries.size(); ++index) + RenX::ExemptionDatabase::write(RenX::ExemptionDatabase::entries.get(index), file); + + fclose(file); + } +} + +void RenX::ExemptionDatabase::write(RenX::ExemptionDatabase::Entry *entry) +{ + FILE *file = fopen(filename.c_str(), "r+b"); + fsetpos(file, std::addressof(RenX::ExemptionDatabase::eof)); + if (file != nullptr) + { + RenX::ExemptionDatabase::write(entry, file); + fclose(file); + } +} + +void RenX::ExemptionDatabase::write(RenX::ExemptionDatabase::Entry *entry, FILE *file) +{ + Jupiter::DataBuffer buffer; + fgetpos(file, &entry->pos); + + // push data from entry to buffer + buffer.push(entry->flags); + buffer.push(static_cast(std::chrono::duration_cast(entry->timestamp.time_since_epoch()).count())); + buffer.push(static_cast(entry->length.count())); + buffer.push(entry->steamid); + buffer.push(entry->ip); + buffer.push(entry->prefix_length); + buffer.push(entry->setter); + + // push buffer to file + buffer.push_to(file); + fgetpos(file, std::addressof(RenX::ExemptionDatabase::eof)); +} + +void RenX::ExemptionDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &setter, std::chrono::seconds length, uint8_t flags) +{ + RenX::ExemptionDatabase::add(player->ip32, 32U, player->steamid, setter, length, flags); +} + +void RenX::ExemptionDatabase::add(uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &setter, std::chrono::seconds length, uint8_t flags) +{ + Entry *entry = new Entry(); + entry->set_active(); + entry->flags |= flags; + entry->timestamp = std::chrono::system_clock::now(); + entry->length = length; + entry->steamid = steamid; + entry->ip = ip; + entry->prefix_length = prefix_length; + entry->setter = setter; + + entries.add(entry); + RenX::ExemptionDatabase::write(entry); +} + +bool RenX::ExemptionDatabase::deactivate(size_t index) +{ + RenX::ExemptionDatabase::Entry *entry = RenX::ExemptionDatabase::entries.get(index); + if (entry->is_active()) + { + entry->unset_active(); + FILE *file = fopen(RenX::ExemptionDatabase::filename.c_str(), "r+b"); + if (file != nullptr) + { + fsetpos(file, &entry->pos); + fseek(file, sizeof(size_t), SEEK_CUR); + fwrite(std::addressof(entry->flags), sizeof(entry->flags), 1, file); + fclose(file); + } + return true; + } + return false; +} + +void RenX::ExemptionDatabase::exemption_check(RenX::PlayerInfo *player) +{ + RenX::ExemptionDatabase::Entry *entry; + uint32_t netmask; + size_t index = RenX::ExemptionDatabase::entries.size(); + while (index != 0) + { + entry = RenX::ExemptionDatabase::entries.get(--index); + if (entry->is_active()) + { + if (entry->length == std::chrono::seconds::zero() || entry->timestamp + entry->length < std::chrono::system_clock::now()) + { + netmask = Jupiter_prefix_length_to_netmask(entry->prefix_length); + if ((player->steamid != 0 && entry->steamid == player->steamid) // SteamID exemption + || (player->ip32 != 0U && (player->ip32 & netmask) == (entry->ip & netmask))) // IP address exemption + player->exemption_flags |= entry->flags; + } + else + RenX::ExemptionDatabase::deactivate(index); + } + } +} + +uint8_t RenX::ExemptionDatabase::getVersion() const +{ + return RenX::ExemptionDatabase::write_version; +} + +const Jupiter::ReadableString &RenX::ExemptionDatabase::getFileName() const +{ + return RenX::ExemptionDatabase::filename; +} + +const Jupiter::ArrayList &RenX::ExemptionDatabase::getEntries() const +{ + return RenX::ExemptionDatabase::entries; +} + +RenX::ExemptionDatabase::ExemptionDatabase() +{ + RenX::ExemptionDatabase::filename = Jupiter::IRC::Client::Config->get(STRING_LITERAL_AS_REFERENCE("RenX"), STRING_LITERAL_AS_REFERENCE("ExemptionDB"), STRING_LITERAL_AS_REFERENCE("Exemptions.db")); + this->process_file(filename); +} + +RenX::ExemptionDatabase::~ExemptionDatabase() +{ + RenX::ExemptionDatabase::entries.emptyAndDelete(); +} diff --git a/RenX.Core/RenX_ExemptionDatabase.h b/RenX.Core/RenX_ExemptionDatabase.h new file mode 100644 index 0000000..ca6566f --- /dev/null +++ b/RenX.Core/RenX_ExemptionDatabase.h @@ -0,0 +1,202 @@ +/** + * 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 + */ + +#if !defined _RENX_EXEMPTIONDATABASE_H_HEADER +#define _RENX_EXEMPTIONDATABASE_H_HEADER + +#include +#include +#include "Jupiter/Database.h" +#include "Jupiter/String.h" +#include "Jupiter/CString.h" +#include "Jupiter/ArrayList.h" +#include "RenX.h" + +/** DLL Linkage Nagging */ +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) +#endif + +namespace RenX +{ + struct PlayerInfo; + class Server; + + /** + * @brief Represents the local exemption database. + */ + class RENX_API ExemptionDatabase : public Jupiter::Database + { + public: // Jupiter::Database + /** + * @brief Processes a chunk of data in a database. + * + * @param buffer Buffer to process + * @param file File being processed + * @param pos position that the buffer starts at in the file + */ + void process_data(Jupiter::DataBuffer &buffer, FILE *file, fpos_t pos) override; + + /** + * @brief Processes the header for a database. + * + * @param file File being processed + */ + void process_header(FILE *file) override; + + /** + * @brief Generates a header for a database. + * + * @param file File being created + */ + void create_header(FILE *file) override; + + /** + * @brief Called when process_file() is successfully completed. + * + * @param file File being processed + */ + void process_file_finish(FILE *file) override; + + struct RENX_API Entry + { + fpos_t pos; /** Position of the entry in the database */ + uint8_t flags /** Flags affecting this exemption entry (See below for flags) */ = 0x00; + std::chrono::system_clock::time_point timestamp /** Time the exemption was created */; + std::chrono::seconds length /** Duration of the exemption; 0 if permanent */; + uint64_t steamid /** SteamID of the exempted player */; + uint32_t ip /** IPv4 address of the exempted player */; + uint8_t prefix_length /** Prefix length for the IPv4 address block */; + Jupiter::StringS setter /** Name of the user who added the exemption */; + + static const uint8_t FLAG_ACTIVE = 0x80; + static const uint8_t FLAG_USE_IP = 0x40; + static const uint8_t FLAG_TYPE_KICK = 0x04; + static const uint8_t FLAG_TYPE_BAN = 0x02; + + inline bool is_active() { return (flags & FLAG_ACTIVE) != 0; }; + inline bool is_ip_exemption() { return (flags & FLAG_USE_IP) != 0; }; + inline bool is_type_kick() { return (flags & FLAG_TYPE_KICK) != 0; }; + inline bool is_type_ban() { return (flags & FLAG_TYPE_BAN) != 0; }; + + inline void set_active() { flags |= FLAG_ACTIVE; }; + inline void set_ip_exemption() { flags |= FLAG_USE_IP; }; + inline void set_type_kick() { flags |= FLAG_TYPE_KICK; }; + inline void set_type_ban() { flags |= FLAG_TYPE_BAN; }; + + inline void unset_active() { flags &= ~FLAG_ACTIVE; }; + inline void unset_ip_exemption() { flags &= ~FLAG_USE_IP; }; + inline void unset_type_kick() { flags &= ~FLAG_TYPE_KICK; }; + inline void unset_type_ban() { flags &= ~FLAG_TYPE_BAN; }; + }; + + /** + * @brief Adds an exemption entry for a player and immediately writes it to the database. + * + * @param server Server the player is playing in + * @param player Data of the player to be exempted + * @param length Duration of the exempt + */ + void add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &setter, std::chrono::seconds length, uint8_t flags); + + /** + * @brief Adds an exemption entry for a set of player information and immediately writes it to the database. + * + * @param name Name of the player to exempt + * @param ip IPv4 address of the player to exempt + * @param steamid SteamID of the player to exempt + * @param setter Person implementing the exempt + * @param length Duration of the exemption + */ + void add(uint32_t ip, uint8_t prefix_length, uint64_t steamid, const Jupiter::ReadableString &setter, std::chrono::seconds length, uint8_t flags); + + /** + * @brief Upgrades the exemption database to the current write_version. + */ + void upgrade_database(); + + /** + * @brief Writes an exemption entry to the database. + * + * @param entry Entry to write to the database. + */ + void write(Entry *entry); + + /** + * @brief Writes an exemption entry to the database. + * + * @param entry Entry to write to the database. + * @param file FILE stream to write to. + */ + void write(Entry *entry, FILE *file); + + /** + * @brief Deactivates an exemption entry. + * + * @param index Index of the entry to deactivate. + * @param True if the entry was active and is now inactive, false otherwise. + */ + bool deactivate(size_t index); + + /** + * @brief Checks a player for any relevant ban exemptions, and assigns their exemption_flags + * + * @param player Player to check exemption flags for + */ + void exemption_check(RenX::PlayerInfo *player); + + /** + * @brief Fetches the version of the database file. + * + * @return Database version + */ + uint8_t getVersion() const; + + /** + * @brief Fetches the name of the database file. + * + * @return Database file name + */ + const Jupiter::ReadableString &getFileName() const; + + /** + * @brief Fetches the list of exemption entries. + * + * @return List of entries + */ + const Jupiter::ArrayList &getEntries() const; + + ExemptionDatabase(); + ~ExemptionDatabase(); + + private: + /** Database version */ + const uint8_t write_version = 0U; + uint8_t read_version = write_version; + fpos_t eof; + + Jupiter::CStringS filename; + Jupiter::ArrayList entries; + }; + + RENX_API extern RenX::ExemptionDatabase *exemptionDatabase; + RENX_API extern RenX::ExemptionDatabase &defaultExemptionDatabase; +} + +#endif // _RENX_EXEMPTIONDATABASE_H_HEADER \ No newline at end of file diff --git a/RenX.Core/RenX_PlayerInfo.h b/RenX.Core/RenX_PlayerInfo.h index f70f57f..243f42e 100644 --- a/RenX.Core/RenX_PlayerInfo.h +++ b/RenX.Core/RenX_PlayerInfo.h @@ -54,6 +54,7 @@ namespace RenX uint64_t steamid = 0; uint32_t ip32 = 0; uint16_t ban_flags = 0; + uint8_t exemption_flags = 0; TeamType team = TeamType::Other; int id = 0; bool isBot = false; diff --git a/RenX.Core/RenX_Server.cpp b/RenX.Core/RenX_Server.cpp index e9e9573..2cc6cce 100644 --- a/RenX.Core/RenX_Server.cpp +++ b/RenX.Core/RenX_Server.cpp @@ -29,6 +29,7 @@ #include "RenX_Functions.h" #include "RenX_Plugin.h" #include "RenX_BanDatabase.h" +#include "RenX_ExemptionDatabase.h" #include "RenX_Tags.h" using namespace Jupiter::literals; @@ -369,7 +370,8 @@ void RenX::Server::kickPlayer(int id, const Jupiter::ReadableString &reason) void RenX::Server::kickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason) { - RenX::Server::kickPlayer(player->id, reason); + if ((player->exemption_flags & RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK) == 0) + RenX::Server::kickPlayer(player->id, reason); } void RenX::Server::forceKickPlayer(int id, const Jupiter::ReadableString &reason) @@ -382,7 +384,8 @@ void RenX::Server::forceKickPlayer(int id, const Jupiter::ReadableString &reason void RenX::Server::forceKickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason) { - RenX::Server::forceKickPlayer(player->id, reason); + if ((player->exemption_flags & RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK) == 0) + RenX::Server::forceKickPlayer(player->id, reason); } void RenX::Server::banCheck() @@ -394,6 +397,9 @@ void RenX::Server::banCheck() void RenX::Server::banCheck(RenX::PlayerInfo *player) { + if ((player->exemption_flags & (RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN | RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK)) != 0) + return; + const Jupiter::ArrayList &entries = RenX::banDatabase->getEntries(); RenX::BanDatabase::Entry *entry = nullptr; uint32_t netmask; @@ -406,13 +412,13 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player) { if (last_to_expire[index] == nullptr) last_to_expire[index] = entry; - else if (last_to_expire[index]->length == 0) + else if (last_to_expire[index]->length == std::chrono::seconds::zero()) { // favor older bans if they're also permanent - if (entry->length == 0 && entry->timestamp < last_to_expire[index]->timestamp) + if (entry->length == std::chrono::seconds::zero() && entry->timestamp < last_to_expire[index]->timestamp) last_to_expire[index] = entry; } - else if (entry->length == 0 || entry->timestamp + entry->length > last_to_expire[index]->timestamp + last_to_expire[index]->length) + else if (entry->length == std::chrono::seconds::zero() || entry->timestamp + entry->length > last_to_expire[index]->timestamp + last_to_expire[index]->length) last_to_expire[index] = entry; }; @@ -421,7 +427,7 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player) entry = entries.get(i); if (entry->is_active()) { - if (entry->length != 0 && entry->timestamp + entry->length < time(0)) + if (entry->length != std::chrono::seconds::zero() && entry->timestamp + entry->length < std::chrono::system_clock::now()) banDatabase->deactivate(i); else { @@ -459,8 +465,8 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player) char timeStr[256]; if (last_to_expire[0] != nullptr) // Game ban { - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[0]->timestamp + last_to_expire[0]->length))); - if (last_to_expire[0]->length == 0) + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(last_to_expire[0]->timestamp + last_to_expire[0]->length)))); + if (last_to_expire[0]->length == std::chrono::seconds::zero()) this->forceKickPlayer(player, Jupiter::StringS::Format("You were permanently banned from the server on %s for: %.*s", timeStr, last_to_expire[0]->reason.size(), last_to_expire[0]->reason.ptr())); else this->forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server until %s for: %.*s", timeStr, last_to_expire[0]->reason.size(), last_to_expire[0]->reason.ptr())); @@ -471,9 +477,9 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player) { if (last_to_expire[1] != nullptr) // Chat ban { - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[1]->timestamp + last_to_expire[1]->length))); + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(last_to_expire[1]->timestamp + last_to_expire[1]->length)))); this->mute(player); - if (last_to_expire[1]->length == 0) + if (last_to_expire[1]->length == std::chrono::seconds::zero()) this->sendMessage(player, Jupiter::StringS::Format("You were permanently muted on this server on %s for: %.*s", timeStr, last_to_expire[1]->reason.size(), last_to_expire[1]->reason.ptr())); else this->sendMessage(player, Jupiter::StringS::Format("You are muted on this server until %s for: %.*s", timeStr, last_to_expire[1]->reason.size(), last_to_expire[1]->reason.ptr())); @@ -482,16 +488,16 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player) } else if (last_to_expire[2] != nullptr) // Bot ban { - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[2]->timestamp + last_to_expire[2]->length))); - if (last_to_expire[2]->length == 0) + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(last_to_expire[2]->timestamp + last_to_expire[2]->length)))); + if (last_to_expire[2]->length == std::chrono::seconds::zero()) this->sendMessage(player, Jupiter::StringS::Format("You were permanently bot-muted on this server on %s for: %.*s", timeStr, last_to_expire[2]->reason.size(), last_to_expire[2]->reason.ptr())); else this->sendMessage(player, Jupiter::StringS::Format("You are bot-muted on this server until %s for: %.*s", timeStr, last_to_expire[2]->reason.size(), last_to_expire[2]->reason.ptr())); } if (last_to_expire[3] != nullptr) // Vote ban { - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[3]->timestamp + last_to_expire[3]->length))); - if (last_to_expire[3]->length == 0) + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(last_to_expire[3]->timestamp + last_to_expire[3]->length)))); + if (last_to_expire[3]->length == std::chrono::seconds::zero()) this->sendMessage(player, Jupiter::StringS::Format("You were permanently vote-muted on this server on %s for: %.*s", timeStr, last_to_expire[3]->reason.size(), last_to_expire[3]->reason.ptr())); else this->sendMessage(player, Jupiter::StringS::Format("You are vote-muted on this server until %s for: %.*s", timeStr, last_to_expire[3]->reason.size(), last_to_expire[3]->reason.ptr())); @@ -499,16 +505,16 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player) if (last_to_expire[4] != nullptr) // Mine ban { this->mineBan(player); - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[4]->timestamp + last_to_expire[4]->length))); - if (last_to_expire[4]->length == 0) + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(last_to_expire[4]->timestamp + last_to_expire[4]->length)))); + if (last_to_expire[4]->length == std::chrono::seconds::zero()) this->sendMessage(player, Jupiter::StringS::Format("You were permanently mine-banned on this server on %s for: %.*s", timeStr, last_to_expire[4]->reason.size(), last_to_expire[4]->reason.ptr())); else this->sendMessage(player, Jupiter::StringS::Format("You are mine-banned on this server until %s for: %.*s", timeStr, last_to_expire[4]->reason.size(), last_to_expire[4]->reason.ptr())); } if (last_to_expire[5] != nullptr) // Ladder ban { - strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(last_to_expire[5]->timestamp + last_to_expire[5]->length))); - if (last_to_expire[5]->length == 0) + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(std::chrono::system_clock::to_time_t(last_to_expire[5]->timestamp + last_to_expire[5]->length)))); + if (last_to_expire[5]->length == std::chrono::seconds::zero()) this->sendMessage(player, Jupiter::StringS::Format("You were permanently ladder-banned on this server on %s for: %.*s", timeStr, last_to_expire[5]->reason.size(), last_to_expire[5]->reason.ptr())); else this->sendMessage(player, Jupiter::StringS::Format("You are ladder-banned on this server until %s for: %.*s", timeStr, last_to_expire[5]->reason.size(), last_to_expire[5]->reason.ptr())); @@ -556,24 +562,27 @@ void RenX::Server::banPlayer(int id, const Jupiter::ReadableString &banner, cons } } -void RenX::Server::banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length) +void RenX::Server::banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, std::chrono::seconds length) { - if (RenX::Server::localBan) - RenX::banDatabase->add(this, player, banner, reason, length); - - if (length == 0) + if ((player->exemption_flags & RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN) == 0) { - if (RenX::Server::rconBan) - RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", player->id, reason.size(), reason.ptr())); + if (RenX::Server::localBan) + RenX::banDatabase->add(this, player, banner, reason, length); + + 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())); + else if (banner.isNotEmpty()) + RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are permanently banned from the server by %.*s for: %.*s", banner.size(), banner.ptr(), reason.size(), reason.ptr())); + else + RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are permanently banned from the server for: %.*s", reason.size(), reason.ptr())); + } else if (banner.isNotEmpty()) - RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are permanently banned from the server by %.*s for: %.*s", banner.size(), banner.ptr(), reason.size(), reason.ptr())); + RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server by %.*s for the next %lld days, %.2d:%.2d:%.2d for: %.*s", banner.size(), banner.ptr(), static_cast(length.count() / 86400), static_cast(length.count() % 3600), static_cast((length.count() % 3600) / 60), static_cast(length.count() % 60), reason.size(), reason.ptr())); else - RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are permanently banned from the server for: %.*s", reason.size(), reason.ptr())); + RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server for the next %lld days, %.2d:%.2d:%.2d for: %.*s", static_cast(length.count() / 86400), static_cast(length.count() % 3600), static_cast((length.count() % 3600) / 60), static_cast(length.count() % 60), reason.size(), reason.ptr())); } - else if (banner.isNotEmpty()) - RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server by %.*s for the next %lld days, %.2d:%.2d:%.2d for: %.*s", banner.size(), banner.ptr(), static_cast(length / 86400), static_cast(length % 3600), static_cast((length % 3600) / 60), static_cast(length % 60), reason.size(), reason.ptr())); - else - RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server for the next %lld days, %.2d:%.2d:%.2d for: %.*s", static_cast(length/86400), static_cast(length%3600), static_cast((length%3600)/60), static_cast(length%60), reason.size(), reason.ptr())); } bool RenX::Server::removePlayer(int id) @@ -1224,6 +1233,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) this->players.add(r); r->uuid = calc_uuid(r); + RenX::exemptionDatabase->exemption_check(r); this->banCheck(r); for (size_t i = 0; i < xPlugins.size(); i++) @@ -1253,6 +1263,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) if (recalcUUID) { this->setUUIDIfDifferent(r, calc_uuid(r)); + RenX::exemptionDatabase->exemption_check(r); this->banCheck(r); } } diff --git a/RenX.Core/RenX_Server.h b/RenX.Core/RenX_Server.h index c92917c..a9712ef 100644 --- a/RenX.Core/RenX_Server.h +++ b/RenX.Core/RenX_Server.h @@ -368,7 +368,7 @@ namespace RenX * @param player Data of the player to ban. * @param length Duration of the ban (0 for permanent). */ - void banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, time_t length = 0); + void banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &banner, const Jupiter::ReadableString &reason, std::chrono::seconds length = std::chrono::seconds::zero()); /** * @brief Removes a player's data based on their ID number. diff --git a/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp b/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp index 293e389..adfb300 100644 --- a/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp +++ b/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp @@ -48,7 +48,7 @@ void RenX_ExcessiveHeadshotsPlugin::RenX_OnKill(RenX::Server *server, const RenX { unsigned int flags = 0; std::chrono::milliseconds game_time = server->getGameTime(player); - double kps = game_time == std::chrono::milliseconds(0) ? static_cast(player->kills) : static_cast(player->kills) / static_cast(game_time.count()); + double kps = game_time == std::chrono::milliseconds::zero() ? static_cast(player->kills) : static_cast(player->kills) / static_cast(game_time.count()); if (player->kills >= RenX_ExcessiveHeadshotsPlugin::minKills) flags++; if (RenX::getHeadshotKillRatio(player) >= RenX_ExcessiveHeadshotsPlugin::ratio) flags++; if (RenX::getKillDeathRatio(player) >= RenX_ExcessiveHeadshotsPlugin::minKD) flags++; diff --git a/RenX.Logging/RenX_Logging.cpp b/RenX.Logging/RenX_Logging.cpp index 1662041..6a63cdd 100644 --- a/RenX.Logging/RenX_Logging.cpp +++ b/RenX.Logging/RenX_Logging.cpp @@ -1571,9 +1571,9 @@ void RenX_LoggingPlugin::RenX_OnExecute(RenX::Server *server, const Jupiter::Rea Jupiter::String msg; if (user.equals(RenX::DevBotName)) - msg = this->executeFmt; - else msg = this->devBotExecuteFmt; + else + msg = this->executeFmt; if (msg.isNotEmpty()) { diff --git a/RenX.ModSystem/RenX_ModSystem.cpp b/RenX.ModSystem/RenX_ModSystem.cpp index 4c4f1c1..cb9f9b4 100644 --- a/RenX.ModSystem/RenX_ModSystem.cpp +++ b/RenX.ModSystem/RenX_ModSystem.cpp @@ -175,7 +175,7 @@ int RenX_ModSystemPlugin::auth(RenX::Server *server, const RenX::PlayerInfo *pla if (player->access != 0) { 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->getRCONUsername().equals("DevBot"_jrs)) + if (server->isDevBot()) server->sendData(Jupiter::StringS::Format("d%d\n", player->id)); } Jupiter::String playerName = RenX::getFormattedPlayerName(player); @@ -358,7 +358,7 @@ void RenX_ModSystemPlugin::RenX_OnPlayerDelete(RenX::Server *server, const RenX: void RenX_ModSystemPlugin::RenX_OnIDChange(RenX::Server *server, const RenX::PlayerInfo *player, int oldID) { - if (player->access != 0 && server->getRCONUsername().equals("DevBot"_jrs)) + if (player->access != 0 && server->isDevBot()) server->sendData(Jupiter::StringS::Format("d%d\n", player->id)); } diff --git a/RenX.Warn/RenX_Warn.cpp b/RenX.Warn/RenX_Warn.cpp index c7f53ee..cbeed04 100644 --- a/RenX.Warn/RenX_Warn.cpp +++ b/RenX.Warn/RenX_Warn.cpp @@ -77,7 +77,7 @@ void WarnIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &cha source->sendNotice(nick, Jupiter::StringS::Format("%.*s has been kicked from the server for exceeding the warning limit (%d warnings).", player->name.size(), player->name.ptr(), warns)); break; default: - server->banPlayer(player, STRING_LITERAL_AS_REFERENCE("Jupiter Bot/RenX.Warn"), Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), pluginInstance.warnAction); + server->banPlayer(player, STRING_LITERAL_AS_REFERENCE("Jupiter Bot/RenX.Warn"), Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), std::chrono::seconds(pluginInstance.warnAction)); source->sendNotice(nick, Jupiter::StringS::Format("%.*s has been banned from the server for exceeding the warning limit (%d warnings).", player->name.size(), player->name.ptr(), warns)); break; } @@ -187,7 +187,7 @@ void WarnGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, co source->sendMessage(player, Jupiter::StringS::Format("%.*s has been kicked from the server for exceeding the warning limit (%d warnings).", target->name.size(), target->name.ptr(), warns)); break; default: - source->banPlayer(target, STRING_LITERAL_AS_REFERENCE("Jupiter Bot/RenX.Warn"), Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), pluginInstance.warnAction); + source->banPlayer(target, STRING_LITERAL_AS_REFERENCE("Jupiter Bot/RenX.Warn"), Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), std::chrono::seconds(pluginInstance.warnAction)); source->sendMessage(player, Jupiter::StringS::Format("%.*s has been banned from the server for exceeding the warning limit (%d warnings).", target->name.size(), target->name.ptr(), warns)); break; }