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.