Getting Online with Multiplayer Gaming(10)

Getting Online with Multiplayer Gaming(10)

 

DirectPlay Messages to Game Messages

As I’ve mentioned before, the server needs to convert the DirectPlay network messages
into the game-related messages you’ve just read about. You accomplish this by
processing incoming player connection, disconnection, and receive data messages from
DirectPlay and converting those messages into game messages.

To accomplish this conversion of messages, you derive a class from cNetworkServer
and override the create_player, destroy_player, and receive functions:

class  cServer :  public  cNetworkServer
{
protected :
    
virtual   bool  create_player( const  DPNMSG_CREATE_PLAYER* msg);
    
virtual   bool  destroy_player( const  DPNMSG_DESTROY_PLAYER* msg);
    
virtual   bool  receive( const  DPNMSG_RECEIVE* msg);
};

Because I’m using the System Core to handle application processing, a problem
quickly arises when dealing with the network. The network component and application
component are two separate entities, which means that neither component is
allowed to modify the other's private data.

As Figure 19.11 illustrates, the network component needs a way to siphon incoming
messages into the application, which by chance is handled by creating three public
functions that match the network class’s functions.

Getting Online with Multiplayer Gaming(10)_第1张图片

To use the three message functions in the application component, you construct a
derived cFramework class that contains the three public functions as follows:

class  cApp :  public  cFramework
{
private :
    HWND                m_controls[3];
    
    CRITICAL_SECTION    m_msg_cs;
    cMesh               m_level_mesh;

    GUID*               m_adapter_guid;
    cNetworkAdapter     m_adapter;
    cServer             m_server;

    
long                 m_connected_player_num; 
    sPlayer*            m_players;

    sMsg*               m_msgs;
    
long                 m_msg_head;
    
long                 m_msg_tail;

    
////////////////////////////////////////////////////////////////////////////// /

public :
    
void  set_adapter_guid(GUID* adapter_guid)
    {
        m_adapter_guid = adapter_guid;
    }

public :
    cApp();
    
    
virtual   bool  init();
    
virtual   bool  frame();
    
virtual   void  shutdown();

    
void  create_player( const  DPNMSG_CREATE_PLAYER* msg);
    
void  destroy_player( const  DPNMSG_DESTROY_PLAYER* msg);
    
void  receive( const  DPNMSG_RECEIVE* msg);

private :
    
bool  select_adapter();
    
void  setup_app_window();
    
bool  init_game();
    
bool  host_game();    

    
void  list_players();

    
void  process_queue_msg();
    
void  update_players();
    
void  update_network();
    
void  update_latency();

    
bool  send_player_info( const  sMsg* msg, DPNID to);
    
bool  queue_msg( const   void * msg);
    
bool  add_player( const  sMsg* msg);
    
void  remove_player( const  sMsg* msg);
    
bool  player_state_change( const  sMsg* msg);

    
bool  send_network_msg( void * msg,  long  send_flags,  int  to);

    
bool  check_intersect(cMesh* mesh,
                         
float  x_start,  float  y_start,  float  z_start,
                         
float  x_end,    float  y_end,    float  z_end);
};

To start sending DirectPlay messages to the application class, you code the overridden
cServer functions to call upon the matching application functions. In order for the
server to know which application class instance to send messages to, you need to
declare a global variable that points to the current application class instance in use:

cApp* g_app;
cNetworkAdapter* g_adapter;

Inside the derived application class’s constructor, you then point the global
g_app variable to the application class instance:

cApp::cApp()
{    
    m_adapter_guid = NULL;
    m_msgs         = NULL;
    m_msg_head     = 0;
    m_msg_tail     = 0;

    m_connected_player_num = 0;
    m_players = NULL;

    g_app     = 
this ;
    g_adapter = &m_adapter;

    InitializeCriticalSection(&m_msg_cs);
}

Now, you can code the network server component to send incoming messages to
the application object defined by the global g_app pointer:

bool  cServer::create_player( const  DPNMSG_CREATE_PLAYER* msg)
{
    g_app->create_player(msg);

    
return   true ;
}

bool  cServer::destroy_player( const  DPNMSG_DESTROY_PLAYER* msg)
{
    g_app->destroy_player(msg);

    
return   true ;
}

bool  cServer::receive( const  DPNMSG_RECEIVE* msg)
{
    g_app->receive(msg);

    
return   true ;
}

The server component is now complete and is forwarding network messages to the
application class. To convert those network messages to game-related messages,
the application class must contain the following public functions:

void  cApp::create_player( const  DPNMSG_CREATE_PLAYER* msg)
{
    sCreatePlayerMsg create_msg;

    create_msg.header.type      = MSG_CREATE_PLAYER;
    create_msg.header.size      = 
sizeof (sCreatePlayerMsg);
    create_msg.header.player_id = msg->dpnidPlayer;

    queue_msg(&create_msg);
}

///////////////////////////////////////////////////////////////////////////////////////

void  cApp::destroy_player( const  DPNMSG_DESTROY_PLAYER* msg)
{
    sDestroyPlayerMsg destroy_msg;

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

    queue_msg(&destroy_msg);
}

///////////////////////////////////////////////////////////////////////////////////////

void  cApp::receive( const  DPNMSG_RECEIVE* msg)
{
    sMsgHeader* header = (sMsgHeader*) msg->pReceiveData;

    
// make sure it is a valid message type and queue it
     switch (header->type)
    {
    
case  MSG_SEND_PLAYER_INFO:
    
case  MSG_STATE_CHANGE:
        queue_msg(msg->pReceiveData);
        
break ;
    }
}

You can see that in each of the three functions, I’m constructing a game-related
message using the data from the DirectPlay messages provided. When a player tries
to connect to the server, a create-player message is created that stores the connecting
player’s DirectPlayer identification number (along with the message type and size).
That create-player message is then queued.

As for players disconnecting from the game, a disconnect-player message is constructed
and queued. Last, whenever data (other than a system message) is
received from a client, the cApp::receive function checks it to see whether it’s a valid
message type, and if so, the message is queued.

I keep mentioning the message queue and how the previously shown function adds
messages to the queue. Next, you find out what the queue is and how it works.

你可能感兴趣的:(Getting Online with Multiplayer Gaming(10))