Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Games Programming with DirectPlay


SP 96: Games Programming with DirectPlay

Chris is the president of Genus Microprogramming, a provider of programming tools and services. He can be reached at [email protected].


Games have come a long way since we began playing Snipes over a Novell network. A few years ago Spectre became popular because of its multiplayer capability. Recently, games like Doom and Descent have illustrated that there is a great demand for good, multiplayer games. And as a programmer, I've wanted a fast and easy way to add multiplayer support to my own games, without having to delve into the depths of modem communication and various network APIs.

That's where DirectPlay comes in. DirectPlay, which comes with the Windows 95 Game SDK, provides access to communication services for your games. In this article I'll examine DirectPlay and show how you can use it. I'll also describe my experience with creating a multiplayer game called "NetRoids" using the DirectPlay interface. The source code and related files for NetRoids are available electronically; see "Availability" on page 3.

What Is DirectPlay?

DirectPlay is one of the DirectX components of the Microsoft Windows 95 Game SDK. The SDK includes components for video (DirectDraw), sound (DirectSound), input (DirectInput), setup (DirectSetup), and networking (DirectPlay), all designed for creating games. The name "DirectPlay'' seems nonintuitive to me, but I guess "DirectMultiPlayer" was too long and "DirectNet" too limiting. DirectPlay allows easy access to communication services provided by your computer, including modems, networks, and on-line systems. DirectPlay servers allow games to skip connectivity and communication details by providing a common interface. The DirectPlay software interface provides a simple way for games to communicate with each other over a wide range of services. This frees you from the need to create complex communication implementations and allows you to concentrate on producing a great game. DirectPlay consists of two API functions and roughly two dozen member functions.

The DirectPlay APIs initiate communication through the DirectPlay interface. DirectPlayEnumerate is used to obtain a list of available DirectPlay service providers, and DirectPlayCreate instantiates a DirectPlay object for a particular provider. There are service providers for modem communication and for network interfaces. A service provider for the Internet is reportedly being worked on. Again, what's important is that it doesn't matter which provider is selected--it is transparent to the programming of the game.

As Table 1 illustrates, the member functions are divided into four categories:

  • Session management.
  • Player management.
  • Group management.
  • Message management.

A DirectPlay session is an instance of a game. Session member functions can open a new session or connect to an existing or saved session. You can also list the sessions currently available, save sessions, and close a session. You need to open a session with the Open member function to begin your game.

The player-management functions manage the players in a game session. The most important functions in this category are CreatePlayer and DestroyPlayer. These are the only two functions in this category that I used in my game, but other functions allow you to list the current players, limit the number of players in a session, and set formal and friendly names.

DirectPlay also lets you create groups of players. The group member functions available are similar to the player functions. By grouping players, you can simplify message handling and conserve bandwidth by sending a message to a group rather than to each individual player. (I didn't use any functions in this category.)

The final category is what DirectPlay is all about: message management. The Send and Receive functions let you communicate with other players. Messages can be any kind of data your game requires. You can send messages to a particular player, to a group, or to all the players in the session. Obviously, I used both Send and Receive.

Using DirectPlay

Using DirectPlay in your game usually involves the following steps:

    1. Request that the user select a communication method for the game. Use the DirectPlayEnumerate function to find all of the available service providers and then list them in a listbox.

    2. Once a service provider has been chosen, create a DirectPlay object for that provider by calling the DirectPlayCreate function. This causes DirectPlay to load the appropriate service provider library.

    3. Optionally, you can request information from the user, such as a friendly name. Other game-specific information can be stored in the DPSESSIONDESC structure in the dwUser fields.

    4. Ask if the player wants to start a new game or join an existing one. If the player is joining an existing game, proceed to step 5. Otherwise, create the game by specifying the DPOPEN_ CREATESESSION flag and using the Open member function. Skip to step 6.

    5. Find existing sessions with EnumSessions and present them to the player. After a selection is made, connect to it by specifying the DPOPEN _OPENSESSION flag and using the Open member function.

    6. Create a player or players using the CreatePlayer member function. You can also find other players by using EnumPlayers.

    7. Use the Send and Receive member functions to communicate with the system and with other players.

    8. When the player exits the game, use DestroyPlayer to delete the player from the session, and the Close member function to close the communication channel.

Now that you have the APIs and member functions, and an overview of how to use DirectPlay, all you need is a game to try it out on.

NetRoids

To see what it takes to add multiplayer capabilities to a game, I decided to add network support to the Roids game, which is supplied with the Game SDK. While space does not permit the listings to be presented here, I've provided the source code and project files electronically. The Game SDK includes a multiplayer game called Duel, but it already contains DirectPlay support and I wanted something that I could do myself. This approach also allows us to focus on DirectPlay. Roids already has all of the drawing, sound, and art files--all it needs is more players. Additionally, it provides a good benchmark for determining how fast and easy it is to add multiplayer support to an existing game.

Every game needs a globally unique identifier (GUID). This is a long, complicated-looking number that the game uses to identify itself over the communication interface. Fortunately, the GUID can be generated for you by a program called UUIDGEN.EXE, which is provided with the Microsoft Win32 SDK (GUIDs are sometimes called UUIDs). I used the GUIDGEN.EXE program that comes with Visual C++ 4.0. GUIDGEN.EXE presents several ways of formatting the number, and since it is a Windows program, it has the ability to place the GUID on the clipboard for you to paste into your program. The GUID for NetRoids looks like Example 1. You can see why placing this number on the clipboard would be a handy feature! You create the GUID once while developing the game and use it from then on. The number is unique, so you don't have to register it with Microsoft.

For the DEFINE_GUID macro to work properly, you must #define INITGUID before the WINDOWS.H include file. And you need the DPLAY.H include file for the DirectPlay interface. Once you have the GUID, follow the outline given earlier. For NetRoids, I turned to the Duel multiplayer example provided by the SDK and borrowed its dialog boxes for choosing a provider and a session (they're not great, but they'll do for this test). The Duel DirectPlay code provided a starting point for the NetRoids DirectPlay support. Most of the code is for the support of the dialog boxes, since it only takes two function calls to establish the DirectPlay session and one to create the player.

Creating the Session

When the player presses Enter on the splash screen, the RemoteCreate function is called. This, in turn, calls GetProvider, which presents a dialog box listing the available service providers. You will find the DirectPlayEnumerate call in the dialog-box initialization section; see Example 1(b). The EnumSP function receives the service-provider information and adds the name strings to the contents of the listbox. Once a service provider is chosen or the dialog box is canceled, you return to the RemoteCreate function.

If a service provider was chosen, the CreateGame function is called, which presents a dialog box asking if the player wants to create a new game or connect to an existing one. If creating a new game, you can call the Open member function with DPOPEN_CREATESESSION; see Example2. If connecting to an existing game, the GetGame function is called. This displays a dialog box listing all of the currently available sessions of NetRoids. In the dialog box initialization, the EnumSessions member function is called, where the EnumSession callback (not to be confused with the EnumSessions member!) adds the sessions found to the listbox; see Example 3.

Let's Synchronize

DirectPlay provides everything you need, with the exception of guidelines for game synchronization. The SDK documentation skirts the issue by stating that "DirectPlay does not attempt to provide a general approach for game synchronization; to do so would necessarily impose limitations on the game-playing paradigm."

Well, an overview of how that might be accomplished would be nice.

If you were writing a simple chat program, your work would be done. You would simply send what one player typed to the other player and receive what he or she typed. But how do you keep several ships and several hundred random and fast-moving objects synchronized across four different computers? What exactly do you send back and forth, and how do you keep it straight? For NetRoids, I selected a client/server approach. The creator of the session becomes the host and the keeper of the universe. The host becomes ship 0 (I added the ship number in the upper-right corner of the display for reference), and the game operates much like it did as a single-player game. In fact, you can still play the game by yourself.

The IsHost variable is the key to making the game act like two different programs. The host does everything the single-player version does and more. But the client doesn't have to do certain tasks, like create the objects for the level or check for object collisions. Once the host has created a session, other players can connect to it. These players will be clients, and they need a way to tell the host that they are there. Our first two messages handle this. The MSG_HEREIAM message is sent by a client to all players in the session. The host will receive it, and then try to find a ship for the new player. If a ship is available, the host sends a MSG_INIT message back to the client (and only that client) telling the player his or her ship number and the current level number. The client then joins the game.

The messaging in NetRoids is handled by the SendGameMessage, ReceiveGameMessages, and EvaluateMessage functions. A structure is defined for each message packet, which is initialized and then sent with the DirectPlay Send member. Example 4 shows how to create and send the MSG_HEREIAM message. The first parameter is the DirectPlay object, followed by the DirectPlay sender ID, receiver ID, message flags, the communication buffer, and the number of bytes to send. To send to everyone in the session, you send to ID 0, which represents the DirectPlay server.

The message is received by ReceiveGameMessages using the Receive member function, see Example 5(a), and then handled by EvaluateMessage; see Example 5(b). As you can see, only the host answers MSG_HEREIAM message and then immediately tries to initialize that player. All messaging is handled through the Send and Receive member functions. It really is easy, but you still need a way to synchronize the game.

Synchronization means the synchronization of data. The original Roids game managed all of the objects through a doubly linked list with dynamically allocated members, with the player's ship at the top of the list. You can tell the other players to add an object, but how do you tell them which object to delete? To make things simple, I changed the object list into a fixed array. Now each object is placed at a specific index entry, and is easily identified. The top of the array still contains the player ships, but now there can be more than one player. This didn't require many code changes, but it is an important change.

Three messages control the objects in the game, including the ships:

  • MSG_UPDATE is sent by the host to update the position of objects on the client machines, and the clients send it to update their own ship position.
  • MSG_ADDOBJ is sent by the clients when they want to add an object--usually bullets. The add object message uses the same structure as the update message (the host ignores update messages because it sends them, so a separate message type was created).
  • MSG_DELOBJ is sent by the host when an object should be deleted from the list.
The last two messages deal with synchronization as well. The MSG_LEVEL message tells the clients that the host has started a new level, and the MSG_SCORE message allows the host to update a client's score. The score message actually makes the most use of direct host-to-client messaging, since most messages from the host are broadcast to all players in the session. When an object is created, the DirectPlay ID of the creator is stamped into the structure. When the host sees that a bullet object has collided with another object, it credits the creator of the bullet by sending the score message directly to the bullet creator's ID.

I didn't sit down and decide all at once that these were the messages needed for NetRoids to become multiplayer. I added them as I went along, in pretty much the order I presented them here. For instance, I didn't have a delete-object message at first--I handled that with an update message, because the object structure contains an enabled flag that determines whether the object is visible or not. This didn't allow for playing the appropriate sound when the object was destroyed, though, and the update message was already overloaded.

Multiplayer Changes

The display list (or object array) was one major change in turning the Roids game into NetRoids. There were many other details to add that didn't involve DirectPlay. The changes were necessary to make the game multiplayer capable. For example, any time the logic looked at the player ship, you had to worry about an array of ships. Global variables for controlling shields now had to be part of the ship structure. Ships couldn't be initialized at the center of the screen, since they all would appear on top of one another. And of course, there needed to be judicious use of the IsHost variable for controlling the host and client behavior.

A lot of the original code remains intact, however. If you have Visual C++, run the WinDiff program. Compare ROIDS.C to NETROIDS.C, ROIDS.H to NETROIDS.H, and ROIDS.RC to NETROIDS.RC. This will give you a good idea of what had to be changed and what stayed the same.

Performance

In my first attempt, I sent an update message for an object every frame. On my 133-MHz Pentium machine, I was getting 85 frames per second (fps), which I sent to a 486/66 and got 60 fps. I was able to drive the 486 down to its knees as soon as I got about 15 objects on the screen, and by doing a little math it is easy to understand why. I was sending 85*15*120= 153,000 bytes per second down the wire. Since I wanted several hundred objects on the screen at once, this obviously wouldn't do.

My second attempt was to update the objects only once per second, instead of 85. The client could move the objects in between the one-second sync pulse so they would still move smoothly. This would increase my bandwidth by 85 times! Add and delete messages would still occur immediately, and ship updates would still occur every frame. This worked a lot better, but there was a momentary pause at regular intervals when there were many objects on the screen.

The pause occurred because the pipeline was being maximized once a second--like turning the water on full blast, and off again, at regular intervals. To keep the water flowing through the pipe at a constant rate, my last attempt was to time-stamp each object as it was created. Objects are created sporadically as roids are destroyed, which can happen at any point in a one-second interval. An update message is sent to the clients only when that object's unique timer has expired, thereby keeping its position in the pipeline each second. This produced a smooth update on all of the clients.

I'm sure more tweaking could be done. The player ships probably don't need to be updated 85 times a second--only when their velocity, angle, or shields change. Decreasing the object update sync pulse to every two seconds would double the game bandwidth without sacrificing anything.

Exercises for the Reader

The dialog boxes could be kept from displaying over the splash screen. They have a funny green color because of the palette, and the mouse isn't visible outside the dialog box. For cosmetic reasons, they should probably appear before the game enters the DirectDraw display mode.

The Roids game is not much of a challenge, since you can fire so many shots at a time. I left that part alone in NetRoids, because I wanted to see how the performance kept up. The game would be much more challenging if each player were limited in the number of shots they could fire at one time and if there were a minimum amount of elapsed time between shots. Then I would change it from a cooperative game to a head-to-head game, where the players could shoot each other.

You could prompt the user for a friendly name, and then display it beside their ship as it moves around. You could use the GetPlayerName and SetPlayerName functions for this. Displaying the name is the harder task, however, since you would need a font and another display surface. Also, I didn't use any of the group functions, which may improve performance over some providers. The SDK recommends specifying a notification event when your application creates a player and then using the Win32 function WaitForSingleObject to find out whether there is a message pending for that player. And finally, I ignored all system messages. System messages are sent when players or groups are added or deleted, or when connections are lost. A robust DirectPlay game should monitor those messages.

Conclusion

I wanted to keep most of the Roids source intact and include a header file and a separate source file for the DirectPlay support. Eventually, I had to give up on that idea. You really have to plan ahead when writing a game for more than one player, especially in the area of data management. In some ways, starting with an existing single-player game is more of a hassle than starting from scratch.

I found the DirectPlay interface to be exactly as I anticipated--quick and easy. I spent most of my time on synchronization and message management, and I suppose that will vary with the type of game. A card game would take considerably less effort, since timing is not critical. I spent about one week learning the DirectPlay interface and creating the NetRoids game, which isn't bad at all. If there is an Internet service provider on the horizon, you'll be able to create a single game that plays over a modem, a network, and the Internet. I'm game.

DirectPlay Gotchas

Jim Mischel

Jim, who is coauthor of The New Delphi 2 Programming Explorer (Coriolis Group, 1996), can be contacted at [email protected].

Designed and developed by Cinematronics and published by Virgin Interactive Entertainment, TriTryst was one of the first Windows 95 games to incorporate multiplayer support using DirectPlay. As the primary programmer on that project, I was assigned, among other things, the unenviable task of implementing the multiplayer portions of the game. Working with a new library on a new project is usually difficult, and DirectPlay proved to be no exception. In the course of the project, I gained new insight into the structure of multiplayer games and learned quite a bit about DirectPlay's strengths, weaknesses, and limitations.

Multiplayer First, Game Second

Multiplayer issues extend into all parts of your game, from the highest levels all the way down to the base-data structures. If there's any chance your game will turn into a multiplayer game, design the code around that eventuality. Don't make the mistake of writing code that works fine for a single-player game with the intention of revisiting it later to "add" multiplayer support. Almost always, you'll end up rewriting large amounts of code from the ground up, and regression testing the result is not pretty. The only alternative is to special case the single-player game, and that's an even worse mess.

Get the multiplayer communications working before doing anything else. Even if your game is initially single-player, design and write the code so that the single-player game sends messages to and receives messages from itself in the same client/server fashion that you'll eventually use for the multiplayer game.

Know Your Limitations

The greatest shortcoming of DirectPlay is that it doesn't use reliable transport. My understanding is that unreliable transport was used in the interest of performance--reliable network transport is somewhat slower. That argument doesn't hold water, though. If DirectPlay doesn't let the network guarantee transport, then your program has to do it. That means packet ACK/NAK code and a checksum or CRC to ensure data integrity. So, build the packet verification code into your program at the beginning of the development cycle. You're going to need it, and it's much easier to design it in up front than it is to retrofit later.

On a related note, DirectPlay doesn't always tell you when a player leaves the game. If a player's network connection goes down or the computer crashes, your game gets no notification from DirectPlay. An added benefit of the packet verification is that you can determine when a player is no longer responding and act accordingly.

Also keep in mind that there is a limit on packet size. This isn't a limitation of DirectPlay, but rather the service provider you're using. Normally, the maximum packet size will be at least 512 bytes, but you can't guarantee that. You can determine the maximum packet size for a particular provider by calling the DirectPlay object's GetCaps() member function, and examining the dwMaxBufferSize variable in the returned DPCAPS structure.

The DirectPlay object's Send() member function won't always return an error if you attempt to send a packet that's longer than dwMaxBufferSize. Send() returns DP_OK, leaving you wondering why your packet didn't get there. If there's a possibility that your program's packet size will be too long, you'll have to add code that will split packets or reject an attempt to play the game using a service provider whose maximum packet size is too small.

Another limitation is that DirectPlayEnumerate() is a "call once" function. I would never have discovered this if it weren't for the testers at Virgin Interactive. One of their people decided to see what would happen if he repeatedly pulled up and then canceled TriTryst's multiplayer game dialog. This worked six times. The seventh time it would crash, right in the middle of one of DirectPlay's DLLs.

DirectPlay apparently allocates an internal array that will hold up to 20 service providers. Every time you call DirectPlayEnumerate(), all of the service providers it finds are added to this array--even if they were added on a previous call. Since there were three service providers supplied with DirectPlay, there was enough room for six calls to DirectPlayEnumerate() (3*6 = 18). The seventh call would overflow the array.

This is an interesting limitation. If a user happens to install more than 20 different DirectPlay service providers (assuming that many ever exist), DirectPlay won't be able to run on that system, and the user is likely to blame the game or the manufacturer of the last service provider that was installed.

DirectPlay Messages

You have to poll for DirectPlay messages. The rationale behind this design decision escapes me. Windows is event-driven from the ground up, and programmers are encouraged to make use of this architecture. But DirectPlay doesn't notify your program when a message is received. You have to write a separate polling loop to ask DirectPlay if there are any messages available. This "feature" of DirectPlay touches your program at a very basic level. Not only do you have to add a call to the message polling function in your PeekMessage() or GetMessage() loop, but you also have to set up a timer to ensure that the polling function gets called when a dialog box is being displayed or when some other application is active.

Other Issues

DirectPlay is full of little oddities that make it very frustrating to work with. For example, many of the member functions have the annoying habit of returning DPERR_GENERIC when they're unable to execute a particular function. I'm happy that the function tells me that it didn't complete successfully, but I'd also like to know why.

Another oddity is that when you call EnumSessions(), your program essentially locks up for the time period that you specify in the dwTimeout parameter. Empirical evidence shows that EnumSessions() will take at least two seconds and possibly longer. During this time, your program will not respond to the user's input. Two seconds doesn't seem like very long until you have to wait that long before a program responds to pressing the OK button in a dialog box.

Finally, DirectPlay provides one outstanding feature--transport independence. With DirectPlay you're able to write and test your game on one system, and be confident that it will work without modification on other transports. This is the only hands-down "plus" of DirectPlay. A big one, to be sure, but it doesn't necessarily outweigh the drawbacks.

Table 1: DirectPlay member functions. (a) Session management; (b) player management; (c) group management; (d) message management.

Function  Description

(a)

Close                  Closes a communication channel.

EnumSessions           Enumerates all of the sessions connected

                        to a specific DirectPlay object.

GetCaps                Retrieves the capabilities of the

                        specified DirectPlay object.

Open                   Establishes a new gaming session or

                        connects to an existing one.

SaveSession            Saves the current session in the

                        registry. Currently supported only by

                        modem service providers.



(b)

CreatePlayer           Creates a player for a session.

DestroyPlayer          Deletes a player from a session.

EnableNewPlayers       Enables or disables the addition of new

                        players or groups.

EnumPlayers            Enumerates all of the players in a

                        specified session.

GetPlayerCaps          Retrieves the capabilities of a player's

                        connection.

GetPlayerName          Retrieves a player's friendly and formal

                        names.

SetPlayerName          Changes the player's friendly and formal

                        names.



(c)

AddPlayerToGroup       Adds a player to an existing group.

CreateGroup            Creates an empty group of players for a

                        session.

ATTR 5|DeletePlayerFromGroup  Deletes a player from a group.

DestroyGroup           Destroys a group of players for a session.

EnumGroupPlayers       Enumerates the players in a group.

EnumGroups             Enumerates all of the groups associated

                        with a specified session.



(d)

GetMessageCount        Retrieves the number of messages waiting

                        for a player.

Receive                Retrieves messages that have been sent to

                        a player.

Send                   Sends messages to other players or all of

                        the players in a session.

Example 1: (a) Creating a globally unique identifier; (b) filling a list with names of DirectPlay service providers.

(a)
// NetRoids a64e8e40-5750-11cf-a4ac-0000c0ec0b9f


DEFINE_GUID(NETROIDS_GUID,0xa64e8e40,0x5750,0x11cf,


                            0xa4,0xac,0x00,0x00,


                            0xc0,0xec,0x0b,0x9f);

(b)
DirectPlayEnumerate(EnumSP, (LPVOID) hWndCtl);

Example 2: Creating a new game session.

// Create a new game, so we're the host
IsHost = TRUE;
// Initialize session description structure
memset(&dpDesc, 0x00, sizeof(DPSESSIONDESC));
dpDesc.dwSize = sizeof(dpDesc);
dpDesc.dwMaxPlayers = MAXPLAYERS;
dpDesc.dwFlags = DPOPEN_CREATESESSION;
dpDesc.guidSession = pGuid;
strcpy(dpDesc.szSessionName, FullName);
// Try to open the session
if ((hr = lpIDC->lpVtbl->Open(lpIDC, &dpDesc)) != DP_OK)
{
    // We failed
    lpIDC->lpVtbl->Release(lpIDC);
    lpIDC = NULL;
    return(FALSE);
}

Example 3: (a) Opening an existing game; (b) if a session is selected, it is opened with DPOPEN_OPENSESSION; (c) creating a player.

(a)
// Initialize the session description structure


memset(&dpDesc, 0x00, sizeof(DPSESSIONDESC));


dpDesc.dwSize = sizeof(dpDesc);


dpDes.guidSession = *g_lpGuid;


// Enum sessions with 5 second timeout


lpIDC->lpVtbl->EnumSessions(lpIDC, &dpDesc,


    (DWORD)5000, EnumSession,


    (LPVOID) hWndCtl, (DWORD)NULL);

(b)
// Initialize session description struc to open it


memset(&dpDesc, 0x00, sizeof(DPSESSIONDESC));


dpDesc.dwSize      = sizeof(dpDesc);


dpDesc.guidSession = *g_lpGuid;


dpDesc.dwFlags     = DPOPEN_OPENSESSION;


dpDesc.dwSession   = SendMessage((HWND) hWndCtl,


LB_GETITEMDATA, iIndex, 0);
hr = lpIDC->lpVtbl->Open(lpIDC, &dpDesc);

(c)
// Either way, we have to create a player


if ((hr = lpIDC->lpVtbl->CreatePlayer(lpIDC, &dcoID, NickName, "NetRoids Player", &dphEvent)) 


!= DP_OK)


{


    // We failed


    lpIDC->lpVtbl->Close(lpIDC);


    lpIDC->lpVtbl->Release(lpIDC);


    lpIDC = NULL;


    return(FALSE);


}

Example 4: (a) Creating the MSG_HEREIAM message; (b) sending the message.

(a)
case MSG_HEREIAM:


    // Tell host we are here


    lpHereIAm = (LPHEREIAMMSG)CommBuff;


    lpHereIAm->MsgCode = msg;


    lpHereIAm->ID = (DWORD)dcoID;


    nBytes = sizeof(HEREIAMMSG);


    break;

(b)
// Send the message


lpIDC->lpVtbl->Send(lpIDC, dcoID, send_to, 0, 
(LPSTR) CommBuff, nBytes);

Example 5: (a) Receiving the MSG_HEREIAM message; (b) handling the message.

(a)
status = lpIDC->lpVtbl->Receive(lpIDC,


     &fromID, &dcoReceiveID, DPRECEIVE_ALL,


     CommBuff, &nBytes);

(b)
case MSG_HEREIAM:


    // Someone wants to play


    if (IsHost)


    {


        // I'm the host, so find out who is here


        lpHereIAm = (LPHEREIAMMSG)CommBuff;


        shipID = lpHereIAm->ID;


        // Initialize them


        SendGameMessage(MSG_INIT, shipID, 0);


    }


    break;


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.