```lua
-- This file is part of the SAMP.Lua project.
-- Licensed under the MIT License.
-- Copyright (c) 2016, FYP @ BlastHack Team <blast.hk>
-- https://github.com/THE-FYP/SAMP.Lua

local mod = {}
local vector3d = require 'vector3d'
local ffi = require 'ffi'

local function bitstream_read_fixed_string(bs, size)
	local buf = ffi.new('uint8_t[?]', size + 1)
	raknetBitStreamReadBuffer(bs, tonumber(ffi.cast('intptr_t', buf)), size)
	buf[size] = 0
	-- Length is not specified to throw off trailing zeros.
	return ffi.string(buf)
end

local function bitstream_write_fixed_string(bs, str, size)
	local buf = ffi.new('uint8_t[?]', size, string.sub(str, 1, size))
	raknetBitStreamWriteBuffer(bs, tonumber(ffi.cast('intptr_t', buf)), size)
end

mod.bool = {
	read = function(bs) return raknetBitStreamReadBool(bs) end,
	write = function(bs, value) return raknetBitStreamWriteBool(bs, value) end
}

mod.uint8 = {
	read = function(bs) return raknetBitStreamReadInt8(bs) end,
	write = function(bs, value) return raknetBitStreamWriteInt8(bs, value) end
}

mod.uint16 = {
	read = function(bs) return raknetBitStreamReadInt16(bs) end,
	write = function(bs, value) return raknetBitStreamWriteInt16(bs, value) end
}

mod.uint32 = {
	read = function(bs)
		local v = raknetBitStreamReadInt32(bs)
		return v < 0 and 0x100000000 + v or v
	end,
	write = function(bs, value)
		return raknetBitStreamWriteInt32(bs, value)
	end
}

mod.int8 = {
	read = function(bs)
		local v = raknetBitStreamReadInt8(bs)
		return v >= 0x80 and v - 0x100 or v
	end,
	write = function(bs, value)
		return raknetBitStreamWriteInt8(bs, value)
	end
}

mod.int16 = {
	read = function(bs)
		local v = raknetBitStreamReadInt16(bs)
		return v >= 0x8000 and v - 0x10000 or v
	end,
	write = function(bs, value)
		return raknetBitStreamWriteInt16(bs, value)
	end
}

mod.int32 = {
	read = function(bs) return raknetBitStreamReadInt32(bs) end,
	write = function(bs, value) return raknetBitStreamWriteInt32(bs, value) end
}

mod.float = {
	read = function(bs) return raknetBitStreamReadFloat(bs) end,
	write = function(bs, value) return raknetBitStreamWriteFloat(bs, value) end
}

mod.string8 = {
	read = function(bs)
		local len = raknetBitStreamReadInt8(bs)
		if len <= 0 then return '' end
		return raknetBitStreamReadString(bs, len)
	end,
	write = function(bs, value)
		raknetBitStreamWriteInt8(bs, #value)
		raknetBitStreamWriteString(bs, value)
	end
}

mod.string16 = {
	read = function(bs)
		local len = raknetBitStreamReadInt16(bs)
		if len <= 0 then return '' end
		return raknetBitStreamReadString(bs, len)
	end,
	write = function(bs, value)
		raknetBitStreamWriteInt16(bs, #value)
		raknetBitStreamWriteString(bs, value)
	end
}

mod.string32 = {
	read = function(bs)
		local len = raknetBitStreamReadInt32(bs)
		if len <= 0 then return '' end
		return raknetBitStreamReadString(bs, len)
	end,
	write = function(bs, value)
		raknetBitStreamWriteInt32(bs, #value)
		raknetBitStreamWriteString(bs, value)
	end
}

mod.bool8 = {
	read = function(bs)
		return raknetBitStreamReadInt8(bs) ~= 0
	end,
	write = function(bs, value)
		raknetBitStreamWriteInt8(bs, value == true and 1 or 0)
	end
}

mod.bool32 = {
	read = function(bs)
		return raknetBitStreamReadInt32(bs) ~= 0
	end,
	write = function(bs, value)
		raknetBitStreamWriteInt32(bs, value == true and 1 or 0)
	end
}

mod.int1 = {
	read = function(bs)
		if raknetBitStreamReadBool(bs) == true then return 1 else return 0 end
	end,
	write = function(bs, value)
		raknetBitStreamWriteBool(bs, value ~= 0 and true or false)
	end
}

mod.fixedString32 = {
	read = function(bs)
		return bitstream_read_fixed_string(bs, 32)
	end,
	write = function(bs, value)
		bitstream_write_fixed_string(bs, value, 32)
	end
}

mod.string256 = mod.fixedString32

mod.encodedString2048 = {
	read = function(bs) return raknetBitStreamDecodeString(bs, 2048) end,
	write = function(bs, value) raknetBitStreamEncodeString(bs, value) end
}

mod.encodedString4096 = {
	read = function(bs) return raknetBitStreamDecodeString(bs, 4096) end,
	write = function(bs, value) raknetBitStreamEncodeString(bs, value) end
}

mod.compressedFloat = {
	read = function(bs)
		return raknetBitStreamReadInt16(bs) / 32767.5 - 1
	end,
	write = function(bs, value)
		if value < -1 then
			value = -1
		elseif value > 1 then
			value = 1
		end
		raknetBitStreamWriteInt16(bs, (value + 1) * 32767.5)
	end
}

mod.compressedVector = {
	read = function(bs)
		local magnitude = raknetBitStreamReadFloat(bs)
		if magnitude ~= 0 then
			local readCf = mod.compressedFloat.read
			return vector3d(readCf(bs) * magnitude, readCf(bs) * magnitude, readCf(bs) * magnitude)
		else
			return vector3d(0, 0, 0)
		end
	end,
	write = function(bs, data)
		local x, y, z = data.x, data.y, data.z
		local magnitude = math.sqrt(x * x + y * y + z * z)
		raknetBitStreamWriteFloat(bs, magnitude)
		if magnitude > 0 then
			local writeCf = mod.compressedFloat.write
			writeCf(bs, x / magnitude)
			writeCf(bs, y / magnitude)
			writeCf(bs, z / magnitude)
		end
	end
}

mod.normQuat = {
	read = function(bs)
		local readBool, readShort = raknetBitStreamReadBool, raknetBitStreamReadInt16
		local cwNeg, cxNeg, cyNeg, czNeg = readBool(bs), readBool(bs), readBool(bs), readBool(bs)
		local cx, cy, cz = readShort(bs), readShort(bs), readShort(bs)
		local x = cx / 65535
		local y = cy / 65535
		local z = cz / 65535
		if cxNeg then x = -x end
		if cyNeg then y = -y end
		if czNeg then z = -z end
		local diff = 1 - x * x - y * y - z * z
		if diff < 0 then diff = 0 end
		local w = math.sqrt(diff)
		if cwNeg then w = -w end
		return {w, x, y, z}
	end,
	write = function(bs, value)
		local w, x, y, z = value[1], value[2], value[3], value[4]
		raknetBitStreamWriteBool(bs, w < 0)
		raknetBitStreamWriteBool(bs, x < 0)
		raknetBitStreamWriteBool(bs, y < 0)
		raknetBitStreamWriteBool(bs, z < 0)
		raknetBitStreamWriteInt16(bs, math.abs(x) * 65535)
		raknetBitStreamWriteInt16(bs, math.abs(y) * 65535)
		raknetBitStreamWriteInt16(bs, math.abs(z) * 65535)
		-- w is calculated on the target
	end
}

mod.vector3d = {
	read = function(bs)
		local x, y, z =
			raknetBitStreamReadFloat(bs),
			raknetBitStreamReadFloat(bs),
			raknetBitStreamReadFloat(bs)
		return vector3d(x, y, z)
	end,
	write = function(bs, value)
		raknetBitStreamWriteFloat(bs, value.x)
		raknetBitStreamWriteFloat(bs, value.y)
		raknetBitStreamWriteFloat(bs, value.z)
	end
}

mod.vector2d = {
	read = function(bs)
		local x = raknetBitStreamReadFloat(bs)
		local y = raknetBitStreamReadFloat(bs)
		return {x = x, y = y}
	end,
	write = function(bs, value)
		raknetBitStreamWriteFloat(bs, value.x)
		raknetBitStreamWriteFloat(bs, value.y)
	end
}

local function bitstream_io_interface(field)
	return setmetatable({}, {
		__index = function(t, index)
			return mod[index][field]
		end
	})
end

mod.bs_read = bitstream_io_interface('read')
mod.bs_write = bitstream_io_interface('write')

return mod

```
```lua
-- This file is part of the SAMP.Lua project.
-- Licensed under the MIT License.
-- Copyright (c) 2016, FYP @ BlastHack Team <blast.hk>
-- https://github.com/THE-FYP/SAMP.Lua

local MODULE = {
	MODULEINFO = {
		name = 'samp.events',
		version = 4
	},
	INTERFACE = {
		OUTCOMING_RPCS    = {},
		OUTCOMING_PACKETS = {},
		INCOMING_RPCS     = {},
		INCOMING_PACKETS  = {}
	},
	EXPORTS = {}
}

-- check dependencies
assert(isSampLoaded(), 'SA-MP is not loaded')
assert(isSampfuncsLoaded(), 'samp.events requires SAMPFUNCS')
assert(getMoonloaderVersion() >= 20, 'samp.events requires MoonLoader v.020 or greater')

local BitStreamIO            = require 'samp.events.bitstream_io'
MODULE.INTERFACE.BitStreamIO = BitStreamIO


local function read_data(bs, dataType)
	if type(dataType) ~= 'table' then
		return BitStreamIO[dataType].read(bs)
	else -- process nested structures
		local values = {}
		for _, it in ipairs(dataType) do
			local name, t = next(it)
			values[name] = read_data(bs, t)
		end
		return values
	end
end

local function write_data(bs, dataType, value)
	if type(dataType) ~= 'table' then
		BitStreamIO[dataType].write(bs, value)
	else -- process nested structures
		for _, it in ipairs(dataType) do
			local name, t = next(it)
			write_data(bs, t, value[name])
		end
	end
end

local function process_event(bs, callback, struct, ignorebits)
	local args = {}
	if bs ~= 0 then
		if ignorebits then
			raknetBitStreamIgnoreBits(bs, ignorebits)
		end
		if type(struct[2]) == 'function' then
			local r1, r2 = struct[2](bs) -- call custom reading function
			if type(callback) == 'table' and type(r1) == 'string' then
				callback = callback[r1]
				if callback then
					args = r2
				else
					return
				end
			else
				args = r1
			end
		else
			-- skip event name
			for i = 2, #struct do
				local _, t = next(struct[i]) -- type
				table.insert(args, read_data(bs, t))
			end
		end
	end
	local result = callback(unpack(args))
	if result == false then
		return false -- consume packet
	end
	if bs ~= 0 and type(result) == 'table' then
		raknetBitStreamSetWriteOffset(bs, ignorebits or 0)
		if type(struct[3]) == 'function' then
			struct[3](bs, result) -- call custom writing function
		else
			assert(#struct - 1 == #result)
			for i = 2, #struct do
				local _, t = next(struct[i]) -- type
				write_data(bs, t, result[i - 1])
			end
		end
	end
end

local function process_packet(id, bs, event_table, ignorebits)
	local entry = event_table[id]
	if entry ~= nil then
		local key = entry[1]
		local callback = nil
		if type(key) == 'table' then
			for i, name in ipairs(key) do
				if type(MODULE[name]) == 'function' then
					if not callback then callback = {} end
					callback[name] = MODULE[name]
				end
			end
		elseif type(MODULE[key]) == 'function' then
			callback = MODULE[key]
		end
		if callback then
			return process_event(bs, callback, entry, ignorebits)
		end
	end
end


local interface = MODULE.INTERFACE
local function samp_on_send_rpc(id, bitStream, priority, reliability, orderingChannel, shiftTs)
	return process_packet(id, bitStream, interface.OUTCOMING_RPCS)
end

local function samp_on_send_packet(id, bitStream, priority, reliability, orderingChannel)
	return process_packet(id, bitStream, interface.OUTCOMING_PACKETS, 8)
end

local function samp_on_receive_rpc(id, bitStream)
	return process_packet(id, bitStream, interface.INCOMING_RPCS)
end

local function samp_on_receive_packet(id, bitStream)
	return process_packet(id, bitStream, interface.INCOMING_PACKETS, 8)
end

addEventHandler('onSendRpc', samp_on_send_rpc)
addEventHandler('onSendPacket', samp_on_send_packet)
addEventHandler('onReceiveRpc', samp_on_receive_rpc)
addEventHandler('onReceivePacket', samp_on_receive_packet)

return MODULE

```
```lua
-- This file is part of the SAMP.Lua project.
-- Licensed under the MIT License.
-- Copyright (c) 2016, FYP @ BlastHack Team <blast.hk>
-- https://github.com/THE-FYP/SAMP.Lua

local BitStreamIO = require 'samp.events.bitstream_io'
local utils = require 'samp.events.utils'

BitStreamIO.Int32Array3 = {
	read = function(bs)
		local arr = {}
		for i = 1, 3 do arr[i] = raknetBitStreamReadInt32(bs) end
		return arr
	end,
	write = function(bs, value)
		for i = 1, 3 do raknetBitStreamWriteInt32(bs, value[i]) end
	end
}

BitStreamIO.AimSyncData = {
	read = function(bs) return utils.read_sync_data(bs, 'AimSyncData')  end,
	write = function(bs, value) utils.write_sync_data(bs, 'AimSyncData', value) end
}

BitStreamIO.UnoccupiedSyncData = {
	read = function(bs) return utils.read_sync_data(bs, 'UnoccupiedSyncData')  end,
	write = function(bs, value) utils.write_sync_data(bs, 'UnoccupiedSyncData', value) end
}

BitStreamIO.PassengerSyncData = {
	read = function(bs) 	return utils.read_sync_data(bs, 'PassengerSyncData')  end,
	write = function(bs, value) utils.write_sync_data(bs, 'PassengerSyncData', value) end
}

BitStreamIO.BulletSyncData = {
	read = function(bs) return utils.read_sync_data(bs, 'BulletSyncData')  end,
	write = function(bs, value) utils.write_sync_data(bs, 'BulletSyncData', value) end
}

BitStreamIO.TrailerSyncData = {
	read = function(bs) return utils.read_sync_data(bs, 'TrailerSyncData')  end,
	write = function(bs, value) utils.write_sync_data(bs, 'TrailerSyncData', value) end
}

return BitStreamIO

```
```lua
-- This file is part of the SAMP.Lua project.
-- Licensed under the MIT License.
-- Copyright (c) 2016, FYP @ BlastHack Team <blast.hk>
-- https://github.com/THE-FYP/SAMP.Lua

local bs_io = require 'samp.events.bitstream_io'
local utils = require 'samp.events.utils'
local bsread, bswrite = bs_io.bs_read, bs_io.bs_write
local handler = {}

--- onSendGiveDamage, onSendTakeDamage
function handler.rpc_send_give_take_damage_reader(bs)
	local take = bsread.bool(bs) -- 'true' is take damage
	local data = {
		bsread.uint16(bs), -- playerId
		bsread.float(bs), -- damage
		bsread.int32(bs), -- weapon
		bsread.int32(bs), -- bodypart
		take,
	}
	return (take and 'onSendTakeDamage' or 'onSendGiveDamage'), data
end

function handler.rpc_send_give_take_damage_writer(bs, data)
	bswrite.bool(bs, data[5]) -- give or take
	bswrite.uint16(bs, data[1]) -- playerId
	bswrite.float(bs, data[2]) -- damage
	bswrite.int32(bs, data[3]) -- weapon
	bswrite.int32(bs, data[4]) -- bodypart
end

--- onInitGame
function handler.rpc_init_game_reader(bs)
	local settings                 = {}
	settings.zoneNames             = bsread.bool(bs)
	settings.useCJWalk             = bsread.bool(bs)
	settings.allowWeapons          = bsread.bool(bs)
	settings.limitGlobalChatRadius = bsread.bool(bs)
	settings.globalChatRadius      = bsread.float(bs)
	settings.stuntBonus            = bsread.bool(bs)
	settings.nametagDrawDist       = bsread.float(bs)
	settings.disableEnterExits     = bsread.bool(bs)
	settings.nametagLOS            = bsread.bool(bs)
	settings.tirePopping           = bsread.bool(bs)
	settings.classesAvailable      = bsread.int32(bs)
	local playerId                 = bsread.uint16(bs)
	settings.showPlayerTags        = bsread.bool(bs)
	settings.playerMarkersMode     = bsread.int32(bs)
	settings.worldTime             = bsread.uint8(bs)
	settings.worldWeather          = bsread.uint8(bs)
	settings.gravity               = bsread.float(bs)
	settings.lanMode               = bsread.bool(bs)
	settings.deathMoneyDrop        = bsread.int32(bs)
	settings.instagib              = bsread.bool(bs)
	settings.normalOnfootSendrate  = bsread.int32(bs)
	settings.normalIncarSendrate   = bsread.int32(bs)
	settings.normalFiringSendrate  = bsread.int32(bs)
	settings.sendMultiplier        = bsread.int32(bs)
	settings.lagCompMode           = bsread.int32(bs)
	local hostName                 = bsread.string8(bs)
	local vehicleModels = {}
	for i = 0, 212 - 1 do
		vehicleModels[i] = bsread.uint8(bs)
	end
	settings.vehicleFriendlyFire = bsread.bool32(bs)
	return {playerId, hostName, settings, vehicleModels, settings.vehicleFriendlyFire}
end

function handler.rpc_init_game_writer(bs, data)
	local settings = data[3]
	local vehicleModels = data[4]
	bswrite.bool(bs, settings.zoneNames)
	bswrite.bool(bs, settings.useCJWalk)
	bswrite.bool(bs, settings.allowWeapons)
	bswrite.bool(bs, settings.limitGlobalChatRadius)
	bswrite.float(bs, settings.globalChatRadius)
	bswrite.bool(bs, settings.stuntBonus)
	bswrite.float(bs, settings.nametagDrawDist)
	bswrite.bool(bs, settings.disableEnterExits)
	bswrite.bool(bs, settings.nametagLOS)
	bswrite.bool(bs, settings.tirePopping)
	bswrite.int32(bs, settings.classesAvailable)
	bswrite.uint16(bs, data[1]) -- playerId
	bswrite.bool(bs, settings.showPlayerTags)
	bswrite.int32(bs, settings.playerMarkersMode)
	bswrite.uint8(bs, settings.worldTime)
	bswrite.uint8(bs, settings.worldWeather)
	bswrite.float(bs, settings.gravity)
	bswrite.bool(bs, settings.lanMode)
	bswrite.int32(bs, settings.deathMoneyDrop)
	bswrite.bool(bs, settings.instagib)
	bswrite.int32(bs, settings.normalOnfootSendrate)
	bswrite.int32(bs, settings.normalIncarSendrate)
	bswrite.int32(bs, settings.normalFiringSendrate)
	bswrite.int32(bs, settings.sendMultiplier)
	bswrite.int32(bs, settings.lagCompMode)
	bswrite.string8(bs, data[2]) -- hostName
	for i = 1, 212 do
		bswrite.uint8(bs, vehicleModels[i])
	end
	bswrite.bool32(bs, settings.vehicleFriendlyFire)
end

--- onInitMenu
function handler.rpc_init_menu_reader(bs)
	local colWidth2
	local rows = {}
	local columns = {}
	local readColumn = function(width)
		local title = bsread.fixedString32(bs)
		local rowCount = bsread.uint8(bs)
		local column = {title = title, width = width, text = {}}
		for i = 1, rowCount do
			column.text[i] = bsread.fixedString32(bs)
		end
		return column
	end
	local menuId = bsread.uint8(bs)
	local twoColumns = bsread.bool32(bs)
	local menuTitle = bsread.fixedString32(bs)
	local x = bsread.float(bs)
	local y = bsread.float(bs)
	local colWidth1 = bsread.float(bs)
	if twoColumns then
		colWidth2 = bsread.float(bs)
	end
	local menu = bsread.bool32(bs)
	for i = 1, 12 do
		rows[i] = bsread.int32(bs)
	end
	columns[1] = readColumn(colWidth1)
	if twoColumns then
		columns[2] = readColumn(colWidth2)
	end
	return {menuId, menuTitle, x, y, twoColumns, columns, rows, menu}
end

function handler.rpc_init_menu_writer(bs, data)
	local columns = data[6]
	bswrite.uint8(bs, data[1])      -- menuId
	bswrite.bool32(bs, data[5])    -- twoColumns
	bswrite.fixedString32(bs, data[2]) -- title
	bswrite.float(bs, data[3])     -- x
	bswrite.float(bs, data[4])     -- y
	-- columns width
	bswrite.float(bs, columns[1].width)
	if data[5] then
		bswrite.float(bs, columns[2].width)
	end
	bswrite.bool32(bs, data[8]) -- menu
	 -- rows
	for i = 1, 12 do
		bswrite.int32(bs, data[7][i])
	end
	-- columns
	for i = 1, (data[5] and 2 or 1) do
		bswrite.fixedString32(bs, columns[i].title)
		bswrite.uint8(bs, #columns[i].text)
		for r, t in ipairs(columns[i].text) do
			bswrite.fixedString32(bs, t)
		end
	end
end

--- onMarkersSync
function handler.packet_markers_sync_reader(bs)
	local markers = {}
	local players = bsread.int32(bs)
	for i = 1, players do
		local playerId = bsread.uint16(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 handler.packet_markers_sync_writer(bs, data)
	bswrite.int32(bs, #data)
	for i = 1, #data do
		local it = data[i]
		bswrite.uint16(bs, it.playerId)
		bswrite.bool(bs, it.active)
		if it.active then
			bswrite.uint16(bs, it.coords.x)
			bswrite.uint16(bs, it.coords.y)
			bswrite.uint16(bs, it.coords.z)
		end
	end
end

--- onPlayerSync
function handler.packet_player_sync_reader(bs)
	local has_value = bsread.bool
	local data = {}
	local playerId = bsread.uint16(bs)
	if has_value(bs) then data.leftRightKeys = bsread.uint16(bs) end
	if has_value(bs) then data.upDownKeys = bsread.uint16(bs) end
	data.keysData = bsread.uint16(bs)
	data.position = bsread.vector3d(bs)
	data.quaternion = bsread.normQuat(bs)
	data.health, data.armor = utils.decompress_health_and_armor(bsread.uint8(bs))
	data.weapon = bsread.uint8(bs)
	data.specialAction = bsread.uint8(bs)
	data.moveSpeed = bsread.compressedVector(bs)
	if has_value(bs) then
		data.surfingVehicleId = bsread.uint16(bs)
		data.surfingOffsets = bsread.vector3d(bs)
	end
	if has_value(bs) then
		data.animationId = bsread.uint16(bs)
		data.animationFlags = bsread.uint16(bs)
	end
	return {playerId, data}
end

function handler.packet_player_sync_writer(bs, data)
	local playerId = data[1]
	local data = data[2]
	bswrite.uint16(bs, playerId)
	bswrite.bool(bs, data.leftRightKeys ~= nil)
	if data.leftRightKeys then bswrite.uint16(bs, data.leftRightKeys) end
	bswrite.bool(bs, data.upDownKeys ~= nil)
	if data.upDownKeys then bswrite.uint16(bs, data.upDownKeys) end
	bswrite.uint16(bs, data.keysData)
	bswrite.vector3d(bs, data.position)
	bswrite.normQuat(bs, data.quaternion)
	bswrite.uint8(bs, utils.compress_health_and_armor(data.health, data.armor))
	bswrite.uint8(bs, data.weapon)
	bswrite.uint8(bs, data.specialAction)
	bswrite.compressedVector(bs, data.moveSpeed)
	bswrite.bool(bs, data.surfingVehicleId ~= nil)
	if data.surfingVehicleId then
		bswrite.uint16(bs, data.surfingVehicleId)
		bswrite.vector3d(bs, data.surfingOffsets)
	end
	bswrite.bool(bs, data.animationId ~= nil)
	if data.animationId then
		bswrite.uint16(bs, data.animationId)
		bswrite.uint16(bs, data.animationFlags)
	end
end

--- onVehicleSync
function handler.packet_vehicle_sync_reader(bs)
	local data = {}
	local playerId = bsread.uint16(bs)
	local vehicleId = bsread.uint16(bs)
	data.leftRightKeys = bsread.uint16(bs)
	data.upDownKeys = bsread.uint16(bs)
	data.keysData = bsread.uint16(bs)
	data.quaternion = bsread.normQuat(bs)
	data.position = bsread.vector3d(bs)
	data.moveSpeed = bsread.compressedVector(bs)
	data.vehicleHealth = bsread.uint16(bs)
	data.playerHealth, data.armor = utils.decompress_health_and_armor(bsread.uint8(bs))
	data.currentWeapon = bsread.uint8(bs)
	data.siren = bsread.bool(bs)
	data.landingGear = bsread.bool(bs)
	if bsread.bool(bs) then
		data.trainSpeed = bsread.int32(bs)
	end
	if bsread.bool(bs) then
		data.trailerId = bsread.uint16(bs)
	end
	return {playerId, vehicleId, data}
end

function handler.packet_vehicle_sync_writer(bs, data)
	local playerId = data[1]
	local vehicleId = data[2]
	local data = data[3]
	bswrite.uint16(bs, playerId)
	bswrite.uint16(bs, vehicleId)
	bswrite.uint16(bs, data.leftRightKeys)
	bswrite.uint16(bs, data.upDownKeys)
	bswrite.uint16(bs, data.keysData)
	bswrite.normQuat(bs, data.quaternion)
	bswrite.vector3d(bs, data.position)
	bswrite.compressedVector(bs, data.moveSpeed)
	bswrite.uint16(bs, data.vehicleHealth)
	bswrite.uint8(bs, utils.compress_health_and_armor(data.playerHealth, data.armor))
	bswrite.uint8(bs, data.currentWeapon)
	bswrite.bool(bs, data.siren)
	bswrite.bool(bs, data.landingGear)
	bswrite.bool(bs, data.trainSpeed ~= nil)
	if data.trainSpeed ~= nil then
		bswrite.int32(bs, data.trainSpeed)
	end
	bswrite.bool(bs, data.trailerId ~= nil)
	if data.trailerId ~= nil then
		bswrite.uint16(bs, data.trailerId)
	end
end

--- onVehicleStreamIn
function handler.rpc_vehicle_stream_in_reader(bs)
	local data = {modSlots = {}}
	local vehicleId = bsread.uint16(bs)
	data.type = bsread.int32(bs)
	data.position = bsread.vector3d(bs)
	data.rotation = bsread.float(bs)
	data.bodyColor1 = bsread.uint8(bs)
	data.bodyColor2 = bsread.uint8(bs)
	data.health = bsread.float(bs)
	data.interiorId = bsread.uint8(bs)
	data.doorDamageStatus = bsread.int32(bs)
	data.panelDamageStatus = bsread.int32(bs)
	data.lightDamageStatus = bsread.uint8(bs)
	data.tireDamageStatus = bsread.uint8(bs)
	data.addSiren = bsread.uint8(bs)
	for i = 1, 14 do
		data.modSlots[i] = bsread.uint8(bs)
	end
	data.paintJob = bsread.uint8(bs)
	data.interiorColor1 = bsread.int32(bs)
	data.interiorColor2 = bsread.int32(bs)
	return {vehicleId, data}
end

function handler.rpc_vehicle_stream_in_writer(bs, data)
	local vehicleId = data[1]
	local data = data[2]
	bswrite.uint16(bs, vehicleId)
	bswrite.int32(bs, data.type)
	bswrite.vector3d(bs, data.position)
	bswrite.float(bs, data.rotation)
	bswrite.uint8(bs, data.bodyColor1)
	bswrite.uint8(bs, data.bodyColor2)
	bswrite.float(bs, data.health)
	bswrite.uint8(bs, data.interiorId)
	bswrite.int32(bs, data.doorDamageStatus)
	bswrite.int32(bs, data.panelDamageStatus)
	bswrite.uint8(bs, data.lightDamageStatus)
	bswrite.uint8(bs, data.tireDamageStatus)
	bswrite.uint8(bs, data.addSiren)
	for i = 1, 14 do
		bswrite.uint8(bs, data.modSlots[i])
	end
	bswrite.uint8(bs, data.paintJob)
	bswrite.int32(bs, data.interiorColor1)
	bswrite.int32(bs, data.interiorColor2)
end

local MATERIAL_TYPE = {
	NONE = 0,
	TEXTURE = 1,
	TEXT = 2,
}

local function read_object_material(bs)
	local data = {}
	data.materialId = bsread.uint8(bs)
	data.modelId = bsread.uint16(bs)
	data.libraryName = bsread.string8(bs)
	data.textureName = bsread.string8(bs)
	data.color = bsread.int32(bs)
	data.type = MATERIAL_TYPE.TEXTURE
	return data
end

local function write_object_material(bs, data)
	bswrite.uint8(bs, data.type)
	bswrite.uint8(bs, data.materialId)
	bswrite.uint16(bs, data.modelId)
	bswrite.string8(bs, data.libraryName)
	bswrite.string8(bs, data.textureName)
	bswrite.int32(bs, data.color)
end

local function read_object_material_text(bs)
	local data = {}
	data.materialId = bsread.uint8(bs)
	data.materialSize = bsread.uint8(bs)
	data.fontName = bsread.string8(bs)
	data.fontSize = bsread.uint8(bs)
	data.bold = bsread.uint8(bs)
	data.fontColor = bsread.int32(bs)
	data.backGroundColor = bsread.int32(bs)
	data.align = bsread.uint8(bs)
	data.text = bsread.encodedString2048(bs)
	data.type = MATERIAL_TYPE.TEXT
	return data
end

local function write_object_material_text(bs, data)
	bswrite.uint8(bs, data.type)
	bswrite.uint8(bs, data.materialId)
	bswrite.uint8(bs, data.materialSize)
	bswrite.string8(bs, data.fontName)
	bswrite.uint8(bs, data.fontSize)
	bswrite.uint8(bs, data.bold)
	bswrite.int32(bs, data.fontColor)
	bswrite.int32(bs, data.backGroundColor)
	bswrite.uint8(bs, data.align)
	bswrite.encodedString2048(bs, data.text)
end

--- onSetObjectMaterial
function handler.rpc_set_object_material_reader(bs)
	local objectId = bsread.uint16(bs)
	local materialType = bsread.uint8(bs)
	local material
	if materialType == MATERIAL_TYPE.TEXTURE then
		material = read_object_material(bs)
	elseif materialType == MATERIAL_TYPE.TEXT then
		material = read_object_material_text(bs)
	end
	local ev = materialType == MATERIAL_TYPE.TEXTURE and 'onSetObjectMaterial' or 'onSetObjectMaterialText'
	return ev, {objectId, material}
end

function handler.rpc_set_object_material_writer(bs, data)
	local objectId = data[1]
	local mat = data[2]
	bswrite.uint16(bs, objectId)
	if mat.type == MATERIAL_TYPE.TEXTURE then
		write_object_material(bs, mat)
	elseif mat.type == MATERIAL_TYPE.TEXT then
		write_object_material_text(bs, mat)
	end
end

--- onCreateObject
function handler.rpc_create_object_reader(bs)
	local data = {materials = {}, materialText = {}}
	local objectId = bsread.uint16(bs)
	data.modelId = bsread.int32(bs)
	data.position = bsread.vector3d(bs)
	data.rotation = bsread.vector3d(bs)
	data.drawDistance = bsread.float(bs)
	data.noCameraCol = bsread.bool8(bs)
	data.attachToVehicleId = bsread.uint16(bs)
	data.attachToObjectId = bsread.uint16(bs)
	if data.attachToVehicleId ~= 0xFFFF or data.attachToObjectId ~= 0xFFFF then
		data.attachOffsets = bsread.vector3d(bs)
		data.attachRotation = bsread.vector3d(bs)
		data.syncRotation = bsread.bool8(bs)
	end
	data.texturesCount = bsread.uint8(bs)
	while raknetBitStreamGetNumberOfUnreadBits(bs) >= 8 do
		local materialType = bsread.uint8(bs)
		if materialType == MATERIAL_TYPE.TEXTURE then
			table.insert(data.materials, read_object_material(bs))
		elseif materialType == MATERIAL_TYPE.TEXT then
			table.insert(data.materialText, read_object_material_text(bs))
		end
	end
	data.materials_text = data.materialText -- obsolete
	return {objectId, data}
end

function handler.rpc_create_object_writer(bs, data)
	local objectId = data[1]
	local data = data[2]
	bswrite.uint16(bs, objectId)
	bswrite.int32(bs, data.modelId)
	bswrite.vector3d(bs, data.position)
	bswrite.vector3d(bs, data.rotation)
	bswrite.float(bs, data.drawDistance)
	bswrite.bool8(bs, data.noCameraCol)
	bswrite.uint16(bs, data.attachToVehicleId)
	bswrite.uint16(bs, data.attachToObjectId)
	if data.attachToVehicleId ~= 0xFFFF or data.attachToObjectId ~= 0xFFFF then
		bswrite.vector3d(bs, data.attachOffsets)
		bswrite.vector3d(bs, data.attachRotation)
		bswrite.bool8(bs, data.syncRotation)
	end
	bswrite.uint8(bs, data.texturesCount)
	for _, it in ipairs(data.materials) do
		write_object_material(bs, it)
	end
	for _, it in ipairs(data.materialText) do
		write_object_material_text(bs, it)
	end
end

function handler.rpc_update_scores_and_pings_reader(bs)
	local data = {}
	for i = 1, raknetBitStreamGetNumberOfBytesUsed(bs) / 10 do
		local playerId = bsread.uint16(bs)
		local playerScore = bsread.int32(bs)
		local playerPing = bsread.int32(bs)
		data[playerId] = {score = playerScore, ping = playerPing}
	end
	return {data}
end

function handler.rpc_update_scores_and_pings_writer(bs, data)
	for id, info in pairs(data[1]) do
		bswrite.uint16(bs, id)
		bswrite.int32(bs, info.score)
		bswrite.int32(bs, info.ping)
	end
end

function handler.packet_weapons_update_reader(bs)
	local playerTarget = bsread.uint16(bs)
	local actorTarget = bsread.uint16(bs)
	local weapons = {}
	local count = raknetBitStreamGetNumberOfUnreadBits(bs) / 32
	for i = 1, count do
		local slot = bsread.uint8(bs)
		local weapon = bsread.uint8(bs)
		local ammo = bsread.uint16(bs)
		weapons[i] = {slot = slot, weapon = weapon, ammo = ammo}
	end
	return {playerTarget, actorTarget, weapons}
end

function handler.packet_weapons_update_writer(bs, data)
	bswrite.uint16(bs, data[1])
	bswrite.uint16(bs, data[2])
	for i, weap in ipairs(data[3]) do
		bswrite.uint8(bs, weap.slot)
		bswrite.uint8(bs, weap.weapon)
		bswrite.uint16(bs, weap.ammo)
	end
end

return handler

```
```lua
-- This file is part of the SAMP.Lua project.
-- Licensed under the MIT License.
-- Copyright (c) 2016, FYP @ BlastHack Team <blast.hk>
-- https://github.com/THE-FYP/SAMP.Lua

local ffi = require 'ffi'
local utils = {}

function utils.decompress_health_and_armor(hpAp)
	local hp = math.min(bit.rshift(hpAp, 4) * 7, 100)
	local armor = math.min(bit.band(hpAp, 0x0F) * 7, 100)
	return hp, armor
end

function utils.compress_health_and_armor(health, armor)
	local hp = health >= 100 and 0xF0 or bit.lshift(health / 7, 4)
	local ap = armor >= 100 and 0x0F or bit.band(armor / 7, 0x0F)
	return bit.bor(hp, ap)
end

function utils.create_sync_data(st)
	require 'samp.synchronization'
	return ffi.new(st)
end

function utils.read_sync_data(bs, st)
	local dataStruct = utils.create_sync_data(st)
	local ptr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', dataStruct)))
	raknetBitStreamReadBuffer(bs, ptr, ffi.sizeof(dataStruct))
	return dataStruct
end

function utils.write_sync_data(bs, st, ffiobj)
	require 'samp.synchronization'
	local ptr = tonumber(ffi.cast('intptr_t', ffi.cast('void*', ffiobj)))
	raknetBitStreamWriteBuffer(bs, ptr, ffi.sizeof(st))
end

function utils.process_outcoming_sync_data(bs, st)
	local data = raknetBitStreamGetDataPtr(bs) + 1
	require 'samp.synchronization'
	return {ffi.cast(st .. '*', data)}
end

return utils

```
Проведите профессионально глубокий анализ кода и найдите несоответствия в стиле кода, места. Профессионально найдите части кода которые можно сократить, удалить, оптимизировать, улучшить, добавить что-то. Профессионально оптимизируйте код, упростите код, структурируйте код, улучшите код, избегайте ненужных повторений, приведите код к стандартам 2024 года. Сделайте код профессиональным и современным. Пишите сразу полный код.