SAMP.Lua

Изначально эта библиотека задумывалась как простой апи исключительно для обработки сетевых пакетов, но в процессе было решешо, что лучше бы сделать её полноценной библиотекой для работы с сампом, в будущем это позволит избавить от необходимости использования SAMPFUNCS. Пока реализован только модуль, значительно упрощающий работу с пакетами, так что говорить сейчас будем о нём.

SAMP.Events
Этот модуль добавляет событийную обработку входящих и исходящих RakNet пакетов. Имеет удобный API, полностью кастомизируем, предоставляет те же возможности, что и обычные хуки: чтение, перезапись, игнорирование.


Использование
Простой пример обработки исходящего сообщения в чат:
Lua:
local sampev = require 'lib.samp.events'

function sampev.onSendChat(msg)
  print('You said: ' .. msg)
end
Да, настолько просто. Нужно лишь загрузить библиотеку и добавить функцию с соответствующими аргументами, вот и всё.
Перезапись данных в пакете настолько же проста, нужно лишь вернуть из функции все значения в том же порядке, занеся их в таблицу.
Lua:
function sampev.onSendChat(msg)
  return {'I said: ' .. msg}
end
Будет дописывать текст "I said:" в начало каждого отправляемого сообщения.
Если вернуть из функции false, то пакет будет проигнорирован клиентом. Этот код запретит серверу изменять позицию игрока:
Lua:
function sampev.onSetPlayerPos(position)
  print(string.format('Server tried to change your position to %0.2f %0.2f %0.2f', position.x, position.y, position.z))
  return false
end
Структуры всех пакетов находятся в файле events.lua.

Перезапись исходящих пакетов синхронизации слегка отличается от всех остальных - они не требуют возврата аргументов, а вместо этого данные перезаписываются как в обычной таблице lua. Это сделано для повышения производительности.
Пример перезаписи позиции в исходящих данных синхронизации игрока:
Lua:
function sampev.onSendPlayerSync(data)
  print('Original position:', data.position.x, data.position.y, data.position.z)
  data.position.x = 1337
  data.position.y = 1488
  data.position.z = 228
end
Это применимо только в событиях onSendPlayerSync, onSendVehicleSync, onSendPassengerSync, onSendAimSync, onSendUnoccupiedSync, onSendTrailerSync, onSendBulletSync, onSendSpectatorSync.
Структуры всех пакетов синхронизации находятся в файле synchronization.lua.


Примеры скриптов
AntiCarJack - перехват и игнорирование входящего RPC и пакета синхронизации
Bubble Sniffer - перехват и обработка входящего RPC
DerpCam - перезапись данных исходящего пакета синхронизации
Chat Bliss - перезапись, игнорирование и обработка исходящего RPC


Кастомизация
API структурирован так, что позволяет вносить в него любые изменения, не прибегая к изменению исходных файлов модулей.
Добавление своего обработчика для серверного пакета проигрывания звука:
Lua:
local sampev = 'lib.samp.events'
local raknet = 'lib.samp.raknet'
sampev.INTERFACE.INCOMING_RPCS[raknet.RPC.PLAYSOUND] = {'onPlaySound', {soundId = 'int32'}, {x = 'float'}, {y = 'float'}, {z = 'float'}}
И теперь в тот же файл можно добавить функцию для обработки события:
Lua:
function sampev.onPlaySound(sound, x, y, z)
-- добавляем сообщение в лог
print(string.format('Sound %d at coords %0.2f, %0.2f, %0.2f', sound, x, y, z))
-- и отключаем звук, запрещая дальнейшую обработку пакета
return false
end
Следует сразу разобрать код регистрации события, чтобы стало понятнее.
Lua:
sampev.INTERFACE. -- поле INTERFACE - это все экспортируемые элементы модуля
INCOMING_RPCS -- обращение к списку входящих RPC
[raknet.RPC.PLAYSOUND] -- обращение к элементу таблицы INCOMING_RPCS по индексу, в данном случае INCOMING_RPCS - это таблица, содержащая список структур всех входящих RPC, а raknet.RPC.PLAYSOUND - идентификатор требуемого RPC
= -- присваиваем ему новое значение - таблицу с информацией о событии и структурой пакета
{'onPlaySound', -- название события, функция события будет использовать это имя
-- структура пакета. каждый параметр должен быть заключен в отдельную таблицу, иметь название и тип в формате {названиеПараметра = 'тип'}
{soundId = 'int32'}, {coords = 'vector3d'}}
Очевидно, что INCOMING_RPCS - это не единственная таблица, их четыре:
INCOMING_RPCS - входящие RPC
OUTCOMING_RPCS - исходящие RPC
INCOMING_PACKETS - входящие пакеты
OUTCOMING_PACKETS - исходящие пакеты

Новый тип тоже можно добавить без изменения исходников библиотеки:
Lua:
events.INTERFACE.BitStreamIO.fvector3 = { -- название типа после последней точки
   read = function(bs) -- функция чтения. первый аргумент - битстрим
     local vec = {}
     vec.x = raknetBitStreamReadFloat(bs)
     vec.y = raknetBitStreamReadFloat(bs)
     vec.z = raknetBitStreamReadFloat(bs)
     return vec
   end,
   write = function(bs, value) -- функция записи
     raknetBitStreamWriteFloat(bs, value.x)
     raknetBitStreamWriteFloat(bs, value.y)
     raknetBitStreamWriteFloat(bs, value.z)
   end
}
В случае, если пакет имеет какую-то сложную структуру, не описываемую даже с помощью пользовательских типов, то вместо структуры можно передать функцию, которая будет обрабатывать данные. Например как это сделано для RPC InitGame:
Lua:
INCOMING_RPCS[RPC.INITGAME] = {'onInitGame', onInitGameReader, onInitGameWriter} -- второй и третий аргумент - это функции чтения и записи
Для большего количества примеров смотрите исходный код.

Что касается всей библиотеки в целом, то она ещё находится на зачаточной стадии и обсуждать кроме планов нечего. По сути она должна заменить собой основную часть SAMPFUNCS - моддинг сампа.

Скачать последнюю версию и следить за изменениями всегда можно на GitHub.
Установка: скачать samp.zip из последнего релиза и целиком скопировать папку 'samp' (не содержимое папки!) из архива в каталог 'moonloader/lib/'.

Любая помощь в разработке приветствуется, особенно с добавлением новых структур. Предложите Pull request на гитхабе или напишите в этой теме.

Ну и конечно же крохотное нано-спасибо hnnssy за помощь.
 
Последнее редактирование:

bereg101

Участник
55
0
А просто интересно через эту библиотеку возможно создавать подключение к сампу?
 

ARMOR

I'm sorry that I let you down
Модератор
4,815
5,926
как сделать активацию на команду?
Lua:
-- В начало кода
local ev = require 'lib.samp.events'
local enable = false

-- В функцию main
sampRegisterChatCommand("hook", function()
    enable = not enable
    sampAddChatMessage(enable and "Скрипт работает!" or "Скрипт не работает!", -1)
end)

-- Ну и сам хук, у меня onServerMessage, но у тебя может быть любой другой.
function ev.onServerMessage(color, text)
    if text:find("Тут свой текст") and enable then
        -- Тут свой код.
    end
end
-- Или ещё можно сделать так
function ev.onServerMessage(color, text)
    if enable then
        if text:find("Тут свой текст") then
            --Тут свой код.
        end
    end
end
 
  • Нравится
Реакции: E E E E E E E

E E E E E E E

Участник
112
15
А как сделать телепорт к метке? (да знаю что вопрос не по теме но другой подобной темы не нашёл)
 

kyrtion

Известный
603
217
А как сделать телепорт к метке? (да знаю что вопрос не по теме но другой подобной темы не нашёл)
говнокодер написал говнокод...:
local sampev = require 'lib.samp.events'

local checkpoint_x = 0
local checkpoint_y = 0
local checkpoint_z = 0
local checkpoint_r = 0

function main()
    repeat wait(0) until isSampAvailable();
    sampRegisterChatCommand('tp_m', function()
        result, x, y, z = getTargetBlipCoordinatesFixed()
        if result then
            if isCharInAnyCar(PLAYER_PED) then
                lua_thread.create(carTeleport, x, y, z)
            else
                lua_thread.create(teleport, x, y, z)
            end
        else
            printStringNow('Marker not found', 2500)
        end
    end)
        sampRegisterChatCommand('tp_c', function()
        if checkpoint_x ~= 0 then
            if isCharInAnyCar(PLAYER_PED) then
                lua_thread.create(carTeleport, checkpoint_x, checkpoint_y, checkpoint_z)
            else
                lua_thread.create(teleport, checkpoint_x, checkpoint_y, checkpoint_z)
            end
        else
            printStringNow('Checkpoint not found', 2500)
        end
        -- end
    end)
    wait(-1)
end

function onReceiveRpc(int,bit)
    if int == 107 then -- Если SetCheckpoint(Обычный checkpoint)
        checkpoint_x = raknetBitStreamReadFloat(bit)
        checkpoint_y = raknetBitStreamReadFloat(bit)
        checkpoint_z = raknetBitStreamReadFloat(bit)
        checkpoint_r = raknetBitStreamReadFloat(bit)
        print(checkpoint_x,checkpoint_y,checkpoint_z,checkpoint_r)
    end
end

function carTeleport(x, y, z)
    local car = storeCarCharIsInNoSave(PLAYER_PED)
    nopHook("onSendUnoccupiedSync", true)
    nopHook("onSendPassengerSync", true)
    lockPlayerControl(true)
    setCarCoordinates(car, x, y, z)
    lockPlayerControl(false)
end

function teleport(x, y, z)
    sendOnfootSync(x, y, z)
    setCharCoordinates(PLAYER_PED, x, y, z)
end

function sendPassengerSync(x, y, z)
    local data = allocateMemory(24)
    local _, pId = sampGetPlayerIdByCharHandle(PLAYER_PED)
    sampStorePlayerPassengerData(pId, data)
    setStructElement(data, 2, 1, 1, false) 
    setStructFloatElement(data, 12, x, false)
    setStructFloatElement(data, 16, y, false)
    setStructFloatElement(data, 20, z, false)
    sampSendPassengerData(data)
    freeMemory(data)
end

function sendUnoccupiedSyncBySeatId(x, y, z, seatId)
    local data = allocateMemory(67)
    local car = storeCarCharIsInNoSave(PLAYER_PED)
    local _, vehicleId = sampGetVehicleIdByCarHandle(car)
    local rx, ry, rz = getCarRoll(car)
    local health = getCarHealth(car)
    setStructElement(data, 0, 2, vehicleId, false)
    setStructElement(data, 2, 1, seatId, false)
    -- roll
    setStructFloatElement(data, 3, rx, false)
    setStructFloatElement(data, 7, ry, false)
    setStructFloatElement(data, 11, rz, false)
    -- direction
    setStructFloatElement(data, 15, 0.09, false)
    setStructFloatElement(data, 19, -0.8, false)
    setStructFloatElement(data, 23, 0.02, false)
    -- position
    setStructFloatElement(data, 27, x, false)
    setStructFloatElement(data, 31, y, false)
    setStructFloatElement(data, 35, z, false)
    -- speed
    setStructFloatElement(data, 39, 0.0012, false)
    setStructFloatElement(data, 43, 0.02, false)
    setStructFloatElement(data, 47, 0.0017, false)
    -- turning speed
    setStructFloatElement(data, 51, 0.005, false)
    setStructFloatElement(data, 55, -0.002, false)
    setStructFloatElement(data, 59, 0.3, false)
    -- car health
    setStructElement(data, 63, 4, health, false)
    sampSendUnoccupiedData(data)
    freeMemory(data)
end

function nopHook(name, bool)
    local samp = require 'samp.events'
    samp[name] = function()
        if bool then
            return false
        end
    end
end

function getTargetBlipCoordinatesFixed()
    local bool, x, y, z = getTargetBlipCoordinates()
    if not bool then return false end
    requestCollision(x, y)
    loadScene(x, y, z)
    local bool, x, y, z = getTargetBlipCoordinates()
    return bool, x, y, z
end

function SearchMarker(posX, posY, posZ)
    local ret_posX = 0.0
    local ret_posY = 0.0
    local ret_posZ = 0.0
    local isFind = false
    for id = 0, 31 do
        local MarkerStruct = 0
        MarkerStruct = 0xC7F168 + id * 56
        local MarkerPosX = representIntAsFloat(readMemory(MarkerStruct + 0, 4, false))
        local MarkerPosY = representIntAsFloat(readMemory(MarkerStruct + 4, 4, false))
        local MarkerPosZ = representIntAsFloat(readMemory(MarkerStruct + 8, 4, false))
        if MarkerPosX ~= 0.0 or MarkerPosY ~= 0.0 or MarkerPosZ ~= 0.0 then
            ret_posX = MarkerPosX
            ret_posY = MarkerPosY
            ret_posZ = MarkerPosZ
            isFind = true
        end
    end
    return isFind, ret_posX, ret_posY, ret_posZ
end
 

E E E E E E E

Участник
112
15
говнокодер написал говнокод...:
local sampev = require 'lib.samp.events'

local checkpoint_x = 0
local checkpoint_y = 0
local checkpoint_z = 0
local checkpoint_r = 0

function main()
    repeat wait(0) until isSampAvailable();
    sampRegisterChatCommand('tp_m', function()
        result, x, y, z = getTargetBlipCoordinatesFixed()
        if result then
            if isCharInAnyCar(PLAYER_PED) then
                lua_thread.create(carTeleport, x, y, z)
            else
                lua_thread.create(teleport, x, y, z)
            end
        else
            printStringNow('Marker not found', 2500)
        end
    end)
        sampRegisterChatCommand('tp_c', function()
        if checkpoint_x ~= 0 then
            if isCharInAnyCar(PLAYER_PED) then
                lua_thread.create(carTeleport, checkpoint_x, checkpoint_y, checkpoint_z)
            else
                lua_thread.create(teleport, checkpoint_x, checkpoint_y, checkpoint_z)
            end
        else
            printStringNow('Checkpoint not found', 2500)
        end
        -- end
    end)
    wait(-1)
end

function onReceiveRpc(int,bit)
    if int == 107 then -- Если SetCheckpoint(Обычный checkpoint)
        checkpoint_x = raknetBitStreamReadFloat(bit)
        checkpoint_y = raknetBitStreamReadFloat(bit)
        checkpoint_z = raknetBitStreamReadFloat(bit)
        checkpoint_r = raknetBitStreamReadFloat(bit)
        print(checkpoint_x,checkpoint_y,checkpoint_z,checkpoint_r)
    end
end

function carTeleport(x, y, z)
    local car = storeCarCharIsInNoSave(PLAYER_PED)
    nopHook("onSendUnoccupiedSync", true)
    nopHook("onSendPassengerSync", true)
    lockPlayerControl(true)
    setCarCoordinates(car, x, y, z)
    lockPlayerControl(false)
end

function teleport(x, y, z)
    sendOnfootSync(x, y, z)
    setCharCoordinates(PLAYER_PED, x, y, z)
end

function sendPassengerSync(x, y, z)
    local data = allocateMemory(24)
    local _, pId = sampGetPlayerIdByCharHandle(PLAYER_PED)
    sampStorePlayerPassengerData(pId, data)
    setStructElement(data, 2, 1, 1, false)
    setStructFloatElement(data, 12, x, false)
    setStructFloatElement(data, 16, y, false)
    setStructFloatElement(data, 20, z, false)
    sampSendPassengerData(data)
    freeMemory(data)
end

function sendUnoccupiedSyncBySeatId(x, y, z, seatId)
    local data = allocateMemory(67)
    local car = storeCarCharIsInNoSave(PLAYER_PED)
    local _, vehicleId = sampGetVehicleIdByCarHandle(car)
    local rx, ry, rz = getCarRoll(car)
    local health = getCarHealth(car)
    setStructElement(data, 0, 2, vehicleId, false)
    setStructElement(data, 2, 1, seatId, false)
    -- roll
    setStructFloatElement(data, 3, rx, false)
    setStructFloatElement(data, 7, ry, false)
    setStructFloatElement(data, 11, rz, false)
    -- direction
    setStructFloatElement(data, 15, 0.09, false)
    setStructFloatElement(data, 19, -0.8, false)
    setStructFloatElement(data, 23, 0.02, false)
    -- position
    setStructFloatElement(data, 27, x, false)
    setStructFloatElement(data, 31, y, false)
    setStructFloatElement(data, 35, z, false)
    -- speed
    setStructFloatElement(data, 39, 0.0012, false)
    setStructFloatElement(data, 43, 0.02, false)
    setStructFloatElement(data, 47, 0.0017, false)
    -- turning speed
    setStructFloatElement(data, 51, 0.005, false)
    setStructFloatElement(data, 55, -0.002, false)
    setStructFloatElement(data, 59, 0.3, false)
    -- car health
    setStructElement(data, 63, 4, health, false)
    sampSendUnoccupiedData(data)
    freeMemory(data)
end

function nopHook(name, bool)
    local samp = require 'samp.events'
    samp[name] = function()
        if bool then
            return false
        end
    end
end

function getTargetBlipCoordinatesFixed()
    local bool, x, y, z = getTargetBlipCoordinates()
    if not bool then return false end
    requestCollision(x, y)
    loadScene(x, y, z)
    local bool, x, y, z = getTargetBlipCoordinates()
    return bool, x, y, z
end

function SearchMarker(posX, posY, posZ)
    local ret_posX = 0.0
    local ret_posY = 0.0
    local ret_posZ = 0.0
    local isFind = false
    for id = 0, 31 do
        local MarkerStruct = 0
        MarkerStruct = 0xC7F168 + id * 56
        local MarkerPosX = representIntAsFloat(readMemory(MarkerStruct + 0, 4, false))
        local MarkerPosY = representIntAsFloat(readMemory(MarkerStruct + 4, 4, false))
        local MarkerPosZ = representIntAsFloat(readMemory(MarkerStruct + 8, 4, false))
        if MarkerPosX ~= 0.0 or MarkerPosY ~= 0.0 or MarkerPosZ ~= 0.0 then
            ret_posX = MarkerPosX
            ret_posY = MarkerPosY
            ret_posZ = MarkerPosZ
            isFind = true
        end
    end
    return isFind, ret_posX, ret_posY, ret_posZ
end
А можно комментариями пж? просто я мало чего тут понимаю