Исходник J-bp. Расширенная библиотека для работы с JSON в Moonloader | Удобные конфиги. Работа с imgui/mimgui. cData

1736436937546.png

Заголовок: Расширенная библиотека для работы с JSON в Moonloader.
Установка: Переместить файл jbp.lua в moonlaoder/lib
Подключение: local jbp = require 'jbp'
Библиотека JBP предоставляет простой и надёжный способ работы с данными в скриптах GTA SA. Особенно полезна при работе с указателями и сложными структурами данных, делая код более чистым и понятным.
На данный момент реализовано:
  1. decode(json_str) - преобразует JSON строку в таблицу Lua
  2. encode(value, pretty, indent) - преобразует Lua значение в JSON строку
  3. compress(json_str) - минифицирует JSON строку, удаляя пробелы
  4. sanitize(value) - очищает значение от недопустимых для JSON данных
  5. validate(value, schema) - проверяет JSON данные по заданной схеме
  6. path_query(data, path) - ищет значения по JSON Path выражению
  7. pointer_set(data, path, value) - устанавливает значение по указателю
  8. pointer_get(data, path) - получает значение по JSON Pointer
  9. patch(data, patch) - применяет JSON Patch операции к данным
  10. create(initial_data) - создает новый построитель JSON объектов
  11. :set_pointer(path, ptr) - сохраняет указатель в поле
  12. :get_pointer(path) - получает указатель из поля
  13. :set(path, value) - устанавливает значение по пути
  14. :get(path, default) - получает значение по пути
  15. :merge(other) - объединяет с другим JSON объектом
  16. :clone() - создает копию объекта
  17. :keys() - Возвращает массивы верхнего уровня
  18. :clear - Очищает обьект
  19. :remove(path) - Удаляет значение по указанному пути
  20. :to_json(pretty, indent) - Конвертирует в JSON строку
  21. :validate(data, schema) - Валидирует данные в обьекте по схеме
  22. :filter(keys) - Фильтрует объект, оставляя только указанные ключи
  23. :flatten() - Преобразует вложенный объект в плоский с точечной нотацией
  24. :diff(other) - Сравнивает с другой таблицей или обьектом
  25. :watch(callback) - Отслеживает изменения в объекте
  26. .from_json(json_string) - Создает JsonBuilder из JSON строки
  27. :save_to_file(filename, pretty) - сохраняет в файл
  28. .load_from_file(filename) - загружает из файла
  29. Работа с простыми указателями ffi
  30. Работа с cdata mimgui и userdata imgui
  31. Сохранение в Json происходит с форматированием. Ключи сортируются по степени важности.

1. API:
  • Создание объекта
  • Lua:
    -- Создание пустого объекта
    local jbp = require 'jbp'
    local empty = jbp.create()
    
    -- Создание с начальными данными
    local obj = jbp.create({
        name = "John",
        age = 25,
        settings = {
            theme = "dark"
        }
    })
2. Базовые операции:
  • set(path, value) - Установка значения
  • Lua:
    local user = jbp.create()
    
    -- Простая установка
    user:set("name", "John")               -- {name = "John"}
    
    -- Вложенные объекты
    user:set("address.city", "Moscow")     -- {name = "John", address = {city = "Moscow"}}
    
    -- Массивы
    user:set("scores[1]", 100)            -- {name = "John", scores = {100}}
    user:set("items[2]", "Sword")         -- {name = "John", items = {nil, "Sword"}}
    
    -- Цепочка вызовов
    user:set("level", 10)
        :set("health", 100)
        :set("mana", 50)
  • get(path, value) - Получение значения
  • Lua:
    local user = jbp.create({
        name = "John",
        stats = {health = 100},
        inventory = {"Sword", "Shield"}
    })
    
    print(user:get("name"))                -- "John"
    print(user:get("stats.health"))        -- 100
    print(user:get("inventory[1]"))        -- "Sword"
    print(user:get("missing", "N/A"))      -- "N/A" (значение по умолчанию)
3. Трансформация:
  • transform(fn) - Преобразование значений
  • Lua:
    local stats = jbp.create({
        health = 100,
        mana = 50,
        name = "Player",
        equipment = {
            weapon = {damage = 25},
            armor = {defense = 15}
        }
    })
    
    -- Умножаем все числа на 2
    local doubled = stats:transform(function(v)
        return type(v) == "number" and v * 2 or v
    end)
    
    print(doubled:to_json(true))
    -- Результат:
    {
        "health": 200,
        "mana": 100,
        "name": "Player",
        "equipment": {
            "weapon": {
                "damage": 50
            },
            "armor": {
                "defense": 30
            }
        }
    }
  • flatten() - Уплощение структуры
  • Lua:
    local nested = jbp.create({
        user = {
            name = "John",
            address = {
                city = "Moscow",
                street = {
                    name = "Lenin",
                    number = 123
                }
            }
        },
        settings = {
            theme = {
                color = "dark",
                font = {
                    size = 12,
                    family = "Arial"
                }
            }
        }
    })
    
    local flat = nested:flatten()
    print(flat:to_json(true))
    -- Результат:
    {
        "user.name": "John",
        "user.address.city": "Moscow",
        "user.address.street.name": "Lenin",
        "user.address.street.number": 123,
        "settings.theme.color": "dark",
        "settings.theme.font.size": 12,
        "settings.theme.font.family": "Arial"
    }
  • filter(keys) - Фильтрация данных
  • Lua:
    local user = jbp.create({
        username = "john_doe",
        password = "secret123",
        email = "john@example.com",
        profile = {
            age = 25,
            avatar = "photo.jpg",
            private = {
                phone = "1234567890",
                notes = "personal info"
            }
        },
        settings = {
            newsletter = true,
            theme = "dark"
        }
    })
    
    -- Оставляем только публичные данные
    local public = user:filter({"username", "profile", "settings"})
    print(public:to_json(true))
    -- Результат:
    {
        "username": "john_doe",
        "profile": {
            "age": 25,
            "avatar": "photo.jpg"
        },
        "settings": {
            "newsletter": true,
            "theme": "dark"
        }
    }
  • clear() - Очистка всех данных
  • Lua:
    local data = jbp.create({
        name = "John",
        age = 25,
        scores = {100, 200, 300}
    })
    
    print(data:to_json())
    -- Вывод: {"name":"John","age":25,"scores":[100,200,300]}
    
    data:clear()
    print(data:to_json())
    -- Вывод: {}
  • clone() - Создание глубокой копии
  • Lua:
    local original = jbp.create({
        player = {
            stats = {
                health = 100,
                mana = 50
            },
            inventory = {"Sword", "Shield"}
        }
    })
    
    local copy = original:clone()
    copy:set("player.stats.health", 80)
    copy:set("player.inventory[1]", "Axe")
    
    print("Original health:", original:get("player.stats.health"))  -- 100
    print("Copy health:", copy:get("player.stats.health"))         -- 80
    print("Original weapon:", original:get("player.inventory[1]"))  -- "Sword"
    print("Copy weapon:", copy:get("player.inventory[1]"))         -- "Axe"
  • merge(other) - Объединение объектов
  • Lua:
    local base_config = jbp.create({
        graphics = {
            quality = "medium",
            resolution = "1920x1080"
        },
        audio = {
            volume = 80
        }
    })
    
    local user_config = {
        graphics = {
            quality = "high",
            vsync = true
        },
        audio = {
            music = true
        }
    }
    
    base_config:merge(user_config)
    print(base_config:to_json(true))
    -- Результат:
    {
        "graphics": {
            "quality": "high",
            "resolution": "1920x1080",
            "vsync": true
        },
        "audio": {
            "volume": 80,
            "music": true
        }
    }
  • from_json(json_string) - Создание из JSON строки
  • Lua:
    local json_str = [[
    {
        "name": "John",
        "stats": {
            "level": 10,
            "experience": 1500
        },
        "achievements": ["Winner", "Explorer"]
    }
    ]]
    
    local obj, error = jbp.from_json(json_str)
    if obj then
        print("Name:", obj:get("name"))
        print("Level:", obj:get("stats.level"))
        print("First achievement:", obj:get("achievements[1]"))
    else
        print("Error:", error)
    end
    -- Вывод:
    -- Name: John
    -- Level: 10
    -- First achievement: Winner
  • to_json(pretty, indent) - Преобразование в JSON строку
  • Lua:
    local data = jbp.create({
        player = {
            name = "Hero",
            stats = {
                level = 30,
                class = "Warrior"
            },
            inventory = {"Sword", "Shield"}
        }
    })
    
    -- Компактный вывод
    print(data:to_json())
    -- Вывод: {"player":{"name":"Hero","stats":{"level":30,"class":"Warrior"},"inventory":["Sword","Shield"]}}
    
    -- Форматированный вывод
    print(data:to_json(true, 2))
    -- Вывод:
    {
      "player": {
        "name": "Hero",
        "stats": {
          "level": 30,
          "class": "Warrior"
        },
        "inventory": [
          "Sword",
          "Shield"
        ]
      }
    }
  • remove(path) - Удаление по пути
  • Lua:
    local data = jbp.create({
        user = {
            name = "John",
            contacts = {
                email = "john@example.com",
                phone = "1234567890"
            },
            settings = {
                theme = "dark",
                notifications = true
            }
        }
    })
    
    -- Удаление отдельного поля
    data:remove("user.contacts.phone")
    
    -- Удаление вложенного объекта
    data:remove("user.settings")
    
    print(data:to_json(true))
    -- Результат:
    {
        "user": {
            "name": "John",
            "contacts": {
                "email": "john@example.com"
            }
        }
    }
4. Сравнение и отслеживание
  • diff(other) - Поиск различий
  • Lua:
    local config_v1 = jbp.create({
        graphics = {
            quality = "high",
            resolution = "1920x1080",
            vsync = false
        },
        audio = {
            volume = 80,
            music = true
        },
        controls = {
            sensitivity = 1.0
        }
    })
    
    local config_v2 = jbp.create({
        graphics = {
            quality = "ultra",
            resolution = "1920x1080",
            vsync = true
        },
        audio = {
            volume = 90,
            music = false
        }
    })
    
    local diff = config_v1:diff(config_v2)
    print(diff:to_json(true))
    -- Результат:
    {
        "graphics.quality": {
            "old": "high",
            "new": "ultra",
            "status": "changed"
        },
        "graphics.vsync": {
            "old": false,
            "new": true,
            "status": "changed"
        },
        "audio.volume": {
            "old": 80,
            "new": 90,
            "status": "changed"
        },
        "audio.music": {
            "old": true,
            "new": false,
            "status": "changed"
        },
        "controls": {
            "old": {
                "sensitivity": 1.0
            },
            "new": null,
            "status": "removed"
        }
    }
  • watch(callback) - Отслеживание изменений
  • Lua:
    local settings = jbp.create({
        volume = 50,
        brightness = 0.8
    })
    
    settings:watch(function(change)
        print(string.format("Changed %s: %s -> %s at %s",
            change.key,
            tostring(change.old_value),
            tostring(change.new_value),
            os.date("%H:%M:%S", change.timestamp)
        ))
    end)
    
    settings.volume = 75
    -- Вывод: Changed volume: 50 -> 75 at 14:30:45
    
    settings.brightness = 1.0
    -- Вывод: Changed brightness: 0.8 -> 1.0 at 14:30:46
5. Валидация
  • validate(schema) - Валидация по схеме
  • Lua:
    local player = jbp.create({
        name = "John",
        level = 25,
        stats = {
            health = 100,
            mana = 50
        },
        inventory = ["Sword", "Shield"],
        settings = {
            difficulty = "hard"
        }
    })
    
    local schema = {
        type = "object",
        properties = {
            name = {type = "string", required = true},
            level = {type = "number", min = 1, max = 100},
            stats = {
                type = "object",
                properties = {
                    health = {type = "number", min = 0, max = 100},
                    mana = {type = "number", min = 0, max = 100}
                }
            },
            inventory = {type = "array"},
            settings = {
                type = "object",
                properties = {
                    difficulty = {type = "string", enum = {"easy", "normal", "hard"}}
                }
            }
        }
    }
    
    local valid, error = player:validate(schema)
    print(valid and "Valid" or error)
    -- Вывод: Valid
    
    -- Пример с ошибкой
    player:set("level", 150)
    valid, error = player:validate(schema)
    print(error)
    -- Вывод: Value 150 greater than maximum 100
6. Файловые операции
  • save_to_file(filepath, pretty, indent) - Сохранение с форматированием и без.
  • Lua:
    local game_state = jbp.create({
        player = {
            name = "Hero",
            level = 30,
            inventory = {"Sword", "Shield", "Potion"},
            position = {x = 100, y = 200, z = 300}
        },
        world = {
            time = "day",
            weather = "sunny",
            enemies = 10
        }
    })
    
    -- Сохранение с форматированием
    
    ---@param filepath string Путь к файлу
    ---@param pretty boolean|nil Форматировать ли вывод
    ---@param indent number|nil Размер отступа при форматиров
    ---@return boolean success Успешно ли сохранение
    ---@return string|nil error Текст ошибки в случае неудачи
    game_state:save_to_file("save.json", true, 2)
    -- Результат в файле save.json:
    {
      "player": {
        "name": "Hero",
        "level": 30,
        "inventory": [
          "Sword",
          "Shield",
          "Potion"
        ],
        "position": {
          "x": 100,
          "y": 200,
          "z": 300
        }
      },
      "world": {
        "time": "day",
        "weather": "sunny",
        "enemies": 10
      }
    }
  • load_from_file(filepath) - Загрузка json из файла
  • Lua:
    local loaded = jbp.load_from_file("save.json")
    if loaded then
        print("Player name:", loaded:get("player.name"))
        print("World time:", loaded:get("world.time"))
    end
В этом спойлере описаны методы и примеры которые позволяют работать с любыми таблицами и строками json в луа. При этом, нет зависимости от создания новой таблицы через библиотеку, для использования вложенных методов, по типу table:method* table:get:set.

1. Прямая работа с JSON​

Традиционный подход:
Код:
local t = {}
local success = t:decode(json_string)
if not success then
    print("Ошибка")
end
Новый подход с JBP:
Lua:
local data, err = jbp.decode(json_string)
if data then
    print("name:", data.name)
else
    print("error:", err) -- Подробное описание ошибки
end
  • Явное возвращение ошибки
  • Нет необходимости создавать временную таблицу
  • Более информативные сообщения об ошибках

2. JSON Path Query​

JSONPath - это способ находить нужные данные в JSON файле, как будто вы ищете файл в папках на компьютере.
Lua:
-- Структура данных
local data = {
    store = {
        books = {
            {
                title = "Война и мир",
                price = 1000,
                categories = {"классика", "роман"}
            },
            {
                title = "Мастер и Маргарита",
                price = 800,
                categories = {"фэнтези", "классика"}
            }
        }
    }
}

-- Получение всех цен
local prices = jbp.path_query(data, "$.store.books[*].price")
-- Результат: [1000, 800]

-- Получение всех книг в категории "классика"
local classics = jbp.path_query(data, "$.store.books[*]")
  • Декларативный синтаксис
  • Меньше кода
  • Меньше вложенных циклов
  • Защита от nil

3. Валидация по схеме​

Валидирует данные в массиве. К примеру если age больше 150, выдаст ошибку с указанием на позицию в таблице. Этот способ так-же есть для обьекта jbp
Lua:
local schema = {
    type = "object",
    properties = {
        name = {type = "string", required = true},
        age = {type = "number", min = 0, max = 150},
        email = {type = "string", pattern = "^[%w.]+@[%w.]+$"}
    }
}

local user = {
    name = "Иван",
    age = 25,
    email = "ivan@mail.com"
}

local is_valid, err = jbp.validate(user, schema)
  • Декларативное описание схемы
  • Автоматическая проверка всех правил
  • Подробные сообщения об ошибках
  • Вложенная валидация объектов

4. JSON Pointer​

JSON Pointer - это более простой способ указать путь к данным в JSON. Он похож на путь к файлу, только использует "/" для разделения.
Lua:
local data = {
    users = {
        {
            name = "Иван",
            contacts = {
                email = "ivan@mail.com"
            }
        }
    }
}

-- Получение email первого пользователя
local email = jbp.pointer_get(data, "/users/0/contacts/email")
Lua:
{
  "магазин": {
    "книги": [
      {"название": "Гарри Поттер", "цена": 1000},
      {"название": "Властелин колец", "цена": 800}
    ]
  }
}

/магазин/книги/0/название - получить название первой книги
/магазин/книги/1/цена - получить цену второй книги

5. JSON Patch​

JSON Patch - Это как список инструкций "что и где изменить".
Lua:
local user = {
    name = "Иван",
    age = 25
}

local patch = {
    {op = "replace", path = "/age", value = 26},
    {op = "add", path = "/city", value = "Москва"}
}

local updated = jbp.patch(user, patch)
  1. Каждое изменение описывается операцией (op):
    • add - "добавить поле city со значением Москва"
    • replace - "заменить age на 26"
    • remove - "удалить поле old_field"
    • copy - "скопировать значение из name в username"
  2. Путь (path) указывает, где делать изменение:
    • /age - изменить поле age в корне
    • /user/name - изменить name внутри объекта user
    • /items/0 - изменить первый элемент массива items

6. Санитизация данных​

Это процесс очистки данных JSON от вредоносных или нежелательных элементов
Lua:
local dirty_data = {
    inf = math.huge,
    nan = 0/0,
    func = function() end,
    valid = "это останется",
    nested = {
        bad = math.huge,
        good = 42
    }
}

local clean = jbp.sanitize(dirty_data)
  • Автоматическая очистка недопустимых значений
  • Рекурсивная обработка вложенных таблиц
  • Сохранение структуры данных
Почему иногда : перед функцией, а иногда .
:
Используется для вложенных методов для обьекта jbp.
Lua:
local basic = jbp.create()
basic:set("name", "John")
basic:set("age", 25)
basic:set("items", {1, 2, 3})
basic:save_to_file("data.json")
. Используется для любых таблиц и строк.
Lua:
local pretty_json = [[
{
"name": "John",
"age": 30,
"city": "New York"
}
]]
local compressed = jbp.compress(pretty_json)

Создание и загрузка
:create(initial_data)Создает новый JSON объект с опциональными начальными данными
@param initial_data table|nil Начальные данные для JSON объекта
@return table JsonBuilder объект с методами для работы с JSON
.from_json(json_string)Создает объект из JSON строки
@param json_string string JSON строка
@return table|nil builder JsonBuilder объект или nil при ошибке
@return string|nil error Текст ошибки в случае неудачи
.load_from_file(filepath)Загружает JSON объект из файла
@param filepath string Путь к файлу
@return table|nil builder JsonBuilder объект или nil при ошибке
@return string|nil error Текст ошибки в случае неудачи
Получение и установка значений
:set(path, value)Устанавливает значение по указанному пути (поддерживает вложенные пути)
@param path string Путь к значению (например "user.name" или "items[1]")
@param value any Значение для установки
@return table self Текущий объект для цепочки вызовов
:get(path, default)Получает значение по пути, возвращает default если путь не найден
@param path string Путь к значению
@param default any Значение по умолчанию, если путь не найден
@return any value Найденное значение или default
:exists(path)Проверяет существование значения по указанному пути
@param path string Путь для проверки
@return boolean exists Существует ли значение
:remove(path)Удаляет значение по указанному пути
@param path string Путь к удаляемому значению
@return table self Текущий объект для цепочки вызовов

:clear()Очищает все данные объекта
@return table self Текущий объект для цепочки вызовов
:clone()Создает глубокую копию объекта
@return table copy Копия текущего объекта
:merge(other)Объединяет текущий объект с другим
@param other table Таблица для объединения
@return table self Текущий объект для цепочки вызовов
:keys()Возвращает массив ключей верхнего уровня
@return table keys Массив ключей

Трансформации
:transform(fn)Применяет функцию преобразования ко всем значениям
@param fn function Функция для преобразования значений
@return table transformed Преобразованный объект
:filter(keys)Создает новый объект только с указанными ключами
@param keys table Массив ключей для сохранения
@return table filtered Отфильтрованный объект
:flatten()Преобразует вложенный объект в плоскую структуру с точечной нотацией
@return table flattened Уплощенный объект
.compressПроизводит сжатие таблицы
@param json_str string JSON строка для минификации
@return string|nil minified Минифицированная строка или nil при ошибке
@return string|nil error Текст ошибки в случае неудачи
.sanitize(value)Очищает значение от недопустимых для JSON значений
@param value any Значение для очистки
@return any sanitized Очищенное значение
.path(data, path)Применяет операции JSON Patch (RFC 6902)
@param data table Данные для изменения
@param patch table Массив операций patch
@return table|nil result Измененные данные или nil при ошибке
@return string|nil error Текст ошибки в случае неудачи


Валидация и сравнение
:validate(schema)Проверяет объект на соответствие схеме
@param value any Данные для валидации
@param schema table Схема для валидации
@return boolean valid Валидны ли данные
@return string|nil error Сообщение об ошибке если не валидны
:diff(other)Находит различия между текущим и другим объектом
@param other table Объект для сравнения
@return table diff Объект с различиями
:watch(callback)Отслеживает изменения в объекте через callback
@param callback function Функция вызываемая при изменениях

Сериализация
:to_json(pretty, indent)Преобразует объект в JSON строку с опциональным форматированием
@param pretty boolean|nil Форматировать ли вывод
@param indent number|nil Размер отступа при форматировании
@return string json_string JSON представление объекта
:save_to_file(filepath, pretty, indent)Сохраняет объект в файл в формате JSON
@param filepath string Путь к файлу
@param pretty boolean|nil Форматировать ли вывод
@param indent number|nil Размер отступа при форматировании
@return boolean success Успешно ли сохранение
@return string|nil error Текст ошибки в случае неудачи

Кодирование и декодирование
.encode(data, format: bool)Кодирование, стандартная функция. True False для форматирования.
.decode(data)Декодирование, стандартная функция

Навигация по данным
.path_query(data, path)Поиск по выражению
@param data table Данные для поиска
@param path string JSON Path выражение (например: $.store.book[*].author)
@return table|nil result Найденные значения или nil при ошибке
@return string|nil error Текст ошибки в случае неудачи
.pointer_get(data, pointer)Получить конкретные данные
@param data table Данные для поиска
@param pointer string JSON Pointer (например: /store/book/0/author)
@return any|nil value Найденное значение или nil если не найдено
@return string|nil error Текст ошибки в случае неудачи
Внимание, для сериализации cdata откройте спойлер "Работа с cdata".
1. Почему нужен специальный подход:
  • JSON не может хранить cdata типы
  • Нужно сохранять и тип, и значение указателя
  • При загрузке нужно восстанавливать оригинальный тип
2. Как использовать:
  • Lua:
    -- Сохранение
    basic:set_pointer("car", vehicle)  -- vehicle это cdata
    
    -- Загрузка
    local car = loaded:get_pointer("car")  -- получаем с оригинальным типом
3. Ограничения:
  • Указатели работают только в текущей сессии*
  • После перезапуска игры нужно получать новые указатели
  • Нельзя использовать сохраненные указатели между сессиями
4. Примеры использования:
Lua:
local jbp = require 'jbp'
local ffi = require 'ffi'

local samem = require "SAMemory"
samem.require "CAutomobile"

function main()
    if not isSampLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end

    local basic = jbp.create()

    -- Сохранение машины
    if isCharInAnyCar(playerPed) then
        local car = storeCarCharIsInNoSave(playerPed)
        if car then
            local CarPointer = getCarPointer(car)
            if CarPointer ~= 0 then
                local vehicle = ffi.cast("CAutomobile*", CarPointer)
                basic:set_pointer("vehicle", vehicle)
                basic:save_to_file("moonloader/config/save.json", true, 2)
                print("Saved")
            end
        end
    end

    -- Загрузка и тест
    wait(100)
    local loaded = jbp.load_from_file("moonloader/config/save.json")
    if loaded then
        local vehicle = loaded:get_pointer("vehicle")
        if vehicle then
            vehicle.wheelOffsetZ[0] = 0.5
            vehicle.wheelOffsetZ[1] = 0.5
            vehicle.wheelOffsetZ[2] = 0.5
            vehicle.wheelOffsetZ[3] = 0.5
            print("Modified wheels")
        end
    end

    while true do
        wait(0)
    end
end
5. Как выглядит сохраненный файл:
  • JSON:
    {
      "vehicle":{
        "type":"ctype<struct CAutomobile *>",
        "pointer":480169768
      }
    }
-- Поддерживаемые типы Imgui
  • ImBool - Булево значение
  • ImInt - Целое число
  • ImFloat - Число с плавающей точкой
  • ImBuffer - Текстовый буфер
  • ImVec2 - 2D вектор (x, y)
  • ImVec4 - 4D вектор (x, y, z, w)
Функции возвращают обьекты imgui, что позволяет вам работать с ними, как с обычным обьектом imgui:
Lua:
local config = jbp.create()

-- Для стандартного ImGui
jbp.pointer_set(config, "Imgui/Window/visible", imgui_default.ImBool(false))

--Сохраняем в конфиг
config:save_to_file("moonloader/config/test.json", true, 2)

-- Загружаем конфиг
local loaded = jbp.load_from_file("moonloader/config/test.json")

--Через .pointer_get:
if jbp.pointer_get(loaded, "Imgui/Window/visible").v == false then
print("Visible Pointer = false")
end

--Через стандартный :get
if loaded:get('Imgui.Window.visible').v == false then
print("Visible get = false")
end
Пример работы с Imgui:
Lua:
local imgui = require 'imgui'
local jbp = require 'jbp'
local data = jbp.create()


-- Сохранение

--Можно использовать как :set, так и jbp.pointer_set(data, "/checkbox", imgui.ImBool(true))

data:set("checkbox", imgui.ImBool(true))
data:set("slider", imgui.ImInt(50))
data:set("input", imgui.ImBuffer(256, "Текст"))
data:set("position", imgui.ImVec2(100, 100))
data:set("color", imgui.ImVec4(1, 0, 0, 1))
data:save_to_file("moonloader/config/test.json", true, 2) -- True включить форматирование, 2 - размер отступа


-- Загрузка
local data = jbp.load_from_file("moonloader/config/test.json")

--Так-же можно использовать jbp.pointer_get(data, '/checkbox')

local checkbox = data:get("checkbox") -- imgui.ImBool
local slider = data:get("slider") -- imgui.ImInt
local input = data:get("input") -- imgui.ImBuffer
local pos = data:get("position") -- imgui.ImVec2
local color = data:get("color") -- imgui.ImVec4
Поддерживаемые типы Mimgui:
  • bool[1] - Булево значение
  • int[1] - Целое число
  • float[1] - Число с плавающей точкой
  • char[] - Текстовый буфер
  • ImVec2 - 2D вектор
  • ImVec4 - 4D вектор
Функции возвращают обьекты mimgui, что позволяет вам работать с ними, как с обычным обьектом mimgui:
Lua:
local jbp = require 'jbp'
local imgui = require 'mimgui'
local config = jbp.create()

local config = jbp.create()

-- pointer_set
jbp.pointer_set(config, "Mimgui/Window/visible", new.bool(true))
jbp.pointer_set(config, "Mimgui/Window/position", imgui.ImVec2(100, 100))
jbp.pointer_set(config, "Mimgui/Window/vectors/vector4D", imgui.ImVec4(100, 100, 100, 100))
jbp.pointer_set(config, "Mimgui/Window/input_text", new.char[256]('hello world'))

--Или стандартное set
config:set("Mimgui.Bools.Visible", new.bool(true))
config:set("Mimgui.Ints.SelectedTab", new.int(0))
config:set("Mimgui.Window.position", imgui.ImVec2(100, 100))
config:set("Mimgui.Window.vectors/vector4D", imgui.ImVec4(100, 100, 100, 100))
config:set("Mimgui.Window.Chars.input_text", new.char[256]('hello world'))

--Сохраняем в файл
config:save_to_file("moonloader/config/test.json", true, 2)

-- Загружаем конфиг
local loaded = jbp.load_from_file("moonloader/config/test.json")

--Дальнейшие манипуляции, как с обычными обьектами mimgui
if jbp.pointer_get(loaded, "Mimgui/Window/visible")[0] == true then
    print("Visible Pointer = true")
end

-- Для получения значения из буфера MImGui
local buffer = jbp.pointer_get(loaded, "Mimgui/Window/input_text")
if buffer then
    local text = ffi.string(buffer) -- Получаем строку из буфера
    print("Buffer text:", text)
end

--Через стандартный :get
if loaded:get('Mimgui.Window.visible')[0] == true then
print("Visible get = true")
end

Через присваивание переменной, а затем её использование:

--Для мимгуи:
local VisibleMimgui = loaded:get("Mimgui.Window.visible")[0];
JSON:
{
  "Imgui":{
    "Bools":{
      "ClosedMenu":{
        "__type":"imgui_bool",
        "value":false
      }
    },
    "Buffers":{
      "InputText":{
        "__type":"imgui_buffer",
        "value":"hello world"
      }
    },
    "Ints":{
      "SelectedTab":{
        "__type":"imgui_int",
        "value":0
      }
    },
    "Window":{
      "input_text":{
        "__type":"imgui_buffer",
        "value":"hello world"
      },
      "position":{
        "__type":"imgui_vec2",
        "x":0,
        "y":0
      },
      "size":{
        "__type":"imgui_vec2",
        "x":800,
        "y":600
      },
      "visible":{
        "__type":"imgui_bool",
        "value":false
      }
    }
  },
  "Mimgui":{
    "Bools":{
      "Visible":{
        "__type":"bool",
        "value":true
      }
    },
    "Ints":{
      "SelectedTab":{
        "__type":"int",
        "value":0
      }
    },
    "Strs":{
      "InputText":{
        "__type":"mimgui_buffer",
        "value":"hello world"
      }
    },
    "Window":{
      "input_text":{
        "__type":"mimgui_buffer",
        "value":"hello world"
      },
      "position":{
        "__type":"vec2",
        "x":100,
        "y":100
      },
      "visible":{
        "__type":"bool",
        "value":true
      },
      "vectors":{
        "vector4D":{
          "__type":"vec4",
          "x":100,
          "y":100,
          "z":100,
          "w":100
        }
      }
    }
  }
}

Пример 1: Работа с API​

Lua:
-- Получение данных из API
local response = [[{"users":[{"id":1,"name":"Иван"},{"id":2,"name":"Мария"}]}]]

-- Декодирование и валидация
local data = jbp.decode(response)
local users = jbp.path_query(data, "$.users[*].name")
print("users:", table.concat(users, ", "))

Пример 2: Сохранение конфигурации​

Lua:
local config = {
    window = {
        width = 800,
        height = 600,
        position = {x = 100, y = 100}
    },
    settings = {
        volume = 0.8,
        fullscreen = true
    }
}

-- Сохранение с форматированием
local success = jbp.encode(config, true, 2)

-- Загрузка отдельных настроек
local volume = jbp.pointer_get(config, "/settings/volume")

Пример 3: Обновление данных​

Lua:
local user_data = {
    name = "Иван",
    settings = {
        notifications = true,
        theme = "dark"
    }
}

-- Применение нескольких изменений
local patches = {
    {op = "replace", path = "/settings/theme", value = "light"},
    {op = "add", path = "/settings/language", value = "ru"}
}

local updated = jbp.patch(user_data, patches)

Пример 4: Поиск похожих значений​

Lua:
local data = {
    ["Иван Петров"] = "программист",
    ["Петр Иванов"] = "дизайнер",
    ["Иван Сидоров"] = "менеджер",
    ["Сидор Иванов"] = "программист"
}

-- Функция поиска похожих значений
function find_similar(query, data, by_key)
    local results = {}
    query = query:lower() -- приводим к нижнему регистру для поиска
 
    for k, v in pairs(data) do
        local search_text = by_key and k or v
        search_text = tostring(search_text):lower()
 
        if search_text:find(query) then
            table.insert(results, {key = k, value = v})
        end
    end
 
    return results
end

-- Пример использования
local query = "иван"
local results = find_similar(query, data, true) -- поиск по ключам
-- Результат:
-- {
--   {key = "Иван Петров", value = "программист"},
--   {key = "Иван Сидоров", value = "менеджер"}
-- }

-- Поиск по значению
local prof_query = "прог"
local prof_results = find_similar(prof_query, data, false)
-- Результат:
-- {
--   {key = "Иван Петров", value = "программист"},
--   {key = "Сидор Иванов", value = "программист"}
-- }

Пример 5: Сжатие​

Lua:
local pretty_json = [[
{
"name": "John",
"age": 30,
"city": "New York"
}
]]

local compressed = jbp.compress(pretty_json)

print("Original size:", #pretty_json)
print("Compressed size:", #compressed)
print("Compressed JSON:", compressed)

Продвинутый поиск с расстоянием Левенштейна​

Lua:
-- Функция расчета расстояния Левенштейна
function levenshtein_distance(str1, str2)
    str1, str2 = str1:lower(), str2:lower()
    local len1, len2 = #str1, #str2
    local matrix = {}
 
    for i = 0, len1 do
        matrix[i] = {[0] = i}
    end
    for j = 0, len2 do
        matrix[0][j] = j
    end
 
    for i = 1, len1 do
        for j = 1, len2 do
            local cost = str1:sub(i,i) == str2:sub(j,j) and 0 or 1
            matrix[i][j] = math.min(
                matrix[i-1][j] + 1,      -- удаление
                matrix[i][j-1] + 1,      -- вставка
                matrix[i-1][j-1] + cost  -- замена
            )
        end
    end
 
    return matrix[len1][len2]
end

-- Функция поиска похожих значений с учетом расстояния Левенштейна
function find_similar_advanced(query, data, max_distance)
    local results = {}
    query = query:lower()
    max_distance = max_distance or 3 -- максимальное допустимое расстояние
 
    for k, v in pairs(data) do
        local key_distance = levenshtein_distance(query, k)
        local value_distance = levenshtein_distance(query, tostring(v))
 
        if key_distance <= max_distance or value_distance <= max_distance then
            table.insert(results, {
                key = k,
                value = v,
                key_relevance = 1 - (key_distance / #query),
                value_relevance = 1 - (value_distance / #query)
            })
        end
    end
 
    -- Сортировка по релевантности
    table.sort(results, function(a, b)
        return math.max(a.key_relevance, a.value_relevance) >
               math.max(b.key_relevance, b.value_relevance)
    end)
 
    return results
end

-- Пример использования продвинутого поиска
local database = {
    ["Иван Петров"] = "программист",
    ["Петр Иванов"] = "дизайнер",
    ["Иван Сидоров"] = "менеджер",
    ["Сидор Иванов"] = "программист",
    ["Иван Иванов"] = "аналитик"
}

-- Поиск с опечаткой
local query = "Иван Петроф" -- опечатка в фамилии
local results = find_similar_advanced(query, database)

-- Вывод результатов с релевантностью
for i, result in ipairs(results) do
    print(string.format(
        "%s (%s) - релевантность: %.2f",
        result.key,
        result.value,
        math.max(result.key_relevance, result.value_relevance)
    ))
end

Поиск с учетом категорий​

Lua:
local categorized_data = {
    programmers = {
        ["Иван Петров"] = {
            position = "Senior Developer",
            skills = {"Lua", "Python", "C++"}
        },
        ["Петр Иванов"] = {
            position = "Junior Developer",
            skills = {"Lua", "JavaScript"}
        }
    },
    designers = {
        ["Анна Сидорова"] = {
            position = "UI Designer",
            skills = {"Figma", "Photoshop"}
        }
    }
}

-- Функция поиска по категориям
function search_in_categories(query, data, category)
    local results = {}
    query = query:lower()
 
    local function search_category(cat_data, cat_name)
        for name, info in pairs(cat_data) do
            if name:lower():find(query) then
                table.insert(results, {
                    name = name,
                    category = cat_name,
                    info = info
                })
            else
                -- Поиск по навыкам
                for _, skill in ipairs(info.skills) do
                    if skill:lower():find(query) then
                        table.insert(results, {
                            name = name,
                            category = cat_name,
                            info = info,
                            matched_skill = skill
                        })
                        break
                    end
                end
            end
        end
    end
 
    if category then
        if data[category] then
            search_category(data[category], category)
        end
    else
        for cat_name, cat_data in pairs(data) do
            search_category(cat_data, cat_name)
        end
    end
 
    return results
end

-- Примеры использования
-- Поиск по всем категориям
local results1 = search_in_categories("lua", categorized_data)
-- Найдет всех, кто знает Lua

-- Поиск только среди программистов
local results2 = search_in_categories("junior", categorized_data, "programmers")
-- Найдет только Junior Developer

-- Вывод результатов
for _, result in ipairs(results1) do
    print(string.format(
        "%s (%s) - %s",
        result.name,
        result.category,
        result.info.position
    ))
    if result.matched_skill then
        print("Найдено по навыку:", result.matched_skill)
    end
end


Рекомендации по использованию:
  1. Выбор метода доступа к данным:
    • Используйте path_query для сложных запросов и массивов
    • Используйте pointer_get для прямого доступа к известным путям
    • Используйте стандартный доступ для простых операций
    • Используйте сжатие для больших JSON
    • Избегайте излишней валидации для доверенных данных
    • Используйте схемы для критичных данных
    • Применяйте санитизацию для пользовательского ввода

1. Подключение библиотеки​

Lua:
local jbp = require 'jbp'

2. Создание и сохранение данных​

Lua:
-- Создаем новый объект
local config = jbp.create()

-- Сохраняем простые значения
config:set("name", "John")
config:set("health", 100)
config:set("weapons", {"Desert Eagle", "M4"})

-- Сохраняем вложенные данные
config:set("stats.strength", 80)
config:set("stats.agility", 75)

-- Сохраняем в файл
config:save_to_file("moonloader/config/my_config.json", true, 2)

3. Загрузка и использование данных​

Lua:
-- Загружаем файл
local data = jbp.load_from_file("moonloader/config/my_config.json")
if data then
    -- Получаем значения
    local name = data:get("name")                -- "John"
    local strength = data:get("stats.strength")  -- 80
    local weapons = data:get("weapons")          -- {"Desert Eagle", "M4"}
end

Разное:
Lua:
-- Создание с начальными данными
local profile = jbp.create({
    name = "John",
    level = 1,
    inventory = {"Pistol"}
})
Lua:
-- Значение по умолчанию
local money = profile:get("money", 0)  -- Вернёт 0 если money не найден
Lua:
-- Проверка существования
if profile:get("weapon.ammo") then
    -- Значение существует
end

1. Установка библиотеки​

Lua:
-- Скачайте файл jbp.lua и поместите его в папку moonloader/lib
-- В начале скрипта подключите библиотеку:
local jbp = require 'jbp'

2. Создание простого конфига​

Lua:
-- Создаем новый конфиг
local config = jbp.create()

-- Добавляем настройки
config:set("name", "Мой скрипт")
config:set("version", 1.0)
config:set("settings.enabled", true)
config:set("settings.hotkey", 0x42) -- клавиша B

-- Сохраняем в файл
config:save_to_file("moonloader/config/myscript.json")

3. Загрузка конфига​

Lua:
-- Создаем конфиг и загружаем из файла
local loaded = jbp.load_from_file("moonloader/config/myscript.json")
if not loaded then
    -- Если файл не существует, создаем стандартные настройки
    config:set("name", "Мой скрипт")
    config:set("version", 1.0)
    config:set("settings.enabled", true)
    config:set("settings.hotkey", 0x42)
    -- Сохраняем стандартные настройки
    config:save_to_file("moonloader/config/myscript.json")
end

4. Различные функции​

Lua:
-- Создание вложенных настроек
config:set("players.max", 50)
config:set("players.list[1].name", "Player1")
config:set("players.list[1].score", 100)
config:set("players.list[2].name", "Player2")
config:set("players.list[2].score", 200)

-- Получение значений
local maxPlayers = config:get("players.max") -- 50
local player1Name = config:get("players.list[1].name") -- "Player1"

-- Работа с массивами
local weapons = {0x1, 0x2, 0x3, 0x4}
config:set("weapons", weapons)

-- Слияние конфигов
local otherConfig = jbp.create({
    newSetting = true,
    extraValue = 123
})
config:merge(otherConfig)

-- Клонирование конфига
local configBackup = config:clone()

Внутри библиотеки используется стандартная cjson для базовых функций.
Прикрепил файл test_lua.lua с примерами работы с библиотекой.
Прикрепил файл gui_examples.lua с примерами Imgui и Mimgui используя данную библиотеку.

Обновлено: 09.01.2025 18:36
09.01.2025. 01:06: .sanitize, .decode, .encode, .compress, .validate, .path_query, .pointer_get, .path
09.01.2025. 18:36: Работа с cdata mimgui, userdata imgui
 

Вложения

  • test_lua.lua
    5.5 KB · Просмотры: 1
  • gui_examples.lua
    4.4 KB · Просмотры: 1
  • jbp.lua
    58.3 KB · Просмотры: 2
Последнее редактирование:

UBP

Известный
Автор темы
355
222
Последнее редактирование:

chromiusj

average yakuza perk user
Модератор
5,689
4,002
Я только начал разработку, буду пробовать. Создание темы мискликнул и не вписал все что хотел.
Скоро обновлю


Обновил, добавил сохранение cdata,
я вообще имел ввиду как будут храниться данные из инпутов mimgui, как в carbjsonconfig типа,но это тоже прикольно
 
  • Нравится
Реакции: UBP

UBP

Известный
Автор темы
355
222
я вообще имел ввиду как будут храниться данные из инпутов mimgui, как в carbjsonconfig типа,но это тоже прикольно
Я понял. Работаю над обновлением. Сейчас в планах реализовать сериализацию сложных структур ffi без reflection.
Насчёт мимгуи, это точно будет. Скоро
 
  • Нравится
Реакции: cloused2 и chromiusj