LAN party manager

Context

From when I was young I organised LAN parties for my friends. The main issue when organizing a fun LAN party is an organisational one: who wants to play what game, who is on what team, and how did everyone do compared across all games played.

Over the years I have made several tools for supporting the LAN parties I organised. This time I wanted to make something that was technical superior to what is already out there. So this is what it had to be:

  • Blazing fast networking
  • Maximal automatic discovery (clients, games, shares, match requests, match results)
  • Easy product to use (little to no constraint on system resources while running in the background, easy transferable executable, nice non-technical UI)

It is loosely based on an earlier low-level fileshare program I worked on.

Product

This is what the product looks like today:

  • Very lean background desktop application with task-bar presence
  • Local webserver for displaying the UI
    • Angular 2 website with a responsive layout
    • Automatically starting/stopping based on access to a specific local URL
  • Automatic network discovery
    • UDP and TCP broadcast, handshake and unique client identification (without login)
    • Automatic distribution of game/tool info and the clients that have (parts) of it available
    • Automatic downloading/uploading/resizing of the pictures associated to the game/tool info
    • Automatic distribution of ‘knowledge pieces’ among clients (like a transaction log). Parts of knowledge are: votes on games, match results.

My part

There are a lot of moving parts in this projects.
So this are the major points I want you to know:

  • I use an on-the-fly created Nancy instance to create a webserver on the background to serve the UI. This instance is a sub-container of my main Autofac instance.
  • I build a programmable TCP/UDP protocol to eliminate networking issues. For me it is now as easy as:
///<summary>
/// Initialisation triggered in StartBackgroundWorkers() before starting any task.
/// </summary>protected override void InitialisationBeforeStartingOnWorkitems()
{
    // template message streams and callbacks
    var instance1 = _connectionManager
        .StartRegisteringMessageBehaviour((int)MessageStreamType.MakeKnownOnTheNetwork, ConnectionError, ConvertTransportToAdvancedSession)
        .SendUnreliable(MessageTypeIdentifier.Discovery, DiscoveryMessageStream);
    var template1 = _connectionManager
        .StartRegisteringMessageBehaviour((int)MessageStreamType.AskForClientIdentity, ConnectionError, ConvertTransportToAdvancedSession)
        .Send(0, MessageTypeIdentifier.IdentityRequest, ClientIdentityMessageStream)
        .Receive(1, MessageTypeIdentifier.IdentityResponse, ClientIdentityMessageStream)
        .Close();
    var template2 = _connectionManager
        .StartRegisteringMessageBehaviour((int)MessageStreamType.IsAlive, ConnectionError, ConvertTransportToAdvancedSession)
        .Send(0, MessageTypeIdentifier.IsAliveRequest, IsAliveMessageStream)
        .Receive(1, MessageTypeIdentifier.IsAliveResponse, IsAliveMessageStream)
        .Close();
    // register templates
    _messageStreams.Add(MessageStreamType.MakeKnownOnTheNetwork, instance1);
    _messageStreams.Add(MessageStreamType.AskForClientIdentity, template1.Build());
    _messageStreams.Add(MessageStreamType.IsAlive, template2.Build());

    // Prestart connection manager
    _connectionManager.Start();
}
  • The network protocol I invented
    • For events it uses GUID’s for unique identification of ‘knowledge parts’. These GUID’s are on a transaction log that is automatically shared partially across clients. This mechanism creates a shared eventual consistency database.
    • For data it uses transfer blocks to get two important benefits: 1) resume-able downloads and 2) efficient distribution among multiple clients
  • Speed
    • Consistency is guaranteed by having 4 area’s with their own ‘owning’ thread.
    • Speed is achieved by splitting all non-colliding work in chunks and doing it parallel on worker threads.
    • And awaitables everywhere: the program itself heavily relies on awaitables (the ‘old’ method, before task factories and the build-in async await) to signal work blocks ready for continuation.

Related stuff

Random order, tooling used

  • C# OWin Nancy (website/webapi)
  • TCP/UDP Client
  • C# Forms
  • Autofac dependency injection
  • Threading with wait handles
  • C# LiteDB
  • GIT sourcecontrol