天行健 君子当自强而不息

Getting Online with Multiplayer Gaming(13)

 

cApp::remove_player

Just as players join the game, so do players quit, and that’s the purpose of the
remove_player function. In the remove_player function, the server will scan the list of
connected players for a match of a DirectPlay identification number (from the disconnecting
player) and remove that player from the list. After the scan is complete
and the appropriate player is removed from the list, all clients are notified of the
disconnecting player, and the server rebuilds a list of existing players.

void cApp::remove_player(const sMsg* msg)
{
    
// search for player in list
    for(long i = 0; i < MAX_PLAYERS; i++)
    {
        
if(m_players[i].player_id == msg->header.player_id && m_players[i].connected)
        {
            m_players[i].connected = 
false;

            
// send remove player message to all players

            sDestroyPlayerMsg destroy_msg;

            destroy_msg.header.type      = MSG_DESTROY_PLAYER;
            destroy_msg.header.size      = 
sizeof(sDestroyPlayerMsg);
            destroy_msg.header.player_id = msg->header.player_id;

            send_network_msg(&destroy_msg, DPNSEND_NOLOOPBACK, ALL_CLIENT_PLAYERS);

            m_connected_player_num--;
            list_players();

            
break;
        }
    }
}
 

cApp::send_player_info

Unfortunately, in network gaming, game messages sometimes get lost along the
way. What if one of those lost messages intended to inform the client application
that a player had joined the game? Furthermore, what if the client started receiving
messages related to a player that the client didn’t know existed (because of a lost
message)?

In cases where the client has no knowledge of a player and is receiving messages
related to that player, the client will request the appropriate player’s data from the
server in order to continue. The server, in turn, will send the requested player’s
information to the client using the send_player_info function:

bool cApp::send_player_info(const sMsg* msg, DPNID to)
{
    sRequestPlayerInfoMsg* request_msg = (sRequestPlayerInfoMsg*) msg;

    
for(long i = 0; i < MAX_PLAYERS; i++)
    {
        
// only send if found in list
        if(m_players[i].player_id == request_msg->request_player_id && m_players[i].connected)
        {
            
// send player infomation to requesting player

            sCreatePlayerMsg create_msg;

            create_msg.header.type      = MSG_SEND_PLAYER_INFO;
            create_msg.header.size      = 
sizeof(sCreatePlayerMsg);
            create_msg.header.player_id = request_msg->request_player_id;
            create_msg.x_pos            = m_players[i].x_pos;
            create_msg.y_pos            = m_players[i].y_pos;
            create_msg.z_pos            = m_players[i].z_pos;
            create_msg.direction        = m_players[i].direction;

            send_network_msg(&create_msg, DPNSEND_NOLOOPBACK, to);

            
break;
        }
    }

    
return true;
}
 

cApp::player_state_change

The major message-processing function in the server must be player_state_change,
which takes incoming actions from the clients and updates the internal player data.

bool cApp::player_state_change(const sMsg* msg)
{
    
// get player index in list
    long player_index = -1;
    
    
for(long i = 0; i < MAX_PLAYERS; i++)
    {
        
if(m_players[i].player_id == msg->header.player_id && m_players[i].connected)
        {
            player_index = i;
            
break;
        }
    }

    
if(player_index == -1)
        
return false;

    sPlayer* player = &m_players[player_index];

    
bool allow_change = true;
    
    
// refuse to update player if swinging sword or hurt
    if(player->last_state == STATE_SWING || player->last_state == STATE_HURT)
        allow_change = 
false;    

    
// only change state if allowed
    if(! allow_change)
        
return false;

    sStateChangeMsg* change_msg = (sStateChangeMsg*) msg;

    
// update selected player
    player->last_update_time = timeGetTime();
    player->last_state       = change_msg->state;
    player->direction        = change_msg->direction;

    
// adjust action time based on latency
    player->last_update_time -= player->latency;

    sStateChangeMsg state_msg;

    
// send player data to all clients
    state_msg.header.type      = MSG_STATE_CHANGE;
    state_msg.header.size      = 
sizeof(sStateChangeMsg);
    state_msg.header.player_id = change_msg->header.player_id;
    state_msg.state            = player->last_state;
    state_msg.x_pos            = player->x_pos;
    state_msg.y_pos            = player->y_pos;
    state_msg.z_pos            = player->z_pos;
    state_msg.direction        = player->direction;
    state_msg.speed            = player->speed;

    send_network_msg(&state_msg, DPNSEND_NOLOOPBACK, ALL_CLIENT_PLAYERS);

    
if(change_msg->state != STATE_SWING)
        
return true;

    
// If swinging sword, determing who is hurt, check all players.
    for(long i = 0; i < MAX_PLAYERS; i++)
    {
        
// only check against other players that are connected
        if(i == player_index || !m_players[i].connected)
            
continue;

        
// get distance to player
        float x_diff = fabs(player->x_pos - m_players[i].x_pos);
        
float z_diff = fabs(player->z_pos - m_players[i].z_pos);
        
float dist   = x_diff * x_diff + z_diff * z_diff;

        
// continue if distance between players acceptable
        if(dist >= 10000.0f)
            
continue;

        
// get angle between players
        float angle = -atan2(m_players[i].z_pos - player->z_pos, m_players[i].x_pos - player->x_pos) + 1.570796f;

        angle -= player->direction;     
// adjust for attacker's direction
        angle += 0.785f;                // adjust for FOV

        // bounds angle value
        if(angle < 0.0f)        angle += 6.28f;
        
if(angle >= 6.28f)      angle -= 6.28f;

        
// player hit if in front of attacker (90 FOV)
        if(angle >= 0.0f && angle <= 1.570796f && m_players[i].last_state != STATE_HURT)
        {
            m_players[i].last_state = STATE_HURT;
            m_players[i].last_update_time = timeGetTime();

            
// send network message
            state_msg.header.type      = MSG_STATE_CHANGE;
            state_msg.header.size      = 
sizeof(sStateChangeMsg);
            state_msg.header.player_id = m_players[i].player_id;
            state_msg.state            = m_players[i].last_state;
            state_msg.x_pos            = m_players[i].x_pos;
            state_msg.y_pos            = m_players[i].y_pos;
            state_msg.z_pos            = m_players[i].z_pos;
            state_msg.direction        = m_players[i].direction;
            state_msg.speed            = m_players[i].speed;

            send_network_msg(&state_msg, DPNSEND_NOLOOPBACK, ALL_CLIENT_PLAYERS);
        }
    }

    
return true;
}

Up to this point, the server has looked for the player that uses the state-change
message. If a message is coming from a player who is not connected, the message
is ignored. From now on, the game’s logic takes over.

Players are allowed to walk, stand still, or swing their weapons. Players whose states
are already set as swinging their weapons or being hurt are not allowed to update
their states (until those states are cleared).

Now the player’s state is updated (if allowed) and sent out to all other connected
players. Next, if the player has swung his weapon, all players are scanned to see
whether the attacker hit them. If so, the states of those hurt are changed to HURT.

Also, notice that I offset the state’s time variable (sPlayer::last_update_time) by the player’s
latency value (sPlayer::latency). This adjusts for network transmission delays and
improves synchronization. If you remove the latency offset, you’ll see a jumping
effect when players are moving around the level.

Note that players who are swinging their swords have a chance to hit the players in
front of them. To check whether another player was hit during an attack, you first
perform a distance calculation, and if any characters are considered close enough,
the angles between the players are checked. If the players being attacked are within
a 90-degree field of view in front of the attackers (as illustrated in Figure 19.14),
they are considered hit, at which point, those victims’ states are changed to HURT.

And that’s it for dealing with the game messages and state changes in players.
Although the player_state_change function is responsible for parsing the queued game
messages, it’s really up to another function to move players and clear their swinging
or hurt states, as you see in the following section.

posted on 2007-12-18 22:39 lovedday 阅读(231) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论