LUA скрипт Просто о простом. Таблицы и их мета версия.

Разработчик
Статус
Оффлайн
Регистрация
1 Сен 2018
Сообщения
1,599
Реакции[?]
881
Поинты[?]
116K
Вступление
Всем привет! Это вводная часть серии статей в которых рассказывается о простых вещах, которые бывает сложно понять еще более простым языком. Данная часть полностью посвящена таблицам, и их мета друзьям, и их использовании в ваших скриптах, или API в вашем приложении/чите.
От таблиц до метамодов
Таблицы
Как вы можете знать в Lua почти всё связано таблицами, они по своей природе довольно простые, и представляют из себя массив ключ → значение ( Пример в С++: std::map ). Ключом может выступать абсолютно любой объект в Lua, кроме nil, а значением могут выступать абсолютно любые типы ( и даже таблицы ).

Пример использования:

code_language.lua:
-- Можно установить ключи и значения на этапе инициализации
local someTable =  { key= "value", key2="another_value" }

-- Или после создания таблицы
-- Перезаписываем оригинальное значение
someTable.key = "not_orig_value"

-- Вы также можете итерировать ключи и значения в таблицах
for key, value in pairs(someTable) do
    -- Выводит все ключи и их значения
    print(key .. " = " .. value)
end
Мета-таблицы
Экспериментируя над таблицами в один момент вам захочется большего, и даже для этого придумали решение - это мета-таблицы! Мета-таблицы хранят пользовательские методы для контроля поведения таблиц, и при связывании с таблицей контролируют её поведение.
Например вы можете показать коллеге на наглядном примере кто он, из-за того что он выбрал JavaScript вместо Lua
Пример подобного в Lua:
code_language.lua:
-- Определяем мета-таблицу
local someMetaTable = {}

-- Переопределяем её стандартный метод __index
function someMetaTable.__index(t, key)
    -- Печатаем при попытке обращения к нашей таблице для получения значения
    print("Accessing key " .. key)
    -- Если ключ равен "Alexander", то возвращаем строку "gay"
    if key == "Alexander" then
        return "gay"
    end
  
    -- В ином случае возвращаем оригинальное значение для ключа
    return t[key]
end

local someTable = {}

-- Связываем нашу таблицу с мета-таблицей
setmetatable(someTable, someMetaTable)

-- Проверяем на правильность нашего кода
print(someTable.Alexander)
Отлично, коллега сидит в углу обиженный, но вы задаетесь вопросом - “Какие метамоды я могу определить?”, и даже на это у меня есть ответ!
Вот их список:
  • __index: Как было видно выше этот метамод вызывается, когда скрипт пытается получить доступ по ключу в таблице, но такого ключа не было найдено.
  • __newindex. Этот метамод вызывается когда пытается присвоить значение ключу в таблице, но ключ также не был найден.
  • __len: Метамод, который вызывается когда скрипт пытается получить длину через #
  • __pairs: Данный метамод вызывается когда Lua выполняет итерацию по таблице с помощью функции pairs()
  • __ipairs: Аналог pairs, но вместо pairs() → ipairs()
  • __tostring: Метамод, который вызывается когда скрипт пытается преобразовать таблицу в строку с помощью tostring()
Я хочу контролировать с помощью __index все обращения к таблице
Конечно это сделать достаточно легко, но любой контроль имеет негативные последствия, и этот не стаёт исключением, вы теряете производительность, из-за прохождение двух мета-таблиц.
Код:

code_language.lua:
local someMetaTable = {}
local myTable = {someKey = "someValue"}
function someMetaTable.__index(t, key)
    -- Пытаемся найти значение в таблице не вызывая __index
    local value = rawget(t,key)
    if value == nil then
        -- Получаем оригинальную мета таблицу
        local originalMetaTable = getmetatable(t)
        if originalMetaTable ~= nil and originalMetaTable.__index ~= nil then
            return originalMetaTable.__index(t,key)
    end
    -- Возвращаем значение, независимо от того, было ли оно найдено в таблице или в оригинальной метатаблице
    return value
end

-- Устанавливаем мета таблицу
setmetatable(myTable, someMetaTable)

-- Заставляем все обращения к ключам проходить через метаметод __index
setmetatable(myTable, myTable)
Использование в С++
Таблицы
Всё достаточно просто, привожу вам пример создания таблицы с комментариями:
C++:
int main()
{
// Создаем новый луа стейт
lua_State* pLuaState = luaL_newstate();

// Создаём новую таблицу в стеке
lua_newtable(pLuaState);

// Пушим ключ в стек
lua_pushstring(pLuaState, "Enq");
// Пушим значение в стек
lua_pushstring(pLuaState, "Where is the design?")
-- Устанавливаем пару в таблицу
lua_settable(pLuaState,-3)

lua_close(pLuaState);
}
Мета таблицы:
C++:
int SomeTableIndex( lua_State* pLuaState)
{
// Получаем ключ
const char* szKey = lua_tostring(pLuaState, -1);

// Сравниваем ключ с "Alexander"
    if( strstr(szKey, "Alexander" )
    {
        // Пушим значение
        lua_pushstring(pLuaState, "Gay");
        return 1;
    }

    // В ином случае возвращаем 300
    lua_pushinteger(pLuaState, 300);
    return 1;
}

int main()
{
// Создаем новый луа стейт
lua_State* pLuaState = luaL_newstate();

// Создаём новую таблицу в стеке
lua_newtable(pLuaState);

// Cоздаём вторую таблицу, чтобы использовать её как мета-таблицу
lua_newtable(pLuaState);

// Устанавливаем __index нашей функцией
lua_pushstring(pLuaState, SomeTableIndex);
lua_settable(pLuaState, -3);

// Связываем мета-таблицу с нашей таблицей
lua_setmetatable(pLuaState,-2);

}
Пример использования в читах мета-таблиц: Neverlose ( Netvars, globals etc ), Gamesense ( globals )

Заключение
В данном материале я попытался максимально коротко, и в тоже время понятным языком объяснить таблицы и мета-таблицы, надеюсь для вас это было полезно. Спасибо!

P.S: Возможно вас может заинтересовать канал, где можно узнать о выходе подобного заранее -
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
Сверху Снизу