Topic : A DirectPlay Tutorial
Author : Sobeit Void
Page : << Previous 4  Next >>
Go to page :


rather than using the name. Save this in the local player struct. The player_name passed is obtained earlier in the information asked from user. The middle parameters are used if you do not want to poll the receive queue. Use them if you want to do multithreading. The player flags is either DPPLAYER_SERVERPLAYER or 0, which means non-server player. There can only be one server player in a session. There is also a spectator player but its meaning is defined by the application so we don't use it here.

The other function needed is EnumPlayers. I know you all are masters at enumerating now so I leave you all to implement this. Remember the global list of players we defined earlier? Just add the player inside the callback. It works the same way as the enumeration above. You don't have to enumerate the players in this chat but it is cool that you can see who is also connected at the same time.

You do not need to destroy the player because closing the session does that automatically and I don't see why you need to destroy and create another player while you are still connected. Still, it is your application.

Message Management
Now that we have a player, we need to know how to send and receive messages. I will talk about sending messages first.

Sending Messages
There is only one way to send a message and that is through the Send function. (Actually there is another if you use a lobby). If you remember, sending a message requires the id of the sender and the receiver. Good thing you have saved the local player id in a local player struct and all the players' ids in a global player list. So call the function as follows:


lpdp->Send(idFrom, idTo, dwFlags, lpData, dwDataSize);


The idFrom is the id of the local player. This must be set to a locally created player only (we don't want to impersonate another player, do we?) which in most cases is only one. The idTo is the receiver's id. Use DPID_SERVERPLAYER to send to the server only and DPID_ALLPLAYERS to send to everybody (except yourself). Note you cannot send a message to yourself. If you want to direct the message to a specific player, use the player list. You do not have to use a player list in a chat; instead you can add everything to a listbox. But later in the game, a list of players comes handy.

The flag parameter is how should the message be sent. The default is 0, which means non-guaranteed. The other options are DPSEND_GUARANTTED, DPSEND_ENCRYPTED and DPSEND_SIGNED. Sending a guaranteed message can take more than 3 times longer than a non-guaranteed one so only use guaranteed sending for important messages (like text). The signed and encrypted messages require a secure server, which we did not setup. Also any message received is guaranteed to be free of corruption (DirectPlay performs integrity checks on them).

Something to note: If you create a session that specifies no message id, their message idFrom will make no sense and the receiver will receive a message from DPID_UNKNOWN. Why anyone would want to disable message id is beyond me.

The last two parameters are a pointer to the data to send and the size of that block. Note that DirectPlay has no upper limit of the size you can send. DirectPlay will break large messages into smaller packets and reassemble them at the other end. Beware when sending non-guaranteed messages too; if one packet is lost, the whole message is discarded.

Since we are doing a chat program, I will show an example of sending a chat message


// types of messages the application will receive
const DWORD DP_MSG_CHATSTRING = 0;  // chat message

// the structure of a string message to send
typedef struct DP_STRING_MSG_TYP   // for variable string
{
    DWORD  dwType;     // type of message
    char   szMsg[1];   // variable length message

} DP_STRING_MSG. *DP_STRING_MSG_PTR;

// function to send string message from local player
int DP_Send_String_Mesg(DWORD type, DPID idTo, LPSTR lpstr)
{
    DP_STRING_MSG_PTR   lpStringMsg;    // message pointer
    DWORD               dwMessageSize;  // size of message
 
    // if empty string, return

    dwMessageSize = sizeof(DP_STRING_MSG)+lstrlen(lpstr); // get size

    // allocate space
    lpStringMsg = (DP_STRING_MSG_PTR)GlobalAllocPtr(GHND, dwMessageSize);

    lpStringMsg->dwType = type;          // set the type
    lstrcpy(lpStringMsg->szMsg, lpstr);  // copy the string

    // send the string
    lpdp->Send(local_player_id,idTo, DP_SEND_GUARANTEED,
        lpStringMsg, dwMessageSize);
 
    GlobalFreePtr(lpStringMsg);    // free the mem

    return(1);   // success
} // end int DP_Send_String_Mesg(..)



We first define the types of messages we can have. Since this is a chat program, there can only be one type, which I set to DP_MSG_CHATSTRING. You may add others and set the type so you can reuse the string sending function for different things. That is why the string message struct has a type to differentiate the string contents. The send function basically allocates space and sends the function to the desired player. Note the local_player_id is stored somewhere globally, or you can set it to pass another variable to set the local_player_flag. Do check the errors returned especially with allocation routines.

Receiving Messages
Receiving messages requires slightly more work than sending. There are two types of messages we can receive – a player message and a system message. A system message is sent when a change in the session state occurs. The system messages we trapped in this chat are:


DPSYS_SESSIONLOST          - the session was lost
DPSYS_HOST                 - the current host has left and you are the new host
DPSYS_CREATEPLAYERORGROUP  – a new player has join
DPSYS_DESTROYPLAYERORGROUP – a player has left


Some of those messages are only sent if certain flags are specified when then host creates the session. Consult the MSDN.

The Receive function has similar syntax to the Send function. The only different thing worth mentioning is the third parameter. Instead of the sending parameter, it is a receiving parameter. Set that to 0 for the default value, meaning extract the first message and delete it from the queue.

The whole difficult part about the receiving is that we need to cast the message to DPMSG_GENERIC and it gets messy there. So I give you the function and explain it below.


void Receive_Mesg()
{
    DPID    idFrom, idTo;         // id of player from and to
    LPVOID  lpvMsgBuffer = NULL;  // pointer to receiving buffer
    DWORD   dwMsgBufferSize;      // sizeof above buffer
    HRESULT hr;                   // temp result

    DWORD   count = 0;            // temp count of message

    // get number of message in the queue
    lpdp->GetMessageCount(local_player_id , &count);

    if (count == 0)    // if no messages
        return;        // do nothing

    do  // read all messages in queue
    {
        do  // loop until a single message is read successfully
        {
            idFrom  = 0;    // init var
            idTo    = 0;

            // get size of buffer required
            hr = lpdp->Receive(&idFrom, &idTo, 0, lpvMsgBuffer, &dwMsgBufferSize);
            if (hr == DPERR_BUFFERTOOSMALL)
            {
                if (lpvMsgBuffer)    // free old mem
                    GlobalFreePtr(lpvMsgBuffer);

                // allocate new mem
    


Page : << Previous 4  Next >>