Add optional reason to kick/ban commands

And enhance `SV_IsBanned` to return ban reason and update `SV_DirectConnect` to display it
This commit is contained in:
ysdragon 2025-03-12 07:30:40 +02:00
parent 1e2a331518
commit f164e889cc
No known key found for this signature in database
GPG key ID: AE6E8E17DA81BA0E
2 changed files with 205 additions and 44 deletions

View file

@ -342,6 +342,7 @@ Kick a user off of the server FIXME: move to game
static void SV_Kick_f( void ) {
client_t *cl;
int i;
char *reason = NULL;
// make sure server is running
if ( !com_sv_running->integer ) {
@ -349,11 +350,16 @@ static void SV_Kick_f( void ) {
return;
}
if ( Cmd_Argc() != 2 ) {
Com_Printf ("Usage: kick <player name>\nkick all = kick everyone\nkick allbots = kick all bots\n");
if ( Cmd_Argc() < 2 ) {
Com_Printf ("Usage: kick <player name> [reason]\nkick all = kick everyone\nkick allbots = kick all bots\n");
return;
}
// Check if there's a reason provided
if ( Cmd_Argc() >= 3 ) {
reason = Cmd_ArgsFrom(2);
}
cl = SV_GetPlayerByHandle();
if ( !cl ) {
if ( !Q_stricmp(Cmd_Argv(1), "all") ) {
@ -387,7 +393,25 @@ static void SV_Kick_f( void ) {
return;
}
SV_DropClient( cl, "was kicked" );
// Check if there's a reason provided
if ( reason ) {
// Send the kick message to the client
SV_NET_OutOfBandPrint(&svs.netprofile, cl->netchan.remoteAddress, "droperror\nKicked from server for:\n%s", reason);
// Print the kick to all clients
SV_SendServerCommand(NULL, "print \"" HUD_MESSAGE_WHITE "%s was kicked for %s\n\"", cl->name, reason);
SV_DropClient( cl, va("was kicked for %s", reason) );
} else {
// Send the kick message to the client
SV_NET_OutOfBandPrint(&svs.netprofile, cl->netchan.remoteAddress, "droperror\nKicked from server");
// Print the kick to all clients
SV_SendServerCommand( NULL, "print \"" HUD_MESSAGE_WHITE "%s was kicked\n\"", cl->name );
SV_DropClient( cl, "was kicked" );
}
cl->lastPacketTime = svs.time; // in case there is a funny zombie
}
/*
@ -425,11 +449,12 @@ static void SV_KickAll_f( void ) {
==================
SV_KickNum_f
Kick a user off of the server
Kick a user off of the server, optionally with a reason
==================
*/
static void SV_KickNum_f( void ) {
client_t *cl;
char *reason = NULL;
// make sure server is running
if ( !com_sv_running->integer ) {
@ -437,8 +462,8 @@ static void SV_KickNum_f( void ) {
return;
}
if ( Cmd_Argc() != 2 ) {
Com_Printf ("Usage: %s <client number>\n", Cmd_Argv(0));
if ( Cmd_Argc() < 2 ) {
Com_Printf ("Usage: %s <client number> [reason]\n", Cmd_Argv(0));
return;
}
@ -451,7 +476,30 @@ static void SV_KickNum_f( void ) {
return;
}
SV_DropClient( cl, "was kicked" );
// Check if there's a reason provided
if ( Cmd_Argc() >= 3 ) {
reason = Cmd_ArgsFrom(2);
}
// Check if there's a reason provided
if ( reason ) {
// Send the kick message to the client
SV_NET_OutOfBandPrint(&svs.netprofile, cl->netchan.remoteAddress, "droperror\nKicked from server for:\n%s", reason);
// Print the kick to all clients
SV_SendServerCommand(NULL, "print \"" HUD_MESSAGE_WHITE "%s was kicked for %s\n\"", cl->name, reason);
SV_DropClient( cl, va("was kicked for %s", reason) );
} else {
// Send the kick message to the client
SV_NET_OutOfBandPrint(&svs.netprofile, cl->netchan.remoteAddress, "droperror\nKicked from server");
// Print the kick to all clients
SV_SendServerCommand( NULL, "print \"" HUD_MESSAGE_WHITE "%s was kicked\n\"", cl->name );
SV_DropClient( cl, "was kicked" );
}
cl->lastPacketTime = svs.time; // in case there is a funny zombie
}
@ -580,7 +628,7 @@ static void SV_RehashBans_f(void)
{
int index, filelen;
fileHandle_t readfrom;
char *textbuf, *curpos, *maskpos, *newlinepos, *endpos;
char *textbuf, *curpos, *maskpos, *newlinepos, *endpos, *reasonpos;
char filepath[MAX_QPATH];
// make sure server is running
@ -623,12 +671,29 @@ static void SV_RehashBans_f(void)
maskpos++;
// find the end of the subnet specifier
for(newlinepos = maskpos; newlinepos < endpos && *newlinepos != '\n'; newlinepos++);
for(newlinepos = maskpos; newlinepos < endpos && *newlinepos != '\n' && *newlinepos != ':'; newlinepos++);
if(newlinepos >= endpos)
break;
*newlinepos = '\0';
if(*newlinepos == ':') {
// We have a reason
*newlinepos = '\0';
reasonpos = newlinepos + 1;
// Find the end of the reason string
for(newlinepos = reasonpos; newlinepos < endpos && *newlinepos != '\n'; newlinepos++);
if(newlinepos >= endpos)
break;
*newlinepos = '\0';
Q_strncpyz(serverBans[index].reason, reasonpos, sizeof(serverBans[index].reason));
} else {
// No reason
*newlinepos = '\0';
serverBans[index].reason[0] = '\0';
}
if(NET_StringToAdr(curpos + 2, &serverBans[index].ip, NA_UNSPEC))
{
@ -676,15 +741,21 @@ static void SV_WriteBans(void)
if((writeto = FS_SV_FOpenFileWrite(filepath)))
{
char writebuf[128];
char writebuf[128 + MAX_REASON_LENGTH];
serverBan_t *curban;
for(index = 0; index < serverBansCount; index++)
{
curban = &serverBans[index];
Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d\n",
curban->isexception, NET_AdrToString(curban->ip), curban->subnet);
if(curban->reason[0]) {
Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d:%s\n",
curban->isexception, NET_AdrToString(curban->ip), curban->subnet, curban->reason);
} else {
Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d\n",
curban->isexception, NET_AdrToString(curban->ip), curban->subnet);
}
FS_Write(writebuf, strlen(writebuf), writeto);
}
@ -771,10 +842,13 @@ Ban a user from being able to play on this server based on his ip address.
static void SV_AddBanToList(qboolean isexception)
{
char *banstring;
char *reason = NULL;
char addy2[NET_ADDRSTRMAXLEN];
netadr_t ip;
int index, argc, mask;
serverBan_t *curban;
client_t *cl = NULL;
qboolean isdigit_only;
// make sure server is running
if ( !com_sv_running->integer ) {
@ -784,9 +858,9 @@ static void SV_AddBanToList(qboolean isexception)
argc = Cmd_Argc();
if(argc < 2 || argc > 3)
if ( argc < 2 )
{
Com_Printf ("Usage: %s (ip[/subnet] | clientnum [subnet])\n", Cmd_Argv(0));
Com_Printf ("Usage: %s (ip[/subnet] | clientnum [subnet] [reason])\n", Cmd_Argv(0));
return;
}
@ -798,9 +872,13 @@ static void SV_AddBanToList(qboolean isexception)
banstring = Cmd_Argv(1);
// Get optional reason - can be at arg 2 for IP bans or arg 3 for client num bans
if(strchr(banstring, '.') || strchr(banstring, ':'))
{
// This is an ip address, not a client num.
// IP address ban
if(argc >= 3) {
reason = Cmd_ArgsFrom(2);
}
if(SV_ParseCIDRNotation(&ip, &mask, banstring))
{
@ -810,13 +888,10 @@ static void SV_AddBanToList(qboolean isexception)
}
else
{
client_t *cl;
// client num.
// client num ban
cl = SV_GetPlayerByNum();
if(!cl)
if ( !cl )
{
Com_Printf("Error: Playernum %s does not exist.\n", Cmd_Argv(1));
return;
@ -824,23 +899,45 @@ static void SV_AddBanToList(qboolean isexception)
ip = cl->netchan.remoteAddress;
if(argc == 3)
if(argc >= 3)
{
mask = atoi(Cmd_Argv(2));
if(ip.type == NA_IP)
{
if(mask < 1 || mask > 32)
mask = 32;
// Check if second argument is all digits (CIDR notation)
isdigit_only = qtrue;
for(index = 0; Cmd_Argv(2)[index]; index++) {
if(Cmd_Argv(2)[index] < '0' || Cmd_Argv(2)[index] > '9') {
isdigit_only = qfalse;
break;
}
}
else
{
if(mask < 1 || mask > 128)
mask = 128;
if(isdigit_only) {
// Second arg is CIDR mask
mask = atoi(Cmd_Argv(2));
if(ip.type == NA_IP)
{
if(mask < 1 || mask > 32)
mask = 32;
}
else
{
if(mask < 1 || mask > 128)
mask = 128;
}
// Get optional reason after subnet
if(argc >= 4) {
reason = Cmd_ArgsFrom(3);
}
} else {
// Second arg is start of reason
reason = Cmd_ArgsFrom(2);
mask = (ip.type == NA_IP6) ? 128 : 32;
}
}
else
else {
mask = (ip.type == NA_IP6) ? 128 : 32;
}
}
if(ip.type != NA_IP && ip.type != NA_IP6)
@ -896,12 +993,47 @@ static void SV_AddBanToList(qboolean isexception)
serverBans[serverBansCount].subnet = mask;
serverBans[serverBansCount].isexception = isexception;
if(reason) {
Q_strncpyz(serverBans[serverBansCount].reason, reason, sizeof(serverBans[serverBansCount].reason));
} else {
serverBans[serverBansCount].reason[0] = '\0';
}
serverBansCount++;
SV_WriteBans();
Com_Printf("Added %s: %s/%d\n", isexception ? "ban exception" : "ban",
NET_AdrToString(ip), mask);
// Find and kick any connected clients matching the banned IP
for(index = 0; index < sv_maxclients->integer; index++) {
client_t *kickcl = &svs.clients[index];
if(!kickcl->state)
continue;
if ( NET_CompareBaseAdrMask(kickcl->netchan.remoteAddress, ip, mask) ) {
if ( reason ) {
SV_NET_OutOfBandPrint(&svs.netprofile, kickcl->netchan.remoteAddress,
"droperror\nYou have been banned from this server for:\n%s", reason);
SV_SendServerCommand(NULL, "print \"" HUD_MESSAGE_WHITE "%s was banned for %s\n\"", kickcl->name, reason);
SV_DropClient(kickcl, va("was banned for %s", reason));
} else {
SV_NET_OutOfBandPrint(&svs.netprofile, kickcl->netchan.remoteAddress,
"droperror\nYou have been banned from this server");
SV_SendServerCommand(NULL, "print \"" HUD_MESSAGE_WHITE "%s was banned\n\"", kickcl->name);
SV_DropClient(kickcl, "was banned");
}
kickcl->lastPacketTime = svs.time;
}
}
Com_Printf("Added %s: %s/%d%s%s\n", isexception ? "ban exception" : "ban",
NET_AdrToString(ip), mask,
reason ? " with reason: " : "",
reason ? reason : "");
}
/*
@ -1023,8 +1155,14 @@ static void SV_ListBans_f(void)
{
count++;
Com_Printf("Ban #%d: %s/%d\n", count,
Com_Printf("Ban #%d: %s/%d", count,
NET_AdrToString(ban->ip), ban->subnet);
if(ban->reason[0]) {
Com_Printf(" - Reason: %s", ban->reason);
}
Com_Printf("\n");
}
}
// List all exceptions
@ -1035,8 +1173,14 @@ static void SV_ListBans_f(void)
{
count++;
Com_Printf("Except #%d: %s/%d\n", count,
Com_Printf("Except #%d: %s/%d", count,
NET_AdrToString(ban->ip), ban->subnet);
if(ban->reason[0]) {
Com_Printf(" - Reason: %s", ban->reason);
}
Com_Printf("\n");
}
}
}
@ -1816,6 +1960,8 @@ void SV_AddOperatorCommands(void) {
Cmd_AddCommand("kickall", SV_KickAll_f);
Cmd_AddCommand("kicknum", SV_KickNum_f);
Cmd_AddCommand("clientkick", SV_KickNum_f); // Legacy command
Cmd_AddCommand("clientkickr", SV_KickNum_f); // Legacy command with reason
Cmd_AddCommand("kickr", SV_KickNum_f); // Legacy command with reason
Cmd_AddCommand("status", SV_Status_f);
Cmd_AddCommand("serverinfo", SV_Serverinfo_f);
Cmd_AddCommand("systeminfo", SV_Systeminfo_f);

View file

@ -196,18 +196,23 @@ void SV_AuthorizeIpPacket( netadr_t from ) {
SV_IsBanned
Check whether a certain address is banned
Returns the reason string if available
==================
*/
static qboolean SV_IsBanned(netadr_t *from, qboolean isexception)
static qboolean SV_IsBanned(netadr_t *from, qboolean isexception, char *reason, int reason_size)
{
int index;
serverBan_t *curban;
if(reason && reason_size > 0) {
reason[0] = '\0';
}
if(!isexception)
{
// If this is a query for a ban, first check whether the client is excepted
if(SV_IsBanned(from, qtrue))
if(SV_IsBanned(from, qtrue, NULL, 0))
return qfalse;
}
@ -217,8 +222,13 @@ static qboolean SV_IsBanned(netadr_t *from, qboolean isexception)
if(curban->isexception == isexception)
{
if(NET_CompareBaseAdrMask(curban->ip, *from, curban->subnet))
if(NET_CompareBaseAdrMask(curban->ip, *from, curban->subnet)) {
// Copy the ban reason if available and a buffer was provided
if(reason && reason_size > 0 && curban->reason[0]) {
Q_strncpyz(reason, curban->reason, reason_size);
}
return qtrue;
}
}
}
@ -333,13 +343,18 @@ void SV_DirectConnect( netadr_t from ) {
qboolean compat = qfalse;
#endif
challenge_t* ch;
char banReason[MAX_REASON_LENGTH];
Com_DPrintf ("SVC_DirectConnect ()\n");
// Check whether this client is banned.
if(SV_IsBanned(&from, qfalse))
if(SV_IsBanned(&from, qfalse, banReason, sizeof(banReason)))
{
SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nYou are banned from this server.\n");
if(banReason[0]) {
SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nYou are banned from this server.\nReason: %s\n", banReason);
} else {
SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nYou are banned from this server.\n");
}
return;
}