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


   lpdp->Release();

        CoUninitialize();  // unregister the COM
    }

    lpdp       = NULL;  // set to NULL, safe practice here
    lpdplobby  = NULL;

    return(1);  // always success
}  // end int DirectPlay_Shutdown();



Stick this function at the close connection section.

Sessions
You will have no choice but to do a callback function here. The main functions you mainly need to do session management are:


EnumSessions      - enumerates all the session available sessions
Open              - joins or hosts a new session
Close             - close the session
GetSessionDesc    - get session properties
SetSessionDesc    - set session properties


I will only talk about the 3 functions above that we will use in this tutorial. Once you understand them, it is very easy to understand the others.


lpdp->Close();


Simple. Just close the session before you call DirectPlay_Shutdown(): All the local players created will be destroyed and the DPMSG_DESTROYPLAYERORGROUP will be sent to other players in the session.


lpdp->EnumSessions(..); // enumerates all the sessions


This function will only be called by the client side so if you are hosting the session, call Open. What is so troublesome is that the client needs to search for a session to join when they have made a connection, and since there may be more than one session in the host, we need to get every available session and present them to the user. This will require the use of a callback function. The callback function prototype is:


BOOL FAR PASCAL EnumSessionsCallback2(LPCDPSESSIONDESC2 lpThisCD,
                   LPDWORD  lpdwTimeOut,
                   DWORD    dwFlags,
                   LPVOID   lpContext);



Things to note: This callback function that we implement will be called once for each session that is found using EnumSessions. Once all the sessions are enumerated, the function will be called one more time with the DPESC_TIMEOUT flag.

Any pointers returned in a callback function are only temporary and are only valid in the callback function. We must save any information we want from the enumeration. This applies to the player enumeration later. EnumSessions has the prototype:


EnumSessions(LPDPSESSIONDESC2 lpsd, DWORD dwTimeOut,
             LPDPENUMSESSIONSCALLBACK2 lpEnumSessionCallback2,
             LPVOID context, DWORD dwFlags);



If you wondering why there is a 2 behind certain typedefs, the two means the second version of the type. A rule of thumb is to always use the latest typedefs as they encapsulate more things.

The first parameter is a session descriptor so you need to initialize one.


DPSESSIONDESC2   session_desc;    // session desc

ZeroMemory(&session_desc, sizeof(DPSESSIONDESC2));  // clear the desc
session_desc.dwSize = sizeof(DPSESSIONDESC2);
session_desc.guidApplication = our_program_id;      // we define this earlier



This will ensure our program only returns sessions hosted by our program. ZeroMemory is similar to memset to 0 and as you should know, many DirectC calls require you to put the size in the structure passed.

The second parameter should be set to 0 (recommended) for a default timeout value.

The third parameter is the callback function so we pass the function to it

The fourth parameter is a user-defined context that is passed to the enumeration callback. I will describe how to use it later.

The fifth is the type of sessions to enumerate. Just pass it the default 0 meaning it will only enumerate available sessions. Check out the MSDN for more options.

All these seem to be a good candidate for a function so let's do it.


// our callback function
BOOL FAR PASCAL EnumSessionsCallback(LPCDPSESSIONDESC2 lpThisSD,
                                     LPDWORD lpdwTimeOut,
                                     DWORD dwFlags, LPVOID lpContext)
{
    HWND   hwnd;      // handle. I suggest as listbox handle

    if (dwFlags & DPESC_TIMEOUT)  // if finished enumerating stop
        return(FALSE);

    hwnd = (HWND) lpContext;   // get window handle

    // lpThisSd-> lpszSessionNameA  // store this value, name of session
    // lpThis->guidInstance         // store this, the instance of the host

    return(TRUE);  // keep enumerating
} // end callback

// our enumeration function
int EnumSessions(HWND hwnd, GUID app_guid, DWORD dwFlags)
{
    DPSESSIONDESC2   session_desc;   // session desc

    ZeroMemory(..);    // as above
    // set size of desc
    // set guid to the passed guid

    // enumerate the session. Check for error here. Vital
    lpdp->EnumSessions(&session_desc, 0, EnumSessionsCallback, hwnd, dwFlags);

    return(1);   // success
} // end int EnumSessions



I suggest sending a listbox handle as the context so we can save the information and display it to the user. In the callback function, you must allocate space to hold the guidInstance. This is the instance of the program that is hosting the session. You need to pass this information for the user to select which one session. I have commented out the name and the instance. You save them in whatever way you want. Declare a global array and fill in the member. Whatever. I suggest a listbox so you can send messages via the hwnd parameter. Remember, the instance and name are only valid inside the callback function so you must save them to be able to present them later.

Note: If you return false in the callback, the enumeration will stop and return control the EnumSessions. If you don't stop it, it will loop forever. Also return false if you encounter an error inside. It is imperative you check the value from EnumSessions especially if you are not enumerating available sessions only. Also, the whole program will block while you are enumerating because it has to search for sessions. You can do an asynchronous enumeration too. Check it out yourself.


lpdp->Open(LPDPSESSIONDESC2 lpsd, DWORD dwFlags)


This functions hosts or joins a session using the dwFlags. If you are joining a session, you only need to fill the dwSize and guidInstance (you saved it somewhere) of the descriptor.

So we define a descriptor as:


DPSESSIONDESC2   session_desc;

ZeroMemory(&session_desc, sizeof(DPSESSIONDESC2));
session_desc.dwSize = sizeof(DPSESSIONDESC2);
session_desc.guidInstance = // instance you have save somewhere;

// and join the session
lpdp->Open(&session_desc, DPOPEN_JOIN);



If you are hosting a session, you need to fill in, in addition to the above, the name of the session, the maximum number of players allowed in the session and the session flags. You set the flag to Open as DPOPEN_CREATE | DPOPEN_RETURNSTATUS.

The return status flag hides a dialog box displaying the progress status and returns immediately. Although the documentation says I should keep calling Open with the return status flag, I have not found a need to do so (nor can I comprehend the reason they gave). You can change the session properties if you are a host using the other two methods I didn't cover.

The flags in the session desc you should set in this tutorial are:


DPSESSION_KEEPALIVE   – keeps the session alive when players are abnormally dropped.
DPSESSION_MIGRATEHOST – if the current host exits, another computer will become the host.


You can Or the flags together like so: FLAG_1 | FLAG_2 | FLAG_3. Check out the MSDN for more flag options.

Player Creation
We are just about done setting up. Now we create a local player using CreatePlayer. You have to define a name struct like so:


DPNAME    name;      // name type
DPID      dpid;      // the dpid of the player created given by directplay

ZeroMemory(&name,sizeof(DPNAME));   // clear out structure
name.size = sizeof(DPNAME);
name.lpszShortNameA = player_name;  // the name the from the user
name.lpszLongNameA = NULL;

lpdp->CreatePlayer(&dpid, &name, NULL, NULL, 0, player_flags);


This function will return a unique id for the local player within the session. Use this to identify the player

Page : << Previous 3  Next >>