190 lines
4.1 KiB
Lua
190 lines
4.1 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 )
|
|
assert( t.size < 500, t.name )
|
|
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 = "clientInfo",
|
|
netname = 22,
|
|
"char username[31]",
|
|
}
|
|
|
|
newStruct{
|
|
name = "metaServer",
|
|
netname = 123,
|
|
"char padding[300]" --Just a bunch of padding to mitigate amplification.
|
|
}
|
|
|
|
newStruct{
|
|
name = "heartbeat",
|
|
netname = 69,
|
|
"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",
|
|
}
|
|
|
|
newStruct{
|
|
name = "advertised",
|
|
netname = 144,
|
|
"uint32_t time",
|
|
}
|
|
|
|
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 or 0 )
|
|
str.netname = assert( struct.netname )
|
|
writeBuffer:putcdata( str, struct.size )
|
|
return str
|
|
end
|
|
|
|
function packet.get()
|
|
return writeBuffer:get()
|
|
end
|
|
|
|
mt.__call = packet.add
|
|
|
|
local testing = testing
|
|
--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 |