poppy-client/shared/networkFormat.lua

155 lines
5.0 KiB
Lua

table.unpack = table.unpack or unpack
string.pack = function(...) return love.data.pack("string", ...) end
string.unpack = love.data.unpack
string.packsize = string.packsize or love.data.getPackedSize
-- Lua data types: nil, boolean, number, string, function, userdata, thread, and table.
-- Not representable here: function, userdata, thread.
format = {
--[[ Poppy specific
0x81 = "remoteCall",
-- lua specific
0x02 = "shortString",
0x03 = "longString",
0x04 = "nil",
0x05 = "true",
0x06 = "false",
0x07 = "number",
-- love2d/poppy specific
0x48 = "colorAlpha",
0x49 = "color"]]--
}
format.decodeFunction = function(encoded, index)
local nameLength, index = string.unpack("<I1", encoded, index)
local name, index = string.unpack("<c".. nameLength, encoded, index)
if name == "init" then DEBUG = true end
local argNumber, index = string.unpack("<I1", encoded, index)
local clientID, index = string.unpack("<I2", encoded, index)
local arguments = {}
for i = 1, argNumber do
local argtype = ""
argType, index = string.unpack("<I1", encoded, index)
if argType == 0x02 then
local stringLength, nwString
stringLength, index = string.unpack("<I1", encoded, index)
nwString, index = string.unpack("<c"..stringLength, encoded, index)
arguments[i] = nwString
elseif argType == 0x03 then
local stringLength, nwString
stringLength, index = string.unpack("<I2", encoded, index)
nwString, index = string.unpack("<c"..stringLength, encoded, index)
arguments[i] = string
elseif argType == 0x04 then
arguments[i] = nil
elseif argType == 0x05 then
arguments[i] = true
elseif argType == 0x06 then
arguments[i] = false
elseif argType == 0x07 then
local number
number, index = string.unpack("<n", encoded, index)
arguments[i] = number
elseif argType == 0x48 then
local color = {}
color[1], color[2], color[3], color[4], index = string.unpack("<ffff", encoded, index)
arguments[i] = color
elseif argType == 0x49 then
local color = {nwType = "color"}
color[1], color[2], color[3], index = string.unpack("<fff", encoded, index)
arguments[i] = color
else
error("failed to decode argument " .. i .. " index is at " ..index.. " len is " .. encoded:len() .. "value is " .. argType)
end
end
return name, clientID, arguments
end
format.encodeFunction = function(name, clientID, ...)
assert(name and clientID)
-- Format
-- byte1 0x81 to indicate a function
-- byte2 length of the name of the function
-- byte3-byteN name of the function
-- byteN+1 number of arguments to the function
-- byteN+2 type of argument
-- byteN+3-byteM argument specific syntax
-- byteM+1 type of next argument
-- and so on
local encoded = "\x81"
encoded = encoded .. string.pack("<I1", name:len())
encoded = encoded .. name .. string.pack("<I1", select("#", ...))
-- can't use ipairs, it does stop on nil
-- can't use pairs, it ignores nil :D
encoded = encoded .. string.pack("<I2", clientID)
local args = { ... }
for i=1, select("#", ...) do
local value = args[i]
if type(value) == "function" or type(value) == "CFunction" or type(value) == "userdata"
or type(value) == "thread" then
error("unencodeable data passed! of type: " .. type(value))
elseif type(value) == "nil" then
encoded = encoded .. "\x04"
elseif type(value) == "boolean" then
if value then
encoded = encoded .. "\x05"
else
encoded = encoded .. "\x06"
end
elseif type(value) == "string" then
if value:len() > 255 then
encoded = encoded .. "\x03" .. string.pack("<I2", value:len()) .. value
else
encoded = encoded .. "\x02" .. string.pack("<I1", value:len()) .. value
end
elseif type(value) == "number" then
encoded = encoded .. "\x07" .. string.pack("<n", value)
elseif type(value) == "table" then
if value.nwType == "color" and #value == 3 then
encoded = encoded .. "\x49" .. string.pack("<fff", value[1], value[2], value[3])
elseif value.nwType == "color" and #value == 4 then
encoded = encoded .. "\x48" ..
string.pack("<ffff", value[1], value[2], value[3], value[4])
else
error("Got table but don't know how to encode it")
end
else
error("unimplemented type for encoding!")
end
end
return encoded
end
function format.testEncoder()
local testCases = {
Jeremeeeey = { true, true, nil, false, 69, "Jeremy stop that >:(" },
buildBlocks = { 32, 32, 64, 64, "longHashValueISwear" },
colorWorld = { {nwType = "color", 0.4, 0.4, 0.4 }, {nwType = "color", 0.4, 0.4, 0.4, 0.5}},
slangvariants = { "oy mate", "how ya doing", "sup brooo", "heya", "hey there ma dude" }
}
print("Running encoder tests...")
for name, value in pairs(testCases) do
local encoded = format.encodeFunction(name, 3, table.unpack(value))
local name, _, decodedArgs = format.decodeFunction(encoded)
local sucess = true
for iter= 1, #value do
local var = value[iter]
if var ~= decodedArgs[iter] and not type(var) == table then
sucess = false
end
end
if sucess then
print(name .. " suceeded!")
else
print(name .. " failed.")
end
end
end
return format