From f66ffb825783bca2c8593a945ff2d0eb9997b71f Mon Sep 17 00:00:00 2001 From: JAJames Date: Wed, 27 Jan 2016 22:25:35 -0500 Subject: [PATCH] General code improvements RenX.Core: * Added Ban/Kick Exemptions --- Bot/Main.cpp | 2 +- Release/Bot.lib | Bin 24762 -> 24762 bytes Release/Plugins/RenX.Core.lib | Bin 156476 -> 172438 bytes RenX.Commands/RenX_Commands.cpp | 797 +++++++++++++----- RenX.Commands/RenX_Commands.h | 12 +- RenX.Core/RenX.Core.vcxproj | 2 + RenX.Core/RenX.Core.vcxproj.filters | 6 + RenX.Core/RenX_BanDatabase.cpp | 25 +- RenX.Core/RenX_BanDatabase.h | 11 +- RenX.Core/RenX_ExemptionDatabase.cpp | 197 +++++ RenX.Core/RenX_ExemptionDatabase.h | 202 +++++ RenX.Core/RenX_PlayerInfo.h | 1 + RenX.Core/RenX_Server.cpp | 73 +- RenX.Core/RenX_Server.h | 2 +- .../ExcessiveHeadshots.cpp | 2 +- RenX.Logging/RenX_Logging.cpp | 4 +- RenX.ModSystem/RenX_ModSystem.cpp | 4 +- RenX.Warn/RenX_Warn.cpp | 4 +- 18 files changed, 1090 insertions(+), 254 deletions(-) create mode 100644 RenX.Core/RenX_ExemptionDatabase.cpp create mode 100644 RenX.Core/RenX_ExemptionDatabase.h 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 306934dc44a4b0e5c2346ea92354256dc8915d86..3ae9a14c4fbcbedb7452bc9ea83a48f1dfb49b73 100644 GIT binary patch delta 1756 zcmZuxJxo(!6m10*g;=D3k5qmtAQ+4l1Cmlp`zS4xwv-mij}+QQ2$LZW#5gbzLmW_% z+&CFy7#xg2Bn%`Z{&Zw8#L>irI2Z>9aWD=h>iypRE_@hZf%Bbv?mhSCZD!4zS@Xj) zdCum`EiG&IX&e0o_Fip$#dcf%yJSCYZEkI?rl0hQusO$6HT8Tw(fZ9y0my))vnWTP z)6N_Peb7eea+^(}kaaZ*B7ArGWxBOcV*w(AXH6!P*^~=EDm1vK7r=OXiDYoh$E!+= zp%Qa@_Ba}>xUGdksRosD@fXY%E9|)I6v`tXQ7Vy=N0c&H)bkHy(_pdPYUn>}cgtXG z3jCF5sb@q%7d*sW_9PT^XoS%7-hPSBdCB>zcUB@zIw2`NB9S$T?C3s;WL+9~I!G(g z!EV)_oy4BK%dtVyCr1hRYElbpw6jbCI@~+mX%^11#@!?iXc@!+{+tM(N$7 z4cMTHL(0kXv;13EOte^-OW{6lhh-m^?B#GJ zQf!PU6p6A2+{KNEtXM(GYqU3}@Lu;?i^NShvD|tr*OQr;fY@jY%*IP4&77dXvFMaU zg!$DtCHNJU<{{HZyv4rj3eOU=8j}uTf<~NlwyYB>NAeZ@&nb$_#&|o#G4a{B<9s$U zt8v0zsCE%A6rLXQfTxcaXE|98-Tl;$vHrUU%Q!B4iZU+2cA^Hn177)#1sHD^@U6*s z`!rB3-`Kn(!RC1gTL~_A&Z~iAkZi_LqZw_!sSTk`p1LSK_Np6fNUlOC`DXgUpYbh)qhM73`NYrxw8dV!Xh6@>M=K}u?g-=i) zRZnnZRBqMi-VVtNiY51!G)d#!PTo=aKRren!#75JwXe;$R#c#KAb2sP}vKyYOLv15lemy{So zCGPR=ax_@+SPO(w4Jzf}FPIChuq5q`aBZILi z@K>Ux-Vp^|^b$AYO)BWn2%%?v{Suw`k@FY6If*psgrxPTL{=rTt@|aCb!*`5AgyEv zyH$I39DDZGVuO@ljuPFYS z6v7OnKN%Hfdb`QYbT^x^hbYm9?y!_(Bo<;s$gPJc_4^Pfne5Y`zK2jx&oo6Z+ckL8 zLmSk9&Z@))ohYh?KRw)gJ*a^*OmWe0T4v>mY=>pbgpxZ#w0ML|;XZCfWFMF9<DV;;G#wP0={FQn!@;&x}#V2xjzQ#+`aQJReggVC=AcoO{Z5_-I_} lii^liQ+ivPKZxQu7bN;Ht?v!gkSm;EIl-HqpyA|m37DCot=iB?x8D;5wS||Tk7~U|nc|&1va8UmHfdA`;p^X~~ z^yf>CCgg~-i1Wes5a&Z*C(fNm5$FC3iSsAlB<>4tA?~^}i1U>PiSwrq5a+A#_s`wS z=))Hi2gp79QAX!15f^FieT+`MmbggIyn@k*bHqh@&-;8j9zKEYxQ5Z?rxF+G`TH5Q zE+Q^c^L|RsA74T!{m!Yx0dk-5ASLIcE@5;Pd|pM|sXG`Q|7GF=IbV7Mqdz>6I7lD; zDWi{FNgSkmKg8(czz4{^?E{S74&_IBCzJ>2_Pstm1fM{+{+!V}t|u<{_svlUp^Pg1oZK18Qt?#;voGV;6u9SeU#i=t6;N0?;K=w%g2aI3G{jk&!JRTUi&3R9>_*I0QP`Xfi#px;1hKFXFn#+*DfUP*r|-he??rR(>}{+2K0n< z#R{VV_!;S?pJ8;-k;FxMOOer=ZzC?!O+REbJVRWhv+In`g|Z;cgDoJ<{fyE14-gk= zu0zTB+QZO)T?l>JUgEC*rcXQI6KD)%BfaHeO3q!-uOWRHYys&bgpzv==m%-&SW3

2E+j(u2^RkskRjqemYi4kf6UM}hwRI!f-b-v|Cc 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 O$aoLRV5?b0%Kri3DC_(H delta 19619 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 Su;s3`r38a*x}LdK)&4IsJu>qE 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; }