Анти АФК

Тема в разделе "Общего назначения", создана пользователем Flame, 18 ноя 2016.

  1. Flame

    Flame -Заслуженый кодер форума-

    На днях глядел я серверный вариант мутатора кикающего игроков, если они находятся в состоянии "away from keyboard" - проще говоря AFK. А буквально только что пришло мне в голову, что клиентский вариант данного мутатора будет на порядок изящнее и короче.

    Ниже я приведу этот мутатор, а позже может выложу ещё и серверный вариант для сравнения. В комментариях к коду можно прочесть все нюансы мутатора.

    Настройки:
    bTryToMoveToSpectator - будем ли мы пытаться отправить игрока в зрители или сразу выкидываем из игры
    bAdminIsImmune - действует ли мутатор на админов
    bIgnoreTrader - действует ли мутатор во время трейдера
    AFKTime - через сколько секунд начинаем карательные действия против игрока
    InformTime - за сколько секунд до карательных действий выводим обратный отчёт игроку и звуки обратного отсчёта

    Код:
    Код:
    class AntiAFKMut extends Mutator config(AntiAFKMut);
    
    #exec OBJ LOAD FILE=Inf_Weapons_Foley.uax
    
    var config bool bTryToMoveToSpectator, bAdminIsImmune, bIgnoreTrader;
    var config int AFKTime, InformTime;
    
    var Vector oldLocation;
    var Rotator oldRotation;
    var int Counter;
    var bool bAlreadyTried;
    
    //Настройки лежат на сервере, а используются на клиенте
    replication
    {
        reliable if(bNetDirty && Role == Role_Authority)
            AFKTime,InformTime,bTryToMoveToSpectator,bAdminIsImmune,bIgnoreTrader;
    }
    
    //Таймер будет работать только на клиенте
    simulated function PostBeginPlay()
    {
        if(Role<Role_Authority)
            SetTimer(1.0,true);
    }
    
    simulated function Timer()
    {
        local PlayerController PC;
        PC=Level.GetLocalPlayerController();
    
        //Если контроллер none, то незачем идти дальше
        if(PC==none)
            return;
    
        //Если сейчас трейдер и переменная bIgnoreTrader=false, то счётчик AFK обнуляем и дальше не идём
        if    (
                !bIgnoreTrader
                &&    KFGameReplicationInfo(PC.GameReplicationInfo)!=none
                &&    !KFGameReplicationInfo(PC.GameReplicationInfo).bWaveInProgress
            )
        {
            if(Counter>0)
                Counter=0;
            return;
        }
    
        //Нету тела (мёртв или зритель или этот контроллер - не игрок) - нет смысла идти дальше
        if(PC.Pawn==none)
            return;
    
        //Если в настройках задан иммунитет админов, то их не трогаем и выходим из таймера
        if(PC.PlayerReplicationInfo.bAdmin && bAdminIsImmune)
            return;
    
        //Если в прошлый раз (секунду назад) было то же самое положение и поворот тела - увеличиваем счётчик на 1, иначе сбрасываем на 0
        if(oldLocation==PC.Pawn.Location && oldRotation==PC.Pawn.Rotation)
            Counter++;
        else
            Counter=0;
    
        //Информирование игрока о том, что скоро им займутся
        if(AFKTime-Counter<InformTime && Counter>0)
        {
            PC.ClientMessage("ÿAntiAFK Timer:"@Max(AFKTime-Counter,0));
            PC.PlayOwnedSound(Sound'Inf_Weapons_Foley.dryfire_rifle');
        }
    
        //Если прошло AFKTime секунд и игрок не пошевелился - начинаем над ним расправу
        if(Counter>=AFKTime)
        {
            //Вначале пытаемся запихнуть его в зрители (если в настройках bTryToMoveToSpectator==true)
            if(bTryToMoveToSpectator && !bAlreadyTried)
            {
                PC.BecomeSpectator();
                bAlreadyTried=true;
            }
            //Ну если не получилось сделать зрителем, то игрок как бы сам набирает команду disconnect :)
            else
                ConsoleCommand("disconnect");
        }
    
        //Обновляем "старые" значения положения и поворота
        oldLocation=PC.Pawn.Location;
        oldRotation=PC.Pawn.Rotation;
    }
    
    defaultproperties
    {
        bAddToServerPackages=true
        bAlwaysRelevant=true
        RemoteRole=ROLE_SimulatedProxy
        GroupName="KF-AntiAFK"
        FriendlyName="AntiAFKMut"
        Description="AntiAFKMut"
    }
    

    Ссылка 1 или Ссылка 2
    AntiAFKMut.AntiAFKMut

    Upd. Кстати, вот серверный вариант мутатора. Там правда вроде с косяками версия - поизучаю попозже и поправлю при необходимости
     
    Последнее редактирование: 18 ноя 2016
    LLIePLLIeHb, RaideN-, HATAXA и ещё 1-му нравится это.
  2. Essence

    Essence Moderator Команда форума

    Серверный вариант мутатора.

    Настройки:
    bTryToMoveToSpectator - будем ли мы пытаться отправить игрока в зрители или сразу кикаем из игры
    bAdminIsImmune - действует ли мутатор на админов
    bIgnoreTrader - действует ли мутатор во время трейдера
    bCheckSpectators - действует ли мутатор на зрителей
    AFKTime - через сколько секунд начинаем карательные действия против игрока
    InformTime - за сколько секунд до карательных действий выводим обратный отчёт игроку
    KickMessagePlayer - сообщение для нарушителя
    KickMessage - сообщение для остальных игроков
    InformMessage - сообщение в виде предупреждения о том, что скоро он будет наказан

    Код с комментариями:
    Код:
    class AntiAFKMut extends Mutator Config(AntiAFKMut);
    
    var config array<string> ExceptionList;
    var config bool bTryToMoveToSpectator, bAdminIsImmune, bIgnoreTrader, bCheckSpectators;
    var config string KickMessage, KickMessagePlayer;
    var config string InformMessage;
    var config int AFKTime, InformTime;
    
    struct PlayerRecord
    {
        var PlayerController PC;
        var Vector Location;
        var Rotator Rotation;
        var int Counter;
    };
    var array<PlayerRecord> PendingPlayers;
    
    // Отлавливаем игроков и заносим их в массив
    function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
    {
        // Проверка MessagingSpectator(Other)==None здесь для того, чтобы исключить Web Admin'a
        if(PlayerController(Other)!=None && MessagingSpectator(Other)==None)
        {
            PendingPlayers.Insert(0,1);
            PendingPlayers[0].PC=PlayerController(Other);
        }
        Return True;
    }
    
    // Мутатор не заработает, пока не начнётся игра
    // В случае афк до начала игры существует ForceGameStartMut
    // function PostBeginPlay()
    // Игра началась, запускаем таймер
    function MatchStarting()
    {
        SetTimer(1.0, True);
    }
    
    function Timer()
    {
        local int i;
        // Обработка игроков
        for(i=PendingPlayers.Length-1; i>=0; i--)
        {
            // Если этот контроллер None (кикнули игрока или он вышел сам), то незачем идти дальше
            // Удаляем игрока из массива и продолжаем обработку игроков
            if(PendingPlayers[i].PC==None || PendingPlayers[i].PC.PlayerReplicationInfo==None)
            {
                PendingPlayers.Remove(i,1);
                Continue;
            }
            // Если сейчас трейдер и переменная bIgnoreTrader=False, то обнуляем счётчик AFK и дальше не идём
            if(!bIgnoreTrader && KFGameType(Level.Game)!=None && !KFGameType(Level.Game).bWaveInProgress)
            {
                if(PendingPlayers[i].Counter>0) PendingPlayers[i].Counter=0;
                Continue;
            }
            // Нет тела
            if(PendingPlayers[i].PC.Pawn==None)
            {
                // Если в настройках bCheckSpectators=True, то зритель тоже будет наказан за AFK
                if(bCheckSpectators)
                {
                    // Мёртв, но не зритель - нет смысла идти дальше, продолжаем обработку игроков
                    if(!PendingPlayers[i].PC.PlayerReplicationInfo.bOnlySpectator)
                        Continue;
                }
                // Мёртв или зритель - нет смысла идти дальше, продолжаем обработку игроков
                else Continue;
            }
            // Если игрок в списке исключений или в настройках задан иммунитет для админов, то не трогаем его и продолжаем обработку игроков
            if(InExceptionList(PendingPlayers[i].PC))
                Continue;
            //Если в прошлый раз (секунду назад) было то же самое положение и поворот тела - увеличиваем счётчик на 1, иначе сбрасываем на 0
            if(PendingPlayers[i].Location==PendingPlayers[i].PC.Location && PendingPlayers[i].Rotation==PendingPlayers[i].PC.Rotation)
                PendingPlayers[i].Counter++;
            else
                PendingPlayers[i].Counter=0;
            // Информирование игрока о том, что скоро им займутся
            if(AFKTime-PendingPlayers[i].Counter<=InformTime && PendingPlayers[i].Counter>0)
                PendingPlayers[i].PC.ClientMessage(Repl(InformMessage,"%time%",Max(AFKTime-PendingPlayers[i].Counter,0)));
            // Если прошло AFKTime секунд и игрок не пошевелился - начинаем над ним расправу
            if(PendingPlayers[i].Counter>=AFKTime)
            {
                // Если игрок зритель - кикаем его сразу
                if(PendingPlayers[i].PC.PlayerReplicationInfo.bOnlySpectator)
                    KickAFKPlayer(PendingPlayers[i].PC, KickMessagePlayer);
                // Если же нет, то пытаемся запихнуть его в зрители (если в настройках bTryToMoveToSpectator=True)
                else if(bTryToMoveToSpectator)
                {
                    PendingPlayers[i].PC.BecomeSpectator();
                    // Если получилось сделать игрока зрителем, то обнуляем счётчик
                    // Ибо если bCheckSpectators=True, то зритель будет кикнут сразу
                    // Если игрок и в качестве зрителя продолжит оставаться в состоянии AFK
                    // То кикнем его по истечении AFKTime
                    if(PendingPlayers[i].PC.PlayerReplicationInfo.bOnlySpectator)
                        PendingPlayers[i].Counter=0;
                    // Если же нет, то кикаем его
                    else KickAFKPlayer(PendingPlayers[i].PC, KickMessagePlayer);
                }
                // Иначе кикаем игрока сразу
                else KickAFKPlayer(PendingPlayers[i].PC, KickMessagePlayer);
                Continue;
            }
            // Обновляем "старые" значения положения и поворота
            PendingPlayers[i].Location=PendingPlayers[i].PC.Location;
            PendingPlayers[i].Rotation=PendingPlayers[i].PC.Rotation;
        }
    }
    
    // Кикаем и информируем
    function KickAFKPlayer(PlayerController PC, string Message)
    {
        local string PlayerName;
        if(NetConnection(PC.Player)!=None)
        {
            PlayerName=PC.PlayerReplicationInfo.PlayerName;
            PC.ClientNetworkMessage("AC_Kicked", Message);
            if(PC.Pawn!=None) PC.Pawn.Destroy();
            PC.Destroy();
            if(PlayerName!="") Level.Game.Broadcast(Self, Repl(KickMessage,"%Player%",PlayerName), 'AntiAFK');
        }
    }
    
    // Проверяем, включен ли игрок в список исключений
    function bool InExceptionList(PlayerController PC)
    {
        local string Hash;
        local int i;
        if(bAdminIsImmune && PC.PlayerReplicationInfo.bAdmin) Return True;
        Hash=PC.GetPlayerIDHash();
        for(i=0; i<ExceptionList.Length; i++) if(ExceptionList[i]~=Hash) Return True;
        Return False;
    }
    
    defaultproperties
    {
        GroupName="KF-AntiAFK"
        FriendlyName="AntiAFKMut"
        Description="AntiAFKMut"
    }

    Скачать:
    Ссылка
     
    RaideN-, Flame, Mashina и ещё 1-му нравится это.