I need help getting my code working, my current goal is to make a version of the example socket program in the docs using the _init and _update functions. my end goal is to make a server that can send and respond to packets from other picotron clients, and I'm hoping to potentially have a UI, hence why I'm using the _update function instead of just running the code without it.
--[[ server (paste in terminal window) listener = socket("tcp://*:8899") if (not listener) print("** could not create socket") local clients = {} print("") while (listener) do print("\rlistening \f8"..("-\\|/")[1+(time()*4%4)\1]) local new_client = listener:accept() if (new_client) then print("\r\fcnew client!") print("") add(clients, new_client) end for client in all(clients) do local dat = client:read() if (dat) print("\rmessage: "..dat) print"" end end --]] function _init() listener = socket("tcp://*:8899") --a socket solely used for listening for new clients. if not listener then print("error: unable to create socket") return end --print(listener:status()) print("listening on port 8899") clients = {} --will be filled with clients found by the listener msgs = {} --will be filled with messages from clients --need to figure out how to link them to their original client though. --can either send a table as a message (use pod to encode as string), or I could --do something server side to link the messages that way. --server side seems better, as the client could disconnect. --client could maybe provide a username to use on first connection? end function _update() if listener or true then --if the listener socket is open? (I think) local new_client = listener:accept() --listener:accept() returns a new socket for a client if there is one, and nil if ther isn't one. --need to figure out how I'm going to link messages to their clients. --I could have a client ID for each client, though I would need to make sure --that it could handle disconnects. --if I can reliably get a "first message" from a client, it could send a username --to be used as it's ID. then, any messages sent could be bundled with that name. if new_client then add(clients,new_client) --adds the new socket to the clients array print("client connected!") end for client in all(clients) do print(client:read()) end end end |
(please ignore the comments, I'm forgettful, so I need to write down what my next steps are, and what approaches I can think of to get there. I'm learning as I go along, so there will probably be a best way to do it, but this isn't going to be a very serious project, most likely I'll call it quits once clients can connect and control a character)
I have no clue why my code broke, I was working on it, took a break, came back, and it was broken.
this is the error I'm getting. it only appears once a client connect to the socket, it runs fine before then.
/ram/cart/main.lua:55: bad argument #1 to 'add'(table expected, got nil) |
I've tried debugging by replacing that portion of the code with print messages, but I can't figure out how to get it to show multiple at once, instead of just whatever the first one was.
I also tried running the example that from the docs again, incase it was a networking issue, it isn't, the example works fine.
edit
I figured out that, for whatever reason, if I declare two tables one after the other, none of the other code in the init function runs. I have no clue why, but that seems to be the case. if I declare one table, it runs fine, but two is too much 😠(if anyone has an idea as to why this is, please post about it, or even just test my cart to see if its an issue with my machine specifically somehow, I can't get it to happen in any carts other than this one too.)



I made an example too, maybe it will help you.
cols = 16 rows = 13 tile_size = 8 screen_w = cols * tile_size screen_h = rows * tile_size screen_w_h = screen_w / 2 screen_h_h = screen_h / 2 -- background color bg = 3 PORT = 8899 PROT = "tcp" -- Menu state menu = true selected_option = 1 -- 1 for Host, 2 for Client mode = nil -- Will be "host" or "client" window { title = "\014Socket App", width = cols * tile_size, height = rows * tile_size, resizeable = false, autoclose = false, pauseable = false } function _init() clients = {} client_resp = {} client_status = {} sock = nil listener = nil debug_log = {} -- For debug messages end function start_server(_prot, _port) clients = {} client_resp = {} debug_log = {} local list = socket(_prot .. "://*:" .. _port) if (list) then add(debug_log, "Server started on " .. _prot .. ":*:" .. _port) return list end add(client_resp, "** could not create socket on port: " .. _port) add(debug_log, "** Failed to create server on port: " .. _port) return nil end function resp_all(_msg) local sent_count = 0 for i, c in pairs(clients) do if c:status() == "ready" then c:write(_msg) sent_count += 1 else add(debug_log, "Client " .. i .. " not ready: " .. c:status()) end end add(debug_log, "Ping sent to " .. sent_count .. "/" .. #clients .. " clients") end function _draw() cls(bg) if menu then print("\014Select Mode:", 4, 4, 6) print("\014" .. (selected_option == 1 and "> " or " ") .. "Host", 4, 20, 6) print("\014" .. (selected_option == 2 and "> " or " ") .. "Client", 4, 28, 6) print("\014<enter> to select", 4, screen_h - 8, 6) elseif mode == "host" then if listener then print("\rlistening " .. PROT .. "://*:" .. PORT .. " \f8" .. ("-\\|/")[1 + (time() * 4 % 4) \ 1]) print("clients: " .. #clients) end for i, l in pairs(client_resp) do print(l, 6, 10 * i) end for i, l in pairs(client_status) do print("Client " .. i .. ": " .. l, screen_w_h, 10 * i, 6) end for i, l in pairs(debug_log) do print("\014"..l, 4, screen_h - 32 - 8 * i, 8) -- Debug log in red end print("\014<p> ping clients", 4, screen_h - 24, 6) print("\014<z> clear clients", 4, screen_h - 16, 6) print("\014<x> clear log", 4, screen_h - 8, 6) elseif mode == "client" then if sock then local status = sock:status() print("\014socket " .. status, 4, 0, 6) else print("\014socket fault", 4, 0, 6) end for i, l in pairs(client_resp) do print(l, 4, 16 + 8 * i, 6) end print("\014<z> send 'hi'", 4, screen_h - 16, 6) print("\014<x> close socket", 4, screen_h - 8, 6) end end function _update() if menu then if btnp(2) then selected_option = selected_option == 1 and 2 or 1 elseif btnp(3) then selected_option = selected_option == 1 and 2 or 1 elseif keyp("enter") then menu = false if selected_option == 1 then mode = "host" listener = start_server(PROT, PORT) else mode = "client" sock = socket("tcp://localhost:" .. PORT) if not sock then add(client_resp, "** could not create socket") add(debug_log, "** Client failed to connect to localhost:" .. PORT) else add(debug_log, "Client connected to localhost:" .. PORT) end end end elseif mode == "host" then if listener then local new_client = listener:accept() if new_client then add(clients, new_client) add(client_resp, "new client connected!") add(debug_log, "New client connected, total: " .. #clients) end for i, client in pairs(clients) do local dat = client:read() if dat then add(client_resp, "Client " .. i .. ": " .. dat) add(debug_log, "Received from client " .. i .. ": " .. dat) end end end local new_status = {} for i, client in pairs(clients) do local status = client:status() if status == "invalid" or status == "closed" or status == "closed by peer" or status == "disconnected" then add(debug_log, "Removing client " .. i .. ": " .. status) del(clients, client) else new_status[i] = status end end client_status = new_status if btnp(4) then clients = {} client_status = {} add(debug_log, "Cleared all clients") elseif btnp(5) then client_resp = {} debug_log = {} add(debug_log, "Cleared logs") elseif keyp("p") then resp_all("Ping!") end elseif mode == "client" then if sock then local dat = sock:read() if dat then add(client_resp, "Server: " .. dat) add(debug_log, "Client received: " .. dat) end local status = sock:status() if status == "closed" or status == "closed by peer" or status == "disconnected" then add(debug_log, "Client socket " .. status) sock = nil end end if btnp(4) then cls() if sock and sock:status() == "ready" then sock:write("hi!") add(client_resp, "Sent: hi") add(debug_log, "Client sent: hi") else add(client_resp, "Cannot send: socket not ready") add(debug_log, "Client cannot send: socket not ready") end elseif btnp(5) then if sock then sock:close() add(debug_log, "Client socket closed") sock = nil end end end end |



@harobling, I'm honestly not sure if it's even a socket issue at this point, I edited the original post with more info, I genuinely have no clue what's going on.



Well you're defining clients as local, so it won't be accessible outside of _init()



@Soupster, I saw that earlier, but when I copy-pasted it in and removed that, it still gave the same error for some reason. (I meant to edit it when I did that, I must have forgot, so I'll edit it now)



Ah. @zep changed the way print works in _init so that it flips after printing. But I think this means it just runs _update after the first print because it doesn't run the rest of _init. I think the solution is to not use print in _init. Try notify() instead?



@Soupster, thanks! Içm not at my computer right now, but I'll be sure to try this and see if it works! What does I'm guessing notify sends a system notification or something along those lines? I haven't heard of it until now 😅
[Please log in to post a comment]