• WriteTable Replacements
    11 replies, posted
post where you're using writetable and give me enough context (what you're sending) and I'll write you a replacement for free. you guys are too lazy to stop using it, well fuck you, now you have no excuse
What is exactly wrong with it?
[QUOTE=code_gs;47313101]What is exactly wrong with it?[/QUOTE] Network overhead. Read/WriteTable sends the type of every key and value in a table alongside their values. Frankly I don't see the big deal as long as you use it conservatively.
writetable: [code]for each item in the table: write 8 bits for the type of the key write the key (bad for non-associative arrays) write 8 bits for the type of the value write the value write another 8 bits[/code] it's way unnecessary, and there's a better way to do it 100% of the time [QUOTE=Joeyl10;47313119] Frankly I don't see the big deal as long as you use it conservatively.[/QUOTE] it's lazy coding, it adds up, and it just slows down servers
Thanks for doing this, but can you show us an example on how to avoid using WriteTable and then we could try it out for ourselves? I have tons of addons using net.WriteTable and I would like to rewrite all of them.
Isn't creating a replacement in your addons as simple as sending the table size then looping thorough the table networking the values with their respective net.Write's and reading it on the client? Would that be the way to do it or am I missing somthing?
it really depends on the type of table you're trying to send. if a table is all the same type, all it really takes is sending the length, and then each value. for more advanced stuff, it gets more complicated. here's a method of sending arguments for chat.AddText that I recently wrote: [lua]nchat = { writetype = { [TYPE_NIL] = function() return 0 end, --[[ [TYPE_BOOL] = function(val) return 1, {val and "true" or "false"} end, ]] [TYPE_NUMBER] = function(val) return 1, {val} end, [TYPE_STRING] = function(val) return 1, {val} end, [TYPE_TABLE] = function(val) assert(IsColor(val), string.format("unsupported object type '%s' (%s)", type(arg), tostring(arg))) return 1, {val} end, [TYPE_ENTITY] = function(val, vals) if(val:GetClass() == "player") then return 3, {team.GetColor(val:Team()), val:Nick(), nchat.lastcol} else return 1, {val:GetClass()} end end, --[[ [TYPE_VECTOR] = function(val) return 1, {tostring(val)} end, [TYPE_ANGLE] = function(val) return 1, {tostring(val)} end ]] }, pack = function(col) local num = col.b num = bit.bor(num, bit.lshift(col.g, 8)) num = bit.bor(num, bit.lshift(col.r, 16)) return num end, unpack = function(num) local b = bit.band(num, 255) local g = bit.band(bit.rshift(num, 8), 255) local r = bit.band(bit.rshift(num, 16), 255) return Color(r, g, b) end } if(SERVER) then AddCSLuaFile() nchat.writechat = function(...) local args = {...} local len = 0 local vals = {} nchat.lastcol = Color(255, 255, 255) for k, arg in pairs(args) do local func = nchat.writetype[TypeID(arg)] --assert(func, string.format("unsupported object type '%s' (%s)", type(arg), tostring(arg))) if(func) then local num, sep = func(arg) len = len + num for k, val in pairs(sep) do vals[#vals + 1] = val end end end if(len < 1) then return end net.WriteUInt(len - 1, 5) --32 things for k, val in pairs(vals) do local color = TypeID(val) == TYPE_TABLE --it's either a string or a color if not(color and nchat.lastcol == color) then net.WriteBit(color) if(color) then net.WriteUInt(nchat.pack(val), 24) --packed colors are 24 bits nchat.lastcol = val else net.WriteString(val) end end end end else nchat.readchat = function() local tab = {} local len = net.ReadUInt(5) + 1 for i = 1, len do local kind = net.ReadBit() == 1 if(kind) then tab[#tab + 1] = nchat.unpack(net.ReadUInt(24)) else tab[#tab + 1] = net.ReadString() end end chat.AddText(unpack(tab)) end end[/lua] it works by converting all arguments to either strings or colors, then sending those
Another one that writes to chat: [QUOTE=zerf;47267092] -stuff- Two bad points on this: 1) Extra for loop to flatten players 2) Couldn't get WriteBit/ReadBit to work so I used WriteUInt with size of 1 Edit: Just tested with this code vs writetable: [lua] if SERVER then util.AddNetworkString("Testerino") function temporary() net.Start("Testerino") WriteChat(color_white, "Hello there, ", player.GetHumans()[1], "!") net.Send(player.GetHumans()[1]) end else net.Receive("Testerino", function(len) local tbl = ReadChat() chat.AddText(unpack(tbl)) end) end [/lua] The len argument of the receiver was 241 with WriteChat and 504 with WriteTable. I'd say that's pretty good. [/QUOTE] [lua] local function WriteChat(...) local args = {...} local sends = {} for i = 1, #args do -- flatten players into team col and nick; can't think of way to get correct len without this local o = args[i] if type(o) == "Player" then sends[#sends + 1] = team.GetColor(o:Team()) sends[#sends + 1] = o:Nick() else sends[#sends + 1] = o end end net.WriteUInt(#sends, 4) for i = 1, #sends do local o = sends[i] local typ = type(o) if typ == "string" or typ == "number" or typ == "boolean" then -- technically you could put whatever here net.WriteUInt(0, 1) net.WriteString(tostring(o)) elseif IsColor(o) then net.WriteUInt(1, 1) net.WriteColor(o) end end end local function ReadChat() local len = net.ReadUInt(4) local ret = {} for i = 1, len do local typ = net.ReadUInt(1) local func = (typ == 0) and net.ReadString or net.ReadColor ret[#ret + 1] = func() end return ret end [/lua]
Here's a thing I made some time ago: [URL]https://github.com/Mijyuoon/starfall/blob/master/lua/autorun/advlib.lua#L456-L642[/URL] It allows you to define a data structure and generates code to send and receive exactly what you defined. Usage is like this: [code] --- CLIENT local ds = adv.NetStruct "i32 {i8 s} ${someKey:fs otherKey:v}" ds:InitReader() net.Receive("someMsg", function() local a, b, c = ds:ReadStruct() -- do something with data end) --- SERVER local ds = adv.NetStruct "i32 {i8 s} ${someKey:fs otherKey:v}" ds:InitWriter() net.Start("someMsg") ds:WriteStruct(1234567, {1, "one", 2, "two"}, {someKey = 2.718, otherKey = Vector(5,5,5)}) net.Send() [/code] What data structure in above example actually means: [I]i32[/I] - 32 bit signed integer [I]{i8 s}[/I] - list of alternating 8 bit signed ints and strings (Lists have length sent as 16 bit unsigned integer, so limit is 65535 entries) [I]${someKey:fs otherKey:v}[/I] - table with two keys: fs - single precision float, v - vector Generated code for that data structure (this is then CompileString'd): [code] --- WRITER local _val0 = {...} net.WriteInt(_val0[1],32) local _val1 = _val0[2] net.WriteUInt(#_val1,16) for _i1 = 1, #_val1, 2 do net.WriteInt(_val1[_i1+0],8) net.WriteString(_val1[_i1+1]) end local _val1 = _val0[3] net.WriteFloat(_val1["someKey"]) net.WriteFloat(_val1["otherKey"].x) net.WriteFloat(_val1["otherKey"].y) net.WriteFloat(_val1["otherKey"].z) --- READER local _val0 = {} _val0[1] = net.ReadInt(32) local _val1 = {} _val0[2] = _val1 local _len1 = net.ReadUInt(16) for _i1 = 1, _len1, 2 do _val1[_i1+0] = net.ReadInt(8) _val1[_i1+1] = net.ReadString() end local _val1 = {} _val0[3] = _val1 _val1["someKey"] = net.ReadFloat() _val1["otherKey"] = Vector(net.ReadFloat(), net.ReadFloat(), net.ReadFloat()) return _val0 [/code]
[QUOTE=mijyuoon;47314472]..[/QUOTE] This is cool. Lulpeg is a pretty big dependency though. I feel like this could be done using Lua pattern matching in less than 50 lines.
22 matches across 11 files God help me.
[QUOTE=Wyozi;47314711]This is cool. Lulpeg is a pretty big dependency though. I feel like this could be done using Lua pattern matching in less than 50 lines.[/QUOTE] I used LPeg there because I was using it for other stuff as well, mainly for Moonscript compiler I ported to gmod. But yea, it can be pretty heavy just for something like this.
Sorry, you need to Log In to post a reply to this thread.