Windows Phone Peer-to-Peer Multiplayer Game using Sockets in XNA
One of the new features for the app platform in Windows Phone Mango is TCP and UDP Sockets. In this blog post, I'll talk about using this to augment an existing game to add peer-to-peer multiplayer over WiFi using UdpAnySourceMulticastClient. Phones running the game on the same WiFi network automatically discover one another and the players just appear on the screen.
The game itself is from another sample, the Windows Phone 7 Platformer Starter Kit from David Rousset's blog.
The changes are to add code to run UDP Multicast sockets and to enable multiplayer.
The source code for the full project is attached in the zip file below.
PlatformerGame.cs: This contains the game code and is where the sockets are initialized, and where the sends and receives are handled.
UdpAnySourceMulticastChannel.cs: This contains the UDP multicast sockets code for joining the group, sending and receiving data.
OtherPlayer.cs: This is a modification of Player.cs to add other players to the game.
Sockets Initialization in PlatformerGame.cs:
private void InitializeSockets() { this.Channel = new UdpAnySourceMulticastChannel(IPAddress.Parse("224.109.108.107"), 3007); this.Channel.PacketReceived += new EventHandler<UdpPacketReceivedEventArgs>(Channel_PacketReceived); this.Channel.Open(); }
This joins the UdpAnySourceMulticastClient group and sets it up to receive data:
public void Open() { if (!IsJoined) { this.Client.BeginJoinGroup( result => { try { this.Client.EndJoinGroup(result); IsJoined = true; this.OnAfterOpen(); this.Receive(); } catch { } }, null); } } private void Receive() { if (IsJoined) { Array.Clear(this.ReceiveBuffer, 0, this.ReceiveBuffer.Length); this.Client.BeginReceiveFromGroup(this.ReceiveBuffer, 0, this.ReceiveBuffer.Length, result => { if (!IsDisposed) { IPEndPoint source; try { this.Client.EndReceiveFromGroup(result, out source); this.OnReceive(source, this.ReceiveBuffer); this.Receive(); } catch { IsJoined = false; this.Open(); } } }, null); } }
Each time a player moves, it sends its new position and velocity to the multicast group.
private void SendPosition(string position) { if (level.Player.Position.X != oldPosition.X || level.Player.Position.Y != oldPosition.Y) { oldPosition = level.Player.Position; this.Channel.Send(position); } }
This sends the packet to the multicast group.
public void Send(string format, params object[] args) { if (IsJoined) { byte[] data = Encoding.UTF8.GetBytes(string.Format(format, args)); this.Client.BeginSendToGroup(data, 0, data.Length, result => { this.Client.EndSendToGroup(result); }, null); } }
When a packet gets received, the receive handler updates the other player so that it looks like the other player is moving. Each phone in the multicast group receives this and updates accordingly.
void Channel_PacketReceived(object sender, UdpPacketReceivedEventArgs e) { string data = e.Message; Console.WriteLine(data); string[] pos = data.Split(','); //Discard packets that do not match if (pos.Length != 5) { return; } try { if (pos[0] != identifier.ToString()) //if not originated from this phone { if (pos[1].Contains("ReachedExit")) { level.TimeRemaining = TimeSpan.Zero; } else { Vector2 position = new Vector2(float.Parse(pos[1]), float.Parse(pos[2])); Vector2 velocity = new Vector2(float.Parse(pos[3]), float.Parse(pos[4])); level.UpdateOtherPlayer(int.Parse(pos[0]), position, velocity); } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Caught unexpected exception: " + ex.Message); } }
To play, use the accelerometer to move left or right. Tap on the screen to jump. Multiplayer requires WiFi network.
To get the Windows Phone Mango Developer Tools Beta: https://www.microsoft.com/downloads/en/details.aspx?FamilyID=77586864-ab15-40e1-bc38-713a95a56a05&displaylang=en
Hope this helps show a simple way to communicate from phone to phone and demonstrate the power of sockets. Happy developing!
Ricky
Comments
Anonymous
June 15, 2011
Where did 224.0.1.100 come from for the IPAddress?Anonymous
June 15, 2011
224.0.1.100 is a multicast address. Multicast addresses are in the range of 224.0.0.0 to 239.255.255.255 (en.wikipedia.org/.../Multicast_address).Anonymous
June 15, 2011
That's my other Live ID above. After some reading, that address falls in the IANA-governed Internetwork Control Block. That particular address is currently assigned to NASDAQ. The IANA also states that addresses in this range should not be used unless assigned. So, what are the rules on choosing an address to use? Was that address picked at random?Anonymous
June 16, 2011
Thanks for pointing that out. That address was picked at random. I've changed it to one that is not in the reserved list, 224.109.108.107.Anonymous
June 16, 2011
The comment has been removedAnonymous
July 25, 2011
Hi, tried implementing this into a new game I am working on to see if I could get it to work outside the demo. I am running it on my phone and in the emulator to represent two seperate devices, this works on your demo. The problem I am having is that one of them freezes at random, doesn't matter if I am running it or selecting from the phone menu. Any ideas? I know its difficult to say without seeing the code. I have tried using "lock" (around the variables) in the event raised for receiving data but still dont know what is causing it. Cheers RedAnonymous
July 27, 2011
Does this only work with XNA or can Silverlight games take advantage of this? Many thanks, KarlAnonymous
July 27, 2011
@Redtalon: This is a known issue, and should be fixed for the final release. @Karl: The Socket APIs are from Silverlight. So Silverlight games can definitely take advantage of this.Anonymous
July 27, 2011
Rick, Thank you for confirming. Best, KarlAnonymous
July 27, 2011
The comment has been removedAnonymous
July 28, 2011
The PacketReceived event comes in on a background thread. To access the UI thread from a background thread, you can use the Dispatcher: this.Dispatcher.BeginInvoke(() => textBlock1.Text = text);Anonymous
August 01, 2011
Are there any plans for Sockets support on the XBOX 360, where players can join in with their phones?Anonymous
August 02, 2011
That would be really cool. I don't know about plans for XBOX 360. You could check on the XBOX 360 forums.Anonymous
August 02, 2011
I think the ability to play an XBOX game and have a WP7 friend jump in would be crazily addictive. I will ask about.Anonymous
August 02, 2011
Would I use this code if I wanted a game where a user could choose to join up with a specific phone, whether or not that phone is on the same WiFi? For example, a two-player tic-tac-toe where users choose their opponents from a list of friends, or another random phone.Anonymous
August 02, 2011
Would I use this code if I wanted a game where a user could choose to join up with a specific phone, whether or not that phone is on the same WiFi? For example, a two-player tic-tac-toe where users choose their opponents from a list of friends, or another random phone.Anonymous
August 02, 2011
Multicast is limited to the same network only. For your scenario, you could use TCP sockets. Earlier this year in MIX 2011, there was a demo of a simple IRC client that does this scenario where your phone could talk to random people on the IRC channel.Anonymous
September 02, 2011
Ricky_T thankyou very much for this... I'm trying to create a simple test with a texture2d player and Vector2 positions test on Emulater and Phone running mango.. No such luck.. Anyway you can create a quick source code just for us people who isnt so great in programming?Anonymous
September 03, 2011
Another question is would it be possible to have say Mango Phone control a character using sockets to a Windows Xna game??Anonymous
September 06, 2011
Hi Tishawn, I'm using Texture2D and Vector2 in this sample. You can download the source code which is attached to this blog. Yes, it's possible. Your Windows XNA game needs to have a socket open for listening. Then have your Mango Phone control the character and send the information to the Windows XNA game via the socket. Regards, RickyAnonymous
September 07, 2011
The comment has been removedAnonymous
October 02, 2011
Hi, is there any way to do a Peer-to-Peer Multiplayer Game with ad-hoc wifi or bluetooth?Anonymous
October 03, 2011
@Tishawn: You could use a sprite to draw the projectile using a png image, similar to drawing a player sprite. @mercator: At this time, I don't think there's a way to do this with ad-hoc wifi or bluetooth.Anonymous
January 03, 2012
Is there a way to implement this in a simple game of pong? my code all takes place in one class and the example uses 3 (OtherPlayer, level, and PlatformerGame)Anonymous
January 03, 2012
Yes, you can implement this in a simple game of pong, with your code in one class. You can follow the sample code in PlatformerGame.cs.Anonymous
January 03, 2012
Thanks for the lighting fast response but i've been working on this i few days now and i can't get it to work. I was wondering: 1). Do I need to create a class for OtherPlayer 2). If not should all the references in the sample code for OtherPlayer simply changed to the name i am using for my paddle's Vector2 position? 3). since everything is in the same class do i still need to call Dictionary?Anonymous
January 03, 2012
What issue are you seeing? It might be easier for you to create a new project, and follow the code in UdpAnySourceMulticastChannel to get simple Send and Receive working. Then put the code into your app to update the paddle's position on Receive.
- No. I just found it easier to different the local player vs the other player.
- Yes, change the logic to update your paddle's Vector2 position.
- No, Dictionary is just a data structure used to store key value pairs.
Anonymous
January 03, 2012
Thanks again i was also wondering if i should put the logic for controlling the paddle inside the Channel_PacketReceived method? the project deploys and running properly but there is no interaction from the other deviceAnonymous
January 03, 2012
You're welcome. Yes, you should put that logic in the Channel_PacketReceived method.Anonymous
January 18, 2012
hey thanks for the example, you rock! one question how would i go about starting the players at different positionsAnonymous
March 08, 2012
Hi, I admire your work ! Looks very simple but effective but I need to ask this lines of codes: this.Client.BeginSendTo(data, 0, data.Length,endPoint, result => { this.Client.EndSendToGroup(result); }, null); I wonder if your code should be like Client.EndSendTo() not Client.EndSendToGroup() ?Anonymous
March 09, 2012
@Mike: You can change the starting positions in the txt file at WindowsPhonePlatformerContentLevels. @jun: In this case, we want to send the same data to all players in the group, so we use EndSendToGroup(). If we wanted to send a private message to a specific player, then we can use EndSendTo().Anonymous
December 17, 2012
Do the players have to be on the same network to play ??Anonymous
December 17, 2012
Yes, players need to be on the same WiFi network to play. To play across different networks, you'll need to set up a TCP or UDP server, and have the different players connect to the server.Anonymous
December 17, 2012
Do you have a tutorial or sample on how to do that??Anonymous
December 18, 2012
It's published as an official MSDN topic: msdn.microsoft.com/.../hh202858(v=vs.105).aspxAnonymous
December 18, 2012
Hi Ricky, and congrats on getting published =) I would like to use your project to make my own multiplayer app on Windows Phone, but when I extract the archive, I get the following error: ! D:DownloadsWindowsPhonePlatformer (1).zip: The archive is corrupt ! D:DownloadsWindowsPhonePlatformer (1).zip: The archive is corrupt ! D:DownloadsWindowsPhonePlatformer (1).zip: CRC failed in WindowsPhonePlatformerWindowsPhonePlatformerContentSoundsExitReached.wma. The file is corrupt ! D:DownloadsWindowsPhonePlatformer (1).zip: The archive is corruptAnonymous
December 18, 2012
Thanks. I'm not sure what happened to the zip package that caused it to stop working. I've re-uploaded the zip file.