/pocketPlay

comment(s) 

DirectPlay for PocketPC and CF .NET; my_DirectPlay_Voice trash talk

http://www.brains-N-brawn.com/pocketPlay 4/13/2003 casey chesnut

DirectPlay for the Pocket PC (and desktop) exists to ease writing multiplayer games. i am graphically challenged to the point that i am not actually going to write a game. instead, this article will show how to access those bits using advanced native interop techniques in CF .NET


multiplayer games anywhere anytime

before i could code ... there was games. atari 400, commodore 64, and then a PC. wolfenstein 3d was the 1st mind blower. had doom memorized to the point that i could be in the same room with somebody else playing with my eyes closed and direct them based on sound effects alone. used to crosstrain with descent, at which nobody could beat me, and i had the ability to make people physically ill if they watched me play. lara series was a fav (i.e. last name is chesnut). internet quake (plus a football injury) put me on the verge of losing a college scholarship. desperately needed that money, so i gave up games at that point. made a small return with the playstation mod ... but i had more fun taking apart my PSX than actually playing the games. metal gear solid did throw me for a loop though. since then i have avoided games (just barely missed the return to castle wolfenstein) because of my obsessive compulsive personality trait of taking things to the extreme

getting this article out has been a real PITA. started it 3 different times over the last month. couldnt get into it because somebody screwed up big on my current contract and brought me on board too early, only to have me wait for approximately 3 weeks. so i was stuck in a state of constant limbo in milwaukee (aka brew city) for 2 weeks with nothing to do but sleep, lift, and drink (might have killed off too many brain cells). luckily i tweaked my low back doing leg press (1000 lbs x 8 reps) and had nothing better to do while in an invalid state than put this together

DirectPlay for PocketPC

DirectPlay is the DirectX bits that pertain to multiplayer games. it lets you write P2P and client-server type games. with DirectX 9, we get managed bits to do this on the desktop. at that same release time, we got DirectPlay 9 bits for the Pocket PC (which happens to be the only DirectX piece that exists for the PPC). the download is pretty thin since it only comes with the binaries. luckily DirectPlay 8 for the PPC is still available to download with documentation and sample source code; and the interfaces are identical as far as i can tell. NOTE MS put out a P2P SDK beta not to long ago. it is unmanaged only, and doesnt exist for the PPC, so ignore it. to make sure it worked, i installed the DirectPlay for PPC samples onto my Pocket PC and fired up the ChatPeer sample. then i fired up the managed DirectPlay 9 ChatPeer sample on my desktop. tried to start chatting and nothing happened. ends up that a USB or serial active sync is not enough for the PPC. put in my compactFlash WiFi card to the PPC, and it all started working like magic. NOTE DirectPlay 9 and DirectPlay 8 protocols are compatible as long as you dont use the 'packet signing' feature of DP9


eVC DirectPlay for PPC ChatPeer sample

Native Interop

now for the painful parts. DirectPlay PPC is unmanaged with a COM interface. NOTE the CF .NET does not support COM, not to mention i was doing J2EE when COM was cool. well you say, odysseysoftware.com just released CfCom to let you do COM in CF .NET. of course it only supports the IDispatch interface, which is not implemented by DirectPlay, and is typically not to be found in DirectX in general. no problem, i can just look at the POOM sample and write an eVC wrapper that is pInvokable. that will let me make calls into DirectPlay from the managed app. oh yeah, DirectPlay will also need to send messages to the app. so that means the MessageWindow is needed as well. so the DirectPlay messages will be handled by the COM wrapper, then it will send messages to a registered HWND. that HWND will belong to a MessageWindow created by the managed app, so that it can indirectly be called by DirectPlay

this shows how to init the layers to make calls into DirectPlay, as well as receive callbacks


indirectly calling DirectPlay, and vice versa

had to ramp up on the pieces i didnt know for this: COM, DirectPlay, and eVC. trip to half-price books and i had enough reading material to get started. with DirectPlay, there were a couple of choices for what the app can do. the 1st choice was to do a P2P or client/server model. didnt want to maintain a server so i opted for P2P. next, you can do a call to EnumHosts() to find peers or you can create a Lobby-able app. EnumHosts() was more direct, so i opted for that. went for the kill shot and started implementing the scenario above. wasn't too bad except debugging is a bear with all the layers. the 2 different scenerios for the app is to Host() a session for others to connect to, or to connect to a session that others are hosting by calling EnumHosts() mentioned above

Host()

tried Host() 1st just because that was the order in which the tutorials came in the documentation. 1st you have to create a DirectPlay8Peer object using CoCreateInstance. then call Initialize() passing it a MessageHandler. next you have to create a DirectPlay8Address object for your own device. NOTE you need to call Release() on these objects when the app closes, else the app will hang your PPC. you can enumerate the service providers (e.g. TCP, serial, modem) and then call SetSP() for a connection with the GUID of that service provider. this is kind of worthless since DirectPlay for PPC only supports TCP and bluetooth ... with most devices not having bluetooth yet. i only have a WiFi card for TCP, so could call SetSP with TCP directly. also, you will want to set the listening port for your app. i set my port to 2502 to match the desktop ChatPeer sample. finally, you have to create a AppDescription that contains the session name and a GUID for your app. again, made the GUID the same as the ChatPeer sample so that they would think they were the same app and would chat with each other. otherwise the GUID allows for diff DP apps to run on the same LAN. after all that, i called Host() and nothing happened. tried to connect from the desktop ... nothing

so i was stuck but had another path to go down ... EnumHosts(). had to do the same as above except call EnumHosts() instead of Host(). also, it requires another DirectPlay8Address describing the possible host the app wants to connect to. i set the port and other relevant info and then called EnumHosts() ... and the call never returned ...

alright, so maybe stuff was happening and i just didnt know it. implemented the MessageWindow and stubbed the MessageHandler() in the COM layer. also made a call to pass the MessageWindow HWND to the COM layer. so the COM layer MessageHandler will receive events, and then do a SendMessage() to the MessageWindow HWND, that will then call a callback method set by the form of the main app ... whew. mucked around with the Host() code a little and then fired it up. got a message fired to my app -65529? great ... -65529 looks bad :( go digging through .h files, use the DirectX Error Lookup tool, and internet searches ... -65529 shows up nowhere. so i'm stuck again

backtrack and decide to develop the simplest DirectPlay app in managed DirectX 9 to see if i had missed something. hour later and myChatPeer code is chatting with the sample ChatPeer that came with the SDK. better yet, its chatting with the DirectPlay PPC ChatPeer sample ... and i saw some stuff i had missed in the managed code

muck around more with the COM wrapper Host() calls and then fire it up to get the -65529 message. then the ChatPeer sample(s) which found my host, listed its session name, and fired a message with a diff id (also negative) to the COM layers message handler. minimal success! so maybe -65529 is a good message after all ... next i tried to send a message as a host. implemented the SendTo() method. fire open my PPC host, and then a desktop peer which connects to it. sent the message from the PPC and the desktop peer received it and displayed it! we now have one way p2p messaging


my CF .NET DirectPlay ChatPeer test app

back to message IDs. go looking through unmanaged DP calls to see what the relevant message IDs are. set those in the switch statement for my app and have those call SendMessages() returning different data so that i can tell which (if any) are being caught in the switch, fire up my client and set it to Host(). it receives message -65529 which is CREATE_PLAYER. then when a desktop peer searches for hosts it catches -65526 ENUM_HOSTS_QUERY. when the other client joins the session, it is -65529 CREATE_PLAYER again. when the other client sends a message it is -65519 RECEIVE. finally, when the other client leaves you get -65527 DESTROY_PLAYER

basically all of those messages look non-interesting, except for RECEIVE. see that RECEIVE returns a structure that has a BYTE* to the buffer of data. use some DirectXUtil functions to convert that buffer into a WCHAR* to be marshalled back over with a StringBuilder, and then rig up the MessageWindow to make that pInvoke when it catches the RECEIVE message. so another peer will send a message, DirectPlay will call the COM Wrapper MessageHandler, the MessageHandler will pull out the buffer data and stuff it in a WCHAR and do a SendMessage() to the MessageWindow, the MessageWindow will then pInvoke back into the COM Wrapper to retrieve that string, and then call a method on the main app which will set that string on the UI. we now have full p2p messaging


right click to save video of it in use (1.3 megs)

hopefully this will help people create more multiplayer PPC games, especially for BattleZone remake John Kennedy is doing on MSDN

EnumHosts()

with Host() working, you can host the app on a PocketPC, and then other devices can do EnumHosts() to connect to the original host. that is the scenario above with the desktop client connecting to the ppc host. the other scenario is for the desktop to Host() and the ppc to EnumHosts() ... except it is not working for me. i have looked at it over and over again and cannot see what the issue is, such that i am now blind to my own code. hopefully, its just a stupid mistake on my part that somebody can point out to me and then i will update this article with that fix

most of the calls between Host() and EnumHosts() are common. EnumHosts is different in that a DirectPlay8Address can be created describing the hosts port and its name (e.g. ip address). then EnumHosts() is called with that address, although i believe it can be called with null as well. regardless, EnumHosts() broadcasts to the specified address or all relevant addresses that it is looking for a host, and if a host find that query, then it is supposed to respond with its info. finally, the client can then Connect() and begin chatting with SendTo(). i have tried every permuatation of null host address, service provider, port, and name that i can think of ... and i get a big fat 0 responses of hosts found. NOTE if i pass the hostname of 'notebook', which is the machine name of the computer hosting the session, then it throws an exception 80158370. if i pass it the ip as the hostname instead, then it either hangs or timesout. the machine name vs ip address was kind of a shock until i used the remote display app with WiFi, and it complained about the machine name as well, but worked fine with the ip

my_DirectPlay_Voice

just the idea, i'm not actually going to code this right now although it would be simple enough ... related to DirectPlay is DirectPlay Voice (does not exist for the PPC). this lets you add voice communication in your multiplayer games ... aka 'trash talk' capabilities. this could be recreated using this article, along with the voice recording capabilities from /freeSpeech. all that is required is to add a mic or some record button to your game. when the user pressed that button it would record their voice to a wav. then the wav could be shipped around using DirectPlay and played on the other peer devices. i expect to be sitting in starbucks playing a multiplayer PPC pong game over tMobile with somebody else in another starbucks and being able to talk trash to them at the same time anyday now ... make it happen

Source

Updates

do have have some more ideas that would make me revisit this article, along with if somebody figures out my EnumHosts() issue. would love to write a game someday ... but not anytime soon. right now i need to trackdown pub friendly games to accomodate my recently acquired alcoholism due to being in brew city (when in rome ...)

Future

currently have a smack load of ideas to work on ... speech .NET control competition, IRDA, tablet pc code competition, telematics, etc... also need to get my server upgraded (hardware and OS), as well as get setup with offshore hosting so i can start pissing more people off. my next article will probably be another 'kitchen sink' style article. meaning lots and lots of different technologies integrated together and doing cool stuff (a la /noSink). ultimately it will allow me to be even more honest, obnoxious, creative, and offensive than i already am ... you have been warned. later