Using DirectPlay

This section contains the following topics that explain how to use different aspects of DirectPlay:

Debug vs. Retail DLLs

The Software Development Kit (SDK) has the option to install debug or retail builds of the DirectPlay dynamic-link libraries (DLLs). When developing software, it is best to install the debug versions of the DLLs. The debug DLLs have additional code that will validate internal data structures and output debug error messages (using the Win32® OutputDebugString API) while your program is executing. When an error occurs, the debug output will give you a more detailed description of what the problem is. The debug DLLs will execute more slowly than the retail DLLs but are much more useful for debugging an application. Be sure to ship the retail version with your application.

In order to see the debug messages, it is necessary to configure your computer so that debug output will be displayed in a window or on a remote computer. An interactive development environment like Microsoft® Visual C++™ allows you to do this. Consult the environment's documentation for set-up instructions.

See Debugging DirectPlay Applications for more information.

Working with GUIDs

Globally unique identifiers (GUIDs) are 16-byte data structures that you can use to identify objects in a globally unique way. Whenever a GUID is required in an API, a symbol representing that GUID should be used. The symbols are either defined in one of the DirectPlay header files, or the application developer must generate them. You can generate GUIDs by using the Guidgen.exe utility that comes with Microsoft Visual C++. For example, every application must define an application GUID that identifies the application that is running in a session.

Note If there are different versions of an application that cannot interoperate in the same session, they should have different application GUIDs to distinguish them.

To use DirectPlay-defined GUIDs successfully in your application, you must either define the INITGUID symbol prior to all other include and define statements, or you must link to the Dxguid.lib library. If you define INITGUID, you should define it in only one of your source modules.

DirectPlay Interfaces

All the functionality in DirectPlay is accessed through member functions on Component Object Model (COM) interfaces. To use them, an application must obtain the appropriate COM interface.

The standard method of obtaining COM interfaces is to use the Win32 CoCreateInstance API. To use it successfully, the application must first call the Win32 CoInitialize API to initialize COM, and then call CoCreateInstance, specifying the GUID of the desired interface. For example, use the following code to obtain an IDirectPlay4A interface:

  // C++ example
  hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, 
                         IID_IDirectPlay4A, (LPVOID*)&lpDirectPlay4A);
  // C example
  hr = CoCreateInstance( &CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, 
                         &IID_IDirectPlay4A, (LPVOID*)&lpDirectPlay4A);

When the program is finished, all the COM interfaces must be freed by calling the Release method on each interface. Finally, the Win32 CoUninitialize method should be called to uninitialize COM.

If you call CoCreateInstance without first calling CoInitialize, you will get a CO_E_NOTINITIALIZED error, with the error text "CoInitialize has not been called."

DirectPlay has several COM interfaces. Each interface represents a revision of an earlier version of DirectPlay in which new methods are added. COM interfaces are numbered sequentially with each revision. The latest COM interface will have all the latest functionality of DirectPlay. To access the new functionality (for example, IDirectPlay4::SendChatMessage), you must use the latest COM interface. Source code written for an earlier COM interface will work.

Once a COM interface is obtained, an alternate interface can be used on the same object by calling the QueryInterface method on the interface. For example, DirectPlayCreate will create a DirectPlay object and return an IDirectPlay interface. If your application requires an IDirectPlay4 interface, it can call QueryInterface on the IDirectPlay interface. Be sure to release the original IDirectPlay interface if it is no longer needed.

Using Callback Functions

The enumeration methods in DirectPlay are used to return a list of items to the application. The application calls an enumeration method (such as IDirectPlay4::EnumPlayers) and supplies a pointer to a callback function that it has implemented. DirectPlay will call the callback function once for each item in the list. The enumeration method will not return until all the items in the list have been returned to the application through the callback function.

It is extremely important that all callbacks be declared correctly. For example:

BOOL FAR PASCAL EnumConnectionsCallback(LPCGUID lpguidSP, LPVOID lpConnection, DWORD dwConnectionSize,
LPCDPNAME lpName, DWORD dwFlags, LPVOID pContext);

The FAR PASCAL symbol will define the function as _stdcall. That means it will clean up the stack before returning to DirectPlay. Do not cast function pointers when passing them to a DirectPlay enumeration method. If there is a compiler warning about the function pointer, fix the function declaration.

Building Lobby-Aware Applications

A lobby-aware application is one that, at a minimum, supports being launched from a lobby. A matchmaking lobby is a site on the Internet where end users can find other people to play games with. Once a group of people has decided to start an application session, the lobby software can launch the application on each person's computer and have them all connect to a session. The main benefit for end users is the ease with which they can establish a session with other players. Not only does it allow a user to easily find opponents, but there is also no need for the user to:

Other benefits of the lobby are:

For a DirectPlay application to be lobby-aware, at a minimum, it must support being launched from a lobby. You can add other features to make it integrate better with the lobby. For additional information see the following topics within this section.

Registering Lobby-Aware Applications

To enable a lobby to launch a DirectPlay application, the application must add entries to the system registry. Once an application has been registered, it will be recognized as a lobby-aware application and lobbies can launch it. These registry entries must be deleted when the application is removed.

You can register your application by filling in the DPAPPLICATIONDESC structure with the necessary information, then calling IDirectPlayLobby3::RegisterApplication. You can unregister your application by calling IDirectPlayLobby3::UnRegisterApplication.

Supporting External Lobby Launching

Once the application has been registered, the DirectPlay application must be able to recognize whether a lobby launched it. If a lobby launched the application, it must follow a slightly different code path to set up the network connection. Consult the DUEL (Lobby.c) and DPCHAT (Lobby.cpp) samples in the SDK for the code necessary to support external lobby launching. See Tutorial 1: Connecting by Using the Lobby for a demonstration on how to connect an application by using a DirectPlay lobby.

The necessary basic steps are:

  1. At startup, create an IDirectPlayLobby3 interface using the CoCreateInstance API.
  2. The application can examine the connection information that was passed in by the lobby and modify some of the connection settings if necessary. The IDirectPlayLobby3::GetConnectionSettings method returns a DPLCONNECTION structure with the connection settings. This method returns DPERR_NOTLOBBIED if a lobby did not launch the application. New settings can be set with IDirectPlayLobby3::SetConnectionSettings.
  3. IDirectPlayLobby3::ConnectEx creates or joins the specified application session using the specified service provider and returns an IDirectPlay interface of your choosing. ConnectEx returns an error if the session could not be created or joined, or if a lobby did not launch the application.
  4. Create a player using the name information supplied in the DPLCONNECTION structure obtained in step two.

At this point, the application can continue on the same code path as if the user had manually selected a connection, joined or created a session, and entered the name of the player to create.

The IDirectPlayLobby3 interface can be saved if the game will pass information back to the lobby (see Lobby Messaging), or it can be discarded by using the Release method if no messages will be sent.

Many lobbies will launch the application and then go into a suspend mode, waiting for the application to terminate. DirectPlay will notify the lobby when the application that it launched has terminated. For this reason it is important that the application launched by the lobby not launch another application.

Supporting Dynamic Lobby Connection

One drawback of the external lobby launching described in the preceding topic is that the lobby server always launches a new instance of your game, even if one is already running. You can avoid this by supporting dynamic lobby connection.

The instructions in this section assume you already have a game running and a connection to a lobby server which will return you connection settings. The disadvantage of this approach is that the lobby server always launches a new instance of your game even when one is already running. This method of supporting dynamic lobby connection is implemented in the Duel sample.

  1. At startup, create an IDirectPlayLobby3 interface using the CoCreateInstance API.
  2. Ask the user to choose dynamic lobby connection. The user must choose dynamic lobby connection instead of manual network configuration.
  3. Call IDirectPlayLobby3::WaitForConnectionSettings to put your application in wait mode.
  4. Wait for a lobby message to come in using IDirectPlayLobby3::ReceiveLobbyMessage. The message type to expect is the DPLSYS_NEWCONNECTIONSETTINGS system message. Note that you can call WaitForConnectionSettings again to cancel wait mode.
  5. After you receive the system message, examine the connection information that was passed in by the lobby and modify some of the connection settings if necessary. The IDirectPlayLobby3::GetConnectionSettings method returns a DPLCONNECTION structure with the connection settings. This method returns DPERR_NOTLOBBIED if a lobby did not launch the application. New settings can be set with IDirectPlayLobby3::SetConnectionSettings.
  6. IDirectPlayLobby3::ConnectEx creates or joins the specified application session using the specified service provider and returns an IDirectPlay interface of your choosing. ConnectEx returns an error if the session could not be created or joined, or if a lobby did not launch the application.
  7. Create a player using the name information supplied in the DPLCONNECTION structure obtained in step five.

Lobby Messaging (Optional)

After registering a lobby-aware application and adding the code to support external lobby launching, the next step in integrating the application with the lobby is to send information back to the lobby or to request information from the lobby. You can do this by exchanging messages with the lobby through the IDirectPlayLobby3::SendLobbyMessage and IDirectPlayLobby3::ReceiveLobbyMessage methods on the IDirectPlayLobby3 interface. Standard message structures have been defined by DirectPlay to facilitate this functionality.

Sending information to the lobby is done through setting properties. The application must create and fill in a DPLMSG_SETPROPERTY structure and send it to the lobby by using the SendLobbyMessage method. Each property identifies a distinct type of data. The application developer should generate GUIDs (using Guidgen.exe) for every property that will be set. The lobby operator needs to obtain this list of GUIDs from the application developer along with the description and data structure of each property.

Properties can take the form of:

The lobby can store this information or display it to other users in the lobby so they can monitor the game's progress. An application can request confirmation that the property was set correctly by supplying a nonzero request ID with the set property message.

Requesting information from the lobby is done through requesting properties. The application must create and fill in a DPLMSG_GETPROPERTY structure and send it to the lobby by using the SendLobbyMessage method. At some later time, the lobby will send a message back to the application—a DPLMSG_GETPROPERTYRESPONSE structure that contains the property data that was requested. The application can retrieve the message from the lobby message queue using the ReceiveLobbyMessage method. As before, the application developer generates the GUIDs for each property and the lobby operator must obtain them from the developer.

The following list shows examples of some properties that can be requested from the lobby:

Not all lobbies will be able to support these standard lobby messages. The application can determine whether the lobby that it was launched from supports standard lobby messages by sending a DPLMSG_GETPROPERTY message requesting the DPLPROPERTY_MessagesSupported property.

DirectPlay Messages

The following sections describe how to use messages in DirectPlay.

Synchronization

An application can use two methods to process DirectPlay messages. The first is to check the receive queue during the main loop of the application. Typically, this means the application is single threaded.

Alternatively, an application can have a separate thread to wait for messages and process them. In this case the application should supply a non-NULL, auto-reset synchronization event handle (see the Win32 CreateEvent API) when it creates players. DirectPlay will set this event when a message arrives for that player. All the local players in an application can share the same event or each can have his or her own event.

The message processing thread should then use the Win32 WaitForSingleObject API to wait until the event is set. Continue calling Receive until there are no more messages in the message queue.

Using System Messages

Messages returned by the IDirectPlay4::Receive method from player ID DPID_SYSMSG are system messages. All system messages begin with a double-word value specified by the dwType DWORD value. You can cast the buffer returned by the IDirectPlay4::Receive method to a generic message (DPMSG_GENERIC) and switch on the dwType element, which will have a value equal to one of the messages with the DPSYS_ prefix. After the application has determined which system message it is, the buffer should be cast to the appropriate structure (beginning with the DPMSG_ prefix) to read the data.

Your application should be prepared to handle the following system messages.

Value of dwTypeMessage structureCause
DPSYS_ADDGROUPTOGROUP DPMSG_ADDGROUPTOGROUP An existing group has been added to an existing group.
DPSYS_ADDPLAYERTOGROUP DPMSG_ADDPLAYERTOGROUP An existing player has been added to an existing group.
DPSYS_CHAT DPMSG_CHAT A chat message has been received.
DPSYS_CREATEPLAYERORGROUP DPMSG_CREATEPLAYERORGROUP A new player or group has been created.
DPSYS_DELETEGROUPFROMGROUP DPMSG_DELETEGROUPFROMGROUP A group has been removed from a group.
DPSYS_DELETEPLAYERFROMGROUP DPMSG_DELETEPLAYERFROMGROUP A player has been removed from a group.
DPSYS_DESTROYPLAYERORGROUP DPMSG_DESTROYPLAYERORGROUP An existing player or group has been destroyed.
DPSYS_HOST DPMSG_HOST The current host has left the session and this application is the new host.
DPSYS_SECUREMESSAGE DPMSG_SECUREMESSAGE A digitally signed or encrypted message has been received.
DPSYS_SENDCOMPLETE DPMSG_SENDCOMPLETE NEW NEW! An asynchronous send is complete.
DPSYS_SESSIONLOST DPMSG_SESSIONLOST The connection with the session has been lost.
DPSYS_SETGROUPOWNER DPMSG_SETGROUPOWNER NEW NEW! The owner of the group has changed.
DPSYS_SETPLAYERORGROUPDATA DPMSG_SETPLAYERORGROUPDATA Player or group data has changed.
DPSYS_SETPLAYERORGROUPNAME DPMSG_SETPLAYERORGROUPNAME Player or group name has changed.
DPSYS_SETSESSIONDESC DPMSG_SETSESSIONDESC Session description has changed.
DPSYS_STARTSESSION DPMSG_STARTSESSION Lobby server is requesting that a session be started.

Using Lobby Messages

Messages returned by the IDirectPlayLobby3::ReceiveLobbyMessage method fall into two categories: DirectPlay-defined messages and custom-defined messages. The message category can be identified by the lpdwMessageFlags parameter of ReceiveLobbyMessage. The flags indicate that the message is either a system message (the DPLMSG_SYSTEM flag) or a standard message (the DPLMSG_STANDARD flag). If neither of these flags is set, the message is custom-defined. System messages are generated automatically by DirectPlay and are sent to the lobby only to inform it of changes in the status of the application. Standard messages can be generated by either the lobby or the application and sent to each other.

The advantage of standard messages over custom-defined messages is that the receiver can positively interpret the message. All applications and lobbies are not required to act on standard messages.

DirectPlay-defined messages all start with a DWORD value that identifies the message type. After retrieving a message using ReceiveLobbyMessage, the lpData pointer to the message data should be cast to the DPLMSG_GENERIC structure and the structure's dwType member examined. Based on the value of dwType, the lpData pointer should then be cast to the appropriate message structure for further processing.

Lobbies should be prepared to handle all the message types in the following table. Applications need to handle the DPLMSG_GETPROPERTYRESPONSE message if they generate DPLMSG_GETPROPERTY messages.

Messages returned by the IDirectPlayLobby3::ReceiveLobbyMessage method that have a dwFlags parameter set to DPLMSG_SYSTEM are system messages. All system messages begin with a double-word value specified by dwType. You can cast the buffer returned by the IDirectPlayLobby3::ReceiveLobbyMessage method to a generic message (DPLMSG_GENERIC) and switch on the dwType element, which will have a value equal to one of the messages with the DPLSYS_ prefix.

The following list shows the possible values of the dwType data member, and the message structure and message cause associated with each value.

Value of dwTypeMessage structureCause
DPLSYS_APPTERMINATED DPLMSG_SYSTEMMESSAGE The application has terminated.
DPLSYS_CONNECTIONSETTINGSREAD DPLMSG_SYSTEMMESSAGE The application has read the connection settings.
DPLSYS_DPLAYCONNECTFAILED DPLMSG_SYSTEMMESSAGE The application failed to connect to the DirectPlay session.
DPLSYS_DPLAYCONNECTSUCCEEDED DPLMSG_SYSTEMMESSAGE The application successfully connected to the DirectPlay session.
DPLSYS_GETPROPERTY DPLMSG_GETPROPERTY The application is requesting a property from the lobby.
DPLSYS_GETPROPERTYRESPONSE DPLMSG_GETPROPERTYRESPONSE The lobby is responding to a prior DPLMSG_GETPROPERTY message.
DPLSYS_NEWCONNECTIONSETTINGS DPLMSG_SYSTEMMESSAGE A waiting application was found when a lobby client called RunApplication. The waiting application can now read the connection settings with GetConnectionSettings and join a session using ConnectEx.
DPLSYS_NEWSESSIONHOST DPLMSG_NEWSESSIONHOST NEW NEW! The application session has a new host.
DPLSYS_SETPROPERTY DPLMSG_SETPROPERTY The application is setting a property on the lobby.
DPLSYS_SETPROPERTYRESPONSE DPLMSG_SETPROPERTYRESPONSE The lobby is responding to a prior DPLMSG_SETPROPERTY message.

DirectPlay Address (Optional)

A DirectPlay Address is a data format that DirectPlay uses to pass addressing information between lobby servers, applications, DirectPlay, and service providers. The format is flexible enough to hold any number of variable-length data fields that make up a network address. You do not need to understand DirectPlay Addresses in order to use DirectPlay. In fact, you can successfully write a DirectPlay application without understanding DirectPlay Addresses.

This section contains the following topics:

You only need to learn about DirectPlay Addresses if you want to do one of the following:

A DirectPlay Address is a sequence of variable-length data chunks tagged with a GUID that supplies all the address information needed by DirectPlay. Examples are the network address of a server, the network address of a player, the e-mail name of a player, or the network address of a session.

DirectPlay Address Data Types

Microsoft has predefined the following general data types for each data chunk. Based on the data type, the data must be cast to the appropriate type or structure to interpret the data.

GUIDType of Data
DPAID_ComPort A DPCOMPORTADDRESS structure that contains all the settings for the COM port. The serial connection service provider will use this information to configure the serial port.
DPAID_INet A null-terminated ANSI string (LPSTR) containing an IP address ("137.55.100.173") or a domain net ("gameworld.com"). The length, in bytes, must include the terminator.

A blank address is a string that contains only the ANSI terminator (0x00) and has a length of 1 byte. If a blank address is supplied, the Internet TCP/IP connection service provider will use this information to enumerate sessions on the specified network address or to broadcast on the subnet.

DPAID_INetW A null-terminated Unicode string (LPWSTR) containing an IP address ("137.55.100.173") or a domain net ("gameworld.com"). The length in bytes must include the terminator.

A blank address is a string that contains only the Unicode terminator (0x0000) and has a length of 2 bytes. If a blank address is supplied, the Internet TCP/IP connection service provider will use this information to enumerate sessions on the specified network address or broadcast on the subnet.

DPAID_INetPort A WORD (2 bytes) containing the specific port number to use. See the section on selecting a specific port in the TCP/IP topic or see Firewall Support in DirectPlay 6.0 for information about using this DirectPlay Address.
DPAID_LobbyProvider The 16-byte GUID that specifies the lobby provider this DirectPlay Address applies to.
DPAID_Modem A variable-length null-terminated ANSI string (LPSTR) specifying which installed modem to use. The length, in bytes, must include the terminator. The modem service provider will use this modem without displaying a dialog box asking the user which modem to use. Use the IDirectPlay4::GetPlayerAddress method to determine which modems are available.
DPAID_ModemW A variable-length, null-terminated Unicode string (LPWSTR) specifying which installed modem to use. The length, in bytes, must include the terminator. The modem service provider will use this modem without displaying a dialog box asking the user which modem to use. Use the IDirectPlay4::GetPlayerAddress method to determine which modems are available.
DPAID_Phone A variable-length, null-terminated ANSI string (LPSTR) containing a phone number. The length, in bytes, must include the terminator. The modem service provider will call this phone number on the IDirectPlay4::EnumSessions method. If no modem is specified, the first modem will be used.
DPAID_PhoneW A variable-length null-terminated Unicode string (LPWSTR) containing a phone number. The length in bytes must include the terminator. The modem service provider will call this phone number on the IDirectPlay4::EnumSessions method. If no modem is specified, the first modem will be used.
DPAID_ServiceProvider The 16-byte GUID that specifies the service provider this DirectPlay Address applies to.
DPAID_TotalSize A DWORD containing the size of entire DPADDRESS structure.

Using DirectPlay Addresses

You can use a DirectPlay Address to encapsulate all the information necessary to initialize a DirectPlay object. At a minimum, this is the GUID of a DirectPlay provider, but can also include the network address of an application or lobby server and even a specific session instance GUID.

A DirectPlay Address can be used to supply enough information to DirectPlay (and the DirectPlay provider) when it is initialized so that no dialog boxes appear later during the process of establishing a session or connecting to a session.

The DirectPlay Addresses returned by EnumConnections are simply registered DirectPlay providers that the user can choose from. When one of these is initialized, dialog boxes can appear, which ask the user for more information.

The application can create a DirectPlay Address directly using the IDirectPlayLobby3::CreateAddress and IDirectPlayLobby3::CreateCompoundAddress methods on the IDirectPlayLobby3 interface and pass the connection to the IDirectPlay4::InitializeConnection method to initialize the DirectPlay object. If enough valid information is supplied, then no DirectPlay dialog boxes will appear.

Examples of Using DirectPlay Addresses

This topic contains examples of DirectPlay Addresses and the data they contain for different connection types.

A DirectPlay Address describing an IPX connection

Initializing this connection will bind the DirectPlay object to the IPX service provider.

guidDataTypedwDataSizeData
DPAID_ServiceProvider 16 {685BC400-9D2C-11cf-A9CD-00AA006886E3}

A DirectPlay Address describing a modem connection

Initializing this connection binds DirectPlay to the modem service provider and stores the phone number. A subsequent call to the IDirectPlay4::EnumSessions will dial the number without asking the user for a phone number.

guidDataTypedwDataSizeData
DPAID_ServiceProvider 16 {44EAA760-CB68-11cf-9C4E-00A0C905425E}
DPAID_Phone 9 (including NULL terminator) "555-8237"

A DirectPlay Address describing a TCP/IP connection

Initializing this connection binds DirectPlay to the TCP/IP service provider and stores the IP address. A subsequent call to IDirectPlay4::EnumSessions will enumerate sessions on this server without asking the user for an IP address.

guidDataTypedwDataSizeData
DPAID_ServiceProvider 16 {36E95EE0-8577-11cf-960C-0080C7534E82}
DPAID_INet 10 (including NULL terminator) "127.0.0.1"

DirectPlay Protocol

The DirectPlay protocol is an implementation of guaranteed messaging on nonguaranteed service providers. For example, the Internetwork Packet Exchange (IPX) service provider does not inherently support guaranteed messaging. When an application tries to send a guaranteed message on IPX, the DirectPlay protocol will take care of doing all the handshaking necessary to guarantee delivery of the message.

The DirectPlay protocol also implements asynchronous messaging and message throttling, although the service provider may optimize this functionality.

An application invokes the DirectPlay protocol by setting the DPSESSION_DIRECTPLAYPROTOCOL flag when creating a session.

System messages are always sent guaranteed, whether or not the service provider natively supports guaranteed messages, and whether or not you set the DirectPlay protocol flag. The DirectPlay protocol will be used automatically to send system messages guaranteed.

For non-system messages, if an application sets the DirectPlay protocol flag and sends a message guaranteed (by setting the DPSEND_GUARANTEED flag in SendEx), the message will be sent guaranteed whether the service provider supports guaranteed messages or not.

For non-system messages, if an application does not set the DirectPlay protocol flag and sends a message guaranteed, the message will be sent guaranteed only if the service provider supports it. If the service provider does not support guaranteed messages, the message will not be sent guaranteed, regardless of the DPSEND_GUARANTEED flag.

An application also can also specify that the DirectPlay protocol be used instead of the default guaranteed messaging implemented by the service provider. This is also done by specifying the DPSESSION_DIRECTPLAYPROTOCOL flag when creating a session.

The following table summarizes how non-system messages are sent.

If the DirectPlay Protocol flag is not set:

Service ProviderMessages Sent Guaranteed Use:Messages Sent Nonguaranteed Use:
IPXIPX, won't be sent guaranteedIPX
TCP/IPTCPUDP
ModemModem, won't be sent guaranteedModem

If the DirectPlay Protocol flag is set:

Service ProviderMessages Sent Guaranteed Use:Messages Sent Nonguaranteed Use:
IPXDirectPlayProtocol/IPXDirectPlayProtocol/IPX
TCP/IPDirectPlayProtocol/UDPDirectPlayProtocol/UDP
ModemDirectPlayProtocol/ModemDirectPlayProtocol/Modem

When the DirectPlay protocol flag is not set, the application must examine the capabilities of the service provider (after the session has been opened) to determine what functionality is available in terms of asynchronous messaging, canceling messages, timeouts on messages, and prioritization of messages. If the DirectPlay protocol flag is set, all this functionality is available.

Unless the service provider is TCP/IP, when the DirectPlay Protocol flag is not explicitly set, messages will not be sent guaranteed.

Asynchronous Messaging

The ability to send messages asynchronously frees the application's communication code from blocking while each message is transmitted. It offloads management of outgoing messages to DirectPlay while still giving the application programmer the ability to monitor the message queue and to cancel messages.

The asynchronous messaging feature is only available if the session is using the DirectPlay Protocol or if it is supported by the service provider. For example, the TCP/IP service provider only supports asynchronous messages when the computer is using WinSock 2.0 or greater. If the DirectPlay Protocol is not being used, it is important to use IDirectPlay4::GetCaps method to check the DPCAPS_ASYNCSUPPORTED flag after the session has been established, to determine whether asynchronous messages are supported.

To send an asynchronous message, you must use the IDirectPlay4::SendEx method with the DPSEND_ASYNC flag set in the dwFlags parameter. SendEx will return immediately. Later, the application will receive a DPMSG_SENDCOMPLETE system message with the status result for the sent message. You can disable the notification by setting the DPSEND_NOSENDCOMPLETEMSG flag when the message is sent. The application can track individual messages using the dwMsgID parameter returned by SendEx. This ID can be used to cancel individual messages or to match completion notification messages.

SendEx can be used to assign a timeout value and a priority to a message in sessions that support these properties. You can also cancel messages in the send queue using the IDirectPlay4::CancelMessage or IDirectPlay4::CancelPriority method. Timeout, priority, and cancellation functionality require the DirectPlay Protocol or service provider support, as determined by the GetCaps method.

You should exercise great care when mixing asynchronous and synchronous messages. A synchronous message will be sent after any asynchronous messages ahead of it in the queue and will then block until completed. If supported, timeouts and priorities can be used with synchronous messages. Message tracking by ID and cancellation do not apply to synchronous messages.

Asynchronous messaging does not increase bandwidth or decrease latency.

Understanding DirectPlay Asynchronous Networking with the DirectPlay Protocol

When you are designing a network gaming application, the object of the networking code is to provide the basis for a synchronized game environment to many players. The networking code must handle many different network environments which may provide low or high latency, low or high bandwidth, and varying reliability.

Until now, it has been left to the game developers to determine the characteristics of the network link and adapt the game appropriately. Typically, the solution that most developers have applied is to minimize the amount and frequency of the data sent to the lowest common denominator. Although this approach provides a solution that works well in the harshest environments, it does not provide the best gaming experience to the end user. In many cases, the game could send many more updates or more information per update to enhance the user experience and keep tighter synchronization of the gaming environment. The goal of the network code should be to use all available bandwidth for updating the game state. At the same time, the code must avoid backlogging the connection by sending faster than the link can handle, which leads to higher latencies and worse game synchronization.

The DirectPlay protocol and the new asynchronous DirectPlay capability, supported by SendEx, CancelMessage, and GetMessageQueue, provide an environment where the network code in an application can recognize link capabilities and adapt its behavior to make maximum use of available bandwidth. The DirectPlay protocol uses feedback from the receiver, letting the protocol know how much data is arriving at the receiver. Unlike existing network protocols, the feedback allows the DirectPlay protocol to precisely control its sending rate so as not to build up a backlog of message packets between the sender and the receiver. The DirectPlay protocol also applies this feedback to unreliable messages. This "throttling" of sent messages not only guarantees that the client application does not introduce excessive latencies into the game by creating a backlog, it also allows the client to adapt itself. By viewing the send queue, the client can recognize when it is sending data faster than the link can deliver the data or when the client is not using nearly the available bandwidth. This gives the client the opportunity to adjust its sending strategy to the capabilities of the existing link.

In order to take maximum advantage of the DirectPlay protocol, you should send messages asychronously, using SendEx with the DPSEND_ASYNC flag set. Messages sent asynchronously do not block. When you send a message asynchronously, the send is not completed when the call to SendEx returns. Instead, the send will complete at a later time and inform you by posting a completion message to the receive queue. Asynchronous sends allow the application to have multiple outstanding reliable sends or non-reliable sends. If you use asynchronous sends, the DirectPlay protocol can be sending many messages, both reliable and non-reliable, simultaneously on a link. That is, the protocol does not wait for the completion of one message before sending another. This prevents single packet losses from hurting link use. It also prevents the loss of one reliable packet from slowing the delivery of other reliable messages.

Typically, network games have used non-reliable messages only for transferring a real-time game state. The primary reasons for this strategy are that sending a non-reliable message generally does not block and that many non-reliable messages can be sent without waiting. The DirectPlay protocol enables you to send reliable messages that do not block and to have many outstanding reliable sends on each connection. This means your game can use reliable sends and be just as responsive as a game that uses non-reliable sends. This can greatly simplify the design of your game state updates and allows you to use incremental updates, which are almost impossible with non-reliable sends.

There are a few things you must watch out for when using the DirectPlay protocol:

Migrating from Previous Versions of DirectPlay

The DirectPlay version 6.0 API is fully compatible with applications written for any previous version of DirectPlay. That is, you can recompile your application by using DirectPlay on the DirectX 6.0 SDK without making any changes to the code. DirectPlay supplied with the DirectX 6.0 SDK supports all the APIs and behavior of earlier DirectPlay versions.

For specific information, see:

Migrating from DirectPlay 3.0 or DirectPlay 5.0

If you are migrating to DirectPlay version 6.0 from DirectPlay version 3.0 or version 5.0, you do not have to make any changes. However, you should upgrade your application to use the new IDirectPlay4 or IDirectPlay4A interfaces.

For a list of new features in version 6.0 of DirectPlay, see What's New in DirectPlay 6.0.

Some system messages were changed in DirectPlay 5.0, with new data members added to the end. These new members are continued in DirectPlay 6.0. Applications that need to be backward-compatible with older runtimes should either:

The system message structures that had new members added for DirectPlay 5.0 were:

StructureNew members
DPMSG_CREATEPLAYERORGROUP DPID dpIdParent
DWORD dwFlags
DPMSG_DESTROYPLAYERORGROUP DPNAME dpnName
DPID dpIdParent
DWORD dwFlags

Migrating from DirectPlay 2.0 or Earlier

The names of the DirectPlay DLLs in version 3.0 and later are different from those in previous DirectPlay versions. Applications compiled with DirectX 2.0 or earlier do not use the new DirectPlay DLLs. To use the new DLLs, the application must be recompiled and linked to the Dplayx.lib import library.

It is also recommended that you add the code necessary to make the application lobby-aware. This means that an external lobby or matchmaking program can start the application and supply it with all the information necessary to connect to a session. The application need not ask the user to decide on a service provider, select a session, or supply any other information (such as a telephone number or network address).

In addition, several other new features were added in the DirectPlay version 6.0 API. (For a description of the new features, see What's New in DirectPlay 6.0.) The new features include:

Migrating to the IDirectPlay4 Interface

To migrate an application created with DirectPlay version 2.0 or earlier, carry out the following steps:

  1. Find out if your application was launched from a lobby client. For more information, see Step 2: Retrieving the Connection Settings.
  2. If your application is enumerating service providers, use the EnumConnections method to determine if a service provider is available. If so, call the InitializeConnection method on the service provider. If InitializeConnection returns an error, the service provider cannot run on the system, and you should not add that service provider to the list to show to the user. If the call succeeds, use the Release method to release the DirectPlay object and add the service provider to the list.
  3. Call the QueryInterface method on the IDirectPlay interface to obtain an IDirectPlay4 (Unicode) or IDirectPlay4A (ANSI) interface. The only difference between the two interfaces is how strings in the structures are read and written. For the Unicode interface, Unicode strings are read and written to the member of the structure that is of the LPWSTR type. For the ANSI interface, ANSI strings are read and written to the member of the structure that is of the LPSTR type.
  4. Make all the changes necessary to use the new structures in existing APIs. For example, instead of the following:
    lpDP->SetPlayerName(pidPlayer, lpszFriendlyName, lpszFormalName)
    

    where lpDP is an IDirectPlay interface, use the following:

    DPNAME PlayerName, *lpPlayerName;
    PlayerName.dwSize = sizeof(DPNAME);
    lpPlayerName = &PlayerName;
    
    lpPayerName->lpszShortNameA = lpszFriendlyName;
    lpPlayerName->lpszLongNameA = lpszFormalName;
    lpDP4A->SetPlayerName(pidPlayer, lpPlayerName, 0)
    

    where lpDP4A is an IDirectPlay4A interface. If the application is using Unicode strings (and therefore instantiates an IDirectPlay4 interface), use the following:

    lpPayerName->lpszShortName = lpwszFriendlyName;
    lpPlayerName->lpszLongName = lpwszFormalName;
    lpDP4->SetPlayerName(pidPlayer, lpPlayerName, 0)
    

    where lpDP4 is an IDirectPlay4 interface.

  5. Update the following system messages:
    • DPSYS_ADDPLAYER has been replaced by DPSYS_CREATEPLAYERORGROUP.
    • DPSYS_DELETEPLAYER and DPSYS_DELETEGROUP have been combined in a single DPSYS_DESTROYPLAYERORGROUP message.
    • DPSYS_DELETEPLAYERFROMGRP has been changed to DPSYS_DELETEPLAYERFROMGROUP.
  6. Update your application to generate a DPSYS_SETPLAYERORGROUPNAME message when a player or group name changes, and a DPSYS_SETPLAYERORGROUPDATA message when the player or group data changes.
  7. Update the DPSESSIONDESC structure to DPSESSIONDESC2, and add the new members to the DPCAPS structure.
  8. Update the callback functions for IDirectPlay4::EnumSessions, IDirectPlay4::EnumGroups, IDirectPlay4::EnumGroupPlayers, and IDirectPlay4::EnumPlayers.
  9. Update the manner in which the hEvent parameter is supplied to the IDirectPlay4::CreatePlayer method. In previous versions of DirectPlay, this parameter was lpEvent. DirectPlay does not return an event; instead, the application must create it. This allows the application the flexibility of creating one event for all the players.
  10. Set the DPSESSION_KEEPALIVE flag in the DPSESSIONDESC2 structure if the application needs DirectPlay to detect when players drop out of the game abnormally.
  11. Update your application to create sessions with the DPSESSION_MIGRATEHOST flag. This enables another computer to become the host if the current host drops out of the session. If your application has any special code that only the host runs, then your application should set this flag when it creates a session. It should also add support for the DPSYS_HOST system message. For a list of system messages, see Using system messages.
  12. Become familiar with the new methods of the IDirectPlay4 interface and use them in your application. You might be able to substitute the code where you broadcast player state information to all the other players by using the IDirectPlay4::SendEx and IDirectPlay4::Receive methods.

Debugging DirectPlay Applications

The Debug versions of the DirectPlay DLLs provide debugging output that can be very useful in tracking down the cause of errors DirectPlay might return to your application. Because the debug DLLs perform extra debugging checks at run time, they should always be used during product development. However, in addition to installing the debug DLLs, you need to take additional steps to see DirectPlay's debugging output.

  1. Install the DLLs from the Debug directory of the DirectX SDK.
  2. Run SYSEDIT.EXE.
  3. Select the WIN.INI window.
  4. Insert the following text at the beginning of the WIN.INI file:
    [DirectPlay]
    Debug=8
    BreakOnAssert=0
    
  5. Close the SYSEDIT window, and select Yes when it asks you if you want to save your changes.

Now, run your game as you normally would from within your debugger (usually Microsoft Visual C++). If you have performed these steps correctly, you will see various messages printed in the Debug pane of the Output window, as DirectPlay logs what is happening internally.

Here is an example of the output DPLAY provides.

DIRECTPLAY :: DPLAYX.DLL - version = 4.05.02.0001

DIRECTPLAY ::====> ENTER: DLLMAIN(7006a430): Process Attach: fff96a6b, tid=fff965bf
DIRECTPLAY ::Platform detected as non-NT -- setting flag to ANSI
DIRECTPLAY ::got path = C:\WINDOWS\SYSTEM\dpmodemx.dll

DIRECTPLAY ::got guid = {0F1D6860-88D9-11cf-9C4E-00A0C905425E}

DIRECTPLAY ::Could not read description err = 2

DIRECTPLAY ::got dwReserved1 = 0

DIRECTPLAY ::got dwReserved2 = 0

DIRECTPLAY ::got path = C:\WINDOWS\SYSTEM\dpmodemx.dll

DIRECTPLAY ::got guid = {44EAA760-CB68-11cf-9C4E-00A0C905425E}

DIRECTPLAY ::Could not read description err = 2

DIRECTPLAY ::got dwReserved1 = 0

DIRECTPLAY ::got dwReserved2 = 0

DIRECTPLAY ::got path = C:\WINDOWS\SYSTEM\dpwsockx.dll

DIRECTPLAY ::got guid = {36E95EE0-8577-11cf-960C-0080C7534E82}

DIRECTPLAY ::Could not read description err = 2

DIRECTPLAY ::got dwReserved1 = 500

DIRECTPLAY ::got dwReserved2 = 0

DIRECTPLAY ::got path = C:\WINDOWS\SYSTEM\dpwsockx.dll

DIRECTPLAY ::got guid = {685BC400-9D2C-11cf-A9CD-00AA006886E3}

DIRECTPLAY ::Could not read description err = 2

DIRECTPLAY ::got dwReserved1 = 50

DIRECTPLAY ::got dwReserved2 = 0

By changing the value of Debug in WIN.INI, you can vary the amount of information DirectPlay gives you. The value can be any number from 0 to 9. A value of 0 will tell DirectPlay to log very little; only information about severe errors will be reported. A value of 9 will log every detail of what's happening inside the DirectPlay DLL. As the value is increased, more and more information is logged. In the example above, Debug was set to 8. For normal operation, when you are not specifically debugging a DirectPlay-related issue, it should be set to 0.

Increasing the value of Debug from 0 to 9 can introduce subtle timing changes in your games, so, for example, bugs that appear at level 0 may not be available at level 7.

The BreakOnAssert field can be set to 0 or 1. A value of 0 means you do not want DirectPlay to throw an exception if an internal assertion fails. A value of 1 means you want DirectPlay to throw an exception (and thereby break into the debugger).

Security and Authentication

DirectPlay security allows an application running on a server to authenticate users against a database of known users before allowing them to join a session. Once a user has been authenticated, all communications between the client and server can be done securely either by digitally signing messages (to verify the identity of the sender) or by encrypting the messages.

The following diagram shows DirectPlay security architecture. (SSPI = Security Support Provider Interface, CAPI = CryptoAPI, MS RSA Base CP = Microsoft RSA Base Cryptographic Provider, NTLM = NT LAN Manager.)

DirectPlay security architecture

User and Message Authentication

DirectPlay provides user and message authentication (digital signing) support through the Windows® Security Support Provider Interface (SSPI). This is a standard interface that gives software access to various security packages under the Windows operating system. A security package is an implementation of a protocol between a client and server that establishes the identity of the client and provides other security services, such as digital signing. The default security package that DirectPlay uses is called NTLM (Windows® NT® LAN Manager) Security Support Provider.

This security package is based on the NTLM authentication protocol. NTLM is a shared-secret, user challenge-response authentication protocol that supports pass-through authentication to a domain controller in the server's domain or in a domain trusted by the current domain's domain controller. This protocol provides a high level of security because passwords are never sent out on the network. NTLMSSP ships with the Windows 95 and Windows NT operating systems.

A DirectPlay application can choose to use a different SSPI security package when it creates a session by calling the SecureOpen method. For example, DPA (Distributed Password Authentication) Security Support Provider is another security package that organizations can use to provide membership services to a large customer base (hundreds of thousands). This security package is available through the Microsoft Commercial Internet Services (MCIS) Membership Server.

Message Privacy (encryption/decryption)

DirectPlay provides message encryption support through the Windows Cryptography Application Programming Interface (CAPI). This is a standard interface similar to SSPI that gives software access to various cryptographic packages under the Windows operating system. This architecture allows DirectPlay applications to plug in cryptographic packages that provide the desired level of encryption (40 bit, 128 bit, and so on) legally allowed in the locale of use.

The default CryptoAPI (CAPI) provider for DirectPlay cryptography services is the Microsoft RSA Base Cryptographic Provider version 1.0. The default CAPI provider type is PROV_RSA_FULL. The default encryption algorithm is the CALG_RC4 stream cipher. This provider is supplied by Microsoft and is included with Microsoft Internet Explorer for Windows 95, Windows 95 OSR-2 Update, and Windows NT 4.0 operating system.

A DirectPlay application can choose to use a different cryptographic provider when it creates a session by using the SecureOpen method. Please note that DirectPlay only supports stream ciphers.

For more information about SSPI, NTLM, DPA, MCIS, CAPI, and the RSA Base Cryptographic Provider, see http://www.microsoft.com.

Secure Sessions

User authentication should not be confused with password protection of a session. Authentication is used to verify that the user is allowed access to the server by virtue of having been added to the membership database by the administrator of the server. Only users that are part of the membership database are permitted to join the session. A password can be added to a session, so that only those members who know the password can join a particular session. Additionally, authentication requires a server that supports authentication, while any computer can put a password on a session.

For example, a server on the Internet might have a membership of a thousand users. Anybody can enumerate the sessions that are available on the server, but only members will be able to join sessions on the server. Users who want only their friends (who are members) to be able to join can put a password on their session.

Once a secure server has been set up and an initial membership list has been established, a secure DirectPlay session can be started. Creating a secure DirectPlay session simply requires the server to create a session using IDirectPlay4::Open or IDirectPlay4::SecureOpen and to specify the DPSESSION_SECURESERVER flag in the DPSESSIONDESC2 structure.

A DirectPlay application can choose to use alternate providers when it creates a session by calling the SecureOpen method and specifying the providers to use in the DPSECURITYDESC structure. For a different SSPI provider (for user and message authentication), an application needs to specify the provider name in the lpszSSPIProvider member of the DPSECURITYDESC structure. For a different CryptoAPI provider (for message privacy), an application needs to specify the provider name, provider type, and encryption algorithm in the lpszCAPIProvider, dwCAPIProviderType, and dwEncryptionAlgorithm members, respectively.

When a client enumerates this session, the DPSESSIONDESC2 structure returned by IDirectPlay4::EnumSessions will have the DPSESSION_SECURESERVER flag set. This tells the client that authentication credentials will be required to join the session. If the client attempts to join the session using IDirectPlay4::Open, the security package may allow the user in if the user's credentials were already established through a system log on (for example, through NT LAN Manager). Otherwise, a DPERR_LOGONDENIED error is returned. The application must then collect credentials from the user, put them in a DPCREDENTIALS structure, and try to join the session again by calling SecureOpen, passing in the DPCREDENTIALS structure. SecureOpen is recommended for security.

Within a secure session, all DirectPlay system messages are digitally signed to verify the identity of the sender. Certain system messages that carry sensitive information are encrypted. System messages that originate from one player and need to be broadcast to all the other players in the session are first sent to the server. The server then puts its signature on the message and forwards the message to all the other computers in the session. Player to player messages are not signed by default and are not routed through the server.

An application can choose to sign or encrypt specific player-to-player messages using the DPSEND_SIGNED and DPSEND_ENCRYPTED flags in the IDirectPlay4::Send method. Signed and encrypted player messages are routed through the server and delivered as secure system messages. When a secure message is received by a player, the message will contain flags indicating whether the message came in signed or encrypted. Messages that do not pass the verification are dropped.


Top of Page Top of Page
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.