Week of AFCS Revisited: SharedObject

After my trainwreck post on using CollectionNode in AFCS Nigel suggested that I try to recreate my example using the higher level APIs instead of trying to build everything from scratch with NodeCollection. As I mentioned in the previous post, the AFCS team did a great job of creating very basic APIs like CollectionNode and then building on top of those to make APIs that are easier to use. SharedObject is a perfect example of that.

Here’s what I wanted to accomplish: Create a basic multiplayer game where players try to match colors with each other using a ColorPicker. Using the SharedObject class it’s really pretty easy. The first thing I did was set up a room where guests are automatically promoted and no username/password is required. Then I used a combination of AFCS components and Flex components to create a user interface for entering a display name, picking the color, and showing how many players there are:

<rtc:AdobeHSAuthenticator id="auth" />
<rtc:ConnectSessionContainer id="cSession" 
          authenticator="{auth}" 
          roomURL="{_roomURL}"
          synchronizationChange="cSession_synchronizationChangeHandler(event)"
     <mx:Panel id="panelColor" title="Pick a Color!"
          horizontalCenter="0" verticalCenter="0" alpha=".3" 
          width="200" height="200" enabled="false"
          <mx:ColorPicker id="color" change="color_changeHandler(event)" width="100%" height="100%" />
<mx:Label text="Playing with {cSession.userManager.userCollection.length - 1} other people" />
</mx:Panel>
 
<mx:TitleWindow id="tw" horizontalCenter="0" verticalCenter="0"
          <mx:TextInput id="username" width="150" text="Enter A Username" focusIn="{username.text = ''}" />
<mx:Button id="btnLogin" label="Login" click="btnLogin_clickHandler(event)" />
</mx:TitleWindow>
</rtc:ConnectSessionContainer>

Nothing major there, notice we’re using the userManager class to get the number of other users in the room (and that it’s bindable). The big thing is the synchronizationChange event handler. In that function we’re going to configure our node and create the SharedObject.

public var sharedColor:com.adobe.rtc.sharedModel.SharedObject;
 
protected function cSession_synchronizationChangeHandler(event:SessionEvent):void
{
if( event.type == SessionEvent.SYNCHRONIZATION_CHANGE )
{
var config:NodeConfiguration = new NodeConfiguration();
config.userDependentItems = true;
 
sharedColor = new com.adobe.rtc.sharedModel.SharedObject();
sharedColor.sharedID = "color";
sharedColor.setNodeConfiguration(config);
sharedColor.subscribe();
sharedColor.addEventListener(SharedObjectEvent.PROPERTY_ADD, onPropertyChange);
sharedColor.addEventListener(SharedObjectEvent.PROPERTY_CHANGE, onPropertyChange);
sharedColor.addEventListener(SharedObjectEvent.PROPERTY_REMOVE, onPropertyRemove);
}
}

We have to use the full namespace when we reference the SharedObject in AFCS because it conflicts with the normal Flash SharedObject. We first do a check go make sure we’re in a SYNCHRONIZATION_CHANGE event and then start setting things up. The only change I make to the default NodeConfiguration is to make it so that when a user leaves the room, they take their items with them so we don’t have “ghost” matches. That’s done by setting the userDependentItems property to false.

Next we create our SharedObject. I give it a sharedID of “color” and then make sure the application is subscribed to the SharedObject so that it sees any changes. Finally I set up event handlers for whenever a property is changed, added and removed. The add and change properties use the same function, onPropertyChange.

Let’s first take a look at how we create the SharedObject; something that happens whenever we change our color selection.

protected function color_changeHandler(event:ColorPickerEvent):void
{
sharedColor.setProperty(cSession.userManager.myUserID,color.selectedColor);
}

We use the setProperty() method on the SharedObject to give it information. A SharedObject is just a name/value pair, or in this case, a propertyName/value pair. I set the propertyName to the userID so we can know where the color came from and then set the value to our selected color. Whenever we set that property it will create a property change event and call our event handler.

protected function onPropertyChange(event:SharedObjectEvent):void
{
if( event.propertyName != cSession.userManager.myUserID)
{
if(event.value == color.selectedColor)
{
var user:UserDescriptor = cSession.userManager.getUserDescriptor(event.propertyName);
sharedColor.removeProperty(cSession.userManager.myUserID);
Alert.show("You matched with " + user.displayName + "!");
}
}
}

This is where the game starts to happen. The way AFCS works is that when you change a something locally inside of your application it is sent to the server and then you get a change event when it comes back. This is so you can be sure the change was successful. In our game we want to make sure that we’re not dealing with the event we just sent so I use the propertyName property and compare it to myUserID to see if it’s an event that the user sent him or herself. If it isn’t, we look for a match using the value property and our selectedColor. If we have a match I use the UserManager class to get the display name of the matched user and pop up an alert box to show that we found a match.

The other thing I do is to call the removeProperty() method the user’s SharedObject. I ran into an issue where when a user selected a color that matched another one, the other user would get the popup but there was no easy way to notify the user who selected the color that it was a match. I solved this by removing the property which would then trigger a PROPERTY_REMOVE event and fire our event handler.

protected function onPropertyRemove(event:SharedObjectEvent):void
{
var user:UserDescriptor = cSession.userManager.getUserDescriptor(event.propertyName);
if( event.propertyName != cSession.userManager.myUserID && user != null)
{
Alert.show("You matched with " + user.displayName + "!");
}
}

It looks similar to our change event handler. I first get the user information for the popup and then I check a couple of things. First, I make sure that the event isn’t coming from the current user, but the user I have the match for. I also want to make sure that the SharedObject wasn’t removed by someone just logging out (remember our NodeConfiguration settings). If my user is null then that means the user is gone and the event fired because the user left the room and not because I specifically removed it.

That’s pretty much all there is to it. You can grab the project here or I’ve embedded the game below (and here’s a direct link), so feel free to play and see if you can match colors!

data=”http://www.digitalbackcountry.com/afcs/colorgame/ColorGame.swf”
width=”450″
height=”400″>

Show Comments

Hide Comments

Comments are closed.