Гайд Урок по регулярным выражениям и использование хука.

  • Автор темы Удалённый пользователь 341712
  • Дата начала
У

Удалённый пользователь 341712

Гость
Автор темы
Доброго времени суток, многие новички не знают как использовать регулярные выражения и хуки, в этом я вам помогу.
И так, приступим, допустим, мы хотим поймать сообщение и дать на него ответ, как это сделать:
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color--[[Цвет]], text--[[Текст]]) -- Этот хук ловит сообщения в чате
    lua_thread.create(function() -- создаем поток
        if text:find('test') then -- если найден текст "test" то
            wait(1000) -- отвечает за ожидание в размере секунды
            sampSendChat('Не тест') -- если в чате засветится сообщение test, то хук поймает данное сообщение, дальше скрипт подождет 1 секунду и выведет "Не тест"
        end
    end) -- закрываем поток
end

Ладно, мы научились ловить сообщения, но допустим у нас есть сообщение: - Fedr_Petrovich[36]: Хорошо, ты выполнил задание, твой отчет под номером 48.
И так, мы хотим достать данное сообщение, ник может быть другой, ИД тоже, так же нам нужно записать в переменную конечную цифру.
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color, text)
    if text:find('- (%w+_%w+)%[(%d+)%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).') then
        local nick, id, number = text:match('- (%w+_%w+)%[(%d+)%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).')
        sampAddChatMessage(nick..' ['..id..'] '..number)
    end
end

Сейчас я привел пример регулярных выражения, давай их изучим:

. -- Любой символ
%a -- Буква (только англ.!)
%A -- Любая буква (русская), символ, или цифра, кроме английской буквы
%c -- Управляющий символ
%d -- Цифра
%D -- Любая буква, или символ, кроме цифры
%l -- Буква в нижней раскладке (только англ.!)
%L -- Любая буква, символ, или цифра, кроме английской буквы в нижней раскладке
%p -- Символ пунктуации
%P -- Любая буква, символ, или цифра, кроме символа пунктуации
%s -- Символ пробел
%S -- Любая буква, символ, или цифра, кроме символа пробела
%u -- Буква в верхней раскладке (только англ.!)
%U -- Любая буква, символ, или цифра, кроме английской буквы в верхней раскладке
%w -- Любая буква, или цифра (только англ.!)
%W -- Любой символ, или буква (русская), кроме английской буквы, или цифры
%x -- Шестнадцатеричное число
%X -- Любая буква, или символ, кроме цифры, или английской буквы, используемой в записи шестнадцатеричного числа
%z -- Строковые параметры, содержащие символы с кодом 0

Так же элементы шаблона:

Элементом шаблона может быть:

•Одиночный символьный класс, который соответствует любому одиночному символу из заданного класса;
•Одиночный символьный класс, сопровождаемый '*', что соответствует 0 или большему количеству повторений символов из заданного класса. Эти элементы повторения будут всегда соответствовать самой длинной возможной последовательности.
•Одиночный символьный класс, сопровождаемый '+', что соответствует 1 или большему количеству повторений символов из заданного класса. Эти элементы повторения будут всегда соответствовать самой длинной возможной последовательности.
•Одиночный символьный класс, сопровождаемый '-', что также соответствует 0 или большему количеству повторений символов из заданного класса. В отличие от *, элементы повторения будут всегда соответствовать самой короткой возможной последовательности
•Одиночный символьный класс, сопровождаемый '?', что соответствует 0 или единственному вхождению символа из заданного класса
•Символ, сопровождаемый '^', что соответствует началу строки

Во всех регулярных выражениях мы использовали знак "+" после регулярки, это означает, что символ может быть не один, от 1 до беск.
В других случаях, когда мы будем использовать знак "*" после регулярки, это будет означать, что символ может быть от 0 до беск.

Пример: Мы ищем один символ - (%d), значит будет принимать только одну цифру.
А если мы будем использовать (%d+), то наш хук будет искать от 1-ой цифры.

В первом регулярном выражении -- (%w+_%w+) мы искали любой алфавитно-цифровые символы по типу: Nick_Nick, в случае, если совпадение будет являться таким: BomjGang, то регулярное выражение не сработает, по скольку мы указали знак "_".

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

Дальше мы записываем в переменные все, что хранится в наших регулярных выражениях используя match.

Когда переменные запишутся, мы получим такой текст в чате: Ник_Ник [Ид] номерОтчета

А если ник не равен данному регистру Ник_ник, что же делать? Во первых мы должны использовать другое регулярное выражение (.), оно будет искать один символ, в данной ситуации можно использовать 2 элемента шаблона -- * и +, т.е. (.*), или (.+), на ваше усмотрение.

А если мы хотим получить только номер шаблона? Это делается так:
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color, text)
    if text:find('- %w+_%w+%[%d+%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).') then
        local number = text:match('- %w+_%w+%[%d+%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).')
        sampAddChatMessage(number)
    end
end

Т.е. мы убрали скобки, значит регулярные выражения будут работать, но просто записать их нельзя будет, регулярное выражение не будет захватываться переменной.

И все те луа-профи сейчас заметили экранирование, мб всего понемногу?

Знак % является частью всего регулярного выражения, так же при помощи его, можно экранировать символы в регулярном выражении, т.е. мы хотим записать определенный текст в переменную, но после вывода появляется это: {FF83E9}Текст, чтобы это исправить мы должны экранировать данный HEX код необходимо провернуть кое какой трюк с экранированием:
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color, text)
    if text:find('Гос.Новости %[.*%] %{......%}(.*)') then
        local org = text:match('Гос.Новости %[.*%] %{......%}(.*)')
        sampAddChatMessage(org, -1)
    end
end

И так, допустим, у нас после Гос.Новости [МЧС] идет текст, но уже с другим цветом, в этом и фишка, что после того как ставится следующий HEX код сервером, то его необходимо экранировать, если мы хотим получить полноценное предложение без {FFFFFF}.
Когда мы использовали %{......%}, там 6 точек, это HEX код, т.е. он может быть любым, напомню, одна точка - один символ, по скольку наши точки под экранированием, то они учитываются, что мы получим: "Текст", а не: "{FFFFFF}Текст".

Два данных символа - "[" и "]" необходимо экранировать, будут использоваться данные регулярные выражения: (.*) -- любой текст, (%d+) -- любые цифры, на примере будет показано, как необходимо экранировать "[" и "]"
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color, text)
    if text:find('(.*)%[(%d+)%]: (.*)') then
        nick, id, message = text:match('(.*)%[(%d+)%]: (.*)')
        sampAddChatMessage(nick..'['..id..']: '..message, -1)
    end
end

Основное использование text:find - поиск шаблона внутри заданной строки, называемой строкой темы. Функция возвращает позицию, в которой она нашла шаблон. Самая простая форма шаблона - это слово, которое соответствует только своей копии. Например, шаблон «привет» будет искать подстроку «привет» внутри строки темы.

Основное использование text:match - поиск первого вхождения шаблона в строку. Возвращает захваченные значения. Для поиска можно использовать регулярные выражения.

На этом всё, удачи.
 
Последнее редактирование модератором:

winten

Потрачен
409
181
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Если бы ты еще показал, как без SAMP.lua это сделать, то еще круче было бы
Все равно от сф зависимости.
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color, text)
    if text:find('^Гос.Новости %[.*%] %{......%}(.*)') --[[^ означает начало строки]] then 
        local org = text:match('Гос.Новости %[.*%] %{......%}(.*)')
        sampAddChatMessage(org, -1)
    end
end
 

Izvinisb

Известный
Проверенный
964
597
И так, допустим, у нас после Гос.Новости [МЧС] идет текст, но уже с другим цветом, в этом и фишка, что после того как ставится следующий HEX код сервером, то его необходимо экранировать, если мы хотим получить полноценное предложение без {FFFFFF}.
пиздеж
 

_raz0r

t.me/sssecretway | ТГК: t.me/razor_code
Модератор
1,888
3,048
Все равно от сф зависимости.
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color, text)
    if text:find('^Гос.Новости %[.*%] %{......%}(.*)') --[[^ означает начало строки]] then
        local org = text:match('Гос.Новости %[.*%] %{......%}(.*)')
        sampAddChatMessage(org, -1)
    end
end
Lua:
local ffi = require 'ffi'

--HOOKS
local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)
ffi.cdef [[
    int VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]
function hook.new(cast, callback, hook_addr, size)
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end

function main()
    newHook = hook.new('void(__thiscall *)(void *this, int msgType, const char* prefix, const char* msg, uint32_t textColor, uint32_t prefixColor)', chatMsgHook, getModuleHandle('samp.dll') + 0x64010)
end

function chatMsgHook(this, msgType, prefix, msg, textColor, prefixColor)
     if ffi.string(msg:find('^Гос.Новости %[.*%] %{......%}(.*)')) then
        msg = msg:match('Гос.Новости %[.*%] %{......%}(.*)')
    end
    chatMsgHook(this, msgType, prefix, msg, textColor, prefixColor)
end
Примерно так без использования SAMPFUNCS (хук функции добавления сообщения)
UPD: конечно лучше еще преобразовать в нужные типы данных переменные, но мне лень и это просто пример
 

winten

Потрачен
409
181
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Lua:
local ffi = require 'ffi'

--HOOKS
local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)
ffi.cdef [[
    int VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]
function hook.new(cast, callback, hook_addr, size)
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end

function main()
    newHook = hook.new('void(__thiscall *)(void *this, int msgType, const char* prefix, const char* msg, uint32_t textColor, uint32_t prefixColor)', chatMsgHook, getModuleHandle('samp.dll') + 0x64010)
end

function chatMsgHook(this, msgType, prefix, msg, textColor, prefixColor)
     if ffi.string(msg:find('^Гос.Новости %[.*%] %{......%}(.*)')) then
        msg = msg:match('Гос.Новости %[.*%] %{......%}(.*)')
    end
    chatMsgHook(this, msgType, prefix, msg, textColor, prefixColor)
end
Примерно так без использования SAMPFUNCS (хук функции добавления сообщения)
UPD: конечно лучше еще преобразовать в нужные типы данных переменные, но мне лень и это просто пример
Пили гайд, я так не умею ((
 
У

Удалённый пользователь 341712

Гость
Автор темы
ну-ну, тот же текст: [Ошибка] -- [[красный цвет HEX кодом]] --[[Синий цвет HEX кодом]]Введите ник, теперь этот текст запиши в переменную и выведи в имгуи как пример
 

Fott

Простреленный
3,426
2,255
- Fedr_Petrovich[36]: Хорошо, ты выполнил задание, твой отчет под номером 48.
И так, мы хотим достать данное сообщение, ник может быть другой, ИД тоже, так же нам нужно записать в переменную конечную цифру.
Lua:
local samp = require 'lib.samp.events'

function samp.onServerMessage(color, text)
if text:find('- (%w+_%w+)%[(%d+)%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).') then
local nick, id, number = text:match('- (%w+_%w+)%[(%d+)%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).')
sampAddChatMessage(nick..' ['..id..'] '..number)
end
end
Список спец символов ^$()%.[]*+-?
 

imring

Ride the Lightning
Всефорумный модератор
2,355
2,516
Lua:
if text:find('- (%w+_%w+)%[(%d+)%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).') then
local nick, id, number = text:match('- (%w+_%w+)%[(%d+)%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+).')
Lua:
local start, en, nick, id, number = text:find('%- (%w+_%w+)%[(%d+)%]: Хорошо, ты выполнил задание, твой отчет под номером (%d+)%.') -- или сразу text:match
if start then
    -- code
end

гайд сделан так себе, лучше бы рассказал только про паттерн функи.
 

AnWu

Guardian of Order
Всефорумный модератор
4,687
5,164
Lua:
local ffi = require 'ffi'

--HOOKS
local hook = {hooks = {}}
addEventHandler('onScriptTerminate', function(scr)
    if scr == script.this then
        for i, hook in ipairs(hook.hooks) do
            if hook.status then
                hook.stop()
            end
        end
    end
end)
ffi.cdef [[
    int VirtualProtect(void* lpAddress, unsigned long dwSize, unsigned long flNewProtect, unsigned long* lpflOldProtect);
]]
function hook.new(cast, callback, hook_addr, size)
    local size = size or 5
    local new_hook = {}
    local detour_addr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffi.cast(cast, callback))))
    local void_addr = ffi.cast('void*', hook_addr)
    local old_prot = ffi.new('unsigned long[1]')
    local org_bytes = ffi.new('uint8_t[?]', size)
    ffi.copy(org_bytes, void_addr, size)
    local hook_bytes = ffi.new('uint8_t[?]', size, 0x90)
    hook_bytes[0] = 0xE9
    ffi.cast('uint32_t*', hook_bytes + 1)[0] = detour_addr - hook_addr - 5
    new_hook.call = ffi.cast(cast, hook_addr)
    new_hook.status = false
    local function set_status(bool)
        new_hook.status = bool
        ffi.C.VirtualProtect(void_addr, size, 0x40, old_prot)
        ffi.copy(void_addr, bool and hook_bytes or org_bytes, size)
        ffi.C.VirtualProtect(void_addr, size, old_prot[0], old_prot)
    end
    new_hook.stop = function() set_status(false) end
    new_hook.start = function() set_status(true) end
    new_hook.start()
    table.insert(hook.hooks, new_hook)
    return setmetatable(new_hook, {
        __call = function(self, ...)
            self.stop()
            local res = self.call(...)
            self.start()
            return res
        end
    })
end

function main()
    newHook = hook.new('void(__thiscall *)(void *this, int msgType, const char* prefix, const char* msg, uint32_t textColor, uint32_t prefixColor)', chatMsgHook, getModuleHandle('samp.dll') + 0x64010)
end

function chatMsgHook(this, msgType, prefix, msg, textColor, prefixColor)
     if ffi.string(msg:find('^Гос.Новости %[.*%] %{......%}(.*)')) then
        msg = msg:match('Гос.Новости %[.*%] %{......%}(.*)')
    end
    chatMsgHook(this, msgType, prefix, msg, textColor, prefixColor)
end
Примерно так без использования SAMPFUNCS (хук функции добавления сообщения)
UPD: конечно лучше еще преобразовать в нужные типы данных переменные, но мне лень и это просто пример
это не хук ракнета, а функции игры. тут этому хуку не место
 
  • Вау
Реакции: Fott