diff --git a/Release/Plugins/RenX.Core.lib b/Release/Plugins/RenX.Core.lib index e59c6e7..b97b55e 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 e9225f8..4a43b03 100644 --- a/RenX.Commands/RenX_Commands.cpp +++ b/RenX.Commands/RenX_Commands.cpp @@ -39,7 +39,8 @@ inline bool togglePhasing(RenX::Server *server) inline void onDie(RenX::Server *server, const RenX::PlayerInfo *player) { - if (player->isBot && server->varData.getBool(STRING_LITERAL_AS_REFERENCE("RenX.Commands"), STRING_LITERAL_AS_REFERENCE("phasing"), false)) server->kickPlayer(player); + if (player->isBot && server->varData.getBool(STRING_LITERAL_AS_REFERENCE("RenX.Commands"), STRING_LITERAL_AS_REFERENCE("phasing"), false)) + server->kickPlayer(player, Jupiter::StringS::empty); } bool RenX_CommandsPlugin::RenX_OnBan(RenX::Server *server, const RenX::PlayerInfo *player, Jupiter::StringType &data) @@ -1517,16 +1518,18 @@ void KickIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &cha RenX::PlayerInfo *player; RenX::Server *server; unsigned int kicks = 0; + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::StringS reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::StringS::gotoWord(parameters, 1, WHITESPACE) : STRING_LITERAL_AS_REFERENCE("No reason"); for (size_t i = 0; i != servers.size(); i++) { server = servers.get(i); if (server != nullptr) { - player = server->getPlayerByPartName(parameters); + player = server->getPlayerByPartName(name); if (player != nullptr) { - server->kickPlayer(player); - kicks++; + server->kickPlayer(player, reason); + ++kicks; } } } @@ -1535,12 +1538,12 @@ void KickIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &cha 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: Kick ")); + else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: Kick [Reason]")); } const Jupiter::ReadableString &KickIRCCommand::getHelp(const Jupiter::ReadableString &) { - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks a player from the game. Syntax: Kick "); + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks a player from the game. Syntax: Kick [Reason]"); return defaultHelp; } @@ -1569,6 +1572,8 @@ void TempBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & RenX::PlayerInfo *player; RenX::Server *server; unsigned int kicks = 0; + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::StringS reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::StringS::gotoWord(parameters, 1, WHITESPACE) : STRING_LITERAL_AS_REFERENCE("No reason"); Jupiter::String banner(nick.size() + 4); banner += nick; banner += "@IRC"; @@ -1577,11 +1582,11 @@ void TempBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & server = servers.get(i); if (server != nullptr) { - player = server->getPlayerByPartName(parameters); + player = server->getPlayerByPartName(name); if (player != nullptr) { player->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), nick); - server->banPlayer(player, pluginInstance.getTBanTime()); + server->banPlayer(player, reason, pluginInstance.getTBanTime()); kicks++; } } @@ -1591,12 +1596,12 @@ void TempBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & 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 ")); + 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 "); + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and temporarily bans a player from the game. Syntax: TempBan [Reason]"); return defaultHelp; } @@ -1625,6 +1630,8 @@ void KickBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & RenX::PlayerInfo *player; RenX::Server *server; unsigned int kicks = 0; + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::StringS reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::StringS::gotoWord(parameters, 1, WHITESPACE) : STRING_LITERAL_AS_REFERENCE("No reason"); Jupiter::String banner(nick.size() + 4); banner += nick; banner += "@IRC"; @@ -1637,7 +1644,7 @@ void KickBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & if (player != nullptr) { player->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), nick); - server->banPlayer(player); + server->banPlayer(player, reason); kicks++; } } @@ -1647,12 +1654,12 @@ void KickBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString & 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: KickBan ")); + else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: KickBan [Reason]")); } const Jupiter::ReadableString &KickBanIRCCommand::getHelp(const Jupiter::ReadableString &) { - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and bans a player from the game. Syntax: KickBan "); + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and bans a player from the game. Syntax: KickBan [Reason]"); return defaultHelp; } @@ -2412,7 +2419,9 @@ void KickGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, co { if (parameters.isEmpty() == false) { - RenX::PlayerInfo *target = source->getPlayerByPartName(parameters); + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::StringS reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::StringS::gotoWord(parameters, 1, WHITESPACE) : STRING_LITERAL_AS_REFERENCE("No reason"); + RenX::PlayerInfo *target = source->getPlayerByPartName(name); if (target == nullptr) source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Player not found.")); else if (player == target) @@ -2421,17 +2430,17 @@ void KickGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, co source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: You can not kick higher level moderators.")); else { - source->kickPlayer(target); + source->kickPlayer(target, reason); source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Player has been kicked from the game.")); } } else - source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Too few parameters. Syntax: kick ")); + source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Too few parameters. Syntax: kick [Reason]")); } const Jupiter::ReadableString &KickGameCommand::getHelp(const Jupiter::ReadableString &) { - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks a player from the game. Syntax: kick "); + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks a player from the game. Syntax: kick [Reason]"); return defaultHelp; } @@ -2451,7 +2460,9 @@ void TempBanGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, { if (parameters.isEmpty() == false) { - RenX::PlayerInfo *target = source->getPlayerByPartName(parameters); + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::StringS reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::StringS::gotoWord(parameters, 1, WHITESPACE) : STRING_LITERAL_AS_REFERENCE("No reason"); + RenX::PlayerInfo *target = source->getPlayerByPartName(name); if (target == nullptr) source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Player not found.")); else if (player == target) @@ -2461,17 +2472,17 @@ void TempBanGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, else { target->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), player->name); - source->banPlayer(target, pluginInstance.getTBanTime()); + source->banPlayer(target, reason, pluginInstance.getTBanTime()); source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Player has been temporarily banned and kicked from the game.")); } } else - source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Too few parameters. Syntax: tban ")); + source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Too few parameters. Syntax: tban [Reason]")); } const Jupiter::ReadableString &TempBanGameCommand::getHelp(const Jupiter::ReadableString &) { - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and temporarily bans a player from the game. Syntax: tban "); + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and temporarily bans a player from the game. Syntax: tban [Reason]"); return defaultHelp; } @@ -2492,6 +2503,8 @@ void KickBanGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, { if (parameters.isEmpty() == false) { + Jupiter::StringS name = Jupiter::StringS::getWord(parameters, 0, WHITESPACE); + Jupiter::StringS reason = parameters.wordCount(WHITESPACE) > 1 ? Jupiter::StringS::gotoWord(parameters, 1, WHITESPACE) : STRING_LITERAL_AS_REFERENCE("No reason"); RenX::PlayerInfo *target = source->getPlayerByPartName(parameters); if (target == nullptr) source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Player not found.")); @@ -2502,17 +2515,17 @@ void KickBanGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, else { target->varData.set(pluginInstance.getName(), STRING_LITERAL_AS_REFERENCE("banner"), player->name); - source->banPlayer(target); + source->banPlayer(target, reason); source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Player has been banned and kicked from the game.")); } } else - source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Too few parameters. Syntax: ban ")); + source->sendMessage(player, STRING_LITERAL_AS_REFERENCE("Error: Too few parameters. Syntax: ban [reason]")); } const Jupiter::ReadableString &KickBanGameCommand::getHelp(const Jupiter::ReadableString &) { - static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and bans a player from the game. Syntax: ban "); + static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and bans a player from the game. Syntax: ban [reason]"); return defaultHelp; } diff --git a/RenX.Core/RenX_BanDatabase.cpp b/RenX.Core/RenX_BanDatabase.cpp index 92710d4..d1b435a 100644 --- a/RenX.Core/RenX_BanDatabase.cpp +++ b/RenX.Core/RenX_BanDatabase.cpp @@ -27,6 +27,9 @@ RenX::BanDatabase _banDatabase; RenX::BanDatabase *RenX::banDatabase = &_banDatabase; RenX::BanDatabase &RenX::defaultBanDatabase = _banDatabase; +const Jupiter::ReferenceString DEFAULT_REASON = "(No reason information provided)"; +const uint8_t write_version = 1; + bool RenX::BanDatabase::load(const Jupiter::ReadableString &fname) { RenX::BanDatabase::filename = fname; @@ -40,6 +43,9 @@ bool RenX::BanDatabase::load(const Jupiter::ReadableString &fname) Jupiter::String playerName(16); Jupiter::String key(32); Jupiter::String value(32); + Jupiter::String reason(128); + if (RenX::BanDatabase::version < 1) + reason = DEFAULT_REASON; Entry *entry; int c; while (!feof(file)) @@ -72,6 +78,24 @@ bool RenX::BanDatabase::load(const Jupiter::ReadableString &fname) } entry->name = playerName; + // load reason + if (RenX::BanDatabase::version >= 1) + { + reason.truncate(reason.size()); + c = fgetc(file); + while (c != '\n' && c != '\0') + { + if (c == EOF) + { + fprintf(stderr, "ERROR: Unexpected EOF in %s at %lu", RenX::BanDatabase::filename.c_str(), ftell(file)); + break; + } + reason += c; + c = fgetc(file); + } + } + entry->reason = reason; + // load variable data while (c == '\0') { @@ -109,11 +133,23 @@ bool RenX::BanDatabase::load(const Jupiter::ReadableString &fname) entries.add(entry); } fclose(file); + if (RenX::BanDatabase::version != write_version) + { + file = fopen(RenX::BanDatabase::filename.c_str(), "wb"); + if (file != nullptr) + { + size_t index = 0; + while (index != RenX::BanDatabase::entries.size()) + RenX::BanDatabase::write(RenX::BanDatabase::entries.get(++index), file); + } + fprintf(stdout, "Updated BanDatabase file \"%s\" from version %d to %d.", RenX::BanDatabase::filename.c_str(), RenX::BanDatabase::version, write_version); + RenX::BanDatabase::version = write_version; + } return true; } else { - RenX::BanDatabase::version = 0; + RenX::BanDatabase::version = write_version; file = fopen(RenX::BanDatabase::filename.c_str(), "ab"); if (file != nullptr) { @@ -126,7 +162,39 @@ bool RenX::BanDatabase::load(const Jupiter::ReadableString &fname) } } -void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player, time_t length) +void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry) +{ + FILE *file = fopen(RenX::BanDatabase::filename.c_str(), "ab"); + if (file != nullptr) + { + RenX::BanDatabase::write(entry, file); + fclose(file); + } +} + +void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry, FILE *file) +{ + fgetpos(file, &entry->pos); + fwrite(&entry->active, 1, 1, file); + fwrite(&entry->timestamp, sizeof(time_t), 1, file); + fwrite(&entry->length, sizeof(time_t), 1, file); + fwrite(&entry->steamid, sizeof(uint64_t), 1, file); + fwrite(&entry->ip, sizeof(uint32_t), 1, file); + fwrite(entry->name.ptr(), sizeof(char), entry->name.size(), file); + + for (size_t index = 0; index != entry->varData.size(); ++index) + { + const Jupiter::INIFile::Section::KeyValuePair *pair = entry->varData.getPair(index); + fputc('\0', file); + fwrite(pair->getKey().ptr(), sizeof(char), pair->getKey().size(), file); + fputc('\0', file); + fwrite(pair->getValue().ptr(), sizeof(char), pair->getValue().size(), file); + } + + fputc('\n', file); +} + +void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length) { Entry *entry = new Entry(); entry->active = 1; @@ -135,36 +203,16 @@ void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player entry->steamid = player->steamid; entry->ip = player->ip32; entry->name = player->name; - entries.add(entry); - FILE *file = fopen(RenX::BanDatabase::filename.c_str(), "ab"); - if (file != nullptr) - { - fgetpos(file, &entry->pos); - fwrite(&entry->active, 1, 1, file); - fwrite(&entry->timestamp, sizeof(time_t), 1, file); - fwrite(&entry->length, sizeof(time_t), 1, file); - fwrite(&entry->steamid, sizeof(uint64_t), 1, file); - fwrite(&entry->ip, sizeof(uint32_t), 1, file); - fwrite(entry->name.ptr(), sizeof(char), entry->name.size(), file); - // add plugin data - Jupiter::String pluginData; - Jupiter::ArrayList &xPlugins = *RenX::getCore()->getPlugins(); - for (size_t i = 0; i < xPlugins.size(); i++) - if (xPlugins.get(i)->RenX_OnBan(server, player, pluginData)) - { - const Jupiter::ReadableString &pluginName = xPlugins.get(i)->getName(); - fputc('\0', file); - fwrite(pluginName.ptr(), sizeof(char), pluginName.size(), file); - fputc('\0', file); - fwrite(pluginData.ptr(), sizeof(char), pluginData.size(), file); - entry->varData.set(pluginName, pluginData); - } + // add plugin data + Jupiter::String pluginData; + Jupiter::ArrayList &xPlugins = *RenX::getCore()->getPlugins(); + for (size_t i = 0; i < xPlugins.size(); i++) + if (xPlugins.get(i)->RenX_OnBan(server, player, pluginData)) + entry->varData.set(xPlugins.get(i)->getName(), pluginData); - - fputc('\n', file); - fclose(file); - } + entries.add(entry); + RenX::BanDatabase::write(entry); } bool RenX::BanDatabase::deactivate(size_t index) diff --git a/RenX.Core/RenX_BanDatabase.h b/RenX.Core/RenX_BanDatabase.h index a8eb205..b2599d9 100644 --- a/RenX.Core/RenX_BanDatabase.h +++ b/RenX.Core/RenX_BanDatabase.h @@ -53,6 +53,7 @@ namespace RenX uint64_t steamid /** SteamID of the banned player */; uint32_t ip /** IPv4 address of the banned player */; Jupiter::StringS name /** Name of the banned player */; + Jupiter::StringS reason /** Reason the player was banned */; Jupiter::INIFile::Section varData; /** Variable entry data */ }; @@ -72,7 +73,22 @@ 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, time_t length); + void add(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length); + + /** + * @brief Writes a ban file to the database. + * + * @param entry Entry to write to the database. + */ + void write(Entry *entry); + + /** + * @brief Writes a ban file to the database. + * + * @param entry Entry to write to the database. + * @param file FILE stream to write to. + */ + static void write(Entry *entry, FILE *file); /** * @brief Deactivates a ban entry. diff --git a/RenX.Core/RenX_Server.cpp b/RenX.Core/RenX_Server.cpp index b45e07d..68cfa46 100644 --- a/RenX.Core/RenX_Server.cpp +++ b/RenX.Core/RenX_Server.cpp @@ -278,37 +278,58 @@ Jupiter::StringS RenX::Server::formatSteamID(uint64_t id) const } } -void RenX::Server::kickPlayer(int id) +void RenX::Server::kickPlayer(int id, const Jupiter::ReadableString &reason) { - RenX::Server::sock.send(Jupiter::StringS::Format("ckick pid%d\n", id)); + if (reason.isEmpty()) + RenX::Server::sock.send(Jupiter::StringS::Format("ckick pid%d\n", id)); + else + RenX::Server::sock.send(Jupiter::StringS::Format("ckick pid%d %.*s\n", id, reason.size(), reason.ptr())); +} + +void RenX::Server::kickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason) +{ + RenX::Server::kickPlayer(player->id, reason); +} + +void RenX::Server::forceKickPlayer(int id, const Jupiter::ReadableString &reason) +{ + if (reason.isEmpty()) + RenX::Server::sock.send(Jupiter::StringS::Format("cfkick pid%d You were kicked from the server.\n", id)); + else + RenX::Server::sock.send(Jupiter::StringS::Format("cfkick pid%d %.*s\n", id, reason.size(), reason.ptr())); } -void RenX::Server::kickPlayer(const RenX::PlayerInfo *player) +void RenX::Server::forceKickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason) { - RenX::Server::kickPlayer(player->id); + RenX::Server::forceKickPlayer(player->id, reason); } -void RenX::Server::banPlayer(int id) +void RenX::Server::banPlayer(int id, const Jupiter::ReadableString &reason) { if (RenX::Server::rconBan) - RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d\n", id)); + RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", id, reason.size(), reason.ptr())); else { RenX::PlayerInfo *player = RenX::Server::getPlayer(id); if (player != nullptr) - RenX::Server::banPlayer(player); + RenX::Server::banPlayer(player, reason); } } -void RenX::Server::banPlayer(const RenX::PlayerInfo *player, time_t length) +void RenX::Server::banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length) { - if (RenX::Server::rconBan && length == 0) - RenX::Server::sock.send(Jupiter::StringS::Format("cadminkickban pid%d\n", player->id)); - else - RenX::Server::kickPlayer(player); - if (RenX::Server::localBan) - RenX::banDatabase->add(this, player, length); + RenX::banDatabase->add(this, player, reason, length); + + if (length == 0) + { + if (RenX::Server::rconBan) + RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", player->id, 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 + RenX::Server::forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server for the next %d days, %d:%d:%d for: %.*s", length/3600, length%3600, length/60, length%60, reason.size(), reason.ptr())); } bool RenX::Server::removePlayer(int id) @@ -906,7 +927,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) auto banCheck = [this](const RenX::PlayerInfo *player) { const Jupiter::ArrayList &entries = RenX::banDatabase->getEntries(); - RenX::BanDatabase::Entry *entry; + RenX::BanDatabase::Entry *entry = nullptr; for (size_t i = 0; i != entries.size(); i++) { entry = entries.get(i); @@ -914,15 +935,25 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) { if (entry->length != 0 && entry->timestamp + entry->length < time(0)) banDatabase->deactivate(i); - else if (this->localSteamBan && entry->steamid != 0 && entry->steamid == player->steamid) - return true; - else if (this->localIPBan && entry->ip != 0 && entry->ip == player->ip32) - return true; - else if (this->localNameBan && entry->name.isEmpty() == false && entry->name.equalsi(player->name)) - return true; + else if ((this->localSteamBan && entry->steamid != 0 && entry->steamid == player->steamid) + || (this->localIPBan && entry->ip != 0 && entry->ip == player->ip32) + || (this->localNameBan && entry->name.isEmpty() == false && entry->name.equalsi(player->name))) + { + char timeStr[256]; + if (entry->length == 0) + { + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(&(entry->timestamp))); + this->forceKickPlayer(player, Jupiter::StringS::Format("You are were permanently banned from the server on %s for: %.*s", timeStr, entry->reason.size(), entry->reason.ptr())); + } + else + { + strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof(entry->timestamp + entry->length))); + this->forceKickPlayer(player, Jupiter::StringS::Format("You are banned from the server until %s for: %.*s", timeStr, entry->reason.size(), entry->reason.ptr())); + } + return; + } } } - return false; }; auto getPlayerOrAdd = [&](const Jupiter::ReadableString &name, int id, RenX::TeamType team, bool isBot, uint64_t steamid, const Jupiter::ReadableString &ip) { @@ -971,8 +1002,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) if (recalcUUID) { this->setUUIDIfDifferent(r, calc_uuid(r)); - if (banCheck(r)) - this->kickPlayer(r); + banCheck(r); } } return r; @@ -1868,8 +1898,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line) if (player != nullptr) { player->id = buff.getToken(3, RenX::DelimC).asInt(); - if (banCheck(player)) - this->kickPlayer(player); + banCheck(player); for (size_t i = 0; i < xPlugins.size(); i++) xPlugins.get(i)->RenX_OnIDChange(this, player, oldID); } diff --git a/RenX.Core/RenX_Server.h b/RenX.Core/RenX_Server.h index 1635d11..812d3ec 100644 --- a/RenX.Core/RenX_Server.h +++ b/RenX.Core/RenX_Server.h @@ -244,21 +244,35 @@ namespace RenX * * @param id Player ID of the player to kick. */ - void kickPlayer(int id); + void kickPlayer(int id, const Jupiter::ReadableString &reason); /** * @brief Kicks a player from the server. * * @param player Data of the player to kick. */ - void kickPlayer(const RenX::PlayerInfo *player); + void kickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason); + + /** + * @brief Kicks a player from the server. + * + * @param id Player ID of the player to kick. + */ + void forceKickPlayer(int id, const Jupiter::ReadableString &reason); + + /** + * @brief Kicks a player from the server. + * + * @param player Data of the player to kick. + */ + void forceKickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason); /** * @brief Bans a player from the server. * * @param id Player ID of the player to ban. */ - void banPlayer(int id); + void banPlayer(int id, const Jupiter::ReadableString &reason); /** * @brief Bans a player from the server. @@ -266,7 +280,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, time_t length = 0); + void banPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason, time_t length = 0); /** * @brief Removes a player's data based on their ID number. diff --git a/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp b/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp index f8be8b4..e766023 100644 --- a/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp +++ b/RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp @@ -53,7 +53,7 @@ void RenX_ExcessiveHeadshotsPlugin::RenX_OnKill(RenX::Server *server, const RenX if (flags >= RenX_ExcessiveHeadshotsPlugin::minFlags) { - server->kickPlayer(player); + server->banPlayer(player, STRING_LITERAL_AS_REFERENCE("Aimbot detected")); server->sendPubChan(IRCCOLOR "13[Aimbot]" IRCCOLOR " %.*s was banned from the server! Kills: %u - Deaths: %u - Headshots: %u", player->name.size(), player->name.ptr(), player->kills, player->deaths, player->headshots); const Jupiter::ReadableString &steamid = server->formatSteamID(player); server->sendAdmChan(IRCCOLOR "13[Aimbot]" IRCCOLOR " %.*s was banned from the server! Kills: %u - Deaths: %u - Headshots: %u - IP: " IRCBOLD "%.*s" IRCBOLD " - Steam ID: " IRCBOLD "%.*s" IRCBOLD, player->name.size(), player->name.ptr(), player->kills, player->deaths, player->headshots, player->ip.size(), player->ip.ptr(), steamid.size(), steamid.ptr()); diff --git a/RenX.ModSystem/RenX_ModSystem.cpp b/RenX.ModSystem/RenX_ModSystem.cpp index b68ecf6..40d0e80 100644 --- a/RenX.ModSystem/RenX_ModSystem.cpp +++ b/RenX.ModSystem/RenX_ModSystem.cpp @@ -197,7 +197,7 @@ int RenX_ModSystemPlugin::auth(RenX::Server *server, const RenX::PlayerInfo *pla } else if (kickLockMismatch_l) { - server->kickPlayer(player); + server->kickPlayer(player, STRING_LITERAL_AS_REFERENCE("Moderator entry lock mismatch")); return -1; } } diff --git a/RenX.Warn/RenX_Warn.cpp b/RenX.Warn/RenX_Warn.cpp index 3c96713..22d0765 100644 --- a/RenX.Warn/RenX_Warn.cpp +++ b/RenX.Warn/RenX_Warn.cpp @@ -72,11 +72,11 @@ void WarnIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &cha switch (pluginInstance.warnAction) { case -1: - server->kickPlayer(player); + server->kickPlayer(player, Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns)); 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, pluginInstance.warnAction); + server->banPlayer(player, Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), 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; } @@ -182,11 +182,11 @@ void WarnGameCommand::trigger(RenX::Server *source, RenX::PlayerInfo *player, co switch (pluginInstance.warnAction) { case -1: - source->kickPlayer(target); + source->kickPlayer(target, Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns)); 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, pluginInstance.warnAction); + source->banPlayer(target, Jupiter::StringS::Format("Warning limit reached (%d warnings)", warns), 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; }