Запрет на поднятие чужих пушек

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

Метки:
  1. Flame

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

    Вечно всплывают просьбы об этом мутаторе
    В принципе на форуме есть практически рабочая версия

    Ну и я выложу пару версий:
    1. Полностью автономная без всяких настроек. Просто всем всё запрещено и всё)
    2. Описание что поправить в ServerPerks, чтобы была такая вот менюшка и можно было запрещать и разрешать

    Хотел ещё выложить полностью автономный вариант с запретом и разрешением с помощью Mutate и использованием LinkedReplicationInfo, но что-то лень)
    При этом я сделал вариант, когда не важно умер игрок или вылетел из игры - его пушки останутся его.
    Возможно вам захочется вариант, когда отсутствие игрока на сервере автоматически позволяет брать его пушки. Ну пните меня тогда, а лучше сами поправьте себе код)
    Достаточно закомментировать
    Код:
        if(PC==None)
            PC=KFWeaponPickup(WP).DroppedBy;
    
    Итак,
    1. Всем и всегда запрещено поднимать чужие пушки

    Код:
    Код:
    class OwnWeaponsMut extends Mutator;
    
    var array<WeaponPickup> PendingPickups;
    var OwnWeaponsRules Rules;
    
    function PostBeginPlay()
    {
        if(Rules==None)
            Rules=Spawn(Class'OwnWeaponsRules');
    }
    
    //Отлавливаем новые WeaponPickup, даём 0.1 секунду на инициализацию необходимых нам переменных и зовём Rules.AddPickup в таймере
    //Если WeaponPickup являются теми, что изначально валяются на карте - они не будут добавлены в массив. У них нет владельца
    function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
    {
        if(WeaponPickup(Other)!=None)
        {
            PendingPickups[PendingPickups.Length] = WeaponPickup(Other);
            SetTimer(0.1,false);
        }
        return true;
    }
    function Timer()
    {
        local int i;
        for(i=0;i<PendingPickups.Length;i++)
        {
            if(PendingPickups[i]!=None)
                Rules.AddPickup(PendingPickups[i]);
        }
        PendingPickups.Length = 0;
    }
    
    defaultproperties
    {
        GroupName="KF-OwnWeapons"
        FriendlyName="OwnWeaponsMut"
        Description="OwnWeaponsMut"
    }
    
    Код:
    class OwnWeaponsRules extends GameRules;
    
    struct PickupInfo
    {
        var string ID;
        var string PlayerName;
        var WeaponPickup P;
    };
    var array<PickupInfo> OwnedPickups;
    //Нельзя на кириллице писать в коде. Только в defaultproperties. Поэтому если хотим написать что по-русски делаем переменную.
    var string rusMessage;
    
    //Стандартные функции GameRules
    function PostBeginPlay()
    {
        if(Level.Game.GameRulesModifiers == none)
            Level.Game.GameRulesModifiers = self;
        else
            Level.Game.GameRulesModifiers.AddGameRules(self);
    }
    function AddGameRules(GameRules GR)
    {
        if(GR != self)
            super.AddGameRules(GR);
    }
    //
    
    //Flame. Здесь мы отслеживаем смерть игрока и ставим свойство bIsTier2Weapon=false его текущей пушке
    //Тут некоторый финт получается. Если bIsTier2Weapon=false, то у KFWeaponPickup класса заполняется свойство
    //DroppedBy при выкидывании - функция KFWeaponPickup.InitDroppedPickupFor.
    //А это поможет нам получить контроллер владельца после смерти владельца. Pawn.Controller то уже None и обычный способ не катит.
    //В принципе можно было бы всем пушкам сразу ставить это свойство (в CheckReplacement, например)
    //Оно относится к стим ачивкам и в общем то нам безразлично. Всё равно мутатор не белый и никаких стим ачивок небось не ожидается
    //Тогда можно было бы работать в едином стиле с KFWeaponPickup объектами и отслеживать только DroppedBy
    //Ну почему-то хочется мне пока сделать так как сделано. Вроде как с минимальными вмешательствами
    function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> damageType, vector HitLocation)
    {
        if    (
                Killed.IsA('KFHumanPawn')
                &&    Killed.Controller!=none
                &&    Killed.Controller.IsA('PlayerController')
            )
        {
            KFWeapon(Killed.Weapon).bIsTier2Weapon=false;
        }
        if(NextGameRules!=None)
            return NextGameRules.PreventDeath(Killed,Killer, damageType,HitLocation);
        return false;
    }
    
    //Flame. В общем то базовая функция на которой держится мутатор. Если out переменная bAllowPickup=0 и
    //функция возвращается true - значит мы переопределяем процесс поднятия пушки и запрещаем поднятие.
    //Если функция возвращает false - нам пофиг на значение переменной bAllowPickup, функция как будто бы и не вызывалась
    //То есть мы проверяем что поднимаем пушку, проверяем можем ли мы её поднимать. Если да, то false и функции как будто бы и не было.
    //Если true, то глядим на bAllowPickup. Оно тут 0, значит запрещаем поднимать.
    function bool OverridePickupQuery(Pawn Other, Pickup Item, out byte bAllowPickup)
    {
        if(WeaponPickup(Item)!=None && Other.Controller!=None)
        {
            bAllowPickup = 0;
            if(CanTakeItem(Other.Controller,Item))
            {
                if(NextGameRules!=None)
                    return NextGameRules.OverridePickupQuery(Other, Item, bAllowPickup);
                return false;
            }
            return true;
        }
        if(NextGameRules!=None)
            return NextGameRules.OverridePickupQuery(Other, Item, bAllowPickup);
        return false;
    }
    //Flame. Функция проверяет принадлежит ли кому-то пушка и если да, то принадлежит ли она нам
    function bool CanTakeItem(Controller C, Pickup WP)
    {
        local int i;
        local PlayerController PC;
        PC=PlayerController(C);
        if(PC==none)
            return false;
        for(i=0;i<OwnedPickups.Length;i++)
        {
            if(OwnedPickups[i].P==WP)
            {
                if(OwnedPickups[i].ID==PC.GetPlayerIDHash())
                    return true;
                PC.ClientMessage("This weapon is owned by /"@rusMessage@OwnedPickups[i].PlayerName);
                return false;
            }
        }
        return true;
    }
    
    //Flame. Функция вызывается, когда на карте появляется новый WeaponPickup объект. Если у неё есть владелец - она заносится в массив.
    //WeaponPickup добавляется в массив либо если Instigator.Controller пушки не None (при обычном выкидывании)
    //Либо если DroppedBy не None, при посмертном выкидывании пушки
    //По-хорошему надо бы чистить этот массив. В том же таймере пробегать по всем WeaponPickup и глядеть каких уже нет в OwnedPickups
    //Но у меня не было целью сделать оптимизированный код, акцент был на простоте и лаконичности). Так что сами можете это поправить.
    function AddPickup(WeaponPickup WP)
    {
        local PlayerController PC;
        local PickupInfo pi;
        if(WP==none || WP.Instigator==none)
            return;
        PC=PlayerController(WP.Instigator.Controller);
        //Отслеживаем пушку выпавшую из рук трупа
        if(PC==None)
            PC=KFWeaponPickup(WP).DroppedBy;
        //
        if(PC==None)
            return;
        pi.P=WP;
        pi.ID=PC.GetPlayerIDHash();;
        pi.PlayerName=PC.PlayerReplicationInfo.PlayerName;
        OwnedPickups[OwnedPickups.Length] = pi;
    }
    
    defaultproperties
    {
        rusMessage="Владельцем является"
    }
    

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

    2. Версия с меню для SP. Используется сохранение настроек на клиенте.

    Код:
    Код:
    class OwnWeaponsMut extends Mutator;
    
    var array<WeaponPickup> PendingPickups;
    var OwnWeaponsRules Rules;
    
    function PostBeginPlay()
    {
        if(Rules==None)
            Rules=Spawn(Class'OwnWeaponsRules');
    }
    
    //Отлавливаем новые WeaponPickup, даём 0.1 секунду на инициализацию необходимых нам переменных и зовём Rules.AddPickup в таймере
    //Если WeaponPickup являются теми, что изначально валяются на карте - они не будут добавлены в массив. У них нет владельца
    function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
    {
        if(WeaponPickup(Other)!=None)
        {
            PendingPickups[PendingPickups.Length] = WeaponPickup(Other);
            SetTimer(0.1,false);
        }
        return true;
    }
    function Timer()
    {
        local int i;
        for(i=0;i<PendingPickups.Length;i++)
        {
            if(PendingPickups[i]!=None)
                Rules.AddPickup(PendingPickups[i]);
        }
        PendingPickups.Length = 0;
    }
    
    defaultproperties
    {
        GroupName="KF-OwnWeapons"
        FriendlyName="OwnWeaponsMut"
        Description="OwnWeaponsMut"
    }
    
    Код:
    class OwnWeaponsRules extends GameRules;
    
    struct PickupInfo
    {
        var string ID;
        var string PlayerName;
        var WeaponPickup P;
    };
    var array<PickupInfo> OwnedPickups;
    //Нельзя на кириллице писать в коде. Только в defaultproperties. Поэтому если хотим написать что по-русски делаем переменную.
    var string rusMessage;
    
    //Стандартные функции GameRules
    function PostBeginPlay()
    {
        if(Level.Game.GameRulesModifiers == none)
            Level.Game.GameRulesModifiers = self;
        else
            Level.Game.GameRulesModifiers.AddGameRules(self);
    }
    function AddGameRules(GameRules GR)
    {
        if(GR != self)
            super.AddGameRules(GR);
    }
    //
    
    //Flame. Здесь мы отслеживаем смерть игрока и ставим свойство bIsTier2Weapon=false его текущей пушке
    //Тут некоторый финт получается. Если bIsTier2Weapon=false, то у KFWeaponPickup класса заполняется свойство
    //DroppedBy при выкидывании - функция KFWeaponPickup.InitDroppedPickupFor.
    //А это поможет нам получить контроллер владельца после смерти владельца. Pawn.Controller то уже None и обычный способ не катит.
    //В принципе можно было бы всем пушкам сразу ставить это свойство (в CheckReplacement, например)
    //Оно относится к стим ачивкам и в общем то нам безразлично. Всё равно мутатор не белый и никаких стим ачивок небось не ожидается
    //Тогда можно было бы работать в едином стиле с KFWeaponPickup объектами и отслеживать только DroppedBy
    //Ну почему-то хочется мне пока сделать так как сделано. Вроде как с минимальными вмешательствами
    function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> damageType, vector HitLocation)
    {
        if    (
                Killed.IsA('KFHumanPawn')
                &&    Killed.Controller!=none
                &&    Killed.Controller.IsA('PlayerController')
            )
        {
            KFWeapon(Killed.Weapon).bIsTier2Weapon=false;
        }
        if(NextGameRules!=None)
            return NextGameRules.PreventDeath(Killed,Killer, damageType,HitLocation);
        return false;
    }
    
    //Flame. В общем то базовая функция на которой держится мутатор. Если out переменная bAllowPickup=0 и
    //функция возвращается true - значит мы переопределяем процесс поднятия пушки и запрещаем поднятие.
    //Если функция возвращает false - нам пофиг на значение переменной bAllowPickup, функция как будто бы и не вызывалась
    //То есть мы проверяем что поднимаем пушку, проверяем можем ли мы её поднимать. Если да, то false и функции как будто бы и не было.
    //Если true, то глядим на bAllowPickup. Оно тут 0, значит запрещаем поднимать.
    function bool OverridePickupQuery(Pawn Other, Pickup Item, out byte bAllowPickup)
    {
        if(WeaponPickup(Item)!=None && Other.Controller!=None)
        {
            bAllowPickup = 0;
            if(CanTakeItem(Other.Controller,Item))
            {
                if(NextGameRules!=None)
                    return NextGameRules.OverridePickupQuery(Other, Item, bAllowPickup);
                return false;
            }
            return true;
        }
        if(NextGameRules!=None)
            return NextGameRules.OverridePickupQuery(Other, Item, bAllowPickup);
        return false;
    }
    //Flame. Функция проверяет принадлежит ли кому-то пушка и если да, то принадлежит ли она нам
    function bool CanTakeItem(Controller C, Pickup WP)
    {
        local int i;
        local PlayerController PC;
        PC=PlayerController(C);
        if(PC==none)
            return false;
        for(i=0;i<OwnedPickups.Length;i++)
        {
            if(OwnedPickups[i].P==WP)
            {
                if(OwnedPickups[i].ID==PC.GetPlayerIDHash() || WeaponPickupIsAllowed(OwnedPickups[i].ID))
                    return true;
                PC.ClientMessage("This weapon is owned by /"@rusMessage@OwnedPickups[i].PlayerName);
                return false;
            }
        }
        return true;
    }
    
    //Flame. Проверяем - не разрешил ли владелец поднимать пушки
    function bool WeaponPickupIsAllowed(string Hash)
    {
        local Controller C;
        for( C = Level.ControllerList; C != None; C = C.nextController )
        {
            if(C.IsA('PlayerController') && C.PlayerReplicationInfo.PlayerID>0)
            {
                if    (
                        PlayerController(C).GetPlayerIDHash()~=Hash
                        &&    bool(C.PlayerReplicationInfo.GetPropertyText("bAllowPickup"))
                    )
                {
                    return true;
                }
            }
        }
        return false;
    }
    
    //Flame. Функция вызывается, когда на карте появляется новый WeaponPickup объект. Если у неё есть владелец - она заносится в массив.
    //WeaponPickup добавляется в массив либо если Instigator.Controller пушки не None (при обычном выкидывании)
    //Либо если DroppedBy не None, при посмертном выкидывании пушки
    //По-хорошему надо бы чистить этот массив. В том же таймере пробегать по всем WeaponPickup и глядеть каких уже нет в OwnedPickups
    //Но у меня не было целью сделать оптимизированный код, акцент был на простоте и лаконичности). Так что сами можете это поправить.
    function AddPickup(WeaponPickup WP)
    {
        local PlayerController PC;
        local PickupInfo pi;
        if(WP==none || WP.Instigator==none)
            return;
        PC=PlayerController(WP.Instigator.Controller);
        //Отслеживаем пушку выпавшую из рук трупа
        if(PC==None)
            PC=KFWeaponPickup(WP).DroppedBy;
        //
        if(PC==None)
            return;
        pi.P=WP;
        pi.ID=PC.GetPlayerIDHash();
        pi.PlayerName=PC.PlayerReplicationInfo.PlayerName;
        OwnedPickups[OwnedPickups.Length] = pi;
    }
    
    defaultproperties
    {
        rusMessage="Владельцем является"
    }
    
    Будем использовать SRPlayerReplicationInfo перекидывания переменной bAllowPickup с клиента на сервер и обратно.
    Меню пользователя, где он разрешает или не разрешает поднимать свои пушки открывается на клиенте, а сам запрет идёт на уровне сервера.
    Код:
    class SRPlayerReplicationInfo extends KFPlayerReplicationInfo;
    
    //Разрешаем поднимать пушки
    var bool bAllowPickup;
    //
    
    replication
    {
        //Добавляем репликацию функции с клиента на сервер
        reliable if(Role < Role_Authority)
            SendSettingsToServer;
        //
    }
    
    //Инициализация значения bAllowPickup из MySettings.ini файла клиента и отсылка значения на сервер.
    simulated function PostNetBeginPlay()
    {
        if(Role<Role_Authority)
        {
            bAllowPickup=bool(class'SRMySettings'.static.Get("bAllowPickup"));
            SendSettingsToServer("bAllowPickup",string(int(bAllowPickup)));
        }
        Super.PostNetBeginPlay();
    }
    
    //Здесь я тоже сделал универсальную функцию, которая принимает 2 строки и использует SetPropertyText для изменения
    //значения переменной с названием Variable на значение заданное строкой bValue
    function SendSettingsToServer(string Variable, string bValue)
    {
        if(bNetOwner)
            SetPropertyText(Variable,bValue);
    }
    
    Если у вас ещё нет класса SRPlayerReplicationInfo - создаёте его с нуля и прописываете в ServerPerksMut
    Код:
    Class ServerPerksMut extends Mutator Config(shServerPerks);
    ...
    function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
    {
        ...
        if(Controller(Other)!=None)
            Controller(Other).PlayerReplicationInfoClass = Class'SRPlayerReplicationInfo';
        ...
    }
    ...
    
    Класс SRMySettings нам понадобится для сохранения в ini на клиенте последнего значения того - разрешили мы поднимать пушки или нет.
    Подробнее про это расписано тут
    Код:
    Class SRMySettings extends Object
        PerObjectConfig
        Config(MySettings);
    
    var private transient SRMySettings Ref;
    
    var config bool bAllowPickup;
    
    static final function SRMySettings GetSettings()
    {
        if( Default.Ref==None )
            Default.Ref = New(None,"Settings")Class'SRMySettings';
        return Default.Ref;
    }
    
    static final function string Get(string Setting)
    {
        local SRMySettings S;
        S = GetSettings();
        return S.GetPropertyText(Setting);
    }
    
    static final function Set(string Setting, string V)
    {
        local SRMySettings S;
        S = GetSettings();
        S.SetPropertyText(Setting,V);
        S.SaveConfig();
    }
    
    Это сама новая вкладка Settings с GUICheckBoxButton (кнопка с крестиком)
    Код:
    class SRSettingsTab extends MidGamePanel;
    
    var automated GUISectionBackground i_BGSec;
    var automated GUICheckBoxButton checkBoxAllowPickup;
    var automated GUILabel labelAllowPickup;
    
    //При первом открытии проставляем значение сохранённое в MySettings.ini файле у клиента. Разрешаем мы поднятие или нет.
    //Кроме того мы правим значение переменной bAllowPickup в SRPlayerReplicationInfo на клиенте и на сервере
    //В общем то это не нужно - мы и так инициализировали эти значения в SRPlayerReplicationInfo, ну да и ладно)
    function InitComponent(GUIController MyController, GUIComponent MyOwner)
    {
    	Super.InitComponent(MyController, MyOwner);
    	checkBoxAllowPickup.bChecked=SRPlayerReplicationInfo(PlayerOwner().PlayerReplicationInfo).bAllowPickup;
    }
    
    //Следующие 2 функции я использую, чтобы элемент управления GUICheckBoxButton был квадратный независимо от разрешения монитора
    function ResolutionChanged(int ResX, int ResY)
    {
        Super.ResolutionChanged(ResX,ResY);
        ReSizeButtons();
    }
    function ReSizeButtons()
    {
        local float bSize;
        bSize=i_BGSec.ActualWidth() / 40.0;
        checkBoxAllowPickup.WinHeight=bSize;
        checkBoxAllowPickup.WinWidth=bSize;
    }
    
    //Если мы нажимаем на GUICheckBoxButton и разрешаем или запрещаем поднимать пушки - переменная bAllowPickup в SRPlayerReplicationInfo изменяется. И на клиенте и на сервере.
    function InternalOnChange( GUIComponent C )
    {
        if(C == checkBoxAllowPickup)
        {
            class'SRMySettings'.static.Set("bAllowPickup",string(int(checkBoxAllowPickup.bChecked)));
            SRPlayerReplicationInfo(PlayerOwner().PlayerReplicationInfo).SendSettingsToServer("bAllowPickup",string(int(checkBoxAllowPickup.bChecked)));
            SRPlayerReplicationInfo(PlayerOwner().PlayerReplicationInfo).bAllowPickup=checkBoxAllowPickup.bChecked;
        }
    }
    
    defaultproperties
    {
        //Фон
        Begin Object Class=GUISectionBackground Name=BGSec
            bFillClient=True
            Caption="Settings"
            WinTop=0.02
            WinLeft=0.02
            WinWidth=0.96
            WinHeight=0.84
            OnPreDraw=BGSec.InternalPreDraw
        End Object
        i_BGSec=BGSec
        //Сам GUICheckBoxButton
        Begin Object Class=GUICheckBoxButton Name=checkBoxAllowPickupD
            Hint="Other players can pick up your weapons / Другие игроки могут поднимать ваши пушки"
            WinTop=0.10
            WinLeft=0.10
            WinWidth=0.03
            TabOrder=11
            OnChange=SRSettingsTab.InternalOnChange
            OnKeyEvent=checkBoxAllowPickupD.InternalOnKeyEvent
        End Object
        checkBoxAllowPickup=checkBoxAllowPickupD
        //Текстовая метка
        Begin Object Class=GUILabel Name=labelAllowPickupD
            Caption="Allow weapon pickup"
            VertAlign=TXTA_Center
            StyleName="TextLabel"
            WinTop=0.10
            WinLeft=0.16
            WinWidth=0.70
            WinHeight=0.04
        End Object
        labelAllowPickup=labelAllowPickupD
    }
    
    Ну и надо прописать эту новую вкладку в SRInvasionLoginMenu.
    Код:
    Class SRInvasionLoginMenu extends UT2K4PlayerLoginMenu;
    
    ...
    
    function InitComponent(GUIController MyController, GUIComponent MyOwner)
    {
        ...
        Panels[5].ClassName = string(Class'SRSettingsTab');
        ...
    }
    
    defaultproperties
    {
        ...
        Panels(5)=(Caption="Settings",Hint="Set your server preferences")
        ...
    }
    

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

    Upd. Поправил мутатор, чтобы он не конфликтовал с другими использующими OverridePickupQuery.

    Upd. Внёс важные изменения

    Вариации на тему:
    Игрок поднимает пушки только своего перка
     
    Последнее редактирование: 30 янв 2017
  2. RaideN111

    RaideN111 Игровая Администрация

    Запрет на поднятие распространяется на оружие которое было в руках?
    А и текущее количество патронов сохраняется?
     
  3. RaideN111

    RaideN111 Игровая Администрация

    И чет довольно сложно ее соединить с твоим управлением вида от 3го лица
     
  4. Flame

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

    Вот поставь и проверь что там куда распространяется)
    Хорош уже тупо копипастить - мне надо чтобы вы хоть чуток напрягали голову
    Иначе я буду выкладывать мутаторы в таком виде, что только те, кто в состоянии хоть чуток соображать смогут что-то себе установить
     
    w.a.l нравится это.
  5. RaideN111

    RaideN111 Игровая Администрация

    Я напрягал голову) 2 мода в одно целое соединил) вот тока с высотой строчек не разобрался до конца
     
  6. Flame

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

    Вот и хорошо
     
  7. Flame

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

    Тут мне поступил сигнал, что часть пушек поднимается тогда, когда не должна
    У меня не получилось добиться этого результата - честно выкинул под 50 пушек и ни одну не смог подобрать другим игроком
    Если у кого такая фигня - пишите
     
  8. Flame

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

    Поправил мутатор, чтобы он не конфликтовал с другими использующими OverridePickupQuery
    В частности речь об этом мутаторе (его я тоже чуток поправил - выложил в шапке доп. ссылку)
     
  9. kok-s

    kok-s Консильери

    Так почитаешь форум)))) и задаёшься мыслишкой - вот столько всего полезного делают, а ни кто не додумался соединить эти наработки в один Server Perks (ну и название ему дать 8.0 и выложить на трипах). А то марко вроде как больше не будет обновлять свою версию. Был бы Server Perks 8.0 Russian edition.
     
  10. Essence

    Essence Бандит

    Ну почему же. Соединяют. Для себя.
    Делать для других нет либо желания, либо времени. А скорее всего и того, и другого.
    Нуу, он ещё не доделал возможность изменения статистики игроков в Web Admin. Так что может обновит. А может и нет.
     
  11. Flame

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

    Важно!

    Ошибка у меня в той части, что касается редактирования SP
    В SRPlayerReplicationInfo я считываю настройки из ini файла на клиенте и передаю значения на сервер и делаю это в PostBeginPlay

    Но в функции PostBeginPlay не работает репликация на сервер!​

    Поэтому если у вас в ini файле стоит галочка на разрешение поднятия пушек, то данные из ini будут прочитаны, галочки в настройках будут выставлены, а вот значение переменной на сервере будет по прежнему false
    А в мутаторе используется именно серверное значение
    Если же считывать настройки не в функции PostBeginPlay, а в функции PostNetBeginPlay, то всё будет хорошо - репликация сработает
    Поэтому кто уже пользуется моим кодом замените
    Код:
    simulated function PostBeginPlay()
    {
        bAllowPickup=bool(class'SRMySettings'.static.Get("bAllowPickup"));
        SendSettingsToServer("bAllowPickup",string(int(bAllowPickup)));
        Super.PostBeginPlay();
    }
    
    на
    Код:
    simulated function PostNetBeginPlay()
    {
        if(Role<Role_Authority)
        {
            bAllowPickup=bool(class'SRMySettings'.static.Get("bAllowPickup"));
            SendSettingsToServer("bAllowPickup",string(int(bAllowPickup)));
        }
        Super.PostNetBeginPlay();
    }
    
    Кроме того я добавил строку if(Role<Role_Authority), ибо зачем нам выполнять этот код на сервере.
    Ничем хорошим это не закончится)

    Шапку я сейчас подредактирую
     
    Последнее редактирование: 4 янв 2017
  12. Flame

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

    Думаете всё? Теперь нормально работать будет?
    А вот нихрена)

    Ещё Важнее

    Я ещё не подумал о важнейшем нюансе
    Смотрите что происходит - PlayerReplicationInfo он релевантен всем - ибо другие игроки должны знать о своих сокомандниках.
    Это значит что в нашем клиентском пространстве существуют все SRPlayerReplicationInfo других игроков

    А как мы вытаскиваем и сохраняем данные?
    Мы берём класс SRMySettings на нашем клиенте, создаём (читаем данные из ini) экземпляр этого класса и прописываем этот объект в default свойства класса

    Но ведь для всех PlayerReplicationInfo на нашем клиенте именно этот класс SRMySettings актуален

    В общем то было бы и пофиг на это, у других клиентов свои клиентские версии для этого класса и в игре они используют именно свои экземпляры. Если бы не одно но - репликация на сервер.

    Пример:
    На сервер заходит Игрок1.
    Создаются ключевые объекты и среди прочего создаётся объект класса PlayerReplicationInfo на сервере.
    После этого идёт репликация этого класса на клиент.
    В классе PlayerReplicationInfo зовётся код
    Код:
    SRMySettings'.static.Get("bAllowPickup")
    
    Что этот код делает:

    1. Get функция вызывает GetSettings
    В GetSettings создаётся объект SRMySettings с настройками из ini Игрока1.
    После чего этот объект цепляется в качестве default значение класса.
    Теперь мы всегда можем получить доступ к полям с помощью статик функции, которая прочтёт значение default переменной класса.
    В принципе мы могли бы просто создать объект SRMySettings и прицепить его хотя бы и в тот же PlayerReplicationInfo в качестве отдельной переменной.

    Так вот. У нас есть класс SRMySettings. Он хранит ссылку на объект прочитанный из ini. Править эту ссылку мы не можем - в функции GetSettings есть проверка на None.

    2. Непосредственно в Get мы возвращаем значение нужного поля.
    Допустим Игрок1 позволяет другим игрокам поднимать пушки и у него bAllowPickup установлена в true.
    В PostNetBeginPlay Игрок1 получил значения для клиента и реплицировал эти значения для сервера.
    Именно серверное значение используется при поднятии пушек.

    3. Пока всё отлично, но вот приходит Игрок2. А у него bAllowPickup=false

    4. Игрок2 точно так же берёт настройки из своего уже ini и копирует их на клиент и сервер своего PlayerReplicationInfo

    5. И вот тут начинается адство. Как я писал игроки должны знать основную информацию друг о друге (имя и прочее).
    Игрок2 "узнаёт" об Игроке1 и PlayerReplicationInfo Игрока1 с сервера реплицируется на клиент Игрока2.
    При этом выполняется PostNetBeginPlay в новосозданном PlayerReplicationInfo Игрока1.

    На клиенте Игрока2 свой класс SRMySettings и там в default лежит ссылка на свой вариант объекта с ini настройками.
    А значит PlayerReplicationInfo Игрока1 получает настройки из ini Игрока2 на компьютере Игрока2.
    Будь это только переменные клиента фиг бы с ними - у Игрока1 свой экземпляр PlayerReplicationInfo и с ним он и работает.

    Но беда то в том, что в PostNetBeginPlay идёт репликация значений на сервер, а вот серверный экземпляр PlayerReplicationInfo Игрока1 единственный и неповторимый.
    В итоге каждый последующий Игрок зашедший на сервер меняет серверные переменные Игрока1.

    Чтобы это поправить - нам надо так сделать, чтобы репликация на сервер шла только с Владельца изначального класса PlayerReplicationInfo. То есть в нашем случае с Игрока1.
    Только то значение нам важно, которое меняется на PlayerReplicationInfo на компьютере Игрока1, а не на тех, которые получились "клонированием" на компьютеры других игроков.

    В общем сумбурно получилось и может где и неправильно, но общий вектор рассуждений понятен (ну тем, кто это чуток изучал)) )
    Короче добавил я одну проверку в функцию SendSettingsToServer и стало хорошо. Просто проверяю являемся ли мы владельцем.
    Вроде для этого отлично подходит переменная bNetOwner
    Код:
    function SendSettingsToServer(string Variable, string bValue)
    {
        if(bNetOwner)
            SetPropertyText(Variable,bValue);
    }
    
    Добавилась одна строка и теперь нет этого косяка. Вообще я подумаю. Может и не стоит эти переменные инициализировать именно в PlayerReplicationInfo - одни проблемы получились.
    Ну а те, кто пользуется этим подходом - будьте осторожны)

    Замечание:
    Может возникнуть вопрос. А почему именно последний влияет на пришедших ранее, а не наоборот.
    Дело в том, что для уже пришедших игроков их операции по присвоению значений переменных из ini уже произошли и новые значения нового игрока переписывают существующие.

    А для только что пришедшего вначале выполнятся именно присвоения на чужих клиентах - к ним реплицированный вариант PlayerReplicationInfo нового игрока докатится раньше, нежели он будет у самого этого нового игрока.

    Поэтому для нового игрока вначале переменные правятся на тех, кто уже в игре, а потом он устанавливает себе правильные значения из свого ini.
     
    Последнее редактирование: 1 янв 2017
    HATAXA нравится это.
  13. RaideN111

    RaideN111 Игровая Администрация

    Флейм, после всех этих добавлений в SRPlayerReplicationInfo на моем сервере появился такой вот занятный баг в таблице: игроки дублируются в списке

    [​IMG]


    Как то раз даже сам WebAdmin появился в списке
    [​IMG]
     
  14. Flame

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

    Каких именно?)
    Изначальных или те, что в последних 2х постах?
     
  15. RaideN111

    RaideN111 Игровая Администрация

    Последних двух
     
  16. Flame

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

    А. Кстати, я неправильно написал (в шапке, кстати, правильно поправил)
    Код:
    simulated function PostNetBeginPlay()
    {
        if(Role<Role_Authority)
        {
            bAllowPickup=bool(class'SRMySettings'.static.Get("bAllowPickup"));
            SendSettingsToServer("bAllowPickup",string(int(bAllowPickup)));
            Super.PostNetBeginPlay();
        }
    }
    
    Super.PostNetBeginPlay(); надо вынести за Role
    Попробуй код
    Код:
    simulated function PostNetBeginPlay()
    {
        if(Role<Role_Authority)
        {
            bAllowPickup=bool(class'SRMySettings'.static.Get("bAllowPickup"));
            SendSettingsToServer("bAllowPickup",string(int(bAllowPickup)));
        }
        Super.PostNetBeginPlay();
    }
    
    Если не поможет - придётся тебе мне помогать
    Вернись к варианту когда всё работало и проверь на каком этапе такая фигня получается
    1. PostNetBeginPlay
    2. SendSettingsToServer
     
  17. RaideN111

    RaideN111 Игровая Администрация

    Совет про Role не сработал.
    Такой баг появляется после изменений в PostNetBeginPlay
    Может конечно и я косячу.
    Вот как у меня прописано

    Код:
    simulated function PostBeginPlay()
    {
        local bool bCustomView;
        if(Role<Role_Authority)
        {
            bCustomView=bool(class'SRMySettings'.static.Get("bCustomView"));
            SendSettingsToServer("bEnhancedShoulderView",string(int(bCustomView)));
            bEnhancedShoulderView=bCustomView;
        }
       
        if(Role<Role_Authority)
        {
            bAllowPickup=bool(class'SRMySettings'.static.Get("bAllowPickup"));
            SendSettingsToServer("bAllowPickup",string(int(bAllowPickup)));
            Super.PostNetBeginPlay();
        }
        Super.PostBeginPlay();
    }
    
     
  18. Flame

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

    Зачем 2 раза?
    Код:
    Super.PostBeginPlay();
    
     
  19. Flame

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

    И какого хера у тебя это все в PostBeginPlay, когда я писал про PostNetBeginPlay

    Читай внимательно шапку и код там.
     
  20. STaJIKeR

    STaJIKeR Солдат

    То чувство когда флейм бомбит))
    [​IMG]
     
    Последнее редактирование модератором: 5 янв 2017
    Flame нравится это.