Message Handling
The client application uses the
same message structures as the server, but the client
has no need for queuing messages. As Figure 19.16 demonstrates, incoming
messages
are immediately parsed by the client.
Now that you’ve seen the
receive function that handles incoming message, it’s time
to examine each message handling function.
cApp::create_player
As clients join the game, the
server informs other connected clients of those new
arrivals. The purpose of the following create_player function is to find room in
the
sPlayer structure and store the player data:
void cApp::create_player(const sMsg* msg)
{
sCreatePlayerMsg* create_msg = (sCreatePlayerMsg*) msg;
// do not add local player to list
if(create_msg->header.player_id == m_players[0].player_id)
return;
long player_index = -1;
for(long i = 1; i < MAX_PLAYERS; i++)
{
if(m_players[i].connected)
{
// makre sure player not already in list
if(m_players[i].player_id == create_msg->header.player_id)
return;
}
else
player_index = i;
}
if(player_index == -1) // no open slots
return;
EnterCriticalSection(&m_update_cs);
// add player data
m_players[player_index].connected = true;
m_players[player_index].player_id = create_msg->header.player_id;
m_players[player_index].x_pos = create_msg->x_pos;
m_players[player_index].y_pos = create_msg->y_pos;
m_players[player_index].z_pos = create_msg->z_pos;
m_players[player_index].direction = create_msg->direction;
m_players[player_index].speed = 0.0f;
m_players[player_index].last_state = STATE_IDLE;
m_num_players++;
LeaveCriticalSection(&m_update_cs);
}
cApp::destroy_player
The server notifies clients when a player is leaving a session. The clients, in
turn,
signal the player as being disconnected and skips updating the player during the
update cycle. The following code determines which client is disconnected and
takes the appropriate steps:
void cApp::destroy_player(const sMsg* msg)
{
sDestroyPlayerMsg* destroy_msg = (sDestroyPlayerMsg*) msg;
// do not remove local player from list
if(destroy_msg->header.player_id == m_players[0].player_id)
return;
long player_index = get_player_index(destroy_msg->header.player_id);
if(player_index == -1)
return;
EnterCriticalSection(&m_update_cs);
m_players[player_index].connected = false;
m_num_players--;
LeaveCriticalSection(&m_update_cs);
}
cApp::change_player_state
The client processes changes of
state in players by pulling out the message data and
putting it in the player’s structure. If a player isn’t found in the list of
players, the
client requests that player’s information via a MSG_GET_PLAYER_INFO message and
exits the
change_player_state function without further ado.
This is the only situation in
which a player’s coordinates can be directly modified
by a state change—clients are not allowed to make direct changes to their
coordinates
(to avoid cheating), so it’s up to the server to tell players just where they
are
in the world during the updates:
void cApp::change_player_state(const sMsg* msg)
{
sStateChangeMsg* change_msg = (sStateChangeMsg*) msg;
long player_index = get_player_index(change_msg->header.player_id);
if(player_index == -1) // unknown player - request information
{
sRequestPlayerInfoMsg request_msg;
request_msg.header.type = MSG_GET_PLAYER_INFO;
request_msg.header.size = sizeof(sRequestPlayerInfoMsg);
request_msg.header.player_id = m_players[0].player_id;
request_msg.request_player_id = change_msg->header.player_id;
send_network_msg(&request_msg, DPNSEND_NOLOOPBACK);
return;
}
EnterCriticalSection(&m_update_cs);
// store new player state information
m_players[player_index].last_state = change_msg->state;
m_players[player_index].x_pos = change_msg->x_pos;
m_players[player_index].y_pos = change_msg->y_pos;
m_players[player_index].z_pos = change_msg->z_pos;
m_players[player_index].direction = change_msg->direction;
m_players[player_index].speed = change_msg->speed;
m_players[player_index].latency = change_msg->latency;
// bounds latency to 1 second
if(m_players[player_index].latency > 1000)
m_players[player_index].latency = 1000;
// adjust time based on latency
m_players[player_index].last_update_time = timeGetTime() - m_players[player_index].latency;
LeaveCriticalSection(&m_update_cs);
}
//////////////////////////////////////////////////////////////////////////////////////////////
bool cApp::send_network_msg(void* msg, long send_flags)
{
sMsgHeader* header = (sMsgHeader*) msg;
if(header->size == 0)
return false;
return m_client.send_data(msg, header->size, send_flags);
}
Just like the server, the client has a
send_network_msg to send the game-related
network messages to the server.
NOTE
The client also depends on the latency time to modify
the timing calculations.The server sends this latency
time to the client, but to make things safe, the client
application is allowed to cut the latency down to one
second if the server states that it is higher.