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

B365

Известный
30
15
Баг в onMarkersSync. Удаляясь от центра мира (0, 0, 0), по координатам начинают приходить значения от 60000 и больше. Первые координаты которые приходят нормальные но все последующие невалидные. В самой игре маркеры нормально отображаются. Проверялось на разных серверах и даже на локалке, в отмеченных координатах для примера по X и Y значения больше 60000. Moonloader v.026.5-beta, версия SAMP.Lua v2.2.0


nazMTOF.png



Lua:
require"lib.moonloader"
local events = require 'lib.samp.events'

--[[
    INCOMING_PACKETS[PACKET.MARKERS_SYNC]         = {'onMarkersSync', handler.packet_markers_sync_reader, handler.packet_markers_sync_writer}
    
--- onMarkersSync
function handler.packet_markers_sync_reader(bs)
    local markers = {}
    local players = bsread.int32(bs)
    for i = 1, players do
        local playerId = bsread.int16(bs)
        local active = bsread.bool(bs)
        if active then
            local vector3d = require 'vector3d'
            local x, y, z = bsread.int16(bs), bsread.int16(bs), bsread.int16(bs)
            table.insert(markers, {playerId = playerId, active = true, coords = vector3d(x, y, z)})
        else
            table.insert(markers, {playerId = playerId, active = false})
        end
    end
    return {markers}
end
--]]

function events.onMarkersSync(data)
    for k, v in pairs(data) do
        for k2, v2 in pairs(v) do
            local playerId = data[k].playerId
            local active = data[k].active
            if data[k]['coords'] ~= nil then
                local x = data[k]['coords'].x
                local y = data[k]['coords'].y
                local z = data[k]['coords'].z
                if x > 6000 or y > 6000 or z > 6000 then
                    sampAddChatMessage(string.format("playerId %s active %s X %s Y %s Z %s", playerId, active, x, y, z), -1)
                end
                print(string.format("playerId %s active %s X %s Y %s Z %s", playerId, active, x, y, z))
            end
        end
    end
end
 
  • Нравится
Реакции: DarkP1xel

Horsi

Участник
11
86
Я получаю ошибки всякий раз, когда использую функции из пакета "events", даже если они пусты, просто сижу и ничего не меняю и не обрабатываю. Прямо сейчас я ограничился "onChatMessage" и "onServerMessage", и он все равно вылетает. Я ломал себе голову уже несколько дней, если подвести итог, возможно, я делаю какую-то тупую ошибку, но я не могу отследить, где, поскольку я ничего не делаю с функцией. Все работает нормально, пока по какой-то причине в случайный момент игра не убьет себя. Пожалуйста помоги. Наиболее распространенный код операции, при котором происходит сбой, - 0x685 (99%).

SA-MP 0.3.7
Exception At Address: 0x6DFE77D8
Base: 0x03DC0000

the Registers:
the EAX: 0x0F8D26A0 the EBX: 0x0000010A the ECX: 0x00000000 the EDX: 0x00000000
ESI: 0x154B8EF8 EDI: 0x154B8EF8 the EBP: 0x0F8101C0 the ESP: 0x0177F098
the EFLAGS: 0x00210202

the Stack:
+0000: 0xFFFFD8F0 0x6E029325 0x154B8EF8 0xFFFFD8F0
+0010: 0x00000000 0xFFFFFFFF 0x21A24850 0x0F810D58
0020 : 0x6E39A935 0x154B8EF8 0xFFFFD8F0 0x0000010A
0.03 thousand: 0x21A24850 0x6E2B0957 0x6E289D70 0x0F8102CC
0 040: 0x6E289D99 0x154B94F0 0x0F8101C0 0x00000001
0.05 thousand: 0x6DFE280D 0x0F8101C0 0x00008002 0x0F83A150
0060: 0x0177F234 0x0177F1B4 0x00000001 0x0F8101C0
0 070: 0x00000005 0x1319A830 0x0177F258 0x6E004360
+0080: 0x0F810100 0x000F5F21 0x0F8101F0 0x0F8101C0
+0090: 0x6E007AFB 0x0F8101C0 0x0177F234 0x00000001
+ 00A0: 0xFFFFFFFF 0x0F8101F0 0x154B94D0 0x0177F174
+ 00B0: 0x0F8101F0 0x6E007D07 0x0F8101F0 0x0F8101C0
+ 00C0: 0x0F869FE8 0x154B94D0 0x0F8101F0 0x0F8101C0
+ 00D0: 0x0F89E8F4 0x0177F180 0x6E0477FF 0x0000076C
+ 00E0: 0x6E0076C0 0x0F8101C0 0x0F810370 0x0F8101C0
+ 00F0: 0x0177F234 0x0F89E8F0 0xFFFFFFFC 0x0F8101C0
+0100: 0x6E026EAC 0x0F810D58 0x0F841440 0x0F8B0668
0110: 0x0F8101C0 0x00000002 0x00000000 0x0F810370
0.12 thousand: 0x0177F1D4 0x0F89E8F0 0x6DFE437F 0x00000000
0130: 0x00000000 0x00000000 0x00000000 0x80000000
+0140: 0x41777F6A 0x00000000 0x40580000 0x0000002E
+0150: 0x00000000 0x3EBCDE3C 0x3F6D88A0 0xBF6DE835
+0160: 0x3EBD0DD0 0x3C896F6A 0x3D55B409 0x00000000
+0170: 0x40580000 0x00000000 0x40580000 0x00000001
+0180: 0x00000001 0x0000004F 0x0F841440 0x0177F234
+0190: 0x0F873DE8 0x0F8B0668 0x0F841440 0x0F810D58
+ 01A0: 0x6E4AFB23 0x0F8B0668 0x0F841440 0x0177F250
+ 01B0: 0x6E4B2587 0x0F89E8F0 0x0177F274 0x6DE0F543
+ 01C0: 0x0177F310 0x6E004360 0x00000000 0x0F83A130
+ 01D0: 0x0F8101F0 0x04FBED80 0x6DFE73EB 0x0F8101C0
+ 01E0: 0x00000000 0x00000000 0x00000008 0x0F8101C0
+ 01F0: 0x00000001 0x0177F340 0x6E289472 0x0F8101C0
+0200: 0x00000002 0x00000001 0xFFFFFFFF 0x0F8101C0
+0210: 0x0177F340 0x0177F31C 0x04FBED80 0x6E3A7C01
+0220: 0x0177F340 0x0177F388 0x0177F388 0x00000002
+0230: 0x0177F304 0x0177F3DC 0x01C79A54 0x04FBED80
+0240: 0x00000000 0x00000000 0x00000000 0x00000000
+0250: 0x00000000 0x00000000 0x00000000 0x00000000
+0260: 0x00000000 0x0177F2A0 0x0177F398 0x04FBED88
+ 0270: 0x00000001 0x00000000 0x0177F398 0x6E402A69

SCM Op: 0x685, lDbg: 0 LastRendObj: 19045

Game Version: US 1.0

State Information: Ped Context: 0
P0 (32.0) P1 (0.0) P2 (32.0) P3 (32 , 0)
P4 (32.0) P5 (0.0) P6 (0.0) P7 (0.0)
P8 (32.0) P9 (32.0) P10 (32.0) P11 (0.0 )
P12 (0.0) P13 (0.0) P14 (0.0) P15 (0.0)
P16 (0.0) P17 (0.0) P18 (0.0) P19 (0.0)
P20 ( 0.0) P21 (32.0) P22 (0.0) P23 (0.0)
P24 (32.0) P25 (0.0) P26 (0.0) P27 (0.0)
P28 (0, 0) P29 (0.0) P30 (32.0) P31 (0.0)
P32 (0.0) P33 (0.0) P34 (0.0) P35 (32.0)
P36 (0.0) P37 (0.0) P38 (32.0) P39 (0.0)
P40 (19.1480) P41 (32.0) P42 (32.0) P43 (0.0)
P44 (0.0) P45 ( 0.0) P46 (0.0) P47 (0.0)
P48 (32.0) P49 (32.0) P50 (0.0) P51 (32.0)
P52 (32.0) P53 (32, 0) P54 (0.0) P55 (32.0)
P56 (0.0) P57 (32.0) P58 (0.0) P59 (32.0)
P60 (0.0) P61 (32.0) P62 (32.0) P63 (0.0)
P64 (0.0) P65 (0.0) P66 (0.0) P67 (0.0)
P68 (0.0) P69 (0.0) P70 ( 0.0) P71 (32.0)
P72 (0.0) P73 (32.0) P74 (32.0) P75 (0.0)
P76 (0.0) P77 (0.0) P78 (0.0) P79 (32.0)
P80 ( 0.0) P81 (0.0) P82 (32.0) P83 (0.0)
P84 (0.0) P85 (0.0) P86 (32.0) P87 (0.0)
P88 (0, 0) P89 (0.0) P90 (0.0) P91 (0.0)
P92 (0.0) P93 (0.0) P94 (0.0) P95 (0.0)
P96 (32.0) P98 (0.0) P99 (0.0)
P100 (0.0) P101 (32.0) P102 (32.0) P103 (32.0)
P104 (0.0) P105 (0.0) P106 ( 32.0) P107 (0.0)
P108 (32.0) P109 (32.0) P110 (0.0) P111 (0.0)
P112 (0.0) P113 (0.0) P114 (0, 0) P115 (32.0)
P116 (0.0) P117 (0.0) P118 (32.0) P119 (32.0)
P120 (0.0) P121 (0.0) P122 (0.0) P123 (0.0)
P124 (0.0) P125 (0.0) P126 (0.0) P127 (32.0)
P128 (0.0) P129 (32.0) P130 (0.0) P131 ( 0.0)
P132 (0.0) P133 (0.0) P134 (0.0) P135 (0.0)
P136 (0.0) P137 (0.0) P138 (0.0) P139 (0.0)
P140 ( 0.0) P141 (0.0) P142 (32.0) P143 (0.0)
P144 (0.0) P145 (0.0) P146 (32.0) P147 (0.0)
P148 (0, 0) P149 (0.0) P150 (32.0) P151 (0.0)
P152 (0.0) P153 (32.0) P154 (32.0) P155 (0.0)
P156 (32.0) P157 (0.0) P158 (32.0) P159 (0.0)
P160 (32.0) P161 (0.0) P162 (0.0) P163 (0.0)
P164 (32.0) P165 ( 0.0) P166 (32.0) P167 (0.0)
P168 (0.0) P169 (0.0) P170 (0.0) P171 (32.0)
P172 (0.0) P173 (0, 0) P174 (0.0) P175 (0.0)
P176 (32.0) P177 (32.0) P178 (0.0) P179 (32.0)
P180 (0.0) P181 (0.0) P182 (32.0) P183 (0.0)
P184 (0.0) P185 (32.0) P186 (32.0) P187 (0.0)
P188 (0.0) P189 (32.0) P190 (32.0) P192 (32.0) P193 (32.0) P194 (32.0) P195 (0.0)
P196 (0.0) P197 ( 0.0) P198 (32.0) P199 (0.0)
P200 (0.0) P201 (32.0) P202 (32.0) P203 (0.0)
P204 (0.0) P205 (0, 0) P206 (0.0) P207 (32.0)
P209 (0.0) P210 (32.0) P211 (32.0)
P212 (32.0) P213 (0.0) P214 (32.0) P215 (32.0)
P216 (0.0) P217 (0.0) P218 (32.0) P219 (0.0)
P220 (32.0) P221 (32.0) P222 (32.0) P223 ( 32.0)
P224 (32.0) P225 (0.0) P226 (32.0) P228 (0.0) P229 (0.0) P230 (0.0) P231 (0.0)
P232 (0, 0) P233 (32.0) P234 (0.0) P235 (0.0)
P236 (32.0) P237 (32.0) P238 (0.0) P239 (0.0)
P240 (0.0) P241 (0.0) P242 (32.0) P243 (0.0)
P244 (0.0) P245 (32.0) P246 (0.0) P247 (0.0)
P248 (0.0) P249 (32.0) P250 (32.0) P252 (0.0) P253 (0.0) P254 (0.0) P255 (32.0)
P256 (32.0) P259 ( 0.0)
P260 (0.0) P261 (0.0) P262 (0.0) P263 (0.0)
P264 (32.0) P265 (32.0) P266 (0.0) P267 (0, 0)
P268 (0.0) P269 (32.0) P271 (32.0)
P272 (0.0) P273 (32.0) P274 (0.0) P275 (0.0)
P276 (32.0) P277 (0.0) P279 (32.0)
P280 (0.0) P281 (0.0) P282 (0.0) P283 (32.0)
P284 (32.0) P285 (0.0) P286 ( 0.0) P287 (32.0)
P288 (32.0) P289 (0.0) P290 (0.0) P291 (0.0)
P292 (32.0) P294 (0.0) P295 (0, 0)
P296 (0.0) P297 (32.0) P298 (32.0) P299 (0.0)
P300 (0.0) P301 (0.0) P302 (0.0) P303 (0.0)
P304 (0.0) P306 (32.0) P307 (0.0)
P308 (0.0) P309 (0.0) P310 (32.0) P311 (0.0)
P312 (0.0) P313 (0.0) P314 (32.0) P315 (0.0)
P316 (0.0) P317 (0.0) P318 (0.0) P319 (0.0)
P321 ( 0.0) P322 (0.0) P324 (32.0) P325 (0.0) P326 (0.0) P327 (0.0)
P329 (32.0) P330 (32.0) P331 (0, 0)
P332 (0.0) P333 (0.0) P334 (0.0) P335 (0.0)
P336 (0.0) P337 (32.0) P338 (32.0) P339 (0.0)
P340 (0.0) P341 (0.0) P342 (0.0) P343 (0.0)
P344 (0.0) P345 (0.0) P346 (0.0) P347 (0.0)
P348 ( 32.0) P349 (32.0) P350 (0.0) P351 (0.0)
P352 (32.0) P353 (32.0) P354 (0.0) P355 (0.0)
P356 (32, 0) P357 (32.0) P358 (0.0) P360 (0.0) P361 (32.0) P362 (32.0) P363 (32.0)
P364 (32.0) P366 (32.0) P368 (32.0) P369 (0.0) P370 (0.0) P371 (0.0)
P372 (0.0) P373 (0.0) P374 (0.0) P375 (32.0)
P376 (32.0) P377 (0.0) P378 (32.0) P379 (0.0)
P380 (0.0) P381 (0.0) P382 (0.0) P383 (0.0)
P384 ( 32.0) P385 (0.0) P386 (32.0) P387 (0.0)
P388 (32.0) P389 (0.0) P390 (0.0) P393 (32.0) P400 (0, 0) P404 (32.0) P410 (0.0) P411 (0.0)
P412 (32.0) P414 (32.0)
 

Dark_Knight

Me, me and me.
Друг
4,078
2,096
Я получаю ошибки всякий раз, когда использую функции из пакета "events", даже если они пусты, просто сижу и ничего не меняю и не обрабатываю. Прямо сейчас я ограничился "onChatMessage" и "onServerMessage", и он все равно вылетает. Я ломал себе голову уже несколько дней, если подвести итог, возможно, я делаю какую-то тупую ошибку, но я не могу отследить, где, поскольку я ничего не делаю с функцией. Все работает нормально, пока по какой-то причине в случайный момент игра не убьет себя. Пожалуйста помоги. Наиболее распространенный код операции, при котором происходит сбой, - 0x685 (99%).

SA-MP 0.3.7
Exception At Address: 0x6DFE77D8
Base: 0x03DC0000

the Registers:
the EAX: 0x0F8D26A0 the EBX: 0x0000010A the ECX: 0x00000000 the EDX: 0x00000000
ESI: 0x154B8EF8 EDI: 0x154B8EF8 the EBP: 0x0F8101C0 the ESP: 0x0177F098
the EFLAGS: 0x00210202

the Stack:
+0000: 0xFFFFD8F0 0x6E029325 0x154B8EF8 0xFFFFD8F0
+0010: 0x00000000 0xFFFFFFFF 0x21A24850 0x0F810D58
0020 : 0x6E39A935 0x154B8EF8 0xFFFFD8F0 0x0000010A
0.03 thousand: 0x21A24850 0x6E2B0957 0x6E289D70 0x0F8102CC
0 040: 0x6E289D99 0x154B94F0 0x0F8101C0 0x00000001
0.05 thousand: 0x6DFE280D 0x0F8101C0 0x00008002 0x0F83A150
0060: 0x0177F234 0x0177F1B4 0x00000001 0x0F8101C0
0 070: 0x00000005 0x1319A830 0x0177F258 0x6E004360
+0080: 0x0F810100 0x000F5F21 0x0F8101F0 0x0F8101C0
+0090: 0x6E007AFB 0x0F8101C0 0x0177F234 0x00000001
+ 00A0: 0xFFFFFFFF 0x0F8101F0 0x154B94D0 0x0177F174
+ 00B0: 0x0F8101F0 0x6E007D07 0x0F8101F0 0x0F8101C0
+ 00C0: 0x0F869FE8 0x154B94D0 0x0F8101F0 0x0F8101C0
+ 00D0: 0x0F89E8F4 0x0177F180 0x6E0477FF 0x0000076C
+ 00E0: 0x6E0076C0 0x0F8101C0 0x0F810370 0x0F8101C0
+ 00F0: 0x0177F234 0x0F89E8F0 0xFFFFFFFC 0x0F8101C0
+0100: 0x6E026EAC 0x0F810D58 0x0F841440 0x0F8B0668
0110: 0x0F8101C0 0x00000002 0x00000000 0x0F810370
0.12 thousand: 0x0177F1D4 0x0F89E8F0 0x6DFE437F 0x00000000
0130: 0x00000000 0x00000000 0x00000000 0x80000000
+0140: 0x41777F6A 0x00000000 0x40580000 0x0000002E
+0150: 0x00000000 0x3EBCDE3C 0x3F6D88A0 0xBF6DE835
+0160: 0x3EBD0DD0 0x3C896F6A 0x3D55B409 0x00000000
+0170: 0x40580000 0x00000000 0x40580000 0x00000001
+0180: 0x00000001 0x0000004F 0x0F841440 0x0177F234
+0190: 0x0F873DE8 0x0F8B0668 0x0F841440 0x0F810D58
+ 01A0: 0x6E4AFB23 0x0F8B0668 0x0F841440 0x0177F250
+ 01B0: 0x6E4B2587 0x0F89E8F0 0x0177F274 0x6DE0F543
+ 01C0: 0x0177F310 0x6E004360 0x00000000 0x0F83A130
+ 01D0: 0x0F8101F0 0x04FBED80 0x6DFE73EB 0x0F8101C0
+ 01E0: 0x00000000 0x00000000 0x00000008 0x0F8101C0
+ 01F0: 0x00000001 0x0177F340 0x6E289472 0x0F8101C0
+0200: 0x00000002 0x00000001 0xFFFFFFFF 0x0F8101C0
+0210: 0x0177F340 0x0177F31C 0x04FBED80 0x6E3A7C01
+0220: 0x0177F340 0x0177F388 0x0177F388 0x00000002
+0230: 0x0177F304 0x0177F3DC 0x01C79A54 0x04FBED80
+0240: 0x00000000 0x00000000 0x00000000 0x00000000
+0250: 0x00000000 0x00000000 0x00000000 0x00000000
+0260: 0x00000000 0x0177F2A0 0x0177F398 0x04FBED88
+ 0270: 0x00000001 0x00000000 0x0177F398 0x6E402A69

SCM Op: 0x685, lDbg: 0 LastRendObj: 19045

Game Version: US 1.0

State Information: Ped Context: 0
P0 (32.0) P1 (0.0) P2 (32.0) P3 (32 , 0)
P4 (32.0) P5 (0.0) P6 (0.0) P7 (0.0)
P8 (32.0) P9 (32.0) P10 (32.0) P11 (0.0 )
P12 (0.0) P13 (0.0) P14 (0.0) P15 (0.0)
P16 (0.0) P17 (0.0) P18 (0.0) P19 (0.0)
P20 ( 0.0) P21 (32.0) P22 (0.0) P23 (0.0)
P24 (32.0) P25 (0.0) P26 (0.0) P27 (0.0)
P28 (0, 0) P29 (0.0) P30 (32.0) P31 (0.0)
P32 (0.0) P33 (0.0) P34 (0.0) P35 (32.0)
P36 (0.0) P37 (0.0) P38 (32.0) P39 (0.0)
P40 (19.1480) P41 (32.0) P42 (32.0) P43 (0.0)
P44 (0.0) P45 ( 0.0) P46 (0.0) P47 (0.0)
P48 (32.0) P49 (32.0) P50 (0.0) P51 (32.0)
P52 (32.0) P53 (32, 0) P54 (0.0) P55 (32.0)
P56 (0.0) P57 (32.0) P58 (0.0) P59 (32.0)
P60 (0.0) P61 (32.0) P62 (32.0) P63 (0.0)
P64 (0.0) P65 (0.0) P66 (0.0) P67 (0.0)
P68 (0.0) P69 (0.0) P70 ( 0.0) P71 (32.0)
P72 (0.0) P73 (32.0) P74 (32.0) P75 (0.0)
P76 (0.0) P77 (0.0) P78 (0.0) P79 (32.0)
P80 ( 0.0) P81 (0.0) P82 (32.0) P83 (0.0)
P84 (0.0) P85 (0.0) P86 (32.0) P87 (0.0)
P88 (0, 0) P89 (0.0) P90 (0.0) P91 (0.0)
P92 (0.0) P93 (0.0) P94 (0.0) P95 (0.0)
P96 (32.0) P98 (0.0) P99 (0.0)
P100 (0.0) P101 (32.0) P102 (32.0) P103 (32.0)
P104 (0.0) P105 (0.0) P106 ( 32.0) P107 (0.0)
P108 (32.0) P109 (32.0) P110 (0.0) P111 (0.0)
P112 (0.0) P113 (0.0) P114 (0, 0) P115 (32.0)
P116 (0.0) P117 (0.0) P118 (32.0) P119 (32.0)
P120 (0.0) P121 (0.0) P122 (0.0) P123 (0.0)
P124 (0.0) P125 (0.0) P126 (0.0) P127 (32.0)
P128 (0.0) P129 (32.0) P130 (0.0) P131 ( 0.0)
P132 (0.0) P133 (0.0) P134 (0.0) P135 (0.0)
P136 (0.0) P137 (0.0) P138 (0.0) P139 (0.0)
P140 ( 0.0) P141 (0.0) P142 (32.0) P143 (0.0)
P144 (0.0) P145 (0.0) P146 (32.0) P147 (0.0)
P148 (0, 0) P149 (0.0) P150 (32.0) P151 (0.0)
P152 (0.0) P153 (32.0) P154 (32.0) P155 (0.0)
P156 (32.0) P157 (0.0) P158 (32.0) P159 (0.0)
P160 (32.0) P161 (0.0) P162 (0.0) P163 (0.0)
P164 (32.0) P165 ( 0.0) P166 (32.0) P167 (0.0)
P168 (0.0) P169 (0.0) P170 (0.0) P171 (32.0)
P172 (0.0) P173 (0, 0) P174 (0.0) P175 (0.0)
P176 (32.0) P177 (32.0) P178 (0.0) P179 (32.0)
P180 (0.0) P181 (0.0) P182 (32.0) P183 (0.0)
P184 (0.0) P185 (32.0) P186 (32.0) P187 (0.0)
P188 (0.0) P189 (32.0) P190 (32.0) P192 (32.0) P193 (32.0) P194 (32.0) P195 (0.0)
P196 (0.0) P197 ( 0.0) P198 (32.0) P199 (0.0)
P200 (0.0) P201 (32.0) P202 (32.0) P203 (0.0)
P204 (0.0) P205 (0, 0) P206 (0.0) P207 (32.0)
P209 (0.0) P210 (32.0) P211 (32.0)
P212 (32.0) P213 (0.0) P214 (32.0) P215 (32.0)
P216 (0.0) P217 (0.0) P218 (32.0) P219 (0.0)
P220 (32.0) P221 (32.0) P222 (32.0) P223 ( 32.0)
P224 (32.0) P225 (0.0) P226 (32.0) P228 (0.0) P229 (0.0) P230 (0.0) P231 (0.0)
P232 (0, 0) P233 (32.0) P234 (0.0) P235 (0.0)
P236 (32.0) P237 (32.0) P238 (0.0) P239 (0.0)
P240 (0.0) P241 (0.0) P242 (32.0) P243 (0.0)
P244 (0.0) P245 (32.0) P246 (0.0) P247 (0.0)
P248 (0.0) P249 (32.0) P250 (32.0) P252 (0.0) P253 (0.0) P254 (0.0) P255 (32.0)
P256 (32.0) P259 ( 0.0)
P260 (0.0) P261 (0.0) P262 (0.0) P263 (0.0)
P264 (32.0) P265 (32.0) P266 (0.0) P267 (0, 0)
P268 (0.0) P269 (32.0) P271 (32.0)
P272 (0.0) P273 (32.0) P274 (0.0) P275 (0.0)
P276 (32.0) P277 (0.0) P279 (32.0)
P280 (0.0) P281 (0.0) P282 (0.0) P283 (32.0)
P284 (32.0) P285 (0.0) P286 ( 0.0) P287 (32.0)
P288 (32.0) P289 (0.0) P290 (0.0) P291 (0.0)
P292 (32.0) P294 (0.0) P295 (0, 0)
P296 (0.0) P297 (32.0) P298 (32.0) P299 (0.0)
P300 (0.0) P301 (0.0) P302 (0.0) P303 (0.0)
P304 (0.0) P306 (32.0) P307 (0.0)
P308 (0.0) P309 (0.0) P310 (32.0) P311 (0.0)
P312 (0.0) P313 (0.0) P314 (32.0) P315 (0.0)
P316 (0.0) P317 (0.0) P318 (0.0) P319 (0.0)
P321 ( 0.0) P322 (0.0) P324 (32.0) P325 (0.0) P326 (0.0) P327 (0.0)
P329 (32.0) P330 (32.0) P331 (0, 0)
P332 (0.0) P333 (0.0) P334 (0.0) P335 (0.0)
P336 (0.0) P337 (32.0) P338 (32.0) P339 (0.0)
P340 (0.0) P341 (0.0) P342 (0.0) P343 (0.0)
P344 (0.0) P345 (0.0) P346 (0.0) P347 (0.0)
P348 ( 32.0) P349 (32.0) P350 (0.0) P351 (0.0)
P352 (32.0) P353 (32.0) P354 (0.0) P355 (0.0)
P356 (32, 0) P357 (32.0) P358 (0.0) P360 (0.0) P361 (32.0) P362 (32.0) P363 (32.0)
P364 (32.0) P366 (32.0) P368 (32.0) P369 (0.0) P370 (0.0) P371 (0.0)
P372 (0.0) P373 (0.0) P374 (0.0) P375 (32.0)
P376 (32.0) P377 (0.0) P378 (32.0) P379 (0.0)
P380 (0.0) P381 (0.0) P382 (0.0) P383 (0.0)
P384 ( 32.0) P385 (0.0) P386 (32.0) P387 (0.0)
P388 (32.0) P389 (0.0) P390 (0.0) P393 (32.0) P400 (0, 0) P404 (32.0) P410 (0.0) P411 (0.0)
P412 (32.0) P414 (32.0)
Source please
 

Legion13

Участник
42
4
Я установил Samp.lua в монлоодер в папку либ.
Но когда я пишу код на самп.луа, все игнорируется
Помогите!


function sampev.OnSendChat(text)
return {'Текст'}
end


unknown.png
 

Horsi

Участник
11
86
soundboard.lua:
script_name('soundboard')
script_properties('work-in-pause')

require 'lib.sampfuncs'
local rmu = require 'rmlib.utils' --small library of my functions
local SE = require 'lib.samp.events'
local ML = require 'moonloader'
local inicfg = require 'inicfg'

local slink = {
    "https://pastebin.com/raw/tEMBtwkd"
}

local coreset = inicfg.load({
    sb = {
        slink = 1,
        enabled = true,
        sb_3d = false,
        autocomp = false,
        chain = false,
        ch_max = 3,
        str_max = 5,
        str_vol = 1,
        r_vol = 1,
        dbg = false
    }
}, "rm_coreset")

if coreset.sb.dbg then rmu.dbg = true end

local operators = {}

local sb_patt = ":(%w+):"

local uid
local uname
local str_sources = {}
local str_groups = {}
local str_info = {}
local str_locals = {}
local str_table = {}
local scount = 0
local rstream
local r_sources = {}

local player_mutes = {}
local str_mutes = {}
local str_vol_mult = {}

local sb_acthr
local sb_ac_cls = false

local debugstr

local soundsearch = {
dict = nil,
spctbl = {},
revtbl = {}
}

local radiosearch = {
dict = nil,
spctbl = {},
revtbl = {}
}

local objArray = {}

local dl_reg = {}
local dl_codes = {}
local users = {}
local rm_ready = false

function inTable(list,key)
    return list[key] ~= nil
end

function load_strvol(tname, tbl)
    local afile = io.open('moonloader/soundboard/'..tname..'.txt', 'r')
    if afile then
        for line in io.lines('moonloader/soundboard/'..tname..'.txt') do
            local alias, vlm = string.match(line,"(%w+) (%S+)")
            vlm = tonumber(vlm)
            if alias and vlm then tbl[alias] = vlm end
        end
    end
end

function dl_handler(id, status, p1, p2)
    local tstatus
    local pstatus
    local code
    if dl_codes[status] then code = dl_codes[status] else code = status end
    dl_reg[id] = status
    if coreset.sb.dbg then
        tstatus = "rm.src_dl: "..code.." "..p1.."/"..p2
        if pstatus ~= tstatus then sampfuncsLog(tstatus) end
        pstatus = tstatus
    end
end

function src_dl(link, path)
    if not path or not link then return nil end
    if coreset.sb.dbg then sampfuncsLog("rm.src_dl: "..path) end
    local dl_id = downloadUrlToFile(link, path, dl_handler)
    dl_reg[dl_id] = false
    return dl_id
end

function dcodes()
    local ori = ML.download_status
    local output = {}
    for k, v in pairs(ori) do output[v] = k end
    return output
end

local set_info = {
    sb = {
        mute = "Disables sound command processing from player SB MUTE [ID/NAME]",
        unmute = "Enables sound command processing from player SB UNMUTE [ID/NAME]",
        volume = "Sets main volume SB VOLUME/VOL [1-100]",
        strvol = "Adjusts single sound command volume without affecting others SB STRVOL [SOUND] [1-100]",
        ignore = "Ignores single sound processing (mutes it) SB IGNORE [SOUND]",
        unignore = "Enables ignored sound processing back SB UNIGNORE [SOUND]",
        strmax = "Sets maximum simultaneous playing sound limit SB STRMAX [1-20-!!100!!]",
        setlink = "Allows usage of backup source in case of Google issues SB SETLINK [PRIMARY/SECONDARY] (SB SBRELOAD after this cmd)",
        sound3d = "Switches 3D sound attachment SB SOUND3D [ENABLE/DISABLE]",
        autocomplete = "Fills the chat with most matching sound command as you type SB AUTOCOMPLETE/AC [ENABLE/DISABLE]",
        chain = "Switches multiple sound chains on/off, or sets maximum chain length SB CHAIN [ENABLE/DISABLE/NUM(2-20)]",
        sbreload = "Resets all temporary data and attempts to reload from source SB SBRELOAD",
        radio = "Plays radio stream, sets its volume or stops it SB RADIO [STREAM ALIAS/STOP/VOLUME/VOL] [0-100]",
        sinfo = "Displays sound command description SB SINFO [SOUND]",
        ginfo = "Shows all sound commands in a group SB GINFO [GROUP] [PAGE]",
        search = "Browses all avalible sound commands by part of a sound name SB SEARCH [NAME]",
        cmds = "mute unmute volume strvol ignore unignore sound3d radio sinfo ginfo search"
    }
}


local sb_set = {
    mute =        function(op)
                    if not op[2] then getinfo({"info","mute"}) ; return nil end
                    local pname = rmu.nicheck(op[2])
                    if not pname then sampAddChatMessage("[SB] Player ID "..op[2].." not connected", 0xC1C1C1) ; return nil end
                    player_mutes[string.lower(pname)] = true
                    rmu.updatelist('moonloader/soundboard/muted.txt', player_mutes)
                    sampAddChatMessage("[SB] "..pname.." is muted", 0xC1C1C1)
                end,

    unmute =    function(op)
                    if not op[2] then getinfo({"info","unmute"}) ; return nil end
                    local pname = rmu.nicheck(op[2])
                    if not pname then sampAddChatMessage("[SB] Player ID "..op[2].." not connected", 0xC1C1C1) ; return nil end
                    if inTable(player_mutes, string.lower(pname)) then
                        player_mutes[string.lower(pname)] = nil
                        rmu.updatelist('moonloader/soundboard/muted.txt', player_mutes)
                        sampAddChatMessage("[SB] "..pname.." is no longer muted", 0xC1C1C1)
                    else
                        sampAddChatMessage("[SB] "..pname.." was not muted", 0xC1C1C1)
                    end
                end,           
                    
    volume =     function(op)
                    local vlm = tonumber(op[2])
                    if vlm and vlm >= 0 and vlm <= 100 then
                        coreset.sb.str_vol = vlm/100
                        sampAddChatMessage("[SB] Volume set to "..vlm, 0xC1C1C1)
                    else
                        sampAddChatMessage("[SB] Volume should be a number between 0 and 100", 0xC1C1C1)
                    end
                end,
                    
    strvol =     function(op)
                    local vlm = tonumber(op[3])
                    if not vlm or vlm < 1 or vlm > 100 then sampAddChatMessage("[SB] Stream volume should be a number between 1 and 100", 0xC1C1C1) ; return nil end
                    if inTable(str_sources, op[2]) then
                        if vlm == 100 then str_vol_mult[op[2]] = nil else str_vol_mult[op[2]] = vlm/100 end
                        io.open('moonloader/soundboard/strvols.txt', 'w'):close()
                        local afile = io.open('moonloader/soundboard/strvols.txt', 'a')
                        for k, v in pairs(str_vol_mult) do
                            afile:write(k.." "..v.."\n")
                        end
                        afile:close()
                        sampAddChatMessage("[SB] Sound command '"..op[2].."' relative volume set to "..vlm, 0xC1C1C1)
                    else
                        sampAddChatMessage("[SB] Sound command not found", 0xC1C1C1)
                    end
                end,

    ignore =     function(op)
                    if not op[2] then getinfo({"info","ignore"}) ; return nil end
                    if inTable(str_sources, op[2]) then
                        str_mutes[op[2]] = true
                        sampAddChatMessage("[SB] Sound command '"..op[2].."' will be ignored", 0xC1C1C1)
                        rmu.updatelist('moonloader/soundboard/ignored.txt', str_mutes)
                    else
                        sampAddChatMessage("[SB] Sound command not found", 0xC1C1C1)
                    end
                end,

    unignore =     function(op)
                    if not op[2] then getinfo({"info","unignore"}) ; return nil end
                    if inTable(str_mutes, op[2]) then
                        str_mutes[op[2]] = nil
                        sampAddChatMessage("[SB] Sound command '"..op[2].."' will be activated", 0xC1C1C1)
                        rmu.updatelist('moonloader/soundboard/ignored.txt', str_mutes)
                    else
                        sampAddChatMessage("[SB] Sound command was not ignored", 0xC1C1C1)
                    end
                end,                   

    strmax =    function(op)
                    local smax = tonumber(op[2])
                    if smax and smax >= 1 and smax <= 100 then
                        if smax > 20 then
                            sampAddChatMessage("[SB] You are setting max possible simultaneous playing streams above 20", 0xC1C1C1)
                            sampAddChatMessage("[SB] I will let that sink in for a minute", 0xC1C1C1)
                        end
                        coreset.sb.str_max = smax
                        sampAddChatMessage("[SB] str_max set to "..coreset.sb.str_max..". Hope you know what youre doing.", 0xC1C1C1)
                    else
                        sampAddChatMessage("[SB] Max running stream count must be a number between 1 and 100", 0xC1C1C1)
                    end
                end,
                
    chain =        function(op)
                    if not op[2] then getinfo({"info","chain"}) ; return nil end
                    local cmax = tonumber(op[2])
                    if cmax then
                        if cmax > 1 and cmax < 21 then
                            coreset.sb.ch_max = cmax
                            sampAddChatMessage("[SB] Sound command chain max length set to "..cmax, 0xC1C1C1)
                        else
                            sampAddChatMessage("[SB] Sound command chain length cannot exceed 20. Because.", 0xC1C1C1)
                        end
                    elseif op[2] == "enable" then
                        coreset.sb.chain = true
                        sampAddChatMessage("[SB] Sound command chain enabled", 0xC1C1C1)
                    elseif op[2] == "disable" then
                        coreset.sb.chain = false
                        sampAddChatMessage("[SB] Sound command chain disabled", 0xC1C1C1)
                    else
                        getinfo({"info","chain"})
                    end
                end,
                    
    setlink =    function(op)
                    if not op[2] then getinfo({"info","setlink"}) ; return nil end
                    if op[2] == "primary" or op[2] == "1" then
                        coreset.sb.slink = 1
                        sampAddChatMessage("[SB] streams.txt source set to primary", 0xC1C1C1)
                    elseif op[2] == "secondary" or op[2] == "2" then
                        coreset.sb.slink = 2
                        sampAddChatMessage("[SB] streams.txt source set to secondary", 0xC1C1C1)
                    else
                        getinfo({"info","setlink"})
                    end
                end,
                    
    sound3d =    function(op)
                    if not op[2] then getinfo({"info","sound3d"}) ; return nil end
                    if op[2] == "enable" or op[2] == "1" then
                        coreset.sb.sb3d = true
                        sampAddChatMessage("[SB] 3D sound commands enabled", 0xC1C1C1)
                    elseif op[2] == "disable" or op[2] == "0" then
                        coreset.sb.sb3d = false
                        sampAddChatMessage("[SB] 3D sound commands disabled", 0xC1C1C1)
                    else
                        getinfo({"info","sound3d"})
                    end
                end,
                
    autocomplete =    function(op)
                    if not op[2] then getinfo({"info","autocomplete"}) ; return nil end
                    if op[2] == "enable" or op[2] == "1" then
                        coreset.sb.autocomp = true
                        sampAddChatMessage("[SB] Sound command autocomplete enabled", 0xC1C1C1)
                    elseif op[2] == "disable" or op[2] == "0" then
                        coreset.sb.autocomp = false
                        sampAddChatMessage("[SB] Sound command autocomplete disabled", 0xC1C1C1)
                    else
                        getinfo({"info","autocomplete"})
                    end
                end,
                    
    sbreload =     function()
                    lua_thread.create(function()
                        sampAddChatMessage("[SB] RELOADING: please wait", 0xC1C1C1)
                        sbquiet() ; sampAddChatMessage("[SB] ending running audio commands", 0xC1C1C1)
                        sb_set["radio"]({"radio","stop"}) ; sampAddChatMessage("[SB] stopping radio", 0xC1C1C1)
                        coreset.sb.enabled = false ; sampAddChatMessage("[SB] disabling command listening", 0xC1C1C1)
                        str_sources = {} ; sampAddChatMessage("[SB] clearing data", 0xC1C1C1)
                        str_locals = {}
                        str_table = {}
                        player_mutes = {}                   
                        str_mutes = {}                     
                        str_vol_mult = {} ; sampAddChatMessage("[SB] downloading streams", 0xC1C1C1)
                        src_dl(slink[coreset.sb.slink], getGameDirectory()..'\\moonloader\\soundboard\\streams.txt') ; sampAddChatMessage("[SB] loading mutes", 0xC1C1C1)
                        rmu.loadlist('moonloader/soundboard/muted.txt', player_mutes) ; sampAddChatMessage("[SB] loading ignores", 0xC1C1C1)
                        rmu.loadlist('moonloader/soundboard/ignored.txt', str_mutes) ; sampAddChatMessage("[SB] loading volume array", 0xC1C1C1)
                        load_strvol("strvol", str_vol_mult)
                        wait(3000)
                        loadstreams()  ; sampAddChatMessage("[SB] streams loaded", 0xC1C1C1)
                        rmu.dictload(str_sources, soundsearch) ; sampAddChatMessage("[SB] loading soundsearch dictionary", 0xC1C1C1)
                        rmu.dictload(r_sources, radiosearch) ; sampAddChatMessage("[SB] loading radiosearch dictionary", 0xC1C1C1)
                        coreset.sb.enabled = true ; sampAddChatMessage("[SB] enabling command listening", 0xC1C1C1)
                        sampAddChatMessage("[SB] reloaded", 0xC1C1C1)
                    end)
                end,
                    
    radio =     function(op)
                    if not op[2] then getinfo({"info","radio"}) ; return nil end
                    if op[2] == "stop" then
                        if rstream then
                            setAudioStreamState(rstream, ML.audiostream_state.STOP)
                            rstream = nil
                        end
                        
                    elseif op[2] == "volume" or op[2] == "vol" then
                        local vlm = tonumber(op[3])
                        if vlm and vlm >= 0 and vlm <= 100 then
                            coreset.sb.r_vol = vlm/100
                            sampAddChatMessage("[SB] Radio volume set to "..vlm, 0xC1C1C1)
                            if rstream then setAudioStreamVolume(rstream, coreset.sb.r_vol) end
                        else
                            sampAddChatMessage("[SB] Volume should be a number between 0 and 100", 0xC1C1C1)
                        end
                    
                    else
                        if inTable(r_sources, op[2]) then
                            if rstream then
                                setAudioStreamState(rstream, ML.audiostream_state.STOP)
                                rstream = nil
                            end
                            rstream = loadAudioStream(r_sources[op[2]])
                            if coreset.sb.r_vol ~= 1 then setAudioStreamVolume(rstream, coreset.sb.r_vol) end
                            setAudioStreamState(rstream, ML.audiostream_state.PLAY)
                            if inTable(str_info, op[2]) then sampAddChatMessage("[SB] Now playing "..str_info[op[2]], 0xC1C1C1) end
                        else
                            if coreset.sb.dbg then sampfuncsLog("rm.sb_set.radio: Could not find radio stream "..op[2]) end
                        end
                    end
                end,
                    
    sinfo =     function(op)
                    if not op[2] then getinfo({"info","sinfo"}) ; return nil end
                    local alias = string.lower(op[2])
                    if inTable(str_info, alias) then sampAddChatMessage("[SB] "..alias..": "..str_info[alias], 0xC1C1C1) end
                end,
                    
    ginfo =     function(op)
                    if not op[2] then getinfo({"info","ginfo"}) ; return nil end
                    local list = ": "
                    local page = 1
                    local gname = string.lower(op[2])
                    if not gname then return nil end
                    if inTable(str_groups, gname) then
                        local ppar =  tonumber(op[3])
                        if ppar then if ppar >= 1 then page = ppar end end
                        local eist = 1+(page-1)*10
                        for ei = eist, #str_groups[gname] do
                            list = list..str_groups[gname][ei].." "
                            if ei > 9+(page-1)*10 then
                                list = list.."(and "..#str_groups[gname]-ei.." more)"
                                break
                            end
                        end
                        sampAddChatMessage("[SB] "..string.upper(gname)..list, 0xC1C1C1)
                    end
                end,
    
    search =     function(op)
                    if not op[2] then getinfo({"info","search"}) ; return nil end
                    local dfind
                    local rfind
                    if op[3] then
                        if string.lower(op[3]) == "scmds" then result, dfind, mcount = rmu.dictfind(op[2], soundsearch) end
                        if string.lower(op[3]) == "radio" then result, rfind, mcount = rmu.dictfind(op[2], radiosearch) end
                    else
                        result, dfind, mcount = rmu.dictfind(op[2], soundsearch)
                        result, rfind, mcount = rmu.dictfind(op[2], radiosearch)
                    end
                    if dfind then sampAddChatMessage("[SB] scmds:"..dfind, 0xC1C1C1) end
                    if rfind then sampAddChatMessage("[SB] radio:"..rfind, 0xC1C1C1) end
                end,
    

    info =         function(op)
                    if not op[2] then
                        sampAddChatMessage("[SoundBoard v1.0.2] Lua script by Pink. RAF2020UIF", 0xFF7AD1)
                    else
                        if inTable(set_info.sb, string.lower(op[2])) then
                            sampAddChatMessage("[SB] "..string.upper(op[2])..": "..set_info.sb[string.lower(op[2])], 0xC1C1C1)
                        else
                            if inTable(sb_set, string.lower(op[2])) then
                                sampAddChatMessage("[SB] No information about that command", 0xC1C1C1)
                            else
                                sampAddChatMessage("[SB] Unknown command", 0xC1C1C1)
                            end
                        end
                    end
                end,

    help =         function(op)
                    sampAddChatMessage("[SoundBoard] Write '//sb info cmds' for command list. Write //sb info [command] for cmd syntax and info", 0xC1C1C1)
                end
    }
sb_set.vol    = sb_set.volume
sb_set.find = sb_set.search
sb_set.ac = sb_set.autocomplete

function getinfo(par)
    sb_set.info(par)
end

function sbenable(par)
    if #par == 0 then
        coreset.sb.enabled = not coreset.sb.enabled
        sampAddChatMessage("[SB] Soundboard "..(coreset.sb.enabled and "enabled" or "disabled"), 0xC1C1C1)
    else
        operators = {}
        for op in par:gmatch("%S+") do table.insert(operators, string.lower(op)) end
        if inTable(sb_set,operators[1]) then sb_set[operators[1]](operators) end
    end
end

function sbquiet()
    for i=1, scount do
        if getAudioStreamState(str_table[i]) == ML.audiostream_status.PLAYING then
            setAudioStreamState(str_table[i], ML.audiostream_state.STOP)
        end
        str_table[i] = nil
        scount = scount - 1
    end
end

function playstream(alias, pname, pid)
    local astream
    lua_thread.create(function()
        if not inTable(str_locals, alias) then
            if not doesFileExist('moonloader\\soundboard\\mp3\\'..alias..'.mp3') then
                local dl_id = downloadUrlToFile(str_sources[alias], getGameDirectory()..'\\moonloader\\soundboard\\mp3\\'..alias..".mp3", dl_handler)
                dl_reg[dl_id] = false
                local tim = 1
                repeat
                    wait(100)
                    tim = tim + 1
                until (dl_reg[dl_id] == ML.download_status.STATUSEX_ENDDOWNLOAD or tim > 50)
                wait(1000)
                if dl_reg[dl_id] == ML.download_status.STATUSEX_ENDDOWNLOAD then astream = loadstream('moonloader\\soundboard\\mp3\\'..alias..'.mp3',coreset.sb.sb3d) end
            end
            str_locals[alias] = true
        end
        if not astream then astream = loadstream('moonloader\\soundboard\\mp3\\'..alias..'.mp3',coreset.sb.sb3d) end
        if not astream then
            if coreset.sb.dbg then sampfuncsLog("rm.playstream: Failure to load stream "..alias.. ", retrying") end
            wait(2000)
            astream = loadstream('moonloader\\soundboard\\mp3\\'..alias..'.mp3',coreset.sb.sb3d)
            wait(500)
            if not astream then if coreset.sb.dbg then sampfuncsLog("rm.playstream: Failure to reload stream "..alias) end ; return nil end
        end
        local end_vol = coreset.sb.str_vol
        if not coreset.sb.sb3d then
            if inTable(str_vol_mult, alias) then end_vol = end_vol * str_vol_mult[alias] end
        else
            if pid == uid then
                setPlay3dAudioStreamAtChar(astream, playerPed)
            else
                local result, ped = sampGetCharHandleBySampPlayerId(pid)
                if result and doesCharExist(ped) then setPlay3dAudioStreamAtChar(astream, ped) end
            end
        end
        setAudioStreamVolume(astream, end_vol)
        setAudioStreamState(astream, ML.audiostream_state.PLAY)
        table.insert(str_table, 1, astream)
        scount = scount + 1
        if scount > coreset.sb.str_max then
            if getAudioStreamState(str_table[coreset.sb.str_max + 1]) == ML.audiostream_status.PLAYING then
                setAudioStreamState(str_table[coreset.sb.str_max + 1], ML.audiostream_state.STOP)
            end
            str_table[coreset.sb.str_max + 1] = nil
            scount = scount - 1
        end
    end)
    return astream
end

function loadstream(strLink, play3d)
    if coreset.sb.dbg then sampfuncsLog("rm.loadstream: Loading "..strLink..(play3d and " for 3d support" or "")) end
    if play3d then return load3dAudioStream(strLink) else return loadAudioStream(strLink) end
end


local loadfunc = {
    scmds =     function(input)
                    local alias, str_link = string.match(input,"(%w+) (%S+)")
                    if alias and str_link then str_sources[alias] = str_link end
                end,
                    
    rstreams =    function(input)
                    local rname, str_link = string.match(input,"(%w+) (%S+)")
                    if rname and str_link then r_sources[rname] = str_link end
                end,
        
    sinfo =     function(input)
                    local alias, info = string.match(input,":(%w+): (.+)")
                    if alias and info then str_info[alias] = info end
                end,
                        
    sgroups =     function(input)
                    local gname, contents = string.match(input,":(%w+): (.+)")
                    if gname and contents then
                        local ctable = {}
                        for entry in contents:gmatch("%w+") do table.insert(ctable, entry) end
                        str_groups[gname] = ctable
                    end
                end
    }
                    
function loadstreams()
    local result
    lua_thread.create(function()
        local timeout = false
        local curfunc = "scmds"
        local afile = io.open('moonloader/soundboard/streams.txt', 'r')
        ::reloadstreams::
        if afile then
            for line in io.lines('moonloader/soundboard/streams.txt') do
                if line ~= '' then
                    local lfunc = string.match(line,"^%[(%w+)%]")
                    if lfunc then
                        if inTable(loadfunc, lfunc) then curfunc = string.lower(lfunc) end
                    else
                        loadfunc[curfunc](line)
                    end
                end
            end
            result = true
        elseif afile == nil and timeout == false then
            timeout = true
            if coreset.sb.dbg then sampfuncsLog("rm.loadstreams: Failure to load streams, retrying") end
                tim = 1
                while (afile == nil or tim < 10) do
                    wait(200)
                    afile = io.open('moonloader/soundboard/streams.txt', 'r')
                    wait(500)
                    tim = tim + 1
                end
            if afile then goto reloadstreams else result = false end
        end
    end)
    return result
end


function main()
    sampRegisterChatCommand("/sb", sbenable)
    sampRegisterChatCommand("/sbq", sbquiet)
    
    local scr_handle = thisScript()
    local tim
    dl_codes = dcodes()
    
    if not doesDirectoryExist('moonloader/soundboard') then createDirectory('moonloader/soundboard') end
    if not doesDirectoryExist('moonloader/soundboard/mp3') then createDirectory('moonloader/soundboard/mp3') end
    
    local streams_load = src_dl(slink[coreset.sb.slink], getGameDirectory()..'\\moonloader\\soundboard\\streams.txt')
    
    tim = 1
    local load_result = false
    local dl_end = ML.download_status.STATUSEX_ENDDOWNLOAD
    repeat
        wait(100)
        if dl_reg[streams_load] == dl_end then load_result = true else load_result = false end
        tim = tim + 1
    until (load_result or tim > 50)
    
    rmu.loadlist('moonloader/soundboard/muted.txt', player_mutes)
    rmu.loadlist('moonloader/soundboard/ignored.txt', str_mutes)
    load_strvol("strvol", str_vol_mult)
    
    if not load_result then wait(3000) end
    
    local sload = loadstreams()
    tim = 1
    repeat
        wait(100)
        tim = tim + 1
    until (sload or tim > 50)
    if not sload then
        if coreset.sb.dbg then sampfuncsLog("rm.main: Script failed to create stream dictionary. Soundboard will be disabled.") end
        coreset.sb.enabled = false
    end
    
    result = rmu.dictload(str_sources, soundsearch)
    if result == false then
        sampfuncsLog("Script failed to create stream dictionary. Soundboard will be disabled.")
        coreset.sb.enabled = false
    end
    result = rmu.dictload(r_sources, radiosearch)
    if result == false then sampfuncsLog("Radio sources were not present to load radio dictionary") end
    
    while not isSampAvailable() do wait(0) end
    while not sampIsLocalPlayerSpawned() do wait(0) end
    
    _, uid = sampGetPlayerIdByCharHandle(playerPed)
    uname = sampGetPlayerNickname(uid)
    
    rm_ready = true
    watchman()
    
    --[[addEventHandler('onWindowMessage', function(msg, wparam, lparam)
            if coreset.sb.autocomp then
                if msg == 0x100 then
                    if wparam == VK_T then
                        lua_thread.create(ac_eval)
                    elseif wparam == VK_RETURN then
                        if sb_acthr then if not sb_acthr.dead then sb_acthr:terminate() end end
                    end
                end
            end
        end)]]
    
    while true do
        wait(0)
    end
end

function watchman()
    local paused
    local ltime = os.time()
    lua_thread.create(function()
        while true do
            if (isGamePaused() or (os.time() - ltime > 5)) and rm_ready then   
                rm_ready = false
                if coreset.sb.dbg then sampfuncsLog("rm.watchman: paused") end
            end
            if not isGamePaused() and (os.time() - ltime < 5) and not rm_ready then
                wait(1000)
                rm_ready = true
                if coreset.sb.dbg then sampfuncsLog("rm.watchman: ready") end
            end
            ltime = os.time()
            wait(0)
        end
    end)
end

function ac_eval()
    wait(50)
    local active = sampIsChatInputActive()
    if active then
        if sb_acthr then
            if sb_acthr.dead then autocomplete() end
        else
            autocomplete()
        end
    else
        if sb_acthr then
            if not sb_acthr.dead then sb_acthr:terminate() end
        end
    end
end

function autocomplete()
    local rinput = false
    local pstring
    local cstring
    local lastsign
    local strbody
    local scmd
    local smatch
    local smatchcount
    local sset
    
    local function sClear()
        rinput = false
        pstring = nil
        cstring = nil
        lastsign = nil
        strbody = nil
        scmd = nil
        smatch = nil
        sset = nil
    end
    
    local function chatset(input, cls)
        cstring = input
        sampSetChatInputText(cstring)
        if cls then
            rinput = false
            sClear()
        end
    end
    
    local function sconstr()
        smatch, _, smatchcount = rmu.dictfind(scmd, soundsearch, true)
        if smatch and smatch ~= scmd then
            sset = strbody..":"..scmd..string.char(smatchcount > 1 and 166 or 124)..string.sub(smatch, #scmd-#smatch)
        else
            sset = strbody..":"..scmd
        end
    end
    
    sb_acthr = lua_thread.create(function()
        while true do
            cstring = sampGetChatInputText()
            if cstring and pstring and pstring ~= cstring then
                lastsign = string.sub(cstring, -1)
                if #cstring > #pstring then
                    if lastsign == ":" then
                        if rinput then
                            sClear()
                        else
                            strbody = pstring
                            rinput = true
                        end
                    elseif rinput and lastsign ~= ":" then
                        if lastsign:match("%a") then
                            scmd = scmd and scmd..lastsign or lastsign
                            sconstr()
                            chatset(sset, false)
                            
                        elseif lastsign == " " or lastsign == ":" then
                            if smatch then chatset(strbody..":"..smatch..":", false) end
                            sClear()
                        else
                            sClear()
                        end
                    end
                else
                    if rinput and scmd then
                        scmd = string.sub(scmd, 1, #scmd-1)
                        if #scmd > 0 then
                            sconstr()
                            chatset(sset, false)
                        else
                            chatset(strbody, true)
                        end
                    end
                end
            end
            pstring = cstring
            wait(10)
        end
    end)
end

function SE.onChatMessage(playerId, text)
    if not rm_ready then return nil end
    if not coreset.sb.enabled then return nil end
    if text == nil then return nil end
    if not sampIsPlayerConnected(playerId) then return nil end
    local pname = sampGetPlayerNickname(playerId)
    if inTable(player_mutes, string.lower(pname)) then return nil end
    sb_rextest(text, pname, playerId)
end

function SE.onServerMessage(color, text)
    if not rm_ready then return nil end
    if not coreset.sb.enabled then return nil end
    if text == nil then return nil end
    local smatch = string.match(text, "(%S+%(%d+%))")
    if smatch == nil then return nil end
    local pname = string.match(smatch, "(%S+)%(%d+%)")
    local pid = tonumber(string.match(smatch, "%S+%((%d+)%)"))
    if inTable(player_mutes, string.lower(pname)) then return nil end
    sb_rextest(text, pname, pid)
end

function SE.onSendChat(message)
    if coreset.sb.autocomp then
        if message then
            msg, _ = string.gsub(message, "(.*:%a+)["..string.char(166)..string.char(124).."](%a+)$", "%1%2:", 1)
            if msg ~= message then
                sb_ac_cls = true
                return {msg}
            end
        end
    end
end

function SE.onSendCommand(message)
    if coreset.sb.autocomp then
        if message then
            msg, _ = string.gsub(message, "(.*:%a+)["..string.char(166)..string.char(124).."](%a+)$", "%1%2:", 1)
            if msg ~= message then
                sb_ac_cls = true
                return {msg}
            end
        end
    end
end

function sb_rextest(text, pname, pid)
    local chi
    local astream
    lua_thread.create(function()
        chi = 1
        for sb_match in string.gmatch(text,sb_patt) do
            if string.len(sb_match) > 2 and string.len(sb_match) < 21 then
                if inTable(str_sources, sb_match) then
                    if not inTable(str_mutes, sb_match) then astream = playstream(sb_match, pname, pid) end
                    if not coreset.sb.chain then break end
                    local tim = 1
                    local as_state
                    repeat
                        if astream then as_state = getAudioStreamState(astream) end
                        tim = tim + 1
                        wait(50)
                    until (as_state == ML.audiostream_status.STOPPED or tim > 200)
                end
            end
            chi = chi + 1
            if chi > coreset.sb.ch_max then break end
        end
    end)
end

function onScriptTerminate(script, quitGame)
    if script == thisScript() then inicfg.save(coreset, "rm_coreset") else return nil end
end

function onQuitGame()
    inicfg.save(coreset, "rm_coreset")
    setClipboardText(debugstr)
end

--[[function onWindowMessage(msg, wparam, lparam)
    if coreset.sb.autocomp then
        if msg == 0x100 then
            if wparam == VK_T then
                lua_thread.create(ac_eval)
            elseif wparam == VK_RETURN then
                if sb_acthr then if not sb_acthr.dead then sb_acthr:terminate() end end
            end
        end
    end
end]]
 

Ferrat

Известный
64
29
как узнать если тебя кикнуло с сервера/сервер рестарт?
Кикнуло вроде onConnectionClosed но не знаю точно
 

FYP

Известный
Автор темы
Администратор
1,763
5,911
Добавьте event onPlayerStateChange
библиотека реализует только события для входящих и исходящих raknet пакетов. в сампе нет отдельного пакета сигнализирующего об изменении состояния игрока. серверное событие OnPlayerStateChange срабатывает когда клиент отправляет пакеты о спавне, уведомлении о смерти и синхронизации, при условии что новое состояние не соответствует прежнему. с помощью samp.lua отправку этих пакетов можно отслеживать с помощью событий onSendSpawn, onSendDeathNotification, onSendPlayerSync и других, благодаря этому можно реализовать OnPlayerStateChange самостоятельно.
 
  • Клоун
Реакции: Fott

Cosmo

Известный
Друг
653
2,724
@FYP я в замешательстве.

Есть такой входящий RPC как SelectTextDraw (ID: 83), который включает у пользователя курсор и устанавливает цвет при наведении на текстдрав, поддерживающий клик по нему (Возможно херово объяснил, но думаю суть ясна)

Дак вот, в sampfuncs.lua под ID 83 указан вот такое название:
1644936583413.png



В SAMP.lua оно используется как исходящий RPC с информацией о том, что игрок кликнул по текстдраву.
Решил проверить входящий и выходящий RPC с таким ID через этот код:
Lua:
function onReceiveRpc(id, bs)
    if id == 83 then
        local color = raknetBitStreamReadInt32(bs)
        local enable = raknetBitStreamReadBool(bs)

        print(("[Receive] SelectTextDraw | Color: 0x%X | Enable: %s"):format(color, enable))
    end
end

function onSendRpc(id, bs)
    if id == 83 then
        local id = raknetBitStreamReadInt16(bs)

        print(("[Send] ClickTextDraw | ID: %d"):format(id))
    end
end
И вот что получилось:
Lua:
 -- Открыл инвентарь на сервере, мне пришла инфа о том что надо расчехлять курсор
>> [Receive] SelectTextDraw | Color: 0xFFFFFFFFAAAAAAFF | Enable: true

-- Клинул по рандомным текстдравам:
>> [Send] ClickTextDraw | ID: 2184
>> [Send] ClickTextDraw | ID: 2185
>> [Send] ClickTextDraw | ID: 2186

-- Нажал ESC, тобишь закрыл инвентарь и сервер прислал инфу о том что пора убирать курсор обратно
>> [Send] ClickTextDraw | ID: 65535
>> [Receive] SelectTextDraw | Color: 0x0 | Enable: false
>> [Send] ClickTextDraw | ID: 65535

Вопрос: почему разные RPC с одинаковыми ID, и почему onSelectTextDraw нет в SAMP.lua?

UPD:
Разобрался, у меня стояла старая версия библиотеки, в которой не было этого хука
 

FYP

Известный
Автор темы
Администратор
1,763
5,911
почему разные RPC с одинаковыми ID
так захотел разраб сампа. это далеко не единственный rpc, ид которого используется для отправки и клиентом, и сервером. другие имеют более схожее назначение в клиенте и сервере, но тем не менее это разные rpc.
почему onSelectTextDraw нет в SAMP.lua?
есть
 
  • Клоун
  • Нравится
Реакции: Fott и Cosmo