]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/client.qc
Fix kicked player not receiving the kick message (if sent with Send_Notification)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / client.qc
index 6bebfeb3666d33591dfe7c5c865344dba112979b..7d3c1faca69520d5faa995bcfa443259d4ba668a 100644 (file)
@@ -199,19 +199,19 @@ string CheckPlayerModel(string plyermodel) {
                FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
        }
        // only in right path
-       if( substring(plyermodel,0,14) != "models/player/")
+       if(substring(plyermodel, 0, 14) != "models/player/")
                return FallbackPlayerModel;
        // only good file extensions
-       if(substring(plyermodel,-4,4) != ".zym")
-       if(substring(plyermodel,-4,4) != ".dpm")
-       if(substring(plyermodel,-4,4) != ".iqm")
-       if(substring(plyermodel,-4,4) != ".md3")
-       if(substring(plyermodel,-4,4) != ".psk")
+       if(substring(plyermodel, -4, 4) != ".iqm"
+               && substring(plyermodel, -4, 4) != ".zym"
+               && substring(plyermodel, -4, 4) != ".dpm"
+               && substring(plyermodel, -4, 4) != ".md3"
+               && substring(plyermodel, -4, 4) != ".psk")
+       {
                return FallbackPlayerModel;
+       }
        // forbid the LOD models
-       if(substring(plyermodel, -9,5) == "_lod1")
-               return FallbackPlayerModel;
-       if(substring(plyermodel, -9,5) == "_lod2")
+       if(substring(plyermodel, -9, 5) == "_lod1" || substring(plyermodel, -9, 5) == "_lod2")
                return FallbackPlayerModel;
        if(plyermodel != strtolower(plyermodel))
                return FallbackPlayerModel;
@@ -253,6 +253,7 @@ void PutObserverInServer(entity this)
                        if (vote_called) { VoteCount(false); }
                        ReadyCount();
                }
+               entcs_update_players(this);
        }
 
        entity spot = SelectSpawnPoint(this, true);
@@ -394,6 +395,9 @@ void PutObserverInServer(entity this)
                SetPlayerTeam(this, -1, TEAM_CHANGE_SPECTATOR);
                this.frags = FRAGS_SPECTATOR;
        }
+
+       bot_relinkplayerlist();
+
        if (CS(this).just_joined)
                CS(this).just_joined = false;
 }
@@ -771,14 +775,15 @@ void PutPlayerInServer(entity this)
        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
                .entity weaponentity = weaponentities[slot];
+               entity w_ent = this.(weaponentity);
                if(slot == 0 || autocvar_g_weaponswitch_debug == 1)
-                       this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity);
+                       w_ent.m_switchweapon = w_getbestweapon(this, weaponentity);
                else
-                       this.(weaponentity).m_switchweapon = WEP_Null;
-               this.(weaponentity).m_weapon = WEP_Null;
-               this.(weaponentity).weaponname = "";
-               this.(weaponentity).m_switchingweapon = WEP_Null;
-               this.(weaponentity).cnt = -1;
+                       w_ent.m_switchweapon = WEP_Null;
+               w_ent.m_weapon = WEP_Null;
+               w_ent.weaponname = "";
+               w_ent.m_switchingweapon = WEP_Null;
+               w_ent.cnt = -1;
        }
 
        MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
@@ -824,6 +829,8 @@ void PutClientInServer(entity this)
        } else if (IS_PLAYER(this)) {
                PutPlayerInServer(this);
        }
+
+       bot_relinkplayerlist();
 }
 
 // TODO do we need all these fields, or should we stop autodetecting runtime
@@ -1200,6 +1207,8 @@ Called when a client disconnects from the server
 =============
 */
 .entity chatbubbleentity;
+void player_powerups_remove_all(entity this);
+
 void ClientDisconnect(entity this)
 {
        assert(IS_CLIENT(this), return);
@@ -1252,6 +1261,8 @@ void ClientDisconnect(entity this)
        ReadyCount();
        if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
 
+       player_powerups_remove_all(this); // stop powerup sound
+
        ONREMOVE(this);
 }
 
@@ -1444,6 +1455,20 @@ void play_countdown(entity this, float finished, Sound samp)
                                sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
 }
 
+void player_powerups_remove_all(entity this)
+{
+       if (this.items & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON))
+       {
+               // don't play the poweroff sound when the game restarts or the player disconnects
+               if (time > game_starttime + 1 && IS_CLIENT(this))
+                       sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
+               stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+               this.items &= ~ITEM_Strength.m_itemid;
+               this.items &= ~ITEM_Shield.m_itemid;
+               this.items -= (this.items & IT_SUPERWEAPON);
+       }
+}
+
 void player_powerups(entity this)
 {
        if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !game_stopped)
@@ -1454,16 +1479,7 @@ void player_powerups(entity this)
        this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
 
        if (IS_DEAD(this))
-       {
-               if (this.items & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON))
-               {
-                       sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
-                       stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
-                       this.items &= ~ITEM_Strength.m_itemid;
-                       this.items &= ~ITEM_Shield.m_itemid;
-                       this.items -= (this.items & IT_SUPERWEAPON);
-               }
-       }
+               player_powerups_remove_all(this);
 
        if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
                return;
@@ -1703,6 +1719,14 @@ void SetZoomState(entity this, float newzoom)
 void GetPressedKeys(entity this)
 {
        MUTATOR_CALLHOOK(GetPressedKeys, this);
+       if (game_stopped)
+       {
+               CS(this).pressedkeys = 0;
+               STAT(PRESSED_KEYS, this) = 0;
+               return;
+       }
+
+       // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
        int keys = STAT(PRESSED_KEYS, this);
        keys = BITSET(keys, KEY_FORWARD,        CS(this).movement.x > 0);
        keys = BITSET(keys, KEY_BACKWARD,       CS(this).movement.x < 0);
@@ -2064,23 +2088,6 @@ int nJoinAllowed(entity this, entity ignore)
        return free_slots;
 }
 
-/**
- * Checks whether the client is an observer or spectator, if so, he will get kicked after
- * g_maxplayers_spectator_blocktime seconds
- */
-void checkSpectatorBlock(entity this)
-{
-       if(IS_SPEC(this) || IS_OBSERVER(this))
-       if(!this.caplayer)
-       if(IS_REAL_CLIENT(this))
-       {
-               if( time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
-                       dropclient(this);
-               }
-       }
-}
-
 void PrintWelcomeMessage(entity this)
 {
        if(CS(this).motd_actived_time == 0)
@@ -2298,6 +2305,7 @@ bool PlayerThink(entity this)
 }
 
 .bool would_spectate;
+// merged SpectatorThink and ObserverThink (old names are here so you can grep for them)
 void ObserverOrSpectatorThink(entity this)
 {
        bool is_spec = IS_SPEC(this);
@@ -2328,6 +2336,8 @@ void ObserverOrSpectatorThink(entity this)
                                TRANSMUTE(Observer, this);
                                PutClientInServer(this);
                        }
+                       else
+                               this.would_spectate = false; // unable to spectate anyone
                        if (is_spec)
                                CS(this).impulse = 0;
                } else if (is_spec) {
@@ -2351,10 +2361,13 @@ void ObserverOrSpectatorThink(entity this)
                        }
                }
                else {
-                       int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? CS_CVAR(this).cvar_cl_clippedspectating : !CS_CVAR(this).cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+                       bool wouldclip = CS_CVAR(this).cvar_cl_clippedspectating;
+                       if (PHYS_INPUT_BUTTON_USE(this))
+                               wouldclip = !wouldclip;
+                       int preferred_movetype = (wouldclip ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
                        set_movetype(this, preferred_movetype);
                }
-       } else {
+       } else { // jump pressed
                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;
@@ -2438,12 +2451,16 @@ void PlayerPreThink (entity this)
        if (frametime) {
                // physics frames: update anticheat stuff
                anticheat_prethink(this);
-       }
 
-       if (blockSpectators && frametime) {
                // WORKAROUND: only use dropclient in server frames (frametime set).
                // Never use it in cl_movement frames (frametime zero).
-               checkSpectatorBlock(this);
+               if (blockSpectators && IS_REAL_CLIENT(this)
+                       && (IS_SPEC(this) || IS_OBSERVER(this)) && !this.caplayer
+                       && time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime))
+               {
+                       if (dropclient_schedule(this))
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
+               }
        }
 
        zoomstate_set = false;
@@ -2497,7 +2514,7 @@ void PlayerPreThink (entity this)
                this.max_armorvalue = 0;
        }
 
-       if (frametime && IS_PLAYER(this))
+       if (frametime && IS_PLAYER(this) && time >= game_starttime)
        {
                if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
                {
@@ -2620,15 +2637,6 @@ void PlayerPreThink (entity this)
        }
 
        target_voicescript_next(this);
-
-       // WEAPONTODO: Move into weaponsystem somehow
-       // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
-       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-       {
-               .entity weaponentity = weaponentities[slot];
-               if(this.(weaponentity).m_weapon == WEP_Null)
-                       this.(weaponentity).clip_load = this.(weaponentity).clip_size = 0;
-       }
 }
 
 void DrownPlayer(entity this)
@@ -2689,13 +2697,14 @@ void PlayerPostThink (entity this)
 {
        Player_Physics(this);
 
-       if (autocvar_sv_maxidle > 0)
+       if (autocvar_sv_maxidle > 0 || (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0))
        if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
        if (IS_REAL_CLIENT(this))
-       if (IS_PLAYER(this) || autocvar_sv_maxidle_spectatorsareidle)
+       if (IS_PLAYER(this) || autocvar_sv_maxidle_alsokickspectators)
+       if (!intermission_running) // NextLevel() kills all centerprints after setting this true
        {
                int totalClients = 0;
-               if(autocvar_sv_maxidle_slots > 0)
+               if(autocvar_sv_maxidle > 0 && autocvar_sv_maxidle_slots > 0)
                {
                        FOREACH_CLIENT(IS_REAL_CLIENT(it) || autocvar_sv_maxidle_slots_countbots,
                        {
@@ -2703,7 +2712,7 @@ void PlayerPostThink (entity this)
                        });
                }
 
-               if (autocvar_sv_maxidle_slots > 0 && (maxclients - totalClients) > autocvar_sv_maxidle_slots)
+               if (autocvar_sv_maxidle > 0 && autocvar_sv_maxidle_slots > 0 && (maxclients - totalClients) > autocvar_sv_maxidle_slots)
                { /* do nothing */ }
                else if (time - CS(this).parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
                {
@@ -2715,19 +2724,36 @@ void PlayerPostThink (entity this)
                }
                else
                {
-                       float timeleft = ceil(autocvar_sv_maxidle - (time - CS(this).parm_idlesince));
-                       if (timeleft == min(10, autocvar_sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10
-                               if (!CS(this).idlekick_lasttimeleft)
+                       float maxidle_time = autocvar_sv_maxidle;
+                       if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
+                               maxidle_time = autocvar_sv_maxidle_playertospectator;
+                       float timeleft = ceil(maxidle_time - (time - CS(this).parm_idlesince));
+                       float countdown_time = max(min(10, maxidle_time - 1), ceil(maxidle_time * 0.33)); // - 1 to support maxidle_time <= 10
+                       if (timeleft == countdown_time && !CS(this).idlekick_lasttimeleft)
+                       {
+                               if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
+                                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOVETOSPEC_IDLING, timeleft);
+                               else
                                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
                        }
                        if (timeleft <= 0) {
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname);
-                               dropclient(this);
+                               if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
+                               {
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING, this.netname, maxidle_time);
+                                       if (this.caplayer)
+                                               this.caplayer = 0;
+                                       PutObserverInServer(this);
+                               }
+                               else
+                               {
+                                       if (dropclient_schedule(this))
+                                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname, maxidle_time);
+                               }
                                return;
                        }
-                       else if (timeleft <= 10) {
+                       else if (timeleft <= countdown_time) {
                                if (timeleft != CS(this).idlekick_lasttimeleft)
-                                       Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft));
+                                       play2(this, SND(TALK2));
                                CS(this).idlekick_lasttimeleft = timeleft;
                        }
                }
@@ -2756,12 +2782,12 @@ void PlayerPostThink (entity this)
                DrownPlayer(this);
                UpdateChatBubble(this);
                if (CS(this).impulse) ImpulseCommands(this);
+               GetPressedKeys(this);
                if (game_stopped)
                {
                        CSQCMODEL_AUTOUPDATE(this);
                        return;
                }
-               GetPressedKeys(this);
        }
        else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
        {