From: LegendaryGuard Date: Thu, 25 May 2023 07:53:21 +0000 (+0000) Subject: New banning systems (ignore, muteban, voteban and playban) and new features for moder... X-Git-Tag: xonotic-v0.8.6~71^2 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=f25c6de690327ed606161da945ed436cca12886e;p=xonotic%2Fxonotic-data.pk3dir.git New banning systems (ignore, muteban, voteban and playban) and new features for moderation purposes --- diff --git a/commands.cfg b/commands.cfg index 3a1ab30b7..d5b03ee3e 100644 --- a/commands.cfg +++ b/commands.cfg @@ -276,11 +276,29 @@ alias banlist "qc_cmd_sv banlist ${* ?}" // List a alias kickban "qc_cmd_sv kickban ${* ?}" // Disconnect a client and ban it at the same time alias mute "qc_cmd_sv mute ${* ?}" // Disallow a client from talking by muting them alias unban "qc_cmd_sv unban ${* ?}" // Remove an existing ban -alias unmute "qc_cmd_sv unmute ${* ?}" // Unmute a client +alias unmute "qc_cmd_sv unmute ${* ?}" // Unmute a client (Remove an existing muting ban) // other aliases for ban commands alias bans "banlist" + +// Client +alias ignore "qc_cmd_cmd ignore ${* ?}" // Keep client out of your personal chat log for a match +alias unignore "qc_cmd_cmd unignore ${* ?}" // Remove an existing ignored client +alias clear_ignores "qc_cmd_cmd clear_ignores" // Remove all existing ignored clients + +// Server +alias playban "qc_cmd_sv playban ${* ?}" // Ban disallowing a client from playing (forced to spectate) +alias unplayban "qc_cmd_sv unplayban ${* ?}" // Remove an existing play ban client +alias voteban "qc_cmd_sv voteban ${* ?}" // Ban disallowing a client from voting +alias unvoteban "qc_cmd_sv unvoteban ${* ?}" // Remove an existing vote ban client + +// other aliases for muteban, playban and voteban lists +alias mutebans "g_muteban_list ${* ?}" +alias playbans "g_playban_list ${* ?}" +alias votebans "g_voteban_list ${* ?}" + + // character classes (intersected with 32..126 minus ", $, ;, ^, \ - if you // want these, include them explicitly) // note that QC code always forbids $ and ; in VoteCommand_checknasty diff --git a/notifications.cfg b/notifications.cfg index c2225f26e..b38304dbe 100644 --- a/notifications.cfg +++ b/notifications.cfg @@ -96,7 +96,11 @@ seta notification_ANNCE_VOTE_FAIL "2" "0 = disabled, 1 = enabled if gentle mode // MSG_INFO notifications: seta notification_INFO_CA_JOIN_LATE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_CA_LEAVE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_CHAT_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_CHAT_NOSPECTATORS "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_CHAT_PRIVATE_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_CHAT_SPECTATOR_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_CHAT_TEAM_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_COINTOSS "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_CONNECTING "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_COUNTDOWN_RESTART "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -258,6 +262,7 @@ seta notification_INFO_QUIT_DISCONNECT "2" "0 = off, 1 = print to console, 2 = p seta notification_INFO_QUIT_KICK_IDLING "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_QUIT_KICK_SPECTATING "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_QUIT_KICK_TEAMKILL "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_QUIT_PLAYBAN_TEAMKILL "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_QUIT_SPECTATE "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_ABANDONED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_FAIL_RANKED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -476,6 +481,7 @@ seta notification_CENTER_ITEM_WEAPON_NOAMMO "1" "0 = off, 1 = centerprint" seta notification_CENTER_ITEM_WEAPON_PRIMORSEC "1" "0 = off, 1 = centerprint" seta notification_CENTER_ITEM_WEAPON_UNAVAILABLE "1" "0 = off, 1 = centerprint" seta notification_CENTER_JOIN_NOSPAWNS "1" "0 = off, 1 = centerprint" +seta notification_CENTER_JOIN_PLAYBAN "1" "0 = off, 1 = centerprint" seta notification_CENTER_JOIN_PREVENT "1" "0 = off, 1 = centerprint" seta notification_CENTER_JOIN_PREVENT_MINIGAME "1" "0 = off, 1 = centerprint" seta notification_CENTER_KEEPAWAY_DROPPED "1" "0 = off, 1 = centerprint" @@ -523,6 +529,7 @@ seta notification_CENTER_POWERUP_INVISIBILITY "1" "0 = off, 1 = centerprint" seta notification_CENTER_POWERUP_SHIELD "1" "0 = off, 1 = centerprint" seta notification_CENTER_POWERUP_SPEED "1" "0 = off, 1 = centerprint" seta notification_CENTER_POWERUP_STRENGTH "1" "0 = off, 1 = centerprint" +seta notification_CENTER_QUIT_PLAYBAN_TEAMKILL "1" "0 = off, 1 = centerprint" seta notification_CENTER_RACE_FINISHLAP "1" "0 = off, 1 = centerprint" seta notification_CENTER_ROUND_OVER "1" "0 = off, 1 = centerprint" seta notification_CENTER_ROUND_PLAYER_WIN "1" "0 = off, 1 = centerprint" @@ -550,6 +557,8 @@ seta notification_CENTER_VEHICLE_ENTER_GUNNER "1" "0 = off, 1 = centerprint" seta notification_CENTER_VEHICLE_ENTER_STEAL "1" "0 = off, 1 = centerprint" seta notification_CENTER_VEHICLE_STEAL "1" "0 = off, 1 = centerprint" seta notification_CENTER_VEHICLE_STEAL_SELF "1" "0 = off, 1 = centerprint" +seta notification_CENTER_VOTEBAN "1" "0 = off, 1 = centerprint" +seta notification_CENTER_VOTEBANYN "1" "0 = off, 1 = centerprint" seta notification_CENTER_WEAPON_MINELAYER_LIMIT "1" "0 = off, 1 = centerprint" // MSG_MULTI notifications: diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc index f8e364fdd..5d55fc1cf 100644 --- a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc +++ b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc @@ -1,7 +1,10 @@ #include "sv_kick_teamkiller.qh" +#include float autocvar_g_kick_teamkiller_rate; float autocvar_g_kick_teamkiller_lower_limit; +int autocvar_g_kick_teamkiller_severity; +float autocvar_g_kick_teamkiller_bantime; REGISTER_MUTATOR(kick_teamkiller, (autocvar_g_kick_teamkiller_rate > 0)); @@ -21,14 +24,53 @@ MUTATOR_HOOKFUNCTION(kick_teamkiller, PlayerDies) return; } + float masksize = autocvar_g_ban_default_masksize; + float bantime = autocvar_g_kick_teamkiller_bantime; + int teamkills = PlayerScore_Get(attacker, SP_TEAMKILLS); // use the players actual playtime float playtime = time - CS(attacker).startplaytime; // rate is in teamkills/minutes, playtime in seconds if (teamkills >= autocvar_g_kick_teamkiller_lower_limit && - teamkills >= autocvar_g_kick_teamkiller_rate*playtime/60.0) + teamkills >= autocvar_g_kick_teamkiller_rate*playtime/60.0) { - if (dropclient_schedule(attacker)) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_TEAMKILL, attacker.netname); + switch (autocvar_g_kick_teamkiller_severity) + { + case 1: + { + if (dropclient_schedule(attacker)) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_TEAMKILL, attacker.netname); + return; + } + case 2: + { + attacker.respawn_flags = RESPAWN_SILENT; + Ban_KickBanClient(attacker, bantime, masksize, "Team Killing"); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_TEAMKILL, attacker.netname); + return; + } + default: + { + attacker.respawn_flags = RESPAWN_SILENT; + string theid = ""; + + if(!PlayerInIPList(attacker, autocvar_g_playban_list)) + theid = cons(theid, attacker.netaddress); + if(!PlayerInIDList(attacker, autocvar_g_playban_list)) + theid = cons(theid, attacker.crypto_idfp); + + LOG_INFO(strcat("Play-banning player ", GetCallerName(attacker), " (", attacker.netaddress, ").")); + PutObserverInServer(attacker, true, true); + cvar_set("g_playban_list", cons(autocvar_g_playban_list, theid)); + + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_PLAYBAN_TEAMKILL, attacker.netname); + Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_QUIT_PLAYBAN_TEAMKILL); + + if (PlayerInList(attacker, autocvar_g_playban_list)) + TRANSMUTE(Observer, attacker); + + return; + } + } } } diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index aa42501a0..061d951a6 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -231,7 +231,11 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_INFO_NOTIF_TEAM(NUM_TEAM_4, prefix##_PINK, prefix, defaultvalue, strnum, flnum, args, hudargs, multiteam_info_sprintf(icon, strtolower(STATIC_NAME_TEAM_4)), TCR(normal, type, 4), TCR(gentle, type, 4)) // MSG_INFO_NOTIFICATIONS + MSG_INFO_NOTIF(CHAT_DISABLED, N_CHATCON, 0, 0, "", "", "", _("^F4NOTE: ^BGChat is currently disabled on this server"), "") MSG_INFO_NOTIF(CHAT_NOSPECTATORS, N_CHATCON, 0, 0, "", "", "", _("^F4NOTE: ^BGSpectator chat is not sent to players during the match"), "") + MSG_INFO_NOTIF(CHAT_PRIVATE_DISABLED, N_CHATCON, 0, 0, "", "", "", _("^F4NOTE: ^BGPrivate chat is currently disabled on this server"), "") + MSG_INFO_NOTIF(CHAT_SPECTATOR_DISABLED, N_CHATCON, 0, 0, "", "", "", _("^F4NOTE: ^BGSpectator chat is currently disabled on this server"), "") + MSG_INFO_NOTIF(CHAT_TEAM_DISABLED, N_CHATCON, 0, 0, "", "", "", _("^F4NOTE: ^BGTeam chat is currently disabled on this server"), "") MULTITEAM_INFO(CTF_CAPTURE, N_CONSOLE, 1, 0, "s1", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag"), "", FLAG) MULTITEAM_INFO(CTF_CAPTURE_BROKEN, N_CONSOLE, 2, 2, "s1 f1dtime s2 f2dtime", "s1", "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG%s^BG's previous record of ^F2%s^BG seconds"), "", FLAG) @@ -423,6 +427,7 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_INFO_NOTIF(MOVETOSPEC_IDLING, N_CHATCON, 1, 1, "s1 f1", "", "", _("^BG%s^F3 was moved to^BG spectators^F3 after idling for %s seconds"), "") MSG_INFO_NOTIF(QUIT_KICK_SPECTATING, N_CONSOLE, 0, 0, "", "", "", _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "") MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 was kicked for excessive teamkilling"), "") + MSG_INFO_NOTIF(QUIT_PLAYBAN_TEAMKILL, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 was forced to spectate for excessive teamkilling"), "") MSG_INFO_NOTIF(QUIT_SPECTATE, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 is now^BG spectating"), "") MSG_INFO_NOTIF(RACE_ABANDONED, N_CONSOLE, 1, 0, "s1", "", "", _("^BG%s^BG has abandoned the race"), "") @@ -709,6 +714,7 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_CENTER_NOTIF(JOIN_NOSPAWNS, N_ENABLE, 0, 0, "", CPID_PREVENT_JOIN, "0 0", _("^K1No spawnpoints available!\nHope your team can fix it..."), "") MSG_CENTER_NOTIF(JOIN_PREVENT, N_ENABLE, 0, 1, "f1", CPID_PREVENT_JOIN, "0 0", _("^K1You may not join the game at this time.\nThis match is limited to ^F2%s^BG players."), "") + MSG_CENTER_NOTIF(JOIN_PLAYBAN, N_ENABLE, 0, 0, "", CPID_PREVENT_JOIN, "0 0", BOLD(_("^K1You aren't allowed to play because you are banned in this server, but you can play minigames")), "") MSG_CENTER_NOTIF(KEEPAWAY_DROPPED, N_ENABLE, 1, 0, "s1", CPID_KEEPAWAY, "0 0", _("^BG%s^BG has dropped the ball!"), "") MSG_CENTER_NOTIF(KEEPAWAY_PICKUP, N_ENABLE, 1, 0, "s1", CPID_KEEPAWAY, "0 0", _("^BG%s^BG has picked up the ball!"), "") @@ -769,6 +775,8 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_CENTER_NOTIF(POWERUP_INVISIBILITY, N_ENABLE, 0, 0, "", CPID_POWERUP, "0 0", _("^F2You are invisible"), "") MSG_CENTER_NOTIF(POWERDOWN_INVISIBILITY, N_ENABLE, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Invisibility has worn off"), "") + MSG_CENTER_NOTIF(QUIT_PLAYBAN_TEAMKILL, N_ENABLE, 0, 0, "", CPID_Null, "0 0", BOLD(_("^K1You are forced to spectate and you aren't allowed to play because you are banned in this server")), "") + MSG_CENTER_NOTIF(RACE_FINISHLAP, N_ENABLE, 0, 0, "", CPID_RACE_FINISHLAP, "0 0", _("^F2The race is over, finish your lap!"), "") MSG_CENTER_NOTIF(SEQUENCE_COMPLETED, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^BGSequence completed!"), "") @@ -800,6 +808,9 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_CENTER_NOTIF(VEHICLE_STEAL, N_ENABLE, 0, 0, "", CPID_VEHICLES_OTHER, "0 0", _("^F2The enemy is stealing one of your vehicles!\n^F4Stop them!"), "") MSG_CENTER_NOTIF(VEHICLE_STEAL_SELF, N_ENABLE, 0, 0, "", CPID_VEHICLES_OTHER, "4 0", _("^F2Intruder detected, disabling shields!"), "") + MSG_CENTER_NOTIF(VOTEBAN, N_ENABLE, 0, 0, "", CPID_Null, "0 0", BOLD(_("^K1You aren't allowed to call a vote because you are banned in this server")), "") + MSG_CENTER_NOTIF(VOTEBANYN, N_ENABLE, 0, 0, "", CPID_Null, "0 0", BOLD(_("^K1You aren't allowed to vote because you are banned in this server")), "") + MSG_CENTER_NOTIF(WEAPON_MINELAYER_LIMIT, N_ENABLE, 0, 1, "f1", CPID_Null, "0 0", _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "") #undef N_DISABL diff --git a/qcsrc/server/chat.qc b/qcsrc/server/chat.qc index 79d866523..5a72b5977 100644 --- a/qcsrc/server/chat.qc +++ b/qcsrc/server/chat.qc @@ -3,10 +3,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -24,6 +26,30 @@ */ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol) { + if(!autocvar_g_chat_allowed && IS_REAL_CLIENT(source)) + { + Send_Notification(NOTIF_ONE_ONLY, source, MSG_INFO, INFO_CHAT_DISABLED); + return 0; + } + + if(!autocvar_g_chat_private_allowed && privatesay) + { + Send_Notification(NOTIF_ONE_ONLY, source, MSG_INFO, INFO_CHAT_PRIVATE_DISABLED); + return 0; + } + + if(!autocvar_g_chat_spectator_allowed && IS_OBSERVER(source)) + { + Send_Notification(NOTIF_ONE_ONLY, source, MSG_INFO, INFO_CHAT_SPECTATOR_DISABLED); + return 0; + } + + if(!autocvar_g_chat_team_allowed && teamsay) + { + Send_Notification(NOTIF_ONE_ONLY, source, MSG_INFO, INFO_CHAT_TEAM_DISABLED); + return 0; + } + if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ") msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) @@ -283,6 +309,9 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source)) { + if(IS_REAL_CLIENT(source) && ignore_playerinlist(source, privatesay)) // check ignored players from personal chat log (from "ignore" command) + return -1; // no sending to this player, thank you very much + sprint(privatesay, msgstr); if(cmsgstr != "") centerprint(privatesay, cmsgstr); @@ -293,6 +322,9 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc sprint(source, sourcemsgstr); dedicated_print(msgstr); // send to server console too FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && CS(it).active_minigame == CS(source).active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + if(IS_REAL_CLIENT(source) && ignore_playerinlist(source, it)) // check ignored players from personal chat log (from "ignore" command) + continue; // no sending to this player, thank you very much + sprint(it, msgstr); }); event_log_msg = sprintf(":chat_minigame:%d:%s:%s", source.playerid, CS(source).active_minigame.netname, msgin); @@ -305,6 +337,9 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc if(sourcecmsgstr != "") centerprint(source, sourcecmsgstr); FOREACH_CLIENT((IS_PLAYER(it) || INGAME(it)) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + if(IS_REAL_CLIENT(source) && ignore_playerinlist(source, it)) // check ignored players from personal chat log (from "ignore" command) + continue; // no sending to this player, thank you very much + sprint(it, msgstr); if(cmsgstr != "") centerprint(it, cmsgstr); @@ -316,6 +351,9 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc sprint(source, sourcemsgstr); dedicated_print(msgstr); // send to server console too FOREACH_CLIENT(!(IS_PLAYER(it) || INGAME(it)) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + if(IS_REAL_CLIENT(source) && ignore_playerinlist(source, it)) // check ignored players from personal chat log (from "ignore" command) + continue; // no sending to this player, thank you very much + sprint(it, msgstr); }); event_log_msg = sprintf(":chat_spec:%d:%s", source.playerid, strreplace("\n", " ", msgin)); @@ -328,6 +366,9 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc MX_Say(strcat(playername(source.netname, source.team, IS_PLAYER(source)), "^7: ", msgin)); } FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + if(IS_REAL_CLIENT(source) && ignore_playerinlist(source, it)) // check ignored players from personal chat log (from "ignore" command) + continue; // no sending to this player, thank you very much + sprint(it, msgstr); }); event_log_msg = sprintf(":chat:%d:%s", source.playerid, strreplace("\n", " ", msgin)); @@ -348,7 +389,7 @@ entity findnearest(vector point, bool checkitems, vector axismod) IL_EACH(((checkitems) ? g_items : g_locations), ((checkitems) ? (it.target == "###item###") : (it.classname == "target_location")), { - if ((it.items == IT_KEY1 || it.items == IT_KEY2) && it.target == "###item###") + if ((it.items == IT_KEY1 || it.items == IT_KEY2) && it.target == "###item###") dist = it.oldorigin; else dist = it.origin; diff --git a/qcsrc/server/chat.qh b/qcsrc/server/chat.qh index 500d757c0..a921f61ec 100644 --- a/qcsrc/server/chat.qh +++ b/qcsrc/server/chat.qh @@ -10,6 +10,10 @@ bool autocvar_g_chat_flood_notify_flooder; float autocvar_g_chat_flood_spl; float autocvar_g_chat_flood_spl_team; float autocvar_g_chat_flood_spl_tell; +bool autocvar_g_chat_allowed; +bool autocvar_g_chat_private_allowed; +bool autocvar_g_chat_spectator_allowed; +bool autocvar_g_chat_team_allowed; int autocvar_g_chat_nospectators; bool autocvar_g_chat_teamcolors; bool autocvar_g_chat_tellprivacy; diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 4c19adcff..1f61b18aa 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -51,7 +51,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -1186,6 +1187,13 @@ void ClientConnect(entity this) Handicap_Initialize(this); + // playban + if (PlayerInList(this, autocvar_g_playban_list)) + TRANSMUTE(Observer, this); + + if (PlayerInList(this, autocvar_g_muteban_list)) // muteban + CS(this).muted = true; + MUTATOR_CALLHOOK(ClientConnect, this); if (player_count == 1) @@ -1209,6 +1217,20 @@ void ClientDisconnect(entity this) { assert(IS_CLIENT(this), return); + /* from "ignore" command */ + strfree(this.ignore_list); + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it.ignore_list, + { + if(it.crypto_idfp && it.crypto_idfp != "") + continue; + string mylist = ignore_removefromlist(it, this); + if(it.ignore_list) + strunzone(it.ignore_list); + + it.ignore_list = strzone(mylist); + }); + /* from "ignore" command */ + PlayerStats_GameReport_FinalizePlayer(this); if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); if (CS(this).active_minigame) part_minigame(this); @@ -2026,6 +2048,17 @@ int nJoinAllowed(entity this, entity ignore) if(this && (Player_GetForcedTeamIndex(this) == TEAM_FORCE_SPECTATOR)) return 0; // forced spectators can never join + static float msg_time = 0; + if(this && !INGAME(this) && ignore && PlayerInList(this, autocvar_g_playban_list)) + { + if(time > msg_time) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PLAYBAN); + msg_time = time + 0.5; + } + return 0; + } + // TODO simplify this int totalClients = 0; int currentlyPlaying = 0; @@ -2044,7 +2077,6 @@ int nJoinAllowed(entity this, entity ignore) else if(player_limit > 0 && currentlyPlaying < player_limit) free_slots = min(maxclients - totalClients, player_limit - currentlyPlaying); - static float msg_time = 0; if(this && !INGAME(this) && ignore && !free_slots && time > msg_time) { Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT, player_limit); @@ -2288,6 +2320,7 @@ void ObserverOrSpectatorThink(entity this) if ((is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) || (!is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this)))) { this.flags |= FL_JUMPRELEASED; + // primary attack pressed if(this.flags & FL_SPAWNING) { this.flags &= ~FL_SPAWNING; diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh index 33b9d4511..8c607cf9a 100644 --- a/qcsrc/server/client.qh +++ b/qcsrc/server/client.qh @@ -359,6 +359,10 @@ void calculate_player_respawn_time(entity this); bool PlayerInList(entity player, string list); +bool PlayerInIDList(entity p, string idlist); + +bool PlayerInIPList(entity p, string iplist); + void ClientData_Touch(entity e); int nJoinAllowed(entity this, entity ignore); diff --git a/qcsrc/server/command/banning.qc b/qcsrc/server/command/banning.qc index a73b09646..52c7e9c43 100644 --- a/qcsrc/server/command/banning.qc +++ b/qcsrc/server/command/banning.qc @@ -119,7 +119,7 @@ void BanCommand_kickban(int request, int argc, string command) } } -void BanCommand_mute(int request, int argc, string command) // TODO: Add a sort of mute-"ban" which allows players to be muted based on IP/cryptokey +void BanCommand_mute(int request, int argc, string command) { switch (request) { @@ -127,12 +127,20 @@ void BanCommand_mute(int request, int argc, string command) // TODO: Add a sort { if (argc >= 2) { - entity client = GetFilteredEntity(argv(1)); + entity client = GetIndexedEntity(argc, 1); float accepted = VerifyClientEntity(client, true, false); if (accepted > 0) { + string theid = ""; + if(!PlayerInIPList(client, autocvar_g_muteban_list)) + theid = cons(theid, client.netaddress); + if(!PlayerInIDList(client, autocvar_g_muteban_list)) + theid = cons(theid, client.crypto_idfp); CS(client).muted = true; + LOG_INFO(strcat("Mute-banning player ", GetCallerName(client), " (", argv(1), ").")); + cvar_set("g_muteban_list", cons(autocvar_g_muteban_list, theid)); + return; } else @@ -148,7 +156,51 @@ void BanCommand_mute(int request, int argc, string command) // TODO: Add a sort { LOG_HELP("Usage:^3 sv_cmd mute "); LOG_HELP(" is the entity number or name of the player to mute."); - LOG_HELP("See also: ^2unmute^7"); + LOG_HELP("See also: ^2unmute, g_muteban_list^7"); + return; + } + } +} + +void BanCommand_playban(int request, int argc, string command) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (argc >= 2) + { + entity client = GetIndexedEntity(argc, 1); + float accepted = VerifyClientEntity(client, true, false); + + if (accepted > 0) + { + string theid = ""; + if(!PlayerInIPList(client, autocvar_g_playban_list)) + theid = cons(theid, client.netaddress); + if(!PlayerInIDList(client, autocvar_g_playban_list)) + theid = cons(theid, client.crypto_idfp); + + LOG_INFO(strcat("Play-banning player ", GetCallerName(client), " (", argv(1), ").")); + PutObserverInServer(client, true, true); + cvar_set("g_playban_list", cons(autocvar_g_playban_list, theid)); + + return; + } + else + { + LOG_INFO("playban: ", GetClientErrorString(accepted, argv(1)), "."); + } + } + } + + default: + LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0)); + case CMD_REQUEST_USAGE: + { + LOG_HELP("Usage:^3 sv_cmd playban "); + LOG_HELP(" is the entity number or name of the player to ban being forced to spectate permanently,"); + LOG_HELP("See also: ^2g_playban_list, unplayban^7"); return; } } @@ -208,12 +260,24 @@ void BanCommand_unmute(int request, int argc) { if (argc >= 2) { - entity client = GetFilteredEntity(argv(1)); + entity client = GetIndexedEntity(argc, 1); float accepted = VerifyClientEntity(client, true, false); + string original_arg = argv(1); if (accepted > 0) { + string tmp_string = ""; + FOREACH_WORD(autocvar_g_muteban_list, it != client.netaddress, + { + if(client.crypto_idfp && it == substring(client.crypto_idfp, 0, strlen(it))) + continue; + tmp_string = cons(tmp_string, it); + }); + + cvar_set("g_muteban_list", tmp_string); + LOG_INFO(strcat("Unmuting player ", GetCallerName(client), " (", original_arg, ").")); CS(client).muted = false; + return; } else @@ -229,7 +293,140 @@ void BanCommand_unmute(int request, int argc) { LOG_HELP("Usage:^3 sv_cmd unmute "); LOG_HELP(" is the entity number or name of the player to unmute."); - LOG_HELP("See also: ^2mute^7"); + LOG_HELP("See also: ^2mute, g_muteban_list^7"); + return; + } + } +} + +void BanCommand_unplayban(int request, int argc) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (argv(1)) + { + entity client = GetIndexedEntity(argc, 1); + float accepted = VerifyClientEntity(client, true, false); + string original_arg = argv(1); + + if (accepted > 0) + { + string tmp_string = ""; + FOREACH_WORD(autocvar_g_playban_list, it != client.netaddress, + { + if(client.crypto_idfp && it == substring(client.crypto_idfp, 0, strlen(it))) + continue; + tmp_string = cons(tmp_string, it); + }); + + cvar_set("g_playban_list", tmp_string); + LOG_INFO(strcat("Releasing forced to spectate player ", GetCallerName(client), " (", original_arg, ").")); + + return; + } + else + { + LOG_INFO("unplayban: ", GetClientErrorString(accepted, argv(1)), "."); + } + } + } + + default: + case CMD_REQUEST_USAGE: + { + LOG_HELP("Usage:^3 sv_cmd unplayban "); + LOG_HELP(" Where is the ID of the forced to spectate ban of which to remove."); + LOG_HELP("See also: ^2playban, g_playban_list^7"); + return; + } + } +} + +void BanCommand_unvoteban(int request, int argc) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (argv(1)) + { + entity client = GetIndexedEntity(argc, 1); + float accepted = VerifyClientEntity(client, true, false); + string original_arg = argv(1); + + if (accepted > 0) + { + string tmp_string = ""; + FOREACH_WORD(autocvar_g_voteban_list, it != client.netaddress, + { + if(client.crypto_idfp && it == substring(client.crypto_idfp, 0, strlen(it))) + continue; + tmp_string = cons(tmp_string, it); + }); + + cvar_set("g_voteban_list", tmp_string); + LOG_INFO(strcat("Unvote-banning player ", GetCallerName(client), " (", original_arg, ").")); + + return; + } + else + { + LOG_INFO("unvoteban: ", GetClientErrorString(accepted, argv(1)), "."); + } + } + } + + default: + case CMD_REQUEST_USAGE: + { + LOG_HELP("Usage:^3 sv_cmd unvoteban "); + LOG_HELP(" Where is the ID of the ban from voting of which to remove."); + LOG_HELP("See also: ^2voteban, g_voteban_list^7"); + return; + } + } +} + +void BanCommand_voteban(int request, int argc, string command) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (argc >= 2) + { + entity client = GetIndexedEntity(argc, 1); + float accepted = VerifyClientEntity(client, true, false); + + if (accepted > 0) + { + string theid = ""; + if(!PlayerInIPList(client, autocvar_g_voteban_list)) + theid = cons(theid, client.netaddress); + if(!PlayerInIDList(client, autocvar_g_voteban_list)) + theid = cons(theid, client.crypto_idfp); + + LOG_INFO(strcat("Vote-banning player ", GetCallerName(client), " (", argv(1), ").")); + cvar_set("g_voteban_list", cons(autocvar_g_voteban_list, theid)); + + return; + } + else + { + LOG_INFO("voteban: ", GetClientErrorString(accepted, argv(1)), "."); + } + } + } + + default: + LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0)); + case CMD_REQUEST_USAGE: + { + LOG_HELP("Usage:^3 sv_cmd voteban "); + LOG_HELP(" is the entity number or name of the player to ban from voting,"); + LOG_HELP("See also: ^2g_voteban_list, unvoteban^7"); return; } } @@ -269,8 +466,12 @@ void BanCommand_(int request) BAN_COMMAND("banlist", BanCommand_banlist(request), "List all existing bans") \ BAN_COMMAND("kickban", BanCommand_kickban(request, arguments, command), "Disconnect a client and ban it at the same time") \ BAN_COMMAND("mute", BanCommand_mute(request, arguments, command), "Disallow a client from talking by muting them") \ + BAN_COMMAND("playban", BanCommand_playban(request, arguments, command), "Force to spectate a client permanently") \ BAN_COMMAND("unban", BanCommand_unban(request, arguments), "Remove an existing ban") \ BAN_COMMAND("unmute", BanCommand_unmute(request, arguments), "Unmute a client") \ + BAN_COMMAND("unvoteban", BanCommand_unvoteban(request, arguments), "Remove an existing voting ban") \ + BAN_COMMAND("unplayban", BanCommand_unplayban(request, arguments), "Remove an existing forced to spectate ban") \ + BAN_COMMAND("voteban", BanCommand_voteban(request, arguments, command), "Disallow a client from voting") \ /* nothing */ void BanCommand_macro_help() diff --git a/qcsrc/server/command/banning.qh b/qcsrc/server/command/banning.qh index 80eb85b56..6aa70c6a8 100644 --- a/qcsrc/server/command/banning.qh +++ b/qcsrc/server/command/banning.qh @@ -10,6 +10,9 @@ string autocvar_g_ban_sync_uri; bool autocvar_g_ban_telluser = true; string autocvar_g_banned_list; bool autocvar_g_banned_list_idmode; +string autocvar_g_muteban_list; // "List of banned players from chat" +string autocvar_g_playban_list; // "List of banned players from playing (forced to spectate)" +string autocvar_g_voteban_list; // "List of banned players from voting" #define GET_BAN_ARG(v, d) if (argc > reason_arg) { if ((v = stof(argv(reason_arg))) != 0) ++reason_arg; else v = d; } else { v = d; } #define GET_BAN_REASON(v, d) if (argc > reason_arg) v = substring(command, argv_start_index(reason_arg), strlen(command) - argv_start_index(reason_arg)); else v = d; diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 947edd891..0eae7e0e6 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -70,6 +70,35 @@ void ClientCommand_autoswitch(entity caller, int request, int argc) } } +void ClientCommand_clear_ignores(entity caller, int request) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + bool advanced = (caller.crypto_idfp && caller.crypto_idfp != ""); + + if(!advanced) + { + sprint(caller, "You don't have a stats UID, unable to clear your ignore list.\n"); + return; + } + + ignore_clearall(caller); + sprint(caller, "All permanent ignores cleared!\n"); + return; + } + + default: + case CMD_REQUEST_USAGE: + { + sprint(caller, "\nUsage:^3 cmd clear_ignores\n"); + sprint(caller, " Removes all existing ignored clients whose are kept out of personal chat log.\n"); + return; + } + } +} + void ClientCommand_clientversion(entity caller, int request, int argc) // internal command, used only by code { switch (request) @@ -249,6 +278,78 @@ void ClientCommand_wpeditor(entity caller, int request, int argc) } } +void ClientCommand_ignore(entity caller, int request, int argc, string command) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (argc >= 2) + { + bool advanced = (caller.crypto_idfp && caller.crypto_idfp != ""); + + if(!argv(1) || argv(1) == "") + { + sprint(caller, "This command requires an argument. Use a player's name or their ID from the ^2status^7 command.\n"); + return; + } + + entity ignore_to = GetIndexedEntity(argc, 1); + float ignore_accepted = VerifyClientEntity(ignore_to, true, false); + + if (ignore_accepted > 0 && IS_REAL_CLIENT(ignore_to)) // the target is a real client + { + if (ignore_to != caller) // and we're allowed to ignore them heh + { + if(ignore_playerinlist(ignore_to, caller)) + { + sprint(caller, ignore_to.netname, " ^7is already ignored!\n"); + return; + } + + // advanced ignore mode, works if both the player and the sender have a stats UID + if(advanced && ignore_to.crypto_idfp && ignore_to.crypto_idfp != "") + { + for(int j = 0; j < IGNORE_MAXPLAYERS; ++j) + { + string pos = db_get(ServerProgsDB, strcat("/ignore/", caller.crypto_idfp, "/", ftos(j))); + if(pos == "") + { + db_put(ServerProgsDB, strcat("/ignore/", caller.crypto_idfp, "/", ftos(j)), ignore_to.crypto_idfp); + sprint(caller, "You will no longer receive messages from ", ignore_to.netname, "^7, use ^2unignore^7 to hear them again.\n"); + return; + } + } + + sprint(caller, "You may only ignore up to ", ftos(IGNORE_MAXPLAYERS), " players, remove one before trying again.\n"); + return; + } + + if(caller.ignore_list) + strunzone(caller.ignore_list); + caller.ignore_list = strzone(cons(caller.ignore_list, ftos(etof(ignore_to)))); + + sprint(caller, "You no longer receive messages from ", ignore_to.netname, "^7, use ^2unignore^7 to hear them again.\n"); + } + else { sprint(caller, "You can't ^2ignore^7 yourself.\n"); } + } + else { print_to(caller, strcat("ignore: ", GetClientErrorString(ignore_accepted, argv(1)), ".\nUnable to ignore this player, check their ID.")); } + + return; + } + } + + default: + sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0))); + case CMD_REQUEST_USAGE: + { + sprint(caller, "\nUsage:^3 cmd ignore \n"); + sprint(caller, " Where is the entity number or name of the player to ignore keeping out of personal chat log.\n"); + return; + } + } +} + void ClientCommand_join(entity caller, int request) { switch (request) @@ -573,7 +674,7 @@ void ClientCommand_spectate(entity caller, int request) if(IS_SPEC(caller) || IS_OBSERVER(caller)) { entity client = GetFilteredEntity(argv(1)); - int spec_accepted = VerifyClientEntity(client, false, false); + float spec_accepted = VerifyClientEntity(client, false, false); if(spec_accepted > 0 && IS_PLAYER(client)) { bool caller_is_observer = (IS_OBSERVER(caller)); @@ -691,6 +792,60 @@ void ClientCommand_tell(entity caller, int request, int argc, string command) } } +void ClientCommand_unignore(entity caller, int request, int argc, string command) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (argc >= 2) + { + bool advanced = (caller.crypto_idfp && caller.crypto_idfp != ""); + + if(!argv(1) || argv(1) == "") + { + sprint(caller, "This command requires an argument. Use a player's name or their ID from the ^2status^7 command.\n"); + return; + } + + entity unignore_to = GetIndexedEntity(argc, 1); + float unignore_accepted = VerifyClientEntity(unignore_to, true, false); + + if (unignore_accepted > 0 && IS_REAL_CLIENT(unignore_to)) // the target is a real client + { + if (unignore_to != caller) + { + string mylist = ignore_removefromlist(caller, unignore_to); + if(!advanced) + { + if(caller.ignore_list) + strunzone(caller.ignore_list); + + caller.ignore_list = strzone(mylist); + } + + sprint(caller, "You can now receive messages from ", unignore_to.netname, " ^7again.\n"); + return; + } + else { sprint(caller, "You can't ^2unignore^7 yourself.\n"); } + } + else { print_to(caller, strcat("unignore: ", GetClientErrorString(unignore_accepted, argv(1)), ".\nUnable to stop ignoring this player, check their ID.")); } + + return; + } + } + + default: + sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0))); + case CMD_REQUEST_USAGE: + { + sprint(caller, "\nUsage:^3 cmd unignore \n"); + sprint(caller, " Where is the entity number or name of the player to stop ignoring when is keeping out of personal chat log.\n"); + return; + } + } +} + void ClientCommand_voice(entity caller, int request, int argc, string command) { switch (request) @@ -770,7 +925,9 @@ void ClientCommand_(entity caller, int request) // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) #define CLIENT_COMMANDS(ent, request, arguments, command) \ CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(ent, request, arguments), "Whether or not to switch automatically when getting a better weapon") \ + CLIENT_COMMAND("clear_ignores", ClientCommand_clear_ignores(ent, request), "Remove all existing ignored clients") \ CLIENT_COMMAND("clientversion", ClientCommand_clientversion(ent, request, arguments), "Release version of the game") \ + CLIENT_COMMAND("ignore", ClientCommand_ignore(ent, request, arguments, command), "Ignore a client in the game keeping out of personal chat log for a match") \ CLIENT_COMMAND("join", ClientCommand_join(ent, request), "Become a player in the game") \ CLIENT_COMMAND("kill", ClientCommand_kill(ent, request), "Become a member of the dead") \ CLIENT_COMMAND("minigame", ClientCommand_minigame(ent, request, arguments, command), "Start a minigame") \ @@ -786,6 +943,7 @@ void ClientCommand_(entity caller, int request) CLIENT_COMMAND("suggestmap", ClientCommand_suggestmap(ent, request, arguments), "Suggest a map to the mapvote at match end") \ CLIENT_COMMAND("tell", ClientCommand_tell(ent, request, arguments, command), "Send a message directly to a player") \ CLIENT_COMMAND("voice", ClientCommand_voice(ent, request, arguments, command), "Send voice message via sound") \ + CLIENT_COMMAND("unignore", ClientCommand_unignore(ent, request, arguments, command), "Remove an existing ignored player") \ CLIENT_COMMAND("wpeditor", ClientCommand_wpeditor(ent, request, arguments), "Waypoint editor commands") \ /* nothing */ diff --git a/qcsrc/server/command/cmd.qh b/qcsrc/server/command/cmd.qh index bb97f0d0b..27be10dd3 100644 --- a/qcsrc/server/command/cmd.qh +++ b/qcsrc/server/command/cmd.qh @@ -1,11 +1,84 @@ #pragma once +#include + float autocvar_sv_clientcommand_antispam_time; int autocvar_sv_clientcommand_antispam_count; .float cmd_floodtime; +.string ignore_list; // stores player id's, maybe can be upgraded to store net address for reconnect protection + +const int IGNORE_MAXPLAYERS = 8; // maximum players to be ignored in the personal chat string MapVote_Suggest(entity this, string m); // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file void ClientCommand_macro_write_aliases(float fh); + +// functions for ignore command +string ignore_removefromlist(entity list, entity ignore) +{ + if(ignore.crypto_idfp && ignore.crypto_idfp != "" && list.crypto_idfp && list.crypto_idfp != "") + { + for(int j = 0; j < IGNORE_MAXPLAYERS; ++j) + { + string pos = db_get(ServerProgsDB, strcat("/ignore/", list.crypto_idfp, "/", ftos(j))); + if(pos == ignore.crypto_idfp) + { + db_remove(ServerProgsDB, strcat("/ignore/", list.crypto_idfp, "/", ftos(j))); + return string_null; + } + } + // should this fall back? we know advanced mode is being used + } + + string newlist = ""; + string theid = ftos(etof(ignore)); + + FOREACH_WORD(list.ignore_list, it != theid, + { + newlist = cons(newlist, it); + }); + + if(newlist == "") + return string_null; + else + return newlist; +} + +bool ignore_playerinlist(entity sender, entity targ) +{ + // TODO: optimize this by saving it to .ignore_list? + if(targ.crypto_idfp && targ.crypto_idfp != "" && sender.crypto_idfp && sender.crypto_idfp != "") + { + string thelist = ""; + for(int j = 0; j < IGNORE_MAXPLAYERS; ++j) + { + string pos = db_get(ServerProgsDB, strcat("/ignore/", targ.crypto_idfp, "/", ftos(j))); + thelist = cons(thelist, pos); + } + + return ((thelist != "") ? PlayerInList(sender, thelist) : false); + } + else if(!targ.ignore_list || targ.ignore_list == "") + return false; + + string theid = ftos(etof(sender)); + + FOREACH_WORD(targ.ignore_list, it == theid, + { + return true; + }); + + return false; +} + +void ignore_clearall(entity this) +{ + for(int j = 0; j < IGNORE_MAXPLAYERS; ++j) + { + string pos = db_get(ServerProgsDB, strcat("/ignore/", this.crypto_idfp, "/", ftos(j))); + if(pos != "") + db_remove(ServerProgsDB, strcat("/ignore/", this.crypto_idfp, "/", ftos(j))); + } +} diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 28fa7e7c0..6af0a28e4 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -849,6 +849,13 @@ void VoteCommand_abstain(int request, entity caller) // CLIENT ONLY { case CMD_REQUEST_COMMAND: { + if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban + { + print_to(caller, "^1You are banned from voting."); + Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN); + return; + } + if (!vote_called) { print_to(caller, "^1No vote called."); } else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) { @@ -891,6 +898,13 @@ void VoteCommand_call(int request, entity caller, int argc, string vote_command) float tmp_playercount = 0; int parse_error; + if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban + { + print_to(caller, "^1You are banned from calling a vote."); + Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBAN); + return; + } + vote_command = VoteCommand_extractcommand(vote_command, 2, argc); if (!autocvar_sv_vote_call && caller) @@ -1106,6 +1120,13 @@ void VoteCommand_no(int request, entity caller) // CLIENT ONLY { case CMD_REQUEST_COMMAND: { + if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban + { + print_to(caller, "^1You are banned from voting."); + Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN); + return; + } + if (!vote_called) { print_to(caller, "^1No vote called."); } else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) { @@ -1188,6 +1209,13 @@ void VoteCommand_yes(int request, entity caller) // CLIENT ONLY { case CMD_REQUEST_COMMAND: { + if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban + { + print_to(caller, "^1You are banned from voting."); + Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN); + return; + } + if (!vote_called) { print_to(caller, "^1No vote called."); } else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) { diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index 124ca5afe..b3b31ff1b 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -236,6 +236,9 @@ void cvar_changes_init() BADCVAR("timeformat"); BADCVAR("timestamps"); BADCVAR("g_require_stats"); + BADCVAR("g_muteban_list"); + BADCVAR("g_playban_list"); + BADCVAR("g_voteban_list"); BADPREFIX("developer_"); BADPREFIX("g_ban_"); BADPREFIX("g_banned_list"); @@ -259,8 +262,6 @@ void cvar_changes_init() // these can contain player IDs, so better hide BADPREFIX("g_forced_team_"); - BADCVAR("sv_muteban_list"); - BADCVAR("sv_voteban_list"); BADCVAR("sv_allow_customplayermodels_idlist"); BADCVAR("sv_allow_customplayermodels_speciallist"); diff --git a/xonotic-server.cfg b/xonotic-server.cfg index 03404ebf4..08987e779 100644 --- a/xonotic-server.cfg +++ b/xonotic-server.cfg @@ -377,7 +377,11 @@ set g_chat_flood_burst_team 2 "team chat: allow bursts of so many chat lines" set g_chat_flood_spl_tell 1 "private chat: seconds between lines to not count as flooding" set g_chat_flood_lmax_tell 2 "private chat: maximum number of lines per chat message at once" set g_chat_flood_burst_tell 2 "private chat: allow bursts of so many chat lines" -set g_chat_flood_notify_flooder 1 "when 0, the flooder still can see their own message" +set g_chat_flood_notify_flooder 1 "when disabled, the flooder still can see their own message" +set g_chat_allowed 1 "allow players to communicate via in-game chat" +set g_chat_private_allowed 1 "allow players to communicate via in-game private chat" +set g_chat_spectator_allowed 1 "allow spectators to communicate via in-game chat" +set g_chat_team_allowed 1 "allow players to communicate via in-game team chat" set g_chat_teamcolors 0 "colorize nicknames in team color for chat" set g_chat_tellprivacy 1 "when disabled, tell messages are also sent to the server console log... otherwise they're kept private between players." set g_nick_flood_timeout 120 "time after which nick flood protection resets (set to 0 to disable nick flood checking)"