Log In  


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]