|Microsoft DirectX 9.0|
NAT devices create an implicit port mapping when an internal computer sends a packet to an external computer. Some NAT devices allow any external computer to use this port mapping to send to the internal computer, instead of only forwarding replies sent by the original external target. These are sometimes referred to as "loose NATs." DirectPlay automatically takes advantage of this behavior when the peer host is outside of the NAT. Your application can use the IDirectPlay8NATResolver interface and address components to also allow peer clients behind a loose NAT to join a session hosted behind the same NAT device with external peer clients already connected to the same session.
You must start by implementing an IDirectPlay8NATResolver server that will be accessible from the Internet. Then your game application can add the DPNA_KEY_NAT_RESOLVER component to the device address passed to IDirectPlay8Peer::EnumHosts or IDirectPlay8Peer::Connect. The component data is a string containing the Internet Protocol (IP) address and port for the NAT resolver to use, separated by a colon. For example, the following code specifies to use a server located on port 5678 at the address 18.104.22.168.
WCHAR * wszNATResolver = L"22.214.171.124:5678"; DWORD dwNATResolverSize = (wcslen(wszNATResolver) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER, wszNATResolver, dwNATResolverSize, DPNA_DATATYPE_STRING);
You can also specify a hostname instead of a numerical IP address as shown in the following example.
WCHAR * wszNATResolver = L"resolver.mydomain.com:5678"; DWORD dwNATResolverSize = (wcslen(wszNATResolver) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER, wszNATResolver, dwNATResolverSize, DPNA_DATATYPE_STRING);
For robustness, you might want to have more than one resolving servers to try. You can specify multiple addresses separated by commas as shown in the following example.
WCHAR * wszNATResolvers = L"126.96.36.199:5678,backupresolver.mydomain.com:6789"; DWORD dwNATResolverSize = (wcslen(wszNATResolvers) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER, wszNATResolvers, dwNATResolverSize, DPNA_DATATYPE_STRING);
Each resolver is tried simultaneously for speed, and the first response is used. If no server responds, the IDirectPlay8Peer::EnumHosts or IDirectPlay8Peer::Connect call still succeeds.
Because hosting these resolving servers requires resources, you might want to prevent arbitrary players from using the resolver. This can be achieved with the DPNA_KEY_NAT_RESOLVER_USER_STRING address component. This value is passed directly to the resolver for verification in the DPN_MSGID_NAT_RESOLVER_QUERY message, and it can choose to respond or ignore as appropriate. The following example shows how to do this.
WCHAR * wszPassword = L"MyPassword"; DWORD dwPasswordSize = (wcslen(wszPassword) + 1) * sizeof(WCHAR); hr = pDP8DeviceAddress->AddComponent(DPNA_KEY_NAT_RESOLVER_USER_STRING, wszPassword, dwPasswordSize, DPNA_DATATYPE_STRING);
The peer client's NAT device generates a port mapping for the NAT resolver query. If the NAT resolver elects to respond to the query, DirectPlay will send the mapping's public address back to the querying peer client. The address will then be reported to the host when connecting to the session so external peer clients can contact this joining internal client.
An example usage of NAT resolver address components can be found in the NATPeer sample.