]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
sv_spectate: add less harsh mode "2", improve notifications
authorbones_was_here <bones_was_here@xonotic.au>
Sat, 5 Apr 2025 11:12:10 +0000 (21:12 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Fri, 2 May 2025 09:09:05 +0000 (19:09 +1000)
More friendly than mode "0": direct spectating is disabled during the
match but observing is still allowed so nobody gets kicked.

Updates two existing sv_spectate notifications to use MULTI macros.

notifications.cfg
qcsrc/common/notifications/all.inc
qcsrc/server/client.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/vote.qc
xonotic-server.cfg

index 98db7e9eba4d29fc7e8893ff274c02bf9c516b9e..5cce58ad1af82bd899d9df1d5db39d6f72f9124f 100644 (file)
@@ -283,6 +283,8 @@ seta notification_INFO_ROUND_PLAYER_WIN "1" "\"0\" = off, \"1\" = print to conso
 seta notification_INFO_ROUND_TEAM_WIN "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_ROUND_TIED "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_SCORES "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_SPECTATE_NOTALLOWED "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_SPECTATE_SPEC_NOTALLOWED "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_SPECTATE_WARNING "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_SUPERSPEC_MISSING_UID "2" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_SUPERWEAPON_PICKUP "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@ -552,6 +554,7 @@ seta notification_CENTER_SEQUENCE_COMPLETED "1" "\"0\" = off, \"1\" = centerprin
 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_SPEC_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"
@@ -676,6 +679,9 @@ seta notification_ITEM_WEAPON_PRIMORSEC "1" "enable this multiple notification"
 seta notification_ITEM_WEAPON_UNAVAILABLE "1" "enable this multiple notification"
 seta notification_MULTI_COINTOSS "1" "enable this multiple notification"
 seta notification_MULTI_INSTAGIB_FINDAMMO "1" "enable this multiple notification"
+seta notification_SPECTATE_NOTALLOWED "1" "enable this multiple notification"
+seta notification_SPECTATE_SPEC_NOTALLOWED "1" "enable this multiple notification"
+seta notification_SPECTATE_WARNING "1" "enable this multiple notification"
 seta notification_WEAPON_ACCORDEON_MURDER "1" "enable this multiple notification"
 seta notification_WEAPON_ACCORDEON_SUICIDE "1" "enable this multiple notification"
 seta notification_WEAPON_ARC_MURDER "1" "enable this multiple notification"
index 1e475b1689d96ed55479d991e09afd85bb4870ec..74713c9bf889b30bba6bfb42638189a18df5215d 100644 (file)
@@ -450,6 +450,8 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MULTITEAM_INFO(SCORES,                                  N_CONSOLE,  0, 0, "", "",           "",                     _("^TC^TT ^BGteam scores!"), "", NAME)
 
     MSG_INFO_NOTIF(SPECTATE_WARNING,                        N_CONSOLE,  0, 1, "f1secs", "",     "",                     _("^F2You have to become a player within the next %s, otherwise you will be kicked, because spectating isn't allowed at this time!"), "")
+    MSG_INFO_NOTIF(SPECTATE_NOTALLOWED,                     N_CONSOLE,  0, 0, "", "",           "",                     _("^F2Spectating isn't allowed at this time!"), "")
+    MSG_INFO_NOTIF(SPECTATE_SPEC_NOTALLOWED,                N_CONSOLE,  0, 0, "", "",           "",                     _("^F2Spectating specific players isn't allowed at this time!"), "")
 
     MSG_INFO_NOTIF(SUPERWEAPON_PICKUP,                      N_CONSOLE,  1, 0, "s1", "s1",       "superweapons",         _("^BG%s^K1 picked up a Superweapon"), "")
 
@@ -799,6 +801,7 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
 
     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(SPECTATE_SPEC_NOTALLOWED,          N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^F2Spectating specific players 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"), "")
@@ -948,6 +951,10 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MSG_MULTI_NOTIF(MULTI_COINTOSS,                     N_ENABLE,  NULL,           INFO_COINTOSS,                          CENTER_COINTOSS)
     MSG_MULTI_NOTIF(MULTI_INSTAGIB_FINDAMMO,            N_ENABLE,  ANNCE_NUM_10,   NULL,                                   CENTER_INSTAGIB_FINDAMMO_FIRST)
 
+    MSG_MULTI_NOTIF(SPECTATE_WARNING,                   N_ENABLE,  NULL,           INFO_SPECTATE_WARNING,                  CENTER_SPECTATE_WARNING)
+    MSG_MULTI_NOTIF(SPECTATE_NOTALLOWED,                N_ENABLE,  NULL,           INFO_SPECTATE_NOTALLOWED,               CENTER_SPECTATE_NOTALLOWED)
+    MSG_MULTI_NOTIF(SPECTATE_SPEC_NOTALLOWED,           N_ENABLE,  NULL,           INFO_SPECTATE_SPEC_NOTALLOWED,          CENTER_SPECTATE_SPEC_NOTALLOWED)
+
     MSG_MULTI_NOTIF(WEAPON_ACCORDEON_MURDER,            N_ENABLE,  NULL,           INFO_WEAPON_ACCORDEON_MURDER,           NULL)
     MSG_MULTI_NOTIF(WEAPON_ACCORDEON_SUICIDE,           N_ENABLE,  NULL,           INFO_WEAPON_ACCORDEON_SUICIDE,          CENTER_DEATH_SELF_GENERIC)
     MSG_MULTI_NOTIF(WEAPON_ARC_MURDER,                  N_ENABLE,  NULL,           INFO_WEAPON_ARC_MURDER,                 NULL)
index 5367d48fe4ad044d95a937d3319333ffd9323df0..8e2408736f43228b67fe385415a9f28b4918b3b7 100644 (file)
@@ -2493,7 +2493,9 @@ void ObserverOrSpectatorThink(entity this)
                || (!is_spec && ((PHYS_INPUT_BUTTON_ATCK(this) && !CS(this).version_mismatch) || this.would_spectate)))
                {
                        this.flags &= ~FL_JUMPRELEASED;
-                       if(SpectateNext(this))
+                       if (autocvar_sv_spectate == 2 && !warmup_stage && !this.vote_master)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_SPECTATE_SPEC_NOTALLOWED);
+                       else if(SpectateNext(this))
                                TRANSMUTE(Spectator, this);
                        else if (is_spec)
                        {
@@ -2825,6 +2827,7 @@ void PlayerFrame (entity this)
        anticheat_prethink(this);
 
        // Check if spectating is allowed
+       // cvar hook/callback TODO: make this event-driven
        if (!autocvar_sv_spectate && IS_REAL_CLIENT(this)
        && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this))
        {
@@ -2833,8 +2836,7 @@ void PlayerFrame (entity this)
                {
                        // 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);
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
                }
                else if (time > cutoff)
                        if (dropclient_schedule(this))
index 5c22952edccb5bf5c2836bf78ee242e06ce36840..d2f8c4055733162689b7efaef2f34f6827f2b716 100644 (file)
@@ -762,6 +762,8 @@ void ClientCommand_spectate(entity caller, int request)
                                                        PutClientInServer(caller);
                                                }
                                        }
+                                       else if (autocvar_sv_spectate == 2 && !warmup_stage && !caller.vote_master)
+                                               Send_Notification(NOTIF_ONE_ONLY, caller, MSG_INFO, INFO_SPECTATE_SPEC_NOTALLOWED);
                                        else
                                        {
                                                entity client = GetFilteredEntity(argv(1));
@@ -786,7 +788,7 @@ void ClientCommand_spectate(entity caller, int request)
                                if (autocvar_sv_spectate)
                                        ClientKill_TeamChange(caller, -2); // observe
                                else
-                                       Send_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CENTER_SPECTATE_NOTALLOWED);
+                                       Send_Notification(NOTIF_ONE_ONLY, caller, MSG_MULTI, SPECTATE_NOTALLOWED);
                        }
                        return; // never fall through to usage
                }
index e2df71b7ffa1eb175104eff3518e1b7422b44e9b..a1695175568908834302fde8272b33050e835fbc 100644 (file)
@@ -489,6 +489,21 @@ void ReadyRestart_force(bool is_fake_round_start)
                FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
        }
 
+       if (autocvar_sv_spectate == 2 && !warmup_stage)
+       {
+               FOREACH_CLIENT(IS_SPEC(it),
+               {
+                       if (it.vote_master)
+                               ClientData_Touch(it.enemy);
+                       else
+                       {
+                               Send_Notification(NOTIF_ONE_ONLY, it, MSG_MULTI, SPECTATE_SPEC_NOTALLOWED);
+                               TRANSMUTE(Observer, it);
+                               PutClientInServer(it);
+                       }
+               });
+       }
+
        if (!sv_ready_restart_after_countdown || warmup_stage)
                reset_map(is_fake_round_start);
 
index 38976ea130ca231e34da1c3e85ecabb054fb42f4..aa22a9cad80bb9a61d3e8e97096cde460b2b14f0 100644 (file)
@@ -97,7 +97,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 "\"1\" = clients are allowed to spectate or observe the game, \"0\" = clients spawn as players immediately or are kicked after g_maxplayers_spectator_blocktime if that's not possible (NOTE: incompatible with spec-based features such as sv_maxidle_playertospectator and g_balance_teams_queue)"
+set sv_spectate 1 "\"1\" = clients are allowed to spectate or observe the game, \"2\" = clients are restricted to observing only during the match (spectating is allowed during warmup), \"0\" = clients spawn as players immediately or are kicked after g_maxplayers_spectator_blocktime if that's not possible (NOTE: \"0\" is incompatible with features based on spectating or observing, such as sv_maxidle_playertospectator and g_balance_teams_queue)"
 set sv_defaultcharacter 0 "master switch, if \"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 \"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 \"1\"; you may append a :<skinnumber> suffix to model names; you can specify multiple, separated by space, and a random one will be chosen"