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?