Unlike Microsoft's DirectPlay or Apple's NetSprocket, it supports not just Windows 95, but also Windows NT, Linux, Java, MacOS, and DOS.
The first function call to support this is dpAccountLogin; others will follow.
Given the file name of a transport DLL, the DLL's description may also be retrieved with dpGetTransportInfo(path, &description).
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.).
Then fill in fields as required by the driver you're using:commInitReq_t params; memset(¶ms, 0, sizeof(commInitReq_t)); params.reqLen = sizeof(commInitReq_t);
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:
A person wishing to join a game can do it one of three ways:
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 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).
#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));
[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 timeAt 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.
[DEBUG] pktloss=20 ; one-way packet loss, percentIn 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 millisecondsTo disable modem delay simulation, don't set those parameters in the .ini file, or set them to zero.
[DEBUG] fault=1 ; Induce an invalid address exceptionThen 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.
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 quitWhen 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.
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: messagewhere 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/.)
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 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.
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;
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.
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.
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.
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.
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.
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.
typedef struct { size_t len; dpid_t id; unsigned short key; void *data; } dp_user_playerData_packet_t;
/* 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;
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.
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".
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 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.
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.
DP_API dp_result_t DP_APIX dpCreate( dp_t **pdp, dp_transport_t *transport, commInitReq_t *params, char *thawFilename);Create a dp_t.
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.
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.
DP_API dp_result_t DP_APIX dpDeclareLobby( dp_t *dp, int flags);Declare that the current session is a lobby.
DP_API dp_result_t DP_APIX dpDeletePlayerFromGroup( dp_t *dp, dpid_t idGroup, dpid_t idPlayer);Delete player idPlayer from the group idGroup.
DP_API dp_result_t DP_APIX dpDestroy( dp_t *dp, int flags);Destroy a dp_t.
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.
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.
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.
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.
DP_API dp_result_t DP_APIX dpEnumApp(dp_t *dp, dpEnumAppCallback_t cb, void *context);Enumerate applications installed on this machine.
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.
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.
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.
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.
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.
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.
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.
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.
DP_API dp_result_t dpFlush(dp_t *dp);Send any unsent packets. Call this after a bunch of dpSend's.
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.
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 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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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).
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.
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'.
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.
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.
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.
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.
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.
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.
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.
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.
DP_API dp_result_t DP_APIX dpReportCrash(LPEXCEPTION_POINTERS pException);Records crash info to logfile. Designed to be called from an exception handler.
DP_API dp_result_t DP_APIX dpReportScore2(dp_t *dp, dpid_t dpId, int scoreId, long scoreVal);Old score reporting function, dummied out.
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.
DP_API dp_result_t DP_APIX dpReportScoreStart(dp_t *dp, int flag);Begin a score report.
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.
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.
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.
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).
DP_API dp_result_t DP_APIX dpSetActiveThread(dp_t *dp);Sets which thread is allowed to make dp calls.
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.
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.
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().
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.
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.
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.
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.
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.
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.
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.
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.
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.
DP_API void DP_APIX dp_setLogFP(FILE *_fp);Call this to set the file pointer which dp should use for logging.
DP_API void DP_APIX dp_setLogFname(const char *fname);Call to set default log filename. (Default value is "dp.log".)
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.
DP_API const char * DP_APIX dpini_GetFile(void);Return the name of the current .INI file.
DP_API void DP_APIX dpini_SetFile(char *iniFileName);Change the .INI file accessed by future calls.
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.