Multiplayer Decisions
I’ve started working on the multiplayer code for Rival Fortress. It’s an exploratory process, as low level networking is very much new to me.
I’m trying to get the simplest and dumbest possible implementation running without adding too much complexity. I’ll probably have to double-back on most of my decisions, so I don’t want to get too attached to the first working implementation.
Network topology
The network topology that I’m leaning towards is client-server with a dedicated headless server executable and a built-in listen server for the standalone game. I’m also considering a deterministic lockstep peer-to-peer networking model, but I think that the ideal topology will emerge once the game gets more fleshed out.
Dedicated server
To get my feet wet I started working on the dedicated server. It is a command-line executable that shares all the code with the game with exception of the rendering code.
The process of adding a new build target was straightforward: it was just a matter of #ifdef
-ing out all rendering code and adding a call to the server loop instead of the game loop. I want to build the dedicated server using the same code as the standalone game, as I think this will make it easier when I’ll implement the listen server.
Serialization and replication
I cobbled together support for basic replication: Plain Old Data (POD) objects get sent back and forth between the client and server using UDP
. As I don’t use inheritance or new
in my C++ code I don’t have to worry about virtual methods or constructors/destructors, so replicating POD objects is just a simple memcpy
into the network packet.
I haven’t handled pointers yet, but I’ll most likely leverage the engine’s reflection preprocessor to generate code for serialization and replication of complext game objects.
I’m also looking at forms of entropy encoding and other forms of data compression to reduce the amount of stuff sent over the wire. I implemented a floating point to half float converter that reduces 32bit floats to 16 (for position information), and a simple entropy encoder that can serialize partial struct
s. Every serialized object has an tiny header that contains serialization information about the object as a bit flag.