Using the Event Model

The majority of actions you perform in a DirectPlay session happen asynchronously. The method will return almost instantly, and then DirectPlay will go to work doing the action you requested. After this action has been completed, or when there is something DirectPlay needs to inform the application about, an event will be fired on a separate thread. These events are how we find out everything we need to know.

In our preceding call to, the method will return right away, but behind the scenes, DirectPlay is busy looking for any available sessions. Whenever one is found the event is fired. Since we want to know about the hosts we find, we should hook this event. Add the following line directly after you've created your new peer object in the InitializeDirectPlay method:

 connection.FindHostResponse += new     FindHostResponseEventHandler(OnFindHost); 

You'll also need to add the actual method that will be used to handle this event. You will find this in Listing 18.2.

Listing 18.2 Event Handler for a Found Host
 private void OnFindHost(object sender, FindHostResponseEventArgs e) {     lock(this)     {         // Do nothing if we're connected already         if (connected)             return;         connected = true;         string foundSession = string.Format             ("Found session ({0}), trying to connect.",             e.Message.ApplicationDescription.SessionName);         this.BeginInvoke(new AddTextCallback(AddText),               new object[] { foundSession });         // Connect to the first one         ((Peer)sender).Connect(e.Message.ApplicationDescription,               e.Message.AddressSender,             e.Message.AddressDevice, null, ConnectFlags.OkToQueryForAddressing);     } } 

As you can see, our event handler includes a FindHostsReponseEventArgs object that will include the data about the host that was found. Examining the rest of this method tells us a lot about how multi-threaded programming should happen.

First, it's entirely possible for two different hosts to be found by DirectPlay. It's also possible that your application could be notified about each on two separate threads at the exact same time. Having two separate threads manipulating the same data at the same time is a big no-no, so we want to avoid that. We do this by using the lock keyword from C# (which is really just Monitor.Enter and Monitor.Exit calls wrapped in a try/finally block). Once the lock block has been entered by one thread, no other thread can enter that block until the first one has left the block.

We can only connect to one host at a time, so we will use a Boolean variable to check if we are already connected (you will need to declare this variable in your class; feel free to declare it publicly). If we are, there's nothing more to do in this method; we can just return. If we aren't, however, the first thing we will do is update this variable so that new enumerations won't interrupt us while we really do try to connect.

Now we want to update our UI to let the user know that we have found a session, and we are going to try to connect to it now. However, this event has been fired on a thread that is not the main UI thread where the control was created. It is not legal to modify or update controls on any thread other than the thread on which it was created. Doing so can result in failures, or even worse, deadlocks. The BeginInvoke method we use will call any delegate with the set of parameters we specify on the thread where the control was created for us. BeginInvoke happens asynchronously, much like DirectPlay. There is also an Invoke method that takes the same parameters, but runs synchronously. The definition for our delegate would be

 private delegate void AddTextCallback(string text); 

Nothing special, and we'll only use it to call our already existing AddText method. It is never a good idea to let a DirectPlay thread be blocked (for example, by using the synchronous Invoke method, or by displaying a modal message box). Always try to get the data you need and let the event end as quickly as possible.

Finally, we are ready to try and connect to this session. The connect call has four overloads, and once again, we used the simplest of them to get our connection started. You'll notice that we use the application description we received from the FindHostsEventArgs object to pass in to the Connect call. Actually, the majority of parameters we pass in to this method are retrieved from this object: the hosts address (the "sender" of the find hosts response), as well as the local address.

The last two parameters we pass in are application defined. The first is any user-defined data you want to pass into the connect call. This could be data that the host uses to determine which "team" you are on, for example; it is completely up to the application. The last parameter allows DirectPlay to query for addressing information, which probably isn't necessary since we are receiving full addressing information from the callback. The Sync flag is also allowed here to change the default behavior of the call to behave synchronously.

Other overloads of the Connect method contain one or more parameters we didn't need to use in this instance. One is an out parameter that is the asynchronous handle of the call. In DirectPlay, all calls that run asynchronously can return a handle to the call. You can use this handle to cancel the operation via the CancelAsyncOperation method if needed. We don't plan on canceling our operations, so this isn't necessary. The last two possible parameters are both user-defined context variables. Context variables can be used to store application-specific data.

SHOP TALK: DEFINING ENOUGH ADDRESSING INFORMATION

When you are running a full-screen game, you don't expect to see an "ugly" windows dialog popping up when you try to join a network game. You expect that any information needed to join this game will be entered inside of the game's user interface.

When you are designing a networking layer for a full-screen application, you will most likely never want to pass in the OkToQueryForAddressing flags on the Connect, Host, or FindHosts methods. Using this flag will allow DirectPlay to display its own custom windows dialog, which more than likely will not match your application's UI.

If you use these methods without this flag and it throws an AddressingException, more than likely there is some component of the address you are missing. In the TCP/IP case, this could be the host name of the server you're trying to reach, or the port the session should be run on. During implementation, make sure you know everything your service provider needs to perform the action, and make sure your addresses have the necessary components.



Managed DirectX 9 Graphics and Game Programming, Kick Start
Managed DirectX 9 Kick Start: Graphics and Game Programming
ISBN: B003D7JUW6
EAN: N/A
Year: 2002
Pages: 180
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net