From 555540100210b8ad198f4671f06b92c1a538d06b Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Sun, 14 Jul 2024 01:04:57 +1000 Subject: [PATCH] Improve sv_spectate 0 notifications and description, refactor nospectators command The blockSpectators global is redundant. See https://gitlab.com/xonotic/xonotic-data.pk3dir/-/merge_requests/1289 Players are now given the grace period and SPECTATE_WARNING when sv_spectate is disabled during a game, as well as when `nospectators` is voted. Centreprints are added and displayed when they don't conflict with join restriction centreprints. --- notifications.cfg | 2 ++ qcsrc/common/notifications/all.inc | 3 +++ qcsrc/server/client.qc | 25 +++++++++++++++---------- qcsrc/server/client.qh | 3 +-- qcsrc/server/clientkill.qc | 3 ++- qcsrc/server/command/cmd.qc | 4 +++- qcsrc/server/command/sv_cmd.qc | 10 +++------- xonotic-server.cfg | 4 ++-- 8 files changed, 31 insertions(+), 23 deletions(-) diff --git a/notifications.cfg b/notifications.cfg index bc2693c21..c6f47737b 100644 --- a/notifications.cfg +++ b/notifications.cfg @@ -555,6 +555,8 @@ seta notification_CENTER_ROUND_TIED "1" "0 = off, 1 = centerprint" seta notification_CENTER_SEQUENCE_COMPLETED "1" "0 = off, 1 = centerprint" seta notification_CENTER_SEQUENCE_COUNTER "1" "0 = off, 1 = centerprint" seta notification_CENTER_SEQUENCE_COUNTER_FEWMORE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_SPECTATE_NOTALLOWED "1" "0 = off, 1 = centerprint" +seta notification_CENTER_SPECTATE_WARNING "1" "0 = off, 1 = centerprint" seta notification_CENTER_SUPERWEAPON_BROKEN "1" "0 = off, 1 = centerprint" seta notification_CENTER_SUPERWEAPON_LOST "1" "0 = off, 1 = centerprint" seta notification_CENTER_SUPERWEAPON_PICKUP "1" "0 = off, 1 = centerprint" diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index 60a9e2449..048310a72 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -801,6 +801,9 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_CENTER_NOTIF(SEQUENCE_COUNTER, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^BGThere are more to go..."), "") MSG_CENTER_NOTIF(SEQUENCE_COUNTER_FEWMORE, N_ENABLE, 0, 1, "f1", CPID_Null, "0 0", _("^BGOnly %s^BG more to go..."), "") + MSG_CENTER_NOTIF(SPECTATE_WARNING, N_ENABLE, 0, 1, "f1secs", CPID_PREVENT_JOIN, "0 0", _("^F2You have to become a player within the next %s, otherwise you will be kicked, because spectating isn't allowed at this time!"), "") // same string as INFO_SPECTATE_WARNING + MSG_CENTER_NOTIF(SPECTATE_NOTALLOWED, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^F2Spectating isn't allowed at this time!"), "") + MSG_CENTER_NOTIF(SUPERWEAPON_BROKEN, N_ENABLE, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Superweapons have broken down"), "") MSG_CENTER_NOTIF(SUPERWEAPON_LOST, N_ENABLE, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Superweapons have been lost"), "") MSG_CENTER_NOTIF(SUPERWEAPON_PICKUP, N_ENABLE, 0, 0, "", CPID_POWERUP, "0 0", _("^F2You now have a superweapon"), "") diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index b25948142..d3ca5508f 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -1171,10 +1171,9 @@ void ClientConnect(entity this) bot_relinkplayerlist(); CS(this).spectatortime = time; - if (blockSpectators) - { + if (!autocvar_sv_spectate) + // no centreprint here: player forced to join, or informed of why they can't via centreprint Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); - } CS(this).jointime = time; @@ -2785,14 +2784,20 @@ void PlayerFrame (entity this) anticheat_prethink(this); // Check if spectating is allowed - if (!autocvar_sv_spectate) blockSpectators = 1; - - if (blockSpectators && IS_REAL_CLIENT(this) - && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this) - && time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime)) + if (!autocvar_sv_spectate && IS_REAL_CLIENT(this) + && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this)) { - if (dropclient_schedule(this)) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); + float cutoff = CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime; + if (time > cutoff + MIN_SPEC_TIME * 0.5) + { + // sv_spectate was disabled recently (or the server was stalled far too long) + CS(this).spectatortime = time; // reset the grace period + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + } + else if (time > cutoff) + if (dropclient_schedule(this)) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); } // Check for nameless players diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh index 890a223a0..d7fb94837 100644 --- a/qcsrc/server/client.qh +++ b/qcsrc/server/client.qh @@ -54,7 +54,7 @@ int autocvar_sv_name_maxlength = 64; string autocvar_sv_quickmenu_file; bool autocvar_sv_servermodelsonly; bool autocvar_sv_showspectators; -int autocvar_sv_spectate; +bool autocvar_sv_spectate; bool autocvar_sv_teamnagger; float autocvar_sv_player_scale; @@ -333,7 +333,6 @@ const int RESPAWN_FORCE = BIT(0); const int RESPAWN_SILENT = BIT(1); const int RESPAWN_DENY = BIT(2); -float blockSpectators; // if set, new or existing spectators or observers will be removed unless they become a player within g_maxplayers_spectator_blocktime seconds .float spectatortime; // point in time since the client is spectating or observing .bool player_blocked; diff --git a/qcsrc/server/clientkill.qc b/qcsrc/server/clientkill.qc index d692d8723..293f42308 100644 --- a/qcsrc/server/clientkill.qc +++ b/qcsrc/server/clientkill.qc @@ -32,7 +32,8 @@ void ClientKill_Now_TeamChange(entity this) } else if (this.killindicator_teamchange == -2) { - if (blockSpectators) + if (!autocvar_sv_spectate) + // shouldn't get here because of condition in ClientCommand_spectate() Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); if (this.wants_join) diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index aaf1c1106..1a45053b6 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -694,8 +694,10 @@ void ClientCommand_spectate(entity caller, int request) if (mutator_returnvalue == MUT_SPECCMD_RETURN) return; if ((IS_PLAYER(caller) || mutator_returnvalue == MUT_SPECCMD_FORCE || caller.wants_join)) - if (autocvar_sv_spectate == 1) + if (autocvar_sv_spectate) ClientKill_TeamChange(caller, -2); // observe + else + Send_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CENTER_SPECTATE_NOTALLOWED); } return; // never fall through to usage } diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 23c0df96f..475f7eb5a 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -1180,13 +1180,8 @@ void GameCommand_nospectators(int request) LOG_HELPF("This command works only when the server is running."); return; } - blockSpectators = 1; - // give every spectator seconds time to become a player - FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_SPEC(it) || IS_OBSERVER(it)) && !INGAME(it), { - CS(it).spectatortime = time; - Send_Notification(NOTIF_ONE_ONLY, it, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); - }); - bprint(strcat("^7All spectators will be automatically kicked when not joining the game after ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds!\n")); + cvar_settemp("sv_spectate", "0"); + // if sv_spectate was enabled PlayerFrame() sends SPECTATE_WARNING notifications return; } @@ -1195,6 +1190,7 @@ void GameCommand_nospectators(int request) { LOG_HELP("Usage:^3 sv_cmd nospectators"); LOG_HELP(" No arguments required."); + LOG_HELP("Temporarily disables the ^3sv_spectate ^7cvar."); return; } } diff --git a/xonotic-server.cfg b/xonotic-server.cfg index f160255de..327abe3f3 100644 --- a/xonotic-server.cfg +++ b/xonotic-server.cfg @@ -27,7 +27,7 @@ alias sv_hook_readyrestart set teamplay_lockonrestart 0 "lock teams once all players readied up and the game restarted (no new players can join after restart unless using the server-command unlockteams)" set g_maxplayers 0 "maximum number of players allowed to play at the same time, 0 means unlimited, -1 uses the map setting or unlimited if not set (rounded to multiple of team number)" -set g_maxplayers_spectator_blocktime 5 "if the players voted for the \"nospectators\" command, this setting defines the number of seconds a observer/spectator has time to join the game before they get kicked" +set g_maxplayers_spectator_blocktime 5 "if sv_spectate is disabled, such as by voting for \"nospectators\", this setting defines the number of seconds an observer/spectator has to join the game before they get kicked" // tournament mod set g_warmup 0 "splits the game into warmup and match stages, 1 means the match starts when g_warmup_majority_factor of players are ready OR g_warmup_limit is hit, >1 also requires at least g_warmup players (including bots) to join, -1 means that minimum player requrement is set by the map (lower bound of 2 or 2 per team)" @@ -95,7 +95,7 @@ set sv_precacheplayermodels 1 "preload all the player models at game start, to a set sv_spectator_speed_multiplier 1.5 "base movement speed factor of spectators, all movement settings scale off this" set sv_spectator_speed_multiplier_min 1 "minimum movement speed factor for spectators as determined by scrolling" set sv_spectator_speed_multiplier_max 5 "maximum movement speed factor for spectators as determined by scrolling" -set sv_spectate 1 "if set to 1, new clients are allowed to spectate or observe the game, if set to 0 joining clients spawn as players immediately (no spectating)" +set sv_spectate 1 "if set to 1 clients are allowed to spectate or observe the game, if set to 0 clients spawn as players immediately or are kicked after g_maxplayers_spectator_blocktime if that's not possible (incompatible with spec-based features such as sv_maxidle_playertospectator and g_balance_teams_queue)" set sv_defaultcharacter 0 "master switch, if set to 1 the further configuration for replacing all player models, skins and colors is taken from the sv_defaultplayermodel, sv_defaultplayerskin and sv_defaultplayercolors variables" set sv_defaultcharacterskin 0 "if set to 1 the further configuration for replacing all skins is taken from the sv_defaultplayerskin variables" set sv_defaultplayermodel "models/player/erebus.iqm" "default model selection, only works if sv_defaultcharacter is set to 1; you may append a : suffix to model names; you can specify multiple, separated by space, and a random one will be chosen" -- 2.39.2