diff --git a/qw/include/server.h b/qw/include/server.h index 8fcd56772..91a70781f 100644 --- a/qw/include/server.h +++ b/qw/include/server.h @@ -135,6 +135,12 @@ typedef struct #define MAX_BACK_BUFFERS 8 #define MAX_STUFFTEXT 512 +typedef enum { + ft_ban, + ft_mute, // mute penalty save over disconnect + ft_cuff, // cuff penatly save over disconnect +} filtertype_t; + typedef struct client_s { sv_client_state_t state; @@ -215,7 +221,8 @@ typedef struct client_s netchan_t netchan; int msecs, msec_cheating; double last_check; - int stdver; + double cuff_time; + int stdver; } client_t; // a client can leave the server in one of four ways: @@ -422,6 +429,10 @@ void Con_DPrintf (const char *fmt, ...) __attribute__((format(printf,1,2))); // // sv_main.c // + +void SV_SavePenaltyFilter (client_t *cl, filtertype_t type, double pentime); +double SV_RestorePenaltyFilter (client_t *cl, filtertype_t type); + void SV_Shutdown (void); void SV_Frame (float time); void SV_FinalMessage (const char *message); diff --git a/qw/source/sv_ccmds.c b/qw/source/sv_ccmds.c index 3c3fa283b..bd29998b3 100644 --- a/qw/source/sv_ccmds.c +++ b/qw/source/sv_ccmds.c @@ -514,6 +514,126 @@ SV_Status_f (void) SV_Printf ("\n"); } +#define MAXPENALTY 10.0 + +void +SV_Cuff_f (void) +{ + int i, uid; + double mins = 0.5; + qboolean all = false, done = false; + client_t *cl; + char text[1024]; + + if (Cmd_Argc() != 2 && Cmd_Argc() != 3) { + SV_Printf ("usage: cuff [minutes]\n" + " (default = 0.5, 0 = cancel cuff).\n"); + return; + } + + if (strequal (Cmd_Argv(1), "ALL")) { + all = true; + } else { + if (SV_Match_User (Cmd_Argv(1), &uid)) { + if (!uid) + return; + } else { + uid = atoi(Cmd_Argv(1)); // assume userid + } + } + if (Cmd_Argc() == 3) { + mins = atof(Cmd_Argv(2)); + if (mins < 0.0 || mins > MAXPENALTY) + mins = MAXPENALTY; + } + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + if (all || (cl->userid == uid)) { + cl->cuff_time = realtime + mins*60.0; + done = true; + if (mins) { + sprintf(text, + "You are cuffed for %.1f minutes\n\n" + "reconnecting won't help...\n", mins); + ClientReliableWrite_Begin(cl,svc_centerprint, 2+strlen(text)); + ClientReliableWrite_String (cl, text); + } + if (!all) + break; + } + } + if (done) { + if (mins) + SV_BroadcastPrintf (PRINT_HIGH, "%s cuffed for %.1f minutes.\n", + all? "All Users" : cl->name, mins); + else + SV_BroadcastPrintf (PRINT_HIGH, "%s un-cuffed.\n", + all? "All Users" : cl->name); + } else { + SV_Printf (all? "No users\n" : "Couldn't find user %s\n", Cmd_Argv(1)); + } +} + + +void +SV_Mute_f (void) +{ + int i, uid; + double mins = 0.5; + qboolean all = false, done = false; + client_t *cl; + char text[1024]; + + if (Cmd_Argc() != 2 && Cmd_Argc() != 3) { + SV_Printf ("usage: mute [minutes]\n" + " (default = 0.5, 0 = cancel mute).\n"); + return; + } + if (strequal(Cmd_Argv(1),"ALL")) { + all = true; + } else { + if (SV_Match_User (Cmd_Argv(1), &uid)) { + if (!uid) + return; + } else { + uid = atoi(Cmd_Argv(1)); + } + } + if (Cmd_Argc() == 3) { + mins = atof(Cmd_Argv(2)); + if (mins < 0.0 || mins > MAXPENALTY) + mins = MAXPENALTY; + } + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + if (all || (cl->userid == uid)) { + cl->lockedtill = realtime + mins*60.0; + done = true; + if (mins) { + sprintf(text, "You are muted for %.1f minutes\n\n" + "reconnecting won't help...\n", mins); + ClientReliableWrite_Begin(cl,svc_centerprint, 2+strlen(text)); + ClientReliableWrite_String (cl, text); + } + if (!all) + break; + } + } + if (done) { + if (mins) + SV_BroadcastPrintf (PRINT_HIGH, "%s muted for %.1f minutes.\n", + all? "All Users" : cl->name, mins); + else + SV_BroadcastPrintf (PRINT_HIGH, "%s allowed to speak.\n", + all? "All Users" : cl->name); + } else { + SV_Printf (all? "No users\n" : "Couldn't find user %s\n", + Cmd_Argv(1)); + } +} + void SV_Tell (const char *prefix) { @@ -559,6 +679,47 @@ SV_Tell (const char *prefix) SV_Printf ("Couldn't find user %s\n", Cmd_Argv(1)); } +void +SV_Ban_f (void) +{ + int i, uid; + double mins = 30.0; + client_t *cl; + + if (Cmd_Argc() != 2 && Cmd_Argc() != 3) { + SV_Printf ("usage: ban [minutes]\n" + " (default = 30, 0 = permanent).\n"); + return; + } + if (SV_Match_User(Cmd_Argv(1), &uid)) { + if (!uid) + return; + } else { + uid = atoi(Cmd_Argv(1)); + } + if (Cmd_Argc() == 3) { + mins = atof(Cmd_Argv(2)); + if (mins<0.0 || mins > 1000000.0) // bout 2 yrs + mins = 0.0; + } + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + if (cl->userid == uid) { + SV_BroadcastPrintf (PRINT_HIGH, "Admin Banned user %s %s\n", + cl->name, mins ? va("for %.1f minutes",mins) + : "permanently"); + SV_DropClient (cl); + Cmd_ExecuteString ( + va ("addip %s %f", + NET_BaseAdrToString(cl->netchan.remote_address), mins), + src_command); + return; + } + } + Con_Printf ("Couldn't find user %s\n", Cmd_Argv(1)); +} + void SV_ConSay (const char *prefix) { @@ -1004,9 +1165,11 @@ SV_InitOperatorCommands (void) Cmd_AddCommand ("tell", SV_Tell_f, "Say something to a specific user on " "the server. Will show up as the name 'Console' (or " "'Admin') in game"); - //XXX Cmd_AddCommand ("ban", SV_Ban_f); - //XXX Cmd_AddCommand ("cuff", SV_Cuff_f); - //XXX Cmd_AddCommand ("mute", SV_Mute_f); + Cmd_AddCommand ("ban", SV_Ban_f, "ban a player for a specified time"); + Cmd_AddCommand ("cuff", SV_Cuff_f, "\"hand-cuff\" a player for a " + "specified time"); + Cmd_AddCommand ("mute", SV_Mute_f, "silience a player for a specified " + "time"); cl_warncmd = Cvar_Get ("cl_warncmd", "1", CVAR_NONE, NULL, "Toggles the " "display of error messages for unknown commands"); diff --git a/qw/source/sv_main.c b/qw/source/sv_main.c index 06eade64f..2f7da1105 100644 --- a/qw/source/sv_main.c +++ b/qw/source/sv_main.c @@ -249,6 +249,9 @@ SV_FinalMessage (const char *message) void SV_DropClient (client_t *drop) { + SV_SavePenaltyFilter (drop, ft_mute, drop->lockedtill); + SV_SavePenaltyFilter (drop, ft_cuff, drop->cuff_time); + // add the disconnect MSG_WriteByte (&drop->netchan.message, svc_disconnect); @@ -830,7 +833,7 @@ SVC_DirectConnect (void) for (i = 0; i < 10; i++) newcl->whensaid[i] = 0.0; newcl->whensaidhead = 0; - newcl->lockedtill = 0; + newcl->lockedtill = SV_RestorePenaltyFilter(newcl, ft_mute); // call the progs to get default spawn parms for the new client PR_ExecuteProgram (&sv_pr_state, sv_funcs.SetNewParms); @@ -848,6 +851,7 @@ SVC_DirectConnect (void) newcl->msec_cheating = 0; newcl->last_check = -1; + newcl->cuff_time = SV_RestorePenaltyFilter(newcl, ft_cuff); } int @@ -1045,6 +1049,7 @@ SV_ConnectionlessPacket (void) typedef struct { unsigned int mask; unsigned int compare; + double time; } ipfilter_t; #define MAX_IPFILTERS 1024 @@ -1052,6 +1057,7 @@ typedef struct { cvar_t *filterban; int numipfilters; ipfilter_t ipfilters[MAX_IPFILTERS]; +filtertype_t filttypes[MAX_IPFILTERS]; qboolean StringToFilter (const char *s, ipfilter_t * f) @@ -1091,6 +1097,26 @@ StringToFilter (const char *s, ipfilter_t * f) return true; } +void +CleanIPlist (void) +{ + int i, j; + + for (i=0 ; iint_val; + for (i = 0; i < numipfilters; i++) { + if (filttypes[i] != ft_ban) + continue; + if ((in & ipfilters[i].mask) == ipfilters[i].compare) { + if (!ipfilters[i].time) { + // normal ban + return filterban->int_val; + } else if (ipfilters[i].time > realtime) { + *until = ipfilters[i].time; + return true; // banned no matter what + } else { + // time expired, set free + ipfilters[i].compare = 0xffffffff; + } + } + } + return !filterban->int_val; +} - return !filterban->int_val; // FIXME eh? +void +SV_SavePenaltyFilter (client_t *cl, filtertype_t type, double pentime) +{ + int i; + unsigned int ip; + + ip = *(unsigned int *) cl->netchan.remote_address.ip; + + // delete any existing penalty filter of same type + for (i=0 ; iuserid); + ipfilters[numipfilters].mask = 0; + ipfilters[numipfilters].compare = ip; + ipfilters[numipfilters].time = pentime; + filttypes[numipfilters] = type; + numipfilters++; + return; +} + +double +SV_RestorePenaltyFilter (client_t *cl, filtertype_t type) +{ + int i; + unsigned int ip; + + ip = * (unsigned int *) cl->netchan.remote_address.ip; + + CleanIPlist (); + // search for existing penalty filter of same type + for (i=0 ; iuserid); + return ipfilters[i].time; + } + } + return 0.0; } void @@ -1264,11 +1354,12 @@ SV_ReadPackets (void) client_t *cl; int qport, i; qboolean good; + double until; good = false; while (NET_GetPacket ()) { - if (SV_FilterPacket ()) { - SV_SendBan (); // tell them we aren't listening... + if (SV_FilterPacket (&until)) { + SV_SendBan (until); // tell them we aren't listening... continue; } // check for connectionless packet (0xffffffff) first diff --git a/qw/source/sv_user.c b/qw/source/sv_user.c index ec990b017..8d972bad1 100644 --- a/qw/source/sv_user.c +++ b/qw/source/sv_user.c @@ -1502,6 +1502,8 @@ SV_RunCmd (usercmd_t *ucmd, qboolean inside) SVfloat (sv_player, button2) = (ucmd->buttons & 2) >> 1; if (ucmd->impulse) SVfloat (sv_player, impulse) = ucmd->impulse; + if (host_client->cuff_time > realtime) + SVfloat (sv_player, button0) = SVfloat (sv_player, impulse) = 0; // // angles