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 за помощь.
 
Последнее редактирование:

FYP

Известный
Автор темы
Администратор
1,738
5,044
забыл о важной особенности исходящих пакетов синхры. добавил её описание перед первым спойлером.
 
  • Нравится
Реакции: Garrus и hnnssy

Dark_Knight

Начинаем начинать
Друг
3,822
1,712
Насколько я понял, ты просто сделал инклуд с обьявленными структурами типов и добавил функций с апи в луа?
это позволит избавить от необходимости использования SAMPFUNCS
А вот это как понять, если в ракнет.луа ты юзаешь это?
Lua:
require 'lib.sampfuncs'
Выходит, ты всего равно юзаешь СФ хотя говоришь, что при помощи этого исключаешь его необходимость? Если это понимать, как полная независимость, то тут я не соглашусь ибо ты его подключаешь. Но если это воспринимать, как вместо, к примеру, трех инклудов юзать один, то тут соглашусь я.
 

MISTER_GONWIK

Всефорумный гонщик
Всефорумный модератор
1,223
1,550
Насколько я понял, ты просто сделал инклуд с обьявленными структурами типов и добавил функций с апи в луа?

А вот это как понять, если в ракнет.луа ты юзаешь это?
Lua:
require 'lib.sampfuncs'
Выходит, ты всего равно юзаешь СФ хотя говоришь, что при помощи этого исключаешь его необходимость? Если это понимать, как полная независимость, то тут я не соглашусь ибо ты его подключаешь. Но если это воспринимать, как вместо, к примеру, трех инклудов юзать один, то тут соглашусь я.
там константы хранятся, тупица
 
  • Нравится
  • Ха-ха
Реакции: FooOoott, bend и Vandal™

FYP

Известный
Автор темы
Администратор
1,738
5,044
Насколько я понял, ты просто сделал инклуд с обьявленными структурами типов и добавил функций с апи в луа?

А вот это как понять, если в ракнет.луа ты юзаешь это?
Lua:
require 'lib.sampfuncs'
Выходит, ты всего равно юзаешь СФ хотя говоришь, что при помощи этого исключаешь его необходимость? Если это понимать, как полная независимость, то тут я не соглашусь ибо ты его подключаешь. Но если это воспринимать, как вместо, к примеру, трех инклудов юзать один, то тут соглашусь я.
перечитав снова, возможно ты обнаружишь для чего вообще эта библиотека нужна и что не было упоминаний о том, что она не зависит от SAMPFUNCS.
возможно нет...

так или иначе, взяв волю в кулак и заглянув в исходник, велика вероятность, что ты заметишь такие строки в самом начале:
Lua:
assert(isSampLoaded(), 'SA:MP is not loaded')
assert(isSampfuncsLoaded(), 'samp.events requires SAMPFUNCS')
assert(getMoonloaderVersion() >= 20, 'samp.events requires MoonLoader v.020 or greater')
о боже! что же это?! проверка на загруженность сф? да ещё и с выводом ошибки! какой ужас!
она зависит от сф, но постепенно будет появляться возможности, избавляющие от этой зависимости, и полная независимость появится лишь только в будущем.

мудрый совет тебе: сначала разберись как следует, потом выёбывайся.
 
  • Нравится
Реакции: Garrus, 4el0ve4ik и san0

Dark_Knight

Начинаем начинать
Друг
3,822
1,712
мудрый совет тебе: сначала разберись как следует, потом выёбывайся.
Во первых, я просто задал вопрос, а не выебывался. Потом просто цитировал твои слова и ничего более.
это позволит избавить от необходимости использования SAMPFUNCS.
Просто эту фразу я расценил, что ты уже в данной библиотеке отказался от СФ. И так может понять каждый.
Во вторых - форум и предназначен для того, чтобы в нем ввелись дискуссии и было общение. Если бы я начал выебыватся, то в начале этого поста я просто бы послал кого-то нахуй и т.д., но я этого же не сделал.
 
  • Нравится
Реакции: Motykoo

Garrus

Участник
159
20
Такая проблемка. Если в обработчике отлавливать определенное сообщение, и, при этом, в этом же обработчике оправлять сообщение серверу, то отловленное сообщение не будет отображаться в чате.
Пример:
Код:
function sampev.onServerMessage(color, text)
if string.find(text, "кек")
then
sampSendChat("привет")
end
end
- отловленное сообщение, содержащее "кек" не будет отображено в чате, хотя серверу будет отправлено сообщение "Привет". Возможно, проблема со стороны moonloader'a.
 

Dark_Knight

Начинаем начинать
Друг
3,822
1,712
А ты уверен, что правильно юзаешь метод финд?
 

FYP

Известный
Автор темы
Администратор
1,738
5,044
Такая проблемка. Если в обработчике отлавливать определенное сообщение, и, при этом, в этом же обработчике оправлять сообщение серверу, то отловленное сообщение не будет отображаться в чате.
Пример:
Код:
function sampev.onServerMessage(color, text)
if string.find(text, "кек")
then
sampSendChat("привет")
end
end
- отловленное сообщение, содержащее "кек" не будет отображено в чате, хотя серверу будет отправлено сообщение "Привет". Возможно, проблема со стороны moonloader'a.
долго не мог разобраться из-за чего это происходит, но потом внезапно дошло - всё из-за того, что функция sampSendChat отправляет RPC средствами сампа, и raknet-хук ловит этот пакет. в итоге из-за того, что другой хук вызывается изнутри первого хука происходит конфликт. это проблема не мунлоадера и не samp.lua, это проблема sampfuncs, и в клео в тех же условиях будет происходить то же самое.
я поищу способ исправить это в мунлоадере, но ничего не гарантирую, скорее всего придётся обновлять sampfuncs.
 
  • Нравится
Реакции: Bogach и Garrus