Implementing WiFi multiplayer on Cordova-based games - Part 3

Concluding our series about local multiplayer via WiFi, we are going to address the Android bug that prevents local hostname resolution, and we will see the aspects to consider when using directly IP addresses instead of host names.

At first, using the device IP address instead of its host name may seem like no big deal, but Apple has a good document on why in fact it's a really bad pratice.

In our specific case, there are 2 important aspects to take into consideration:

  • The device has more than 1 network interface, and therefore more than 1 IP address (for example: 1 IP address for the WiFi network, and 1 IP address for the Cellular network)
  • The device can be compatible with both IPv4 and IPv6, and therefore have both IPv4 and IPv6 addresses, but the server could be listening only on IPv4 or only on IPv6

Therefore, we cannot use directly the IP addresses returned by the zeroconf.watch function, because we don't know which IP address to use.

The solution here is that on the server side we first have to know whether the device is listgening on IPv4 or IPv6, and then advertise the IPv4 or IPv6 address of the WiFi interface on the Zeroconf TXT record, so the server setup will become something like:

var
    SERVICE_TYPE = '_my-service._tcp.',
    HOST_NAME = 'my host'
;

// Specify 0 as the port number, so that a random free port is used
wsserver.start(0, {
    onStart: function (addr, port) {
        var
            interface,
            ip_addresses
        ;

        if (cordova.platformId === 'android') {
            // On Android the WiFi interface name is wlan0
            interface = 'wlan0';
        } else if (cordova.platformId === 'ios') {
            // On iOS the WiFi interface name is en0
            interface = 'en0';
        }

        // Check whether we are listening on IPv4 or IPv6
        if (/^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}$/.test(addr)) {
            ip_addresses = 'ipv4Addresses';
        } else {
            ip_addresses = 'ipv6Addresses';
        }

        // Call getInterfaces in order to know the IP addresses of each network interface
        wsserver.getInterfaces(function(result) {
            zeroconf.register(SERVICE_TYPE, 'local.', HOST_NAME, port, {
                // Publish the correct IP address on the TXT record
                server_ip: result[interface][ip_addresses][0]
            }, function (result) {
                // Here we have successfully advertised the service
            });
        });

    },
    // Other server stuff...
});

For the client side, remember that we are working around a bug that is specific to Android, so let's treat only the Android platform as a special case, like this:

var connection;

zeroconf.watch(SERVICE_TYPE, 'local.', function (result) {
    var
        service = result.service,
        hostname,
        url
    ;

    if (result.action === 'added') {

        if (cordova.platformId === 'android') {
            // On Android use the IP address published on the TXT record
            hostname = service.txtRecord.server_ip;
        } else {
            // Remove any trailing dots from the hostname
            hostname = service.hostname.replace(/[.]+$/g, '');
        }
        url = ['ws://', hostname, ':', service.port, '/'].join('');
        connection = new WebSocket(url);
        connection.onopen = function() {
            // Now the client is connected
        }
    }
});

And at last, now we have a multi-platform connection that works on both iOS and Android.

We are going to implement WiFi local multiplayer on the next version of Dawn Of Ultra Pong, so stay tuned.

Comments !