天行健 君子当自强而不息

Getting Online with Multiplayer Gaming(17)

 

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.


posted on 2007-12-19 16:44 lovedday 阅读(220) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论