Just for testing purposes

function setup()
    socket = require("socket")
    serverInfo = {tcpServer = nil, udpServer = nil, tcpClients = {}, udpClients = {}, worldState = {}}
    clientInfo = {tcpClient = nil, udpClient = nil, sessionID = "", clientList = {}, worldState = {}, tcpConnected = false, udpConnected = false}
    isServer = false
    parameter.action("Start Server", setupServer)
    parameter.action("Connect", connect)
end

function setupServer()
    -- We create a TCP server for clients to connect to
    local tcp = assert(socket.bind("*", "14285"))
    tcp:settimeout(0)
    local routerIp, ip, port = getIP(), tcp:getsockname()
    print("Server opened at "..routerIp.." : "..tostring(port))
    serverInfo.tcpServer = tcp
    local udp = socket.udp()
    udp:setsockname(ip, "14285")
    udp:settimeout(0)
    serverInfo.udpServer = udp
    isServer = true
end

function getIP()
    -- We create a UDP socket and assign it a random address
    local udp = socket.udp()
    udp:setpeername("192.167.188.122", "14285")
    -- This will retrieve the ip and port of our router
    local ip, port = udp:getsockname()
    udp:close()
    return ip
end

function connect()
    -- We create a TCP client and connect it to the server
    local tcp = socket.tcp()
    local routerIp, port = getIP()
    local ip1, ip2 = string.match(routerIp, "(%d+.%d+.%d+.)(%d+)")
    for z = 0, 255 do
        local result, err = tcp:connect(ip1..tostring(z), "14285")
        tcp:settimeout(0.002)
        if err == nil then
            print("TCP Connected!")
            clientInfo.tcpConnected = true
            clientInfo.tcpClient = tcp
            -- Now that we are connected, we can setup our UDP client
            local udp = socket.udp()
            udp:setpeername(ip1..z, "14285")
            udp:settimeout(0)
            clientInfo.udpClient = udp
            -- We can now add ourselves to the list of connected clients
            local udpIP, udpPort = udp:getpeername()
            local uid = {ip = udpIP, port = udpPort}
            table.insert(clientInfo.clientList, uid)
            -- Now we send a connection message to the server so they can send us the connected players
            udp:send("connection confirmation")
            break
        end
    end
end

function updateServer()
    if isServer then
        -- Here we can wait for clients and accept their connection attempts
        if serverInfo.tcpServer then
            local client = serverInfo.tcpServer:accept()
            if client then
                -- If we receive a connection, we can add the client into our TCP list
                table.insert(serverInfo.tcpClients, client)
            end
        end
        -- We can also receive messages from the UDP clients
        local msg, ip, port = serverInfo.udpServer:receivefrom()
        if msg then
            if msg == "connection confirmation" then
                -- If so, we can add the newly connected client into our UDP list
                local uid = {ip = ip, port = port}
                table.insert(serverInfo.udpClients, uid)
                -- Then, we can send them a message back with the list of connected clients
                local list = json.encode(serverInfo.udpClients)
                serverInfo.udpServer:sendto("connection accepted"..list, ip, port)
            elseif string.find(msg, "state update") then
                -- Here, we can retrieve the new state that was sent by the client
                local state = json.decode(string.sub(msg, 13, #msg))
                local hasState = false
                local existingState = nil
                for i,v in pairs(serverInfo.worldState) do
                    if v.sessionID == state.sessionID then
                        -- If it does match, then we know this is an updated version of an existing state
                        hasState = true
                        existingState = v
                        v.posX = state.posX
                        v.posY = state.posY
                        -- We can now send an update state message to the UDP client
                        local state = json.encode(existingState)
                        for i,c in pairs(serverInfo.udpClients) do
                            serverInfo.udpServer:sendto("client state"..tostring(state), c.ip, c.port)
                        end
                        break
                    end
                end
                -- If we don't have the state, then we know that it's a new state and we can add it to our list
                if hasState == false and existingState == nil then
                    table.insert(serverInfo.worldState, state)
                end
            end
        end
    end
end

function updateClient()
    -- If our TCP client is connected, we can let the UDP client receive any messages from the server
    if clientInfo.tcpConnected then
        local msg = clientInfo.udpClient:receive()
        if msg then
            if string.find(msg, "connection accepted") then
                print("UDP Connected!")
                -- Here, we can create the session ID for our client
                local udpString = tostring(clientInfo.udpClient)
                clientInfo.sessionID = string.sub(udpString, 17, #udpString)
                print("Session ID: "..clientInfo.sessionID)
                -- Now, we can update our list of connected clients from the packet, and start sending messages
                local list = json.decode(string.sub(msg, 20, #msg))
                clientInfo.udpConnected = true
                local ip, port = clientInfo.udpClient:getsockname()
                local uid = {ip = ip, port = port}
                for i,v in pairs(list) do
                    table.insert(clientInfo.clientList, v)
                end
                -- We can also update our world state by adding a new state which belongs to our UDP client
                local state = {sessionID = clientInfo.sessionID, posX = 0, posY = 0}
                table.insert(clientInfo.worldState, state)
            elseif string.find(msg, "client state") then
                -- Here, we retrieve the newly updated state from the server
                local state = json.decode(string.sub(msg, 13, #msg))
                for i,v in pairs(clientInfo.worldState) do
                    if v.sessionID == state.sessionID then
                        v.posX = state.posX
                        v.posY = state.posY
                        break
                    end
                end
            end
        end
    end
    -- After our connection has been accepted by the UDP server, we can start sending packets and updating players
    if clientInfo.udpConnected then
        local isLocal = false
        -- For every client currently connected on client-side, we create a "player" for them
        for i,player in ipairs(clientInfo.worldState) do
            if player.sessionID == clientInfo.sessionID then
                fill(255, 0, 0)
                isLocal = true
            else
                fill(0, 255, 0)
            end
            ellipse(player.posX, player.posY, 35)
            if isLocal then
                local newState = {sessionID = clientInfo.sessionID, posX = CurrentTouch.x, posY = CurrentTouch.y}
                clientInfo.udpClient:send("state update"..json.encode(newState))
            end
        end
    end
end

function draw()
    updateServer()
    updateClient()
end

@creator - can you explain the purpose of your project. Is it to enable dialogue between two pads or pad/computer. Or is it just establishing a surfer for general linkage?

function setup()
    socket = require("socket")
    parameter.action("Create Room", createRoom)
    parameter.action("Join Room", function()
        if other_ip then
            joinRoom(other_ip, false)
        else
            parameter.text("IP", "", function(t)
                other_ip = t
            end)
        end
    end)
    parameter.action("Find Room", function()
        findRoom()
    end)
    client = socket.udp()
    client:settimeout(0)
    player = socket.udp()
    player:settimeout(0)
    listener = socket.udp()
    listener:settimeout(0)
    -- We need to create a table that can hold information about our server, if you end up making one
    server_info = {players = 0, clients = {}, world_state = {}, puppet_states = {}, ids_taken = {}}
    -- We also need to create a table that can hold info that the client has
    client_info = {serverIp = "", world_state = {}}
    other_ip = nil
    current_time = nil
    current_room_count = 1
    tick = 0
    finding_room = false
    server = false
    joinedRoom = false
    randomRoom = false
    myPlayerServer = nil
    myPlayerClient = nil
    sendNumber = 0
    sendInterval = 0
    playerTick = 0
end

function createRoom()
    -- We need to find your address, so we create a new UDP socket and assign it a random IP
    local ip, port = getLocalIP()
    client:setsockname("0.0.0.0", "14285")
    local ip2, port2 = client:getsockname()
    client:setoption("broadcast", true)
    your_ip = ip2
    your_port = port2
    print("Room created!")
    print("Connect to "..ip)
    server = true
end

function joinRoom(ip, finding_room)
    if ip ~= "*" then
        if finding_room == false then
            print("Attempting to join the room...")
        end
        -- The port for all the servers is the same, so we only need the IP address of the server
        player:setpeername(ip, "14285")
        player:send("connection confirmation")
        current_time = os.time()
    else
        print("Address not allowed!")
    end
end

function findRoom()
    print("Attempting to find a room...")
    finding_room = true
    local ip, port = getLocalIP()
    local ip1, ip2 = string.match(ip, "(%d+.%d+.%d+.)(%d+)")
    local success, err = pcall(function()
        for z = 0, 255 do
            player:setpeername(ip1..z, "14285")
            player:send("requesting server info")
        end
        player:setpeername("*")
        player:setsockname("*", "14285")
    end)
    if success == false then
        -- An error occured whether that be no internet connection or message wasn't received
        print("An error occured! Please check your internet connection, and try again later.")
        return
    end
    current_time = os.time()
    randomRoom = true
end

function sendMessage(msg)
    if server == true then
        for i,c in pairs(server_info["clients"]) do
            client:sendto(tostring(msg), c.ip, c.port)
        end
    end
end

function leaveRoom()
    -- We need to notify the server that we are leaving, so we send a packet to them
    player:send("disconnection confirmation")
end

function resetParameters(params, args)
    parameter.clear()
    for i = 1, #params do
        parameter.action(params[i], args[i])
    end
end

function getLocalIP()
    local udp = socket.udp()
    udp:setpeername("192.167.188.122", "14285")
    local ip, port = udp:getsockname()
    udp:close()
    return ip, port
end

function receiveData()
    if server == true then
        local msg, ip, port = client:receivefrom()
        if msg ~= nil then
            if msg == "connection confirmation" then
                if server_info["players"] < 32 then
                    -- We can now add 1 more to our player count
                    server_info["players"] = server_info["players"] + 1
                    -- We now need to store the clients IP and port, so we can send them messages later
                    local uid = {ip = ip, port = port}
                    if not server_info["clients"][uid] then
                        table.insert(server_info["clients"], uid)
                    end
                    -- We now create a random ID for the connecting peer so we can differentiate between players on the server
                    local playerID = math.random(10000, 99999)
                    if not server_info["ids_taken"][playerID] then
                        server_info["ids_taken"][playerID] = playerID
                    else
                        playerID = math.random(10000, 99999)
                    end
                    client:sendto("valid confirmation"..playerID, ip, port)
                    client:sendto("peer address info"..json.encode(uid), ip, port)
                    -- We need to send the joining client our IP, so they can send messages to us later
                    local our_ip, our_port = getLocalIP()
                    client:sendto("server ip"..our_ip, ip, port)
                    -- We can now send everyone that has joined this room a packet telling them the amount of players in the room
                    for i,c in pairs(server_info["clients"]) do
                        client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                    end
                else
                    -- We don't have enough room for another player, so we send the peer a message telling them to disconnect
                    local reason = "Max players inside the room!"
                    client:sendto("invalid confirmation"..reason, ip, port)
                end
            elseif msg == "requesting server info" then
                -- We need to find your address, so we create a new UDP socket and assign it a random IP
                local our_ip, our_port = getLocalIP()
                -- We now know that somebody is trying to find a server, so we send a packet back to them with our server IP and port
                local uid = {ip = our_ip}
                client:sendto("server info"..json.encode(uid), ip, port)
            elseif msg and string.find(msg, "disconnection confirmation") then
                -- We know that one of the clients connected to this server wants to leave, so we send a packet back telling them that we have been notified
                client:sendto("notified of disconnection", ip, port)
                local uid = {ip = ip, port = port}
                local clientInfo
                local clientState
                for i = 1, #server_info["clients"] do
                    if server_info["clients"][i] == uid then
                        clientInfo = i
                    end
                end
                for i = 1, #server_info["world_state"] do
                    local state = server_info["world_state"][i]
                    if state["addrInfo"].ip == uid.ip and state["addrInfo"].port == uid.port then
                        clientState = i
                    end
                end
                table.remove(server_info["clients"], clientInfo)
                table.remove(server_info["world_state"], clientState)
                print("server: "..json.encode(server_info["world_state"]))
                -- We also need to deduct the player count
                server_info["players"] = server_info["players"] - 1
                -- We can now send everyone that has joined this room a packet telling them the new amount of players in the room
                for i,c in pairs(server_info["clients"]) do
                    client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                end
            elseif msg and string.find(msg, "client state") then
                -- A connected peer has sent their client state to us, so we update our world state accordingly
                local clientState = json.decode(string.sub(msg, 13, #msg))
                local hasClientState = false
                local existingState = nil
                for i,s in pairs(server_info["world_state"]) do
                    if s.playerName == clientState.playerName then
                        hasClientState = true
                        existingState = s
                        break
                    end
                end
                if hasClientState == true then
                    local uid = {ip = ip, port = port}
                    if clientState.sendNumber > existingState.sendNumber then
                        existingState.sendNumber = clientState.sendNumber
                        -- We now also add a new waypoint for our puppet peer to follow
                        local waypoint = {x = clientState.playerX, y = clientState.playerY}
                        -- We now update the most recent waypoint to the new waypoint
                        existingState["mostRecentWaypoint"] = waypoint
                        existingState["touchDelta"] = clientState.touchDelta
                    end
                else
                    clientState["prevX"] = clientState.playerX
                    clientState["prevY"] = clientState.playerY
                    if not server_info["world_state"][clientState] then
                        table.insert(server_info["world_state"], clientState)
                    end
                end
            end
        end
        -- We now loop through every state and interpolate them to their most recent waypoint
        for i,state in pairs(server_info["world_state"]) do
            updatePositions(state, state.touchDelta)
        end
    end 
    if finding_room == false then
        local result = player:receive()
        if result and string.find(result, "valid confirmation") then
            -- We know that we successfully joined the room, and we can now receive messages from the server!
            if randomRoom == false then
                print("Successfully joined the room!")
            end
            joinedRoom = true
            current_time = nil
            parameter.clear()
            -- We now create a button that allows the player to leave the room
            local paramName = {"Leave Room"}
            local paramArgs = {function() leaveRoom() end}
            resetParameters(paramName, paramArgs)
            -- We now set our player name to the ID that the server sent us
            local playerID = string.sub(result, 19, #result)
            local playerTab = {playerName = playerID, playerX = WIDTH/2, playerY = HEIGHT/2, touchDelta = nil}
            myPlayerClient = playerTab
            if not client_info["world_state"][myPlayerClient] then
                table.insert(client_info["world_state"], myPlayerClient)
            end
            player:setoption("dontroute", true)
        elseif result and string.find(result, "peer address info") then
            local addrInfo = json.decode(string.sub(result, 18, #result))
            if myPlayerClient then
                myPlayerClient["addrInfo"] = addrInfo
            end
            if randomRoom == true then
                print("Successfully joined a room at: "..myPlayerClient["addrInfo"].ip.." : "..myPlayerClient["addrInfo"].port)
            end
        elseif result and string.find(result, "server ip") then
            -- Now that we have the servers IP address, we can message them later if we need to
            local serverIp = string.sub(result, 10, #result)
            client_info["serverIp"] = serverIp
        elseif result and string.find(result, "player count") then
            -- We now know the current amount of players in our room, for now at least
            local count = string.sub(result, 13, #result)
            print("Player count: "..count)
        elseif result and string.find(result, "invalid confirmation") then
            -- We can now see the reason for our invalid confirmation and display it
            local reason = string.sub(result, 21, #result)
            print("Could not join the room: "..reason)
            current_time = nil
            -- We can now also remove the address and port set for our peer
            player:setpeername("*")
        elseif result == "notified of disconnection" then
            -- We know that we can now safely leave the server
            player:setpeername("*")
            local removingState
            for i,s in pairs(client_info["world_state"]) do
                if s["addrInfo"].ip == myPlayerClient.addrInfo.ip and s["addrInfo"].port == myPlayerClient.addrInfo.port then
                    removingState = i
                end
            end
            table.remove(client_info["world_state"], removingState)
            print("client: "..json.encode(client_info["world_state"]))
            joinedRoom = false
            other_ip = nil
            print("Successfully left the room!")
            local paramNames = {"Create Room", "Join Room", "Find Room"}
            local paramArgs = {createRoom, function()
                    if other_ip then
                        joinRoom(other_ip, false)
                    else
                        parameter.text("IP", "", function(t)
                            other_ip = t
                        end)
                    end
                end, findRoom}
            resetParameters(paramNames, paramArgs)
        elseif result == nil then
            if current_time then
                if finding_room == false then
                    if os.time() > current_time + 5 then
                        -- If we get no response back for 5 seconds or more, we know it didn't work
                        print("Failed to connect, please try again later")
                        current_time = nil
                    end
                end
            end
        end
    else
        local result, ip, port = player:receivefrom()
        if result and string.find(result, "server info") then
            -- Now we can attempt to join the room with the given IP address
            local tab = json.decode(string.sub(result, 12, #result))
            if finding_room == true then
                joinRoom(tab.ip, true)
                finding_room = false
            end
        else
            if finding_room == true then
                if current_time and os.time() > current_time + 15 then
                    -- If we get no response back for 15 seconds or more, we know we couldn't find a room
                    print("Failed to find a room!")
                    current_time = nil
                end
            end
        end
    end
end

function lerp(a, b, t)
    return (1 - t) * a + t * b
end

function updatePositions(state, touchDelta)
    if state["mostRecentWaypoint"] == nil then return end
    state.playerX = state["mostRecentWaypoint"].x
    state.playerY = state["mostRecentWaypoint"].y
end

function touched(touch)
    if touch.state ~= ENDED and touch.state ~= CANCELLED then
        if touch.state == BEGAN or touch.state == CHANGED then
            if joinedRoom == true then
                if myPlayerClient then
                    myPlayerClient.playerX = CurrentTouch.x
                    myPlayerClient.playerY = CurrentTouch.y
                    myPlayerClient.touchDelta = {x = touch.delta.x, y = touch.delta.y}
                    sendNumber = sendNumber + 1
                end
            end
        end
    end
end

function updatePlayers()
    -- We now update the position of our player puppets, so it looks like the players are being updated in realtime
    if joinedRoom == true then
        for i,sp in pairs(server_info["world_state"]) do
            fill(255, 13, 0)
            text("server side", sp.playerX, sp.playerY + 30)
            ellipse(sp.playerX, sp.playerY, 35)
        end
        for i,cp in pairs(client_info["world_state"]) do
            fill(0, 255, 0)
            text("client side", cp.playerX, cp.playerY + 30)
            ellipse(cp.playerX, cp.playerY, 35)
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    
    -- This sets the line thickness
    strokeWidth(5)
    
    -- Do your drawing here
    updatePlayers()
    receiveData()
    if myPlayerClient and joinedRoom == true and myPlayerClient.addrInfo ~= nil then
        local playerTab = {playerName = myPlayerClient.playerName, playerX = myPlayerClient.playerX, playerY = myPlayerClient.playerY, sendNumber = sendNumber, addrInfo = myPlayerClient.addrInfo, mostRecentWaypoint = nil, touchDelta = myPlayerClient.touchDelta}
        sendInterval = sendInterval + 1
        if sendInterval >= 3 then
            player:send("client state"..json.encode(playerTab))
            sendInterval = 0
        end
    end
end
function setup()
    socket = require("socket")
    parameter.action("Create Room", createRoom)
    parameter.action("Join Room", function()
        if other_ip then
            joinRoom(other_ip, false)
        else
            parameter.text("IP", "", function(t)
                other_ip = t
            end)
        end
    end)
    parameter.action("Find Room", function()
        findRoom()
    end)
    client = socket.udp()
    client:settimeout(0)
    listener = socket.udp()
    listener:settimeout(0)
    -- We need to create a table that can hold information about our server, if you end up making one
    server_info = {players = 0, clients = {}, world_state = {}, puppet_states = {}, ids_taken = {}}
    -- We also need to create a table that can hold info that the client has
    client_info = {serverIp = "", world_state = {}}
    other_ip = nil
    current_time = nil
    current_room_count = 1
    tick = 0
    finding_room = false
    server = false
    joinedRoom = false
    randomRoom = false
    myPlayerServer = nil
    myPlayerClient = nil
    sendNumber = 0
    sendInterval = 0
    playerTick = 0
end

function createRoom()
    -- We need to find your address, so we create a new UDP socket and assign it a random IP
    local ip, port = getLocalIP()
    client:setsockname("0.0.0.0", "14285")
    local ip2, port2 = client:getsockname()
    client:setoption("broadcast", true)
    your_ip = ip2
    your_port = port2
    print("Room created!")
    print("Connect to "..ip)
    server = true
end

function joinRoom(ip, finding_room)
    if server == false then
        if ip ~= "*" then
            if finding_room == false then
                print("Attempting to join the room...")
            end
            -- The port for all the servers is the same, so we only need the IP address of the server
            client:setpeername(ip, "14285")
            client:send("connection confirmation")
            current_time = os.time()
        else
            print("Address not allowed!")
        end
    else
        print("You have already opened a server!")
    end
end

function findRoom()
    if server == false then
        print("Attempting to find a room...")
        finding_room = true
        local ip, port = getLocalIP()
        local ip1, ip2 = string.match(ip, "(%d+.%d+.%d+.)(%d+)")
        local success, err = pcall(function()
            for z = 0, 255 do
                client:setpeername(ip1..z, "14285")
                client:send("requesting server info")
            end
            client:setpeername("*")
            client:setsockname("*", "14285")
        end)
        if success == false then
            -- An error occured whether that be no internet connection or message wasn't received
            print("An error occured! Please check your internet connection, and try again later.")
            return
        end
        current_time = os.time()
        randomRoom = true
    else
        print("You have already opened a server!")
    end
end

function sendMessage(msg)
    if server == true then
        for i,c in pairs(server_info["clients"]) do
            client:sendto(tostring(msg), c.ip, c.port)
        end
    end
end

function leaveRoom()
    if server == false then
        -- We need to notify the server that we are leaving, so we send a packet to them
        client:send("disconnection confirmation")
    end
end

function resetParameters(params, args)
    parameter.clear()
    for i = 1, #params do
        parameter.action(params[i], args[i])
    end
end

function getLocalIP()
    local udp = socket.udp()
    udp:setpeername("192.167.188.122", "14285")
    local ip, port = udp:getsockname()
    udp:close()
    return ip, port
end

function receiveData()
    if server == true then
        local msg, ip, port = client:receivefrom()
        if msg ~= nil then
            if msg == "connection confirmation" then
                if server_info["players"] < 32 then
                    -- We can now add 1 more to our player count
                    server_info["players"] = server_info["players"] + 1
                    -- We now need to store the clients IP and port, so we can send them messages later
                    local uid = {ip = ip, port = port}
                    if not server_info["clients"][uid] then
                        table.insert(server_info["clients"], uid)
                    end
                    -- We now create a random ID for the connecting peer so we can differentiate between players on the server
                    local playerID = math.random(10000, 99999)
                    if not server_info["ids_taken"][playerID] then
                        server_info["ids_taken"][playerID] = playerID
                    else
                        playerID = math.random(10000, 99999)
                    end
                    client:sendto("valid confirmation"..playerID, ip, port)
                    client:sendto("peer address info"..json.encode(uid), ip, port)
                    -- We need to send the joining client our IP, so they can send messages to us later
                    local our_ip, our_port = getLocalIP()
                    client:sendto("server ip"..our_ip, ip, port)
                    -- We can now send everyone that has joined this room a packet telling them the amount of players in the room
                    for i,c in pairs(server_info["clients"]) do
                        client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                    end
                else
                    -- We don't have enough room for another player, so we send the peer a message telling them to disconnect
                    local reason = "Max players inside the room!"
                    client:sendto("invalid confirmation"..reason, ip, port)
                end
            elseif msg == "requesting server info" then
                -- We need to find your address, so we create a new UDP socket and assign it a random IP
                local our_ip, our_port = getLocalIP()
                -- We now know that somebody is trying to find a server, so we send a packet back to them with our server IP and port
                local uid = {ip = our_ip}
                client:sendto("server info"..json.encode(uid), ip, port)
            elseif msg and string.find(msg, "disconnection confirmation") then
                -- We know that one of the clients connected to this server wants to leave, so we send a packet back telling them that we have been notified
                client:sendto("notified of disconnection", ip, port)
                local uid = {ip = ip, port = port}
                local clientInfo
                local clientState
                for i = 1, #server_info["clients"] do
                    if server_info["clients"][i] == uid then
                        clientInfo = i
                    end
                end
                for i = 1, #server_info["world_state"] do
                    local state = server_info["world_state"][i]
                    if state["addrInfo"].ip == uid.ip and state["addrInfo"].port == uid.port then
                        clientState = i
                    end
                end
                table.remove(server_info["clients"], clientInfo)
                table.remove(server_info["world_state"], clientState)
                print("server: "..json.encode(server_info["world_state"]))
                -- We also need to deduct the player count
                server_info["players"] = server_info["players"] - 1
                -- We can now send everyone that has joined this room a packet telling them the new amount of players in the room
                for i,c in pairs(server_info["clients"]) do
                    client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                end
            elseif msg and string.find(msg, "client state") then
                -- A connected peer has sent their client state to us, so we update our world state accordingly
                local clientState = json.decode(string.sub(msg, 13, #msg))
                local hasClientState = false
                local existingState = nil
                for i,s in pairs(server_info["world_state"]) do
                    if s.playerName == clientState.playerName then
                        hasClientState = true
                        existingState = s
                        break
                    end
                end
                if hasClientState == true then
                    local uid = {ip = ip, port = port}
                    if clientState.sendNumber > existingState.sendNumber then
                        existingState.sendNumber = clientState.sendNumber
                        -- We now also add a new waypoint for our puppet peer to follow
                        local waypoint = {x = clientState.playerX, y = clientState.playerY}
                        -- We now update the most recent waypoint to the new waypoint
                        existingState["mostRecentWaypoint"] = waypoint
                        existingState["touchDelta"] = clientState.touchDelta
                    end
                else
                    clientState["prevX"] = clientState.playerX
                    clientState["prevY"] = clientState.playerY
                    if not server_info["world_state"][clientState] then
                        table.insert(server_info["world_state"], clientState)
                    end
                end
            end
        end
        -- We now loop through every state and interpolate them to their most recent waypoint
        for i,state in pairs(server_info["world_state"]) do
            updatePositions(state, state.touchDelta)
        end
    else
        if finding_room == false then
            local result = client:receive()
            if result and string.find(result, "valid confirmation") then
                -- We know that we successfully joined the room, and we can now receive messages from the server!
                if randomRoom == false then
                    print("Successfully joined the room!")
                end
                joinedRoom = true
                current_time = nil
                parameter.clear()
                -- We now create a button that allows the player to leave the room
                local paramName = {"Leave Room"}
                local paramArgs = {function() leaveRoom() end}
                resetParameters(paramName, paramArgs)
                -- We now set our player name to the ID that the server sent us
                local playerID = string.sub(result, 19, #result)
                local playerTab = {playerName = playerID, playerX = WIDTH/2, playerY = HEIGHT/2, touchDelta = nil}
                myPlayerClient = playerTab
                if not client_info["world_state"][myPlayerClient] then
                    table.insert(client_info["world_state"], myPlayerClient)
                end
                client:setoption("dontroute", true)
            elseif result and string.find(result, "peer address info") then
                local addrInfo = json.decode(string.sub(result, 18, #result))
                if myPlayerClient then
                    myPlayerClient["addrInfo"] = addrInfo
                end
                if randomRoom == true then
                    print("Successfully joined a room at: "..myPlayerClient["addrInfo"].ip.." : "..myPlayerClient["addrInfo"].port)
                end
            elseif result and string.find(result, "server ip") then
                -- Now that we have the servers IP address, we can message them later if we need to
                local serverIp = string.sub(result, 10, #result)
                client_info["serverIp"] = serverIp
            elseif result and string.find(result, "player count") then
                -- We now know the current amount of players in our room, for now at least
                local count = string.sub(result, 13, #result)
                print("Player count: "..count)
            elseif result and string.find(result, "invalid confirmation") then
                -- We can now see the reason for our invalid confirmation and display it
                local reason = string.sub(result, 21, #result)
                print("Could not join the room: "..reason)
                current_time = nil
                -- We can now also remove the address and port set for our peer
                client:setpeername("*")
            elseif result == "notified of disconnection" then
                -- We know that we can now safely leave the server
                client:setpeername("*")
                local removingState
                for i,s in pairs(client_info["world_state"]) do
                    if s["addrInfo"].ip == myPlayerClient.addrInfo.ip and s["addrInfo"].port == myPlayerClient.addrInfo.port then
                        removingState = i
                    end
                end
                table.remove(client_info["world_state"], removingState)
                print("client: "..json.encode(client_info["world_state"]))
                joinedRoom = false
                other_ip = nil
                print("Successfully left the room!")
                local paramNames = {"Create Room", "Join Room", "Find Room"}
                local paramArgs = {createRoom, function()
                        if other_ip then
                            joinRoom(other_ip, false)
                        else
                            parameter.text("IP", "", function(t)
                                other_ip = t
                            end)
                        end
                    end, findRoom}
                resetParameters(paramNames, paramArgs)
            elseif result == nil then
                if current_time then
                    if finding_room == false then
                        if os.time() > current_time + 5 then
                            -- If we get no response back for 5 seconds or more, we know it didn't work
                            print("Failed to connect, please try again later")
                            current_time = nil
                        end
                    end
                end
            end
        else
            local result, ip, port = client:receivefrom()
            if result and string.find(result, "server info") then
                -- Now we can attempt to join the room with the given IP address
                local tab = json.decode(string.sub(result, 12, #result))
                if finding_room == true then
                    joinRoom(tab.ip, true)
                    finding_room = false
                end
            else
                if finding_room == true then
                    if current_time and os.time() > current_time + 15 then
                        -- If we get no response back for 15 seconds or more, we know we couldn't find a room
                        print("Failed to find a room!")
                        current_time = nil
                    end
                end
            end
        end
    end 
end

function lerp(a, b, t)
    return (1 - t) * a + t * b
end

function updatePositions(state, touchDelta)
    if state["mostRecentWaypoint"] == nil then return end
    state.playerX = state["mostRecentWaypoint"].x
    state.playerY = state["mostRecentWaypoint"].y
end

function touched(touch)
    if touch.state ~= ENDED and touch.state ~= CANCELLED then
        if touch.state == BEGAN or touch.state == CHANGED then
            if joinedRoom == true then
                if myPlayerClient then
                    myPlayerClient.playerX = CurrentTouch.x
                    myPlayerClient.playerY = CurrentTouch.y
                    myPlayerClient.touchDelta = {x = touch.delta.x, y = touch.delta.y}
                    sendNumber = sendNumber + 1
                end
            end
        end
    end
end

function updatePlayers()
    -- We now update the position of our player puppets, so it looks like the players are being updated in realtime
    if server == false and joinedRoom == true then
        for i,cp in pairs(client_info["world_state"]) do
            fill(0, 255, 0)
            text("client side", cp.playerX, cp.playerY + 30)
            ellipse(cp.playerX, cp.playerY, 35)
        end
    else
        for i,sp in pairs(server_info["world_state"]) do
            fill(255, 13, 0)
            text("server side", sp.playerX, sp.playerY + 30)
            ellipse(sp.playerX, sp.playerY, 35)
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    
    -- This sets the line thickness
    strokeWidth(5)
    
    -- Do your drawing here
    updatePlayers()
    receiveData()
    if myPlayerClient and joinedRoom == true and myPlayerClient.addrInfo ~= nil then
        local playerTab = {playerName = myPlayerClient.playerName, playerX = myPlayerClient.playerX, playerY = myPlayerClient.playerY, sendNumber = sendNumber, addrInfo = myPlayerClient.addrInfo, mostRecentWaypoint = nil, touchDelta = myPlayerClient.touchDelta}
        sendInterval = sendInterval + 1
        if sendInterval >= 3 then
            client:send("client state"..json.encode(playerTab))
            sendInterval = 0
        end
    end
end
function setup()
    socket = require("socket")
    parameter.action("Create Room", createRoom)
    parameter.action("Join Room", function()
        if other_ip then
            joinRoom(other_ip, false)
        else
            parameter.text("IP", "", function(t)
                other_ip = t
            end)
        end
    end)
    parameter.action("Find Room", function()
        findRoom()
    end)
    client = socket.udp()
    client:settimeout(0)
    listener = socket.udp()
    listener:settimeout(0)
    -- We need to create a table that can hold information about our server, if you end up making one
    server_info = {players = 0, clients = {}, world_state = {}, puppet_states = {}, ids_taken = {}}
    -- We also need to create a table that can hold info that the client has
    client_info = {serverIp = "", world_state = {}}
    other_ip = nil
    current_time = nil
    current_room_count = 1
    tick = 0
    finding_room = false
    server = false
    -- Client bool properties
    joinedRoom = false
    receivedPeerInfo = false
    receivedServerIp = false
    receivedPlayerCount = false
    randomRoom = false
    myPlayerServer = nil
    myPlayerClient = nil
    sendNumber = 0
    sendInterval = 0
    playerTick = 0
end

function createRoom()
    -- We need to find your address, so we create a new UDP socket and assign it a random IP
    local ip, port = getLocalIP()
    client:setsockname("0.0.0.0", "14285")
    local ip2, port2 = client:getsockname()
    client:setoption("broadcast", true)
    your_ip = ip2
    your_port = port2
    print("Room created!")
    print("Connect to "..ip)
    server = true
end

function joinRoom(ip, finding_room)
    if server == false then
        if ip ~= "*" then
            if finding_room == false then
                print("Attempting to join the room...")
            end
            -- The port for all the servers is the same, so we only need the IP address of the server
            client:setpeername(ip, "14285")
            client:send("connection confirmation")
            current_time = os.time()
        else
            print("Address not allowed!")
        end
    else
        print("You have already opened a server!")
    end
end

function findRoom()
    if server == false then
        print("Attempting to find a room...")
        finding_room = true
        local ip, port = getLocalIP()
        local ip1, ip2 = string.match(ip, "(%d+.%d+.%d+.)(%d+)")
        local success, err = pcall(function()
            for z = 0, 255 do
                client:setpeername(ip1..z, "14285")
                client:send("requesting server info")
            end
            client:setpeername("*")
            client:setsockname("*", "14285")
        end)
        if success == false then
            -- An error occured whether that be no internet connection or message wasn't received
            print("An error occured! Please check your internet connection, and try again later.")
            return
        end
        current_time = os.time()
        randomRoom = true
    else
        print("You have already opened a server!")
    end
end

function sendMessage(msg)
    if server == true then
        for i,c in pairs(server_info["clients"]) do
            client:sendto(tostring(msg), c.ip, c.port)
        end
    end
end

function leaveRoom()
    if server == false then
        -- We need to notify the server that we are leaving, so we send a packet to them
        client:send("disconnection confirmation")
    end
end

function resetParameters(params, args)
    parameter.clear()
    for i = 1, #params do
        parameter.action(params[i], args[i])
    end
end

function getLocalIP()
    local udp = socket.udp()
    udp:setpeername("192.167.188.122", "14285")
    local ip, port = udp:getsockname()
    udp:close()
    return ip, port
end

function receiveData()
    if server == true then
        local msg, ip, port = client:receivefrom()
        if msg ~= nil then
            if msg == "connection confirmation" then
                if server_info["players"] < 32 then
                    -- We can now add 1 more to our player count
                    server_info["players"] = server_info["players"] + 1
                    -- We now need to store the clients IP and port, so we can send them messages later
                    local uid = {ip = ip, port = port}
                    if not server_info["clients"][uid] then
                        table.insert(server_info["clients"], uid)
                    end
                    -- We now create a random ID for the connecting peer so we can differentiate between players on the server
                    local playerID = math.random(10000, 99999)
                    if not server_info["ids_taken"][playerID] then
                        server_info["ids_taken"][playerID] = playerID
                    else
                        playerID = math.random(10000, 99999)
                    end
                    for i = 1, 15 do
                        client:sendto("valid confirmation"..playerID, ip, port)
                        client:sendto("peer address info"..json.encode(uid), ip, port)
                    end
                    -- We need to send the joining client our IP, so they can send messages to us later
                    local our_ip, our_port = getLocalIP()
                    for i = 1, 15 do
                        client:sendto("server ip"..our_ip, ip, port)
                    end
                    -- We can now send everyone that has joined this room a packet telling them the amount of players in the room
                    for i,c in pairs(server_info["clients"]) do
                        for i = 1, 15 do
                            client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                        end
                    end
                else
                    -- We don't have enough room for another player, so we send the peer a message telling them to disconnect
                    local reason = "Max players inside the room!"
                    client:sendto("invalid confirmation"..reason, ip, port)
                end
            elseif msg == "requesting server info" then
                -- We need to find your address, so we create a new UDP socket and assign it a random IP
                local our_ip, our_port = getLocalIP()
                -- We now know that somebody is trying to find a server, so we send a packet back to them with our server IP and port
                local uid = {ip = our_ip}
                for i = 1, 15 do
                    client:sendto("server info"..json.encode(uid), ip, port)
                end
            elseif msg and string.find(msg, "disconnection confirmation") then
                -- We know that one of the clients connected to this server wants to leave, so we send a packet back telling them that we have been notified
                client:sendto("notified of disconnection", ip, port)
                local uid = {ip = ip, port = port}
                local clientInfo
                local clientState
                for i = 1, #server_info["clients"] do
                    if server_info["clients"][i] == uid then
                        clientInfo = i
                    end
                end
                for i = 1, #server_info["world_state"] do
                    local state = server_info["world_state"][i]
                    if state["addrInfo"].ip == uid.ip and state["addrInfo"].port == uid.port then
                        clientState = i
                    end
                end
                table.remove(server_info["clients"], clientInfo)
                table.remove(server_info["world_state"], clientState)
                print("server: "..json.encode(server_info["world_state"]))
                -- We also need to deduct the player count
                server_info["players"] = server_info["players"] - 1
                -- We can now send everyone that has joined this room a packet telling them the new amount of players in the room
                for i,c in pairs(server_info["clients"]) do
                    client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                end
            elseif msg and string.find(msg, "client state") then
                -- A connected peer has sent their client state to us, so we update our world state accordingly
                local clientState = json.decode(string.sub(msg, 13, #msg))
                local hasClientState = false
                local existingState = nil
                for i,s in pairs(server_info["world_state"]) do
                    if s.playerName == clientState.playerName then
                        hasClientState = true
                        existingState = s
                        break
                    end
                end
                if hasClientState == true then
                    local uid = {ip = ip, port = port}
                    if clientState.sendNumber > existingState.sendNumber then
                        existingState.sendNumber = clientState.sendNumber
                        -- We now also add a new waypoint for our puppet peer to follow
                        local waypoint = {x = clientState.playerX, y = clientState.playerY}
                        -- We now update the most recent waypoint to the new waypoint
                        existingState["mostRecentWaypoint"] = waypoint
                        existingState["touchDelta"] = clientState.touchDelta
                        updatePositions(existingState, clientState.touchDelta)
                    end
                else
                    clientState["prevX"] = clientState.playerX
                    clientState["prevY"] = clientState.playerY
                    if not server_info["world_state"][clientState] then
                        table.insert(server_info["world_state"], clientState)
                    end
                end
            end
        end
        -- We now loop through every state and interpolate them to their most recent waypoint
        for i,state in pairs(server_info["world_state"]) do
            updatePositions(state, state.touchDelta)
        end
    else
        if finding_room == false then
            local result = client:receive()
            if result and string.find(result, "valid confirmation") then
                if joinedRoom then
                    return
                end
                -- We know that we successfully joined the room, and we can now receive messages from the server!
                if randomRoom == false then
                    print("Successfully joined the room!")
                end
                joinedRoom = true
                current_time = nil
                parameter.clear()
                -- We now create a button that allows the player to leave the room
                local paramName = {"Leave Room"}
                local paramArgs = {function() leaveRoom() end}
                resetParameters(paramName, paramArgs)
                -- We now set our player name to the ID that the server sent us
                local playerID = string.sub(result, 19, #result)
                local playerTab = {playerName = playerID, playerX = WIDTH/2, playerY = HEIGHT/2, touchDelta = nil}
                myPlayerClient = playerTab
                if not client_info["world_state"][myPlayerClient] then
                    table.insert(client_info["world_state"], myPlayerClient)
                end
                client:setoption("dontroute", true)
            elseif result and string.find(result, "peer address info") then
                if receivedPeerInfo then
                    return
                end
                receivedPeerInfo = true
                local addrInfo = json.decode(string.sub(result, 18, #result))
                if myPlayerClient then
                    myPlayerClient["addrInfo"] = addrInfo
                end
                if randomRoom == true then
                    print("Successfully joined a room at: "..myPlayerClient["addrInfo"].ip.." : "..myPlayerClient["addrInfo"].port)
                end
            elseif result and string.find(result, "server ip") then
                if receivedServerIp then
                    return
                end
                receivedServerIp = true
                -- Now that we have the servers IP address, we can message them later if we need to
                local serverIp = string.sub(result, 10, #result)
                client_info["serverIp"] = serverIp
            elseif result and string.find(result, "player count") then
                if receivedPlayerCount then
                    return
                end
                receivedPlayerCount = true
                -- We now know the current amount of players in our room, for now at least
                local count = string.sub(result, 13, #result)
                print("Player count: "..count)
            elseif result and string.find(result, "invalid confirmation") then
                -- We can now see the reason for our invalid confirmation and display it
                local reason = string.sub(result, 21, #result)
                print("Could not join the room: "..reason)
                current_time = nil
                -- We can now also remove the address and port set for our peer
                client:setpeername("*")
            elseif result == "notified of disconnection" then
                -- We know that we can now safely leave the server
                client:setpeername("*")
                local removingState
                for i,s in pairs(client_info["world_state"]) do
                    if s["addrInfo"].ip == myPlayerClient.addrInfo.ip and s["addrInfo"].port == myPlayerClient.addrInfo.port then
                        removingState = i
                    end
                end
                table.remove(client_info["world_state"], removingState)
                print("client: "..json.encode(client_info["world_state"]))
                joinedRoom = false
                other_ip = nil
                print("Successfully left the room!")
                local paramNames = {"Create Room", "Join Room", "Find Room"}
                local paramArgs = {createRoom, function()
                        if other_ip then
                            joinRoom(other_ip, false)
                        else
                            parameter.text("IP", "", function(t)
                                other_ip = t
                            end)
                        end
                    end, findRoom}
                resetParameters(paramNames, paramArgs)
            elseif result == nil then
                if current_time then
                    if finding_room == false then
                        if os.time() > current_time + 5 then
                            -- If we get no response back for 5 seconds or more, we know it didn't work
                            print("Failed to connect, please try again later")
                            current_time = nil
                        end
                    end
                end
            end
        else
            local result, ip, port = client:receivefrom()
            if result and string.find(result, "server info") then
                -- Now we can attempt to join the room with the given IP address
                local tab = json.decode(string.sub(result, 12, #result))
                if finding_room == true then
                    joinRoom(tab.ip, true)
                    finding_room = false
                end
            else
                if finding_room == true then
                    if current_time and os.time() > current_time + 15 then
                        -- If we get no response back for 15 seconds or more, we know we couldn't find a room
                        print("Failed to find a room!")
                        current_time = nil
                    end
                end
            end
        end
    end 
end

function lerp(a, b, t)
    return (1 - t) * a + t * b
end

function updatePositions(state, touchDelta)
    if state["mostRecentWaypoint"] == nil then return end
    state.playerX = state["mostRecentWaypoint"].x
    state.playerY = state["mostRecentWaypoint"].y
end

function touched(touch)
    if touch.state ~= ENDED and touch.state ~= CANCELLED then
        if touch.state == BEGAN or touch.state == CHANGED then
            if joinedRoom == true then
                if myPlayerClient then
                    myPlayerClient.playerX = CurrentTouch.x
                    myPlayerClient.playerY = CurrentTouch.y
                    myPlayerClient.touchDelta = {x = touch.delta.x, y = touch.delta.y}
                    sendNumber = sendNumber + 1
                end
            end
        end
    end
end

function updatePlayers()
    -- We now update the position of our player puppets, so it looks like the players are being updated in realtime
    if server == false and joinedRoom == true then
        for i,cp in pairs(client_info["world_state"]) do
            fill(0, 255, 0)
            text("client side", cp.playerX, cp.playerY + 30)
            ellipse(cp.playerX, cp.playerY, 35)
        end
    else
        for i,sp in pairs(server_info["world_state"]) do
            fill(255, 13, 0)
            text("server side", sp.playerX, sp.playerY + 30)
            ellipse(sp.playerX, sp.playerY, 35)
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    
    -- This sets the line thickness
    strokeWidth(5)
    
    -- Do your drawing here
    updatePlayers()
    receiveData()
    if myPlayerClient and joinedRoom == true and myPlayerClient.addrInfo ~= nil then
        local playerTab = {playerName = myPlayerClient.playerName, playerX = myPlayerClient.playerX, playerY = myPlayerClient.playerY, sendNumber = sendNumber, addrInfo = myPlayerClient.addrInfo, mostRecentWaypoint = nil, touchDelta = myPlayerClient.touchDelta}
        client:send("client state"..json.encode(playerTab))
    end
end
function setup()
    socket = require("socket")
    parameter.action("Create Room", createRoom)
    parameter.action("Join Room", function()
        if other_ip then
            joinRoom(other_ip, false)
        else
            parameter.text("IP", "", function(t)
                other_ip = t
            end)
        end
    end)
    parameter.action("Find Room", function()
        findRoom()
    end)
    client = socket.udp()
    client:settimeout(0)
    listener = socket.udp()
    listener:settimeout(0)
    -- We need to create a table that can hold information about our server, if you end up making one
    server_info = {players = 0, clients = {}, world_state = {}, puppet_states = {}, ids_taken = {}}
    -- We also need to create a table that can hold info that the client has
    client_info = {serverIp = "", world_state = {}}
    other_ip = nil
    current_time = nil
    current_room_count = 1
    tick = 0
    finding_room = false
    server = false
    -- Client bool properties
    joinedRoom = false
    receivedPeerInfo = false
    receivedServerIp = false
    receivedPlayerCount = false
    disconnectionNotified = false
    randomRoom = false
    myPlayerServer = nil
    myPlayerClient = nil
    sendNumber = 0
    sendInterval = 0
    playerTick = 0
    viewer.preferredFPS = 120
end

function createRoom()
    -- We need to find your address, so we create a new UDP socket and assign it a random IP
    local ip, port = getLocalIP()
    client:setsockname("0.0.0.0", "14285")
    local ip2, port2 = client:getsockname()
    client:setoption("broadcast", true)
    your_ip = ip2
    your_port = port2
    print("Room created!")
    print("Connect to "..ip)
    server = true
end

function joinRoom(ip, finding_room)
    if server == false then
        if ip ~= "*" then
            if finding_room == false then
                print("Attempting to join the room...")
            end
            -- The port for all the servers is the same, so we only need the IP address of the server
            client:setpeername(ip, "14285")
            client:send("connection confirmation")
            current_time = os.time()
        else
            print("Address not allowed!")
        end
    else
        print("You have already opened a server!")
    end
end

function findRoom()
    if server == false then
        print("Attempting to find a room...")
        finding_room = true
        local ip, port = getLocalIP()
        local ip1, ip2 = string.match(ip, "(%d+.%d+.%d+.)(%d+)")
        local success, err = pcall(function()
            for z = 0, 255 do
                client:setpeername(ip1..z, "14285")
                client:send("requesting server info")
            end
            client:setpeername("*")
            client:setsockname("*", "14285")
        end)
        if success == false then
            -- An error occured whether that be no internet connection or message wasn't received
            print("An error occured! Please check your internet connection, and try again later.")
            return
        end
        current_time = os.time()
        randomRoom = true
    else
        print("You have already opened a server!")
    end
end

function sendMessage(msg)
    if server == true then
        for i,c in pairs(server_info["clients"]) do
            client:sendto(tostring(msg), c.ip, c.port)
        end
    end
end

function leaveRoom()
    if server == false then
        -- We need to notify the server that we are leaving, so we send a packet to them
        for i = 1, 50 do
            client:send("disconnection confirmation")
        end
        -- We know that we can now safely leave the server
        client:setpeername("*")
        local removingState
        for i,s in pairs(client_info["world_state"]) do
            if s["addrInfo"].ip == myPlayerClient.addrInfo.ip and s["addrInfo"].port == myPlayerClient.addrInfo.port then
                removingState = i
            end
        end
        table.remove(client_info["world_state"], removingState)
        print("client: "..json.encode(client_info["world_state"]))
        joinedRoom = false
        other_ip = nil
        print("Attempting to leave the room... ")
    end
end

function resetParameters(params, args)
    parameter.clear()
    for i = 1, #params do
        parameter.action(params[i], args[i])
    end
end

function getLocalIP()
    local udp = socket.udp()
    udp:setpeername("192.167.188.122", "14285")
    local ip, port = udp:getsockname()
    udp:close()
    return ip, port
end

function receiveData()
    if server == true then
        local msg, ip, port = client:receivefrom()
        if msg ~= nil then
            if msg == "connection confirmation" then
                if server_info["players"] < 32 then
                    -- We can now add 1 more to our player count
                    server_info["players"] = server_info["players"] + 1
                    -- We now need to store the clients IP and port, so we can send them messages later
                    local uid = {ip = ip, port = port}
                    if not server_info["clients"][uid] then
                        table.insert(server_info["clients"], uid)
                    end
                    -- We now create a random ID for the connecting peer so we can differentiate between players on the server
                    local playerID = math.random(10000, 99999)
                    if not server_info["ids_taken"][playerID] then
                        server_info["ids_taken"][playerID] = playerID
                    else
                        playerID = math.random(10000, 99999)
                    end
                    for i = 1, 50 do
                        client:sendto("valid confirmation"..playerID, ip, port)
                        client:sendto("peer address info"..json.encode(uid), ip, port)
                    end
                    -- We need to send the joining client our IP, so they can send messages to us later
                    local our_ip, our_port = getLocalIP()
                    for i = 1, 50 do
                        client:sendto("server ip"..our_ip, ip, port)
                    end
                    -- We can now send everyone that has joined this room a packet telling them the amount of players in the room
                    for i,c in pairs(server_info["clients"]) do
                        for i = 1, 50 do
                            client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                        end
                    end
                else
                    -- We don't have enough room for another player, so we send the peer a message telling them to disconnect
                    local reason = "Max players inside the room!"
                    client:sendto("invalid confirmation"..reason, ip, port)
                end
            elseif msg == "requesting server info" then
                -- We need to find your address, so we create a new UDP socket and assign it a random IP
                local our_ip, our_port = getLocalIP()
                -- We now know that somebody is trying to find a server, so we send a packet back to them with our server IP and port
                local uid = {ip = our_ip}
                for i = 1, 50 do
                    client:sendto("server info"..json.encode(uid), ip, port)
                end
            elseif msg and string.find(msg, "disconnection confirmation") then
                -- We know that one of the clients connected to this server wants to leave, so we send a packet back telling them that we have been notified
                local uid = {ip = ip, port = port}
                for i = 1, 50 do
                    client:sendto("notified of disconnection", ip, port)
                end
                local clientInfo
                local clientState
                for i = 1, #server_info["clients"] do
                    if server_info["clients"][i] == uid then
                        clientInfo = i
                    end
                end
                for i = 1, #server_info["world_state"] do
                    local state = server_info["world_state"][i]
                    if state["addrInfo"].ip == uid.ip and state["addrInfo"].port == uid.port then
                        clientState = i
                    end
                end
                table.remove(server_info["clients"], clientInfo)
                table.remove(server_info["world_state"], clientState)
                print("server: "..json.encode(server_info["world_state"]))
                -- We also need to deduct the player count
                server_info["players"] = server_info["players"] - 1
                -- We can now send everyone that has joined this room a packet telling them the new amount of players in the room
                for i,c in pairs(server_info["clients"]) do
                    client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                end
            elseif msg and string.find(msg, "client state") then
                -- A connected peer has sent their client state to us, so we update our world state accordingly
                local clientState = json.decode(string.sub(msg, 13, #msg))
                local hasClientState = false
                local existingState = nil
                for i,s in pairs(server_info["world_state"]) do
                    if s.playerName == clientState.playerName then
                        hasClientState = true
                        existingState = s
                        break
                    end
                end
                if hasClientState == true then
                    local uid = {ip = ip, port = port}
                    if clientState.sendNumber > existingState.sendNumber then
                        existingState.sendNumber = clientState.sendNumber
                        -- We now also add a new waypoint for our puppet peer to follow
                        local waypoint = {x = clientState.playerX, y = clientState.playerY}
                        -- We now update the most recent waypoint to the new waypoint
                        existingState["mostRecentWaypoint"] = waypoint
                        existingState["touchDelta"] = clientState.touchDelta
                        updatePositions(existingState, clientState.touchDelta)
                    end
                else
                    clientState["prevX"] = clientState.playerX
                    clientState["prevY"] = clientState.playerY
                    if not server_info["world_state"][clientState] then
                        table.insert(server_info["world_state"], clientState)
                    end
                end
            end
        end
        -- We now loop through every state and interpolate them to their most recent waypoint
        for i,state in pairs(server_info["world_state"]) do
            updatePositions(state, state.touchDelta)
        end
    else
        if finding_room == false then
            local result = client:receive()
            if result and string.find(result, "valid confirmation") then
                if joinedRoom then
                    return
                end
                -- We know that we successfully joined the room, and we can now receive messages from the server!
                if randomRoom == false then
                    print("Successfully joined the room!")
                end
                joinedRoom = true
                current_time = nil
                parameter.clear()
                -- We now create a button that allows the player to leave the room
                local paramName = {"Leave Room"}
                local paramArgs = {function() leaveRoom() end}
                resetParameters(paramName, paramArgs)
                -- We now set our player name to the ID that the server sent us
                local playerID = string.sub(result, 19, #result)
                local playerTab = {playerName = playerID, playerX = WIDTH/2, playerY = HEIGHT/2, touchDelta = nil}
                myPlayerClient = playerTab
                if not client_info["world_state"][myPlayerClient] then
                    table.insert(client_info["world_state"], myPlayerClient)
                end
                client:setoption("dontroute", true)
            elseif result and string.find(result, "peer address info") then
                if receivedPeerInfo then
                    return
                end
                receivedPeerInfo = true
                local addrInfo = json.decode(string.sub(result, 18, #result))
                if myPlayerClient then
                    myPlayerClient["addrInfo"] = addrInfo
                end
                if randomRoom == true then
                    print("Successfully joined a room at: "..myPlayerClient["addrInfo"].ip.." : "..myPlayerClient["addrInfo"].port)
                end
            elseif result and string.find(result, "server ip") then
                if receivedServerIp then
                    return
                end
                receivedServerIp = true
                -- Now that we have the servers IP address, we can message them later if we need to
                local serverIp = string.sub(result, 10, #result)
                client_info["serverIp"] = serverIp
            elseif result and string.find(result, "player count") then
                if receivedPlayerCount then
                    return
                end
                receivedPlayerCount = true
                -- We now know the current amount of players in our room, for now at least
                local count = string.sub(result, 13, #result)
                print("Player count: "..count)
            elseif result and string.find(result, "invalid confirmation") then
                -- We can now see the reason for our invalid confirmation and display it
                local reason = string.sub(result, 21, #result)
                print("Could not join the room: "..reason)
                current_time = nil
                -- We can now also remove the address and port set for our peer
                client:setpeername("*")
            elseif result == "notified of disconnection" then
                if disconnectionNotified then
                    return
                end
                print("Successfully left the room!")
                disconnectionNotified = true
                local paramNames = {"Create Room", "Join Room", "Find Room"}
                local paramArgs = {createRoom, function()
                        if other_ip then
                            joinRoom(other_ip, false)
                        else
                            parameter.text("IP", "", function(t)
                                other_ip = t
                            end)
                        end
                    end, findRoom}
                resetParameters(paramNames, paramArgs)
            elseif result == nil then
                if current_time then
                    if finding_room == false then
                        if os.time() > current_time + 5 then
                            -- If we get no response back for 5 seconds or more, we know it didn't work
                            print("Failed to connect, please try again later")
                            current_time = nil
                        end
                    end
                end
            end
        else
            local result, ip, port = client:receivefrom()
            if result and string.find(result, "server info") then
                -- Now we can attempt to join the room with the given IP address
                local tab = json.decode(string.sub(result, 12, #result))
                if finding_room == true then
                    joinRoom(tab.ip, true)
                    finding_room = false
                end
            else
                if finding_room == true then
                    if current_time and os.time() > current_time + 15 then
                        -- If we get no response back for 15 seconds or more, we know we couldn't find a room
                        print("Failed to find a room!")
                        current_time = nil
                    end
                end
            end
        end
    end 
end

function lerp(a, b, t)
    return (1 - t) * a + t * b
end

function updatePositions(state, touchDelta)
    if state["mostRecentWaypoint"] == nil then return end
    state.playerX = state["mostRecentWaypoint"].x
    state.playerY = state["mostRecentWaypoint"].y
end

function touched(touch)
    if touch.state ~= ENDED and touch.state ~= CANCELLED then
        if touch.state == BEGAN or touch.state == CHANGED then
            if joinedRoom == true then
                if myPlayerClient then
                    myPlayerClient.playerX = CurrentTouch.x
                    myPlayerClient.playerY = CurrentTouch.y
                    myPlayerClient.touchDelta = {x = touch.delta.x, y = touch.delta.y}
                    sendNumber = sendNumber + 1
                end
            end
        end
    end
end

function updatePlayers()
    -- We now update the position of our player puppets, so it looks like the players are being updated in realtime
    if server == false then
        if joinedRoom == true then
            for i,cp in pairs(client_info["world_state"]) do
                fill(0, 255, 0)
                text(cp["playerName"], cp.playerX, cp.playerY + 30)
                ellipse(cp.playerX, cp.playerY, 35)
            end
        end
    else
        for i,sp in pairs(server_info["world_state"]) do
            fill(255, 13, 0)
            text(sp["playerName"], sp.playerX, sp.playerY + 30)
            ellipse(sp.playerX, sp.playerY, 35)
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    
    -- This sets the line thickness
    strokeWidth(5)
    
    -- Do your drawing here
    updatePlayers()
    receiveData()
    if myPlayerClient and joinedRoom == true and myPlayerClient.addrInfo ~= nil then
        local playerTab = {playerName = myPlayerClient.playerName, playerX = myPlayerClient.playerX, playerY = myPlayerClient.playerY, sendNumber = sendNumber, addrInfo = myPlayerClient.addrInfo, mostRecentWaypoint = nil, touchDelta = myPlayerClient.touchDelta}
        client:send("client state"..json.encode(playerTab))
    end
end
elseif result and string.find(result, "players update") then
                -- We can now update the client-side world state accordingly
                local newState = json.decode(string.sub(result, 15, #result))
                for i,v in pairs(client_info["world_state"]) do
                    foundState = false
                    if v.playerName == newState["playerName"] then
                        v.playerX = newState.playerX
                        v.playerY = newState.playerY
                        foundState = true
                        break
                    end
                    if not foundState then
                        table.insert(client_info["world_state"], newState)
                    end
                end
elseif result and string.find(result, "players update") then
                -- We now update whatever player specified by the server
                local state = json.decode(string.sub(result, 15, #result))
                for i,v in pairs(client_info["world_state"]) do
                    if v == state then
                        v.playerX = state.playerX
                        v.playerY = state.playerY
                    end
                end
function setup()
    socket = require("socket")
    parameter.action("Create Room", createRoom)
    parameter.action("Join Room", function()
        if other_ip then
            joinRoom(other_ip, false)
        else
            parameter.text("IP", "", function(t)
                other_ip = t
            end)
        end
    end)
    parameter.action("Find Room", function()
        findRoom()
    end)
    client = socket.udp()
    client:settimeout(0)
    host = socket.udp()
    host:settimeout(0)
    listener = socket.udp()
    listener:settimeout(0)
    -- We need to create a table that can hold information about our server, if you end up making one
    server_info = {players = 0, clients = {}, world_state = {}, puppet_states = {}, ids_taken = {}}
    -- We also need to create a table that can hold info that the client has
    client_info = {serverIp = "", world_state = {}}
    other_ip = nil
    current_time = nil
    current_room_count = 1
    tick = 0
    finding_room = false
    server = false
    -- Host properties
    hostJoined  = false
    hostName = ""
    -- Client bool properties
    joinedRoom = false
    receivedPeerInfo = false
    receivedServerIp = false
    receivedPlayerCount = false
    disconnectionNotified = false
    randomRoom = false
    -- More important properties
    myPlayerServer = nil
    myPlayerClient = nil
    sendNumber = 0
    sendInterval = 0
    playerTick = 0
    viewer.preferredFPS = 120
end

function createRoom()
    -- We need to find your address, so we create a new UDP socket and assign it a random IP
    local ip, port = getLocalIP()
    client:setsockname("0.0.0.0", "14285")
    local ip2, port2 = client:getsockname()
    client:setoption("broadcast", true)
    your_ip = ip2
    your_port = port2
    print("Room created!")
    print("Connect to "..ip)
    host:setpeername(ip, "14285")
    host:send("connection confirmation")
    server = true
end

function joinRoom(ip, finding_room)
    if server == false then
        if ip ~= "*" then
            if finding_room == false then
                print("Attempting to join the room...")
            end
            -- The port for all the servers is the same, so we only need the IP address of the server
            client:setpeername(ip, "14285")
            client:send("connection confirmation")
            current_time = os.time()
        else
            print("Address not allowed!")
        end
    else
        print("You have already opened a server!")
    end
end

function findRoom()
    if server == false then
        print("Attempting to find a room...")
        finding_room = true
        local ip, port = getLocalIP()
        local ip1, ip2 = string.match(ip, "(%d+.%d+.%d+.)(%d+)")
        local success, err = pcall(function()
            for z = 0, 255 do
                client:setpeername(ip1..z, "14285")
                client:send("requesting server info")
            end
            client:setpeername("*")
            client:setsockname("*", "14285")
        end)
        if success == false then
            -- An error occured whether that be no internet connection or message wasn't received
            print("An error occured! Please check your internet connection, and try again later.")
            return
        end
        current_time = os.time()
        randomRoom = true
    else
        print("You have already opened a server!")
    end
end

function sendMessage(msg)
    if server == true then
        for i,c in pairs(server_info["clients"]) do
            client:sendto(tostring(msg), c.ip, c.port)
        end
    end
end

function willClose()
    if server == false then
        leaveRoom()
    end
end

function leaveRoom()
    if server == false then
        -- We need to notify the server that we are leaving, so we send a packet to them
        for i = 1, 50 do
            client:send("disconnection confirmation"..myPlayerClient.playerName)
        end
        -- We know that we can now safely leave the server
        client:setpeername("*")
        local removingState
        for i,s in pairs(client_info["world_state"]) do
            if s["addrInfo"].ip == myPlayerClient.addrInfo.ip and s["addrInfo"].port == myPlayerClient.addrInfo.port then
                removingState = i
            end
        end
        table.remove(client_info["world_state"], removingState)
        print("client: "..json.encode(client_info["world_state"]))
        joinedRoom = false
        other_ip = nil
        print("Attempting to leave the room... ")
    end
end

function resetParameters(params, args)
    parameter.clear()
    for i = 1, #params do
        parameter.action(params[i], args[i])
    end
end

function getLocalIP()
    local udp = socket.udp()
    udp:setpeername("192.167.188.122", "14285")
    local ip, port = udp:getsockname()
    udp:close()
    return ip, port
end

function receiveData()
    if server == true then
        local msg, ip, port = client:receivefrom()
        if msg ~= nil then
            if msg == "connection confirmation" then
                if server_info["players"] < 32 then
                    -- We can now add 1 more to our player count
                    server_info["players"] = server_info["players"] + 1
                    if server_info["players"] == 1 then
                        -- Then we know that the host has joined and we can add them to the game state
                        print("Host connected!")
                    end
                    -- We now need to store the clients IP and port, so we can send them messages later
                    local uid = {ip = ip, port = port}
                    if not server_info["clients"][uid] then
                        table.insert(server_info["clients"], uid)
                    else
                        return
                    end
                    -- We now create a random ID for the connecting peer so we can differentiate between players on the server
                    local playerID = math.random(10000, 99999)
                    if not server_info["ids_taken"][playerID] then
                        server_info["ids_taken"][playerID] = playerID
                    else
                        playerID = math.random(10000, 99999)
                    end
                    if server_info["players"] == 1 then
                        hostName = playerID
                    end
                    for i = 1, 50 do
                        client:sendto("valid confirmation"..playerID, ip, port)
                        client:sendto("peer address info"..json.encode(uid), ip, port)
                    end
                    -- We need to send the joining client our IP, so they can send messages to us later
                    local our_ip, our_port = getLocalIP()
                    for i = 1, 50 do
                        client:sendto("server ip"..our_ip, ip, port)
                    end
                    -- We can now send everyone that has joined this room a packet telling them the amount of players in the room
                    for i,c in pairs(server_info["clients"]) do
                        for i = 1, 50 do
                            client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                        end
                    end
                    for i,c in pairs(server_info["world_state"]) do
                        for i = 1, 50 do
                            client:sendto("get player"..json.encode(c), ip, port)
                        end
                    end
                else
                    -- We don't have enough room for another player, so we send the peer a message telling them to disconnect
                    local reason = "Max players inside the room!"
                    client:sendto("invalid confirmation"..reason, ip, port)
                end
            elseif msg == "requesting server info" then
                -- We need to find your address, so we create a new UDP socket and assign it a random IP
                local our_ip, our_port = getLocalIP()
                -- We now know that somebody is trying to find a server, so we send a packet back to them with our server IP and port
                local uid = {ip = our_ip}
                for i = 1, 50 do
                    client:sendto("server info"..json.encode(uid), ip, port)
                end
            elseif msg and string.find(msg, "disconnection confirmation") then
                -- We know that one of the clients connected to this server wants to leave, so we send a packet back telling them that we have been notified
                local uid = {ip = ip, port = port}
                for i = 1, 50 do
                    client:sendto("notified of disconnection", ip, port)
                end
                for k,c in pairs(server_info["clients"]) do
                    if c == uid then
                        table.remove(server_info["clients"], k)
                    end
                end
                for i,v in pairs(server_info["world_state"]) do
                    if v.playerName == string.sub(msg, 27, #msg) then
                        table.remove(server_info["world_state"], i)
                    end
                end
                print("Server world state: "..json.encode(server_info["world_state"]))
                -- We also need to deduct the player count
                server_info["players"] = #server_info["clients"]
                -- We can now send everyone that has joined this room a packet telling them the new amount of players in the room
                for i,c in pairs(server_info["clients"]) do
                    client:sendto("player count"..tostring(server_info["players"]), c.ip, c.port)
                end
            elseif msg and string.find(msg, "client state") then
                -- A connected peer has sent their client state to us, so we update our world state accordingly
                local clientState = json.decode(string.sub(msg, 13, #msg))
                local hasClientState = false
                local existingState = nil
                for i,s in pairs(server_info["world_state"]) do
                    if s.playerName == clientState.playerName then
                        hasClientState = true
                        existingState = s
                        break
                    end
                end
                if hasClientState == true then
                    local uid = {ip = ip, port = port}
                    if clientState.sendNumber > existingState.sendNumber then
                        existingState.sendNumber = clientState.sendNumber
                        -- We now also add a new waypoint for our puppet peer to follow
                        local waypoint = {x = clientState.playerX, y = clientState.playerY}
                        -- We now update the most recent waypoint to the new waypoint
                        existingState["mostRecentWaypoint"] = waypoint
                        existingState["touchDelta"] = clientState.touchDelta
                        updatePositions(existingState, clientState.touchDelta)
                    end
                else
                    clientState["prevX"] = clientState.playerX
                    clientState["prevY"] = clientState.playerY
                    if not server_info["world_state"][clientState] then
                        table.insert(server_info["world_state"], clientState)
                    end
                end
                for i,v in pairs(server_info["world_state"]) do
                    for i,c in pairs(server_info["clients"]) do
                        client:sendto("players update"..json.encode(v), c.ip, c.port)
                    end
                end
            end
        end
        -- We now loop through every state and interpolate them to their most recent waypoint
        for i,state in pairs(server_info["world_state"]) do
            updatePositions(state, state.touchDelta)
        end
        -- We also attempt to receive any data sent from the server to the host
        local hostMsg = host:receive()
        if hostMsg and string.find(hostMsg, "valid confirmation") then
            if hostJoined then
                return
            end
            hostJoined = true
            local playerTab = {playerName = hostName, playerX = WIDTH/2, playerY = HEIGHT/2, touchDelta = nil}
            myPlayerServer = playerTab
            if not server_info["world_state"][myPlayerServer] then
                table.insert(server_info["world_state"], myPlayerServer)
            end
        end
    else
        if finding_room == false then
            local result = client:receive()
            if result and string.find(result, "valid confirmation") then
                if joinedRoom then
                    return
                end
                -- We know that we successfully joined the room, and we can now receive messages from the server!
                if randomRoom == false then
                    print("Successfully joined the room!")
                end
                joinedRoom = true
                current_time = nil
                parameter.clear()
                -- We now create a button that allows the player to leave the room
                local paramName = {"Leave Room"}
                local paramArgs = {function() leaveRoom() end}
                resetParameters(paramName, paramArgs)
                -- We now set our player name to the ID that the server sent us
                local playerID = string.sub(result, 19, #result)
                local playerTab = {playerName = playerID, playerX = WIDTH/2, playerY = HEIGHT/2, touchDelta = nil}
                myPlayerClient = playerTab
                if not client_info["world_state"][myPlayerClient] then
                    table.insert(client_info["world_state"], myPlayerClient)
                end
                client:setoption("dontroute", true)
            elseif result and string.find(result, "peer address info") then
                if receivedPeerInfo then
                    return
                end
                receivedPeerInfo = true
                local addrInfo = json.decode(string.sub(result, 18, #result))
                if myPlayerClient then
                    myPlayerClient["addrInfo"] = addrInfo
                end
                if randomRoom == true then
                    print("Successfully joined a room at: "..myPlayerClient["addrInfo"].ip.." : "..myPlayerClient["addrInfo"].port)
                end
            elseif result and string.find(result, "server ip") then
                if receivedServerIp then
                    return
                end
                receivedServerIp = true
                -- Now that we have the servers IP address, we can message them later if we need to
                local serverIp = string.sub(result, 10, #result)
                client_info["serverIp"] = serverIp
            elseif result and string.find(result, "player count") then
                if receivedPlayerCount then
                    return
                end
                receivedPlayerCount = true
                -- We now know the current amount of players in our room, for now at least
                local count = string.sub(result, 13, #result)
                print("Player count: "..count)
            elseif result and string.find(result, "invalid confirmation") then
                -- We can now see the reason for our invalid confirmation and display it
                local reason = string.sub(result, 21, #result)
                print("Could not join the room: "..reason)
                current_time = nil
                -- We can now also remove the address and port set for our peer
                client:setpeername("*")
            elseif result and string.find(result, "get player") then
                -- We can now create a new player according to the server
                local newState = json.decode(string.sub(result, 15, #result))
                if client_info["world_state"][newState] == nil then
                    table.insert(client_info["world_state"], newState)
                else
                    return
                end
            elseif result and string.find(result, "players update") then
                -- We now update whatever player specified by the server
                local state = json.decode(string.sub(result, 15, #result))
                for i,v in pairs(client_info["world_state"]) do
                    if v == state then
                        v.playerX = state.playerX
                        v.playerY = state.playerY
                    end
                end
            elseif result == "notified of disconnection" then
                if disconnectionNotified then
                    return
                end
                print("Successfully left the room!")
                disconnectionNotified = true
                local paramNames = {"Create Room", "Join Room", "Find Room"}
                local paramArgs = {createRoom, function()
                        if other_ip then
                            joinRoom(other_ip, false)
                        else
                            parameter.text("IP", "", function(t)
                                other_ip = t
                            end)
                        end
                    end, findRoom}
                resetParameters(paramNames, paramArgs)
            elseif result == nil then
                if current_time then
                    if finding_room == false then
                        if os.time() > current_time + 5 then
                            -- If we get no response back for 5 seconds or more, we know it didn't work
                            print("Failed to connect, please try again later")
                            current_time = nil
                        end
                    end
                end
            end
        else
            local result, ip, port = client:receivefrom()
            if result and string.find(result, "server info") then
                -- Now we can attempt to join the room with the given IP address
                local tab = json.decode(string.sub(result, 12, #result))
                if finding_room == true then
                    joinRoom(tab.ip, true)
                    finding_room = false
                end
            else
                if finding_room == true then
                    if current_time and os.time() > current_time + 15 then
                        -- If we get no response back for 15 seconds or more, we know we couldn't find a room
                        print("Failed to find a room!")
                        current_time = nil
                    end
                end
            end
        end
    end 
end

function lerp(a, b, t)
    return (1 - t) * a + t * b
end

function updatePositions(state, touchDelta)
    if state["mostRecentWaypoint"] == nil then return end
    state.playerX = state["mostRecentWaypoint"].x
    state.playerY = state["mostRecentWaypoint"].y
end

function touched(touch)
    if touch.state ~= ENDED and touch.state ~= CANCELLED then
        if touch.state == BEGAN or touch.state == CHANGED then
            if joinedRoom == true then
                if myPlayerClient then
                    myPlayerClient.playerX = CurrentTouch.x
                    myPlayerClient.playerY = CurrentTouch.y
                    myPlayerClient.touchDelta = {x = touch.delta.x, y = touch.delta.y}
                    sendNumber = sendNumber + 1
                end
            end
        end
    end
end

function updatePlayers()
    -- We now update the position of our player puppets, so it looks like the players are being updated in realtime
    if server == false then
        if joinedRoom == true then
            for i,cp in pairs(client_info["world_state"]) do
                fill(0, 255, 0)
                text(cp["playerName"], cp.playerX, cp.playerY + 30)
                ellipse(cp.playerX, cp.playerY, 35)
            end
        end
    else
        for i,sp in pairs(server_info["world_state"]) do
            fill(255, 13, 0)
            text(sp["playerName"], sp.playerX, sp.playerY + 30)
            ellipse(sp.playerX, sp.playerY, 35)
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    
    -- This sets the line thickness
    strokeWidth(5)
    
    -- Do your drawing here
    updatePlayers()
    receiveData()
    if myPlayerClient and joinedRoom == true and myPlayerClient.addrInfo ~= nil then
        local playerTab = {playerName = myPlayerClient.playerName, playerX = myPlayerClient.playerX, playerY = myPlayerClient.playerY, sendNumber = sendNumber, addrInfo = myPlayerClient.addrInfo, mostRecentWaypoint = nil, touchDelta = myPlayerClient.touchDelta}
        client:send("client state"..json.encode(playerTab))
    end
    if myPlayerServer and hostJoined == true then
        myPlayerServer["playerX"] = CurrentTouch.x
        myPlayerServer["playerY"] = CurrentTouch.y
    end
end

@Creator27 - a lot of code with little explanation. Why so many separate bits of code? What are the differences and what would you like us to test?