Query and Reporting SDK

Overview

The GameSpy Server Browsing and Matchmaking systems are based on a central master server that collects information about all of the available game servers on the Internet and delivers that information to clients - either in-game clients based on GameSpy's Server Browsing and Matchmaking Toolkits, or out-of-game clients such as GameSpy Arcade.

The GameSpy Query & Reporting 2 SDK is used to allow your game server to report itself to the GameSpy master server backend and provide information to game clients who query it.

The system works as follows:

Implementation of the Query and Reporting 2 SDK is simple, and you can generally have your game reporting to our backend in a matter of minutes.

Before you begin implementing the SDK, it is important that you confirm that this is the correct SDK for your game, as other options exist. Select the option below that best describes your game, or contact [email protected] if you are unsure before proceeding.

Dedicated server game using the Server Browsing Toolkit for in-game server lists, or only using GameSpy Arcade
If your game follows the "dedicated server" model, where an individual (who is typically not playing in the game on the same machine) starts a server which is available for other users to connect to and play on, and that server is always running, then you should use the Query and Reporting 2 SDK to report that server to our Master Server backend.
Peer-to-Peer game using the Matchmaking Toolkit (Peer SDK) for in-game lobby-based matchmaking
Games using the Peer SDK for lobby-based matchmaking do not need to implement the Query and Reporting 2 SDK directly. The Peer SDK "wraps" the functionality of the QR2 SDK and you can use the API functions provided by Peer for all game listing functionality.
Peer-to-Peer game using only GameSpy Arcade for matchmaking
If your Peer-to-Peer game does not have any in-game matchmaking solution, then you may not need to implement the Query and Reporting 2 SDK at all. Implementation is only required if your game supports "late-entry" - that is, allows players to join a game in progress, even after the initial group of players has started playing. In this case, you should report the game using the Query and Reporting 2 SDK running on the game host to ensure that out-of-game clients can still see the game and join it.

The rest of this document presents a simple, step-by-step set of instructions for implementing the Query and Reporting 2 SDK in your game.

File Manifest

The following files should be included with this package. If any of the files are missing, please contact [email protected].

File
Description
qr2.c
Query and Reporting 2 SDK source
qr2.h
Query & Reporting 2 main header - include this in your source
gr2regkeys.c
Global array of pre-defined key names
gr2regkeys.h
Defines for pre-defined keys
gr2sample.c
Sample "game" in C that uses the SDK
qr2csample.dsp
MSVC project file to build the C sample game

In addition, to build the SDK and samples, you will need to separately download the GameSpy "common code" package, which includes the shared SDK code used by this SDK and others.

When extracting this package, make sure you preserve the directory tree in order to assure that the code builds correctly.

Implementation

Step 1: Determine The Data To Report

Before you begin writing code, you should determine what data you want to report about your game server to clients when they query it. There are three types of data you can report:

Server Data
This is general information about the game in progress, for example - the map that is being played, the type of game, and any specific game settings that would be of interest to players before they joined.
Player Data
This is information about a specific player that is in the current game, for example - the player's name, their current score, what team they are on, and the latency to the game server.
Team Data
This is information about a specific team in the current game, for example - the name of the team and the team score. If your game does not support team play you do not need to report any team information.

Data is reported in a "key / value" format. That is, each piece of data that you want to report has a specific key name associated with it, and when that key is requested you need to return the appropriate value. Key names are short strings that generally describe what the key is. To indicate the difference between types of keys, player keys always end in an "_" character (such as "score_") and team keys always end in a "_t" (such as score_t). Server keys can end in anything other than an _ or _t.

GameSpy has a standard list of keys that you can choose to report for your game. You do not have to report all these keys - in fact, depending on your game type, many of the keys many not be applicable at all. If you have additional data you want to report that is not covered by the standard list of keys, you can register "custom" keys for your game and report any data you want.

The list of standard keys, and descriptions of the data they commonly represent, is below:

hostname
a descriptive host-defined string (can include spaces) that identifies the server (e.g. "Joe's Game!")
gamever
a version specifier (e.g. 1.23)
hostport
the port that the game networking is running on and that the client should connect to. If the game shares a port with the Query and Reporting 2 SDK, you do not need to specify this.
mapname
the map name (either filename or descriptive name)
gametype
string which specifies the type of game, or the mod being played.
gamevariant
if the particular game type has multiple variants, you can report it using this key.
numplayers
numeric string, number of players on the server
numteams
numeric string, number of teams on the server
maxplayers
numeric string, max number of players for this server
gamemode
string which specifies what is going on in the game at that time.
Modes include:
openwaiting
game has not yet started and players can join
closedwaiting
game has not yet started and players cannot join
closedplaying
game is in progress, no joining allowed
openplaying
game is in progress, players may still join
openstaging / closedstaging
Use to report that the game is in staging mode (should generally not be used directly - the Peer SDK handles this automatically).
exiting
server is shutting down
teamplay
number which defines the type of teamplay in use, or 0 for no teamplay. Values > 0 are up to the developer
fraglimit
number of total kills or points before a level change or game restart
teamfraglimit
number of total kills or points for a team before a level change or game restart
timelimit
amount of total time before a level change or game restart occurs (generally in minutes)
timeelapsed
amount of time (in seconds) since the current level or game started
roundtime
amount of time before a round ends (for round based games)
roundelapsed
amount of time (in seconds) that the current round has been in progress
password
0 or not present if no password is required to join, 1 if password is required. Implementation of actual password protection is up to the game developer's network code.
groupid
(optional) If the server being hosted is part of a "group room" then it needs to report which groupid it is part of (as passed in on launch)
player_
a player name (may include spaces)
score_
numeric string that contains the score (kills/points) for a single player
skill_
a skill rating, if applicable, for a single player
ping_
the ping for a player (as measured between the player and the server)
team_
the team a player is on, either numeric or string
deaths_
number of deaths a player has had
pid_
The profileID number for a player (if logged in with the P&M SDK)
team_t
the name for a team
score_t
the score for a team

Keys are identified by a numeric KeyID in addition to their registered names. The Query and Reporting 2 SDK always refers to keys by their KeyID. The KeyIDs for the standard keys can be found as defines in the qr2regkeys.h file.

When you register custom keys for your game, you will need to select your own KeyID values for them. The values 0-49 are reserved, and 50-253 are available for custom keys. When you have decided what custom keys (if any) you need, you should assign KeyID values to them from that range (generally with defines so that you can refer to them easily in your code). The actual values do not matter, but you'll need to make sure you always refer to the same key by the same KeyID.

When you've decided on the custom keys you want to report, you'll need to register them with the qr2_register_key() function. Call this function for each custom key you want to register before initializing the SDK.

void qr2_register_key(int keyid, const char *key);

Simply pass in the keyID you've chosen and the key name for your custom key. Remember that player keys need to end in "_" and team keys need to end in "_t".

Step 2: Create The Callback Function

You need to create a C callback function for each of the 3 key types in order to return the values for those keys when queried by a client. The SDK will call these functions when a user queries the server to get the latest information.

The prototype for the server key callback (from qr2.h) is:

typedef void (*qr2_serverkeycallback_t)(int keyid, qr2_buffer_t outbuf, void *userdata);

A Sample function would be:

void server_key_callback(int keyid, qr2_buffer_t outbuf, void *userdata)
{
//add value to the buffer here
}

KeyID is the id number of the key being requested. You will need to look up the value for that particular game using your game's internal structures, and then add the value to the output buffer.

To add the value, simply call the qr2_buffer_add() or qr2_buffer_add_int() function:

void qr2_buffer_add(qr2_buffer_t outbuf, const char *value);
void qr2_buffer_add_int(qr2_buffer_t outbuf, int value);

Userdata is a pointer that you can set to whatever you want in the qr2_init() function (described below). Generally this is used to store an object pointer or a structure pointer to your game data.

You will also need callbacks for the player and team keys. The prototype for these callbacks is:

typedef void (*qr2_playerteamkeycallback_t)(int keyid, int index, qr2_buffer_t outbuf, void *userdata); 

The extra index parameter specifies the team or player index being requested (0-based).

In addition to the key/value callbacks, there are three additional callback functions you will need to create.

The first is the player/team count callback, which the SDK uses to determine the current number of players or teams (so that it can enumerate through each player or team by index to retrieve a specific key).

The prototype for this callback function is:

typedef int  (*qr2_countcallback_t)(qr2_key_type keytype, void *userdata);        

The keytype parameter will tell you whether the player or team count is being requested. Simply return the current count from the function. If you do not support teams, just return 0 for the team count.

The next callback you need to provide is the key list callback. The SDK uses this callback to determine the complete list of keys you support - both standard and custom keys.

The prototype for this callback function is:

typedef void (*qr2_keylistcallback_t)(qr2_key_type keytype, qr2_keybuffer_t keybuffer, void *userdata);  

The keytype parameter indicates what type of keys are being requested - server, player, or team. You should only add keys of the specified type to the key buffer. Use the qr2_keybuffer_add() function to add each supported key to the keybuffer in turn.

void qr2_keybuffer_add(qr2_keybuffer_t keybuffer, int keyid);

The final callback you will want to provide is the "add error" callback, which is called when the Master Server indicates to the game server that there has been an error adding it to the list. Typically this will only occur if the game server is behind some type of firewall or proxy that is blocking external traffic, and the game does not support NAT negotiation technology.

This callback can also be called if no traffic is received from the master server for approximately 40 seconds after starting reporting. This indicates that the master server is either inaccessible due to network difficulties, is down for maintenance, or is being blocked by an aggressive firewall.

The prototype for this function is below. See the qr2.h header file for descriptions of each parameter.

typedef void (*qr2_adderrorcallback_t)(qr2_error_t error, char *errmsg, void *userdata);  

Step 3: Initialize the SDK

Once you have created the callback functions, you need to initialize the SDK to create the socket needed for queries and heartbeats. This only needs to be done once, and should be done before the actual game starts.

Simply call the qr2_init() function to create the required sockets.

The prototype for qr2_init is:

qr2_error_t qr2_init(qr2_t *qrec, const char *ip, 
int baseport, const char *gamename, 
const char *secret_key,
                  int ispublic, int natnegotiate,
                  qr2_serverkeycallback_t server_key_callback,
                  qr2_playerteamkeycallback_t player_key_callback,
                  qr2_playerteamkeycallback_t team_key_callback,
                  qr2_keylistcallback_t key_list_callback,
                  qr2_countcallback_t playerteam_count_callback,
                  qr2_adderrorcallback_t adderror_callback,
                  void *userdata);

The Query and Reporting 2 SDK can be instantiated multiple times if you are running more than one server in a single process. If you are going to use it this way, you'll need to pass in a pointer to a qr_t variable for the first parameter. The returned value should then be passed into other functions that have a qrec parameter so that the correct instance is used.

If you are running a single game server instance per process (as most games will), then you can simply pass NULL for the qrec parmeter in all of the functions.

IP
An optional dotted IP address for use on multi-homed machines. If you specify IP as NULL, all IP addresses on the machine will be bound. If your game networking supports binding to user-specified IPs, you should make sure the same IP is bound by the Query and Reporting 2 SDK.
baseport
The UDP port that the SDK will attempt to bind to accept queries on. If baseport is unavailable, up to 100 ports above baseport will be scanned to find an open port.
In the highly unlikely event that none of those ports are available, e_qrbinderror will be returned. You can also pass in 0 for baseport, to have a port chosen automatically by the operating system. However, this will make it harder to test the server and scan for it on a LAN, since you never know which query port it is using, and is generally not recommended.
gamename
The unique gamename that you were issued with your secret key.
secret_key
The key you were issued with the gamename. We recommend that you do not just pass in a static string to the function, as this will show up in the executable's string table. Instead, you should set each character in the string individually, as shown in the sample programs. On consoles this is not a concern.
ispublic
Determines whether the server is reported to the GameSpy Master Server. If Ispublic is 0, the server will not be reported. However, it will still respond to queries that come from clients broadcasting on the same LAN, and thus is essentially a "LAN-only" server. You should generally let server operators choose whether a server is public or private.
natnegotiate
a flag that indicates whether your game supports NAT negotiation technology for hosting behind a NAT or firewall. See the appendix on NAT support for further information on this parameter.
userdata
An implementation specific pointer that is passed each time a callback function is called. Use this to pass data structures or object pointers to the callback functions.

The six qr2_* callback parameters are the callback functions you created in the previous step.

If qr2_init() is successful, it will return e_qrnoerror (0), otherwise it will return one of the error codes described in qr2.h.

Step 4: Process Queries in Your Main Loop

Somewhere in your main program (or message) loop, you need to call the qr2_think() function so that the SDK can process any pending server queries.

This function should be called once every 10-100ms. Server queries are used by clients to gauge latency, so slow query replies will make the servers look more lagged than they are.

Step 5: Send statechange Heartbeats Where Needed

Whenever the gamemode of your game changes (going from waiting to playing, open to closed, playing to exiting, etc) you should call qr2_send_statechanged() to send a statechanged heartbeat to the master. What this does is trigger the master server to immediately update the status of the server, instead of waiting up to 60 seconds for the next standard heartbeat to go out. You should be very careful to not send statechanged heartbeats in a tight loop, as this has caused flooding problems in the past when developers put this function in the same place as qr2_think.

Step 6: Cleanup The SDK When Done

When your server is shutting down, you should call the qr2_shutdown() function to close the query socket and do any misc. clean up. A final heartbeat is also sent to the master server automatically, indicating that the game server is being shut down and should be de-listed.

Step 7: Testing

Once you have the SDK implemented, you are ready to test it. For initial testing, it is best to make sure you are running on a machine connected directly to the Internet, with a real, routable IP address and no firewall or proxy. Although NATs and Firewalls are supported (as described in the Appendix), it can be difficult to tell whether a server listing failure is due to a NAT/Firewall or an implementation problem, so testing without the NAT/Firewall to begin with is highly recommended.

Once you've started your server and it has initialized the Query and Reporting 2 SDK, you can check the Development Master Server Web Page to confirm that the server has been listed on our master server.

The page defaults to the "gmtest" gamename. If you are testing under a different gamename/key that you were issued, make sure to input the correct gamename on the page. The page will return a list of all servers currently listed for that gamename.

If your server does not appear on the page, here are some things to check:

Once you've got your server listing on our master server (or if you are trying to diagnose problems with listing), you can use the querytest program included with this SDK to send a query directly to your server and have all the keys/values the server sends back printed out. Use this program to confirm that the server is reporting all the keys and values you are trying to report.

To run querytest, simply specify the IP of the machine the server is running on, and the query port the game is using (as passed to qr2_init).

For example:

C:\Querytest.exe 1.2.3.4 27900

If it reports that the query timed out, then your game may not be implementing the QR2 SDK correctly.

If the data scrolls by too fast, you may either want to pipe the results to "more" or to a file - for example:

C:\Querytest.exe 1.2.3.4 27900 | more
C:\Querytest.exe 1.2.3.4 27900 > out.txt

Appendix: Migration from a Previous Version of Query and Reporting SDK

The Query and Reporting 2 SDK, and the new backend that supports it, offers a number of significant advantages over the previous Query and Reporting SDK:

If your game has already implemented the Query and Reporting SDK, you are not required to switch to the QR2 SDK unless you want to take advantage of the benefits it provides, or use the advanced features of the new Server Browsing SDK, which require QR2.

To convert your game to use Query and Reporting 2, follow these steps:

  1. Implement the new QR2 callbacks, as described in step 1 of the above implementation guide. You can remove the 4 query callbacks used in the original SDK.
  2. Replace your call to qr_init with a call to qr2_init, filling in the appropriate additional parameters.
  3. Replace your calls to qr_process_queries or qr_process_queries_no_heartbeat with a call to qr2_think. Note that there are no longer two processing functions. Whether or not heartbeats is sent is determined by the ispublic parameter of qr2_init.
  4. If you share sockets with the QR2 SDK, you will need to replace your call to qr_parse_query with a call to qr2_parse_query. For the QR2 SDK, the queries will no longer start with the "\" character. You can identify queries by the magic bytes given in the qr2.h file. You also no longer need to NUL-terminate the query data before passing it to qr2_parse_query.
  5. Replace calls to qr_send_statechanged and qr_shutdown with the equivalent qr2 calls.
  6. Calls to qr_send_exiting are no longer needed, as an "exiting" heartbeat is sent as part of qr2_shutdown.

Once you've converted all your code and gotten it to compile, follow the testing guidelines in step 7 above to confirm your new implementation.

UNICODE Support

The GameSpy SDKs support an optional UNICODE interface for widestring applications. To use this interface, first define the symbol GSI_UNICODE. Then, use widestrings wherever ANSI strings were previously called for. When in doubt, please refer to the header files for specific function declarations.

Although the GameSpy SDK interfaces support UNICODE parameters, some items may be stripped of their extra UNICODE information. These items include: nickname, email address, and URL strings. You may pass in widestring values, but they will first be converted to their ANSI counterparts before transmission.

*Note: When using UNICODE, make sure to call qr2_internal_key_list_free after qr2_shutdown in order to free the internal key list created for UNICODE support. Not doing this will lead to memory leaks.

Appendix: NAT and Firewall Support

One of the largest challenges in game networking today is the variety of network topologies in use by players in homes and offices around the world. Technologies created to help users set up home networks and allow corporations to protect their internal networks have not been designed with gaming applications in mind, especially Peer to Peer applications, where a user may act as a host for other players. As more users get broadband connections, and the number of multi-PC households increases, this will only become a larger problem.

Soon we will see another reason emerge for people to purchase these devices - broadband consoles. Since most console users with broadband will also have a PC at home, we can expect a large percentage of online console users to be using a NAT device. Because of the frustration this can cause users, GameSpy has taken an aggressive stance in making sure that users can host multiplayer games no matter what their network topology. Before discussing GameSpy's specific solutions in this area, some background on the technologies and terminology is required.

Connection Types

There are three primary ways a user may be connected to the Internet.

Direct Connection

A user is said to have a "direct" connection if their machine is assigned a single, globally routable IP address for the duration that they are online, that address is not shared with any other users, and no network hardware between the user and the Internet is filtering or dropping any traffic. Direct Connection does not necessarily mean broadband - in fact, most dial-up users are considered Direct Connections as they have a true, routable IP address assigned to them whenever they dial up (even AOL users).

NAT Proxy Connection

NAT (short for Network-Address-Translating) proxies are becoming a more common way for users (and companies) to share a single IP address with multiple computers.

NAT proxies come in either software form (e.g. SyGate, Windows ICS, WinRoute) or hardware (e.g. LinkSys Broadband router, and others).

Computers "behind" the NAT have private IP addresses - e.g. 192.168.0.1 - that are not accessible on the public Internet. The NAT device itself has 1 (or more) public, routable IP addresses. When a computer behind the NAT sends outgoing data or makes an outgoing connection, the NAT "edits" the packet to change the origin address to the NAT IP address, instead of the private IP address. It then chooses a local port on the NAT and uses that as the "public" port for the packet, instead of the port on the private machine. The NAT keeps a table that maps the "public" port on the NAT to the private IP and port that the packet originated from. When the destination machine replies, it goes to the NAT IP address and the "public" port. The NAT machine then forwards the packet to the private machine that matches the mapping, again rewriting the packet headers in the process so that the private machine has no idea anything is in the middle.

Firewalled Connection

A Firewall is a network device that sits on the network between the Internet and the user and looks at all traffic going back and forth to determine which traffic to allow. Firewalls are most often used in corporate environments, but many users have deployed home-based "software" firewalls. Firewalls can be configured hundreds of different ways depending on the desired behavior, however the most common configurations will not allow any "unsolicited" traffic to machines behind the firewall. Only after the machine behind the firewall has contacted an outside machine is any traffic from that outside machine allowed past the firewall.

Some firewalls are much more strict - only allowing outgoing TCP traffic on pre-defined ports (such as web browsing) - any firewall configured this strictly will likely be incompatible with any game. Many firewalls operate in an "invisible" fashion - the users behind the firewalls have publicly routable addresses and have no way to determine that a firewall is blocking traffic. Firewalls may also be combined with NAT devices to provide both sets of functionality.

A special note about software firewalls (such as Zone Alarm or Black Ice): Our experience has shown that this type of software can be very unreliable when used in combination with games and the type of networking that games employ. This is due to both limitations of the software and unexpected interaction with games (such as popping up an invisible dialog during a full-screen game, causing an apparent lock-up). We highly recommend that users disable or at least turn down the security settings on these software firewalls when playing games to avoid problems.

NATs and Firewalls can be further broken down into two categories: Promiscuous and Non-promiscuous.

With a promiscuous NAT or Firewall, when a user sends a packet from their IP and port to a remote machine, a mapping is created on the network device that allows any outside machine to send data back to that user via the mapped port - so once the mapping has occurred, and the mapped port is determined by an outside machine, all other clients can learn about it from the outside machine and connect directly to the protected machine.

A non-promiscuous device is more restrictive - it will only allow incoming data from the specific IP and port that the outgoing data was sent to. Data from any other machines will be dropped.

Most (but not all) NAT devices are promiscuous, and most firewalls are configured to be non-promiscuous.

Hosting Methods

Over the years, a variety of methods have been developed to allow machines behind a NAT or firewall to host services (such as a game server).

Port Mapping

The most basic method is to configure the NAT or firewall device to pass unsolicited incoming traffic to a protected machine automatically. This is typically known as setting up a "port mapping".

For example, if a game accepts Q&R queries on port 27000, and hosts player connections in port 28000, a user could configure their NAT device to pass all traffic directed to those ports on the NAT back to the private address of their machine.

This method has a number of drawbacks:

The primary advantage of this method is that it works without any changes to the game networking and can work with all types of networking - TCP and UDP based.

DMZ Host

Some NAT devices have an option known as "DMZ Host," whereby a specific machine behind the NAT can be designated to receive all unsolicited incoming traffic. This removes the need to set up individual port mappings for each game. However, only one machine can be the DMZ Host at a time, so only a single machine can host any services. In addition, setting a machine as DMZ Host eliminates many of the security features that a NAT provides, since it allows outside users to connect to the machine on any port.

Shared Socket

This method, which has been supported by GameSpy for the past year, allows players behind a promiscuous NAT to host games without any modification to their device or network connection. When a user hosts a game, a heartbeat is sent to the GameSpy master server. The master server automatically determines the "mapped" port that the NAT allocated, and informs other clients about the public address and the mapped port.

Since the NAT is promiscuous, outside clients are able to connect directly to that address and port. The method is called "shared socket" because it requires all game networking to operate on a single UDP socket that is shared with the GameSpy Query & Reporting SDK, since only a single mapping is created. The disadvantages of this method are that it does not support all devices (and it's currently impossible to tell which devices it supports until a user tries and it does not work), and that it requires the game networking use a single UDP socket - which many do, but not all.

NAT Negotiation

Also known as "port guessing", this is the new method supported by the Query and Reporting 2 SDK, as well as the additional NAT Negotiation SDK and the 3rd generation GameSpy Master Server. It requires using a 3rd party server (the NAT Negotiation Server, run by GameSpy) to coordinate the connection between two clients. Both clients connect to the negotiation server, and it determines what port their NAT devices has mapped, and what the next likely port to be mapped will be. The clients use this information to "guess" a port to connect to on the remote address, and begin sending packets to each other. After a few seconds, if the guessing is successful, a UDP connection will be open directly between the clients, and the NAT Negotiation server will not need to pass any data between them.

This method allows clients even behind non-promiscuous device to connect with each other, as long as the device has a predicable port allocation pattern (which currently most devices do). The method also allows developers to use a separate UDP port for each client if desired, although using a single UDP port (and even a shared socket) is still supported. TCP is not supported, because the protocol is not compatible with the type of simultaneous connection being attempted here. There is no known way to splice a TCP connection between NAT clients modifications to the operating system.

Your Options

All of the above methods are compatible with the GameSpy SDKs, and you are free to choose any of them. Below is a description of how you would implement each option, and what it will mean in terms of network device support.

Implementing NAT Negotiation

Supporting the NAT Negotiation option will allow you game to work as a host behind the widest range of NAT devices and firewalls. To enable this, you will need to incorporate the separate GameSpy NAT Negotiation SDK - see that SDK's documentation for details. In the Query and Reporting 2 SDK, you simply need to set the natnegotiate flag in qr2_init to 1, to indicate to our backend that you support this method.

Our backend will determine if the host is behind a NAT or firewall that is blocking traffic, and inform clients when they need to use the NAT Negotiation Server to coordinate a connection to the host. Your game must use UDP-only networking to support NAT Negotiation, although you may use multiple UDP sockets if needed. The GameSpy Transport 2 SDK is fully compatible with NAT Negotiation.

Implementing Shared Socket

The Shared Socket option allows your game to host behind any promiscuous NAT or firewall device. If a user attempts to host behind a non-promiscuous device, the GameSpy Master Server will detect this and send an error message (via the QR2 error callback) to indicate a hosting failure. Details on implementing the shared socket method are in the separate "Shared Socket Implementation" appendix. To support Shared Socket, your game must use a single UDP socket for all client networking. The GameSpy Transport 2 SDK is fully compatible with the Shared Socket method.

Implementing NAT Negotiation + Shared Socket

You can also choose to implement NAT Negotiation and Shared Sockets, by using the NAT Negotiation SDK and the instructions for using shared sockets in the "Shared Socket Implementation" appendix. While this will not necessarily allow any extra players to host that could not otherwise (since the NAT Negotiation SDK works with both promiscuous and non-promiscuous NATs), it does remove the requirement of using the NAT Negotiation server for clients that are behind a promiscuous NAT - likely a large percentage of NAT users. This means faster connections to clients for these servers, less connection overhead, and reduced bandwidth from the master server.

If your game already uses a single UDP socket for all networking or uses the GameSpy Transport 2 SDK, we highly recommend using both Shared Socket and NAT Negotiation for the broadest and most complete NAT and Firewall support.

No Special Implementation

If your networking is not UDP based, or you are not able to implement either NAT Negotiation or Shared Socket, you can choose to do no special implementation. For clients to host you game behind a NAT or firewall, they will need to set up port mapping or DMZ Host options. If they are unable to do this, they will only be able to play as clients, connecting to servers that are hosted on direct connections. You should make sure you thoroughly document this in your manual to avoid any confusion.

Appendix: Shared Socket Implementation

The shared socket method works by using an external 3rd party server to determine the "public" port that the NAT has mapped private IP and port to. In the case of the Query and Reporting 2 SDK, we just use the GameSpy master server. Clients get the list of servers from the master server, including the IP address and port. Because the IP address and port are from the NAT, and are mapped to the private IP and port, clients can connect and be passed through to the private host.

Note that for this to work, the private IP and port used to send heartbeats to the master server must be the same as the IP and port for the game networking. Otherwise outside clients would be able to query the game for information, but not be able to connect to the game port because the NAT would not have a mapping for it. This means the QR2 SDK and the game must share a single UDP port for ALL game networking. TCP will not work as a host behind a NAT, and using multiple ports is not allowed because of the mapping problem.

Some games are already designed to be networked in this manner - the Quake and Unreal Engine games are two examples. They use a single UDP port on the server for all incoming connections and game data, and do not spawn a UDP socket for each client. If you do not have any game networking, you should consider the GameSpy Transport 2 SDK, which fully supports the shared socket method.

Once you have your game networking set up in this manner, integration with the Query and Reporting SDK is simple.

Instead of calling qr2_init, call qr2_init_socket and pass in your UDP game socket. The Query and Reporting 2 SDK will then use this socket to send heartbeats and reply to incoming queries.

However, the SDK still considers the game as the "owner" of the socket, so it will not try to read any data off it. Your game will read UDP datagrams off it as normal, and will need to determine if the packet received is a game packet, or a packet designed for the Query and Reporting 2 SDK. This will probably be easy to determine based on the structure of your game networking packets, but the easiest way to identify an incoming query is to check the two characters - valid queries will always start with the magic numbers defined in qr2.h. Once you've read the data off the socket and identified it as an incoming query, you'll need to call the qr2_parse_query function to parse the query and reply to it. Simply pass the data and length to the function.

Note that you should NOT report a hostport key\value in the Info Callback, since the hostport will be the same as the query port, and will be determined by the NAT.

You should continue to call qr2_think at regular intervals as documented above, since this function is used for sending out heartbeats and other maintenance. Also note that you can use the qr2_init_socket method for all clients - whether or not they are behind a NAT - for clients not behind a NAT it will work fine.

Converted from CHM to HTML with chm2web Pro 2.85 (unicode)