Colyseus - Multiplayer Game Client & Server

Ah, yeah, my bad, that’s the standard pattern for async connect (initiate connect, poll with select() for success or failure, yield’ing along the way). Oops.

So, I temporarily commented out the settimeout(0) call, and changed that area to the following:

        self.sock = socket.tcp()
        -- self.sock:settimeout(0)
        local status, err = self.sock:connect(host,port)
        print("sock:connect", status, err)

and the output was:

DEBUG:SCRIPT: sock:connect	nil	host or service not provided, or not known

I don’t know what version of luasocket is bundled in defold-luasocket but this commit is pretty telling:

Although, I commented out the zero-timeout, so in theory it should try all the addresses, but maybe that was implemented in a commit after the version that’s bundled in defold-luasocket? The latest luasocket docs and source (c.f. src/inet.c) implement a socket.dns module, but I can’t seem to find that in defold-luasocket.

As a quick test, I tried hard-coding my IPv6 address in as host to see if this would successfully connect if I fed it the IPv6 address and didn’t depend on DNS/resolver order… and I managed to get it to work if I changed socket.tcp() to socket.tcp6() … but I’m obviously concerned that won’t work if I’m on an IPv4-only network, because I seem to recall seeing that the Lua socket.tcp6() was implemented to bind IPv6-only mode… otherwise we could just use socket.tcp6() everywhere and let it gracefully degrade to IPv4 when necessary…

1 Like

Just another data point:

So, using socket.tcp6() and the FQDN worked on my T-Mobile LTE connection, as well as the hard-coded IP (after I modified websocket.tools.parse_url() to understand proto://[ipv6]:port/ URLs).

Turning my Wi-Fi back on – my home network is IPv4-only – fails as expected because Lua’s socket.tcp6() sets family to AF_INET6 (at least per upstream LuaSocket’s src/tcp.c).

Although socket.tcp() is supposed to use AF_UNSPEC … that’s what the upstream LuaSocket does, and since Defold is closed source, it’s hard to know what version of LuaSocket they embedded and whether it has the latest improvements from the upstream… socket._VERSION from Defold says LuaSocket 3.0-rc1 but that version string was set back on Jun 11, 2013, but the “New agnostic IPv4 IPv6 functions.” commit was on Aug 22, 2015 …

What chance would there be to move the partial LuaSocket implementation completely out of Defold core and into the defold-luasocket extension, including the native code? Would that result in symbol collisions because of the builtin one?

luasocket is bundled into Defold. It’s version https://github.com/diegonehab/luasocket/releases/tag/v3.0-rc1. What defold-luasocket does is to add the missing .lua files (socket.lua is in builtins, but the rest are missing) and also provides mime.core as a native extension. More info here: https://github.com/britzl/defold-luasocket

v3.0-rc1
tagged on Jun 14, 2013 · 110 commits to master since this tag

So, yeah, all the many improvements to upstream LuaSocket haven’t made it to Defold. It would be nice if Defold removed all the socket stuff from builtin and just let the community maintain defold-luasocket and keep it up-to-date with upstream.

2 Likes

This is likely to happen in the long-run but for now we need to live with the current system. I haven’t had a chance to test this properly, but I believe it might be possible to use socket.dns.getaddrinfo() to get a list of IPs to try and connect to:

self.sock_connect = function(self, host, port)
	assert(corunning(), "You must call the connect function from a coroutine")
	local addrinfo = socket.dns.getaddrinfo(host)
	for _,info in pairs(addrinfo) do
		if info.family == "inet6" then
			self.sock = socket.tcp6()
		else
			self.sock = socket.tcp()
		end
		self.sock:settimeout(0)
		self.sock:connect(host,port)

		local sendt = { self.sock }
		-- start polling for successful connection or error
		while true do
			local receive_ready, send_ready, err = socket.select(nil, sendt, 0)
			if err == "timeout" then
				coroutine.yield()
			elseif err then
				break
			elseif #send_ready == 1 then
				return true
			end
		end
	end
	self.sock = nil
	return nil, "Unable to connect"
end
1 Like

I’ve verified this against an IPv6 only network at work and the code can connect to it properly. I’ve released a new version of defold-websocket that includes this change.

7 Likes

I would like to share this here too. Because It is possible that other dependencies(like defold-websocket) or Defold’s cache may cause this problem:

I am just using default example. When server is not available, onError message “mostly” doesn’t triggered. Sometimes it works when I build and then rebuild the project, but I couldn’t find any clue or pattern about this. Its possible that other dependencies may cause this problem or maybe built in cache of Defold.

This is the client output when there isn’t any server and onError doesn’t triggered :

{
  roomStates = {
  }
  roomsAvailableRequests = {
  }
  requestId = 0,
  on = function: 0x0a1b70e0,
  connection = {
    emit = function: 0x0aaf9d60,
    _enqueuedCalls = {
    }
    on = function: 0x0aaf9d80,
    is_html5 = false,
    off = function: 0x0aaf9f40,
    _on = {
      close = {
        1 = function: 0x0aafa1f0,
      }
      error = {
        1 = function: 0x0aafa140,
      }
      message = {
        1 = function: 0x0aafa180,
      }
      open = {
        1 = function: 0x0aafa0a0,
      }
    }
    state = CLOSED,
    listeners = function: 0x0aaf9d40,
    endpoint = ws://localhost:8080/?colyseusid=xmjmMr3lo,
  }
  rooms = {
  }
  hostname = ws://localhost:8080/,
  connectingRooms = {
  }
  _on = {
  }
  emit = function: 0x0a1b7210,
  listeners = function: 0x0a1b71f0,
  off = function: 0x0a1b7100,
  id = xmjmMr3lo,
}

There isn’t any server and onError triggered :

{
  roomStates = {
  }
  roomsAvailableRequests = {
  }
  requestId = 0,
  on = function: 0x12f4f0e0,
  connection = {
    ws = {
      step = function: 0x13892d10,
      sock_close = function: 0x13892860,
      on_close = function: 0x13892880,
      send = function: 0x13892c30,
      on_disconnected = function: 0x13892d70,
      sock = tcp{client}: 0x13893630,
      sock_send = function: 0x138928a0,
      close = function: 0x13892cc0,
      on_connected = function: 0x13892d50,
      on_message = function: 0x13892d30,
      connect = function: 0x138929f0,
      state = CLOSED,
      sock_connect = function: 0x13892830,
      sock_receive = function: 0x138928c0,
      receive = function: 0x13892c80,
    }
    emit = function: 0x13891d60,
    _enqueuedCalls = {
    }
    on = function: 0x13891d80,
    is_html5 = false,
    off = function: 0x13891f40,
    _on = {
      close = {
        1 = function: 0x138921f0,
      }
      error = {
        1 = function: 0x13892140,
      }
      message = {
        1 = function: 0x13892180,
      }
      open = {
        1 = function: 0x138920a0,
      }
    }
    state = CONNECTING,
    listeners = function: 0x13891d40,
    endpoint = ws://localhost:8080/?colyseusid=xmjmMr3lo,
  }
  rooms = {
  }
  hostname = ws://localhost:8080/,
  connectingRooms = {
  }
  _on = {
  }
  emit = function: 0x12f4f210,
  listeners = function: 0x12f4f1f0,
  off = function: 0x12f4f100,
  id = xmjmMr3lo,
}

I just test the defold-websocket and I can confirm that it works as expected.

ws:on_connected() returns an error with ‘closed’.

Also ws:on_connected returns ‘closed’ on colyseus (connection.lua)

self.ws:on_connected(function(ok, err)
    self.state = self.ws.state
    
    if err then
      pprint(err) -- <- returns closed
      self:emit('error', err)
      self:close()

Ok. Does this mean that self:emit(“error”, err) doesn’t work or?

Yes, looks like emit is not working as expected. Sometimes it work sometimes not.

Ok, makes sense than. Have you opened a ticket in the Colyseus repo?

Yes, in the colyseus-defold repo #

1 Like

Fixed: https://github.com/gamestdio/colyseus-defold/issues/6#issuecomment-427810381

2 Likes

Hello, I’m trying to make a game with syncronous multiplayer with Colyseus and Defold and I have perfomance issues.
At first I was using state sync and messages from server to sync clients. State sync was working on 30 FPS and whole game has around 10 FPS on HTML5, Android, Windows and Mac versions because of

local new_state = msgpack.unpack( utils.byte_array_to_string(self._previousState) )

in room.lua Room:patch.

Then I turned off room patch by copying library to my project and commenting room.lua:87

self:patch(message[2])

and sending state info by message from server to clients every frame at 30 FPS. One message contains array of 6-10 elements like this

2 = {
    segmentRadius = 32,
    skin = cat,
    id = 2,
    segments = {
      1 = {
        y = -316,
        x = -85,
      }
      2 = {
        y = -308,
        x = -93,
      }
      3 = {
        y = -303,
        x = -82,
      }
      4 = {
        y = -295,
        x = -90,
      }
      5 = {
        y = -290,
        x = -79,
      }
      6 = {
        y = -285,
        x = -68,
      }
      7 = {
        y = -277,
        x = -76,
      }
      8 = {
        y = -272,
        x = -65,
      }
      9 = {
        y = -264,
        x = -73,
      }
      10 = {
        y = -259,
        x = -62,
      }
    }
    name = Jessica Jones,
    isolationTimer = 23,
  }

On Windows, Mac and Android it works perfect, but on HTML5 game works at stable 30 FPS around 3-4 seconds and after that Colyseus client freezes and receives no messages from server or client have no resources to decode them: print in room.lua:38 in

 self.connection:on("message", function(message)

works only 3-4 seconds.

Where can I tweak or optimize my system to solve this perfomance issue?

1 Like

Is the game still running (ie animations playing etc) or is the whole app frozen?

2 Likes

game is still running without FPS problems

Oh, well, not sure why then. Perhaps @endel has some ideas on how to best debug connectivity issues?

Btw, @nik.dreml have you created an issue on the Colyseus Defold GitHub page?

2 Likes

No, I’ve created only topic on Colyseus’s forum, I thought it can be my mistake or misunderstanding, not Colyseus’s issue.
Now I’m going to create a issue on the Colyseus Defold GitHub page :slight_smile:

1 Like

@endel can you advise something?