How to send data with dynamic size from clent to server ?

As I was my coding my sent, I needed to transfer parameterization information from the server to the client when the entity is created.
In my case, I only need the following, so the client can view my tracing sensor objects, that are made and working on the server:



local tInit = {
  "{sensor:DFR}{Len: 12}{Org: 17,-47,-4.3}{Dir:Up}",
  "{sensor:DBR}{Len: 12}{Org:-17,-47,-4.3}{Dir:Up}",
  "{sensor:DFL}{Len: 12}{Org: 17, 47,-4.3}{Dir:Up}",
  "{sensor:DBL}{Len: 12}{Org:-17, 47,-4.3}{Dir:Up}",
  "{sensor:SFR}{Len:-15}{Org: 17,-62, 4.0}{Dir:Rg}",
  "{sensor:SBR}{Len:-15}{Org:-17,-62, 4.0}{Dir:Rg}",
  "{sensor:SFL}{Len: 15}{Org: 17, 62, 4.0}{Dir:Rg}",
  "{sensor:SBL}{Len: 15}{Org:-17, 62, 4.0}{Dir:Rg}"
}


I was reading about that “net” library ( Here: http://wiki.garrysmod.com/page/Net_Library_Usage ), but i could not figure out how to make it detect different number of strings like if the table above has 20 rows for example.

Do you guys think that I should use in that case. This seems like a fair choice, as long as it transfers the table as a unit:




if(ERVER) then

util.AddNetworkString( "sent_parameterization_client_transfer" )
net.Start( "sents_parameterization_msg" )
net.WriteTable( tInit  )
net.Send()

elseif(CLIENT) then

net.Receive( "sents_parameterization_msg", function()
    local tInit = net.ReadTable()
    if(tInit) then
      print("The parameterization has arrived !")
      -- Do some processing of the strings table
    end
  end )
end



Send an integer that represents the amount of strings you are going to send first. Then send your strings. The read the number, and read that many strings. You can do the same with just vectors/ints too.

Don’t forget that there’s a limit of size of the net message!

You can use net.WriteTable/ReadTable but iirc these functions also write the data types of the stuff you’re sending so it’s not really optimal if your table’s structure is known.
If you’re sending a table with a variable size containing always the same type of values (in your case, strings), you can do it this way:

[lua]
if (SERVER) then
net.Start(“sents_parameterization_msg”)
net.WriteUInt(table.Count(tInit), 16)
for _, str in pairs (tInit) do
net.WriteString(str)
end
net.Send(target)
else
net.Receive(“sents_parameterization_msg”, function()
local tInit = {}
for i = 1, net.ReadUInt(16) do
tInit* = net.ReadString()
end

	PrintTable(tInit)
end)

end
[/lua]

Basically as Robotboy described, send the amount of values in your table first so the client knows how much stuff to expect, then read

Thanks :wink:

Isn’t it easier to send the whole table instead as I stated above, or that will do additional problems …

It will use more bandwitdh, as you are sending more data.

I only need to do it once :wink:

Well it will still take more bandwidth, and you have to take into account the 64kb limit.

Since you need to do it only once, you probably have a lot of data.

Well then better in piece then :wink:

Thanks !

Hmm, seems like the variable scope is different than the pure Lua …



  local stSet, arSen, lnSen = {}, {}, 0
      net.Receive(gsSentMsg, function()
        lnSen = net.ReadUInt(16)
        logStatus("ENT.NetGetClient: Length A <"..lnSen..">")
        for ID = 1, lnSen, 1 do
          arSen[ID] = net.ReadString() end
        logTable(arSen, "IN > ENT.NetGetClient: arSen")
      end)
      
      logTable(arSen, "OUT > ENT.NetGetClient: arSen")
      
      lnSen = #arSen
      
      logStatus("ENT.NetGetClient: Length B <"..lnSen..">")
      
      for ID = 1, lnSen, 1 do
        if(not setTransform(arSen[ID], stSet)) then
          return logStatus("ENT.NetGetClient: Process failed <"..arSen[ID]..">",false)
        end
        logStatus("ENT.NetGetClient: Process <"..arSen[ID]..">")
      end
      
      logTable(stSet, "ENT.NetGetClient: stSet")


Outputs this:



[CLIENT] [01/08/17 12:54:40:6607.230862] ENT.NetGetClient: Length A <8>
[CLIENT] [01/08/17 12:54:40:6607.236969] IN > ENT.NetGetClient: arSen
[CLIENT] [01/08/17 12:54:40:6607.242190] IN > ENT.NetGetClient: arSen[1] = "{sensor:DFR}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:17,-47,-4.3000001907349}{Len:12}{Dir:Up}"
[CLIENT] [01/08/17 12:54:40:6607.247104] IN > ENT.NetGetClient: arSen[2] = "{sensor:SBR}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:-17,-62,4}{Len:-15}{Dir:Rg}"
[CLIENT] [01/08/17 12:54:40:6607.252115] IN > ENT.NetGetClient: arSen[3] = "{sensor:DBR}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:-17,-47,-4.3000001907349}{Len:12}{Dir:Up}"
[CLIENT] [01/08/17 12:54:40:6607.257042] IN > ENT.NetGetClient: arSen[4] = "{sensor:DFL}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:17,47,-4.3000001907349}{Len:12}{Dir:Up}"
[CLIENT] [01/08/17 12:54:40:6607.262117] IN > ENT.NetGetClient: arSen[5] = "{sensor:SBL}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:-17,62,4}{Len:15}{Dir:Rg}"
[CLIENT] [01/08/17 12:54:40:6607.267208] IN > ENT.NetGetClient: arSen[6] = "{sensor:DBL}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:-17,47,-4.3000001907349}{Len:12}{Dir:Up}"
[CLIENT] [01/08/17 12:54:40:6607.271940] IN > ENT.NetGetClient: arSen[7] = "{sensor:SFL}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:17,62,4}{Len:15}{Dir:Rg}"
[CLIENT] [01/08/17 12:54:40:6607.277031] IN > ENT.NetGetClient: arSen[8] = "{sensor:SFR}{Hit:prop_detail/prop_dynamic/prop_static/prop_physics}{Org:17,-62,4}{Len:-15}{Dir:Rg}"
[CLIENT] [01/08/17 12:54:40:6607.283398] ENT.Initialize: Success
[CLIENT] [01/08/17 12:54:40:6607.290103] OUT > ENT.NetGetClient: arSen = {}
[CLIENT] [01/08/17 12:54:40:6607.292175] ENT.NetGetClient: Length B <0>
[CLIENT] [01/08/17 12:54:40:6607.297028] ENT.NetGetClient: stSet = {}


Here I’ve defined


 local stSet, arSen, lnSen = {}, {}, 0 

in the upper scope, however the network message does not modify them for some reason.

No, your logic doesn’t add up.

Your code will not be ran sequentially like that. First will run everything but the callback in net.Receive.

The whole point of callbacks functions in function arguments is that they will be called at any point in the future undefined amount of times.

So the callback in net.Receive will be called only when a net message is actually received.

That means that first everything will be ran before any net messages can physically be received, and at that point all the variables are indeed {}, {} and 0.

Only after a net message is received they will change, at which point your debug code has already ran.

Oh, that’s the case then. I used Get/Set networked and now it works like a charm :wink: (Only the desired local vectors instead of the whole initialization) since I need to debug the code state in the server. Going to the the next step of the experiment compiling a function from string:

Now I am trying to compile string into functions using the “CompileString” function the string is actually a part from the initialization above and for example “{Err:((DFL < DBL) and DFL or DBL)}”.

I am using my own compiler:



  local function getDevError(sErr, sCon, stSen)
    local sCo = tostring(sCon) -- Copy a temporary error selection
    local sEr = tostring(sErr):rep(1):Trim()
    for key, sen in pairs(stSen) do
      sEr = sEr:gsub(key, "oSens[\""..tostring(key).."\"].Val") end
    local sFc, sID = "function dev"..sCo.."(oSens) return "..sEr.." end", gsSentFile.."_"..sCo
    return logStatus("getDevError("..sCo.."): <"..sFc..">", CompileString(sFc, sID))
  end

  local dev = getDevError("((DFL < DBL) and DFL or DBL)","UL" ...)


Where it compiled (in one line):



function devUL(oSens)
  return ((oSens["DFL"].Val < oSens["DBL"].Val) and oSens["DFL"].Val or oSens["DBL"].Val)
end


My structure already has the “Val” field, though when the function is pcall-ed it returns nil and executes correctly when using



 local suc, out = dev(...)


I need actually to assign the function above to a structure field like so (EDIT):



["UL"] = {ID = 2, Ref = {4.5}, Tun = stringExplode("/",varConUD:GetString()), Cmb = true, Rev = true, -- Positive when falls down (Up-Down-Left)
          Dev1 = function(oSens) return ((oSens["DFL"].Val < oSens["DBL"].Val) and oSens["DFL"].Val or oSens["DBL"].Val) end -- However this one is called perfectly and the compiled one is not!
          Dev2 = getDevError("((DFL < DBL) and DFL or DBL)","UL" ...)  -- Amusing here that Dev1 and Dev2 hold the exact functionality after the compilation, however the compiled's output is nil
        }


Am I compiling this correctly ?

I fixed my problem using this test code:



      local T = {["TEST"] = 5}
      local f = CompileString("return (function(T) return T[\"TEST\"] + 5 end)", "a_test")
      local foo = f()
      print(foo(T))


Turns out that you need to call the compiled string to return a function ( The code above actually prints “10” )