FIT CTU

Adam Vesecký

NI-APH
Lecture 11

Multiplayer

Networking Architecture

Multiplayer

Offers a capability for multiple players to act within the same world at the same time.
A complex feature that has a significant impact on the game architecture, touching almost every fundamental structure.

Multiplayer categories

  • single-screen multiplayer
  • split-screen multiplayer
  • networked multiplayer
  • MMOG
  • Cloud Gaming

Multiplayer history

Local multiplayer games

  • Spacewar! (1962)

Networked multiplayer games

  • Empire (1973)

Local area network games

  • Doom (1993)

Online multiplayer games

  • Quake (1996)
  • Unreal (1998)

Online-only games

  • World of Tanks (2010)
  • Diablo 3 (2012)

MMO

  • Ultima Online (1997)
  • World of Warcraft (2004)

Then and now

Multiplayer in 1977

Multiplayer now

Issues

  • the main objective - how to synchronize several universes
  • all clients have to achieve a certain degree of synchrony
  • attributes that affect the gameplay: capacity, speed, latency, jitter, packet loss
  • there is no known real-world picture for this type of problem
  • it's easy to convert multiplayer game into a single-player game
  • it's very difficult to implement multiplayer features into a single-player game

Methods

  • transfering a complete game state to all clients - for simple games
  • transfering a minimal subset to reconstruct the rest - more common

Topologies

  • peer-to-peer (not used anymore)
  • client-server

Peer-to-peer architecture

  • each device exchanges data with each other in a fully connected graph
  • used in Doom, early Command & Conquer series, Age of Empires, Starcraft
  • given n  peers, each must have n-1  connections -> O(n^2)  in total
  • methods: single master, partial authority, full replication

Peer-to-peer games

Doom (1993)

  • 14.4 kbps PPP or 28.8 kbps SLIP
  • each turn player inputs were exchanged with other peers
  • you had to wait for the inputs from the most lagged player
  • every 30ms, the input from each player is sampled into a tic command
  • when the tic commands for all players have been received, the game advances its state

Age of Empires (1997)

  • for 8-player battle there could be up to 400 units
  • used Turn Timer - queue for commands
  • AoE synchronizes the commands each player issued, rather than units
  • all commands during 200ms are saved into a buffer
  • when the 200-ms-frame is over, all commands for a player's turn are transmitted to others

Client-Server architecture

  • n  devices, n-1  connections
  • server must handle (n-1)\times  more messages per second
  • server quickly becomes the bottleneck (lack of power and bandwidth)
  • Dedicated server - only runs the game state and communicates
  • Listen server - server is an active participant in the game itself

Example: Quake

  • the first game that used partial reliability
  • each client acts as a dumb terminal
  • outputs are picked up by the prediction layer
  • the server runs at 20 FPS, while the client runs at 60 FPS

Command

  • the elementary unit of communication
  • used to udpate a position of the player, orientation, HP,...
  • reliable commands (with impact on the game state) have to be confirmed

NetChannel header

Transport

TCP vs UDP

  • as opposed to SW applications, games have two main types of messages
  • changes to the gameplay (actions, events) need to be confirmed
  • streaming data (positions) don't need to be confirmed
  • critical events may have higher priority over others
  • UDP represents a basic streaming protocol onto which engines built a customized gaming protocol
Using TCP is the worst possible mistake you can make when developing a multiplayer game.Glenn Fielder, 2008

Multiplayer engine architecture

  • the server is running the game and clients only process inputs and display results

Issues

  • each player has only partially consistent view of the game world
  • in some cases, the server may disagree with the client's state
  • there is a delay between taking an action an observing the result

Example: Message Header

  • SEQ number - sequential number of the message
  • ACK number - number of another message to be confirmed
  • ACK bit array - confirmation flags for previous 32 messages
  • Type - type of the message (update, disconnect, command,...)
  • ActionID - id of particular action

Message Types

Stream

  • doesn't need to be confirmed, contains a collection of continuous values (e.g., position change)

Snapshot

  • complete information of the game state, sent either on demand or at given intervals

Event

  • messages that have an impact on the game state, have to be confirmed
  • e.g.: UNIT_CREATED, UNIT_DESTROYED, BUILDING_COMPLETED

Action

  • high-priority messages (player's inputs, fire button,...)

Procedure Call

  • a generic message that allows to call any function (play sound, load assets, reset animation)

Connection messages

  • messages for handshake, ID assignment, disconnect, etc.

Beacon

  • regular messages to inform the server that the connection is still on

Example: Goat Attacks

Serialization

  • a large game world can have hundreds of moving objects
  • we need to remove any information that doesn't need to be sent
  • messages should be as close in size to MTU (~1500B) as possible

Example with no optimization

  • RTS battle
  • 5 players
  • 500 moving units
  • each unit has 20 attributes of 32-bit size -> 80 B per unit
  • the server sends 30 messages per second
  • header size is 42B (IP + UDP + networking header)
  • required bandwidth for the server: 30 \cdot 5 \cdot (42 + 500 \cdot 80) \cdot 8 \approx 52  Mbps

Serialization - Binary Footprint

  • we serialize everything - not very robust solution

struct Mage {

    int health;

    int mana;

}

 

void Serialize(const Mage* mage) {

    SendMessage(reinterpret_cast<const char*>(mage), sizeof(Mage));

}

 

void Deserialize(const Mage* output) {

    ReceiveMessage(reinterpret_cast<char*>(output), sizeof(Mage));

}

Health = 10, Mana = 14; Little Endian

Serialization - Streams

  • streams allow us to customize what attributes to serialize and how
  • better solution for collections

 struct Mage : public Streamable {

    vector<Item*> items;

    int health;

    int mana;  

  

    void SaveToStream(NetWriter* writer) {

        writer->WriteDWord(health);

        writer->WriteDWord(mana);

        writer->WriteDWord(items.size());

        for(auto item : items) item->SaveToStream(writer);

    }

  

    void LoadFromStream(NetReader* reader) {

        health = reader->ReadDWord();

        mana = reader->ReadDWord();

        int itemsCount = reader->ReadDWord();

        for(int i=0; i<itemsCount; i++) {

            items.push_back(new Item(reader));

        }

    }

}

Compression

Compression of bits

  • it is helpful to represent values with as few bits as possible
  • we can work with limited range and precision

Entropy Encoding

  • we compress data based on how unexpected it is
  • example: we can assume that the rotation is mostly 0, hence we can use only one bit to indicate this

Compression

Compression of attributes

  • we can serialize only attributes that vary
  • each object has a bit field that indicates which attributes follow in the stream

Compression of the payload

  • Huffman encoding, run-length encoding, LZ4,...

Compression

Delta messages

  • if a number doesn't change much, nor will its bits
  • between two frames, there is only a few changed bits for common attributes (position, rotation,...)
  • we store only differences by using XOR operation and compress the data
  • very effective but sensitive to packet loss
  • we can store 2/3 of attributes as diffs and 1/3 as full data - we will only need 3 consecutive packets to retrieve the full state

Replication

  • the act of transmitting a state of an object from one device to another
  • each object must be uniquely identified (network ID)
  • the network message contains a type of an object and all parameters required to construct it

 switch(actionType) {

    case OBJECT_CREATED:

      int objectType = reader->ReadDWord();

      auto factory = creatorManager->FindObjectFactory(objectType);

      auto newInstance = factory->CreateInstance(reader); // parse parameters

      objects.push_back(newInstance);

    break;

    case OBJECT_DELETED:

      int objectId = reader->ReadDWord();

      sceneManager->removeObjectById(objectId);

    ...

  }

Reliability

  • packets may get lost
  • server keeps sending messages that have an impact on the game state until the client accepts them

Ordering

  • packets may arrive in a different order
  • the client shouldn't apply a command message to its state before it applies all previous messages

Latency

Latency

  • the amount of time between an observable cause and its observable effect
  • e.g. mouse click and a unit responding to its orders

Suitable latencies

  • FPS: 16-60 ms
  • RTS: < 250 ms

Non-network latency

  • input sampling latency (~2 ms)
  • rendering pipeline latency (< 16 ms)
  • frame decoding latency for cloud gaming (~2-16 ms)

Network latency

  • processing delay (encryption, routing)
  • queuing delay (router can only process a limited number of packets at a time)
  • transmission delay (information can't travel faster than the speed of light)

Latency Example

  • both clients have the same latency

Latency Example

  • client B has a higher latency

Synchronization

Interpolation

  • client can run at 60 FPS, the server usually sends state updates at 10-30 FPS
  • Interpolation: whenever the client receives a new state, it smoothly interpolates to that state
  • Problem: the object might change its interpolated value instantly (teleport)

Interpolation Reserve

  • client renders a state that is 2 frames old
  • smooth interpolation, but we create artificial delay

Extrapolation

  • without interpolation reserve, the client tries to extrapolate
  • results in jittery animation

Deterministic prediction

  • interpolation reserve is precise but delayed behind the server
  • the client is always at least 1/2 RTT behind the true state
  • some attributes can be handled on client side completely (e.g. camera pose in FPS)

Prediction

  • the client predicts the future value by running the same simulation code
  • to perform extrapolation by 1/2 RTT, the client must approximate the RTT

Non-deterministic prediction

  • non-deterministic values are hard to predict (steering behaviors, other players' movement,...)
  • solution: make an educated guess and correct it when an update arrives
  • Dead reckoning - process of predicting a behavior of an entity based on the assumption that it will keep doing what it's currently doing

Server simulation

Client misprediction

Server-side rewind

  • dealing with instant actions that affect the gameplay (e.g. instant hit in FPS)
  • occurs due to the inaccuracies of dead reckoning and time dilation
  • server may change a state that has already been confirmed

Source engine's approach

  • rewinds state on the server to exactly the state in which the player fired
  • server stores the poses of every relevant object for X last frames and looks up the two frames between which the client was interpolating

Wrong

Correct

Example: Server-side rewind

  • Client B has 3x higher latency than Client A
  • Client B performed a change before Client A did

Latency handling summary

Time dilation

  • delays the values by a few frames and interpolates to them

Deterministic prediction

  • runs simulated code, masks latency and keeps the client's state in sync

Dead reckoning

  • non-deterministic prediction
  • client uses the last known state of an object to extrapolate future state

Server-side rewind

  • the server buffers object positions for several frames to match the client's view when processing instant events
It is better to be wrong on time than right but late

Example: Source engine

  • server simulates the world at 30-60 FPS and sends 20 snapshots per second by default, using an interpolation reserve of 100 ms
  • client samples inputs at 30-60 FPS and buffers snapshots for 100 ms
  • server uses time dilation and rewind

Security

Classic threats

  • packet sniffing, man-in-the-middle
  • ghosting - scouting the players using multiple connections

Input validation

  • players can't perform an action that is invalid
  • only a client responsible for Player A can send an action that affects that player

Software cheat detection

  • actively monitors the integrity of the game
  • cheating SW can hook into the game, overwrite memory, modify files
  • map hacking - removing fog of war and revealing resources
  • bot cheat - bot that either plays the game or assists the player
    • e.g. dummy levelling, aimbots
  • Valve Anti-Cheat - available for games that utilize Steamworks SDK
    • maintains a list of banned users, scans for known cheat programs
  • Easy AntiCheat - prevents cheating on a technical level

Libraries

Torque Network Library (opentnl)

ReplicaNet

  • powerful library for object sharing (sessions)

RakNet

NetStalker

Netcode

  • networking library for Unity

Lecture Summary

  • I know the difference between P2P and client-server architecture
  • I know what issues networking architecture may face
  • I know what types of messages can be used in multiplayer games
  • I know what replication is
  • I know how interpolation works
  • I know what server-side rewind is
  • I know how games can deal with latency

Goodbye Quote

Now you see me, now you're dead.Duke Nukem