[WIP] TableStream

Ok I’ve began work on a new project. its a Lua module.
This project is very similar to data-stream but will not use glon.
The code so far is just a mock up. User-messages are restricted to 128 bytes so that’s something i need to work on (not sure how yet).

This is it ATM: http://goluch.pastebin.org/437002 (untested its a mock up)

Though ATM it wil only have the ability to send tables very small in size, i hope to achieve something that will be able to send mass amounts of data with out the use of glon.

If you have any feedback/suggestions please post.
If you have nothing relevant and polite to say please don’t post it here.

User messages are limited to about 255 bytes, not 128.

Could you tell us what exactly the advantage of not using GLON is?

Because Glon means all the data becomes a huge string and strings use up a lot of bytes, with datastream your end up sending way more then you need to.

It’s like that so you can send from client to server.

[editline]06:08AM[/editline]

I’d suggest having a name for types.

TABLESTREAM_COLOR = 10

If you encode this table with GLON:
[lua]{
“string one”,
“string two”,
Color(255, 255, 255, 255)
}[/lua]
It becomes:
[lua]astring oneastring twoaa255ab255ag255ar255[/lua]
Wasting up to 28 bytes (if I assume that one character takes up one byte), when you could just use umsg.String twice and umsg.Char thrice. It’s nothing if you’re working with small tables, but it’s very noticeable if you want to send large tables (which is what datastream was originally intended to do).

That’s nice and all, but the client has no way of knowing it has to read two strings and a char from the user message.

That’s why you need to try to read each thing. If it returns something else than the default value for that type, you’ll know that it’s the correct value.

If it returns the default value, just call Reset on the bf_read object and try again.

Here’s something that I used in my gamemode to determine if it needs to read a char, a short or a long:

[lua]local types = {
{“Char”, 0},
{“Short”, 0},
{“Long”, 0}
};

usermessage.Hook(“AddInventory”, function(um)
local id;

-- Check all types.
for _, v in ipairs(types) do
    local a = um["Read" .. v[1]](um);
    
    if(a ~= v[2]) then
        id = a;
        
        break;
    else
        um:Reset();
    end
end

local slot = um:ReadChar();

LocalPlayer():GetInventory()[slot] = GAMEMODE.ItemIDs[id];

end);[/lua]

Then you’ll have to send a different usermessage if you want to set that value to it’s default value though.

First off, Receive is spelled incorrectly.

And your coding style changes every now and then.

That makes no sense. bf_read simply reads a stream of received bytes, it has no way of knowing which types are present. I don’t understand why you think your system would work, but if you want a concrete example that shows how flawed it is, here you go:

[lua]if ( SERVER ) then
umsg.Start( “Test” )
umsg.Short( 2010 )
umsg.Char( 65 )
umsg.End()
else
local types = {
{ “Char”, 0 },
{ “Short”, 0 }
}

usermessage.Hook( "Test", function( um )
	for _, v in ipairs( types ) do
		local a = um[ "Read" .. v[1] ]( um )
		
		if ( a != v[2] ) then
			print( "First value in user message is a \"" .. v[1] .. "\"" )
			
			break
		else
			um:Reset()
		end
	end
end )

end[/lua]


First value in user message is a "Char"

There must’ve been a flaw in my testing methods when I was trying that. Anyway, you can just send an extra char for the type. It will still be lighter than GLON.

Well, if you want to support everything, including sub-tables and keys of all types, you’ll quickly get data that’s as big as GLONs. But besides that, if you are going to send a substantial amount of data (take the level in GCraft as an example), you would use your own system anyway.

What about this:

[lua]if SERVER then //Mainly proof of concept, not at all perfect, might work in theory.

//PRDMSG_ =
PRDMSG_STR = 0
PRDMSG_NUM = 1
PRDMSG_BOL = 2
PRDMSG_ENT = 3
PRDMSG_VEC = 4
PRDMSG_ANG = 5
PRDMSG_COL = 6

function SendPredictedUsrMsg(ply, …)

local args = {...}

umsg.Start("PredictedUsrMsg", ply)

	local str = ""
	local tbl = {}
	
	for _, v in pairs(args or {}) do //there are two loops, because the types need to be collected before anything is actually sent.
	
		if type(v) == "string" then str = str..PRDMSG_STR table.insert(tbl, v)
		
		elseif type(v) == "number" then str = str..PRDMSG_NUM table.insert(tbl, v)
		
		elseif type(v) == "boolean" then str = str..PRDMSG_BOL table.insert(tbl, v)
		
		elseif type(v) == "Entity" or type(v) == "Player" then str = str..PRDMSG_ENT table.insert(tbl, v)
		
		elseif type(v) == "Vector" then str = str..PRDMSG_VEC table.insert(tbl, v)
		
		elseif type(v) == "Angle" then str = str..PRDMSG_ANG table.insert(tbl, v)
		
		elseif type(v) == "Color" then str = str..PRDMSG_COL table.insert(tbl, v) end
		
	end
	
	umsg.Long(tonumber(str)) //HAHAHAHAH
	
	for _, v in pairs(tbl) do
	
		if type(v) == "string" then umsg.String(v)
		
		elseif type(v) == "number" then umsg.Long(v)
		
		elseif type(v) == "boolean" then umsg.Bool(v)
		
		elseif type(v) == "Entity" or type(v) == "Player" then umsg.Entity(v)
		
		elseif type(v) == "Vector" then umsg.Vector(v)
		
		elseif type(v) == "Angle" then umsg.Angle(v)
		
		elseif type(v) == "Color" then /*However this is done*/ end
	
	end

umsg.End()

end

else

PRDMSG_STR = 0
PRDMSG_NUM = 1
PRDMSG_BOL = 2
PRDMSG_ENT = 3
PRDMSG_VEC = 4
PRDMSG_ANG = 5
PRDMSG_COL = 6

usermessage.Hook(“PredictedUsrMsg”, function(um)

local order = um:ReadLong()
local tbl = {}

for _, v in pairs(string.Explode(order, "")) do

	if v == PRDMSG_STR then table.insert(tbl, um:ReadString())
	
	elseif v == PRDMSG_NUM then table.insert(tbl, um:ReadLong())
	
	elseif v == PRDMSG_BOL then table.insert(tbl, um:ReadBool())
	
	elseif v == PRDMSG_ENT then table.insert(tbl, um:ReadEntity())
	
	elseif v == PRDMSG_VEC then table.insert(tbl, um:ReadVector())
	
	elseif v == PRDMSG_ANG then table.insert(tbl, um:ReadAngle())
	
	elseif v == PRDMSG_COL then table.insert(tbl, /*Something*/) end

end

//Do what you want with "tbl".

end)

end[/lua]

That’s limited to about nine variables per usermessage, because the maximum value for an unsigned long is 4294967295. Then again, of course you could split it up to have multiple longs.

Interesting concept, it might even be cheaper than sending a char for each variable.

This exactly. Glon really is for when you have no idea what the table is and people too lazy to do their own networking, which is always the most efficient way.

Yeah, this has lots of room for improvement.