Консольные переменные в SourceMod (ConVars)

Тема в разделе "Программирование / Скриптинг", создана пользователем Root, 26 июл 2013.

  1. Root

    Root AWOL.

    Сообщения:
    77
    Симпатии:
    184
    Доброго времени суток!
    Я думаю что каждый, кто однажды писал плагины для SourceMod, сталкивался с консольными переменнами (ака ConVars).
    В "блог" всё сообщение не вместилось, да и русскоязычного перевода я не нашел. Вот так и решил выложить оригинальную статью на родном языке, да еще и мудростью поделиться захотелось. :-D

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

    Вступление:
    ConVars это консольные переменные. А значения переменных могут быть как string, float так и другие нумерационные значения.
    Эти значения можно изменять через консоль или конфиги, и они остаются на протяжении всего времени работы сервера.
    Доступ к ConVars можно получить через носители (Handles). Существует два способа контролировать переменные - создать новую или запросить старую.
    Заметка: Носители переменных всегда разные и их не нужно "освобождать" вручную. Когда плагин будет отключен, SourceMod, соответственно, всё выполнит за Вас.

    Поиск переменных:
    Запрос и поиск существующих переменных довольно прост. Допустим, Вы хотите найти mp_startmoney из Counter-Strike: Source.
    PHP:
    new Handle:mp_startmoney
    public OnPluginStart()
    {
        
    mp_startmoney FindConVar("mp_startmoney")
    }
    Заметка: FindConVar() может вернуть нулевое значение (например, когда переменной не существует). Имейте это ввиду, если Вы хотите запрашивать переменные из других плагинов.

    Создание ConVars:
    Простой переменной достаточно всего лишь задать имя и значение. Однако, неплохо бы еще добавить описание:
    PHP:
    new Handle:g_hEnabled
    public OnPluginStart()
    {
        
    g_hEnabled CreateConVar("название_переменной""значение""описание")
    }
    Так же Вы можете задать лимиты значений для своей переменной (допустим от 0 до 1, как в bool)
    PHP:
    new Handle:g_hEnabled
    public OnPluginStart()
    {
        
    g_hEnabled CreateConVar("название_переменной""0""описание",
                
    _,    // Флаг (об этом позже)
                
    true// Зададим минимум
                
    0.0,  // И, соответственно, значение
                
    true// Нужно ли нам задать максимум?
                
    1.0)  // Да!
    }
    Заметка: Если Вы используете не нумерационное значение для переменной, то и лимиты добавлять не нужно!

    Использование/изменение значений:
    Вы хотите изменить значение mp_startmoney, но в дальнейшем вернуть значение к оригинальному? Вот пример:
    PHP:
    new g_oldmoney
    SetStartMoney
    (newmoney)
    {
        
    // Сохраним значение mp_startmoney в глобальный g_oldmoney (integer)
        
    g_oldmoney GetConVarInt(g_hStartMoney)

        
    // А теперь изменим значение mp_startmoney на новое
        
    SetConVarInt(g_hStartMoneynewmoney)
    }

    RestoreStartMoney()
    {
        
    SetConVarInt(g_hStartMoneyg_oldmoney// Вернем оригинальное значение для mp_startmoney
    }
    Не забывайте, что типы значений для каждой переменной могут быть разными (float, string и другие). Само собой разумеется, что и значение сохраняется в том же типе.
    PHP:
    GetStartMoney()
    {
        
    decl String:buffer[128// Буффер в который мы "запишем" значение

        // Запросим значение mp_startmoney
        
    GetConVarString(g_hStartMoneybuffersizeof(buffer))

        
    // Конвертируем string в integer
        
    return StringToInt(buffer)
    }
    Заметка: Несмотря на то, что значение mp_startmoney нумерационное (integer), оно может извлекаться как string!

    Флаги:
    Консольным переменным можно задавать флаги. В отдельных случаях флаги играют важную роль.

    Основные флаги:
    FCVAR_NOTIFY - Игроки будут оповещены об изменениях. Еще при помощи этого флага можно отслеживать кто пользуется Вашим плагином.
    FCVAR_CHEAT - Задает cheat-флаг переменной (т.е. чтобы изменить значение нужно прописать sv_cheats 1 в консоль сервера).
    FCVAR_PLUGIN - Флаг, определяющий что эта переменная создана плагином (на самом деле это вообще ни на что не влияет).
    FCVAR_DONTRECORD - Если переменная имеет этот флаг, то SM не будет записывать её в конфиг (при использовании AutoExecConfig(true).

    Флаги можно не только задавать, но и добавлять/убирать/whatever.
    Например, мы хотим изменять значение переменной без изменения sv_cheats.
    PHP:
    new Handle:MyConVar

    public OnPluginStart()
    {
        
    MyConVar FindConVar("mp_mycheatcvar"// Найдем нашу переменную

        
    if (MyConVar != INVALID_HANDLE// Проверим существует ли она вообще
        
    {
            
    //SetCheatVar(MyConVar) // Добавим чит-флаг
            
    UnsetCheatVar(MyConVar// Извлечем чит-флаг
        
    }
    }

    UnsetCheatVar(Handle:hndl)
    {
        new 
    flags GetConVarFlags(hndl// Запросим оригинальные флаги
        
    flags &= ~FCVAR_CHEAT // Извлечем чит-флаг
        
    SetConVarFlags(hndlflags// Вернем все предыдущие флаги, но теперь без FCVAR_CHEAT
    }

    SetCheatVar(Handle:hndl)
    {
        new 
    flags GetConVarFlags(hndl)
        
    flags |= FCVAR_CHEAT // То же самое, но добавим FCVAR_CHEAT
        
    SetConVarFlags(hndlflags)
    }
    Заметка: Вся эта мишура с флагами необязательна, так как SourceMod предоставляет возможность менять значения для переменных с любым флагом через sm_cvar "cvar_name" "value".
    Список всех флагов можно посмотреть на Developer.Valvesoftware и SM API

    Отслеживание изменений:
    Очень полезная функция. Тем не менее ею нужно пользоваться с осторожностью!
    Нельзя изменять значение переменной внутри хука, иначе цикл будет продолжаться до бесконечности!
    Например, Вы хотите чтобы значение bot_quota не было ниже 2:
    PHP:
    new Handle:g_hBotQuota

    public OnPluginStart()
    {
        
    g_hBotQuota FindConVar("bot_quota")
        if (
    g_hBotQuota != INVALID_HANDLE)
        {
            
    HookConVarChange(g_hBotQuotaOnBotQuotaChange// Hook all the changes
        
    }
    }

    public 
    OnBotQuotaChange(Handle:cvar, const String:oldVal[], const String:newVal[])
    {
        if (
    StringToInt(newVal) < 2// Так как значение всегда string ("золотая середина"), его нужно конвертировать в int или float (т.е. просто убрать скобки)
        
    {
            
    SetConVarInt(cvar2// В нашем случае значение изменилось на 0 или 1, но мы не хотим этого допускать! Возвращаем минимально-допустимое значение....
        
    }
    }
    Заметка: Как уже было сказано ранее, нет необходимости высвобождать переменные и хук, SM всё сделает за Вас.

    Отправка переменных.
    SM предоставляет отличную функцию - отправлять переменные клиентам.
    Таким образом можно отправлять клиентам значение переменной без изменения этой переменной на сервере!
    Это тоже вполне легко реализовать:
    PHP:
    new Handle:g_hMyCvar

    public OnPluginStart()
    {
        
    g_hMyCvar FindConVar("sv_cheats"// Найдем необходмую нам переменную
    }

    public 
    OnClientPutInServer(client)
    {
        
    SendConVarValue(clientg_hMyCvar"1"// И отправим значение 1 когда игрок заходит на сервер
    }
    Здесь можно увидеть яркий пример использования SendConVarValue.


    Полезные советы:
    • Чаще используйте HookConVarChange!
      Если Ваш плагин часто использует значения переменных (например каждый раз когда игрок умирает или получает урон), то можно использовать хук таким образом:
      PHP:
      new Handle:Enabled

      public OnPluginStart()
      {
          
      enabled CreateConVar("mycvar_enable",  "1""shall we work?"FCVAR_PLUGINtrue0.0true1.0);

          
      // hoooooooooooooooooooooooooooooook
          
      HookConVarChange(enabledOnConVarChange);

          
      // Вручную сменим значение с 0 до 1 чтобы загрузить события
          
      OnConVarChange(enabled"0""1");
      }

      public 
      OnConVarChange(Handle:convar, const String:oldValue[], const String:newValue[])
      {
          switch (
      StringToInt(newValue))
          {
              case 
      false:
              {
                  
      UnhookEvent("player_spawn",            Event_Player_Spawn,    EventHookMode_Post);
                  
      UnhookEvent("player_hurt",             Event_Player_Hurt,     EventHookMode_Post);
                  
      UnhookEvent("dod_stats_weapon_attack"Event_Player_Attack,   EventHookMode_Post);
                  
      UnhookEvent("dod_point_captured",      Event_Point_Captured,  EventHookMode_Post);
                  
      UnhookEvent("dod_capture_blocked",     Event_Capture_BlockedEventHookMode_Post);
                  
      UnhookEvent("dod_bomb_planted",        Event_Bomb_Planted,    EventHookMode_Post);
                  
      UnhookEvent("dod_bomb_defused",        Event_Bomb_Defused,    EventHookMode_Post);
                  
      UnhookEvent("dod_round_win",           Event_Round_End,       EventHookMode_Post);
                  
      UnhookEvent("dod_round_start",         Event_Game_Over,       EventHookMode_PostNoCopy);
                  
      UnhookEvent("dod_game_over",           Event_Game_Over,       EventHookMode_PostNoCopy);
              }
              case 
      true:
              {
                  
      HookEvent("player_spawn",            Event_Player_Spawn,    EventHookMode_Post);
                  
      HookEvent("player_hurt",             Event_Player_Hurt,     EventHookMode_Post);
                  
      HookEvent("dod_stats_weapon_attack"Event_Player_Attack,   EventHookMode_Post);
                  
      HookEvent("dod_point_captured",      Event_Point_Captured,  EventHookMode_Post);
                  
      HookEvent("dod_capture_blocked",     Event_Capture_BlockedEventHookMode_Post);
                  
      HookEvent("dod_bomb_planted",        Event_Bomb_Planted,    EventHookMode_Post);
                  
      HookEvent("dod_bomb_defused",        Event_Bomb_Defused,    EventHookMode_Post);
                  
      HookEvent("dod_round_win",           Event_Round_End,       EventHookMode_Post);
                  
      HookEvent("dod_round_start",         Event_Game_Over,       EventHookMode_PostNoCopy);
                  
      HookEvent("dod_game_over",           Event_Game_Over,       EventHookMode_PostNoCopy);
              }
          }
      }
      Согласитель, что этот метод намного лучше чем тот, который указан ниже:
      PHP:
      public Event_Player_Spawn(Handle:event, const String:name[], bool:dontBroadcast)
      {
          if (
      GetConVarBool(enabled))
          {

          }
      }

      public 
      Event_Player_Hurt(Handle:event, const String:name[], bool:dontBroadcast)
      {
          if (
      GetConVarBool(enabled))
          {

          }
      }
      ...
    • Не использутей FCVAR_REPLICATED для Вашей переменной! Иначе каждый раз, когда игрок будет заходить на сервер, он будет получать ошибку в консоли типа:
      "ConVarRef mycvar_version doesn't point to an existing ConVar. SetConVar: No such cvar ( mycvar_version set to XYZ), skipping"
    • Если переменная использует много значений (1 2 3 4 5 6 7 8 9 10), используйте bit оператор вместо GetConVarInt (ну или в конце-концов switch()).
    • Старайтесь избегать одинаковых названий с обычными переменными (типа mp_startmoney), в противном случае плагин откажется работать!
    • Однажды созданная переменная сохраняется в кэше сервера, так что если Вы использовали некорректное описание/лимит - лучше перезагрузите сервер - иначе изменения не вступят в силу.
    • Если Вам надо найти уже существующую переменную, ищите её в событии OnPluginStart() и сохраните как глобальный Handle. Почему? Да потому что тысячу раз твердили что поиск уже существующих переменных тяжеловат для сервера. Лучше один раз найти и сохранить в памяти! Так то.

    У Вас есть плагин, который запрашивает много переменных, но сей процесс требует оптимизации и упрощения? Тогда эта часть статьи для Вас!
    Существует некоторое множество стилей для ConVars, о которых многие не знают или просто догадываются. Чтож, пролью свет на сей вопрос :)

    Самый обычный и оригинальный стиль, который используется во множестве плагинов.
    Преимущества: простота в использовании.
    Недостатки: не лучший способ для работы с крупными плагинами.
    PHP:
    new Handle:MyConVar
    public OnPluginStart()
    {
        
    MyConVar CreateConVar("MyConVar""1""Description");
    }
    public 
    OnMapStart()
    {
        if (
    GetConVarBool(MyConVar) == true)
        {
            
    // бла бла бла
        
    }
    }
    Преимущества: Значение сохраняется в переменной (например somevalue) и изменяется при изменении переменной. Полезно при частом использовании значения.
    Недостатки:
    -не лучший способ для небольших плагинов.
    -много (ненужных) переменных типа somevalue.
    PHP:
    new Handle:somecvar INVALID_HANDLE;
    new 
    somevalue// Место хранения значения

    public OnPluginStart()
    {
        
    somecvar CreateConVar("sm_somecvar""6""some cvar");
        
    HookConVarChange(somecvarOnConVarChange);
    }

    public 
    OnConVarChange(Handle:convar, const String:oldValue[], const String:newValue[])
    {
        
    somevalue GetConVarInt(somecvar); // Проделаем тоже самое когда значение изменилось, например, через консоль
    }

    public 
    OnPlayerSpawn(client)
    {
        switch (
    somevalue)
        {
            case 
    0// Когда значение равно 0
            
    case 1// Или 1
            
    default: // Другое
    }
    Стиль одного из разработчиков SourceMod. Достоин внимания, так как очень оригинален, интересен и в некой степени оптимизирован.
    Преимущества: Глобальные носители (Handle) не используются, что положительно сказывается на производительности сервера.
    Недостатки: нужно отслеживать изменения для каждой переменной по отдельности.
    PHP:
    new g_iMinPCount 12;
    new 
    bool:g_bEnabled true;
    new 
    g_iCmdRate[2];

    public 
    OnPluginStart()
    {
        new 
    Handle:hRegister// Локальный handle для регистрации ConVar'ов

        // Сразу будем отслеживать изменения и зарегистрируем переменную
        
    HookConVarChange((hRegister CreateConVar("enabled""1""Should I even be running?"_true0.0true1.0)), OnEnabledChange);
        
    g_bEnabled GetConVarBool(hRegister); // Мгновенно присвоим значение для глобального буля

        
    HookConVarChange((hRegister CreateConVar("minplayercount""12""How many players need to be ingame for any check to occur.")), OnMinPlayChange);
        
    g_iMinPCount GetConVarInt(hRegister);

        
    // С таким же успехом можно и находить отдельные переменные
        
    if((hRegister FindConVar("sv_mincmdrate")) != INVALID_HANDLE)
        {
            
    g_iCmdRate[0] = GetConVarInt(hRegister);
            
    HookConVarChange(hRegisterOnMinCmdRateChange);
        }

        if((
    hRegister FindConVar("sv_maxcmdrate")) != INVALID_HANDLE)
        {
            
    g_iCmdRate[1] = GetConVarInt(hRegister);
            
    HookConVarChange(hRegisterOnMaxCmdRateChange);
        }

        
    CloseHandle(hRegister); // Нам ведь не нужны утечки памяти
    }

    public 
    OnConfigsExecuted()
    {
        if(
    g_bEnabled)
        {
            
    // бла бла
        
    }
    }

    public 
    OnEnabledChange(Handle:convar, const String:oldValue[], const String:newValue[])
    {
        
    g_bEnabled GetConVarBool(convar);
    }

    public 
    OnMinPlayChange(Handle:convar, const String:oldValue[], const String:newValue[])
    {
        
    g_iMinPCount GetConVarInt(convar); // ... для каждой переменной
    }

    public 
    OnMinCmdRateChange(Handle:convar, const String:oldValue[], const String:newValue[])
    {
        
    g_iCmdRate[0] = GetConVarInt(convar);
    }

    public 
    OnMaxCmdRateChange(Handle:convar, const String:oldValue[], const String:newValue[])
    {
        
    g_iCmdRate[1] = GetConVarInt(convar);
    }
    При создании происходит получение значения и хук, далее Handle удаляется. Для получения значения используется callback, указанный в HookConVarChange, ведь в его параметрах содержится Handle переменной. Экономит память, особенно при большом числе переменных. (c) KorDen
    Автор неизвестен. Но стиль имеет место быть и достоин внимания.
    Полезен в том случае когда много однотипных переменных (полезно для плагина статистики).
    Ничем особо не отличается от оригинальных стилей.
    PHP:
    enum CSWeaponID //взято из cstrike.inc
    {
        
    CSWeapon_NONE 0,
        
    CSWeapon_P228,
        
    CSWeapon_GLOCK,
        
    CSWeapon_SCOUT,
        ...
    }
    enum WeaponType // Список носителей для создания ConVar
    {
        
    Handle:bomb,
        
    Handle:p228,
        
    Handle:glock,
        
    Handle:scout
    };

    new 
    Points[WeaponType]; // "Регистратор"

    public OnPluginStart()
    {
        
    Points[bomb] = CreateConVar("stats_points_bomb""1",  "Points for killing by bomb");
        
    Points[p228]  = CreateConVar("stats_points_p228",  "5",  "Points for killing by p228");
        
    Points[glock] = CreateConVar("stats_points_glock",  "5",  "Points for killing by glock");
        
    Points[scout] = CreateConVar("stats_points_scout",  "7",  "Points for killing by scout");
    }

    public 
    OnPlayerDeath(attacker)
    {
        
    GivePoints(attackerGetConVarInt(Points[GetEventInt(event"weapon")])); // Дадим убийце Х очков в зависимости от оружия.
    }
    Мой любимый стиль. Придуман одним талантливым шведом, который частенько мне помогал осваивать SP :)
    Преимущества:
    • автоматизация процесса отслеживания переменных (хук).
    • безошибочность в извлечении типа значения (float, int или string).
    • простота процесса извлечения значения.
    • отлично подходит для больших плагинов.
    • оптимизированность и оригинальность.
    Недостатки:
    • нельзя мгновенно отслеживать изменения значений типа string
    • может показаться сложным
    Возьму пример из плагина DoD:S DropManager
    PHP:
    enum
    {
        
    AllowHealthkit// Номер переменной
        
    ItemLifeTime,
        
    DeadDrop,
        
    ConfigFile,

        
    ConVar_Size // Максимальное количество переменных
    };

    enum ValueType
    {
        
    ValueType_Bool,
        
    ValueType_Int,
        
    ValueType_Float,
        
    ValueType_String
    };

    enum ConVar
    {
        
    Handle:ConVarHandle// Носитель консольной переменной
        
    ValueType:Type,      // Тип значения (int, bool)
        
    any:Value            // Само значение
    };

    new 
    GetConVar[ConVar_Size][ConVar]; // Функция запроса значения

    LoadConVars() // Добавьте это в OnPluginStart()
    {
        
    // Номер, тип и оригинальный CreateConVar
        
    AddConVar(AllowHealthkitValueType_Bool,  CreateConVar("dod_dropmanager_healthkit""1""Whether or not allow health kits dropping"FCVAR_PLUGINtrue0.0true1.0));

        
    // Значение float/integer/string
        
    AddConVar(ItemLifeTime,  ValueType_Float,  CreateConVar("dod_dropmanager_lifetime""45""Number of seconds a dropped items stays on the ground"FCVAR_PLUGINtrue0.0));
        
    AddConVar(DeadDrop,      ValueType_Int,    CreateConVar("dod_dropmanager_deaddrop""3""item to drop after death"FCVAR_PLUGINtrue0.0true30.0));
        
    AddConVar(ConfigFile,    ValueType_StringCreateConVar("dod_dropmanager_config",   """Load a custom config for DropManager (without .cfg!)"FCVAR_PLUGIN));
    }

    // Регистрирует/добавляет ConVar
    AddConVar(conVarValueType:typeHandle:conVarHandle)
    {
        
    GetConVar[conVar][ConVarHandle] = conVarHandle;
        
    GetConVar[conVar][Type] = type;

        
    // String не поддерживается
        
    if (type != ValueType_String)
        {
            
    // Обновим значения
            
    UpdateConVarValue(conVar);

            
    // И будем отслеживать
            
    HookConVarChange(conVarHandleOnConVarChange);
        }
    }

    UpdateConVarValue(conVar)
    {
        switch (
    GetConVar[conVar][Type])
        {
            case 
    ValueType_Bool:  GetConVar[conVar][Value] = GetConVarBool (GetConVar[conVar][ConVarHandle]);
            case 
    ValueType_Int:   GetConVar[conVar][Value] = GetConVarInt  (GetConVar[conVar][ConVarHandle]);
            case 
    ValueType_FloatGetConVar[conVar][Value] = GetConVarFloat(GetConVar[conVar][ConVarHandle]);
        }
    }

    public 
    OnConVarChange(Handle:conVar, const String:oldValue[], const String:newValue[])
    {
        
    // Цикл через все переменные
        
    for (new 0ConVar_Sizei++)
        {
            if (
    conVar == GetConVar[i][ConVarHandle])
            {
                
    UpdateConVarValue(i);

                
    // Еще можно отдельно для каждой переменной указать свои изменения
                
    if (== ItemLifeTime)
                {
                    
    // ex. KillTimer
                
    }
            }
        }
    }

    public 
    OnConfigsExecuted()
    {
        
    // String можно заполучить только так
        
    decl String:cfg[256];
        
    GetConVarString(GetConVar[ConfigFile][ConVarHandle], cfgsizeof(cfg));

        if (
    cfg[0] != '\0')
        {
            
    AutoExecConfig(falsecfgNULL_STRING);
        }
    }

    public 
    Action:MyFunction()
    {
        if (
    GetConVar[AllowHealthkit][Value])
        {
            
    // bool
        
    }

        
    // Таймер со значением float
        
    CreateTimer(GetConVar[ItemLifeTime][Value], MyTimer_TIMER_FLAG_NO_MAPCHANGE);

        switch (
    GetConVar[DeadDrop][Value])
        {
            
    // int
        
    }
    }
    Схож с предыдущим, но в этом можно запрашивать у одной переменной значения int, float и другие.
    Преимущества и недостатки должны быть очевидны.
    В пример взят DoD:S ZombieMod
    PHP:
    enum
    {
        
    ConVar_Enabled,
        
    ConVar_Zombie_CritHPRefresh,

        
    ConVar_Size
    };

    enum ConVar
    {
        
    Handle:ConVarHandle,

        
    Value_Int,           // Int
        
    bool:Value_Bool,     // Bool
        
    Float:Value_Float    // Float
    };

    new 
    g_ConVars[ConVar_Size][ConVar];

    InitConVars()
    {
        
    CreateConVar("sm_zombiemod_version"PLUGIN_VERSIONPLUGIN_NAMEFCVAR_NOTIFY|FCVAR_DONTRECORD);

        
    AddConVar(ConVar_Enabled,              CreateConVar("sm_zombiemod_enabled",         "1",   "Enable/Disable Zombie Mod."));
        
    AddConVar(ConVar_Zombie_CritHPRefreshCreateConVar("sm_zombiemod_crit_hp_refresh""100""Amount of health a crit zombie will regenerate on kill."));
    }

    AddConVar(conVarHandle:conVarHandle)
    {
        
    g_ConVars[conVar][ConVarHandle] = conVarHandle;

        
    UpdateConVarValue(conVar);
        
    HookConVarChange(conVarHandleOnConVarChange);
    }

    UpdateConVarValue(conVar)
    {
        
    g_ConVars[conVar][Value_Int] = GetConVarInt(g_ConVars[conVar][ConVarHandle]);
        
    g_ConVars[conVar][Value_Bool] = GetConVarBool(g_ConVars[conVar][ConVarHandle]);
        
    g_ConVars[conVar][Value_Float] = GetConVarFloat(g_ConVars[conVar][ConVarHandle]);
    }

    public 
    OnConVarChange(Handle:conVar, const String:oldValue[], const String:newValue[])
    {
        for (new 
    0ConVar_Sizei++)
        {
            if (
    conVar == g_ConVars[i][ConVarHandle])
            {
                
    UpdateConVarValue(i);
                
    ConVarChanged(i);

                break;
            }
        }
    }

    ConVarChanged(conVar)
    {
        switch (
    conVar)
        {
            case 
    ConVar_Enabled// Что нужно сделать когда изменилось значение первой переменной?
            
    {
                
    // Правильно: бла и бла
            
    }
        }
    }

    public 
    Action:MyFunction()
    {
        if (
    g_ConVars[ConVar_Zombie_CritHPRefresh][Value_Float] > 0.0// FLOAT
        
    {
            
    SetEntityHealth(attackerg_ConVars[ConVar_Zombie_CritHPRefresh][Value_Int]); // INT
        
    }
    }
    На первый взгляд может показаться, что стиль Andersso сложноват и запутан, но на самом деле это не так!
    Достаточно просто всё скопировать и добавить AddConVar(ИМЯ, тип_значения, CreateConVar("название", "значение", "описание", "флаг", "лимиты")); и запрашивать значение через GetConVar[ИМЯ][Value].
    Схож с последними двумя, но есть свои недостатки и преимущества.
    Преимущества: Поддержка string и простая регистрация ConVar'ов через .inc файл
    Недостатки: необходимость своих callback'ов, из-за которых стиль уступает в скорости по отношению к последним двум.
    Я, пожалуй, просто приложу библиотеку и напишу как этим ею нужно пользоваться.
    Цитирую Zephyrus'а:

    При первом запуске cvars_unlocker.sp запишет все скрытые консольные команды в console.txt и, соответственно, разблокирует их. Используйте sm_cvar <имя> <значение> для активации.

    На этом пока всё!
    Потом еще чего-нибудь добавлю, а то чувствую что я что-то забыл. :-D
     

    Вложения:

    Последнее редактирование: 27 июл 2013
    KorDen, Scarface_slv, Sam_Fisher и 9 другим нравится это.
  2. KorDen

    KorDen Atra esterní ono thelduin! Ньюсмейкер

    Сообщения:
    2.194
    Симпатии:
    1.398
    >KyleS
    The best :D
    В достоинства еще следует добавить отсутствие глобальных Handle, которых в больших плагинах получается огромное количество, и как следствие уменьшение необходимого количества памяти - при сотне переменных и прочем подобном может начать ощущаться :)

    И да, думаю стоит к каждому стилю кроме кода написать описание принципа, не всегда можно код понять если способ до этого не знал

    И да, "оригинальный 2" лучше так:
    PHP:
    public OnPluginStart() 

        
    somecvar CreateConVar("sm_somecvar""6""some cvar"); 
        
    HookConVarChange(somecvarOnConVarChange); 
        
    somevalue GetConVarInt(somecvar);

    public 
    OnConVarChange(Handle:convar, const String:oldValue[], const String:newValue[]) 

        
    somevalue GetConVarInt(somecvar); // Проделаем тоже самое когда значение изменилось, например, через консоль 
    }
    т.е. не нужно OnConfigsExecuted брать, если оно в конфигах изменилось, то сработает OnConVarChange, а для late load берем в OnPluginStart
     
    R1KO нравится это.
  3. R1KO

    R1KO Супер-модератор

    Сообщения:
    6.001
    Симпатии:
    2.992
    Очень полезная статья, а главное на русском. Большое спасибо!
     
  4. KorDen

    KorDen Atra esterní ono thelduin! Ньюсмейкер

    Сообщения:
    2.194
    Симпатии:
    1.398
    Думаю стоит добавить следующие описания по способам:

    Оригинальный 1:
    При редком использовании значений переменных это значение можно брать в момент использования, не сохраняя в переменную, либо сохраняя в локальную переменную

    Оригинальный 2:
    Значение сохраняется в переменной (например somevalue) и изменяется при изменении переменной. Полезно при частом использовании значения

    KyleS:
    Не создается глобальный Handle - в этом отличие способа от остальных. При создании происходит получение значения и хук, далее Handle удаляется. Для получения значения используется callback, указанный в HookConVarChange, ведь в его параметрах содержится Handle переменной. Экономит память, особенно при большом числе переменных

    Кстати в примере бред:
    PHP:
    public OnEnabledChange(Handle:convar, const String:oldValue[], const String:newValue[]) 

        switch(
    GetConVarBool(convar)) 
        { 
            case 
    falseg_bEnabled false// Изменения нужно задавать вручную каждый раз 
            
    case trueg_bEnabled true
        }
    }
    зачем? ведь проще
    PHP:
    public OnEnabledChange(Handle:convar, const String:oldValue[], const String:newValue[]) 
        
    g_bEnabled=GetConVarBool(convar);
    Unknown:
    //Описывать сложно, способ честно говоря дурацкий, можно сделать гораздо оптимизированнее,чем 4 раза писать GivePoints...
    Например как у AFK manager by Rothgar - дефайнами сделаны номера переменных и тогда вместо свича можно просто использовать одну функцию GivePoints(attacker, GetConVarInt(Points[weapon])), а weapon Заранее приравнять одному из дефайнов. Гораздо меньше кода

    В остальных пока до конца не разобрался чтобы понять фишку...
     
    Root и R1KO нравится это.
  5. Root

    Root AWOL.

    Сообщения:
    77
    Симпатии:
    184
    KorDen, true dat.
    Завтра обновлю инфу о стилях. Утро вечера мудренее.

    Добавлено через 16 часов 56 минут
    Обновил первый пост (спасибо KorDen), добавил cvar_unlocker.sp и забытый zephconvars.inc (файл с расшиением .inc прикреплять нельзя, так что вы просто переименуйте .inc.sp в .inc)
     
    Последнее редактирование: 27 июл 2013
    R1KO нравится это.
  6. R1KO

    R1KO Супер-модератор

    Сообщения:
    6.001
    Симпатии:
    2.992
    Закрепил тему.

    В KyleS

    как я понял хандл можно не закрывать.
     
  7. AlmazON

    AlmazON деревянный © yand3xmail

    Сообщения:
    4.579
    Симпатии:
    1.989
    Он итак не закрывается, по сути (давно проверял):
    PHP:
    PrintToServer("Handle convar's %s!"CloseHandle(hRegister) ? "close successful":"not closeable");
    Но, это не считается ошибкой - можно закрывать (мало ли, к чему ещё Handle будет привязан), а можно и нет.
    Я, например, иногда отдельно использую этот приём с глобальным Handle для убийства таймера, чтобы не проверять его постоянно на INVALID_HANDLE.
     
  8. gibs

    gibs Фитиль народного волненья

    Сообщения:
    541
    Симпатии:
    138
    А разве таймер не логичней убивать через KillTimer()?
    Вообще данная статья с приходом нового синтаксиса является просто бесполезной. Тут просто показывается как красивенько можно организовать тело плагина с использованием старого синтаксиса, не более.