Perita’s Blog

← Return to blog index

Annunciation of the Server

Flick Karts is a multiplayer game at heart; it is actually harder to play it by yourself than with others. And, although we plan on making the game playable with strangers on the Internet using the web client, our primary aim is to enable playing with Android and PC devices connected to the same LAN, without relaying on any external server whatsoever. This means that one player will act as the server and everyone else will connect to it.

Having the server embedded in the same executable as the game is not that different from building a dedicated server, actually. In fact, some aspects are easier because, with a local server, there are no scalability issues: There is only a single game running at a time and that is it; no need for multiple lobbies or multiplexing the packages received to their corresponding race instance.

Of course, nothing is perfect and a local server brings its own set of problems. For starters, you can not use a well-known port because, even if it was registered to IANA, there is no guarantee that it will be available and, therefore, we have to cope with port numbers arbitrarily chosen by the operating system. In the same vein, there is no way to known in advance the address clients need to connect to. With publicly-available dedicated servers we can register a domain name and use it from the client to connect to the correct address, even when it changes after the game is released. With local servers, of course, it is not possible to assign a name, nevermind the same name, to each and every machine that would act as host.

In short, clients need to autodetect any running server in the same network.

This automatic detection of services offered by a server running on a device is called service discovery and, as often happens in computing, there a quite a number of protocols to solve this problem. And most of them are somewhat complex, even when they have the term “simple” in their name. I think that is just the consequence of having to solve this problem for a myriad of different servers and clients implementations. Since this is not our case — there is only one server and one client implementation — we can lower the complexity of our solution.

In fact, we went in the opposite direction from all these protocols and, instead of the client searching for devices that run a particular service, it is the server that, periodically, announces itself to the network. The rationale is that when a player begins hosting a new game, clients will not take long to try to connect to it. Compare this to a network printer, for example, that most of the time waits idle for clients to discover its service and print once in a while.

The idea is, then, that the server broadcasts every five seconds or so a UDP packet with the name of the player that is hosting the game (e.g, “John”) and the TCP port the game is listening for new connections from clients.

@Override
public void run() {
    DatagramSocket socket = null;
    try {
        InetAddress address = InetAddress.getByName("255.255.255.255");
        socket = new DatagramSocket();
        socket.setBroadcast(true);
        String data = port + " " + name;
        DatagramPacket packet = new DatagramPacket(
            data.getBytes(), data.getBytes().length,
            address, LobbyDiscoverer.PORT
	);
        while (!isInterrupted()) {
            socket.send(packet);
            Thread.sleep(5000);
        }
    } catch (Exception e) {
        // Handle errors
    }
    if (socket != null) socket.close();
}
Code the server uses to announce itself to the network.

The data the server sends does not include the IP address because clients can get it from the packet they receive.

@Override
public void run() {
    DatagramSocket socket = null;
    try {
        socket  = new DatagramSocket(PORT);
        byte[] buffer = new byte[128];
        while (!isInterrupted()) {
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            socket.receive(packet);
            String address = packet.getAddress().getHostAddress();
            String data = new String(packet.getData());
	    // Handle data
        }
    } catch (IOException e) {
        // Handle errors
    }
    if (socket != null) socket.close();
}
Code clients use to receive server announcements.

Unfortunately, none of this can work with HTML clients: Not only it is not possible to listen for connections with websockets, it can only use TCP and, therefore, require a known address to connect to. In order to make UDP connections within the browser, we would have to use WebRTC, but then we would still need some kind of signaling server to connect to peers.

We are going to use a signaling server for these clients, one that requires an Internet connection, but we believe that anyone connecting to our website to play is guaranteed to be connected to the Internet, so maybe it is not such a unreasonable requirement, in this case.