LéBlog(string stories){

          return blog;

}

Posted on by Nicholas


So in preparation for Global Game Jam in a couple of weeks, I started looking into multiplayer capabilities using AS3/AIR.

It was a tiring search among many different blogs, videos, sites and presentations... there were many blank stares from me at very early hours of the morning. But in efforts to keep this to the point and make it maybe a bit easier for others who are searching for information on this topic, I'll get right to my point.

I have FINALLY gotten my P2P (Peer to Peer) connection issues sorted. I initially started looking at the ServerSocket(TCP) instead of RTMFP(UDP) within AS3, but I quickly realized that a) P2P would be much easier, and b) more applicable for my needs. I wanted to get LAN connectivity going for the purpose of a game, but to get used to the whole system, I built a chat client first (which will be later implemented into the game).

For an online example you can go here.

For an AIR client, you can download this.

This works strictly on LAN only, so opening multiple clients across multiple machines on the same network should work. AS3 uses RTMFP to connect P2P. You can read all about it on your own time, but one issue you may need to look at is, if your devices/machines can connect due to some UDP and other possible firewall restrictions. The easiest way is to check this site. If the very first light at the top is green, then you should be good to go. Or if your lights look like the below screenshot, all should also be well.

RTMFP Connectivity Check

I have two machines on my network, and for some reason connecting my laptop second takes a little while before the clients see each other. But they definitely do eventually connect. But if I connect the laptop first, and anything else after, then all works immediately. I do believe this problem lies with the desktop, but I cannot figure it out, and don't have much desire to just yet. So you may or may not run into problems like that, but trust that it does work providing your firewalls allow it.

There are a lot of little details that you should know when working within RTMFP, and I won't go into it in detail here as it is very well documented all over the internetz, but of course if you do need any advice/help, please feel free to leave some comments below.

I will post the source code for the client soon, as soon as I clean it up and maybe add some comments. The client uses Minimal Comps for the UI. I found this library while searching for information on P2P, and I love it. It's very much made for just quick prototyping stuff. I did alter a couple of things for text color and whatnot, but the base classes don't need to be altered. They work very well. I highly recommend it.

Here are a couple of good tutorials and sources of information on P2P in AS3. Again, any questions or comments, please feel free to leave them below.

*UPDATE*

So originally my laptop was connected to the network via wifi, and my desktop was plugged directly into the router. And connecting was very hit or miss. Sometimes connecting with the laptop first, then the desktop would be instant, and other times connecting with the desktop first, then laptop, was instant. It was very odd.

I just plugged the laptop in, so now both machines are wired, and both orders of connecting was instant. So looks like having devices connected differently seems to throw a snag into the connectivity. Let me know if you have any of your own experiences with this and let's try to figure it out together!

- Nicholas.

Posted on by Nicholas | Posted in Adobe Air, AS3, Flash, General Information, Helpfulness | Tagged , , , , , , , , , , , , , , , ,


17 Responses to AS3 P2P Chat Client

  1. Steven says:


    Hi Nicholas,
    I am currently doing a p2p multiplayer game project, facing the problem which your example chat room had the answer…so i would like to ask is your project develop by using fully flash and actionscript 3? and how you manage the username key in by different player to display it on the list?

    Thanks in advance, hope to get your reply soon.


    • Nicholas says:


      Hey Steven.

      The project is AS3/AIR. That’s it. I code in FlashDevelop and import asset libraries using Flash.
      The usernames are stored locally on each client when they connect. They send their username to all users connected, and when that is received, each user sends their username back, thus creating a user list containing every user on every chat client.
      So if you store the username locally when the user connects, you then have access to it and can always attach it to objects being sent over the network.
      Hope this helps. Let me know if you need anymore help on the matter. :)

      Cheers.


      • Steven says:


        Hi Nicholas,

        Thanks for quick reply. I’m now going to create a “mastermind board game” in flash. There is a multiplayer mode. Now my problem is, when player join the multiplayer mode, their name suppose display on the “ROOM” ‘s name list, but then, now i only manage to display my own name and their peerID . which mean, only “PLAYER A” manage to see “PLAYER A” in name list. At “PLAYER B” side, they only can see “PLAYER A”‘s peerID .

        Thanks alot. Have a nice day =D


        • Nicholas says:


          Hey Steven.
          I didn’t get a chance to reply last night and right now I’m away from my computer. But as soon as I get back to it, I’ll provide a better explanation with code. But for now:

          On your “connect” event notification, you need to broadcast your username (which would have been saved when they entered it to connect I assume) to all the neighbors in the group.
          On receipt of that message, they need to send their name back to that user.

          Hope that makes sense. I’m on my phone, so typing is a little annoying. As I said, I’ll explain better when I get back to my machine.

          Good luck.


          • Steven says:


            Hi Nicholas,

            Million thanks, i will try to do it while waiting u back to your machine. Have a safe journey. ;)


          • Nicholas says:


            Hey Steven. Hope I’m not too late. Only just got time to get back to you.

            Here is a breakdown of my NetStatus event handler. Hope it helps.

            private function SetupConnection() : void {
            	//setup connection object
            	netConn = new NetConnection();
            	//listen for result of setup
            	netConn.addEventListener(NetStatusEvent.NET_STATUS, NetHandler, false, 0, true);
            	//connect to LAN
            	netConn.connect("rtmfp:");
            }
             
            private function NetHandler(e:NetStatusEvent):void {
            	switch(e.info.code){
            		case "NetConnection.Connect.Success":
            			SetupGroup();
            			break;
            		case "NetGroup.Connect.Success":
            			// clientObject is a local variable Object. It stores the user's information
            			clientObject.label = username;
            			clientObject.timeTag = new Date().time;
            			clientObject.peerID = netConn.nearID;
             
            			peers.push(clientObject);		// peers is an array of objects that store the users connected
            			newUserSignal.dispatch(clientObject);			// this is a "sort of" event dispatch, that sends out the local client object. This is so that the peers array, and user list will also contain this user's info.
            			break;
            		case "NetGroup.Posting.Notify":
            			ReceiveMessage(e.info.message, e.info.fromLocal);		// This just accepts the message sent, and I am using that object to print the message.
            			break;
            		case "NetGroup.Neighbor.Connect":
            			//get group address of peer
            			var destination:String = group.convertPeerIDToGroupAddress(String(e.info.peerID));
            			var obj:Object = new Object();
            			obj.username = username;
            			obj.peerID = clientObject.peerID;		// Put the LOCAL id into the object, cause this object is being sent to OTHER clients.
            			obj.timeTag = new Date().time;
            			//save to message object
            			obj.destination = destination;			// The destination is the destination of the client that just connected. This is used to ensure that the local client's info is sent to the new user(destination).
            			//send message
            			group.sendToNearest(obj, destination);	// Sends the object to the nearest neighbor. If the neighbor is not the destination of the object, then that neighbor will send it to it's nearest neighbor. (See case: "NetGroup.SendTo.Notify")
            			break;
            		case "NetGroup.Neighbor.Disconnect":
            			//neighbour disconnected so remove details from list
            			RemoveUser(e.info.peerID.toString());				// This function iterates through the peers array, and finds the object that contains the peerID of the client that just disconnected.
            			break;
            		case "NetGroup.SendTo.Notify":
            			if (e.info.fromLocal){			// If the message came from this client, that means the destination is correct, so take the information from the object sent, and populate the user list.
            				//is destination so display it
            				var peerObject:Object = new Object();
            				peerObject.peerID = e.info.message.peerID.toString();
            				peerObject.label = e.info.message.username;
            				peerObject.timeTag = new Date().time;
             
            				peers.push(peerObject);
            				newUserSignal.dispatch(peerObject);							// Dispatch the new user's info and add it to the peers array and to the username list
            			}
            			else{
            				//not destination so re-send
            				group.sendToNearest(e.info.message, e.info.message.destination);
            			}
            			break;
            	}
            }
             
            private function SetupGroup() : void {
            	//create a GroupSpecifier object
            	var groupspec:GroupSpecifier = new GroupSpecifier(localGroupName);
            	//enable posting (to entire group)
            	groupspec.postingEnabled = true;
            	//enable direct routing (to individual peer)
            	groupspec.routingEnabled = true;
            	//allow data to be exchanged on IP multicast sockets
            	groupspec.ipMulticastMemberUpdatesEnabled = true;
            	//set the IP adress and port to use
            	groupspec.addIPMulticastAddress("225.225.0.1:30303");
            	//create NetGroup with our NetConnection using GroupSpecifier details
            	group = new NetGroup(netConn, groupspec.groupspecWithAuthorizations());
            	//listen for result of setup
            	group.addEventListener(NetStatusEvent.NET_STATUS, NetHandler, false, 0, true);
            }

  2. Steven says:


    Hi Nicholas,

    Thanks you so much !! it Solve my problem!! hahaha… i made mistake at “NetGroup.Neighbor.Connect”… which i put the destination at “NetGroup.SendTo.Notify”… milion thanks Buddy! This problem been cracking my head for last 2 day… it solved now… THANKS!!


  3. Steven says:


    Hi Nicholas,

    Sorry to bother you again… i got 1 question to ask you.cause i might going to use this again on other project.
    How you actually implement this “newUserSignal.dispatch(clientObject);” ? is the “newUserSignal” consider as “object” too?


    • Nicholas says:


      Hey Steven.

      That variable is an instance of a Signal, which is an event library that you could find out more about at – http://www.peterelst.com/blog/2010/01/22/as3-signals-the-best-thing-since-sliced-bread/

      I highly recommend it, it’s a very neat alternative to the built in events in AS3, much more versatile.

      So basically, in pure vanilla AS3 terms, that line in my code would be something like –

      dispatch(new Event(“USER_HAS_CONNECTED));

      And then I would send the clientObject out as well somehow. But using signals, I can dispatch an “event” and also pass out arguments with the event. So the class that is listening for the signal, receives the signal, and the variable being passed by the signal as well.

      Hope that makes sense.

      Cheers.


  4. Nicholas says:


    No problem.

    Do you understand what I was saying about signals though? And how I’m passing the clientObject out to other clients?


    • Steven says:


      erm, i understand how u passing the clientObject out to other clients, but then a little bit confuse about the signal writing thingy.


  5. Nicholas says:


    This is pseudo code for dispatching the signal. This would be done somewhere in the class that is managing the NetStatus event.

    class Connection { 
    	public var clientObjectSignal:Signal = new Signal();
     
    	// Whenever a user connects to the group, this function should send out the user's info to the userlist in the UserList class
    	private function UserHasConnected() : void {
    			var peerObject:Object = new Object();
    			peerObject.peerID = e.info.message.peerID.toString();
    			peerObject.label = e.info.message.username;
    			peerObject.timeTag = new Date().time;
    			clientObjectSignal.dispatch(peerObject);
    	}
    }

    Then in the UserList that contains the user list, you would listen for the signal like this:

    class UserList { 
    	var connection:Connection;
     
    	connection = new Connection();
    	connection.clientObjectSignal.add(ClientConnectHandler);
     
    	private function ClientConnectHandler(peerObj:Object) : void {
    		// Add user to userlist using the properties of the object passed in.
    	}
    }

    I hope this helps and makes some sense. This is all pseudo code, so you have to implement it into your code in a way that works.

    Let me know if you need any more help.


  6. Steven says:


    Hey Nicholas,

    Much more understand now… thanks for the explanation.. haha… you r a great teacher. Have a nice day buddy.


  7. James says:


    I will post the source code for the client soon, as soon as I clean it up and maybe add some comments.

    Is the source code available to download? Thanks


    • Nicholas says:


      Sadly no. I never got around to cleaning that code to a point where I could put it out into the wild.

      Do you have an issue that I could maybe help you with?


Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Copyright © 2012 Nicholas Ng-A-Fook | All Rights Reserved