User Variables Tutorial
From EsWiki
This is a tutorial that covers the steps of editing and viewing user variables.
Contents
|
What You'll Learn
- How to use user variables
- How to let the user view the user variables of others
- How to let the user add, modify, and delete their own user variables
Prerequisites
- You must have ElectroServer 4 installed and running.
- You need the client-side ElectroServer 4 API.
- Advanced Chat Tutorial
Download
If you wish to simply download the entire set of User Variables files (source code and compiled), it is found in UserVariablesTutorial.zip.
Let's Get Started!
In the steps below you will allow users to view the user variables of others and add, modify, and delete their own.
Planning
First we should plan out what we intend to add to the Advanced Chat. Those things are:
- Allow the user to add, modify, and delete their own user variables
- Allow the user to view the user variables of others
Next, let us figure out how we can tackle this from the user interface side of things.
In this tutorial, we will add a button that opens a window to add, modify and delete variables. This window will contain a combo box to select: "Add", "Modify", or "Delete", with "Create" as the default. When one is selected, it will go to a different frame of the window movieclip. The add frame will have a text input for both the name of the variable and what value it will hold (all values in this example will be strings). The modify frame will have a combo box listing all the variables, and a text input box where the original value will be placed and can be changed. Finally, the delete command will have a combo box, and a text box that shows the current value. All of these screens will also have an do button (be it add the variable, modify it etc.. ) and a done button.
For showing the user variables of others, when a user clicks on a user in the list user list, it will output their user variables to the chat box.
Now that we have planned out the UI, lets begin to update it.
Setting up the UI
Create a new movieclip called UserVariableWindow, and export it for actionscript. Add 3 Layers: Labels, Actions, and Content.
Add a rectangle to the content frame and a listbox. Label the list box c_actionList and under that parameters window, edit the data Provider. Add the labels Add, Modify, and Delete, and the values 1,2 and 3. Create a button, label it c_doneButton, and place it so it is in the same position for all frames (component issue).
Next we are going to add 2 more frames for all layers. For your own clarity, it may be a good idea to space them out. (This is for that component issue). Label them Add, Modify, and Delete.
On the Add frame, add two text inputs, labelling them c_nameInput and c_valueInput. Also add a button, with a new of c_addButton. It may be a good idea for all of the input text boxes to be labeled with static text boxes.
On the Modify frame, add a combobox and label it c_variableList. Then add a text input labelled as c_modifyInput and a new button labelled c_modifyButton.
On the Delete frame reuse the same combobox (same position) and add the text input c_displayValue,
but set modify on it to false. Also add a delete button labelled as c_deleteButton.
Here are sample layouts for the Add, Modify and Delete frame respectively. Note: All frames show the "Add" in the Combo box since that is the default value.
Finally go into the Interface movieclip, and on the Chat frame, add a button and call it
c_userVariablesButton .
Congratulations, you have now finished setting up the UI for this tutorial.
Now we will look at the code that works with the UI.
UserVariableWindow class
What we will do now is similar to what we do when creating the first interface. We are going to link the movieclip to a class we will make. In should already be set up for the class UserVariableWindow.
Create a new actionscript file in the same folder called UserVariableWindow.as .
Class variables
First, lets define a few variables we will be using:
private var currentDisable:Function; //This is the function that should be called when a new type is selected private var userID:String; private var es:ElectroServer; private var parentMain:Main;
This will store a function, the id of the user, the electroserver object so it can connect to the server and a reference to the Main class.
Needed imports
We will also need these imports:
import com.electrotank.electroserver4.esobject.EsObject; import flash.display.MovieClip; import fl.events.ListEvent; import fl.controls.TextInput; import flash.events.MouseEvent; import flash.events.Event; import fl.controls.ComboBox; import fl.controls.Label; import com.electrotank.electroserver4.ElectroServer; import com.electrotank.electroserver4.message.event.UserVariableUpdateEvent; import com.electrotank.electroserver4.entities.UserVariable; import com.electrotank.electroserver4.message.request.UpdateUserVariableRequest;
enable, disable
One of the main things that will be needed in this example is the enabling and disabling of listeners between frames and at the creation and deletion of the window.
public function enable( userID_a:String, es_a:ElectroServer, parentMain_a:Main )
{
c_actionList.addEventListener( Event.CHANGE, selectionChanged );
c_doneButton.addEventListener( MouseEvent.CLICK, closeWindow );
userID = userID_a;
es = es_a;
parentMain = parentMain_a;
}
public function disable()
{
c_actionList.removeEventListener( Event.CHANGE, selectionChanged );
c_doneButton.removeEventListener( MouseEvent.CLICK, closeWindow );
}
The enable function accepts the userID, electroserver object, and the Main parent (this UserVariableWindow will be attached to the Main movieclip). It then sets up a listener for detecting when the combobox that selects what action is occuring is changed. The disable function removes these listeners.
closeWindow
The closeWindow functions calls the disable command, sets the property on the parent that would point to this to null, and removes it from the parents display.
public function closeWindow( event:MouseEvent )
{
disable();
parentMain.window = null;
parentMain.removeChild( this );
}
selectionChanged
The selectionChanged function is called when the combobox selection is changed. It then looks at the current index of the list, and depending on what it is, does the appropriate actions. You will see the currentDisable function is called in all options. This function is assigned to the disable function of what type of action it is currently on ( eg if you are on the add frame, currentDisable = disableAdd ) and thus removes the correct listeners. The frame is then changed to the correct one.
public function selectionChanged(event:Event)
{
if( c_actionList.selectedIndex == 0 )
{
//Add has been selected
currentDisable();
gotoAndStop("Add");
}
else if( c_actionList.selectedIndex == 1 )
{
//Modify has been selected
currentDisable();
gotoAndStop("Modify");
}
else if( c_actionList.selectedIndex == 2 )
{
//Delete has been selected
currentDisable();
gotoAndStop("Delete");
}
}
UserVariableWindow movieclip
Go back into the UserVariableWindow movieclip, and on frame "Add" add the action: enableAdd();, on frame "Modify" add the action enableModify();, and on "Delete" add the action enableDelete();.
Next we will look at the other enable and disable functions.
enableAdd, disableAdd, addVariable
Each of the frames, because of the different listeners needed, have different enable and disable commands.
The enableAdd functions adds a listener for the button click, and sets currentDisable equal to disableAdd. This means when currentDisable is called next, it will really call disableAdd.
The disableAdd function just removes the button listener.
public function enableAdd()
{
c_addButton.addEventListener( MouseEvent.CLICK, addVariable );
currentDisable = disableAdd;
}
public function disableAdd()
{
c_addButton.removeEventListener( MouseEvent.CLICK, addVariable );
}
The addVariable function gets the name and value of the variable from the text inputs, makes sure that neither of them are blank, and then calls the createOrUpdateVariable function that will send the newly created variable to the server.
public function addVariable( event:MouseEvent )
{
var name:String = c_nameInput.text ;
var value:String = c_valueInput.text;
if( name != "" && value !="" )
{
createOrUpdateVariable( name, value );
}
}
The createOrUpdateVariable function deals heavily with the idea of UserVariables, which
we will discuss next.
EsObject
First we need to discuss what an EsObject is. An EsObject is a generic object that can contain various types of parameters. It works by setting up variable name/value pairs. The values can be of many different types, from Strings to Floats to Integers. To add a value you simply call a function on the EsObject. For example: ob.setString( "name", "value" ). To retrieve it simply use ob.getString( "name" ). For each data type there is a different getter/setter. The EsObject is the main type of communication as it allows great flexibility.
The way UserVariables work is there is a name and EsObject pair for each User Variable ( meaning each EsObject has a name, and within the EsObject, each of its properties have names ). So when you create a variable, you just send a message to the server with the name of the variable and the corresponding EsObject. When requesting UserVariables (either your own, or others), you simply send a request containing the names of all the variables you want to receive. The problem in this situation is we are allowing each user to name and create their own variables, and thus don't know the names. To work around this, we will have just one name/EsObject pair ( the name will be "masterObject"), but this EsObject will contain an array of EsObjects (the array will be named "list" ). (Not only can EsObjects support different data types, they also support arrays). So we just need to iterate through the array, retrieving our own name/value pairs that are held in each EsObject that is in the array.
So what the user keeps track of is "masterObject". This will be stored in the Main Class, where it will be updated when the player changes a value on it. Once a value is changed it is sent to the server so anyone can see what each user has for its User Variables (though it is just one in this example).
Now that you have a better understanding of the underlying system, lets plow into the createOrUpdateVariable function.
createOrUpdateVariable
Class variables needed
Add these variables to both classes (Main and UserVariableWindow) :
public static var masterName:String = "masterObject"; public static var listName:String = "list"; public static var varName:String = "varName"; public static var varValueName:String = "varValue"; public static var userVariableName:String = "masterUserVar";
These variables will just store the names of the different parts of our nested EsObjects.
createOrUpdateVariable function
The createOrUpdateVariable function uses an UpdateUserVariableRequest which will contain the name of the variable and the EsObject that associates with it. We already the know the name will be userVariableName (as we are only really using one UserVariable), but we must determine the EsObject to send. The EsObject we are going to use contains an array of EsObjects. Each of those EsObjects has a String name and String value. When a variable is updated/created, we check to see if it exists. First we get the master EsObject from parentMain (with a function we will add), then extract the array of EsObjects. We then iterate over the EsObjects, trying to find one with the same name and if found, adjust its value. If none is found, we add a new EsObject onto the array, with the correct name and values set. Then we set the name of the user variable and the EsObject on to the request and send it.
Place this function in the UesrVariableWindow class.
private function createOrUpdateVariable( name:String, value:String )
{
var updateVariable:UpdateUserVariableRequest = new UpdateUserVariableRequest();
var varHolder:EsObject = parentMain.getUserVariables();
var currentVars:Array = varHolder.getEsObjectArray( listName );
var currentES:EsObject;
var i:int = -1;
var length:int = currentVars.length;
var isFound:Boolean = false;
while( ++i < length )
{
currentES = currentVars[i] as EsObject;
if( currentES.getString( varName ) == name )
{
//Found the match
currentES.setString( varValueName, value );
isFound = true;
break;
}
}
if( !isFound )
{
//Create the variable, does not exist
var newES:EsObject = new EsObject();
newES.setString( varName, name );
newES.setString( varValueName, value );
currentVars.push( newES );
}
updateVariable.setValue( varHolder );
updateVariable.setName( userVariableName );
es.send( updateVariable );
}
This functions covers a lot of ideas all at once, so it may be a good idea to review it once or twice.
Next we will look at the changes to the Main class.
Main class
Now, lets look at the Main class.
Needed imports
Add the following imports:
import com.electrotank.electroserver4.esobject.EsObject; import com.electrotank.electroserver4.message.event.PrivateMessageEvent; import com.electrotank.electroserver4.message.event.UserVariableUpdateEvent; import com.electrotank.electroserver4.entities.UserVariable; import com.electrotank.electroserver4.message.request.GetUserVariablesRequest; import com.electrotank.electroserver4.message.response.GetUserVariablesResponse; import com.electrotank.electroserver4.message.request.UpdateUserVariableRequest;
Class variables
And these new variables:
public var window:MovieClip; private var userID:String; private var userVariables:EsObject;
onButtonClick
The function we again need to change is onButtonClick. When a user is clicked on now, the displayUserVariables function is also called which will show all the user variables for the given user.
Also if the user variables button is pressed a function is called to open the window.
private function onButtonClick(e:MouseEvent):void
{
if (e.target == c_sendButton)
{
attemptSendMessage();
}
else if(e.target == c_privateMessage )
{
attemptToSendPrivateMessage();
}
else if(e.target == c_joinRoom )
{
attemptToJoinRoom();
}
else if(getQualifiedClassName(e.target) == "fl.controls.listClasses::CellRenderer")
{
//Object is of type CellRenderer
var cell:CellRenderer = e.target as CellRenderer;
var data:ListData = e.target.listData as ListData;
if(data.owner == c_userList )
{
displayUserVariables( data.label );
autoFillUserName( data.label );
}
else if(data.owner == c_roomList )
{
autoFillRoomName( data.label );
}
}
else if(e.target == c_logoutButton )
{
logoutUser();
}
else if(e.target == c_userVariablesButton )
{
openUserVariablesWindow();
}
}
openUserVariablesWindow
The openUserVariablesWindow function makes sure a window is not already open, then attaches and places the UserVariableWindow and then enables it.
private function openUserVariablesWindow():void
{
if( window == null )
{
window = new UserVariableWindow();
addChild( window );
window.x = 100;
window.y = 100;
window.enable( userID, es, this );
}
}
displayUserVariables
The displayUserVariables function sends a request to the server to return the user variables for the given user. We are going to add a listener so that any time we get a result from a GetUserVariablesRequest we ouptut the data (as this is the only thing that uses it).
private function displayUserVariables( userName:String )
{
var getRequest:GetUserVariablesRequest = new GetUserVariablesRequest();
getRequest.setUserName( userName );
getRequest.addUserVariableName( userVariableName );
es.send(getRequest);
}
onLoginResponse
Another thing we need to do is update the onLoginResponse function to both save the userID and to set up the initial user variables.
public function onLoginResponse(e:LoginResponse):void
{
trace("After Log in Attempt, getIsLoggedIn returns: " + es.getIsLoggedIn().toString() );
isLoginRequestSent = false;
if (e.getAccepted()) {
trace("LOGIN ACCEPTED");
output("Login accepted.");
userID = e.getUserId();
intializeUserVariables();
joinRoom("Lobby");
} else {
trace("LOGIN FAILED");
output("Login failed: "+e.getEsError().getDescription());
//Assume the error is name related
nameInUse.alpha = 1;
}
}
initializeUserVariables
The initializeUserVariables function sets up the initial EsObject that has the list, but sets it to an empty list. If this step isn't done and the object is queried looking for the array it will through an error as the array does not exist, we must at least have an empty array. It then sends this to the server, so if anyone queries it, it won't throw errors.
private function intializeUserVariables()
{
//Give a default value to user variables
//before we get a response
userVariables = new EsObject();
userVariables.setEsObjectArray( listName, [] );
//Then send a request to server to create the appropriate
//user variable, so we don't have errors
var updateVariable:UpdateUserVariableRequest = new UpdateUserVariableRequest();
updateVariable.setValue( userVariables );
updateVariable.setName( userVariableName );
es.send( updateVariable );
}
Congratulations! You are halfway through the tutorial!
Next we will look at the added event listeners and outputting data received from user variable requests.
onGetUserVariablesResponse
enableChatScreen, disableChatScreen
The enable and disable chat functions need to be changed to add a new event listener. This event listener is called when we get a response from the GetUserVariableRequest. Also, if the window is open when the user leaves the screen, it is now removed.
private function enableChatScreen()
{
es.addEventListener(MessageType.PublicMessageEvent, "onPublicMessageEvent", this);
es.addEventListener(MessageType.UserListUpdateEvent, "onUserListUpdateEvent", this);
es.addEventListener(MessageType.JoinRoomEvent, "onJoinRoomEvent", this);
es.addEventListener(MessageType.ZoneUpdateEvent, "onZoneUpdateEvent", this);
es.addEventListener(MessageType.PrivateMessageEvent, "onPrivateMessageEvent", this);
es.addEventListener(MessageType.GetUserVariablesResponse, "onGetUserVariablesResponse", this);
addEventListener(MouseEvent.CLICK, onButtonClick);
showUserList();
showRoomList();
}
private function disableChatScreen()
{
es.removeEventListener(MessageType.PublicMessageEvent, "onPublicMessageEvent", this);
es.removeEventListener(MessageType.UserListUpdateEvent, "onUserListUpdateEvent", this);
es.removeEventListener(MessageType.JoinRoomEvent, "onJoinRoomEvent", this);
es.removeEventListener(MessageType.ZoneUpdateEvent, "onZoneUpdateEvent", this);
es.removeEventListener(MessageType.PrivateMessageEvent, "onPrivateMessageEvent", this);
es.removeEventListener(MessageType.GetUserVariablesResponse, "onGetUserVariablesResponse", this);
removeEventListener(MouseEvent.CLICK, onButtonClick);
if( window != null )
{
window = null;
removeChild( window );
}
}
onGetUserVariablesResponse function
The onGetUserVariablesResponse function calls the displayUserInfo function, passing in the array of EsObjects and the name of the user. (The list is extracted by getting the correct UserVariable by its name, then getting its EsObject (which is the "masterObject"), and then getting the array of EsObjects).
The displayUserInfo function takes the array of data, and if the size it not zero, iterates over it exporting all the variable names and variables values.
public function onGetUserVariablesResponse( e:GetUserVariablesResponse )
{
displayUserInfo( e.getUserVariableByName( userVariableName ).getValue().getEsObjectArray( listName ), e.getUserName() );
}
private function displayUserInfo( infoArray:Array, userName:String )
{
//infoArray is an array of es object
//with name and value data
var currentES:EsObject;
var i:int = -1;
var length:int = infoArray.length;
if( length == 0 )
{
output( userName+" has no user variables. ");
}
else
{
output( userName+"'s user variables are: " );
}
while( ++i < length )
{
currentES = infoArray[i] as EsObject;
output( " The variable " + currentES.getString(varName) + " has a value of: " + currentES.getString( varValueName ) );
}
}
getUserVariables, setUserVariables
Finally, we need to add a getter/setter for the userVariables EsObject (which contains the EsObject list ).
public function getUserVariables():EsObject
{
return userVariables;
}
public function setUserVariables( newVars:EsObject ):void
{
userVariables = newVars;
}
Next, we will finish off the UserVariableWindow class.
Modifying a User Variable
enableModify, disableModify
We left off working on the add functionality, and we will now look at the modify and delete functionality.
The enableModify and disableModify are similar to the enableAdd and disableAdd, in that they set up and remove the listeners. The enableModify is a bit more complicated due to the list that is populated with the current variables. The populateList functions fills the list, and the Event.CHANGE listener is called when the selected item of the combo box is changed.
public function enableModify()
{
c_modifyButton.addEventListener( MouseEvent.CLICK, modifyVariable );
c_variableList.addEventListener( Event.CHANGE, variableSelected );
currentDisable = disableModify;
populateList( c_modifyInput );
}
public function disableModify()
{
c_modifyButton.removeEventListener( MouseEvent.CLICK, modifyVariable );
c_variableList.removeEventListener( Event.CHANGE, variableSelected );
}
populateList
The populateList functions clears the combo box, then finds the array of EsObjects. It then iterates through them, adding to the list an item with a name given by the EsObject. It accepts a TextInput parameter, and will set the text of it to the first value if there is at least one value, otherwise it will set it to blank.
private function populateList( textInput_c:TextInput )
{
//Clear list
c_variableList.removeAll();
//Populate the drop down menu
var varHolder:EsObject = parentMain.getUserVariables();
var currentVars:Array = varHolder.getEsObjectArray( listName );
var i:int = -1;
var length:int = currentVars.length;
var currentES:EsObject;
while( ++i < length )
{
currentES = currentVars[i] as EsObject;
c_variableList.addItem( {label:currentES.getString( varName ), value:currentES.getString( varValueName )} )
}
if( currentVars.length > 0 )
{
//Set the value to the value of the first item
//Note: Combo box properties(like selectedLabel can't be used for one frame )
textInput_c.text = currentVars[0].getString( varValueName );
}
else
{
textInput_c.text = "";
}
}
modifyVariable
The modifyVariable function makes sure neither the variable name nor the value are blank, and then uses the createOrUpdateVariable function to update the variable. The createOrUpdateVariable should just be an update and should find the variable in the list already.
The variableSelected function (called when a new combo box choice is selected) sets the text box on the Modify frame to be the value associated with the given item.
public function modifyVariable( event:MouseEvent )
{
var name:String = c_variableList.selectedLabel;
var value:String = c_modifyInput.text
if( name != "" && value != "" )
{
createOrUpdateVariable( name, value );
}
}
public function variableSelected( event:Event )
{
c_modifyInput.text = findValueFromName( c_variableList.selectedLabel );
}
We will now look at the delete functionality.
Deleting a User Variable
The final part is the delete functionality. The delete is similar to both the modify and the add functionality. Similar listeners are set up and removed, and the variablesSelected has become variablesSelected_d just for the sake of having a different text box name changed (which is not the best coding practice, but component issue stops us from reusing the name).
enableDelete, disableDelete
public function enableDelete()
{
c_deleteButton.addEventListener( MouseEvent.CLICK, deleteVariable );
currentDisable = disableDelete;
populateList( c_displayValue );
c_variableList.addEventListener( Event.CHANGE, variableSelected_d );
}
public function disableDelete()
{
c_deleteButton.removeEventListener( MouseEvent.CLICK, deleteVariable );
c_variableList.removeEventListener( Event.CHANGE, variableSelected_d );
}
public function variableSelected_d( event:Event )
{
c_displayValue.text = c_variableList.selectedItem.value;
}
deleteVariable
The final function of this tutorial is deleteVariable, which is similar to createOrUpdateVariable function, accept when it finds the matching name, it removes that EsObject from the array and submits the updated EsObject to the server. It then repopulates the list to reflect the removed item.
public function deleteVariable( event:MouseEvent )
{
var name:String = c_variableList.selectedLabel;
if( name!= "" )
{
var deleteVariable:UpdateUserVariableRequest = new UpdateUserVariableRequest();
var varHolder:EsObject = parentMain.getUserVariables();
var currentVars:Array = varHolder.getEsObjectArray( listName );
var currentES:EsObject;
var i:int = -1;
var length:int = currentVars.length;
var isFound:Boolean = false;
while( ++i < length )
{
currentES = currentVars[i] as EsObject;
if( currentES.getString( varName ) == name )
{
//Found the match
currentVars.splice( i, 1 );
isFound = true;
break;
}
}
if( isFound )
{
deleteVariable.setValue( varHolder );
deleteVariable.setName( userVariableName );
parentMain.setUserVariables( varHolder );
populateList( c_displayValue );
es.send( deleteVariable );
}
}
}
Finished!
Well done! You have now added User Variables to your improving chat and game system! The next lesson is either Room Variables Tutorial or Cursors Gone Wild Tutorial.



