Эксклюзив RakLua — RakNet hooks, functions for MoonLoader [UPDATE 2.13 | OPEN-SOURCE]

Представляю вам RakLua.
RakLua — это новая библиотека, которая служит заменой событиям и функциям SAMPFUNCS для взаимодействия с RakNet'ом и BitStream — классом, позволяющим SAMPy отправлять/получать данные от сервера. Библиотека использует sol3 для биндинга C++ (самой библиотеки) и самого Lua. Библиотека включает почти все возможности взаимодействия с RakNet'ом и его событиями, а ключевым моментом является одновременная поддержка SAMP 0.3.7-R1, SAMP 0.3.7-R2, SAMP 0.3.7-R3-1, SAMP 0.3.7-R4-2 и SAMP 0.3.7-R5-1, а так же отсутствие зависимости от SAMPFUNCS и CLEO (потому что SAMPFUNCS не используется).

Содержание:


GitHub: Northn/RakLua
Версия RakLua: 2.13; состояние: Stable

Установка: файлы RakLuaDll.dll, RakLua.lua из архива RakLua2.1.zip, приложенного к теме, перетащить в папку *Корневая папка с игрой*/moonloader/lib

Использованные материалы: mod_s0beit_sa; sol3; RakHook
Отдельное спасибо: @FYP ; @RTD




Список имеющихся функций и событий
Важная заметка: вам может быть неудобно переписывать исходный код в формат RakLua, если это так, то прочтите
данный пост.

ОписаниеПрименение в RakLuaПрименение в SAMPFUNCS
Создание BitStream; удаление BitStream
Lua:
local bs = RakLuaBitStream.new()
-- Обратите внимание, что это новый класс, а не тот самый BitStream
-- Получить сам BitStream можно через bs:getBitStream()
-- Удаление недоступно, сборщик мусора сам удалит
Lua:
local bs = raknetNewBitStream()
raknetDeleteBitStream(bs)
Получение оффсета для чтения/записи данных
Lua:
local offset = bs:getReadOffset()
local offset = bs:getWriteOffset()
Lua:
raknetBitStreamGetReadOffset(bs)
raknetBitStreamGetWriteOffset(bs)
Установка оффсета для чтения/записи данных
Lua:
bs:setReadOffset(offset)
bs:setWriteOffset(offset)
Lua:
raknetBitStreamSetReadOffset(bs, offset)
raknetBitStreamSetWriteOffset(bs, offset)
Сброс указателя для чтения/записи данных
Lua:
bs:resetReadPointer()
bs:resetWritePointer()
Lua:
raknetBitStreamResetReadPointer(bs)
raknetBitStreamResetWritePointer(bs)
Игнор битов/байтов для чтения данных
Lua:
bs:ignoreBits(amount)
bs:ignoreBytes(amount)
-- Альтернатива (вспоминается проще)
bs:skipBits(amount)
bs:skipBytes(amount)
Lua:
raknetBitStreamIgnoreBits(bs, amount)
-- Игнор байтов не имеется
Получение количества использованных битов/байтов
и количества непрочитанных битов/байтов
Lua:
local value = bs:getNumberOfBitsUsed()
local value = bs:getNumberOfBytesUsed()
local value = bs:getNumberOfUnreadBits()
local value = bs:getNumberOfUnreadBytes()
Lua:
local value = raknetBitStreamGetNumberOfBitsUsed(bs)
local value = raknetBitStreamGetNumberOfBytesUsed(bs)
local value = raknetBitStreamGetNumberOfUnreadBits(bs)
-- Получение количества непрочитанных байтов недоступно
Чтение данных
Lua:
local value = bs:readBool() -- bool
local value = bs:readUInt(8/16/32)() -- uint8/16/32; bs:readUInt16()
local value = bs:readInt(8/16/32)() -- int8/16/32; bs:readInt16()
local value = bs:readFloat() -- float
local value = bs:readString(length) -- string
local value = bs:readEncoded(size) -- encodedString
local result = bs:readBuffer(dest, size)
Lua:
local value = raknetBitStreamReadBool(bs) -- bool
local value = raknetBitStreamReadInt8(bs) -- uint8
local value = raknetBitStreamReadInt16(bs) -- uint16
local value = raknetBitStreamReadInt32(bs) -- int32
local value = raknetBitStreamReadFloat(bs) -- float
local value = raknetBitStreamReadString(bs, length) -- string
local value = raknetBitStreamDecodeString(bs, size) -- encodedString
raknetBitStreamReadBuffer(bs, dest, size)
Запись значений
Lua:
bs:writeBool(value)
bs:writeUInt(8/16/32)(value) -- bs:writeUInt16(value)
bs:writeInt(8/16/32)(value) -- bs:writeInt16(value)
bs:writeFloat(value)
bs:writeString(value)
bs:writeEncoded(value)
bs:writeBuffer(dest, size)
bs:writeBitStream(bitStream)
Lua:
raknetBitStreamWriteBool(bs, value)
raknetBitStreamWriteInt8(bs, value)
raknetBitStreamWriteInt16(bs, value)
raknetBitStreamWriteInt32(bs, value)
raknetBitStreamWriteFloat(bs,value)
raknetBitStreamWriteString(bs, value)
raknetBitStreamEncodeString(bs, value)
raknetBitStreamWriteBuffer(bs, dest, size)
raknetBitStreamWriteBitStream(bs, bitStream)
Отправка RPC/пакетов;
Эмуляция входящих RPC/пакетов
Lua:
bs:sendRPC(rpcId)
bs:sendPacket()
bs:emulIncomingRPC(rpcId)
bs:emulIncomingPacket(packetİd)
Lua:
raknetSendRpc(rpcId, bs)
raknetSendBitStream(bs)
raknetEmulRpcReceiveBitStream(rpcId, bs)
raknetEmulPacketReceiveBitStream(packetId, bs)
Установка обработчиков на
входящие/исходящие RPC и пакеты
Lua:
RakLua.registerHandler(RakLuaEvents.INCOMING_RPC, func)
RakLua.registerHandler(RakLuaEvents.INCOMING_PACKET, func)
RakLua.registerHandler(RakLuaEvents.OUTGOING_RPC, func)
RakLua.registerHandler(RakLuaEvents.OUTGOING_PACKET, func)
Lua:
addEventHandler("onReceiveRPC", func)
addEventHandler("onReceivePacket", func)
addEventHandler("onSendRPC", func)
addEventHandler("onSendPacket", func)


СобытиеИндекс
Входящие RPC​
RakLuaEvents.INCOMING_RPC
Исходящие RPC​
RakLuaEvents.OUTGOING_RPC
Входящие пакеты​
RakLuaEvents.INCOMING_PACKET
Исходящие пакеты​
RakLuaEvents.OUTGOING_PACKET

Дополнительная функция: RakLua.getSampVersion(), возвращает индекс исходя из перечисления:
ОписаниеИндекс
SAMP не загружен (одиночная игра / SAMP не установлен)​
RakLuaSampVersions.SAMP_NOT_LOADED
Неизвестная версия SAMP (неподдерживаемая)​
RakLuaSampVersions.SAMP_UNKNOWN
Версия SAMP 0.3.7-R1
RakLuaSampVersions.SAMP_037_R1
Версия SAMP 0.3.7-R3-1
RakLuaSampVersions.SAMP_037_R3_1




Примеры эмуляции исходящих RPC/пакетов
Для начала, хочу подметить, что работа с BitStream производится через новый класс, предоставляемый этой библиотекой — RakLuaBitStream, однако вы можете получить оригинальный BitStream через local bitStream = bs:getBitStream(), но он, скорее всего, для вас является бесполезным.

Давайте начнём с простого: эмуляция отправки, например, команды.

Lua:
function send_rpc_command(text)
    local bs = RakLuaBitStream.new()
    bs:writeInt32(text:len())
    bs:writeString(text)
    if bs:sendRPC(50) --[[RPC_ServerCommand в 0.3.7-R3]] then
        print("RPC was sent!")
    else
        print("We couldn't send RPC.")
    end
end

Довольно просто, не так ли? Вы также можете убрать проверку на успешность отправки RPC, поскольку если вы всё правильно записали, то оно, вероятнее всего, отправится. Также обратите внимание, что мы не удаляем BitStream, нам этого не нужно делать, этим сам SAMP позаботится.

Можем отправить какое-либо сообщение просто в чат:

Lua:
function send_text(text)
    local bs = RakLuaBitStream.new()
    bs:writeUInt8(text:len())
    bs:writeString(text)
    if bs:sendRPC(101) --[[RPC_Chat в 0.3.7-R3]] then
        print("RPC was sent!")
    else
        print("We couldn't send RPC.")
    end
end

Или написать целый sampSendChat(text):
Lua:
function send_rpc_command(text)
    local bs = RakLuaBitStream.new()
    bs:writeInt32(text:len())
    bs:writeString(text)
    return bs:sendRPC(50) --[[RPC_ServerCommand в 0.3.7-R3]]
end

function send_text(text)
    local bs = RakLuaBitStream.new()
    bs:writeUInt8(text:len())
    bs:writeString(text)
    return bs:sendRPC(101) --[[RPC_Chat в 0.3.7-R3]]
end

function sampSendChat(text)
    text = tostring(text)
    return text:sub(1, 1) == "/" and send_rpc_command(text) or send_text(text)
end

Мы также можем отправлять пакеты, достаточно лишь заполнить его и отправить:
Lua:
local bs = RakLuaBitStream.new()
bs:writeUInt8(id) -- ID пакета
-- И заполняем необходимыми данными
bs:sendPacket()




Отлавливание и обработка входящих/исходящих RPC и пакетов
Давайте хукнем, например, всеми известные onServerMessage, onSetPlayerHealth и onSetPlayerPos из входящих RPC:
Lua:
local RakLua = require 'RakLua'

function main()
    RakLua.registerHandler(RakLuaEvents.INCOMING_RPC,
        function(id, bs)
            if id == 93 then -- RPC_ClientMessage
                local color = bs:readInt32() -- Читаем цвет
                local textLen = bs:readInt32() -- Читаем длину текста
                local text = bs:readString(textLen) -- Читаем сам текст
                local res = onServerMessage(color, text) -- Вызываем свою функцию
                if type(res) == "boolean" then -- Если тип возвращаемого значения bool
                    return res -- Возвращаем значение, сам обработчик уже обработает/не обработает
                    -- исходя из возвращаемого значения
                elseif type(res) == "table" then
                    bs:resetWritePointer() -- Сбросим указатель записи, чтобы перезаписать всё
                    bs:writeInt32(res[1]) -- Запишем цвет
                    bs:writeInt32(res[2]:len()) -- Запишем длину текста
                    bs:writeString(res[2]) -- Запишем сам текст
                end
            elseif id == 14 then -- RPC_SetPlayerHealth
                local health = bs:readFloat() -- Получим количество HP, которое сервер
                -- Хочет нам установить
                if health < 5 then
                    return false
                end
            elseif id == 12 then -- RPC_SetPlayerPos
                local x, y, z = bs:readFloat(), bs:readFloat(), bs:readFloat()
                print(string.format("Server tried to set your position at %.2f; %.2f; %.2f", x, y, z))
                return false -- Проигнорируем телепортацию
            end
        end
    )
end

function onServerMessage(color, text)
    if color == -1 then
        return false
    elseif text == "hello" then
        return true
    end
    return {color, "hello"}
end

Мы написали обработчик, который при получении RPC ClientMessage будет вызывать функцию onServerMessage, при получении RPC SetPlayerHealth, если сервер попытается установить HP ниже пяти, будет игнорировать, и RPC SetPlayerPos, который будет всегда игнорировать попытку сервера телепортировать нас и будет писать в moonloader.log соответствующую информацию.

Давайте напишем обработчик на исходящие RPC:

Lua:
RakLua.registerHandler(RakLuaEvents.OUTGOING_RPC,
    function(id, bs, priority, reliability, orderingChannel, shiftTimestamp)
        if id == 101 then -- RPC_Chat
            local textLen = bs:readUInt8()
            local text = bs:readString(textLen)
            local res = onSendChat(text)
            if type(res) == "boolean" then
                return res
            elseif type(res) == "table" then
                bs:resetWritePointer()
                bs:writeUInt8(res[1]:len())
                bs:writeString(res[1])
            end
        elseif id == 50 then -- RPC_ServerCommand
            local cmdLen = bs:readInt32()
            local cmd = bs:readString(cmdLen)
            local res = onSendCommand(cmd)
            if type(res) == "boolean" then
                return res
            elseif type(res) == "table" then
                bs:resetWritePointer()
                bs:writeInt32(res[1]:len())
                bs:writeString(res[1])
            end
        end
    end
)

function onSendChat(text)
    if text == "hello" then
        text = "bye"
    elseif text == "privet" then
        return false
    end
    return {"[Префикс] "..text}
end

function onSendCommand(cmd)
    if cmd == "/help" then
        cmd = "/mm"
    elseif cmd == "/admins" then
        return false
    end
    return {cmd}
end

Обработчик, при получении RPC Chat; ServerCommand, будет вызывать соответствующие функции и исходя из их возвращаемых аргументов действовать.

Обработка исходящих пакетов:

Lua:
RakLua.registerHandler(RakLuaEvents.OUTGOING_PACKET,
    function(id, bs, priority, reliability, orderingChannel)
        if id == 207 then -- ID_PLAYER_SYNC
            bs:ignoreBytes(35) -- or bs:skipBytes(35); пропускаем 35 байт
            local hp, armor = bs:readUInt8(), bs:readUInt8()
            print(string.format("I sent %d HP; %d armor.", hp, armor))
        end
    end
)

Если отправляем Packet PLAYER_SYNC, то пропускаем первые 35 байт, читаем количество здоровья, брони и выводим в moonloader.log.




Вот и всё, мы научились работать с RakLua, возможно даже что и с BitStream, раз ранее не работали.
Также хочу отметить, что поддержка с SAMP.Lua в наличии, но только начиная с версии 2.0. Для совместимости с SAMP.Lua необходимо вызвать функцию RakLua.defineSampLuaCompatibility()
до подгрузки SAMP.Lua.
 

Вложения

  • RakLua.zip
    156.6 KB · Просмотры: 231
  • RakLua1.1.zip
    157.2 KB · Просмотры: 35
  • RakLua1.2.zip
    156.8 KB · Просмотры: 27
  • RakLua1.3.zip
    172.7 KB · Просмотры: 129
  • RakLua2.0.zip
    176.6 KB · Просмотры: 48
  • RakLua2.01.zip
    176.4 KB · Просмотры: 84
  • RakLua2.1.zip
    89 KB · Просмотры: 575
  • RakLua2.11.zip
    103.6 KB · Просмотры: 47
  • RakLua2.12.zip
    104.3 KB · Просмотры: 381
  • RakLua2.13.zip
    104.8 KB · Просмотры: 1,381
Последнее редактирование:

whyega

52NGG
Модератор
2,617
2,344
помогите плиз, у меня есть строка, которая выводит в чат сообщение с помощью sampAddChatMessage()
как мне проверять когда этот sampAddChatMessage() сработал и проверять что он вывел? пробовал некоторые способы и функции, но ничего не видит текст именно из sampAddChatMessage
Эта функция выводит локальный текст, никакие рпц и пакеты не связаны с ней (как я помню), можешь попробовать хукнуть ее через память сампа

всё верно
Кстати, если юзать bs:writeString(value) с переменной внутри, ее надо переводить в строку, даже если вводятся символы, хз баг это или просто особенность самого языка
 
Последнее редактирование:

#Northn

Pears Project — уже запущен!
Автор темы
Всефорумный модератор
2,649
2,499
Библиотека обновлена до версии 2.11. Изменения:
  • Исправлена несовместимость с плагином connd от @kin4stat

Обновление носит рекомендательный характер, если вы имеете проблемы с данным плагином -- можете обновиться.
Коммит: https://github.com/Northn/RakLua/commit/80b28ddbf930c7982416f5979873bd2486c7f0d5
 
  • Нравится
  • Вау
Реакции: qdIbp, _razor и whyega

#Northn

Pears Project — уже запущен!
Автор темы
Всефорумный модератор
2,649
2,499
Библиотека обновлена до версии 2.12. Изменения:
  • Исправлена поломка луастейта, которая могла при случайных обстоятельствах привести к бесконечному вызову main()

Обновление крайне рекомендуется к установке.
Коммит: https://github.com/Northn/RakLua/commit/d5e810989cc1f52e6fd7a8618bc13831eb1768d2
 
  • Клоун
  • Нравится
Реакции: Fott и whyega

whyega

52NGG
Модератор
2,617
2,344
Представляю вам RakLua.

RakLua — это новая библиотека, которая служит заменой событиям и функциям SAMPFUNCS для взаимодействия с RakNet'ом и BitStream — классом, позволяющим SAMPy отправлять/получать данные от сервера. Библиотека использует sol3 для биндинга C++ (самой библиотеки) и самого Lua. Библиотека включает почти все возможности взаимодействия с RakNet'ом и его событиями, а ключевым моментом является одновременная поддержка SAMP 0.3.7-R1, SAMP 0.3.7-R3-1 и SAMP 0.3.7-R4-2 (возможно расширение, вы только напишите о необходимости) и отсутствие зависимости от SAMPFUNCS и CLEO (потому что SAMPFUNCS не используется).

Содержание:

GitHub: Northn/RakLua
Версия RakLua: 2.12; состояние: Stable

Установка: файлы RakLuaDll.dll, RakLua.lua из архива RakLua2.1.zip, приложенного к теме, перетащить в папку *Корневая папка с игрой*/moonloader/lib

Использованные материалы: mod_s0beit_sa; sol3; RakHook
Отдельное спасибо: @FYP ; @RTD




Список имеющихся функций и событий
Важная заметка: вам может быть неудобно переписывать исходный код в формат RakLua, если это так, то прочтите данный пост.

ОписаниеПрименение в RakLuaПрименение в SAMPFUNCS
Создание BitStream; удаление BitStream
Lua:
local bs = RakLuaBitStream.new()
-- Обратите внимание, что это новый класс, а не тот самый BitStream
-- Получить сам BitStream можно через bs:getBitStream()
-- Удаление недоступно, сборщик мусора сам удалит
Lua:
local bs = raknetNewBitStream()
raknetDeleteBitStream(bs)
Получение оффсета для чтения/записи данных
Lua:
local offset = bs:getReadOffset()
local offset = bs:getWriteOffset()
Lua:
raknetBitStreamGetReadOffset(bs)
raknetBitStreamGetWriteOffset(bs)
Установка оффсета для чтения/записи данных
Lua:
bs:setReadOffset(offset)
bs:setWriteOffset(offset)
Lua:
raknetBitStreamSetReadOffset(bs, offset)
raknetBitStreamSetWriteOffset(bs, offset)
Сброс указателя для чтения/записи данных
Lua:
bs:resetReadPointer()
bs:resetWritePointer()
Lua:
raknetBitStreamResetReadPointer(bs)
raknetBitStreamResetWritePointer(bs)
Игнор битов/байтов для чтения данных
Lua:
bs:ignoreBits(amount)
bs:ignoreBytes(amount)
-- Альтернатива (вспоминается проще)
bs:skipBits(amount)
bs:skipBytes(amount)
Lua:
raknetBitStreamIgnoreBits(bs, amount)
-- Игнор байтов не имеется
Получение количества использованных битов/байтов
и количества непрочитанных битов/байтов
Lua:
local value = bs:getNumberOfBitsUsed()
local value = bs:getNumberOfBytesUsed()
local value = bs:getNumberOfUnreadBits()
local value = bs:getNumberOfUnreadBytes()
Lua:
local value = raknetBitStreamGetNumberOfBitsUsed(bs)
local value = raknetBitStreamGetNumberOfBytesUsed(bs)
local value = raknetBitStreamGetNumberOfUnreadBits(bs)
-- Получение количества непрочитанных байтов недоступно
Чтение данных
Lua:
local value = bs:readBool() -- bool
local value = bs:readUInt(8/16/32)() -- uint8/16/32; bs:readUInt16()
local value = bs:readInt(8/16/32)() -- int8/16/32; bs:readInt16()
local value = bs:readFloat() -- float
local value = bs:readString(length) -- string
local value = bs:readEncoded(size) -- encodedString
local result = bs:readBuffer(dest, size)
Lua:
local value = raknetBitStreamReadBool(bs) -- bool
local value = raknetBitStreamReadInt8(bs) -- uint8
local value = raknetBitStreamReadInt16(bs) -- uint16
local value = raknetBitStreamReadInt32(bs) -- int32
local value = raknetBitStreamReadFloat(bs) -- float
local value = raknetBitStreamReadString(bs, length) -- string
local value = raknetBitStreamDecodeString(bs, size) -- encodedString
raknetBitStreamReadBuffer(bs, dest, size)
Запись значений
Lua:
bs:writeBool(value)
bs:writeUInt(8/16/32)(value) -- bs:writeUInt16(value)
bs:writeInt(8/16/32)(value) -- bs:writeInt16(value)
bs:writeFloat(value)
bs:writeString(value)
bs:writeEncoded(value)
bs:writeBuffer(dest, size)
bs:writeBitStream(bitStream)
Lua:
raknetBitStreamWriteBool(bs, value)
raknetBitStreamWriteInt8(bs, value)
raknetBitStreamWriteInt16(bs, value)
raknetBitStreamWriteInt32(bs, value)
raknetBitStreamWriteFloat(bs,value)
raknetBitStreamWriteString(bs, value)
raknetBitStreamEncodeString(bs, value)
raknetBitStreamWriteBuffer(bs, dest, size)
raknetBitStreamWriteBitStream(bs, bitStream)
Отправка RPC/пакетов;
Эмуляция входящих RPC/пакетов
Lua:
bs:sendRPC(rpcId)
bs:sendPacket()
bs:emulIncomingRPC(rpcId)
bs:emulIncomingPacket(packetİd)
Lua:
raknetSendRpc(rpcId, bs)
raknetSendBitStream(bs)
raknetEmulRpcReceiveBitStream(rpcId, bs)
raknetEmulPacketReceiveBitStream(packetId, bs)
Установка обработчиков на
входящие/исходящие RPC и пакеты
Lua:
RakLua.registerHandler(RakLuaEvents.INCOMING_RPC, func)
RakLua.registerHandler(RakLuaEvents.INCOMING_PACKET, func)
RakLua.registerHandler(RakLuaEvents.OUTGOING_RPC, func)
RakLua.registerHandler(RakLuaEvents.OUTGOING_PACKET, func)
Lua:
addEventHandler("onReceiveRPC", func)
addEventHandler("onReceivePacket", func)
addEventHandler("onSendRPC", func)
addEventHandler("onSendPacket", func)


СобытиеИндекс
Входящие RPCRakLuaEvents.INCOMING_RPC
Исходящие RPCRakLuaEvents.OUTGOING_RPC
Входящие пакетыRakLuaEvents.INCOMING_PACKET
Исходящие пакетыRakLuaEvents.OUTGOING_PACKET

Дополнительная функция: RakLua.getSampVersion(), возвращает индекс исходя из перечисления:
ОписаниеИндекс
SAMP не загружен (одиночная игра / SAMP не установлен)RakLuaSampVersions.SAMP_NOT_LOADED
Неизвестная версия SAMP (неподдерживаемая)RakLuaSampVersions.SAMP_UNKNOWN
Версия SAMP 0.3.7-R1RakLuaSampVersions.SAMP_037_R1
Версия SAMP 0.3.7-R3-1RakLuaSampVersions.SAMP_037_R3_1




Примеры эмуляции исходящих RPC/пакетов
Для начала, хочу подметить, что работа с BitStream производится через новый класс, предоставляемый этой библиотекой — RakLuaBitStream, однако вы можете получить оригинальный BitStream через local bitStream = bs:getBitStream(), но он, скорее всего, для вас является бесполезным.

Давайте начнём с простого: эмуляция отправки, например, команды.
Lua:
function send_rpc_command(text)
    local bs = RakLuaBitStream.new()
    bs:writeInt32(text:len())
    bs:writeString(text)
    if bs:sendRPC(50) --[[RPC_ServerCommand в 0.3.7-R3]] then
        print("RPC was sent!")
    else
        print("We couldn't send RPC.")
    end
end

Довольно просто, не так ли? Вы также можете убрать проверку на успешность отправки RPC, поскольку если вы всё правильно записали, то оно, вероятнее всего, отправится. Также обратите внимание, что мы не удаляем BitStream, нам этого не нужно делать, этим сам SAMP позаботится.

Можем отправить какое-либо сообщение просто в чат:
Lua:
function send_text(text)
    local bs = RakLuaBitStream.new()
    bs:writeUInt8(text:len())
    bs:writeString(text)
    if bs:sendRPC(101) --[[RPC_Chat в 0.3.7-R3]] then
        print("RPC was sent!")
    else
        print("We couldn't send RPC.")
    end
end

Или написать целый sampSendChat(text):
Lua:
function send_rpc_command(text)
    local bs = RakLuaBitStream.new()
    bs:writeInt32(text:len())
    bs:writeString(text)
    return bs:sendRPC(50) --[[RPC_ServerCommand в 0.3.7-R3]]
end

function send_text(text)
    local bs = RakLuaBitStream.new()
    bs:writeUInt8(text:len())
    bs:writeString(text)
    return bs:sendRPC(101) --[[RPC_Chat в 0.3.7-R3]]
end

function sampSendChat(text)
    text = tostring(text)
    return text:sub(1, 1) == "/" and send_rpc_command(text) or send_text(text)
end

Мы также можем отправлять пакеты, достаточно лишь заполнить его и отправить:
Lua:
local bs = RakLuaBitStream.new()
bs:writeUInt8(id) -- ID пакета
-- И заполняем необходимыми данными
bs:sendPacket()




Отлавливание и обработка входящих/исходящих RPC и пакетов
Давайте хукнем, например, всеми известные onServerMessage, onSetPlayerHealth и onSetPlayerPos из входящих RPC:
Lua:
local RakLua = require 'RakLua'

function main()
    RakLua.registerHandler(RakLuaEvents.INCOMING_RPC,
        function(id, bs)
            if id == 93 then -- RPC_ClientMessage
                local color = bs:readInt32() -- Читаем цвет
                local textLen = bs:readInt32() -- Читаем длину текста
                local text = bs:readString(textLen) -- Читаем сам текст
                local res = onServerMessage(color, text) -- Вызываем свою функцию
                if type(res) == "boolean" then -- Если тип возвращаемого значения bool
                    return res -- Возвращаем значение, сам обработчик уже обработает/не обработает
                    -- исходя из возвращаемого значения
                elseif type(res) == "table" then
                    bs:resetWritePointer() -- Сбросим указатель записи, чтобы перезаписать всё
                    bs:writeInt32(res[1]) -- Запишем цвет
                    bs:writeInt32(res[2]:len()) -- Запишем длину текста
                    bs:writeString(res[2]) -- Запишем сам текст
                end
            elseif id == 14 then -- RPC_SetPlayerHealth
                local health = bs:readFloat() -- Получим количество HP, которое сервер
                -- Хочет нам установить
                if health < 5 then
                    return false
                end
            elseif id == 12 then -- RPC_SetPlayerPos
                local x, y, z = bs:readFloat(), bs:readFloat(), bs:readFloat()
                print(string.format("Server tried to set your position at %.2f; %.2f; %.2f", x, y, z))
                return false -- Проигнорируем телепортацию
            end
        end
    )
end

function onServerMessage(color, text)
    if color == -1 then
        return false
    elseif text == "hello" then
        return true
    end
    return {color, "hello"}
end

Мы написали обработчик, который при получении RPC ClientMessage будет вызывать функцию onServerMessage, при получении RPC SetPlayerHealth, если сервер попытается установить HP ниже пяти, будет игнорировать, и RPC SetPlayerPos, который будет всегда игнорировать попытку сервера телепортировать нас и будет писать в moonloader.log соответствующую информацию.

Давайте напишем обработчик на исходящие RPC:
Lua:
RakLua.registerHandler(RakLuaEvents.OUTGOING_RPC,
    function(id, bs, priority, reliability, orderingChannel, shiftTimestamp)
        if id == 101 then -- RPC_Chat
            local textLen = bs:readUInt8()
            local text = bs:readString(textLen)
            local res = onSendChat(text)
            if type(res) == "boolean" then
                return res
            elseif type(res) == "table" then
                bs:resetWritePointer()
                bs:writeUInt8(res[1]:len())
                bs:writeString(res[1])
            end
        elseif id == 50 then -- RPC_ServerCommand
            local cmdLen = bs:readInt32()
            local cmd = bs:readString(cmdLen)
            local res = onSendCommand(cmd)
            if type(res) == "boolean" then
                return res
            elseif type(res) == "table" then
                bs:resetWritePointer()
                bs:writeInt32(res[1]:len())
                bs:writeString(res[1])
            end
        end
    end
)

function onSendChat(text)
    if text == "hello" then
        text = "bye"
    elseif text == "privet" then
        return false
    end
    return {"[Префикс] "..text}
end

function onSendCommand(cmd)
    if cmd == "/help" then
        cmd = "/mm"
    elseif cmd == "/admins" then
        return false
    end
    return {cmd}
end

Обработчик, при получении RPC Chat; ServerCommand, будет вызывать соответствующие функции и исходя из их возвращаемых аргументов действовать.

Обработка исходящих пакетов:
Lua:
RakLua.registerHandler(RakLuaEvents.OUTGOING_PACKET,
    function(id, bs, priority, reliability, orderingChannel)
        if id == 207 then -- ID_PLAYER_SYNC
            bs:ignoreBytes(35) -- or bs:skipBytes(35); пропускаем 35 байт
            local hp, armor = bs:readUInt8(), bs:readUInt8()
            print(string.format("I sent %d HP; %d armor.", hp, armor))
        end
    end
)

Если отправляем Packet PLAYER_SYNC, то пропускаем первые 35 байт, читаем количество здоровья, брони и выводим в moonloader.log.




Вот и всё, мы научились работать с RakLua, возможно даже что и с BitStream, раз ранее не работали.
Также хочу отметить, что поддержка с SAMP.Lua в наличии, но только начиная с версии 2.0. Для совместимости с SAMP.Lua необходимо вызвать функцию RakLua.defineSampLuaCompatibility() до подгрузки SAMP.Lua.
под р5 будет обнова?
 

whyega

52NGG
Модератор
2,617
2,344
@#Northn, чекнул сурс, но чет не понял, эмулируя пакет нам надо вписывать Id пакета самому или оно само записывается из функции bs:emulIncomingPacket(id)?
Lua:
local bs = RakLuaBitStream.new()
bs:writeUInt8(id packet)
bs:emulIncomingPacket(id packet)

--  как правильно?

local bs = RakLuaBitStream.new()
bs:emulIncomingPacket(id packet)
 

#Northn

Pears Project — уже запущен!
Автор темы
Всефорумный модератор
2,649
2,499
@#Northn, чекнул сурс, но чет не понял, эмулируя пакет нам надо вписывать Id пакета самому или оно само записывается из функции bs:emulIncomingPacket(id)?
Lua:
local bs = RakLuaBitStream.new()
bs:writeUInt8(id packet)
bs:emulIncomingPacket(id packet)

--  как правильно?

local bs = RakLuaBitStream.new()
bs:emulIncomingPacket(id packet)
оно само записывается
 
  • Клоун
  • Нравится
Реакции: Fott и whyega

whyega

52NGG
Модератор
2,617
2,344
@#Northn , вроде ничего такого не сделал, а игра крашится. Пытался просто записать ид игрока, сдвинуться на позицию и записать ее, а после эмулировать - не получилось, попробовал заполнить фулл пакет - тоже. в чом дело может быть?
Lua:
function emul_packet(id) -- PLAYER_SYNC
    local bs = RakLuaBitStream.new()
    bs:writeUInt16(tonumber(id))
    bs:writeUInt16(0)
    bs:writeUInt16(0)
    bs:writeUInt16(0)
    bs:writeFloat(0)
    bs:writeFloat(0)
    bs:writeFloat(0.1)
    bs:writeFloat(0)
    bs:writeFloat(0)
    bs:writeFloat(0.1)
    bs:writeFloat(0.1)
    bs:writeUInt8(1)
    bs:writeUInt8(1)
    bs:writeUInt8(1)
    bs:writeUInt8(1)
    bs:writeFloat(0)
    bs:writeFloat(0)
    bs:writeFloat(0.1)
    bs:writeFloat(0)
    bs:writeFloat(0)
    bs:writeFloat(0.1)
    bs:writeUInt16(0)
    bs:writeUInt16(0)
    bs:writeUInt16(0)
    bs:emulIncomingPacket(207)
end

Lua:
function emul_packet(id) -- все нормально
    local bs = raknetNewBitStream()
    raknetBitStreamWriteInt16(bs, tonumber(id))
    raknetBitStreamSetWriteOffset(bs, 6)
    raknetBitStreamWriteFloat(bs, 0)
    raknetBitStreamWriteFloat(bs, 0)
    raknetBitStreamWriteFloat(bs, 0)
    raknetEmulPacketReceiveBitStream(207, bs)
    raknetDeleteBitStream(bs)
end

function raklua_emul_packet(id) -- крашит игру
    local bs = RakLuaBitStream.new()
    bs:writeUInt16(tonumber(id))
    bs:setWriteOffset(6)   
    bs:writeFloat(0)
    bs:writeFloat(0)
    bs:writeFloat(0)   
    bs:emulIncomingPacket(207)
end
 
Последнее редактирование:

whyega

52NGG
Модератор
2,617
2,344
крашит именно при заполнение битстрима как я понял, если эмулировать что-то по типу потери соединения, то все нормально
 
Последнее редактирование:
  • Вау
Реакции: qdIbp

#Northn

Pears Project — уже запущен!
Автор темы
Всефорумный модератор
2,649
2,499