Browse Source

General code improvements

RenX.Core:
* Added Ban/Kick Exemptions
pull/3/head
Jessica James 9 years ago
parent
commit
f66ffb8257
  1. 2
      Bot/Main.cpp
  2. BIN
      Release/Bot.lib
  3. BIN
      Release/Plugins/RenX.Core.lib
  4. 795
      RenX.Commands/RenX_Commands.cpp
  5. 12
      RenX.Commands/RenX_Commands.h
  6. 2
      RenX.Core/RenX.Core.vcxproj
  7. 6
      RenX.Core/RenX.Core.vcxproj.filters
  8. 25
      RenX.Core/RenX_BanDatabase.cpp
  9. 11
      RenX.Core/RenX_BanDatabase.h
  10. 197
      RenX.Core/RenX_ExemptionDatabase.cpp
  11. 202
      RenX.Core/RenX_ExemptionDatabase.h
  12. 1
      RenX.Core/RenX_PlayerInfo.h
  13. 51
      RenX.Core/RenX_Server.cpp
  14. 2
      RenX.Core/RenX_Server.h
  15. 2
      RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp
  16. 4
      RenX.Logging/RenX_Logging.cpp
  17. 4
      RenX.ModSystem/RenX_ModSystem.cpp
  18. 4
      RenX.Warn/RenX_Warn.cpp

2
Bot/Main.cpp

@ -67,7 +67,7 @@ int main(int argc, const char **args)
std::set_terminate(onTerminate); std::set_terminate(onTerminate);
std::thread inputThread(inputLoop); std::thread inputThread(inputLoop);
srand(static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count())); srand(static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()));
puts(Jupiter::copyright); puts(Jupiter::copyright);
const char *configFileName = CONFIG_INI; const char *configFileName = CONFIG_INI;

BIN
Release/Bot.lib

Binary file not shown.

BIN
Release/Plugins/RenX.Core.lib

Binary file not shown.

795
RenX.Commands/RenX_Commands.cpp

@ -27,6 +27,7 @@
#include "RenX_BuildingInfo.h" #include "RenX_BuildingInfo.h"
#include "RenX_Functions.h" #include "RenX_Functions.h"
#include "RenX_BanDatabase.h" #include "RenX_BanDatabase.h"
#include "RenX_ExemptionDatabase.h"
#include "RenX_Tags.h" #include "RenX_Tags.h"
using namespace Jupiter::literals; using namespace Jupiter::literals;
@ -65,7 +66,7 @@ void RenX_CommandsPlugin::RenX_OnDie(RenX::Server *server, const RenX::PlayerInf
int RenX_CommandsPlugin::OnRehash() 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::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::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("%")); 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; return 0;
} }
time_t RenX_CommandsPlugin::getTBanTime() const std::chrono::seconds RenX_CommandsPlugin::getTBanTime() const
{ {
return RenX_CommandsPlugin::_defaultTempBanTime; return RenX_CommandsPlugin::_defaultTempBanTime;
} }
@ -1130,144 +1131,6 @@ const Jupiter::ReadableString &ModsIRCCommand::getHelp(const Jupiter::ReadableSt
IRC_COMMAND_INIT(ModsIRCCommand) 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 &parameters)
{
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<bool(unsigned int)> 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] <player ip/steam/name/banner>");
return defaultHelp;
}
IRC_COMMAND_INIT(BanSearchIRCCommand)
// ShowRules IRC Command // ShowRules IRC Command
void ShowRulesIRCCommand::create() void ShowRulesIRCCommand::create()
@ -1914,78 +1777,218 @@ const Jupiter::ReadableString &KickIRCCommand::getHelp(const Jupiter::ReadableSt
IRC_COMMAND_INIT(KickIRCCommand) 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("bansearch"));
this->addTrigger(STRING_LITERAL_AS_REFERENCE("tb")); this->addTrigger(STRING_LITERAL_AS_REFERENCE("bsearch"));
this->addTrigger(STRING_LITERAL_AS_REFERENCE("tempban")); this->addTrigger(STRING_LITERAL_AS_REFERENCE("banfind"));
this->setAccessLevel(3); 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 &parameters) void BanSearchIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString &parameters)
{ {
auto entries = RenX::banDatabase->getEntries();
if (parameters.isNotEmpty()) if (parameters.isNotEmpty())
{ {
Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); if (entries.size() == 0)
if (chan != nullptr) source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("The ban database is empty!"));
{ else
Jupiter::ArrayList<RenX::Server> 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); RenX::BanDatabase::Entry *entry;
if (server != nullptr) Jupiter::ReferenceString params = Jupiter::ReferenceString::gotoWord(parameters, 1, WHITESPACE);
std::function<bool(unsigned int)> isMatch = [&](unsigned int type_l) -> bool
{ {
player = server->getPlayerByPartName(name); switch (type_l)
if (player != nullptr)
{ {
server->banPlayer(player, banner, reason, pluginInstance.getTBanTime()); default:
kicks++; case 0: // ANY
} return isMatch(1) || isMatch(2) || isMatch(3) || isMatch(4);
} case 1: // ALL
} return true;
source->sendMessage(channel, Jupiter::StringS::Format("%u players kicked.", kicks)); case 2: // IP
} return entry->ip == params.asUnsignedInt();
else source->sendMessage(channel, STRING_LITERAL_AS_REFERENCE("Error: Channel not attached to any connected Renegade X servers.")); case 3: // RDNS
} return entry->rdns.equals(params);
} case 4: // STEAM
else source->sendNotice(nick, STRING_LITERAL_AS_REFERENCE("Error: Too Few Parameters. Syntax: TempBan <Player> [Reason]")); 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();
} }
};
const Jupiter::ReadableString &TempBanIRCCommand::getHelp(const Jupiter::ReadableString &) 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
{ {
static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Kicks and temporarily bans a player from the game. Syntax: TempBan <Player> [Reason]"); type = 0;
return defaultHelp; params = parameters;
} }
IRC_COMMAND_INIT(TempBanIRCCommand) Jupiter::String out(256);
Jupiter::String types(64);
// KickBan IRC Command char timeStr[256];
for (size_t i = 0; i != entries.size(); i++)
void KickBanIRCCommand::create()
{ {
this->addTrigger(STRING_LITERAL_AS_REFERENCE("kickban")); entry = entries.get(i);
this->addTrigger(STRING_LITERAL_AS_REFERENCE("kb")); if (isMatch(type))
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 &parameters)
{ {
if (parameters.isNotEmpty()) Jupiter::StringS &ip_str = Jupiter::Socket::ntop4(entry->ip);
strftime(timeStr, sizeof(timeStr), "%b %d %Y, %H:%M:%S", localtime(std::addressof<const time_t>(std::chrono::system_clock::to_time_t(entry->timestamp))));
if ((entry->flags & 0x7FFF) == 0)
types = " NULL;"_jrs;
else
{ {
Jupiter::IRC::Client::Channel *chan = source->getChannel(channel); 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] <player ip/steam/name/banner>");
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 &parameters)
{
if (parameters.isNotEmpty())
{
Jupiter::IRC::Client::Channel *chan = source->getChannel(channel);
if (chan != nullptr)
{
Jupiter::ArrayList<RenX::Server> 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 <Player> [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 <Player> [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 &parameters)
{
if (parameters.isNotEmpty())
{
Jupiter::IRC::Client::Channel *chan = source->getChannel(channel);
if (chan != nullptr) if (chan != nullptr)
{ {
Jupiter::ArrayList<RenX::Server> servers = RenX::getCore()->getServers(chan->getType()); Jupiter::ArrayList<RenX::Server> servers = RenX::getCore()->getServers(chan->getType());
@ -2068,7 +2071,7 @@ void AddBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &c
Jupiter::StringS rdns; Jupiter::StringS rdns;
Jupiter::String banner = nick + "@IRC"_jrs; Jupiter::String banner = nick + "@IRC"_jrs;
Jupiter::ReferenceString reason = "No reason"_jrs; Jupiter::ReferenceString reason = "No reason"_jrs;
time_t duration = 0; std::chrono::seconds duration(0);
uint16_t flags = 0; uint16_t flags = 0;
Jupiter::ReferenceString word; Jupiter::ReferenceString word;
@ -2135,7 +2138,7 @@ void AddBanIRCCommand::trigger(IRC_Bot *source, const Jupiter::ReadableString &c
return; 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)) else if (word.equalsi("Game"_jrs))
flags |= RenX::BanDatabase::Entry::FLAG_TYPE_GAME; flags |= RenX::BanDatabase::Entry::FLAG_TYPE_GAME;
@ -2234,6 +2237,406 @@ const Jupiter::ReadableString &UnBanIRCCommand::getHelp(const Jupiter::ReadableS
IRC_COMMAND_INIT(UnBanIRCCommand) 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 &parameters)
{
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<bool(unsigned int)> 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<const time_t>(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] <player ip/steam/setter>");
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 &parameters)
{
if (parameters.isNotEmpty())
{
Jupiter::IRC::Client::Channel *chan = source->getChannel(channel);
if (chan != nullptr)
{
Jupiter::ArrayList<RenX::Server> 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 <Player> [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 <Player> [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 &parameters)
{
if (parameters.isNotEmpty())
{
Jupiter::IRC::Client::Channel *chan = source->getChannel(channel);
if (chan != nullptr)
{
Jupiter::ArrayList<RenX::Server> 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 <Player> [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 <Player> [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 &parameters)
{
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 <Player> [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 <Key> <Value> [...]"));
}
const Jupiter::ReadableString &AddExemptionIRCCommand::getHelp(const Jupiter::ReadableString &parameters)
{
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 <Key> <Value> [<Key> <Value> ...]");
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 &parameters)
{
if (parameters.isNotEmpty())
{
size_t index = static_cast<size_t>(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 <Exemption ID>"));
}
const Jupiter::ReadableString &UnExemptIRCCommand::getHelp(const Jupiter::ReadableString &)
{
static STRING_LITERAL_AS_NAMED_REFERENCE(defaultHelp, "Deactivates an exemption. Syntax: unexempt <Exemption ID>");
return defaultHelp;
}
IRC_COMMAND_INIT(UnExemptIRCCommand)
// AddBots IRC Command // AddBots IRC Command
void AddBotsIRCCommand::create() void AddBotsIRCCommand::create()

12
RenX.Commands/RenX_Commands.h

@ -19,6 +19,7 @@
#if !defined _RENX_COMMANDS_H_HEADER #if !defined _RENX_COMMANDS_H_HEADER
#define _RENX_COMMANDS_H_HEADER #define _RENX_COMMANDS_H_HEADER
#include <chrono>
#include "Console_Command.h" #include "Console_Command.h"
#include "IRC_Command.h" #include "IRC_Command.h"
#include "RenX_GameCommand.h" #include "RenX_GameCommand.h"
@ -36,7 +37,7 @@ public: // Jupiter::Plugin
int OnRehash() override; int OnRehash() override;
public: public:
time_t getTBanTime() const; std::chrono::seconds getTBanTime() const;
const Jupiter::ReadableString &getPlayerInfoFormat() const; const Jupiter::ReadableString &getPlayerInfoFormat() const;
const Jupiter::ReadableString &getAdminPlayerInfoFormat() const; const Jupiter::ReadableString &getAdminPlayerInfoFormat() const;
const Jupiter::ReadableString &getBuildingInfoFormat() const; const Jupiter::ReadableString &getBuildingInfoFormat() const;
@ -44,7 +45,7 @@ public:
private: private:
STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX.Commands"); STRING_LITERAL_AS_NAMED_REFERENCE(name, "RenX.Commands");
time_t _defaultTempBanTime; std::chrono::seconds _defaultTempBanTime;
Jupiter::StringS playerInfoFormat; Jupiter::StringS playerInfoFormat;
Jupiter::StringS adminPlayerInfoFormat; Jupiter::StringS adminPlayerInfoFormat;
Jupiter::StringS buildingInfoFormat; Jupiter::StringS buildingInfoFormat;
@ -69,7 +70,6 @@ GENERIC_IRC_COMMAND(SteamIRCCommand)
GENERIC_IRC_COMMAND(KillDeathRatioIRCCommand) GENERIC_IRC_COMMAND(KillDeathRatioIRCCommand)
GENERIC_IRC_COMMAND(ShowModsIRCCommand) GENERIC_IRC_COMMAND(ShowModsIRCCommand)
GENERIC_IRC_COMMAND(ModsIRCCommand) GENERIC_IRC_COMMAND(ModsIRCCommand)
GENERIC_IRC_COMMAND(BanSearchIRCCommand)
GENERIC_IRC_COMMAND(ShowRulesIRCCommand) GENERIC_IRC_COMMAND(ShowRulesIRCCommand)
GENERIC_IRC_COMMAND(RulesIRCCommand) GENERIC_IRC_COMMAND(RulesIRCCommand)
GENERIC_IRC_COMMAND(SetRulesIRCCommand) GENERIC_IRC_COMMAND(SetRulesIRCCommand)
@ -84,10 +84,16 @@ GENERIC_IRC_COMMAND(DisarmC4IRCCommand)
GENERIC_IRC_COMMAND(DisarmBeaconIRCCommand) GENERIC_IRC_COMMAND(DisarmBeaconIRCCommand)
GENERIC_IRC_COMMAND(MineBanIRCCommand) GENERIC_IRC_COMMAND(MineBanIRCCommand)
GENERIC_IRC_COMMAND(KickIRCCommand) GENERIC_IRC_COMMAND(KickIRCCommand)
GENERIC_IRC_COMMAND(BanSearchIRCCommand)
GENERIC_IRC_COMMAND(TempBanIRCCommand) GENERIC_IRC_COMMAND(TempBanIRCCommand)
GENERIC_IRC_COMMAND(KickBanIRCCommand) GENERIC_IRC_COMMAND(KickBanIRCCommand)
GENERIC_IRC_COMMAND(AddBanIRCCommand) GENERIC_IRC_COMMAND(AddBanIRCCommand)
GENERIC_IRC_COMMAND(UnBanIRCCommand) 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(AddBotsIRCCommand)
GENERIC_IRC_COMMAND(KillBotsIRCCommand) GENERIC_IRC_COMMAND(KillBotsIRCCommand)
GENERIC_IRC_COMMAND(PhaseBotsIRCCommand) GENERIC_IRC_COMMAND(PhaseBotsIRCCommand)

2
RenX.Core/RenX.Core.vcxproj

@ -74,6 +74,7 @@
<ClInclude Include="RenX_BuildingInfo.h" /> <ClInclude Include="RenX_BuildingInfo.h" />
<ClInclude Include="RenX_Core.h" /> <ClInclude Include="RenX_Core.h" />
<ClInclude Include="RenX.h" /> <ClInclude Include="RenX.h" />
<ClInclude Include="RenX_ExemptionDatabase.h" />
<ClInclude Include="RenX_Functions.h" /> <ClInclude Include="RenX_Functions.h" />
<ClInclude Include="RenX_GameCommand.h" /> <ClInclude Include="RenX_GameCommand.h" />
<ClInclude Include="RenX_Map.h" /> <ClInclude Include="RenX_Map.h" />
@ -85,6 +86,7 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="RenX_BanDatabase.cpp" /> <ClCompile Include="RenX_BanDatabase.cpp" />
<ClCompile Include="RenX_Core.cpp" /> <ClCompile Include="RenX_Core.cpp" />
<ClCompile Include="RenX_ExemptionDatabase.cpp" />
<ClCompile Include="RenX_Functions.cpp" /> <ClCompile Include="RenX_Functions.cpp" />
<ClCompile Include="RenX_GameCommand.cpp" /> <ClCompile Include="RenX_GameCommand.cpp" />
<ClCompile Include="RenX_Map.cpp" /> <ClCompile Include="RenX_Map.cpp" />

6
RenX.Core/RenX.Core.vcxproj.filters

@ -56,6 +56,9 @@
<ClInclude Include="RenX_Map.h"> <ClInclude Include="RenX_Map.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="RenX_ExemptionDatabase.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="RenX_Plugin.cpp"> <ClCompile Include="RenX_Plugin.cpp">
@ -82,5 +85,8 @@
<ClCompile Include="RenX_Map.cpp"> <ClCompile Include="RenX_Map.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="RenX_ExemptionDatabase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

25
RenX.Core/RenX_BanDatabase.cpp

@ -16,6 +16,7 @@
* Written by Jessica James <jessica.aj@outlook.com> * Written by Jessica James <jessica.aj@outlook.com>
*/ */
#include <ctime>
#include <cstdio> #include <cstdio>
#include "Jupiter/IRC_Client.h" #include "Jupiter/IRC_Client.h"
#include "Jupiter/INIFile.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 // Read data from buffer to entry
entry->flags = buffer.pop<uint16_t>(); entry->flags = buffer.pop<uint16_t>();
entry->timestamp = buffer.pop<time_t>(); if (RenX::BanDatabase::read_version >= 4U)
entry->length = buffer.pop<time_t>(); {
entry->timestamp = std::chrono::system_clock::time_point(std::chrono::seconds(buffer.pop<uint64_t>()));
entry->length = std::chrono::seconds(buffer.pop<uint64_t>());
}
else
{
entry->timestamp = std::chrono::system_clock::from_time_t(buffer.pop<time_t>());
entry->length = std::chrono::seconds(static_cast<long long>(buffer.pop<time_t>()));
}
entry->steamid = buffer.pop<uint64_t>(); entry->steamid = buffer.pop<uint64_t>();
entry->ip = buffer.pop<uint32_t>(); entry->ip = buffer.pop<uint32_t>();
entry->prefix_length = buffer.pop<uint8_t>(); entry->prefix_length = buffer.pop<uint8_t>();
@ -119,8 +128,8 @@ void RenX::BanDatabase::write(RenX::BanDatabase::Entry *entry, FILE *file)
// push data from entry to buffer // push data from entry to buffer
buffer.push(entry->flags); buffer.push(entry->flags);
buffer.push(entry->timestamp); buffer.push(static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::seconds>(entry->timestamp.time_since_epoch()).count()));
buffer.push(entry->length); buffer.push(static_cast<uint64_t>(entry->length.count()));
buffer.push(entry->steamid); buffer.push(entry->steamid);
buffer.push(entry->ip); buffer.push(entry->ip);
buffer.push(entry->prefix_length); 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)); 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 *entry = new Entry();
entry->set_active(); entry->set_active();
entry->flags |= flags; entry->flags |= flags;
entry->timestamp = time(0); entry->timestamp = std::chrono::system_clock::now();
entry->length = length; entry->length = length;
entry->steamid = player->steamid; entry->steamid = player->steamid;
entry->ip = player->ip32; entry->ip = player->ip32;
@ -172,12 +181,12 @@ void RenX::BanDatabase::add(RenX::Server *server, const RenX::PlayerInfo *player
RenX::BanDatabase::write(entry); 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 *entry = new Entry();
entry->set_active(); entry->set_active();
entry->flags |= flags; entry->flags |= flags;
entry->timestamp = time(0); entry->timestamp = std::chrono::system_clock::now();
entry->length = length; entry->length = length;
entry->steamid = steamid; entry->steamid = steamid;
entry->ip = ip; entry->ip = ip;

11
RenX.Core/RenX_BanDatabase.h

@ -19,12 +19,12 @@
#if !defined _RENX_BANDATABASE_H_HEADER #if !defined _RENX_BANDATABASE_H_HEADER
#define _RENX_BANDATABASE_H_HEADER #define _RENX_BANDATABASE_H_HEADER
#include <ctime>
#include <cstdint> #include <cstdint>
#include "Jupiter/Database.h" #include "Jupiter/Database.h"
#include "Jupiter/String.h" #include "Jupiter/String.h"
#include "Jupiter/CString.h" #include "Jupiter/CString.h"
#include "Jupiter/ArrayList.h" #include "Jupiter/ArrayList.h"
#include "RenX.h"
/** DLL Linkage Nagging */ /** DLL Linkage Nagging */
#if defined _MSC_VER #if defined _MSC_VER
@ -81,8 +81,8 @@ namespace RenX
{ {
fpos_t pos; /** Position of the entry in the database */ fpos_t pos; /** Position of the entry in the database */
uint16_t flags /** Flags affecting this ban entry (See below for flags) */ = 0x00; uint16_t flags /** Flags affecting this ban entry (See below for flags) */ = 0x00;
time_t timestamp /** Time the ban was created */; std::chrono::system_clock::time_point timestamp /** Time the ban was created */;
time_t length /** Duration of the ban; 0 if permanent */; std::chrono::seconds length /** Duration of the ban; 0 if permanent */;
uint64_t steamid /** SteamID of the banned player */; uint64_t steamid /** SteamID of the banned player */;
uint32_t ip /** IPv4 address of the banned player */; uint32_t ip /** IPv4 address of the banned player */;
uint8_t prefix_length /** Prefix length for the IPv4 address block */; uint8_t prefix_length /** Prefix length for the IPv4 address block */;
@ -102,7 +102,6 @@ namespace RenX
static const uint16_t FLAG_TYPE_LADDER = 0x0004U; static const uint16_t FLAG_TYPE_LADDER = 0x0004U;
static const uint16_t FLAG_TYPE_ALERT = 0x0002U; static const uint16_t FLAG_TYPE_ALERT = 0x0002U;
inline bool is_active() { return (flags & FLAG_ACTIVE) != 0; }; inline bool is_active() { return (flags & FLAG_ACTIVE) != 0; };
inline bool is_rdns_ban() { return (flags & FLAG_USE_RDNS) != 0; }; inline bool is_rdns_ban() { return (flags & FLAG_USE_RDNS) != 0; };
inline bool is_type_game() { return (flags & FLAG_TYPE_GAME) != 0; }; inline bool is_type_game() { return (flags & FLAG_TYPE_GAME) != 0; };
@ -143,7 +142,7 @@ namespace RenX
* @param player Data of the player to be banned * @param player Data of the player to be banned
* @param length Duration of the ban * @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. * @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 reason Reason the player is getting banned
* @param length Duration of the ban * @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. * @brief Upgrades the ban database to the current write_version.

197
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 <jessica.aj@outlook.com>
*/
#include <cstdio>
#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<uint8_t>();
entry->timestamp = std::chrono::system_clock::time_point(std::chrono::seconds(buffer.pop<uint64_t>()));
entry->length = std::chrono::seconds(buffer.pop<int64_t>());
entry->steamid = buffer.pop<uint64_t>();
entry->ip = buffer.pop<uint32_t>();
entry->prefix_length = buffer.pop<uint8_t>();
entry->setter = buffer.pop<Jupiter::String_Strict, char>();
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<int64_t>(std::chrono::duration_cast<std::chrono::seconds>(entry->timestamp.time_since_epoch()).count()));
buffer.push(static_cast<int64_t>(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::Entry> &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();
}

202
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 <jessica.aj@outlook.com>
*/
#if !defined _RENX_EXEMPTIONDATABASE_H_HEADER
#define _RENX_EXEMPTIONDATABASE_H_HEADER
#include <cstdint>
#include <chrono>
#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<RenX::ExemptionDatabase::Entry> &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<RenX::ExemptionDatabase::Entry> entries;
};
RENX_API extern RenX::ExemptionDatabase *exemptionDatabase;
RENX_API extern RenX::ExemptionDatabase &defaultExemptionDatabase;
}
#endif // _RENX_EXEMPTIONDATABASE_H_HEADER

1
RenX.Core/RenX_PlayerInfo.h

@ -54,6 +54,7 @@ namespace RenX
uint64_t steamid = 0; uint64_t steamid = 0;
uint32_t ip32 = 0; uint32_t ip32 = 0;
uint16_t ban_flags = 0; uint16_t ban_flags = 0;
uint8_t exemption_flags = 0;
TeamType team = TeamType::Other; TeamType team = TeamType::Other;
int id = 0; int id = 0;
bool isBot = false; bool isBot = false;

51
RenX.Core/RenX_Server.cpp

@ -29,6 +29,7 @@
#include "RenX_Functions.h" #include "RenX_Functions.h"
#include "RenX_Plugin.h" #include "RenX_Plugin.h"
#include "RenX_BanDatabase.h" #include "RenX_BanDatabase.h"
#include "RenX_ExemptionDatabase.h"
#include "RenX_Tags.h" #include "RenX_Tags.h"
using namespace Jupiter::literals; using namespace Jupiter::literals;
@ -369,6 +370,7 @@ void RenX::Server::kickPlayer(int id, const Jupiter::ReadableString &reason)
void RenX::Server::kickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason) void RenX::Server::kickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason)
{ {
if ((player->exemption_flags & RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK) == 0)
RenX::Server::kickPlayer(player->id, reason); RenX::Server::kickPlayer(player->id, reason);
} }
@ -382,6 +384,7 @@ void RenX::Server::forceKickPlayer(int id, const Jupiter::ReadableString &reason
void RenX::Server::forceKickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason) void RenX::Server::forceKickPlayer(const RenX::PlayerInfo *player, const Jupiter::ReadableString &reason)
{ {
if ((player->exemption_flags & RenX::ExemptionDatabase::Entry::FLAG_TYPE_KICK) == 0)
RenX::Server::forceKickPlayer(player->id, reason); RenX::Server::forceKickPlayer(player->id, reason);
} }
@ -394,6 +397,9 @@ void RenX::Server::banCheck()
void RenX::Server::banCheck(RenX::PlayerInfo *player) 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<RenX::BanDatabase::Entry> &entries = RenX::banDatabase->getEntries(); const Jupiter::ArrayList<RenX::BanDatabase::Entry> &entries = RenX::banDatabase->getEntries();
RenX::BanDatabase::Entry *entry = nullptr; RenX::BanDatabase::Entry *entry = nullptr;
uint32_t netmask; uint32_t netmask;
@ -406,13 +412,13 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player)
{ {
if (last_to_expire[index] == nullptr) if (last_to_expire[index] == nullptr)
last_to_expire[index] = entry; 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 // 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; 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; last_to_expire[index] = entry;
}; };
@ -421,7 +427,7 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player)
entry = entries.get(i); entry = entries.get(i);
if (entry->is_active()) 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); banDatabase->deactivate(i);
else else
{ {
@ -459,8 +465,8 @@ void RenX::Server::banCheck(RenX::PlayerInfo *player)
char timeStr[256]; char timeStr[256];
if (last_to_expire[0] != nullptr) // Game ban if (last_to_expire[0] != nullptr) // Game ban
{ {
strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(last_to_expire[0]->timestamp + last_to_expire[0]->length))); strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(std::chrono::system_clock::to_time_t(last_to_expire[0]->timestamp + last_to_expire[0]->length))));
if (last_to_expire[0]->length == 0) 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())); 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 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())); 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 if (last_to_expire[1] != nullptr) // Chat ban
{ {
strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(last_to_expire[1]->timestamp + last_to_expire[1]->length))); strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(std::chrono::system_clock::to_time_t(last_to_expire[1]->timestamp + last_to_expire[1]->length))));
this->mute(player); 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())); 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 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())); 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 else if (last_to_expire[2] != nullptr) // Bot ban
{ {
strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(last_to_expire[2]->timestamp + last_to_expire[2]->length))); strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(std::chrono::system_clock::to_time_t(last_to_expire[2]->timestamp + last_to_expire[2]->length))));
if (last_to_expire[2]->length == 0) 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())); 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 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())); 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 if (last_to_expire[3] != nullptr) // Vote ban
{ {
strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(last_to_expire[3]->timestamp + last_to_expire[3]->length))); strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(std::chrono::system_clock::to_time_t(last_to_expire[3]->timestamp + last_to_expire[3]->length))));
if (last_to_expire[3]->length == 0) 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())); 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 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())); 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 if (last_to_expire[4] != nullptr) // Mine ban
{ {
this->mineBan(player); this->mineBan(player);
strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(last_to_expire[4]->timestamp + last_to_expire[4]->length))); strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(std::chrono::system_clock::to_time_t(last_to_expire[4]->timestamp + last_to_expire[4]->length))));
if (last_to_expire[4]->length == 0) 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())); 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 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())); 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 if (last_to_expire[5] != nullptr) // Ladder ban
{ {
strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(last_to_expire[5]->timestamp + last_to_expire[5]->length))); strftime(timeStr, sizeof(timeStr), "%b %d %Y at %H:%M:%S", localtime(std::addressof<const time_t>(std::chrono::system_clock::to_time_t(last_to_expire[5]->timestamp + last_to_expire[5]->length))));
if (last_to_expire[5]->length == 0) 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())); 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 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())); 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,12 +562,14 @@ 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 ((player->exemption_flags & RenX::ExemptionDatabase::Entry::FLAG_TYPE_BAN) == 0)
{ {
if (RenX::Server::localBan) if (RenX::Server::localBan)
RenX::banDatabase->add(this, player, banner, reason, length); RenX::banDatabase->add(this, player, banner, reason, length);
if (length == 0) if (length == std::chrono::seconds::zero())
{ {
if (RenX::Server::rconBan) if (RenX::Server::rconBan)
RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", player->id, reason.size(), reason.ptr())); RenX::Server::sock.send(Jupiter::StringS::Format("ckickban pid%d %.*s\n", player->id, reason.size(), reason.ptr()));
@ -571,9 +579,10 @@ void RenX::Server::banPlayer(const RenX::PlayerInfo *player, const Jupiter::Read
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 permanently banned from the server for: %.*s", reason.size(), reason.ptr()));
} }
else if (banner.isNotEmpty()) 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<long long>(length / 86400), static_cast<int>(length % 3600), static_cast<int>((length % 3600) / 60), static_cast<int>(length % 60), 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<long long>(length.count() / 86400), static_cast<int>(length.count() % 3600), static_cast<int>((length.count() % 3600) / 60), static_cast<int>(length.count() % 60), reason.size(), reason.ptr()));
else 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<long long>(length/86400), static_cast<int>(length%3600), static_cast<int>((length%3600)/60), static_cast<int>(length%60), 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<long long>(length.count() / 86400), static_cast<int>(length.count() % 3600), static_cast<int>((length.count() % 3600) / 60), static_cast<int>(length.count() % 60), reason.size(), reason.ptr()));
}
} }
bool RenX::Server::removePlayer(int id) bool RenX::Server::removePlayer(int id)
@ -1224,6 +1233,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
this->players.add(r); this->players.add(r);
r->uuid = calc_uuid(r); r->uuid = calc_uuid(r);
RenX::exemptionDatabase->exemption_check(r);
this->banCheck(r); this->banCheck(r);
for (size_t i = 0; i < xPlugins.size(); i++) for (size_t i = 0; i < xPlugins.size(); i++)
@ -1253,6 +1263,7 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
if (recalcUUID) if (recalcUUID)
{ {
this->setUUIDIfDifferent(r, calc_uuid(r)); this->setUUIDIfDifferent(r, calc_uuid(r));
RenX::exemptionDatabase->exemption_check(r);
this->banCheck(r); this->banCheck(r);
} }
} }

2
RenX.Core/RenX_Server.h

@ -368,7 +368,7 @@ namespace RenX
* @param player Data of the player to ban. * @param player Data of the player to ban.
* @param length Duration of the ban (0 for permanent). * @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. * @brief Removes a player's data based on their ID number.

2
RenX.ExcessiveHeadshots/ExcessiveHeadshots.cpp

@ -48,7 +48,7 @@ void RenX_ExcessiveHeadshotsPlugin::RenX_OnKill(RenX::Server *server, const RenX
{ {
unsigned int flags = 0; unsigned int flags = 0;
std::chrono::milliseconds game_time = server->getGameTime(player); std::chrono::milliseconds game_time = server->getGameTime(player);
double kps = game_time == std::chrono::milliseconds(0) ? static_cast<double>(player->kills) : static_cast<double>(player->kills) / static_cast<double>(game_time.count()); double kps = game_time == std::chrono::milliseconds::zero() ? static_cast<double>(player->kills) : static_cast<double>(player->kills) / static_cast<double>(game_time.count());
if (player->kills >= RenX_ExcessiveHeadshotsPlugin::minKills) flags++; if (player->kills >= RenX_ExcessiveHeadshotsPlugin::minKills) flags++;
if (RenX::getHeadshotKillRatio(player) >= RenX_ExcessiveHeadshotsPlugin::ratio) flags++; if (RenX::getHeadshotKillRatio(player) >= RenX_ExcessiveHeadshotsPlugin::ratio) flags++;
if (RenX::getKillDeathRatio(player) >= RenX_ExcessiveHeadshotsPlugin::minKD) flags++; if (RenX::getKillDeathRatio(player) >= RenX_ExcessiveHeadshotsPlugin::minKD) flags++;

4
RenX.Logging/RenX_Logging.cpp

@ -1571,9 +1571,9 @@ void RenX_LoggingPlugin::RenX_OnExecute(RenX::Server *server, const Jupiter::Rea
Jupiter::String msg; Jupiter::String msg;
if (user.equals(RenX::DevBotName)) if (user.equals(RenX::DevBotName))
msg = this->executeFmt;
else
msg = this->devBotExecuteFmt; msg = this->devBotExecuteFmt;
else
msg = this->executeFmt;
if (msg.isNotEmpty()) if (msg.isNotEmpty())
{ {

4
RenX.ModSystem/RenX_ModSystem.cpp

@ -175,7 +175,7 @@ int RenX_ModSystemPlugin::auth(RenX::Server *server, const RenX::PlayerInfo *pla
if (player->access != 0) 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())); 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)); server->sendData(Jupiter::StringS::Format("d%d\n", player->id));
} }
Jupiter::String playerName = RenX::getFormattedPlayerName(player); 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) 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)); server->sendData(Jupiter::StringS::Format("d%d\n", player->id));
} }

4
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)); 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; break;
default: 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)); 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; 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)); 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; break;
default: 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)); 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; break;
} }

Loading…
Cancel
Save