Websocket server for remote connections to touch table games

I am considering creating a system for touch table games so that they can be played both by people sitting around the table and by individuals at their own computer.

This post will talk about what I’d like to do and show the steps that I’ve taken to setup a websocket server on my domain and to connect to it from Unity.

Overview

I am planning to use our event save system and have each instance of the game send locally created events to a web server and also receive updates from the web server when other clients add events. So each client will be running the game, but will receive events from remote clients that will be executed as though they were created locally.

There are pros and cons to this approach. The big advantages are that it ties into our existing event system which may make it possible to add this capability to existing games without much rework. It should be fairly fault tolerant, allowing clients to connect/disconnect from a game easily. It also allows games to be played asynchronously, with players taking their turns at different times – playing whenever they are free.

The idea of a web based save file could be implemented lots of ways. I’ve decided to run a websocket server on my domain backed by an sqlite database for storing the games, client data and game events. Websockets will allow me to have minimum lag for distributing moves without writing my own network code. The biggest disadvantage of websockets is that, to run a websocket server, you need to be able to open ports on an internet connected machine. Most home routers have firewalls and NAT tables which make setting up connections between two peers difficult. And most web hosting companies will not allow users to open ports. Fortunately I have access to a machine where I can run a websocket server.

Setting up the server

I’m going to use a product called websocketd as my websocket server. My server is running Ubuntu 16 and I was able to run their pre-built Linux executable. websocketd works like old CGI scripts did. You write an application that reads from stdin and writes to stdout. websocketd converts incoming socket messages into input on stdin and takes your output on stdout and writes it to the socket. It can handle secure sockets.

Here is the code for my very simple test server:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>

#include <sqlite3.h>

using namespace std;

// This is the callback function to display the select data in the table
static int callback(void *NotUsed, int argc, char **argv, char **szColName)
{
  for(int i = 0; i < argc; i++)
    std::cout << szColName[i] << "=" << argv[i] << ", ";

  std::cout << "\n";
  return 0;
}

sqlite3 *db;
static void in()
{
  for (string input; getline(cin, input);)
  {
    char *szErrMsg = 0;
    int rc = sqlite3_exec(db, input.c_str(), callback, 0, &szErrMsg);
    if(rc != SQLITE_OK)
    {
      std::cout << "SQL Error: " << szErrMsg << std::endl;
      sqlite3_free(szErrMsg);
    }
  }
}

int main()
{
  setbuf(stdout, NULL);
  setbuf(stdin, NULL);

  // open database
  int rc = sqlite3_open("Sqlite_Test.db", &db);
  if(rc)
  {
    std::cout << "Can't open database\n";
    return 0;
  } else {
    std::cout << "Open database successfully\n";
  }

  thread inThread(in);
  inThread.join();

  // close database
  if(db)
    sqlite3_close(db);

  return 0;
}

Compile and run:

g++ -std=c++11 -L/usr/include -pthread sqliteTest.cpp -lsqlite3
sudo ./websocketd --port=8080 --ssl --sslcert=/etc/letsencrypt/live/www.billandchad.com/cert.pem --sslkey=/etc/letsencrypt/live/www.billandchad.com/privkey.pem ./a.out

websocketd listens for websocket connections on port 8080 and spawns a new instance of my executable to handle each connection. Unfortunately this means that I need some way for one client to notify the others that it has added an event that needs to be pushed out. I plan to use SQLite’s notification callback system.

A test client

To connect to the websocket server in Unity, I’m using a library called websocket-sharp. I built the dll from the project files, but the provided dll should work fine. I dropped the dll into the plugin directory and created a C# behavior that looks like this:

using UnityEngine;
using UnityEngine.UI;

public class TestWebserver : MonoBehaviour {

  public InputField CommandField;
  WebSocketSharp.WebSocket _webServer = null;
	// Use this for initialization
	public void Start() {
    Debug.Log("Starting webserver");
    _webServer = new WebSocketSharp.WebSocket("wss://billandchad.com:8080");
    _webServer.OnMessage += (sender, e) =>
      Debug.Log(sender.ToString() + ": " + e.Data);
    _webServer.OnError += (sender, e) =>
      Debug.LogError(sender.ToString() + ": " + e.Message);
    _webServer.OnClose += (sender, e) =>
      Debug.Log(sender.ToString() + " CLOSED="+e.Code+" "+e.Reason+" "+e.WasClean);
    _webServer.OnOpen += (sender, e) =>
      Debug.Log(sender.ToString() + " OPENED");
    _webServer.ConnectAsync();
	}
  public void Send()
  {
    if (_webServer != null)
    {
      Debug.Log("Sending request: "+CommandField.text);
      _webServer.SendAsync(CommandField.text, (q) => Debug.Log("Data sent: " +q));
    }
  }
  public void CloseWSClient()
  {
    if (_webServer != null)
    {
      Debug.Log("Stoping webserver");
      _webServer.Close();
      _webServer = null;
    }
  }

  void OnDestroy()
  {
    if (_webServer != null)
    {
      Debug.Log("Stoping webserver");
      _webServer.Close();
    }
  }
}

I hooked this up with a couple buttons and an input field and I can send SQL commands to the database sitting on my server.

Next steps

With this proof of concept working, I now need to think through how the touch game clients can use this capability. I need to come up with a way to start, list and join games.

There will also need to be a way to handle timing conflicts between clients. It may end up being too difficult or laggy to use this system for a game where multiple clients can act at the same time and the order of the actions matters. If I stick to turn based games, the only timing issue will be with undo and it is OK if undo takes a second to coordinate between the clients.

There also needs to be a way to disconnect the displayed position of all the players from the modeled positions so that each person can have their position displayed right side up.

Leave a Reply