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
// 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)"
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)"
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"
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"
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:
#include "sv_kick_teamkiller.qh"
+#include <server/ipban.qh>
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));
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;
+ }
+ }
}
}
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)
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"), "")
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!"), "")
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!"), "")
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
#include <common/gamemodes/_mod.qh>
#include <common/mapobjects/target/location.qh>
#include <common/mapobjects/triggers.qh>
+#include <common/notifications/all.qh>
#include <common/teams.qh>
#include <common/util.qh>
#include <common/weapons/weapon.qh>
#include <common/wepent.qh>
+#include <server/command/cmd.qh>
#include <server/command/common.qh>
#include <server/gamelog.qh>
#include <server/main.qh>
*/
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!)
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);
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);
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);
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));
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));
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;
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;
#include <server/chat.qh>
#include <server/cheats.qh>
#include <server/clientkill.qh>
-#include <server/command/common.qh>
+#include <server/command/banning.qh>
+#include <server/command/cmd.qh>
#include <server/command/common.qh>
#include <server/command/vote.qh>
#include <server/compat/quake3.qh>
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)
{
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);
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;
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);
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;
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);
}
}
-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)
{
{
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
{
LOG_HELP("Usage:^3 sv_cmd mute <client>");
LOG_HELP(" <client> 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 <client>");
+ LOG_HELP(" <client> 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;
}
}
{
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
{
LOG_HELP("Usage:^3 sv_cmd unmute <client>");
LOG_HELP(" <client> 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 <banid>");
+ LOG_HELP(" Where <banid> 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 <banid>");
+ LOG_HELP(" Where <banid> 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 <client>");
+ LOG_HELP(" <client> is the entity number or name of the player to ban from voting,");
+ LOG_HELP("See also: ^2g_voteban_list, unvoteban^7");
return;
}
}
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()
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;
}
}
+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)
}
}
+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 <client>\n");
+ sprint(caller, " Where <client> 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)
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));
}
}
+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 <client>\n");
+ sprint(caller, " Where <client> 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)
// 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") \
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 */
#pragma once
+#include <server/world.qh>
+
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)));
+ }
+}
{
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)
{
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)
{
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)
{
{
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)
{
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");
// 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");
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)"