Multiplayer Games Chapter 6 Network Topologies.ppt

MoissFreitas13 14 views 34 slides Oct 17, 2024
Slide 1
Slide 1 of 34
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34

About This Presentation

Multiplayer Games Programming Network Topologies


Slide Content

Multiplayer Game Programming
Chapter 6
Network Topologies

Chapter 6
Objectives
Network Topologies
–How should different computers be connected to each other,
in the game, and what are the tradeoffs?
Implementing Client-Server
–What are the specific challenges of implementing the client-
server topology?
Implementing Peer-to-Peer
–How to guarantee synchronization between peers in a
deterministic lockstep simulation?

Network Topologies
Which hosts send data to which other hosts
Impacts total bandwidth used by game
Impacts latency players experience
Impacts method used to share consistent game world
Impacts what game can do
Impacts how players can and cannot cheat
Impacts how player hardware affects other players
Impacts cost to operate game

Client-Server
???INSERT FIGURE 6.1 PLEASE

Client-Server Properties
All clients connect to only one server
–O(2n) connections total
•Server: O(n) connections
•Client:1 connection
–Asymmetric requirements
•Server needs better hardware and much more
upstream bandwidth than clients
•Server needs scale as client count increases
•Client needs remain relatively constant

Server Authority
“The Server Is The Man” –Tim Sweeney
The game server’s simulation of the game is
considered correct – if a client disagrees, the server
wins the argument
This may increase the lag or latency for commands,
since they must always be validated by the server
The round trip time (RTT) between clients is
increased, since it must go through the server
–Between Clients A and B, the worst-case latency is
½ Client A’s RTT + server processing time + ½
Client B’s RTT

Types of Servers
A dedicated server is only a server process, and can
run on a completely separate machine than any clients
–Many bigger games run their own servers
A listen server involves a server that is also an active
participant in the game
–Saves developers money
–May give the player who is on the listen server an
advantage in terms of latency
–May need a complex setup to support host
migration – changing from one player hosting to
another

Peer-to-Peer
???INSERT FIGURE 6.2 PLEASE

Peer-to-Peer Properties
Each computer connects to every other computer
–O(n
2
) connections total
•Each peer: O(n – 1) connections
–Symmetric bandwidth requirements
•The more players in the game, the more
bandwidth required
•Each peer requires the same amount of
bandwidth
–Better latency than client-server:
•Worst-case is ½ RTT

Input Sharing
Concept of authority is much more nebulous in peer-
to-peer
Most peer-to-peer games implement some sort of
input sharing – all actions are shared across all peers
Recall that Age of Empires used such a model
(deterministic lockstep)

Caveat
For now, the implementation of both the client-server
and peer-to-peer games will assume there is no
latency nor packet loss
This is not a realistic assumption for a real game
How to handle latency and packet loss is discussed in
Chapter 7 and 8

Implementing Client-Server
(RoboCat)
???INSERT FIGURE 6.3 PLEASE

Separating Server and Client
Code
For a particular game object:
–Some code is specific to the server
–Some code is specific to the client
–Some code is shared between the server and the
client
One solution: use inheritance!
–Have a base class for the shared code
–A separate derived class for the server, and a
separate one for the client

Separating Server and Client
Code
???INSERT FIGURE 6.4 PLEASE

Network Manager
The NetworkManager does much of the heavy-lifting
of interacting with the network
Responsbilities are split up between subclasses, so:
–The base NetworkManager contains shared
functionality, such as basic UDP packet processing
–Server/client code is split up into
NetworkManagerServer and
NetworkManagerClient

Welcoming New Clients
1.Client sends the server a “hello” packet
2.Server assigns the new client a player ID, and sends
the client a “welcome” packet
3.When the client receives the welcome packet, it saves
its player ID
4.At some point in the future, the server spawns objects
for the new client, and starts sending object updates to
the new and existing clients

Client Sending and Receiving
Packets
void NetworkManagerClient ::SendOutgoingPackets()
{
switch(mState)
{
case NCS_SayingHello:
UpdateSayingHello();
break;
case NCS_Welcomed:
UpdateSendingInputPacket();
break;
}
}

Client Sending and Receiving
Packets
void NetworkManagerClient ::ProcessPacket
( InputMemoryBitStream & inInputStream,
const SocketAddress& inFromAddress )
{
uint32_t packetType;
inInputStream.Read(packetType);
switch(packetType)
{
case kWelcomeCC:
HandleWelcomePacket(inInputStream);
break;
case kStateCC:
HandleStatePacket(inInputStream);
break;
}
}

Client Sending Hello Packets
void NetworkManagerClient ::UpdateSayingHello()
{
float time = Timing::sInstance.GetTimef();
 
if(time > mTimeOfLastHello + kTimeBetweenHellos)
{
SendHelloPacket();
mTimeOfLastHello = time;
}
}
 
void NetworkManagerClient ::SendHelloPacket()
{
OutputMemoryBitStream helloPacket;
 
helloPacket.Write(kHelloCC);
helloPacket.Write(mName);
 
SendPacket(helloPacket, mServerAddress);
}

Client Reading Welcome Packets
void NetworkManagerClient ::HandleWelcomePacket(
InputMemoryBitStream& inInputStream)
{
if (mState == NCS_SayingHello)
{
//if we received a player id, we've been welcomed!
int playerId;
inInputStream.Read(playerId);
mPlayerId = playerId;
mState = NCS_Welcomed;
LOG("'%s' was welcomed on client as player %d" ,
mName.c_str(), mPlayerId);
}
}

Server Tracking Clients
Server contains an address-to-client map
When data is received from an address, the server can
determine which client sent the packet
If the client is unknown, the only packets that are
considered are “hello” packets, and only if the game is
in a state where hello packets are valid

Input Sharing and Client Proxies
The client shouldn’t send replication data to the server,
since the server is the authority
Instead, the client’s (abstracted) input is sent to the
server for processing and validation
The server uses a client proxy for its representation
of each client in the game
As input data comes in, it is associated with the
corresponding proxy, and then all proxies are updated
every frame

Implementing Peer-to-Peer
(RoboCat RTS)
???INSERT FIGURE 6.5 PLEASE

Welcoming New Peers
In peer-to-peer, adding new players is a bit more complex
There is a master peer that coordinates adding a new
player, to ensure there are no race conditions
When a hello packet is received by a peer, there are three
possible responses:
–Welcome – Only the master peer will send this out,
signifying the player is welcomed to the game
–Not joinable – The game cannot be currently joined
–Not master peer – The hello packet was received by a
different peer, who informs the sender who the master
peer is

Peer-to-peer Addresses
One major issue in peer-to-peer is that not all peers
may be reachable to every other peer
This is especially an issue when some peers are on a
local network, whereas others have a global IP
address
One solution to this is a rendezvous server, that
insures all players are registered with their global IP
address

Rendezvous Server
In (a) Peer C cannot connect to Peer B, but this is solved
by the rendezvous server in (b)
???INSERT FIGURE 6.6 PLEASE

Command Sharing
As in Age of Empires, send commands rather than
object replication updates
RoboCat RTS uses 100ms turn durations
Commands issued by a peer on turn x are not
executed until turn x + 2, giving time for network
transmission
At the end of a turn, a turn packet is sent to each peer

Sending Turn Data to Peers
void NetworkManager::UpdateSendTurnPacket()
{
mSubTurnNumber++;
if (mSubTurnNumber == kSubTurnsPerTurn)
{
//create our turn data
TurnData data(mPlayerId,
RandGen::sInstance->GetRandomUInt32(0, UINT32_MAX),
ComputeGlobalCRC(),
InputManager::sInstance->GetCommandList());
 
//we need to send a turn packet to all of our peers
OutputMemoryBitStream packet;
packet.Write(kTurnCC);
//we're sending data for 2 turns from now
packet.Write(mTurnNumber + 2);
packet.Write(mPlayerId);
data.Write(packet);
 
for (auto &iter: mPlayerToSocketMap)
{
SendPacket(packet, iter.second);
}
 
//continued on next slide...

Sending Turn Data to Peers
 
//save our turn data for turn + 2
mTurnData[mTurnNumber + 2].emplace(mPlayerId, data);
InputManager::sInstance->ClearCommandList();
if (mTurnNumber >= 0)
{
TryAdvanceTurn();
}
else
{
//a negative turn means there's no possible commands yet
mTurnNumber++;
mSubTurnNumber = 0;
}
}
}

Advancing the Turn
TryAdvanceTurn will only advance to the next turn if all
the command data for that turn has been received
from all peers
Otherwise, go into a delay state and wait for the
missing data
This is to ensure the peers never desynchronize

Synchronizing PRNGs
Pseudo-random number generators (PRNGs) rely on a
seed value to generate the sequences
PRNGs can be synchronized across peers:
–Each PRNG starts with the same seed
–Each peer will always make the same number of
PRNG calls per frame
–Each call will occur in the same exact locations in
the code
–Each call will occur in the same order for each peer
Can’t use Standard C rand because the PRNG
algorithm is implementation-defined

C++11 Random Numbers
C++11 has several tightly specified PRNG algorithms
in the <random> header
A popular choice for games is the 32-bit Mersenne
Twister algorithm (MT19937)
The Mersenne Twister sequence has a period of 2
19937
,
which means it realistically will not repeat during the
course of a game – and even if it does, it would be
difficult for the player to exploit

Implementing a RandGen Class
void RandGen::StaticInit()
{
sInstance = std::make_unique< RandGen>();
//just use a default random seed, we'll reseed later
std::random_device rd;
sInstance->mGenerator.seed(rd());
}
 
void RandGen::Seed(uint32_t inSeed)
{
mGenerator.seed(inSeed);
}
 
uint32_t RandGen::GetRandomUInt32( uint32_t inMin, uint32_t inMax)
{
std::uniform_int_distribution <uint32_t> dist(inMin, inMax);
return dist(mGenerator);
}

Verifying Game Synchronization
Much how packets are validated, use a checksum to
determine whether peers remain synchronized
Every frame, perform a 32-bit CRC calculation over all
relevant data for each game object
Include this checksum in each turn packet
Before advancing to the next turn, verify all the
checksums are consistent – if they aren’t, the game
has desynchronized
Tags