[SourcePawn] Урок 8 - Структуры Key Values

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #1
[SourcePawn] Урок 8 - KeyValues

<- К содержанию

KeyValues (далее просто KV) - производный тип от Handle, который являет собое дерево-подобную структуру и позволяет хранить данные в виде пар "ключ - значение", а так же вложенные разделы.
Вот пример как выглядит структура KeyValues:
PHP:
"GlobalKey"
{
    "param1"    "value1"
    "param2"    "value2"

    "key1"
    {
        "param1"    "value1"
        "param2"    "value2"
    }
    "key2"
    {
        "param1"    "value1"
        "param2"    "value2"

        "subkey1"
        {
            "param1"    "value1"
            "param2"    "value2"
        }

        "subkey2"
        {
            "param1"    "value1"
            "param2"    "value2"
        }
    }
    "key3"
    {
        "param1"    "value1"
        "param2"    "value2"
    }
}

KV поддерживает только однострочные комментарии (//).
Для использования многострочных комментариев (/* */) нужно использовать SMC Parser (урок будет добавлен позже).

Прежде чем рассматривать методы для работы с KV введем несколько основных понятий.
  • Указатель (или Курсор) - условное понятие (допустим стрелочка), которое указывает на текущую позицию в структуре.
    mSZrRO5MShiBHNzxQv89tw.png

    Как только мы создали структуру, курсор находится на позиции 1.
    Если мы перейдем в ключ "key1", то курсор откажется на позиции 2.
    Если мы перейдем в ключ "key2", то курсор откажется на позиции 3 и так далее.
  • Значение) - самое простое понятие, не знаю как его описать, просто покажу скрином.
    0R0WauoARbOSwKvOAX47Aw.png

    Вот это всё - это значения.
  • Ключ) - уникальный набор символов (как в примере key1, key2, key3, subkey1, subkey2, param1, param2).
  • Секция) - Каждый обведенный блок отмеченный цифрой. Т.к. он хранится в регистронезависимом виде - нельзя полагаться на регистр букв получаемого результата.
    kqijvZpmQdCHcYgtUS5Yug.png
К одной и той же вещи можно применять разные понятия, в зависимости о того, где находится указатель.
Вот примеры:

K8_tRYXRQIe-pK1BbHjXBg.png


SFUnMGSxSx_ekgnfZJjm_w.png


По началу это кажется сложным но на примерах это намного проще.
Давайте рассмотрим основные методы для работы с KV, а потом займемся примерами.
  • KeyValues - Создает объект типа KeyValues
    PHP:
    KeyValues hKeyValues = new KeyValues("GlobalKey");
  • ExportToFile - Сохраняет KV структуру в файл
    PHP:
    char szPath[256]
    BuildPath(Path_SM, szPath, sizeof(szPath), "configs/test_kv.ini");    // Формируем путь к файлу
    hKeyValues.Rewind();    // Возвращаем указатель позиции в самое начало (подробнее - далее)
    hKeyValues.ExportToFile(szPath);    // Сохраняем в файл
  • ImportFromFile - Загружает KV структуру из файла
    PHP:
    char szPath[256]
    BuildPath(Path_SM, szPath, sizeof(szPath), "configs/test_kv.ini");    // Формируем путь к файлу
    KeyValues hKeyValues = new KeyValues("GlobalKey");
    if(hKeyValues.ImportFromFile(szPath))    // Загружаем из файла
    {
        // Успешно загрузили
    }
  • ImportFromString - Загружает KV структуру из строки
    PHP:
    KeyValues hKeyValues = new KeyValues("GlobalKey");
    if(hKeyValues.ImportFromString("\"GlobalKey\" { \"param1\"    \"value1\" \"param2\"    \"value2\" \"key1\" { \"param1\"    \"value1\" \"param2\"    \"value2\" } }"))    // Загружаем из строки
    {
        // Успешно загрузили
    }
  • Import - Клонирует KV структуру из другой KV структуры

    К примеру есть hKeyValues с такой структурой:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
        }
    }
    И есть hKeyValues2 с такой структурой:
    PHP:
    "MyTestKv"
    {
        // <- указатель стоит здесь
        "test_key1"
        {
            "param1"    "value1"
            "param2"    "value2"
        }
        "test_key2"
        {
            "param1"    "value1"
            "param2"    "value2"
        }
    }

    PHP:
    if(hKeyValues.Import(hKeyValues2))    // Клонируем
    {
        // Успешно
    }
    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key1"
            {
                "param1"    "value1"
                "param2"    "value2"
            }
            "test_key2"
            {
                "param1"    "value1"
                "param2"    "value2"
            }
        }
    }
  • SetString - Записывает строку по ключу
    К примеру есть hKeyValues с такой структурой:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
        }
    }

    PHP:
    hKeyValues.SetString("test_key", "my_value");    // Ключ - "test_key", Значение - "my_value"

    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
        }
    }
  • SetNum - Записывает целочисленное значение по ключу

    Возьмем KV из предыдущего примера

    PHP:
    hKeyValues.SetNum("test_key2", 10);    // Ключ - "test_key2", Значение - 10

    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
        }
    }
  • SetFloat - Записывает дробное значение по ключу

    Возьмем KV из предыдущего примера

    PHP:
    hKeyValues.SetFloat("test_key3", 5.7);    // Ключ - "test_key3", Значение - 5.7

    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
        }
    }
  • SetColor - Записывает значение RGBA цвета по ключу
    Возьмем KV из предыдущего примера

    PHP:
    hKeyValues.SetColor("test_key4", 128, 17, 32, 250);    // Ключ - "test_key5", Значение - {128, 17, 32, 250}

    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
        }
    }
  • SetColor4 - Записывает значение RGBA цвета по ключу (В отличии от предыдущего метода - массивом)
    Возьмем KV из предыдущего примера

    PHP:
    int iColor[4] = {12, 35, 72, 230};
    hKeyValues.SetColor4("test_key5", iColor);    // Ключ - "test_key5", Значение - {12, 35, 72, 230}

    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
        }
    }
  • SetVector - Записывает значение вектора по ключу
    Возьмем KV из предыдущего примера

    PHP:
    float fVec[3] = {-12.0, 1650.0, 24224.5};
    hKeyValues.SetVector("test_key6", fVec);    // Ключ - "test_key5", Значение - {-12.0, 1650.0, 24224.5}

    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
        }
    }
  • SetUInt64 - Записывает большое целочисленное значение по ключу
    Возьмем KV из предыдущего примера

    PHP:
    hKeyValues.SetUInt64("test_key7", {0x02C8CE06, 0xCE0602C8});    // Ключ - "test_key4", Значение - 2 4-х байтных числа

    В результате в hKeyValues будет:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
    }
  • GetString - Получает строку по ключу
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
    }

    PHP:
    char szBuffer[256];
    hKeyValues.GetString("test_key", szBuffer, sizeof(szBuffer));
    // Результат:
    // szBuffer = "my_value"
  • GetNum - Получает целочисленное значение по ключу
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
        }
    }

    PHP:
    int iValue = hKeyValues.GetNum("test_key2");
    // Результат:
    // iValue = 10
  • GetFloat - Получает дробное значение по ключу
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
        }
    }

    PHP:
    float fValue = hKeyValues.GetFloat("test_key3");
    // Результат:
    // fValue = 5.7
  • GetColor - Получает значение RGBA цвета по ключу
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
        }
    }

    PHP:
    int r, g, b, a;
    hKeyValues.GetColor("test_key4", r, g, b, a);
    // Результат:
    // r = 128
    // g = 17
    // b = 32
    // a = 250
  • GetColor4 - Получает значение RGBA цвета по ключу (Массив)
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
        }
    }

    PHP:
    int iColor[4];
    hKeyValues.GetColor("test_key5", iColor);
    // Результат:
    // iColor[0] = 12
    // iColor[1] = 35
    // iColor[2] = 72
    // iColor[3] = 230
  • GetVector - Получает значение вектора по ключу
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
        }
    }

    PHP:
    float fVec[3];
    hKeyValues.GetVector("test_key6", fVec);
    // Результат:
    // fVec[0] = -12.0
    // fVec[1] = 1650.0
    // fVec[2] = 24224.5
  • GetUInt64 - Получает большое целочисленное значение по ключу
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
    }

    PHP:
    int iValue[2];
    hKeyValues.GetUInt64("test_key7", iValue);
    
    // Результат:
    // todo
  • Rewind - Возвращает указатель на позицию в самое начало структуры.
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель находится здесь
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
    }

    PHP:
    hKeyValues.Rewind();
    PHP:
    "GlobalKey"
    {
        // <- теперь указатель находится здесь
    
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
    }
    Всегда используте этот метод перед началом работы со структурой. Обычно его нужно использовать:
    • Перед сохранением структуры в файл
    • Перед JumpToKey/GotoFirstSubKey (при условии что это не необходимо сделать относительно текущей позиции)
    • После удаления ключа
  • JumpToKey - Переходит к указанному ключу внутри текущей секции. Один из наиболее часто используемых методов.
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        // <- указатель стоит здесь
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // будем пытаться перейти сюда
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
    }

    PHP:
    if(hKeyValues.JumpToKey("key1")) // Попытка перейти к ключу "key1"
    {
        // Если ключ "key1" сущевствует - проверка будет пройдена.
    }

    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель стоит здесь
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
    }
    PHP:
    // Вернем указатель в начало
    hKeyValues.Rewind();
    if(hKeyValues.JumpToKey("key2", true)) // Попытка перейти к ключу "key2". Если 2-й аргумент true то при отсутствии ключа он будет создан.
    {
        hKeyValues.SetString("new_param", "new value");
    }

    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
    
            "test_key"    "my_value"
            "test_key2"    "10"
            "test_key3"    "5.7"
            "test_key4"    "128 17 32 250"
            "test_key5"    "12 35 72 230"
            "test_key6"    "-12.0 1650.0 24224.5"
            "test_key7"    "..."
        }
        "key2" // Этот ключ мы только что создали
        {
            // <- указатель стоит здесь
            "new_param"    "new value"
        }
    }
  • GotoFirstSubKey - Переходит к первому ключу внутри текущей секции.
  • GotoNextKey - Переходит к следующему ключу в секции на уровень выше.
    Возьмем KV:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "param1"    "value1"
            "param2"    "value2"
        }
    
        "key2"
        {
            "param1"    "value1"
            "param2"    "value2"
        }
    
        "key3"
        {
            "param1"    "value1"
            "param2"    "value2"
        }
    
        "key4"
        {
            "param1"    "value1"
            "param2"    "value2"
        }
    }
    PHP:
    // Вернем указатель в начало
    hKeyValues.Rewind();
    if(hKeyValues.GotoFirstSubKey()) // Переходим к первому ключу внутри "GlobalKey"
    {
        // тут мы попали в ключ "key1"
        do    // Создаем цикл с послеусловием
        {
            // тут можем выполнять какие-то действия с ключем
        } while (hKeyValues.GotoNextKey()); // Условие продолжения цикла: До тех пор, пока можем перейти к следующему ключу
        // Это цикл поочередно пройдет по ключам: "key1" (тут мы уже и так стоим), "key2", "key3", "key4".
    }
  • GoBack - Возвращает указатель на позицию на 1 уровень назад.
    Возьмем KV:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    hKeyValues.Rewind(); // Возвращаем указатель в начало
    hKeyValues.JumpToKey("key1"); // Переходим в ключ "key1"
    hKeyValues.JumpToKey("sub_key1"); // Переходим в ключ "sub_key1"
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                // <- теперь указатель находится здесь
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    hKeyValues.GoBack();    // Возвращаем указатель внутрь "key1" (т.е. на 1 уровень вверх)
    hKeyValues.JumpToKey("sub_key2"); // Переходим в ключ "sub_key2"

    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                // <- теперь указатель находится здесь
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    hKeyValues.GoBack();    // Возвращаем указатель внутрь "key1" (т.е. на 1 уровень вверх)
    hKeyValues.GoBack();    // Возвращаем указатель внутрь "GlobalKey" (т.е. еще на 1 уровень вверх)
    hKeyValues.JumpToKey("key2"); // Переходим в ключ "key1"

    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            // <- теперь указатель находится здесь
            "test_key2"    "10"
        }
    }
  • DeleteKey - Удаляет из структуры указанный ключ (и всё что в нем).
    Возьмем KV из предыдущего примера:

    PHP:
    "GlobalKey"
    {
        // <- указатель находится здесь
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    // В том положении, где сейчас находится указатель можно удалить только "key1" и "key2".
    // Сейчас мы хотим удалить "sub_key1". Для этого перейдем в "key1"
    if(hKeyValues.JumpToKey("key1")) // Проверка необходима чтобы убедиться что мы точно попали в ключ (т.е. что он сущевствует)
    {
        hKeyValues.DeleteKey("sub_key1"); // Удаляем ключ "sub_key1"
    }
    Теперь KV выглядит вот так:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            // <- указатель находится здесь
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    hKeyValues.Rewind(); // Возвращаем указатель в начало
    hKeyValues.DeleteKey("key1"); // Удаляем ключ "key1"
    Теперь KV выглядит вот так:
    PHP:
    "GlobalKey"
    {
        // <- указатель находится здесь
        "param1"    "value1"
        "param2"    "value2"
    
        "key2"
        {
            "test_key2"    "10"
        }
    }
  • DeleteThis - Удаляет из структуры текущий ключ (и всё что в нем).
    Возьмем KV из предыдущего примера:

    PHP:
    "GlobalKey"
    {
        // <- указатель находится здесь
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    if(hKeyValues.JumpToKey("key1")) // Перейдем в "key1"
    {
        hKeyValues.DeleteThis(); // Удаляем ключ "key1"
    }
    Теперь KV выглядит вот так:
    PHP:
    "GlobalKey"
    {
        // <- указатель находится здесь
        "param1"    "value1"
        "param2"    "value2"
    
        "key2"
        {
            "test_key2"    "10"
        }
    }
  • GetSectionName - Получает имя текущей секции (т.е. ключ где сейчас находится указатель)
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        // <- указатель находится здесь
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    char szBuffer[256];
    hKeyValues.GetSectionName(szBuffer, sizeof(szBuffer));
    // Результат:
    // szBuffer = "GlobalKey"
    hKeyValues.JumpToKey("key1");
    hKeyValues.GetSectionName(szBuffer, sizeof(szBuffer));
    // Результат:
    // szBuffer = "key1"
    hKeyValues.JumpToKey("sub_key2");
    hKeyValues.GetSectionName(szBuffer, sizeof(szBuffer));
    // Результат:
    // szBuffer = "sub_key2"
    hKeyValues.Rewind();
    hKeyValues.JumpToKey("key4");
    hKeyValues.GetSectionName(szBuffer, sizeof(szBuffer));
    // Результат:
    // szBuffer = "GlobalKey"
    // А почему? А потому что "key4" не сущевствует и указатель останется в главной секции
  • SetSectionName - Устанавливает имя текущей секции (т.е. ключ где сейчас находится указатель)
    Возьмем KV из предыдущего примера:
    PHP:
    "GlobalKey"
    {
        // <- указатель находится здесь
        "param1"    "value1"
        "param2"    "value2"
    
        "key1"
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }

    PHP:
    hKeyValues.JumpToKey("key1");
    hKeyValues.JumpToKey("sub_key2");
    hKeyValues.SetSectionName("sub_key2_new"); // Изменим имя "sub_key2" на "sub_key2_new"
    hKeyValues.GoBack();    // Вернемся в "key1"
    hKeyValues.SetSectionName("key1_new"); // Изменим имя "key1" на "key1_new"
    hKeyValues.Rewind();
    Теперь KV выглядит так:
    PHP:
    "GlobalKey"
    {
        "param1"    "value1"
        "param2"    "value2"
    
        "key1_new"    // Тут изменилось
        {
            "test_key"    "my_value"
            "test_key2"    "10"
    
            "sub_key1"
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
    
            "sub_key2_new"    // И тут изменилось
            {
                "test_key4"    "128 17 32 250"
                "test_key5"    "12 35 72 230"
            }
        }
    
        "key2"
        {
            "test_key2"    "10"
        }
    }
  • GetDataType - Получает тип данных ключа (Заполню позже)
  • SavePosition - (Заполню позже)
  • SetEscapeSequences - (Заполню позже)
  • NodesInStack - (Заполню позже)
  • JumpToKeySymbol - (Заполню позже)
  • FindKeyById - (Заполню позже)
  • GetNameSymbol - (Заполню позже)
  • GetSectionSymbol - (Заполню позже)

Еще примеров добавлю позже.
Напишите какие примеры хотели бы увидеть (или на примере чего) и что не ясно по KV (не только в уроке, а вообще, например, в других плагинах).
 
Последнее редактирование:

Exle

Участник
Сообщения
190
Реакции
216
Добавлю от себя пример с комментариями.
PHP:
public void OnPluginStart()
{
    char path[PLATFORM_MAX_PATH];
    BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "configs/test/test.txt");
    //--------------------------------------------------------------------------
    //  configs/test/test.txt файл не создан.
    //--------------------------------------------------------------------------

    KvWriteToFile(path);
    KvReadFromFile(path);
}

void KvWriteToFile(const char[] path)
{
    KeyValues kv = new KeyValues("Test");

    kv.SetString("KeyValues/it", "works");
    kv.SetNum("KeyValues/param", 20);
    //--------------------------------------------------------------------------
    //  "Test"
    //  {
    //      "KeyValues"
    //      {
    //          "it"        "works"
    //          "param"     "20"
    //      }
    //  }
    //--------------------------------------------------------------------------

    kv.SetString("KeyValues/Lesson/For/Users/test", "it works");
    //--------------------------------------------------------------------------
    //  "Test"
    //  {
    //      "KeyValues"
    //      {
    //          "it"        "works"
    //          "param"     "20"
    //          "Lesson"
    //          {
    //              "For"
    //              {
    //                  "Users"
    //                  {
    //                      "Test"      "it works" // Создали.
    //                  }
    //              }
    //          }
    //      }
    //  }
    //--------------------------------------------------------------------------


    kv.ExportToFile(path);
    //--------------------------------------------------------------------------
    //  configs/test/test.txt был создан.
    //--------------------------------------------------------------------------

    delete kv;
}

void KvReadFromFile(const char[] path)
{
    KeyValues kv = new KeyValues("Test");
    if (kv.ImportFromFile(path))
    //  Получение кв из файла.
    {
        char value[64], key[64];
        kv.GetString("KeyValues/Lesson/For/Users/test", value, 64);
        PrintToServer("value 'KeyValues/Lesson/For/Users/test' = %s", value);
        //--------------------------------------------------------------------------
        //  Server Console: value 'KeyValues/Lesson/For/Users/test' = it works
        //--------------------------------------------------------------------------

        // Переходим в начало
        kv.Rewind();

        // Хочу получить значение и название ключа.
        // Который создавался выше kv.SetString("KeyValues/it", "works").
        // Допустим, что я не знаю название ключа.
        if (kv.JumpToKey("KeyValues") && kv.GotoFirstSubKey(false))
                                       // Если в kv.GotoFirstSubKey(bool keyOnly=true) не трогать аргумент,
                                       // То указатель перешел бы к ключу "Lesson", а так переидет к первому значению.
                                       // Но если нет значения, то все равно переидет к первому ключу.
        {
            //--------------------------------------------------------------------------
            //  "Test"
            //  {
            //      "KeyValues"
            //      {
            //          "it"        "works" // <- указатель, так как kv.GotoFirstSubKey(false).
            //          "param"     "20"
            //          "Lesson"
            //          {
            //              // <- указатель был бы здесь, если бы мы не трогали аргумент у kv.GotoFirstSubKey(), так как стандартное значение - true.
            //              "For"
            //              {
            //                  "Users"
            //                  {
            //                      "Test"      "it works"
            //                  }
            //              }
            //          }
            //      }
            //  }
            //--------------------------------------------------------------------------

            // Получение значения ключа, но мы не знаем его название.
            // NULL_STRING, что бы получить значение ключа текущей позиции.
            // NULL_STRING - пустая строка (https://github.com/alliedmodders/sourcemod/blob/master/plugins/include/core.inc#L144).
            // Если значение не найдено, то в value запишется пустая строка.
            kv.GetString(NULL_STRING, value, 64);

            // Получение названия ключа.
            kv.GetSectionName(key, 64);

            if (value[0])
            // Проверка на пустоту строки. Если она пуста, то мы не получили значение, ибо у kv.GetString(NULL_STRING, value, 64); есть четвертый аргумент defvalue, который равен NULL_STRING. условие не сработает.
            // Тут можно сверять название ключей, которые Вам нужны.
            {
                PrintToServer("'key value' = %s %s", key, value);
                //--------------------------------------------------------------------------
                //  Server Console: 'key value' = it works
                //--------------------------------------------------------------------------

                // Тут мне приспичило получить следующее значение, которое числовое, если оно существует.
                if (kv.GotoNextKey(false))
                //  Переход к следующему ключу.
                //  Но если его нет, то Вы переидете к следующему ключу, а не к значению.
                //  Как и в kv.GotoFirstSubKey (про значения аргументов).
                {
                    // Получение названия ключа.
                    kv.GetSectionName(key, 64);

                    // Получение значения ключа.
                    // Мы знаем название ключа. И хочу получить значение используя название ключа.
                    // kv.GetNum(key) не сможем использовать. Так как указатель находится в ключе.

                    // Возвращаемся на 1 уровень вверх.
                    kv.GoBack();
                    //--------------------------------------------------------------------------
                    //  "Test"
                    //  {
                    //      "KeyValues"
                    //      {
                    //          // <- указатель, так как kv.GoBack().
                    //          "it"        "works"
                    //          "param"     "20"
                    //          "Lesson"
                    //          {
                    //              "For"
                    //              {
                    //                  "Users"
                    //                  {
                    //                      "Test"      "it works"
                    //                  }
                    //              }
                    //          }
                    //      }
                    //  }
                    //--------------------------------------------------------------------------

                    // Теперь мы можем получить значение по названию ключа.
                    int num = kv.GetNum(key, -1);   // Стандартное значение, если оно не найдено -1 (для проверки)

                    if (num != -1)
                    //  Для проверки на существование значения. Так как GotoNextKey переходит к ключу независимо от аргумента.
                    {
                        PrintToServer("'key value' = %s %d", key, num);
                        //--------------------------------------------------------------------------
                        //  Server Console: 'key value' = param 20
                        //--------------------------------------------------------------------------
                    }
                }
            }
        }
    }

    delete kv;
}
 
Последнее редактирование:

MAGNAT2645

Участник
Сообщения
63
Реакции
7
Например, можно добавить лёгкий пример того, как выбрать случайное значение среди ключей "1", "2", "3"... "n". Таким способом работают конфиги в плагине Freak Fortress 2. Там используется цикл, но я так и не понял, каким образом определяются границы (конечный ключ).
PHP:
"GlobalKey"
{
    "test_key"
    {
        "1" "value1"
        "2" "value2"
        "3" "value3"
        "4" "value4"
        "5" "value5"
        "6" "value6"
        "...n" "value...n"
    }
}
 

Nico Yazawa

Бывший MrChester =(
Сообщения
326
Реакции
303
А как можно изменить значение уже записанного ключа?
PHP:
"Admins"
{
   "Nico Yazawa"
   {
       "AdmLvl"       "5"
       "SteamId64"    "123"
       "NameInBase"   "Nico Yazawa"
   }
}
 
Последнее редактирование:

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #6
Например, можно добавить лёгкий пример того, как выбрать случайное значение среди ключей "1", "2", "3"... "n". Таким способом работают конфиги в плагине Freak Fortress 2. Там используется цикл, но я так и не понял, каким образом определяются границы (конечный ключ).
PHP:
"GlobalKey"
{
    "test_key"
    {
        "1" "value1"
        "2" "value2"
        "3" "value3"
        "4" "value4"
        "5" "value5"
        "6" "value6"
        "...n" "value...n"
    }
}
GotoFirstSubKey (false)

А как можно изменить значение уже записанного ключа?
PHP:
"Admins"
{
   "Nico Yazawa"
   {
       "AdmLvl"       "5"
       "SteamId64"    "123"
       "NameInBase"   "Nico Yazawa"
   }
}
зайти в ключ и сделать SetNum/String
 

pashek71

Участник
Сообщения
232
Реакции
43
C-подобный:
bool KvReadFromFile()
{
    char szPath[256]; char buffer[256]; char buffer2[64];
    BuildPath(Path_SM, szPath, sizeof(szPath), "configs/test/overlays.txt");
    KeyValues kv = new KeyValues("overlays");
    kv.ImportFromFile(szPath);
    int i = 1; IntToString(i, buffer, sizeof(buffer)); kv.Rewind();
    while(kv.JumpToKey(buffer))
    {
        
        kv.GetString(buffer, buffer2, sizeof(buffer2));
        hOverlays.PushString(buffer2);
        PrintToServer("i %i, buffer %s, buffer2 %s", i, buffer, buffer2);
        kv.Rewind(); i++; IntToString(i, buffer, sizeof(buffer));
    }
    delete kv;
}
Почему результат в консоли
C-подобный:
i 1, buffer 1, buffer2
i 2, buffer 2, buffer2
i 3, buffer 3, buffer2
i 4, buffer 4, buffer2
i 5, buffer 5, buffer2
i 6, buffer 6, buffer2
i 7, buffer 7, buffer2
i 8, buffer 8, buffer2
i 9, buffer 9, buffer2
i 10, buffer 10, buffer2
i 11, buffer 11, buffer2
overlays.txt :
C-подобный:
"overlays"
{
    "1"    "test"
    "2"    "test"
    "3"    "test"
    "4"    "test"
    "5"    "test"
    "6"    "test"
    "7"    "test"
    "8"    "test"
    "9"    "test"
    "10"    "test"
    "11"    "test"
}
Только вместо test пути к оверлеям
 

pashek71

Участник
Сообщения
232
Реакции
43
тоже не верно, должно быть:
kv.GotoFirstSubKey(false);
kv.GetSectionName(buffer, sizeof(buffer));
kv.GetString(NULL_STRING, path, sizeof(path));
while(kv.GotoNextKey(false));
C-подобный:
bool KvReadFromFile()
{
    char szPath[256]; char buffer[256]; char buffer2[256];
    BuildPath(Path_SM, szPath, sizeof(szPath), "configs/test/overlays.txt");
    KeyValues kv = new KeyValues("overlays");
    kv.ImportFromFile(szPath);
    kv.Rewind();
    kv.GotoFirstSubKey(false);
    int i = 1;
    do
    {
        i++;
        kv.GetSectionName(buffer, sizeof(buffer)); //Получаем имя секции(в логе отображается верно)
        kv.GetString(buffer, buffer2, sizeof(buffer2)); //1 параметр имя секции? Во 2 параметр запихиваем ключ(т.е. в buffer2 должна быть моя строка из файла, но в лог почему-то выводит пустоту). В примере вроде именно так.
        LogToFile("test.log", "i %i, buffer %s, buffer2 %s", i, buffer, buffer2);
        PrintToServer("i %i, buffer %s, buffer2 %s", i, buffer, buffer2);
        hOverlays.PushString(buffer2);
    } while(kv.GotoNextKey(false)); //и почему без false цикл выполняется только 1 раз(в примере такой  цикл без false)
    delete kv;
}
L 11/21/2019 - 18:15:15: [aaa.smx] i 2, buffer 1, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 3, buffer 2, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 4, buffer 3, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 5, buffer 4, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 6, buffer 5, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 7, buffer 6, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 8, buffer 7, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 9, buffer 8, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 10, buffer 9, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 11, buffer 10, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 12, buffer 11, buffer2
p.s. если указывать в GetString NULL_STRING вместо имени секции работает корректно, но я не понимаю почему не работает если указываешь имя секции.
 
Последнее редактирование:

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #13
C-подобный:
bool KvReadFromFile()
{
    char szPath[256]; char buffer[256]; char buffer2[256];
    BuildPath(Path_SM, szPath, sizeof(szPath), "configs/test/overlays.txt");
    KeyValues kv = new KeyValues("overlays");
    kv.ImportFromFile(szPath);
    kv.Rewind();
    kv.GotoFirstSubKey(false);
    int i = 1;
    do
    {
        i++;
        kv.GetSectionName(buffer, sizeof(buffer)); //Получаем имя секции(в логе отображается верно)
        kv.GetString(buffer, buffer2, sizeof(buffer2)); //1 параметр имя секции? Во 2 параметр запихиваем ключ(т.е. в buffer2 должна быть моя строка из файла, но в лог почему-то выводит пустоту). В примере вроде именно так.
        LogToFile("test.log", "i %i, buffer %s, buffer2 %s", i, buffer, buffer2);
        PrintToServer("i %i, buffer %s, buffer2 %s", i, buffer, buffer2);
        hOverlays.PushString(buffer2);
    } while(kv.GotoNextKey(false)); //и почему без false цикл выполняется только 1 раз(в примере такой  цикл без false)
    delete kv;
}
L 11/21/2019 - 18:15:15: [aaa.smx] i 2, buffer 1, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 3, buffer 2, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 4, buffer 3, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 5, buffer 4, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 6, buffer 5, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 7, buffer 6, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 8, buffer 7, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 9, buffer 8, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 10, buffer 9, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 11, buffer 10, buffer2
L 11/21/2019 - 18:15:15: [aaa.smx] i 12, buffer 11, buffer2
p.s. если указывать в GetString NULL_STRING вместо имени секции работает корректно, но я не понимаю почему не работает если указываешь имя секции.
потому что его там нет.
посмотри внимательно скрины в статье что такое имя секции
и про фолс там же написано
 

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #15
когда делается kv.GotoFirstSubKey(false) + while(kv.GotoNextKey(false))
то фактически происходит цикл по такой структуре:
C-подобный:
"overlays"
{
    "1"
    {
        ""   "test"
    }
    "2"
    {
        ""   "test"
    }
    // ...
}
 

pashek71

Участник
Сообщения
232
Реакции
43
когда делается kv.GotoFirstSubKey(false) + while(kv.GotoNextKey(false))
то фактически происходит цикл по такой структуре:
C-подобный:
"overlays"
{
    "1"
    {
        ""   "test"
    }
    "2"
    {
        ""   "test"
    }
    // ...
}
Сейчас понятнее стало.
потому что его там нет.
посмотри внимательно скрины в статье что такое имя секции
и про фолс там же написано
А про фолс если честно в глаза долблюсь и не могу найти.
p.s. извиняюсь за тупость
 

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #17
Сейчас понятнее стало.

А про фолс если честно в глаза долблюсь и не могу найти.
p.s. извиняюсь за тупость
я помню где-то писал, но не помню где
крч если фолс - это значит что оно будет идти по парам ключ-значение как будто по секциям
 

Banana

Участник
Сообщения
892
Реакции
113
PHP:
"GlobalKey"
{
    "key1"
    {
        "test_key1"    "10" //Как удалить этот ключ и значение?
        "test_key2"    "120"
    }
}
Сообщения автоматически склеены:

 

pimnik98

Участник
Сообщения
21
Реакции
2
А можно ли тут хранить массивы и как с ними работать?
 
Сверху Снизу