Room Variables Tutorial
From EsWiki
This is a tutorial that covers the steps of editing and viewing Room Variables.
Contents |
What You'll Learn
- How to use Room Variables
- How to let the user view the Room Variables of a room
- How to let the user add, modify, and delete Room Variables
Prerequisites
- You must have ElectroServer 4 installed and running.
- You need the client-side ElectroServer 4 API.
- This tutorial continues after the User Variables tutorial and uses it as base. It is recommended you complete that tutorial or download its source files.
Download
If you wish to simply download the entire set of Room Variables files (source code and compiled), it is found in RoomVariablesTutorial.zip.
Let's Get Started!
In the steps below you will allow users to view, add, modify, and delete Room Variables.
The Plan
First we should plan out what we intend to add to the Advance Chat with User Variables.
- Allow the user to add, modify, and delete Rooms Variables
- Allow the user to view the Room Variables of the room they are in
Next, lets figure out how we will handle this from the User Interface side of things, reusing as much as possible from the User Variables tutorial.
What we will do is add a button to open and edit user variables, which will open up a window exactly like the one to edit user variables. Also, when the user clicks on the room they are in from the list, it will display the room variables.
Open the .fla, and go into the Interface movieclip. On the chat frame, add a new Button and label it c_roomVariablesButton. Now go into the library, and duplicate (right click option) the UserVariableWindow. Name the duplicate RoomVariableWindow, and have it export for Actionscript with that name also.
Next, we will look at the changes we need to make the code.
Creating the RoomVariablesWindow Class
A lot of the functionality we are going to have that allows the users to edit Room Variables is already done for User Variables. We could make a parent class, and have each inherit from it and just override what needs to be changed, but this seems more complicated in this case. What we are going to do is just open the UserVariableWindow file, and save it as RoomVariableWindow.as, thus creating a copy.
Edit the name of the class in the file to be RoomVariableWindow.
Remove the imports, and use these instead (though many are used in both):
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.room.Room; import com.electrotank.electroserver4.message.request.UpdateRoomVariableRequest; import com.electrotank.electroserver4.message.request.CreateRoomVariableRequest; import com.electrotank.electroserver4.esobject.EsObject;
Also add these variable:
public var myRoom:Room; public static var roomVariableName:String = "masterRoomVar";
Quite a few functions will need a few edits, while some will need more extensive edits.
Lets begin looking at these changes.
Modifications to the RoomVariablesWindow Class
The closeWindow function needs to be changed to edit a different value on the Main object, as there is now two different windows.
public function closeWindow( event:MouseEvent )
{
disable();
parentMain.roomWindow = null;
parentMain.removeChild( this );
}
Next, the populate list function must be modified to request the current Room Variables, instead of the user variables.
It also now checks to make sure the array of "room variables" (the list of EsObjects within the single Room Variable that we will use) exists
before using it. If it doesn't, just clear the list and set the value the blank.
private function populateList( textInput_c:TextInput )
{
//Clear list
c_variableList.removeAll();
//Populate the drop down menu
var varHolder:EsObject = parentMain.getRoomVariables();
if( varHolder.doesPropertyExist( listName ) )
{
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 = "";
}
}
else
{
textInput_c.text = "";
}
}
The createOrUpdateVariable function works a little bit differently now. It first checks to see if any of our "custom room variables" exist, and if not creates an empty list to hold them. Then it updates the EsObject array as before, but now at the end, instead of sending an UpdateUserVariableRequest, it sends a CreateRoomVariableRequest or an UpdateRoomVariableRequest. The reason we need a choice at the end is because we don't know if the room variable exists yet and if it does not, the request sent to create it is different then the request sent to update it (though when using user variables, only update requests are sent, there are no creation requests).
Note: Remember that though we appear to creating more variables all the time in the room, we aren't actually creating any more RoomVariables. Instead we are just adding to the array of "custom room variables" that we have defined and which only uses one Room Variable. This allows us to contain which variables the user can edit in this manner, so we can use Room Variables for other reasons. So the CreateRoomVariableRequest should only be sent when the first "custom room variable" is added, but not when the following ones are.
private function createOrUpdateVariable( name:String, value:String )
{
var isCreate:Boolean = false;
var varHolder:EsObject = parentMain.getRoomVariables();
var currentVars:Array;
if( !varHolder.doesPropertyExist( listName ) )
{
isCreate = true;
currentVars = [];
varHolder.setEsObjectArray( listName, currentVars );
}
else
{
currentVars = 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 );
}
if( isCreate )
{
var createVariable:CreateRoomVariableRequest = new CreateRoomVariableRequest();
createVariable.setValue( varHolder );
createVariable.setName( roomVariableName );
createVariable.setRoomId( myRoom.getRoomId() );
createVariable.setZoneId( myRoom.getZoneId() );
es.send( createVariable );
}
else
{
var updateVariable:UpdateRoomVariableRequest = new UpdateRoomVariableRequest();
updateVariable.setValue( varHolder );
updateVariable.setName( roomVariableName );
updateVariable.setRoomId( myRoom.getRoomId() );
updateVariable.setZoneId( myRoom.getZoneId() );
es.send( updateVariable );
}
}
The last function we need to update in this class is the deleteVariables function.
The only changes we make here are ones to make this work with Room variables instead
of User variables. The variable varHolder now gets its EsObject from the "custom room variables"
instead of the "custom user variables". The request was modified to be an UpdateRoomVariableRequest
and now sets the room ID and zone ID, which is necessary for updating room variables.
Note: Again, though we are deleting variables, we use the UpdateRoomVariableRequest function since we are just shortening the array of EsObjects within the RoomVariable and not actually deleting the RoomVariable itself.
public function deleteVariable( event:MouseEvent )
{
var name:String = c_variableList.selectedLabel;
if( name!= "" )
{
var deleteVariable:UpdateRoomVariableRequest = new UpdateRoomVariableRequest();
var varHolder:EsObject = parentMain.getRoomVariables();
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( roomVariableName );
deleteVariable.setRoomId( myRoom.getRoomId() );
deleteVariable.setZoneId( myRoom.getZoneId() );
parentMain.setRoomVariables( varHolder );
populateList( c_displayValue );
es.send( deleteVariable );
}
}
}
Now we need to look at code changes to the Main class.
Modifying the Main class
First we need to add these variables to the Main class:
private var roomVariables:EsObject; public var roomWindow:MovieClip; public static var roomVariableName:String = "masterRoomVar";
And in the initialize function add this code:
roomVariables = new EsObject();
Add the following functions for retrieving and setting the EsObject that contains the list of custom room variables.
public function getRoomVariables():EsObject
{
return roomVariables;
}
public function setRoomVariables( newVars:EsObject ):void
{
roomVariables = newVars;
}
What we need to do now is add an event listener to listen for changes to Room Variables.
Add this code to add the event listener to the enableChatScreen function.
es.addEventListener(MessageType.RoomVariableUpdateEvent, "onRoomVariableUpdateEvent", this );
Then in the disableChatScreen function, add this code to remove it.
es.removeEventListener(MessageType.RoomVariableUpdateEvent, "onRoomVariableUpdateEvent", this );
Now we need to define the function that is called when the event occurs.
The onRoomVariableUpdateEvent functions checks to make sure the action was the variable being created or updated, as we won't handle the variable being deleted. Then it makes sure it is the RoomVariable with the correct name, the one that contains the list of our custom room variables. Then it retrieves the value and sets the roomVariables variable to it.
public function onRoomVariableUpdateEvent( e:RoomVariableUpdateEvent )
{
if( e.getUpdateAction() == RoomVariableUpdateEvent.VariableCreated || e.getUpdateAction() == RoomVariableUpdateEvent.VariableUpdated )
{
if( e.getName() == roomVariableName )
{
roomVariables = e.getValue();
}
}
}
In the next step, we will continue to look at the functions we need to change and add.
Continuing to Modify the Main Class
The onButtonClick function needs to be changed to display the room variables if the user clicks on the room they are in and open the room variables window if they press the correct button. To do this we check the name of the room they click on, and if it equals the current room the displayRoomVariables function is called to display the variables. Also, if they press the room variables button, the openRoomVariablesWindow 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 )
{
if( data.label == myRoom.getRoomName() )
{
displayRoomVariables();
}
autoFillRoomName( data.label );
}
}
else if(e.target == c_logoutButton )
{
logoutUser();
}
else if(e.target == c_userVariablesButton )
{
openUserVariablesWindow();
}
else if(e.target == c_roomVariablesButton )
{
openRoomVariablesWindow();
}
}
The displayRoomVariables function first makes sure the list of custom room variables exists, and if it
does the displayRoomInfo function is called to iterate through it. If there is no list, the no variables
found message is shown.
The displayRoomInfo function is virtually the same as the displayUserInfo function from last time, except it has slightly different messages shown.
private function displayRoomVariables()
{
if( roomVariables.doesPropertyExist( listName ) )
{
displayRoomInfo( roomVariables.getEsObjectArray( listName ) );
}
else
{
output( "This room has no user variables" );
}
}
private function displayRoomInfo( infoArray:Array )
{
//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( "This room has no user variables" );
}
else
{
output( "This rooms variables are: " );
}
while( ++i < length )
{
currentES = infoArray[i] as EsObject;
output( " The variable " + currentES.getString(varName) + " has a value of: " + currentES.getString( varValueName ) );
}
}
The openRoomVariablesWindow function is the same as the openUserVariablesWindow function, except
it edits the roomWindow variable instead of the window variable (and attaches a RoomVariablesWindow instance).
private function openRoomVariablesWindow():void
{
if( roomWindow == null )
{
roomWindow = new RoomVariableWindow();
addChild( roomWindow );
roomWindow.x = 300;
roomWindow.y = 100;
roomWindow.myRoom = myRoom;
roomWindow.enable( userID, es, this );
}
}
The last two functions we need to change are onJoinRoomEvent and onJoinFirstRoomEvent.
The part we need to add is to set the players room variables to the room variables
of the room they just joined. Note that it assumes there is only the one Room Variable
that contains the list of our custom room variables. If you wanted to allow other Room Variables,
you would have to add functionality to iterate through them and find the one that contains the list
of custom room variables (by checking the names).
public function onJoinRoomEvent(e:JoinRoomEvent):void
{
if( e.getRoomVariables().length == 1 )
{
setRoomVariables(e.getRoomVariables()[0].getValue());
}
else
{
setRoomVariables(new EsObject());
}
myRoom = e.room;
myZone = zoneManager.getZoneById( e.getZoneId() );
showUserList();
showRoomList();
}
public function onJoinFirstRoomEvent(e:JoinRoomEvent):void
{
if( e.getRoomVariables().length == 1 )
{
setRoomVariables(e.getRoomVariables()[0].getValue());
}
else
{
setRoomVariables(new EsObject());
}
myRoom = e.room;
myZone = zoneManager.getZoneById( e.getZoneId() );
disableLoginScreen();
gotoAndStop( "Prechat" );
}
And your finished!
Finished!
Well done, you have now added the ability for users to create, edit, delete and view room variables! From here you can look at Cursors Gone Wild Tutorial or Joining Games Tutorial.
Room Variables and the Web Admin
If you need to add a room variable to a persistent room using the web admin, the easiest way to determine the format is to write a quick Java or ActionScript program that populates an EsObject that would be the room variable, and then use the EsObject.toXML() method to display the XML version. Note: Java programmers need to wait for ES4.0.5 for this; Flash programmers have it already.
For example:
<Variables>
<Variable name='life' type='string' >42</Variable>
</Variables>
Don't try to save a room variable that has null value, or contains a null EsObject or an Array that has a null element.
