Microsoft DirectX 9.0 SDK Update (Summer 2003) |
Handling Standard Peer-to-Peer Messages
This document describes how to handle Microsoft® DirectPlay® messaging for a normal member of a peer-to-peer session. It does not discuss messages that are specific to a host. For a discussion of host-related messaging, see Peer-to-Peer Host Messages. For a discussion of general messaging issues, see Handling DirectPlay Messaging.
Startup Messages
The host can set up a peer-to-peer session in two ways.
- Arrange the session in advance. For example, you can use an online lobby to collect a group of players prior to starting the session. The player selected as host is responsible for calling IDirectPlay8Peer::Host to start the session. The lobby typically provides the host's address to the other players, and they use that address to connect directly to the session. Players usually need not do a host enumeration. See DirectPlay Lobby for details on how to handle lobbies.
- Create a stand-alone session. When a player does a host enumeration, an enumeration query is sent to every host at the specified network location that matches the player's description of the type of session they want to join. If the host chooses to respond, the host passes the player the information the player needs to connect to the session. With a stand-alone session, there is no need for a lobby. The host calls IDirectPlay8Peer::Host and assembles the group of players by responding to enumeration queries and connection attempts.
This section describes the messages you may receive when selecting and joining a session.
- DPN_MSGID_ENUM_HOSTS_RESPONSE
If you need to locate a standalone session, call IDirectPlay8Peer::EnumHosts to enumerate the available hosts. You will receive a DPN_MSGID_ENUM_HOSTS_RESPONSE message for each host that responds to your enumeration request. You may receive multiple DPN_MSGID_ENUM_HOSTS_RESPONSE messages from the same host.
By default, host enumeration is performed asynchronously. When an asynchronous enumeration terminates, for instance after the retry count is reached, you will be notified with a DPN_MSGID_ASYNC_OP_COMPLETE message. You will receive no further DPN_MSGID_ENUM_HOSTS_RESPONSE messages after DPN_MSGID_ASYNC_OP_COMPLETE arrives. You can cancel the operation at any time by calling IDirectPlay8Peer::CancelAsyncOperation. You can also cancel a host enumeration by calling IDirectPlay8Peer::Connect to connect to a session. The enumeration is halted as soon as the connect request completes successfully.
Note You should call IDirectPlay8Peer::Connect outside of the DPN_MSGID_ENUM_HOSTS_RESPONSE message handler. - DPN_MSGID_CONNECT_COMPLETE
Typically, the next step you will take is to attempt to join a session by calling IDirectPlay8Peer::Connect. You normally receive a DPN_MSGID_CONNECT_COMPLETE message with the host's response, even if the host rejects your connection request. You can receive this message for asynchronous and synchronous calls, but there are differences in detail.
- Asynchronous calls: When the connection attempt is underway, IDirectPlay8Peer::Connect normally returns DPNSUCCESS_PENDING. When you get this return value, you will always receive a DPN_MSGID_CONNECT_COMPLETE message containing the outcome of the connection attempt. However, the message might be indicated on a different thread before IDirectPlay8Peer::Connect returns. If the method returns DPNSUCCESS_PENDING, you also receive an asynchronous operation handle that you can use to cancel the operation by calling IDirectPlay8Peer::CancelAsyncOperation. This handle is valid only until you receive the DPN_MSGID_CONNECT_COMPLETE message. If IDirectPlay8Peer::Connect does not return DPNSUCCESS_PENDING, you will not receive a DPN_MSGID_CONNECT_COMPLETE message. Typically, this occurs when there is a problem with parameter validation, memory allocation, or addressing.
- Synchronous calls: The method returns after the connection attempt is completed. You may receive a DPN_MSGID_CONNECT_COMPLETE message, as well as a return value. However, the message is guaranteed to arrive before the method returns. If your connection attempt is successful, you will receive a DPN_MSGID_CONNECT_COMPLETE, followed by a return value of DPN_OK. You may also receive a DPN_MSGID_CONNECT_COMPLETE message before other return values, such as DPNERR_HOSTREJECTEDCONNECTION. You will not receive DPN_MSGID_CONNECT_COMPLETE if the method call fails because of parameter validation, memory allocation, or addressing problems. You do not receive an asynchronous operation handle and cannot use IDirectPlay8Peer::CancelAsyncOperation to cancel a synchronous connection attempt.
- DPN_MSGID_CREATE_PLAYER and DPN_MSGID_CREATE_GROUP
If your connection attempt was successful, you will receive a DPN_MSGID_CREATE_PLAYER message for each player in the session at the time you joined. At a minimum, you will receive one message for the host and one for your player. If any groups have been created, you will receive a DPN_MSGID_CREATE_GROUP message for each group. You will also receive a series of DPN_MSGID_ADD_PLAYER_TO_GROUP messages for each group, one for each player in the group. All of these messages will arrive before DPN_MSGID_CONNECT_COMPLETE.
You must define your player and group context values when you handle the associated creation messages. These context values can also be defined earlier, when IDirectPlay8Peer::Host, IDirectPlay8Peer::Connect, or IDirectPlay8Peer::CreateGroup methods are called. You can change those player or group context values when you handle DPN_MSGID_CREATE_PLAYER or DPN_MSGID_CREATE_GROUP respectively. However, once that handler returns, the corresponding context value cannot be changed again. See Using Player Context Values for further discussion.
Messaging During Normal Game Play
During a game, you might receive any of the following messages.
- DPN_MSGID_CREATE_PLAYER and DPN_MSGID_DESTROY_PLAYER
Players can typically enter or leave while the game is in progress. You receive a DPN_MSGID_CREATE_PLAYER when a player enters a game, and a DPN_MSGID_DESTROY_PLAYER message when that player leaves. You might receive DPN_MSGID_CREATE_PLAYER and DPN_MSGID_DESTROY_PLAYER messages simultaneously on different threads, but only for different players. You will not receive a DPN_MSGID_DESTROY_PLAYER message before your callback function has returned from receiving the corresponding DPN_MSGID_CREATE_PLAYER message. If you want to create a player context value, you must do so before you return from the DPN_MSGID_CREATE_PLAYER message handler.
- DPN_MSGID_CREATE_GROUP and DPN_MSGID_DESTROY_GROUP
Many games simplify messaging by allowing players to be grouped. When a group is created, you receive a DPN_MSGID_CREATE_GROUP message, regardless of whether your player is a member. If you want to create a group context value, you must do so before you return from the DPN_MSGID_CREATE_GROUP message handler. When a group is destroyed, you receive a DPN_MSGID_DESTROY_GROUP message. You will not receive a DPN_MSGID_DESTROY_GROUP message before your callback function has returned from processing the corresponding DPN_MSGID_CREATE_GROUP message.
- DPN_MSGID_ADD_PLAYER_TO_GROUP and DPN_MSGID_REMOVE_PLAYER_FROM_GROUP
You receive DPN_MSGID_ADD_PLAYER_TO_GROUP messages for each group member after a group is created, and when a player is added to a group. You receive a DPN_MSGID_REMOVE_PLAYER_FROM_GROUP message when a player is removed from a group, and for all the remaining players in the group when the group is destroyed. You will not receive a DPN_MSGID_REMOVE_PLAYER_FROM_GROUP message before your callback function has returned from processing the corresponding DPN_MSGID_ADD_PLAYER_TO_GROUP message.
You are guaranteed to receive every DPN_MSGID_REMOVE_PLAYER_FROM_GROUP message for a group before you receive the DPN_MSGID_DESTROY_GROUP message. When a player is destroyed, you will receive a DPN_MSGID_REMOVE_PLAYER_FROM_GROUP message for every group in which that player is a member, before you receive the player's DPN_MSGID_DESTROY_PLAYER message.
- DPN_MSGID_SEND_COMPLETE
You send data to a player or group by calling IDirectPlay8Peer::SendTo. If you call this message asynchronously, the method will normally return DPNERR_PENDING. You receive a DPN_MSGID_SEND_COMPLETE message when the data is sent. The DPN_MSGID_SEND_COMPLETE message can arrive before or after the method returns.
Receiving a DPN_MSGID_SEND_COMPLETE message does not necessarily mean that the target has received and processed the message. By default, if the network is overloaded, packets may be dropped. If you want to be certain that the message has arrived at the target, set the DPNSEND_GUARANTEED flag when you call IDirectPlay8Peer::SendTo. You will not receive a DPN_MSGID_SEND_COMPLETE message until DirectPlay has verified that the target has received the data. If you want to be certain that the message has also been processed by the target, set the DPNSEND_COMPLETEONPROCESS flag. If you set this flag, you will not receive a DPN_MSGID_SEND_COMPLETE message until the target's message handler has processed the message and returned.
Note Setting the DPNSEND_GUARANTEED and DPNSEND_COMPLETEONPROCESS flags add overhead to the Send process. You may want to design your communication process to be able to tolerate some data loss in return for increased performance. - DPN_MSGID_RECEIVE
When another player sends you data, it is delivered to your message handler with a DPN_MSGID_RECEIVE message. By default, the buffer containing the data is valid only until you return from the message handler. If you want to retain control over the data buffer after your message handler returns, have your message handler return DPNERR_PENDING. This return value will prevent DirectPlay from freeing or modifying the buffer. When you no longer need the buffer, you must return it to the control of DirectPlay by calling IDirectPlay8Peer::ReturnBuffer.
- DPN_MSGID_PEER_INFO, DPN_MSGID_GROUP_INFO, and DPN_MSGID_APPLICATION_DESC
Information structures are associated with the application, and with each player and group in the session. If this information changes during a session, you will receive the corresponding message. To obtain up-to-date information, you must call IDirectPlay8Peer::GetApplicationDesc, IDirectPlay8Peer::GetGroupInfo, or IDirectPlay8Peer::GetPeerInfo.
- DPN_MSGID_HOST_MIGRATE
A session must have a host. If the host set the DPNSESSION_MIGRATE_HOST flag when it created the session and then leaves the session without terminating it, DirectPlay chooses a new host. DirectPlay notifies the remaining players of the change by sending them a DPN_MSGID_HOST_MIGRATE message with the new host's identifier (ID).
Session Termination Messages
Sessions terminate for a variety of reasons. Typically, the session terminates when the host calls IDirectPlay8Peer::TerminateSession. If host migration is not permitted, the session terminates when the host calls IDirectPlay8Peer::Close or is involuntarily disconnected.
- DPN_MSGID_TERMINATE_SESSION
The DPN_MSGID_TERMINATE_SESSION message notifies you that the session is over. It is followed by a series of DPN_MSGID_DESTROY_PLAYER, DPN_MSGID_DESTROY_GROUP, and DPN_MSGID_REMOVE_PLAYER_FROM_GROUP messages.
- DPN_MSGID_DESTROY_PLAYER
You will receive a DPN_MSGID_DESTROY_PLAYER message for each player in the session. If the host has intentionally terminated the session, this is considered normal behavior and the dwReason member of the associated structure is set to DPNDESTROYPLAYERREASON_NORMAL. The DPNDESTROYPLAYERREASON_SESSIONTERMINATED value is set only for unexpected disconnections from a session that does not allow host migration.
- DPN_MSGID_DESTROY_GROUP and DPN_MSGID_REMOVE_PLAYER_FROM_GROUP
If any groups have been formed, you will receive DPN_MSGID_REMOVE_PLAYER_FROM_GROUP messages for each member of each group, and a DPN_MSGID_DESTROY_GROUP message for each group itself. The DPN_MSGID_REMOVE_PLAYER_FROM_GROUP messages will always arrive before the corresponding DPN_MSGID_DESTROY_PLAYER or DPN_MSGID_DESTROY_GROUP messages.