vision/src/shared/packet.lua

178 lines
3.9 KiB
Lua

local ffi = assert( require 'ffi' )
local buffer = assert( require( "string.buffer" ) )
local ipString = assert( require 'shared.ipstring' )
local packet = {}
local mt = { __index = { new = function( self ) return ffi.new( self.ct ) end } }
local function newStruct( t )
assert( not( packet[ t.name ] or packet[ t.netname ] ))
packet[ t.name ] = t
packet[ t.netname ] = t
ffi.cdef( ("typedef struct {uint8_t netname;\n%s;\n} %s;"):format( table.concat( t, ";\n" ) , t.name ) )
t.ct = ffi.typeof( ffi.new( t.name ) )
t.size = ffi.sizeof( t.ct )
setmetatable( t, mt )
--print( "Packet:", t.name, "Members:", #t + 1, "Size:", t.size, "Alignment:", ffi.alignof( t.ct ) )
end
newStruct{
name = "serverInfo",
netname = 42,
"uint8_t players",
"uint8_t capacity",
"ipAddress ip",
"uint16_t version",
"uint16_t port",
"char svname[32]",
"char map[32]",
}
newStruct{
name = "requestServers",
netname = 123,
"char padding[999]"
}
newStruct{
name = "heartbeat",
netname = 69,
"uint16_t protocol",
"uint32_t tick",
"uint32_t hash",
}
newStruct{
name = "insect",
netname = 81,
"uint8_t id",
"bool dead",
"int8_t hp",
"int8_t vx",
"int8_t vy",
"uint8_t folio",
"uint32_t x",
"uint32_t y",
}
newStruct{
name = "soleil",
netname = 27,
"uint16_t azimuth",
"uint16_t altitude",
"int8_t vazi",
"int8_t valt",
}
newStruct{
name = "playerChange",
netname = 72,
"uint8_t id",
"uint8_t role",
"char username[31]",
}
newStruct{
name = "chatMessage",
netname = 54,
"char cmsg[127]",
}
newStruct{
name = "command",
netname = 32,
"char command",
}
local readBuffer = buffer.new( 1024 )
function packet.deserialise( str )
readBuffer:set( str )
local data = {}
local types = {}
local n = 0
while #readBuffer ~= 0 do
local netname = readBuffer:ref()[0] --Read a byte to determine the packet type.
local t = packet[ netname ]
if not t then
error( "Malformed packet. Unknown header:\n"..readBuffer:get() )
end
if #readBuffer < t.size then
error( "Malformed packet. Packet too small:\n"..readBuffer:get() )
end --Malformed packets might cause an overread.
--Allocate new struct and copy into it.
n = n + 1
data[n] = ffi.new( t.ct )
types[n] = t.name
ffi.copy( data[n], readBuffer:ref(), t.size )
readBuffer:skip( t.size )
end
return data, types
end
function packet.getString( member )
return ffi.string( member, ffi.sizeof( member ) )
end
local writeBuffer = buffer.new( 1024 )
function packet.add( struct, data )
local str = ffi.new( struct.ct, data )
str.netname = assert( struct.netname )
writeBuffer:putcdata( str, struct.size )
return writeBuffer
end
function packet.get()
return writeBuffer:get()
end
mt.__call = packet.add
local testing = true
--TESTS--
if testing then
packet.serverInfo{
players = 0,
capacity = 255,
map = "abcdefghijklmnopqrstuvwxyz1234567890",
svname = "😘😘😘😘😘😘😘kissyfaceserver",
version = 25,
port = 51312,
ip = ipString.new{ 132, 213, 45, 21 }
}
packet.heartbeat{
tick = 49,
hash = 33753745832876,
protocol = 25
}
packet.insect{
id = 5,
dead = true,
hp = -3,
vx = -5,
vy = 47,
x = 59183,
y = 21412
}
local d, t = packet.deserialise( packet.get() )
assert( #writeBuffer == 0, "Test failed. Write buffer not empty!" )
for i = 1, #d do
print( "", t[i], d[i] )
if t[i] == 'serverInfo' then
print( "", "", packet.getString( d[i].map ), packet.getString( d[i].svname ) )
end
if t[i] == 'insect' then print( d[i].vx ) end
end
packet[42]{}
assert( not( pcall( packet.deserialise, "grgrsgs" ) ), "Test failed. Failed to reject malformed packet." )
end
--END TESTS--
return packet