The Anet API

Copyright 1995-2001, Activision

Contents


API Overview

Purpose

The Anet API, like Microsoft's DirectPlay, takes care of the dirty part of writing networked games (e.g. dialing the modem or connecting over the 'Net), and lets you concentrate on writing the game. It supports many kinds of connections: LAN, Internet, modem, direct serial connection, and proprietary gaming networks such as Heat.

Unlike Microsoft's DirectPlay or Apple's NetSprocket, it supports not just Windows 95, but also Windows NT, Linux, Java, MacOS, and DOS.

What's under the hood?

The Anet API includes a library, known as DP, written in C, which handles high-level functions such as player and session management (described by this document), a number of dynamic-link libraries, called Transport DLL's, and a helper app called anetdrop.

Internationalization

In these days of the global Internet, the only reliable way to share player names across the network is to use the Unicode character set. To make this transparant to the programmer, the Anet functions that manipulate names will be upgraded soon to automatically convert between the default character set and Unicode. Following Microsoft's standard approach, each of these functions will have two versions: one that uses a wide Unicode character string, and another that uses a normal 8-bit character string. If you use the latter, the strings will be converted to Unicode for you.

The first function call to support this is dpAccountLogin; others will follow.

The Transport DLL's

A transport DLL is a simple library that provides an unreliable packet delivery service. These can be wrappers around existing API's (as in the case of IPX, DOS Dwango, and Win95 Internet transports), or they can actually implement a packet protocol and talk to hardware (as in the Modem, Null-Modem, and DOS Internet transports). It's not too hard to develop new transport DLL's; if an API exists, and you just need to wrap it into a DLL, you might be able to get it up and running in two days, and finished in a week.

Listing available transport DLL's

Before initializing the DP library, you need to choose a transport DLL. The available transport DLL's can be listed by calling dpEnumTransports(path, callback, context). The callback function dpEnumTransportCallback_t(fname, description, context) returns both a filename and a description. Description->name is usually presented to the user in a menu, as in Mercnet's Connection Type screen. Certain transport DLL's may prefer to be hidden, for instance, the Dwango DLL has the comm_DRIVER_IS_VISIBLE bit of description->capabilities cleared because the Dwango transport can only be used if the game was launched from the Dwango client program.

Given the file name of a transport DLL, the DLL's description may also be retrieved with dpGetTransportInfo(path, &description).

Listing available comm ports / modems

If the DLL is a serial or modem DLL, i.e. if it has the comm_DRIVER_NEEDS_PORTNUM bit set in description->needs during dpEnumTransport's callback, you can retrieve a list of available port names and numbers by calling dpEnumPorts. Under Windows, this will return not just available comm ports, but also available installed modems. It will not return any comm ports which are in use by the mouse or by other programs. You should present the names from this list to the user, and pass the matching port number in to dpCreate in the portnum field of the params argument.

If the user chooses one of the installed modems, dpCreate will then get the init string for the modem from the registry. This is very helpful, since some 'modern' modems have impossible-to-figure-out init strings.

The DP library

The following paragraphs describe the major functions of the library. An API Function Reference is also available. In general, all calls to the library are nonblocking. If an operation involves waiting for an event, a callback function is used to return the result(s) of the operation. The only exception is dpCreate, which may block under some circumstances (DNS lookup, modem dialing, etc.).

Startup

The DP library is initialized by calling dpCreate(&dp, NULL, NULL, "freeze.dat") or by choosing a transport DLL (and possibly a comm port and baud rate), then calling dpCreate(&dp, transportName, params, NULL). The first form restores a session saved with dpFreeze. The second form loads the selected transport DLL, and passes it the given params. If no special parameters are needed, for instance, when using the ipx or internet drivers, params may be NULL. Otherwise, params is a pointer to a commInitReq_t. To initialize this variable, first memset it to zero and set its reqLen field as follows:
commInitReq_t params;
memset(&params, 0, sizeof(commInitReq_t));
params.reqLen = sizeof(commInitReq_t); 
Then fill in fields as required by the driver you're using:

During normal operation, the DP library's dpReceive(dp, idFrom, idTo, flags, buffer, size) function must be called frequently. Certain housekeeping tasks depend on calls to this function to trigger them.

Shutdown

When the user wishes to quit the program, the main loop should call dpShutdown once per loop until it returns something other than dp_RES_BUSY. This gives all the connections, including the connection to the game server, time to shut down properly. When the main loop finally exits, the DP library should be freed by calling dpDestroy. This unloads the transport DLL and frees memory.

Selecting a Game Server

If the user chooses the Internet transport, an Internet game server should be specified by calling dpSetGameServer(dp, masterHostName) before listing games. You can get a list of valid game servers by calling dpRequestObjectDeltas() to monitor servers, then watching for dp_objectDelta_packet_t messages. You will receive a constant stream of messages describing the available servers and their status. Be sure to turn off the messages by calling dpRequestObjectDeltas() again when the user has finished picking a server.

You can also call dpEnumServers(dp, timeout_ms, callback, context) or dpEnumServersEx(dp, timeout_ms, session_type, callback, context) Use a timeout of 2000.

If the file bootserv.txt exists, an initial list of game servers is read in from it (one server per line; ip address or hostname followed by hostname). Otherwise a hard-coded bootstrap list is used. The list of game servers is updated whenever you connect to a game server and use any of the above ways of listing game servers. The current server list is saved in servers.dat between runs.

Server Login

If the user wishes his score to be used to update his skill rating on the game vendor web site, he or she needs to log in after connecting to the game server.

Before logging in, the player must have obtained a username and password from the game vendor web site. API functions have been written to allow account creation within games, but it was not settled whether to use these or a web interface for user account management, and development was never quite completed.

The function dpAccountLogin accepts a username and password, and sends them to the server; the server replies with a message of type dp_ACCOUNT_PACKET_ID explaining whether the login was successful. A login might be unsuccessful for two reasons: the account might still need to be activated, in which case the user needs to visit the account signup web page again, or the username or password might not be valid.

Once the first game using score reporting is deployed, we will add additional functionality that takes advantage of the user's login status, e.g. buddy tracking and user-to-user messaging.

Currently (9/98), the lower 16 bits of the player's userid are available in dp_playerId_t.karma. This can be used to tell whether remote players have logged in. Only if all players in a session are logged in will scores be reported. The field containing the userid will be renamed and will hold the full 32 bits of the userid before the first game using this feature is released.

There are two versions of dpAccountLogin - one for Unicode, and one for the default system character set. If you use the latter, the username and password will be converted to Unicode for you.

Session Management

When a bunch of people get together to play a game, that's called a session.

A person wishing to start a game creates a session by calling dpOpen(dp, s, callback, ...) with the following fields set:

Sessions start out with zero players in them. The callback dpEnumSessionsCallback_t(s, ...) is called when the operation completes.

A person wishing to join a game can do it one of three ways:

Leaving a session
When the user is done with a session, the program leaves the session by calling dpClose(dp). It should then wait for the dpClose to finish; this is done by hanging out in the main loop, calling dpReceive as normal, until about one second after dpReadyToFreeze returns something other than dp_RES_BUSY. This gives all the peer-to-peer connections opened for the session time to shut down properly.

Passing an Open Session Between Programs

A program can save the state of a session by calling dpFreeze(dp, fname). A session can also be saved before it's even started; see anetdrop's -x option. This is needed when using third-party drivers that can only be initialized once.

To restore the state of a saved session, pass the same filename as the last parameter to dpCreate() instead of NULL.

This makes it possible to create separate lobby programs that set up games, and pass them to the game program proper. The game program can use the same interface to pass the session back to the lobby program upon completion, if a debriefing room is desired.

The ID of the local player should be determined by calling dpEnumPlayers() and watching for a player with the dp_EPC_FLAGS_LOCAL flag bit set, or by calling dpGetSessionDesc() to get the current session description, dpGetSessionId() to extract its id, and dpRequestObjectDeltas() to monitor players, then watching for a dp_objectDelta_packet_t message with the dp_OBJECTDELTA_FLAG_LOCAL flag bit set.

See services.htm for more info on launching into a game from 3rd party gaming networks.

See also anetdrop, which lets you start sessions from the commandline and pass them easily to a game program.

Player Management

Anet keeps track of the players in each session, and assigns each player a unique id which will not be reused for any other player during the session. Player id's are 16 bit numbers which tend to start near zero, and tend to increase as players are added. Their most important use is with dpSend and dpReceive, where they indicate destination and source of the message. After opening a session, a player needs to be created by calling dpCreatePlayer(dp, callback, context, name). The callback dpEnumPlayersCallback_t(id, name, flags, context) is called when the operation completes; save the value of id, this is the id of the new player.

To get the name of a particular player, call dpGetPlayerName(dp, id, buf, buflen).

To get the number of players in the currently open session, call dpNumPlayers(dp).

To get a list of the players in the currently open session, call dpEnumPlayers(dp, NULL, callback, context, timeout). The callback dpEnumPlayersCallback_t(id, name, flags, context) returns a description of each player. (To get a list of the players in some other session, call dpEnumPlayers(dp, s, ...), using one of the session descriptions s returned by dpEnumSessions.)

When a player wants to leave the session, call dpDestroyPlayer(dp, id). You can omit this if you're about to call dpClose(), which destroys all the locally created players anyway.

To prevent new players from seeing or joining a session in progress, call dpEnableNewPlayers(dp, FALSE). Call it again with TRUE to re-enable new players.

Score Reporting

Score reporting is an interesting and challenging aspect of producing a successful computer game, since it involves linking the game to a database of very sensitive (at least to the players!) data, and because each game may have very different ideas of what kinds of data it wants to report.

Early game networks such as Heat and Dwango tended to let the game report a single number, or possibly several numbers, as a player's score; the format of the number is more or less rigidly defined by the game network. This was initially supported with the dpReportScore() (for Dwango) and dpReportScoreStart/dpReportScore2/dpReportScoreEnd (for Heat) interfaces, which immediately transmitted score data via a special call to the transport DLL. These interfaces are now deprecated.

More recent networks such as WON prefer to let the game report a blob of data; it is up to the game programmer to describe the blob's format to the WON database programmer so he or she can interpret the score data and display it on the WON web site. This style is supported with the dpReportScoreBuf() interface.

dpReportScoreBuf() takes a player id and a buffer of game-defined score data. It is called whenever a significant change in any player's score occurs (e.g. when a player kills somebody or is killed, or completes a game objective). The Anet library will then use that data to report a full set of score buffers to the game server when appropriate; currently, this is done whenever any player leaves the game.

In order for this interface to support the older game networks, the first three bytes of the score buffer must follow a standard format; see the comment for dpReportScoreBuf(). The rest of the score buffer may be formatted any way the game likes, but should be kept compact; the format of the buffer will need to be documented so the score server database programmer can decode the buffer.

When connected to an Anet game server, scores will only be reported if all players in the session have logged in to the game server.

Group Management

If a program wants to send messages often to a particular group of players, a player group can be created by calling dpCreateGroup(dp, &idGroup, name). The new group's id will be placed in idGroup. This function call is currently allowed only on the master.

When the group is no longer needed, call dpDestroyGroup(dp, id).

To get a list of the groups in the current session, call dpEnumGroups(dp, NULL, callback, context, timeout). The callback dpEnumPlayersCallback_t(id, name, flags, context) returns a description of each group. (Only groups in the current session can currently be listed.)

To add a player to a group, call dpAddPlayerToGroup(dp, idGroup, idPlayer).

To delete a player from a group, call dpDeletePlayerFromGroup(dp, idGroup, idPlayer).

To get a list of the players in a particular group, call dpEnumGroupPlayers(dp, idGroup, NULL, callback, context, timeout). The callback dpEnumPlayersCallback_t(id, name, flags, context) returns a description of each player. (Only players in groups in the current session can currently be listed.)

Sending/Receiving Datagrams

dpSend() and dpReceive() provide a way to send data between players. Both source and destination are identified by the 16 bit player id number returned by dpCreatePlayer.

By default, packets are sent immediately. To reduce network overhead, call dpFlush() at the end of each frame's network processing to send the current frame's packets and start buffering the next's.

To send a packet to a player, call dpSend(dp, idFrom, idTo, flags, buf, &msglen) where idFrom is the id of the player sending the message, and idTo is the id of the player who should receive the message.

Note: Currently, the first byte of buf must not be ASCII 'd', and there must be six bytes of extra space at buf[msglen] to buf[msglen+5]. Do not report these extra bytes in the msglen parameter!

The 'flags' parameter determines whether the packet will be sent reliably (dp_SEND_RELIABLE) or unreliably (dp_SEND_UNRELIABLE). Packets sent reliably are guarenteed to be delivered correctly, and in the order originally sent. Packets sent unreliably will only be sent once, might arrive in the wrong order, and might not arrive at all.

If your game is an action game that can use dead reckoning (aka interpolation) to recover from lost packets, you should use unreliable transmission, as it gives the best performance.

If your game is a turn-based game internally, and requires all machines in the game to stay perfectly in sync, you probably need to use reliable transmission.

There is a limited amount of space for buffering outgoing packets (currently, 16 packets' worth). Be sure to check the return value of dpSend; if it returns dp_RES_FULL, your packet has not been accepted for transmission, and you need to send it later, after letting your main loop call dpReceive() for a while to empty the outgoing buffer. Many programmers are used to ignoring the return value of 'send' functions. Don't assume you can get away with this.

To receive a packet, set msglen to sizeof(buf), then call dpReceive(dp, &idFrom, &idTo, flags, buf, &msglen). The return value will be dp_RES_OK if a packet was received. In that case, msglen will contain the number of bytes received, and idFrom and idTo will be set to the id's of the sender and recipient.

Note: the buffer passed to dpReceive or dpSend should be dpio_MAXLEN_UNRELIABLE bytes long, regardless of the size of the messages being sent or received.

Note: dpReceive must be called frequently, as it performs internal housekeeping. You should define a "doNetworking" function that loops, calling dpReceive and processing the messages it returns, until it returns dp_RES_EMPTY. doNetworking should be called at least once per frame. If possible, try to arrange for doNetworking to be called once in the middle of your rendering code, to allow retransmission requests to be handled promptly.

Shared Memory / Player Variables

Each player (or group) may have associated with it any number of variables. Each variable is identified by an integer key between 0 and dp_PLAYERDATA_KEY_USERMAX, and can be up to dp_PLAYERDATA_LEN_MAX bytes long. To set a variable, call dpSetPlayerData(dp, id, key, buf, buflen, 0). That variable is then sent to all present and future nodes in the session. When it arrives at each node, dpReceive returns a dp_user_playerData_packet_t to announce the variable's value change. Variable values may be retrieved at any time by calling dpGetPlayerData(dp, id, key, buf, &buflen, 0).

Variables can also be sent to individual players, for instance, variables holding entire game levels so large that they take a long time to transfer might be sent only on demand. To do this, first set the variable as normal, but with a flags parameter of dp_PLAYERDATA_FLAG_NOFLOOD, then call dpSendPlayerData(dp, idPlayer, key, idTo).

If a variable is set to a first one value, then another, some nodes might never see the first value.

Variables can be small or large. Good uses of player variables might include

Variable values are preserved across dpFreeze/dpCreate cycles (however, callbacks are not; set any necessary callbacks immediately after the call to dpCreate()). Variables are sent in a way that avoids monopolizing modem bandwidth.

Note: Player variables are not suitable for shared variables that are not clearly associated with a given player. This is because when the player disappears, so do the variables. Variables that are clearly associated with the game as a whole should probably be carried by normal packets rather than player variables. A new kind of shared variable may be introduced in future releases to deal with this need.

Performance Measurement

You can ask the system to actively measure the interplayer latency and packet loss by calling dpSetPingIntervals(). The system also makes latency measurements whenever a reliable packet is acknowledged on the first try.

To receive continuous updates for all players in the current session, call dpRequestObjectDeltas() with the key dp_KEY_PLAYER_LATENCIES while monitoring player deltas. Then whenever a new latency/packet loss measurement is made, a player delta containing the new values is queued for pickup with dpReceive().
You can also retrieve the most recent measurement for a given player with dpGetPlayerCaps().

An obsolete way to immediately trigger pings and report on the results of previous pings is to pick a unique query id (or karma), and call dpPingUser(dp, dest, karma, cb). Later, the callback cb will be called like this: dp_ping_callback_t(karma, time_t avg_ms, int loss_pct) giving the average delay in milliseconds and the average packet loss in percent. Ignore the callback if it has the wrong karma. Only one user at a time can be checked this way.
This interface is obsolete and may go away in a future release.

See also the bandwidth report produced in the log file and the packet loss simulation feature accessible via the .ini file.

Launching and Being Launched

When the user picks a game in the Netshell and says GO, that's called launching the application. The application that did the launching usually terminates immediately to free system memory for the application being launched. A tiny program called the Stub (which is part of the Netshell) then waits for the application to terminate, and starts the Netshell again if appropriate. Applications need to follow a few rules for this to work properly. The rules are stated in the Netshell doc, but briefly, they are:

You can get a list of launchable applications by calling dpEnumApp(), which calls a callback once for each installed Anet-capable application. The dp_appParam_t parameter of the callback can be used later to launch the app.

Programs that launch other Anet applications - and this can happen even inside a game, if the game is broken up into two executables - should call dpLaunchApp to start the second application, then immediately call dpDestroy(dp, TRUE) to close the network driver without hanging up the phone line, and then call dpExitFromApp(0).

Programs launched by other Anet applications should call dpDestroy(), then call dpExitFromApp(int exit_status) when they want to terminate.

dpExitFromApp notifies the Anet Stub whether or not it should start Netshell again (yes if exit_status is nonzero), then terminates the current program. (Under Microsoft Windows, it calls exit(exit_status). Under MacOS, it sends an AppleEvent to the stub, then calls ExitToShell().)

Downloading Patches

The Anet library and the game servers together support downloading patches via the Internet. This feature is commonly called the Updater. To use this feature, a game using the Anet library connects to a game server, and calls dpGetAppVersion. The return code dp_RES_OK indicates an update is needed, and that the game should minimize, and call dpDownloadUpdate, which reads a list of update servers from updaters.dat, connects to one at random (or to updater.activision.com, if the file is not found), downloads the patch, and runs it. The game should exit if dpDownloadUpdate returns dp_RES_OK (indicating Patch Downloaded And Running).

Debugging Aides

Checking return values

Many programmers often forget to check return values, then have trouble figuring out where their program started to have problems. An easy way to check Anet return values during development is to use the standard C library's assert function in those places where you normally would be tempted to ignore error codes. For instance,
#include <assert.h>
dp_result_t dperr;
...
   dperr = dpEnumPlayers(dp, ...);
   assert(!dperr);
When it's time to build the final version of the program, you can turn off the asserts by defining the NDEBUG preprocessor symbol, if assert's overhead is objectionable.

This works for programs trying to conform to both Anet and Microsoft's API, too, e.g.

#include <assert.h>
#include "dpcompat.h"
DP_HRESULT dperr;
...
   dperr = DP2_EnumPlayers(dp, ...);
   assert(!DP_FAILED(dperr));

.ini (Configuration) files

A few parameters are read in at startup time from a file, by default, dp.ini. To change the name of the file that will be read, call dpini_SetFile(char *fname) before the first call to DPRINT or to dpCreate. Here is an example .ini file:
[DEBUG]
pktloss=33     ; set to simulate Internet by dropping a % of incoming packets
All=1          ; Turn on DPRINT
ToLog=1        ; Set to 1 to send DPRINT messages to Logfile
ToALog=0       ; Set to 1 to flush after each DPRINT; helpful if crashing
ToStdout=0     ; Set to 1 to send DPRINT messages to stdout
Logfile=dp.log ; name of log file
nouploadcrash=1; don't upload any crash records found at dpSetGameServer time
At the moment, you can read parameters from the .ini file by calling findINISection(char *section) to select a section, and readINIParameter(char *name, int keepWhitespace) to read a parameter from the selected section. This interface will change in later releases.

Simulating Internet packet loss

Many programs work fine on the LAN, but fail on the Internet, where packet loss under normal circumstances can reach 40%. The function call dpio_forceRxDropPercent() can be used to simulate packet loss. dpio_forceRxDropPercent() will be called automatically on startup if the current .ini file contains a pktloss line in the [Debug] section, e.g.
[DEBUG]
pktloss=20			; one-way packet loss, percent
In general, networked games should function correctly with pktloss set to 20 even at their maxium number of players, and should be thoroughly tested both with and without packet loss before release.

(Note: since pktloss controls one-way packet loss, it's related to round-trip packet loss with the formula rtpktloss = (1 - (1 - pktloss)^2); thus pktloss of 20 percent corresponds to a round-trip packet loss of 1 - (1 - .20)^2) = 1 - .64 = .36 = 36 percent.)

Simulating Internet latency

Another reason games work poorly on the Internet is because packets take a while to get from here to there. The biggest source of latency is player's modems. Another source of latency is the Internet "backbone", the high-speed links between Internet Service Providers.

The dp library can simulate both of these effects, if desired. To turn these behaviors on, edit the current .ini file and in the [Debug] section, insert a bitsPerSec line and a backbone_ms line, e.g.

[DEBUG]
bitsPerSec=28800    ; Simulated modem speed, in bits per second
backbone_ms=50      ; Simulated non-modem latency, in milliseconds
To disable modem delay simulation, don't set those parameters in the .ini file, or set them to zero.

Simulating Crashes or Program Faults

To test how your program handles crashes, create a file dp.ini containing
[DEBUG]
fault=1				; Induce an invalid address exception
Then start the multiplayer part of your program, and try to create a game. This causes the Anet library to try to access through a null pointer, so you can verify quickly that your debugger or crash logger is set up properly.

Running Multiple Anet Programs on one machine

Often, it is convenient to run more than one Anet program on your workstation at a time, for testing purposes. For instance, if you want to stress-test your program, you might want to write a single batch file that simulates several players running your program on their computers and playing together.

This is currently supported with the internet, modem, serial, and loopback drivers, but not with the IPX driver, or with Heat, Mpath, or any other 3rd party gaming network driver.

See the example batch files LOOPD.BAT, SPAWN3.BAT, and SPAWN.BAT in the demo/gtest/tests folder. Here's an example session. (The text in bold is what you type.)

C:\anet> cd demo\gtest\tests
C:\anet\demo\gtest\tests> spawn3      Starts a three player gtest fest
C:\anet\demo\gtest\tests> loopd       Starts gtest in this window
gtest> sessions             
gtest> join spawn3                    Join the game started by spawn3
gtest> players
gtest> ping user0                     Measure latency to one player
gtest> newplayer me
gtest> remotequit                     Tell all the windows to quit
When you try this example, you'll notice that ping reports about 200 milliseconds of latency for each player, even if you don't have modem simulation turned on. That's because the LOOP driver introduces about 100 milliseconds of latency in each direction.

Logfiles

Built in to the dp library is a logfile system which can be quite helpful in resolving problems. Often, when a dp, dpio, or comm dll function returns an error code, it puts a descriptive error message in the log file. Functions also generate log messages in normal operation, to help you understand the context when errors do occur.

The log file is enabled in the debugging version (anetdlld.dll) but not the production version (anetdll.dll) of the library. Also, you need to have a .ini file that has "All=1" and "Tolog=1" lines. The default name for the .ini file is "dp.ini". This can be changed at runtime; for instance, the test program gtest uses gtest.ini as the name of its .ini file. To change the name of the .ini file, add the line

 dpini_SetFile("foo.ini");	/* optional; default is dp.ini */
in your code near the beginning of the program.

You can add your own messages to the log file as follows:

 DPRINT(("Test Message: time of day is %d\n", clock()));
and compiling with the preprocessor symbols DPRNT or _DEBUG defined. (If you don't define DPRNT or _DEBUG, the DPRINT macro doesn't generate any code.)

The debugging log file is a little cryptic, but usually has lines of the form

FUNCTION NAME: message
where FUNCTION NAME is the name of an Anet function your program called (or an internal function). The trick to tracking down problems is often to figure out which kind of lines you are interested in, then look at just those lines with a filter like egrep, e.g. if you are interested in all lines that have dpio_put or the word "session", you could use egrep "session|dpio_put" dp.log to see just those lines. (A good egrep can be downloaded along with the rest of the Gnu tools at ftp://ftp.cygnus.com/pub/gnu-win32/latest/.)

Packet Trace Logfiles

The debugging versions of most of the drivers, e.g. winets2d.dll, will save a complete log of all packets sent and received to the ASCII file output.1, and rename the previous output.1 to be output.2, etc. A perl script to analyse this output file is available upon request; it shows the bandwidth used for each second of the game's run. This is quite handy when trying to optimize your game's network performance.

Mysterious crashes in release builds

If your game runs well when compiled for debugging, but crashes mysteriously in release builds, it is probably due to a calling convention mismatch in a callback function. Be sure you declare any callbacks using the dp_PASCAL keyword.

In nasty cases, we can provide a version of anet2.dll that has logging enabled but is not compiled for debugging; it sometimes helps resolve the mystery to see what Anet was trying to do when your program crashed.


System Message Reference

Last update: 1 Jul 1998
Note: this may lag slightly behind anet.h.

System messages are messages returned by dpReceive() with a sender of dp_ID_NAMESERVER and a recipient of dp_ID_BROADCAST. The message data consists of a dp_packetType_t optionally followed by message-specific data. The message types and their message-specific data (if any) are listed below.

Message data common to several message types:
typedef struct {
    dpid_t      id;
    dp_karma_t  karma;
    byte        adr[dp_MAX_ADR_LEN];
    char_t      name[dp_PNAMELEN];
} dp_playerId_t;

typedef struct dp_groupId_s {
    dpid_t      id;                 /* msgs to id reach all members */
    dp_karma_t  karma;              /* used to verify group identity */
    char_t      name[dp_PNAMELEN];  /* name of group */
    dp_karma_t  sessionKarma;       /* group only valid within this session */
} dp_groupId_t;

dp_USER_ADDPLAYER_PACKET_ID
typedef dp_playerId_t dp_user_addPlayer_packet_t;
Notification that a player has joined the currently open game. Returned by dpReceive whenever it notices a new player. This message is not handled by dp at all; it is simply a courtesy to the user program so it doesn't have to call dpEnumPlayers all the time.
dp_USER_DELPLAYER_PACKET_ID
typedef dp_playerId_t dp_user_delPlayer_packet_t;
Notification that a player has left the currently open game. Returned by dpReceive whenever it notices that a player has vanished. This message is not handled by dp at all; it is simply a courtesy to the user program so it doesn't have to call dpEnumPlayers all the time.
dp_USER_ADDGROUP_PACKET_ID
typedef struct dp_groupId_s dp_user_addGroup_packet_t;
Notification that a group has been created. Returned by dpReceive whenever it notices a new group.
dp_USER_DELGROUP_PACKET_ID
typedef struct dp_groupId_s dp_user_delGroup_packet_t;
Notification that a group has been deleted. Returned by dpReceive whenever it notices a group disappear.
dp_USER_ADDPLAYERTOGROUP_PACKET_ID
typedef struct {
    dpid_t      dpIdGroup;
    dpid_t      dpIdPlayer;
    dp_karma_t  sessionKarma;       /* group only valid within this session  */
} dp_addPlayerToGroup_packet_t;
Notification that a player has joined a group. Returned by dpReceive whenever it notices a new player in a group.
dp_USER_DELPLAYERFROMGROUP_PACKET_ID
typedef dp_addPlayerToGroup_packet_t dp_delPlayerFromGroup_packet_t;
Notification that a player has left a group. Returned by dpReceive whenever it notices that a player has vanished.
dp_USER_HOST_PACKET_ID
Notification that the local machine is now the session host. There is no payload data associated with this packet type.
dp_USER_PLAYERDATA_PACKET_ID
Notification that a group or player variable has been assigned a new value.
typedef struct {
	size_t len;
	dpid_t id;
	unsigned short key;
	void *data;
} dp_user_playerData_packet_t;

DP_OBJECTDELTA_PACKET_ID
Packet carrying object change info.

/* Structure to hold info about a server.  To be used by a future
 * dpEnumServersEx(), and by the Java interface.
 */
typedef struct dp_serverInfo_s {
	short len;						/* length of this structure */
	short rtt_ms_avg;				/* Average round trip time, millisec */
	short loss_percent;				/* Average packet loss */
	short cur_users;				/* people currently connected */
	short max_users;				/* max # allowed to connect */

	char hostname[64];				/* ASCII server name (often hostname) */
	dp_species_t sessType;			/* session type given in dpEnumServersEx()*/
	short cur_sessTypeUsers;		/* people currently in sessType sessions */
	char reserved[16];				/* for internal use */
} dp_serverInfo_t;

/* The kinds of objects that can be monitored with dpRequestObjectDeltas(). */
typedef union {
	dp_playerId_t   p;
	dp_session_t    sess;
	dp_serverInfo_t serv;
} dp_object_t;

#define dp_OBJECTDELTA_PACKET_ID		dppt_MAKE(dp_PACKET_INITIALBYTE,'+')
#define dp_KEY_MAXLEN 12    /* Keys must be 12 bytes in length or less. */
#define dp_OBJECTDELTA_FLAG_LOCAL 1	/* set if object created by this machine */
#define dp_OBJECTDELTA_FLAG_INOPENSESS 2	/* player in session hosted or joined by this machine */
#define dp_OBJECTDELTA_FLAG_ISHOST 4	/* player is master of session */

typedef struct {
	short pktloss PACK;		/* player deltas include a loss in percent */
	short latency PACK;     /* player deltas include a latency in ms */

	long flags PACK;		/* one or more of dp_OBJECTDELTA_FLAG_* */

	/* If an object is being created, status = dp_RES_CREATED.
	 * If an object is being changed, status = dp_RES_CHANGED.
	 * If an object is being deleted, status = dp_RES_DELETED.
	 */
	dp_result_t status PACK;

	/* For players,  key is {dp_KEY_PLAYERS, reserved bytes... }
	 * For sessions, key is {dp_KEY_SESSIONS, reserved bytes...}
	 * For servers,  key is {dp_KEY_SERVERPINGS, reserved bytes...}
	 */
	short keylen PACK;
	char key[dp_KEY_MAXLEN] PACK;/* long key of context for following data */

	/* Subkey is reserved. */
	short subkeylen PACK;
	char subkey[dp_KEY_MAXLEN] PACK;

	/* For players,  data is dp_playerId_t
	 * For sessions, data is dp_session_t 
	 * For servers,  data is dp_serverInfo_t
	 * If status == dp_RES_CHANGED, data is after change.
	 */
	dp_object_t data;
	/* nothing may go here; data is variable length */
} dp_objectDelta_packet_t;

Function Reference

Last update: 22 September 1998
Note: this may be somewhat incomplete; anet.h contains the complete definitions (but is a little messier to read).

dpAccountLogin, dpAccountLoginA, dpAccountLoginW

DP_API dp_result_t DP_APIX dpAccountLoginW(
	dp_t *dp,
	const wchar_t *username,
	const wchar_t *password);

DP_API dp_result_t DP_APIX dpAccountLoginA(
	dp_t *dp,
	const char *username,
	const char *password);
After using dpSetGameServerEx(), call this function to log in to an existing account on the server. dpReceive() will later return a packet of type dp_ACCOUNT_PACKET_ID to indicate success or failure, or to tell you to call dpAccountActivate().

'username' is the user name specified by the user in dpAccountCreate(). 'password' is the password set by the user in dpAccountCreate().

dpAddPlayerToGroup

DP_API dp_result_t DP_APIX dpAddPlayerToGroup(
	dp_t	*dp,
	dpid_t	idGroup,
	dpid_t	idPlayer);
Add player idPlayer to the group idGroup.

dpCastoffSession

DP_API dp_result_t DP_APIX dpCastoffSession(dp_t *dp);
Cast off an open session - it stays open, but is no longer the "current session".
After this, you can call dpOpen() to create a new session.

Caveats:
Intended for use only on game servers.
Can only be called on the master.

dpCheckAppVersion

DP_API dp_result_t DP_APIX dpCheckAppVersion(dp_t *dp, dp_appParam_t *app);
Determine whether application *app needs to be updated.
Returns latest app version from game server in *app.
Not needed by most user programs.

Returns dp_RES_OK if update needed,
dp_RES_EMPTY if no update needed.

dpClose

DP_API dp_result_t DP_APIX dpClose(
	dp_t *dp);
Close an open session.

dpCommThaw

DP_API dp_result_t DP_APIX dpCommThaw(dp_t **pdp, FILE *thawfp, dpCommThawCallback_t cb, void *context);
Given a file pointer pointing to a dp_launchParams_t, initialize a dp_t and connect to or create a game session specified by the file.
Do not return until the session has been established and a player has been created.
During any delays, this function will call the given callback periodically to both inform the caller of its progress joining the session and to give the caller a chance to abort (by returning 0).

dpCreate

DP_API dp_result_t DP_APIX dpCreate(
	dp_t **pdp,
	dp_transport_t *transport,
	commInitReq_t *params,
	char *thawFilename);
Create a dp_t.
This is the very first step in using the dp layer.
Loads and initializes the given comm driver.
If thawFilename is NULL, use the params argument to initialize communications. (Consult the comm_driverInfo_t.needs field returned by dpEnumTransports to determine which fields of params must be filled in.)

If thawFilename is not NULL, restore from disk the session previously saved by dpFreeze or anetdrop.exe, and ignore the params argument. Note: this does not restore any callbacks or enumerations that were in progress when dpFreeze was called. Also, some transports (e.g. modem and Heat) do not allow restoring frozen sessions.

dpCreateGroup

DP_API dp_result_t DP_APIX dpCreateGroup(
	dp_t	*dp,
	dpid_t *id,
	dp_char_t	*name);
Add a new group to the currently open session.
Can only be called on game host!

dpCreatePlayer

DP_API dp_result_t DP_APIX dpCreatePlayer(
	dp_t	*dp,
	dpEnumPlayersCallback_t cb,
	void	*context,
	dp_char_t	*name);
Add a new player to the currently open session.
Calls given function when user is created and its dpid becomes available.
On failure, either returns dp_RES_FULL immediately, or calls callback with dp_ID_NONE during later call to dpReceive.

dpDeclareLobby

DP_API dp_result_t DP_APIX dpDeclareLobby(
	dp_t *dp,
	int flags);
Declare that the current session is a lobby.
May only be called by host.
The parameter 'flags' should be set to 0 for a peer lobby, and to 1 for a server lobby.
Bug: This function is incorrect. Please talk to Dan about how to indicate that a session is a lobby. The basic idea is to use the dp_SESSION_FLAGS_ISLOBBY flag when creating the session.

dpDeletePlayerFromGroup

DP_API dp_result_t DP_APIX dpDeletePlayerFromGroup(
	dp_t	*dp,
	dpid_t	idGroup,
	dpid_t	idPlayer);
Delete player idPlayer from the group idGroup.

dpDestroy

DP_API dp_result_t DP_APIX dpDestroy(
	dp_t *dp,
	int flags);
Destroy a dp_t.
Shuts down and unloads the comm driver (!).
If the flags argument is non-zero, does not hang up the phone line (if any).

dpDestroyGroup

DP_API dp_result_t DP_APIX dpDestroyGroup(
	dp_t   *dp,
	dpid_t id);
Destroy the given group; removes the group from the game session.
The dpID will not be reused during the current session.
Can only be called on game host!

dpDestroyPlayer

DP_API dp_result_t DP_APIX dpDestroyPlayer(
	dp_t   *dp,
	dpid_t id);
Destroy the given player; removes the player from the game session.
The dpID will not be reused during the current session.

dpDownloadUpdate

DP_API dp_result_t DP_APIX dpDownloadUpdate(dp_t *dp, const dp_appParam_t *app);
Call to download the patch corresponding to the given product, but only if dpGetAppVersion says you need to. app is the structure filled in by dpGetAppVersion.
If it returns dp_RES_OK, you should exit your application immediately!
See also updater.htm (contact Dan Kegel if you need this).

Returns dp_RES_OK if patch is downloaded and running;
dp_RES_EMPTY if no patch needed,
dp_RES_BAD if can't find updater dll (anet/dkupddll.dll).
dp_RES_USER_CANCELLED if user offered patch, but declined it.

dpEnableNewPlayers

DP_API dp_result_t DP_APIX dpEnableNewPlayers(
	dp_t *dp,
	int enable);
Enable or disable new players from entering the game.
May only be called by host.
If called with FALSE, session packets will no longer be broadcast, and add player requests will be ignored.

dpEnumApp

/*---------------------------------------------------------------------- Function type called for each application enumerated by dpEnumApp(). ----------------------------------------------------------------------*/ typedef void (dp_FAR dp_PASCAL *dpEnumAppCallback_t)(dp_appParam_t *appParam, void *context); Callback used to list servers and the round trip delay to them.

dpEnumApp

DP_API dp_result_t DP_APIX dpEnumApp(dp_t *dp, dpEnumAppCallback_t cb, void *context);
Enumerate applications installed on this machine.
Callback is called once for each application found.
Callback ends when dpEnumApp() exits.

dpEnumGroupPlayers

DP_API dp_result_t DP_APIX dpEnumGroupPlayers(
	dp_t *dp,
	dpid_t groupId,
	dp_session_t *s,
	dpEnumPlayersCallback_t cb,
	void *context,
	long timeout				/* How long in milliseconds to wait. */
	);
Calls the given function once for each player in the given group, then calls the given function once with dp_ID_NONE to indicate end of list.
If s is NULL, lists the players in the current session.
If s is not NULL, it must be a value from dpEnumSessions.

s must currently be NULL - that is, you can't yet enumerate the group members of sessions you haven't joined.

dpEnumGroups

DP_API dp_result_t DP_APIX dpEnumGroups(
	dp_t *dp,
	dp_session_t *s,
	dpEnumPlayersCallback_t cb,
	void *context,
	long timeout				/* How long in milliseconds to wait. */
	);
Calls the given function once for each group in the given session, then calls the given function once with dp_ID_NONE to indicate end of list.
If s is NULL, lists the group in the current session.
If s is not NULL, it must be a value from dpEnumSessions.

s must currently be NULL - that is, you can't yet enumerate the groups of sessions you haven't joined.

dpEnumPlayers

DP_API dp_result_t DP_APIX dpEnumPlayers(
	dp_t *dp,
	dp_session_t *s,
	dpEnumPlayersCallback_t cb,
	void *context,
	long timeout				/* How long in milliseconds to wait. */
	);
Calls the given function once for each player in the given session, then calls the given function once with dp_ID_NONE to indicate end of list.
If s is NULL, lists the players in the current session.
If s is not NULL, it must be a value from dpEnumSessions.

dpEnumPlayersEx

DP_API dp_result_t DP_APIX dpEnumPlayersEx(
	dp_t *dp,
	dp_session_t *s,
	dpEnumPlayersExCallback_t cb,
	void *context,
	long timeout				/* How long in milliseconds to wait. */
	);
Calls the given function once for each player in the given session, then calls the given function once with dp_ID_NONE to indicate end of list.
If s is NULL, lists the players in the current session.
If s is not NULL, it must be a value from dpEnumSessions.

dpEnumPorts

DP_API dp_result_t DP_APIX dpEnumPorts(const dp_transport_t *transport, commPortName_t *ports, int maxports, int *pnPorts);
Call this during or after dpEnumTransports, but before dpCreate, and only on transports which have the comm_INIT_NEEDS_PORTNUM bit set in comm_driverInfo_t.needs.
Present the returned list of port names to the user, then use the matching portnum as the value for commInitReq_t.portnum in dpCreate.

On entry, transport is the name (from dpEnumTransports) of the transport to query. ports is an array to be filled with portnames. maxports is the size of the ports array.

On exit, ports is filled with descriptions of the available ports. *pnPorts is set to the number of portnames placed in the ports array.

dpEnumServers

DP_API dp_result_t DP_APIX dpEnumServers(
	dp_t *dp,
	long timeout,				/* How long in milliseconds to wait. */
	dpEnumServersCallback_t cb,
	void *context);
Calls the callback function (cb) once with a description, including the server's hostname and delay, of each game server that this object could connect to.
Servers may be reported multiple times.
Finishes by calling the callback with a null server pointer.

Since our functions must be non-blocking, returns immediately, before any calls to the callback. The callback is called by dpReceive, which must be called frequently.

dpEnumServersEx

DP_API dp_result_t DP_APIX dpEnumServersEx(
	dp_t *dp,
	long timeout,				/* How long in milliseconds to wait. */
	dp_species_t sessType,
	dpEnumServersExCallback_t cb,
	void *context);
Similar to dpEnumServers() but returns more server information during callback.

dpEnumServersEx() takes an additional parameter for session type and returns additional server information in a dp_serverInfo_t structure during callback.
This info includes latency, packet loss, number of users of given session type, total number of users, and maximum allowed users.

dpEnumSessions

DP_API dp_result_t DP_APIX dpEnumSessions(
	dp_t *dp,
	dp_session_t *sDesc,
	char *mhn,					/* must be NULL */
	long timeout,				/* How long in milliseconds to wait. */
	dpEnumSessionsCallback_t cb,
	void *context);
Calls the callback function once with a description of each game session that this object could connect to, finishes by calling the callback with a null session pointer.

Since our functions must be non-blocking, returns immediately, before any calls to the callback. The callback is called by dpReceive.

dpEnumTransports

DP_API dp_result_t DP_APIX dpEnumTransports(
	dp_transport_t *path,
	dpEnumTransportCallback_t cb,
	void *context);
Calls the given function once for each transport available.
All calls are made before dpEnumTransports returns.
Path argument is to directory to search for DLL's.
Eventually, will read description out of the DLL files.

dpExitFromApp

DP_API void DP_APIX dpExitFromApp(int status);
Searches for stub and if it exists, notifies it of pending exit and if it wants NetShell relaunched (yes if status is zero) then exits application.

dpFlush

DP_API dp_result_t dpFlush(dp_t *dp);
Send any unsent packets. Call this after a bunch of dpSend's.
Once this is called the first time, dpSend no longer flushes its buffer automatically; it waits for you to call this.

dpFreeze

DP_API dp_result_t DP_APIX dpFreeze(
	dp_t   *dp,
	char *fName);
Freeze the current running game, and save it on disk for use during a future dpCreate.
Returns dp_RES_BUSY if dp can't freeze due to ongoing reliable transmission.

dpGetAppVersion

DP_API dp_result_t DP_APIX dpGetAppVersion(dp_t* dp, dp_appParam_t *app);
Determine the current application's version, and whether it needs to be updated.
Returns info about the current app in *app. Strings are allocated with strdup.

Returns dp_RES_OK if update needed,
dp_RES_EMPTY if no update needed, other values on failure.

dpGetCaps

DP_API dp_result_t DP_APIX dpGetCaps(
	dp_t *dp,
	dp_caps_t *info,
	long flags);
Retrieve the capabilities of the currently loaded transport, plus some info about the current session, especially whether this machine is the host of the current session.
Flags is currently unused.

dpGetCurrentHostId

DP_API dp_result_t DP_APIX dpGetCurrentHostId(
	dp_t *dp,
	dpid_t *phid);
Get the id of the current host for the open session, if he has any players.
Returns dp_RES_OK on success,
dp_RES_CLOSED if no session is open,
dp_RES_EMPTY if the host has no players.

dpGetCurrentTransportInfo

DP_API dp_result_t DP_APIX dpGetCurrentTransportInfo(
	dp_t *dp,
	dp_transport_t *path,
	comm_driverInfo_t *info);
Get path and info about of currently loaded transport.

dpGetGameServerEx

DP_API dp_result_t DP_APIX dpGetGameServerEx(
	dp_t *dp,
	char *masterHostNameBuf,
	size_t masterHostNameBufLen,
	dp_species_t *psessionType);
Retrieve the current game server name and default session type.
Returns dp_RES_FULL if buffer too small,
dp_RES_NOTYET if no server has been set.

dpGetParameterLong

DP_API dp_result_t DP_APIX dpGetParameterLong(dp_t *dp, int paramId, long *pparamVal);
Retrieve a parameter set by an external game launcher, if any.
On success, places the value of the parameter into *pparamVal.
Returns dp_RES_OK on success.
Returns dp_RES_UNIMPLEMENTED if that parameter is not supported by this driver.
May return other error codes as well; depends on the driver being used.

dpGetPlayerBlob

DP_API dp_result_t DP_APIX dpGetPlayerBlob(
	dp_t *dp,
	dpid_t id,
	dp_char_t *buf,
	size_t *pbuflen);
Retrieve the blob associated with player having the given id.
The blob is just a block of data that is carried with the player.

On entry, *pbuflen holds the length of buf.
If *pbuflen is not big enough to hold the blob, the actual size of the blob is placed in *pbuflen, and dp_RES_FULL is returned.

On exit, *pbuflen holds the actual length of the blob, and the blob is copied into buf.
On success return dp_RES_OK

dpGetPlayerCaps

DP_API dp_result_t DP_APIX dpGetPlayerCaps(
	dp_t *dp,
	dpid_t id,
	dp_caps_t *info,
	long flags);
Retrieve the capabilities of the currently loaded transport, plus some info about the given player, especially the current latency and packet loss to that player.
If flags is dp_SEND_RELIABLE, retrieves info about reliable packet services instead of unreliable services.

dpGetPlayerData

DP_API dp_result_t DP_APIX dpGetPlayerData(
	dp_t   *dp,
	dpid_t idPlayer,
	int    key,
	void   *buf,
	size_t *pbuflen,
	long   flags);
Get a variable for a player.
Caller must set *buflen before calling to the size of buf.

If the player or variable does not exist, dp_RES_EMPTY is returned.
If *buflen is not big enough, *buflen is set to the current size, and dp_res_FULL is returned.

dpGetPlayerName

DP_API dp_result_t DP_APIX dpGetPlayerName(
	dp_t *dp,
	dpid_t id,
	dp_char_t *buf,
	size_t bufLen);
Convert a dpid into a player name.
On success return dp_RES_OK, and copy at most bufLen-1 bytes of player name into buf; always null terminate result.
On failure, return dp_RES_EMPTY.

dpGetPlayerUid

DP_API dp_uid_t DP_APIX dpGetPlayerUid(dp_t *dp, dpid_t id);
Convert a player id (dpid_t) to a user id (dp_uid_t).
Returns dp_UID_NONE on any error.

dpGetSessionDesc

DP_API dp_result_t DP_APIX dpGetSessionDesc(
	dp_t *dp,
	dp_session_t *buf,
	size_t *pbuflen);
Retrieve the current session description. *pbuflen must be filled with the size of the buffer before calling; it will be set to sizeof(dp_session_t).
If pbuflen is NULL, it is assumed that the buffer is the right size.

dpGetSessionId

DP_API dp_result_t DP_APIX dpGetSessionId(
	dp_t *dp,
	const dp_session_t *sess,	/* session to convert */
	char *id,					/* resulting id stored here */
	int *pidlen);				/* length of resulting id stored here */
Convert a session description to its unique id (aka key). key must put to a buffer of length dp_KEY_MAXLEN; the session's id will be copied to this buffer. *pidlen will be filled with the number of bytes of id used.

Session ids are unique, and do not change during the duration of a game session.

dpGetStats

DP_API dp_result_t DP_APIX dpGetStats(
	dp_t *dp,
	int statkind,
	dp_stat_t *buf,
	size_t buflen);
Retrieve statistics from dp about network traffic.
On entry, statkind is one of dp_STAT_*; for example, if you want to know how many reliable packets have been received by the underlying reliable transport since dpCreate, use dp_STAT_DPIO_RX_REL_PKTS. buf should be a pointer to a dp_stat_t. buflen should be sizeof(dp_stat_t).
Return value is dp_RES_OK on success, and dp_RES_BAD if dp is null or id is invalid.

dpGetTransportInfo

DP_API dp_result_t DP_APIX dpGetTransportInfo(
	dp_transport_t *path,
	comm_driverInfo_t *info);
Read description out of the DLL file named by 'path'.

dpLaunchApp

DP_API dp_result_t DP_APIX dpLaunchApp(dp_appParam_t *appParam);
Launches an application from another application and notifies stub of the newly launched application, if stub exists.
If /NEWCONSOLE is found in appParam->shellOpts, a new console is created.
If /OLDCONSOLE is found in appParam->shellOpts, the old console is used.

result may be NULL. If result is not NULL, it is filled with system dependant information about the newly launched app. On Win32 systems, if result is NULL, the handle to the newly created process is closed, otherwise it is left open for the caller.

dpNumPlayers

DP_API int DP_APIX dpNumPlayers(
	dp_t *dp);
Return the number of players in the current session.

dpOpen

DP_API dp_result_t DP_APIX dpOpen(
	dp_t *dp,
	dp_session_t *s,
	dpEnumSessionsCallback_t cb,
	void *context);
Start a new session, or join an existing one.
If starting a new session, include dp_SESSION_FLAGS_CREATESESSION in flags argument for s.
Otherwise if joining a specific session, pass that session in s.
Or if joining any session, either pass in NULL for s if joining a lobby on the game server (dpSetGameServer must have been called) or pass in address including port number (in format returned by dpResolveHostname()) as well as the session type and a flags argument (indicating lobby or game) in s.
Returns immediately. dp_receive will call the callback when operation completes, and will indicate success with a non-zero session description pointer.

dpPingUser

DP_API dp_result_t DP_APIX dpPingUser(
	dp_t *dp,
	dpid_t dest,
	short karma,
	dp_ping_callback_t cb);
Measure round-trip transmission time to a player or the gamehost.
To ping the gamehost, use dest = 0.
Result is returned after a couple seconds via callback.
Call with a unique karma (say, 1 for first call, 2 for 2nd call, etc.)

dpReadAnetInf

DP_API dp_result_t DP_APIX dpReadAnetInf(const char *path, dp_appParam_t *pAppParam);
Gets appParam parameters from an anet.inf file. path is name of directory containing anet.inf.
Not needed by most user programs.

dpReadyToFreeze

DP_API dp_result_t DP_APIX dpReadyToFreeze(
	dp_t   *dp);
Returns dp_RES_BUSY if dp can't freeze due to ongoing reliable transmission.

dpReceive

DP_API dp_result_t DP_APIX dpReceive(
	dp_t   *dp,
	dpid_t *idFrom,
	dpid_t *idTo,
	int    flags,
	void *buffer,
	size_t *size);
Receive a packet.
Also triggers internal housekeeping. Must be called frequently.
Caller must initialize *size with the size of the buffer before calling. idFrom and idTo must be set to the addresses of uninitialized dpid_t's.
On return, *size is set to the number of bytes received, and *idFrom and *idTo are set to the dpid's of the sender and recipient.
Returns dp_RES_BUG if dp was invalid.
Returns dp_RES_OK if a user packet was receieved.
Returns dp_RES_EMPTY if NO user packet was receieved.
Returns dp_RES_BAD if a user packet with a bad envelope was receieved.
Returns dp_RES_NETWORK_NOT_RESPONDING if transmit buffers become too full.
Returns dp_RES_HOST_NOT_RESPONDING if game server handle resets.
Returns dp_RES_MODEM_NO_RESPONSE if modem connection lost is detected.

NOTE: It is best to always use a receive buffer of size dpio_MAXLEN_UNRELIABLE (not dp_MAXLEN_RELIABLE, which is less).
This is because an extra sizeof_dp_envelope_t bytes at end of buffer are sometimes used for dpid's.

dpReportAssertionFailure

DP_API dp_result_t DP_APIX dpReportAssertionFailure(int lineno, char *file, char *linetxt);
Records assertion failure to logfile.

dpReportCrash

DP_API dp_result_t DP_APIX dpReportCrash(LPEXCEPTION_POINTERS pException);
Records crash info to logfile. Designed to be called from an exception handler.
For Win32, pException is the LPEXCEPTION_POINTERS structure returned by GetExceptionInformation(). crshtxt is included in the crash record; crshtxt is truncated if longer than 100 bytes or if the crash record is already 512 bytes.
Returns dp_RES_ALREADY if dpReportCrash(Ex) was called previously; in this case, does not write a new crash record to log unless the crash was caused during dpReportCrash(Ex).
See aeh.htm for more info.

dpReportScore2

DP_API dp_result_t DP_APIX dpReportScore2(dp_t *dp, dpid_t dpId, int scoreId, long scoreVal);
Old score reporting function, dummied out.

dpReportScoreBuf

DP_API dp_result_t DP_APIX dpReportScoreBuf(dp_t *dp, dpid_t dpId, int scoreId, const char *scorebuf, int scorelen);
Report the score(s) for player dpId.

The idea is to pack all the score info of interest about a particular player into a compact buffer, then call this function. This should be done periodically, e.g. after every major event.

You should use the first two bytes for a rough 'score', most significant byte first, and the third byte should be 0 normally, and 1 if this player has 'won'. e.g. buf[0] = dpGETSHORT_FIRSTBYTE(score); buf[1] = dpGETSHORT_SECONDBYTE(score); buf[2] = 0;
Additional details can be appended. You should write up a spec on how to interpret these bytes, and provide it to the game server administrators.

scoreId should be zero.

dpReportScoreEnd

DP_API dp_result_t DP_APIX dpReportScoreEnd(dp_t *dp);
End a score report.

dpReportScoreStart

DP_API dp_result_t DP_APIX dpReportScoreStart(dp_t *dp, int flag);
Begin a score report.
Flag must be zero.
This should only be called at the end of the game (but before dpClose).

Call dpReportScoreStart before calling dpReportScore2, then call dpReportScore2 to report as many scores as you like, then finally call dpReportScoreEnd to finish sending the block of scores.

dpRequestObjectDeltas

DP_API dp_result_t DP_APIX dpRequestObjectDeltas(
	dp_t *dp,
	int monitor,		/* TRUE to start, FALSE to stop */
	const char *key,
	int keylen);
Start or stop monitoring the given object table for changes.
Initially, and on any change to the given table, a dp_objectDelta_packet_t is generated and placed in a queue for retrieval with dpReceive.
See anet.h for the definition of dp_objectDelta_packet_t.

When called with monitor=TRUE, a burst of messages are generated giving the initial contents of the table.
When called with monitor=FALSE, no more messages of that sort will be generated, although there may still be some in the queue; you can call dpReceive until it returns empty to flush these out if desired.

The key argument is a variable-length binary string that indicates what objects to start or stop monitoring.

To start or stop monitoring sessions, use keylen=1, key[0] = dp_KEY_SESSIONS
To stop monitoring servers, use keylen=1, key[0] = dp_KEY_SERVERPINGS,
To start monitoring servers, use keylen=3; key[0] = dp_KEY_SERVERPINGS, key[1] = (char) dpGETSHORT_FIRSTBYTE(sessiontype); key[2] = (char) dpGETSHORT_SECONDBYTE(sessiontype);
To monitor players, use char key[dp_KEY_MAXLEN+1]; key[0] = dp_KEY_PLAYERS; dpGetSessionId(dp, &sess, &key[1], &keylen); keylen++;
To request that latencies be included in player deltas for the current session, use keylen = 1, key[0] = dp_KEY_PLAYER_LATENCIES;
The latency in milliseconds will be placed in the latency field of the object delta packet.
The frequency of latency measurements is influenced by the intervals set with dpSetPingIntervals.
Deltas are synthesized locally and do not load the network.

Note: keylen will be dp_KEY_MAXLEN+1 when requesting player deltas in IPX sessions!

dpResolveHostname

DP_API dp_result_t DP_APIX dpResolveHostname(
	dp_t *dp,
	char *hostname,
	char adrbuf[dp_MAX_ADR_LEN]);
Convert an ASCII hostname into a binary address.

dpSend

DP_API dp_result_t DP_APIX dpSend(
	dp_t  *dp,
	dpid_t idFrom,
	dpid_t idTo,
	int flags,
	void *buffer,
	size_t size);
Send a packet from one player to another.
First two bytes of the message must indicate the message type.
Users must not use any message type starting with dp_PACKET_INITIALBYTE.
Message types are created with the dppt_MAKE macro.

If the flags argument is dp_SEND_RELIABLE, dp will continue trying to send the packet until it is acknowledged. Otherwise it will just send it once.

NOTE: It is best to always use a send buffer of size dpio_MAXLEN_UNRELIABLE (not dp_MAXLEN_RELIABLE, which is less).
This is because an extra sizeof_dp_envelope_t bytes at end of buffer are sometimes used for dpid's.

dpSendPlayerData

DP_API dp_result_t DP_APIX dpSendPlayerData(
	dp_t   *dp,
	dpid_t idPlayer,
	int    key,
	dpid_t idTo);
Send a given player's variable 'key' to a player (or to everybody, if idTo == dp_ID_BROADCAST).
Only used for variables that were not sent when they were set, i.e. which were set with dpSendPlayerData(..., dp_PLAYERDATA_FLAG_NOFLOOD)

dpSetActiveThread

DP_API dp_result_t DP_APIX dpSetActiveThread(dp_t *dp);
Sets which thread is allowed to make dp calls.
Avoid using this call. It's dangerous.

dpSetClocksPerSec

DP_API dp_result_t DP_APIX dpSetClocksPerSec(
	dp_t *dp,
	int cps);
If your program changes the speed of the system clock, pass in the new value of the ANSI C constant CLOCKS_PER_SEC here.
This is used to along with the clock() function to set internal timeouts.
This is not needed under Windows 95.

dpSetConnCallback

DP_API dp_result_t DP_APIX dpSetConnCallback(
	dp_t *dp, dpOpenConnCallback_t cb, void *context);
Sets a callback to be called for opening and closing of connections.
This is done via a callback translation of the dpio_setIncomingCallback mechanism, and passing the callback translation to dpio_openHdl.

dpSetGameServer

DP_API dp_result_t DP_APIX dpSetGameServer(
	dp_t *dp,
	char *masterHostName);	/* server's name, or NULL to clear */
Same as dpSetGameServerEx, but gets sessionType by calling dpReadAnetInf().
Obsolete.

dpSetGameServerEx

DP_API dp_result_t DP_APIX dpSetGameServerEx(
	dp_t *dp,
	char *masterHostName,	/* server's name, or NULL to clear */
	dp_species_t sessionType);
When using an Internet driver, call this function with the ascii name of the server, and explicit requests for sessions will be sent to that server.
Closes any open sessions.

Call with NULL to clear the game server.

dpSetGameServerEx lets you specify the session type you will be calling dpEnumSessions() later with. This tells the game server to send only information about that particular session type.
To get information about all session types, specify 0 for sessionType.

Returns:
dp_RES_OK Server set/cleared successfully
dp_RES_BUG Internal error.
dp_RES_BAD Called with invalid parameters, or internal error.
dp_RES_HOST_NOT_RESPONDING Could not resolve new host name.
dp_RES_BUSY Trying to open server that is being closed
dp_RES_FULL Transmission queue full. (Other values may also be returned.)

dpSetPingIntervals

DP_API dp_result_t DP_APIX dpSetPingIntervals(
	dp_t *dp,
	int piggybackPingIntervalMS,
	int forcedPingIntervalMS);
Set the intervals used by the latency measurement system.
If both intervals are 0, only ACKs of non-retransmitted reliable packets will generate new measurements. No extra bandwidth is used, but measurements may be infrequent if there are few reliable packets or there is heavy packet loss.
If piggbackPingInterval is set, additional small ping packets and responses will be piggybacked onto gathered packets every piggybackPingInterval ms when possible. No extra physical packets will be generated, and any network traffic at all will generate latency and packet loss measurements.
If forcedPingInterval is set and no piggybacked pings have been sent in the past forcedPingInterval ms, an additional ping packet will be sent to get a latency measurement.

Pings will only be sent to players in the current session, and will never be sent more than once per round trip time, regardless of the intervals requested.

Returns dp_RES_OK on success, or dp_RES_BAD on bad parameters.

dpSetPlayerBlob

DP_API dp_result_t DP_APIX dpSetPlayerBlob(
	dp_t *dp,
	dpid_t id,
	void *blob,
	size_t bloblen);
Set the blob for the player indicated by the given dpid.
If blob is too long, dp_RES_FULL is returned.
On success return dp_RES_OK

dpSetPlayerData

DP_API dp_result_t DP_APIX dpSetPlayerData(
	dp_t   *dp,
	dpid_t idPlayer,
	int    key,
	void   *buf,
	size_t buflen,
	long   flags);
Set variable 'key' for a player. See dp.htm for more info.

dpSetPlayerName

DP_API dp_result_t DP_APIX dpSetPlayerName(
	dp_t *dp,
	dpid_t id,
	dp_char_t *name);
Set the name for the player indicated by the given dpid.
If name is too long, it will be truncated.
On success return dp_RES_OK
On failure, return dp_RES_EMPTY.

dpSetSessionDesc

DP_API dp_result_t DP_APIX dpSetSessionDesc(
	dp_t *dp,
	const dp_session_t *buf,
	long flags);
Change the name or user data of the current session.
May be called only by master.
Flags is currently unused.
The only fields of the dp_session_t that may be changed with this function are dwUser1, maxPlayers, szUserField, and sessionName.
To set or clear the dp_SESSION_FLAGS_ENABLE_NEWPLAYERS flag bit, call dpEnableNewPlayers().

dpShutdown

DP_API dp_result_t dpShutdown(dp_t *dp, clock_t timeout, clock_t wait_after, int flags);
When you want to quit and terminate the game, you should poll this routine in a loop with dpReceive. This will close the connection to the server and delete all sessions and players associated with it.
Flags should normally be zero.

dpShutdown will wait an additional wait_after ms after closing the connection to handle packet retries.
If flags is 1, the connection to the game server will not be closed.
Steps which are dependant on network events will time out in timeout ms, if timeout is non-zero. Since there are three such states, the maximum total time for shut down is about (timeout * 3 + wait_after) ms.

Returns dp_RES_OK when finished, dp_RES_BUSY while still shutting down.

dp_enableDebugPrint

DP_API void DP_APIX dp_enableDebugPrint(int enable);
Not normally needed.

Call with 1 to increase debug printing level (i.e. show more info); call with -1 to decrease debug printing level (i.e. show less info); call with 0 to disable all debug printing.

dp_enableDebugPrint

DP_API void DP_APIX dp_enableDebugPrint(int enable);
Not normally needed.

Call with 1 to increase debug printing level (i.e. show more info); call with -1 to decrease debug printing level (i.e. show less info); call with 0 to disable all debug printing.

dp_flushLog

DP_API void DP_APIX dp_flushLog(void);
Not normally needed.

Call to flush log file.

dp_getLogFP

DP_API FILE * DP_APIX dp_getLogFP(void);
Call this to retrieve the file pointer which dp is using for logging.
Most programs won't need this.
The only legal thing to do with the return value is to pass it to dp_setLogFP(). This is handy for context switching when using multiple dp_t's in the same program, as in our test bench.

dp_pack_session

DP_API int DP_APIX dp_pack_session(dp_t *dp, dp_species_t defaultSessionType, const dp_session_t *p, char *buf);
Pack a dp_session_t into a compact, uniform byte order form for transmission.
Doesn't need to send external address or karma, but will need to send internal address if it's not the same as external.
If any field is too long, it is truncated.
Returns length used.

dp_setLogFP

DP_API void DP_APIX dp_setLogFP(FILE *_fp);
Call this to set the file pointer which dp should use for logging.
Most programs won't need this.
If _fp is dp_LOG_FILE_NONE, this disables logging entirely.
This will override any filename specified by the .ini file. (Note that dp_setLogFname closes the currently open log file, regardless of whether it was passed in via this function, or opened by dp.)
WARNING: unless you're very sure your program is using the same C runtime library instance as does this code, only use the return value of dp_getLogFP().

dp_setLogFname

DP_API void DP_APIX dp_setLogFname(const char *fname);
Call to set default log filename. (Default value is "dp.log".)
This value may be overridden by setting a log filename in the .ini file.
Note: no log file unless you are linked with the debugging version of the library, and a .ini file with (for example) All=1 and ToLog=1 lines in the [DEBUG] section exists (see dp.htm#inifile).

dp_unpack_session

DP_API int DP_APIX dp_unpack_session(dp_t *dp, const char *subkey, int subkeylen, const char *buf, size_t buflen, dp_session_t *p);
Unpack the compact, byte-order-uniform version of a dp_session_t into the fluffy form we use internally.
Returns number of bytes used, or -1 on error.

dpini_GetFile

DP_API const char * DP_APIX dpini_GetFile(void);
Return the name of the current .INI file.
Value is a pointer to a static string; the caller should copy it to a buffer immediately.

dpini_SetFile

DP_API void DP_APIX dpini_SetFile(char *iniFileName);
Change the .INI file accessed by future calls.

Compatibility with other API's

Last update: 22 September 1998 The Anet API was initially designed to be similar to the DirectPlay API.

Some sections of the game will need to be different for Anet's API (for instance, initial creation of the Anet object).

Windows programmers should note that the Anet API is currently single-threaded. Calls that might block (i.e. might not return immediately) use callback functions instead of blocking, letting the main thread issue a lengthy operation request without having to wait around for it to complete. It is suggested that the main idle loop of your Windows application have a call to dpReceive, that no other place in your program call dpReceive, and that only one thread ever issue calls to the Anet API.


Dan Kegel, et al.