Доброго времени вечера.
В этом гайде я расскажу что такое BitStream и как с ним правильно работать.
В этом гайде я расскажу что такое BitStream и как с ним правильно работать.
SAMP для обменна данными между сервером и игроком использует сетевой движок RakNet.
Передаваемые данные храняться в битовом потоке BitStream.
BitStream - штука (структура), которая хранит в себе даннные, в виде битов.
SAMP может отправлять на сервер пакеты и RPC. Пакеты отличаются от рпц тем, что сначала идет ID пакета, а потом уже его данные (если они есть), а у RPC сразу идут данные (опять же, если есть).
Мы можем перехватывать/отправлять/эмулировать как пакеты, так и рпц, этим мы сейчас и займемся.
Если вы что-то писали на языках по типу C++, то вы знаете, что в таких языках существуют типы данных, вот и при записи и чтении данных с битстрима, нам необходимо указывать тип, которые мы будем читать/вписывать.
Вы заметили, что у некоторых функций есть приставки 8, 16, 32 - это количество битов, которые нам надо записать или прочитать. Float всегда равен 32 битам (4 байта, в одном байте 8 битов, если вы не ходили на информатику). Также существуют другие типы, допустим UInt, который может записать уже число не от –2147483648 до 2147483647 как в int32, а от 0 до 4294967295. Другие функции мунлоадера, для взаимодействия с битстримом вы сможете посмотреть тут или тут.
Допустим нам надо отправить серверу информацию о том, что мы заспавнились, для этого нам нужен 52 RPC, смотрим его структуру и видим, что никаких данных она не содержит.
Чтобы отправить его нам понадобиться просто объявить объекты битстрима и отправить его.
И так, отправить RPC мы смогли, теперь попробуем эмулировать рпц о изменение позиции нашего ТС. Смотрим его структуру.
Мы видим, что в ней находяться значения int16 - айди машины и 3 float, обозначающие координаты x,y,z, объявляем битстрим и заполняем структуру.
С отправкой и эмуляцией RPC мы разобрались, перейдем к пакетам.
Допустим, мы хотим отправить серверу фейковую позицию, для этого нам нужен PACKET_PLAYER_SYNC, вот его структура:
Видем в ней много данных, отвечающих за позицию, скорость, анимацию и так далее, но нам надо изменить только позицию, для этого мы будем устанавливать offset (сдвиг) записи, ведь как мы помним, в битстриме все данные храняться по порядку, т.е. нам нужно передвинуться на нужное количество битов, чтобы достать позицию. Оффсеты можно получить тут (не пинайте, не реклама)
Пакет отправили, теперь давайте эмулируем позицию машины, т.е. PACKET_VEHICLE_SYNC
С отправкой и эмуляциец закончили, теперь перейдем к перехвату ака хуку данных.
О том, как отлавливать RPC, вы можете почитать в этом гайде, я же расскажу про перехват пакетов.
Представим, что мы хотим узнать скокость всех каров в зоне стрима, для этого нам необходимо добавить событие, которое будет отлавливать входящий пакет PACKET_VEHICLE_SYNC, делается это так:
Хук есть, теперь нам нужно прочитать данные из пакета, чтобы после допустим вывести их на экран, для этого используем функции чтения данных из битстрима.
Теперь давайте по аналогии перехватим исходящий пакет PACKET_PLAYER_SYNC и изменим в нем данные.
структуры всех пакетов можно узнать в moonloader/samp/synchronization.lua или в этой теме.
ну вот вроде и все, дописывайте, чем можно дополнить статью.
Передаваемые данные храняться в битовом потоке BitStream.
BitStream - штука (структура), которая хранит в себе даннные, в виде битов.
SAMP может отправлять на сервер пакеты и RPC. Пакеты отличаются от рпц тем, что сначала идет ID пакета, а потом уже его данные (если они есть), а у RPC сразу идут данные (опять же, если есть).
Мы можем перехватывать/отправлять/эмулировать как пакеты, так и рпц, этим мы сейчас и займемся.
Если вы что-то писали на языках по типу C++, то вы знаете, что в таких языках существуют типы данных, вот и при записи и чтении данных с битстрима, нам необходимо указывать тип, которые мы будем читать/вписывать.
Lua:
raknetBitStreamWriteBool(bs, value) -- вписывает true/false значение в битстрим
raknetBitStreamWriteInt8(bs, value) -- вписывает числовое значение, весом до 8 бит
raknetBitStreamWriteInt16(bs, value) -- вписываетчисловое значение, весом до 16 бит
raknetBitStreamWriteInt32(bs, value) -- вписывает числовое значение, весом до 32 бит
raknetBitStreamWriteFloat(bs,value) -- вписывает значение с плавающей точкой (дроби)
raknetBitStreamWriteString(bs, value) -- вписывает строку
raknetBitStreamEncodeString(bs, value) -- вписывает декодированную строку (используется допустим в dialog'ах)
raknetBitStreamWriteBuffer(bs, dest, size) -- вписывает buffer значение
Вы заметили, что у некоторых функций есть приставки 8, 16, 32 - это количество битов, которые нам надо записать или прочитать. Float всегда равен 32 битам (4 байта, в одном байте 8 битов, если вы не ходили на информатику). Также существуют другие типы, допустим UInt, который может записать уже число не от –2147483648 до 2147483647 как в int32, а от 0 до 4294967295. Другие функции мунлоадера, для взаимодействия с битстримом вы сможете посмотреть тут или тут.
Допустим нам надо отправить серверу информацию о том, что мы заспавнились, для этого нам нужен 52 RPC, смотрим его структуру и видим, что никаких данных она не содержит.
Lua:
{52, {}, description = "Отправляется при появлении.", name = "SPAWN"}
Lua:
local bs = raknetNewBitStream() -- объявляем битстрим класс
raknetSendRpc(52, bs) -- 52 - id RPC, bs - наш битстрим, который мы объявили выше
raknetDeleteBitStream(bs) -- удаляем наш объект (в RakLua можно не удалять, т.к. в нем сборщик мусора делает это за вас, но в данном случае мы используем функции sampfuncs'а, а не RakLua
И так, отправить RPC мы смогли, теперь попробуем эмулировать рпц о изменение позиции нашего ТС. Смотрим его структуру.
Lua:
{159, {"vehicleId", "int16"}, {"positionX", "float"}, {"positionY", "float"}, {"positionZ", "float"}, name = "SETVEHICLEPOS"}
Lua:
local bs = raknetNewBitStream()
raknetBitStreamWriteInt16(bs, 1023) -- изменяем позицию у машины с ID 1023
raknetBitStreamWriteFloat(bs, 0) -- posX
raknetBitStreamWriteFloat(bs, 0) -- posY
raknetBitStreamWriteFloat(bs, 0) -- posZ
raknetEmulRpcReceiveBitStream(159, bs) -- id RPC - 159
raknetDeleteBitStream(bs)
С отправкой и эмуляцией RPC мы разобрались, перейдем к пакетам.
Допустим, мы хотим отправить серверу фейковую позицию, для этого нам нужен PACKET_PLAYER_SYNC, вот его структура:
Lua:
{207, {"leftRightKeys", "int16"}, {"upDownKeys", "int16"}, {"keysData", "int16"}, {"positionX", "float"}, {"positionY", "float"}, {"positionZ", "float"}, {"quaternionW", "float"}, {"quaternionX", "float"}, {"quaternionY", "float"}, {"quaternionZ", "float"}, {"health", "int8"}, {"armor", "int8"}, {"weapon", "int8"}, {"specialAction", "int8"}, {"moveSpeedX", "float"}, {"moveSpeedY", "float"}, {"moveSpeedZ", "float"}, {"surfingOffsetsX", "float"}, {"surfingOffsetsY", "float"}, {"surfingOffsetsZ", "float"}, {"surfingVehicleId", "int16"}, {"animationId", "int16"}, {"animationFlags", "int16"}, name = "PACKET_PLAYER_SYNC"}
Lua:
local bs = raknetNewBitStream()
raknetBitStreamWriteInt8(bs, 207) -- первым делом, мы вписываем ID пакета, который нам надо отправить
raknetBitStreamSetWriteOffset(bs, 6) -- вписываем полученный сдвиг (6 байтов)
-- указываем X, Y, Z координаты (0, 0, 0)
raknetBitStreamWriteFloat(bs, 0)
raknetBitStreamWriteFloat(bs, 0)
raknetBitStreamWriteFloat(bs, 0)
raknetSendBitStream((207, bs) -- как мы помним, PACKET_PLAYER_SYNC имеет 207 айдишник
raknetDeleteBitStream(bs)
Lua:
{200, {"playerId", "int16"}, {"vehicleId", "int16"}, {"leftRightKeys", "int16"}, {"upDownKeys", "int16"}, {"keysData", "int16"}, {"quaternionW", "float"}, {"quaternionX", "float"}, {"quaternionY", "float"}, {"quaternionZ", "float"}, {"positionX", "float"}, {"positionY", "float"}, {"positionZ", "float"}, {"moveSpeedX", "float"}, {"moveSpeedY", "float"}, {"moveSpeedZ", "float"}, {"vehicleHealth", "float"}, {"playerHealth", "int8"}, {"armor", "int8"}, {"weapon", "int8"}, {"siren", "int8"}, {"landingGearState", "int8"}, {"trailerId", "int16"}, {"trainSpeed", "float"}, name = "PACKET_VEHICLE_SYNC"}
Lua:
local bs = raknetNewBitStream()
raknetBitStreamSetWriteOffset(bs, 56) -- запись позиции начинается с 56 байта (не 48, т.к. во входящем пакете вех синхры после ид пакета идет ид игрока
raknetBitStreamWriteFloat(bs, 0)
raknetBitStreamWriteFloat(bs, 0)
raknetBitStreamWriteFloat(bs, 0)
raknetEmulPacketReceiveBitStream(200, bs) -- у пакета вех синхры 200 айди
raknetDeleteBitStream(bs)
С отправкой и эмуляциец закончили, теперь перейдем к перехвату ака хуку данных.
О том, как отлавливать RPC, вы можете почитать в этом гайде, я же расскажу про перехват пакетов.
Представим, что мы хотим узнать скокость всех каров в зоне стрима, для этого нам необходимо добавить событие, которое будет отлавливать входящий пакет PACKET_VEHICLE_SYNC, делается это так:
Lua:
function onReceivePacket(id, bs)
if id == 200 then -- если id пакета = 200, то будем его насиловать
-- eblya
end
end
Lua:
function onReceivePacket(id, bs) -- нам приходит id пакета и его битстрим
if id == 200 then
raknetBitStreamIgnoreBits(bs, 416) -- игнорируем 416 битов где 64 бита - это ID пакета и еще 64 бита - айди игрока, а дальше уже остальные данные, т.к. сампфункс не позволяет игнорить байты :(
-- moveSpeed по X, Y, Z
local xMov = raknetBitStreamReadFloat(bs)
local yMov = raknetBitStreamReadFloat(bs)
local zMov = raknetBitStreamReadFloat(bs)
printStringNow("Speed: X " .. xMov .. " Y " .. yMov .. " Z " .. zMov, 1000)
end
end
Lua:
function onSendPacket(id, bs, priority, reliability, orderingChannel)
if id == 207 then
raknetBitStreamResetWritePointer(bs) -- т.к. пакеты к нам приходят уже заполненные, там необходимо обнулить указатель записи
raknetBitStreamSetWriteOffset(bs, 6) -- переходим к записи позиции, то есть смещаемся на 6 байтов
raknetBitStreamWriteFloat(bs, 0) -- X
raknetBitStreamWriteFloat(bs, 0) -- Y
raknetBitStreamWriteFloat(bs, 0) -- Z
return {id, bs, priority, reliability, orderingChannel} -- возвращаем перезаписанные значения
end
end
ну вот вроде и все, дописывайте, чем можно дополнить статью.
Последнее редактирование: