class ElectroServer extends XMLSocket { //=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+ //ElectroServer Class //XML Protocol Version 3.2.6 //ActionScript Version 2.0 //Author: Jobe Makar, jobe@electrotank.com //Company: Electrotank, Inc. http://www.electrotank.com // //Need help? http://www.electro-server.com/Forum // //Last edit: 11/11/2004 // //Started: 8/15/03 // //-----Revision History--------- // 9/18/03 Officially released // 9/30/03 Added version detection. If class connects to an older ElectroServer 3 version then an error appears // 10/8/03 Fixed createRoom() auto_join bug. Now behaves properly // 10/11/03 Fixed chat message bug. A pure number chat message was being converted to a number on reentry // 10/20/03 Changed the way variables are parsed in received plugin message // 11/5/03 Fixed a case sensitive issue with room variables // 11/5/03 Fixed a sendMove/moveReceived issue // 11/7/03 Added FloodingFilterEnabled support // 11/12/03 In createGameRoom I added support of either description or Description, case insensitive // 12/14/03 Added the ability to receive an array of variables in the loggedIn event // 12/16/03 Added setModeratorLabel(true|false, "[Mod]") -- If someone is a moderator then their label gets "[Mod]" appended // 1/5/04 Fixed default zone name issue // 1/19/04 Fixed user variable support issues // 2/1/04 Added getLoggedInUserCount(). Requests the total number of users logged into the server in all zones // 2/1/04 Added getServerTime(). Returns the number of miliseconds since 1970. // 2/12/04 getRoomsInZone('zone name') added. It is there for clients that recieve higher loads of bandwidth. It triggers onRoomsInZoneLoaded // 2/23/04 Fixed a WDDX GetRoomsInZone parsing bug // 2/26/04 Added a hidden room bug work around to class. // 5/6/04 Added event for the response of getLoggedInUserCount. It is es.loggedInUserCountUpdated(num_logged_in) // 5/22/04 Added an event to capture the getServerTime method results. onGetServerTime // 8/8/04 Fixed a bug that caused the userlist to show incorrect results // 8/15/04 Added rawMessageReceived() // 8/28/04 Fixed userVariable client-side data saving bug // 9/26/2004 Added buddy stuff. es.addBuddy("jobem"), es.removeBuddy("jobem"), es.buddyLoggedIn event, es.buddyLoggedOut event // 9/26/2004 Added getUserLocation("jobem") es.userLocationLoaded event is fired when loaded. username, zone, and room are passed in as a string // 9/26/2004 Added getUsersInRoom("zone name", "room name"). es.usersInRoomLoaded event is fired. An array of objects with a username property is returned // 10/15/2004 Enhanced usersInRoomLoaded event. It now also returns the room and zone names // 11/10/2004 Fixed a uservariable update bug. // 11/11/2004 Fixed a createGameRoom bug where updatable was always false. // 12/15/2004 Added a 4th param to the userListUpdated event, it is a reference to the user object // 1/13/2005 Added an optional 3rd parameter to the login(name, pass, variables) method, 'variables'. It is an object that can contain name value pairs, captured by the login event handler on the server // // //!!! Download ElectroServer class updates from here: http://www.electrotank.com/ElectroServer/downloads.asp // //=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+=~=+ private static var _instance:ElectroServer; var MajorVersion:Number = 3; var MinorVersion:Number = 2; var SubVersion:Number = 0; var debug:Boolean = false; var showModeratorLabel:Boolean = false; var moderatorLabel:String; private var ip:String, port:Number, ConnectedToServer:Boolean, pendingRoom:String; var inGame:Boolean, joiningRoom:Boolean, pendingZone:String, zone:Object, auto_join:Boolean; var myUser:Object, isGameMaster:Boolean, configXML:String, username:String, password:String; var buddyList:Array; //Events var onConnection:Function, loggedIn:Function, zoneChanged:Function, roomListUpdated:Function; var roomVariablesUpdated:Function, roomJoined:Function, userListUpdated:Function, messageReceived:Function; var moveReceived:Function, usersRenumbered:Function, pluginMessageReceived:Function, zoneUpdated:Function; var configurationLoaded:Function, connectionClosed:Function, userVariableUpdated:Function, onRoomsInZoneLoaded:Function; var loggedInUserCountUpdated:Function, onGetServerTime:Function, rawMessageReceived:Function; var buddyLoggedIn:Function, buddyLoggedOut:Function, userLocationLoaded:Function, usersInRoomLoaded:Function, allZonesLoaded:Function; // var nodeNameList:Object = {Users:true, Zones:true, UserVariables:true, RoomVariables:true, Rooms:true, Variables:true, BannedUsers:true, Plugins:true, Moderators:true, Words:true, RootWords:true, Templates:true}; var isConnected:Boolean = false; //Cunstructor private function ElectroServer() { buddyList = new Array(); } private function ValidateVersion(Major:Number, Minor:Number, Sub:Number) { var ReturnVal:Boolean = true; if (MajorVersion>Major || isNaN(Major)) { ReturnVal = false; } else if (MinorVersion>Minor && MajorVersion == Major) { ReturnVal = false; } else if (SubVersion>Sub && MinorVersion == Minor) { ReturnVal = false; } return ReturnVal; } // private function onClose() { isConnected = false; connectionClosed(true); } function removeBuddy(name:String) { for (var i = 0; i"; send(action, parameters); } function getUsersInRoom(zone:String, room:String) { var action = "GetUsersInRoom"; var parameters = ""+zone+""+room+""; send(action, parameters); } function getUserLocation(name:String) { var action = "GetUserLocation"; var parameters = ""+name+""; send(action, parameters); } function getBuddyList():Array { return buddyList; } function addBuddy(name:String) { var buddyExists:Boolean = false; for (var i = 0; i"; send(action, parameters); } } function close() { super.close(); connectionClosed(false); } function getServerTime() { var action:String = "GetServerTime"; var parameters:String = ""; send(action, parameters); } function getLoggedInUserCount() { var action:String = "GetLoggedInUserCount"; var parameters:String = ""; send(action, parameters); } function setModeratorLabel(val:Boolean, str:String) { showModeratorLabel = val; moderatorLabel = str; if (str == undefined) { moderatorLabel = " [Moderator]"; } } public static function getInstance():ElectroServer { if (_instance == null) { _instance = new ElectroServer(); } return _instance; } function setDebug(val:Boolean) { debug = val; } function getDebug():Boolean { return debug; } function getIP():String { return ip; } function getPort():Number { return port; } function setIP(tempIP:String) { ip = tempIP; } function setPort(tempPort:Number) { port = tempPort; } function deleteRoomVariable(name:String) { var action:String = "DeleteRoomVariable"; var parameters:String = ""+name+""; send(action, parameters); } // function getRoomsInZone(tmp_zone:String) { var action:String = "GetRoomsInZone"; var parameters:String = ""+tmp_zone+""; send(action, parameters); } function changeRoomDetail(detail:String, value) { var action:String = "ChangeRoomDetails"; var minorAction:String; var field:String; detail = detail.toLowerCase(); var pwordXML:String = ""; if (detail == "description") { minorAction = "ChangeDescription"; field = "Description"; value.attributes.isGameRoom = true; var wddxOb = new Wddx(); var descXML = ""; } else if (detail == "updatable") { minorAction = "ChangeUpdatable"; field = "Updatable"; var descXML:String = value; } else if (detail == "hidden") { minorAction = "ChangeVisibility"; field = "Hidden"; var descXML:String = value; } else if (detail == "capacity") { minorAction = "ChangeCapacity"; field = "Capacity"; if (value == undefined || value == 0) { value = -1; } var descXML:String = value; } else if (detail == "password") { minorAction = "ChangePasswordProtected"; field = "Password"; var descXML:String = value; var protected:Boolean; if (value == undefined) { value = ""; } if (value.length>=1) { protected = true; } else { protected = false; } pwordXML = ""+protected+""; } var parameters:String = ""+minorAction+""+pwordXML+"<"+field+">"+descXML+""; send(action, parameters); } function kick(name:String, reason:String) { if (reason == undefined) { reason = ""; } var action:String = "ModeratorCommand"; var parameters:String = "Kick"+name+""+reason+""; send(action, parameters); } function ban(name:String, reason:String, expires) { if (reason == undefined) { reason = ""; } if (expires == undefined) { expires = "-1"; } var action:String = "ModeratorCommand"; var parameters:String = "Ban"+name+""+reason+""+expires+""; send(action, parameters); } function createUserVariable(name:String, value:String) { var action:String = "UpdateUserVariable"; var parameters:String = "Create"+name+""+value+""; send(action, parameters); } function getAllZones() { send("GetAllZones", ""); } function updateUserVariable(name:String, value:String) { var action:String = "UpdateUserVariable"; var parameters:String = "Update"+name+""+value+""; send(action, parameters); } function deleteUserVariable(name:String, value:String) { var action:String = "UpdateUserVariable"; var parameters:String = "Delete"+name+""; send(action, parameters); } function createRoomVariable(ob:Object) { var name:String = ob.name; var data = ob.data; var persistent:Boolean = ob.persistent; var locked:Boolean = ob.locked; if (persistent == undefined) { persistent = false; } if (locked == undefined) { locked = false; } if (zone.myRoom.roomVariables[name] == undefined) { var action:String = "CreateRoomVariable"; var parameters:String = ""+name+""; } else { var action:String = "UpdateRoomVariable"; var parameters:String = ""+name+""; } send(action, parameters); } function getZone() { return zone; } function sendMove(who, ob:Object) { var wddxOb:Wddx = new Wddx(); var moveXML:XML = wddxOb.serialize(ob); if (who.toLowerCase() == "all") { var action:String = "SendPublicMessage"; var parameters:String = "ActionMoveMoveTypePublic"; } else { var action:String = "SendPrivateMessage"; var users:Array = who; var usersXML:String = ""; for (var i = 0; i"; } usersXML += ""; var parameters:String = usersXML+"ActionMoveMoveTypePublic"; } send(action, parameters); } function pluginRequest(plugin:String, method:String, parameters:Object) { var action:String = "ExecutePlugin"; var variableXML:String = ""; if (parameters != undefined) { variableXML = ""; for (var i in parameters) { variableXML += ""+i+""+parameters[i]+""; } variableXML += ""; } var params:String = ""+plugin+""+method+""+variableXML; send(action, params); } function getRoomVariables() { return zone.myRoom.roomVariables; } function getRoomList() { return zone.rooms; } function getRoom() { return zone.myRoom; } function getUserList() { return zone.users; } function createGameRoom(roomOb:Object) { if (roomOb.attributes == undefined) { roomOb.attributes = new Object(); } roomOb.attributes.isGameRoom = true; if (roomOb.updatable != true) { roomOb.updatable = false; } roomOb.numbered = true; createRoom(roomOb); inGame = true; } function joinGame(room:String, password:String, type:String, zone:String) { type = type.toLowerCase(); if (type == "player" || type == undefined) { var numbered:Boolean = true; } else if (type == "spectator") { var numbered:Boolean = false; } joinRoom(room, password, zone, numbered); inGame = true; } function adminLogin(tempUsername:String, tempPassword:String) { myUser.username = tempUsername; if (tempPassword == undefined) { tempPassword = ""; } myUser.password = tempPassword; username = myUser.username; password = myUser.password; var action:String = "AdminLogin"; var parameters:String = ""+myUser.username+""+myUser.password+""; send(action, parameters); } function loadConfiguration() { var action:String = "LoadConfiguration"; var parameters:String = ""; send(action, parameters); } function parseConfig(config:String) { XML.prototype.ignoreWhite = true; var temp:XML = new XML(config); var params:Object = new Object(); parseParameters(temp.firstChild.childNodes, params); configurationLoaded(params); } function sendMessage(type:String, message:String, users:Array, variables:Object) { var type:String = type.toLowerCase(); if (type == "public" || type == "all") { //'users' is actually the variables for public messages sendPublicMessage(message, users); } else if (type == "private") { sendPrivateMessage(message, users, variables); } } function joinRoom(room:String, password:String, zoneName:String, numbered:Boolean) { var action = "JoinRoom"; if (numbered == undefined) { numbered = true; } if (password == undefined) { password = ""; } if (zoneName == undefined) { zoneName = getZone().name; if (zoneName == undefined) { zoneName = "Chat Area"; } } pendingRoom = room; inGame = false; var parameters:String = ""+zoneName+""+room+""+password+""+numbered+""; send(action, parameters); } function createRoom(roomOb:Object, auto:Boolean) { //roomOb properties: zone, roomName, hidden, numbered, UserVariablesEnabled, Description //Password, Capacity, roomVariables if (auto == undefined || auto == "true") { auto = true; } else { auto = false; } auto_join = auto; var action:String = "CreateRoom"; var password:String = roomOb.password; var UserVariablesEnabled:Boolean = roomOb.userVariablesEnabled; var hidden:Boolean = roomOb.hidden; var zoneName:String = roomOb.zone; var roomName:String = roomOb.roomName; var numbered:Boolean = roomOb.numbered; var capacity:Number = roomOb.capacity; var description:String = roomOb.description; var roomVariables:Array = roomOb.roomVariables; var updatable:Boolean = roomOb.updatable; var plugins:Array = roomOb.plugins; var FloodingFilterEnabled:Boolean = roomOb.FloodingFilterEnabled; var descOb:Object = new Object(); if (description == undefined) { description = roomOb.Description; if (description == undefined) { description = ""; } } if (FloodingFilterEnabled == undefined) { FloodingFilterEnabled = false; } descOb.description = description; if (roomOb.attributes != undefined) { descOb.attributes = roomOb.attributes; } var wddxOb:Wddx = new Wddx(); var descXML:XML = wddxOb.serialize(descOb); if (zoneName == undefined) { zoneName = getZone().name; if (zoneName == undefined) { zoneName = "Chat Area"; } } if (updatable == undefined) { updatable = true; } if (hidden == undefined) { hidden = false; } if (capacity == undefined) { capacity = -1; } if (numbered == undefined) { numbered = false; } if (password == undefined) { password = ""; } if (UserVariablesEnabled == undefined) { UserVariablesEnabled = false; } if (roomVariables == undefined) { var roomVariableXML:String = ""; } else { var roomVariableXML:String = ""; for (var i = 0; i"; str += ""+name+""; str += ""; str += ""; roomVariableXML += str; } roomVariableXML += ""; } if (plugins == undefined) { var pluginXML:String = ""; } else { var pluginXML:String = ""; for (var i = 0; i"+pluginVarValue+""; } pluginVariablesXML += ""; } pluginXML += ""+name+""+pluginVariablesXML+""; } pluginXML += ""; } var parameters:String = ""+zoneName+""; joiningRoom = true; pendingRoom = roomName; pendingZone = zoneName; inGame = false; send(action, parameters); } function sendPublicMessage(message:String, variables:Object) { var action:String = "SendPublicMessage"; //var parameters = "Test VarYaya"; var variableXML:String = ""; for (var i in variables) { var value:String = variables[i].toString(); variableXML += ""+i+""; } if (variableXML != "") { variableXML = ""+variableXML+""; } else { variableXML = ""; } var parameters:String = ""+variableXML; send(action, parameters); } function sendPrivateMessage(message:String, users:Array, variables:Object) { var action:String = "SendPrivateMessage"; var usersXML:String = ""; for (var i = 0; i"; } usersXML += ""; var variableXML:String = ""; for (var i in variables) { var value = variables[i]; variableXML += ""+i+""; } if (variableXML != "") { variableXML = ""+variableXML+""; } else { variableXML = ""; } var parameters:String = usersXML+""+message+""+variableXML; send(action, parameters); } function connectionResponse(success:Boolean) { //This occurs when the basic connection is established with the socket-server ConnectedToServer = true; if (!ConnectedToServer) { //Could not find server or server is turned off connectionEstablished(false, "Could not establish a server connection"); } } function connectionEstablished(success:Boolean, reason:String) { //Sucessfully connection to ElectroServer is a two stage process. //First you must establish a connection. Then ElectroServer must tell you that it is ok. if (success && ConnectedToServer) { //connected successfully isConnected = true; onConnection(true); } else { //Did not connect successfully onConnection(false, reason); } } function XMLReceived(info:XML) { var validXML:Boolean = info.toString().substr(0, 1) == "<"; if (validXML) { parseXML(info); } else { rawMessageReceived(info.toString()); } } function parseXML(data:XML) { //hidden //Every packet of XML that is pushed in from the server hits this method forist if (getDebug()) { trace("-----incomming----"); trace(data); } var info:Array = data.firstChild.childNodes; //Every XML packet has an action var action:String = info[0].firstChild.nodeValue; //Parse the parameters into easy-to-use data objects var parameters:Array = info[1].childNodes; var params:Object = new Object(); parseParameters(parameters, params); //Apply the action applyAction(action, params); } function isArrayNodeName(nodeName:String):Boolean { //hidden //The parseParameters function treats all nodes the same unless they are in // the 'nodeNameList', in which case they are converted to arrays //This function returns true|false depending on if the nodeName is in the list if (nodeNameList[nodeName]) { return true; } else { return false; } } function terminateHere(xmls:XML):Boolean { //hidden //This is used by parseParameters to determine how deep the XML goes return xmls.childNodes[0].hasChildNodes() ? false : true; } function send(action:String, parameters:String) { //hidden var message:String = ""+action+""+parameters+""; if (getDebug()) { trace("---out going----"); trace(message); } super.send(message); } function parseParameters(info:Array, parentOb:Object) { //hidden //This is a generic recursive method which walks the parameters node of the message //it parses the XML into generic data objects //==== // example: // Success // parses to parameters.results which is an object, the value of 'Results' is: // parameters.Results.value for (var i = 0; i"+password+""; if (variables != undefined) { parameters += ""; for (var i in variables) { parameters += ""; parameters += ""+i+""; parameters += ""+variables[i]+""; parameters += ""; } parameters += ""; } send(action, parameters); } function connect() { //Override the XMLSocket connect method onConnect = connectionResponse; onXML = XMLReceived; super.connect(getIP(), getPort()); } function getUser() { return myUser; } private function applyAction(action:String, params:Object) { if (action == "ConnectionResponse") { //This is a willful response from the server var Major:Number = Number(params.Version.attributes.Major); var Minor:Number = Number(params.Version.attributes.Minor); var Sub:Number = Number(params.Version.attributes.Sub); if (params.Result.value == "Accepted" && ValidateVersion(Major, Minor, Sub)) { connectionEstablished(true); } else { //did not connect properly if (params.Result.value == "Accepted" && !ValidateVersion(Major, Minor, Sub)) { var error = "ElectroServer 3 version is too old. Please install latest."; trace("====================================================="); trace("===============Error Error Error====================="); trace("This class file is newer than the version of ElectroServer 3 that you are using."); trace("AS 2.0 Class version: "+MajorVersion+"."+MinorVersion+"."+SubVersion); trace("ElectroServer version: "+Major+"."+Minor+"."+Sub); trace("If it says NaN above, then you definately need to update."); trace("Visit http://www.electrotank.com/ElectroServer/"); trace("====================================================="); trace("====================================================="); } else { var error = params.Reason.value; } connectionEstablished(false, error); } } else if (action == "LoginResponse") { if (params.Result.value.toLowerCase() == "accepted") { //var variables_array:Array = params.Variables.Variables; var variables:Array = params.Variables.Variables; var var_ob:Object = new Object(); for (var j=0;j-1 && auto_join) { joinRoom(pendingRoom, "", pendingZone); } } else { //Success, do nothing } } else if (action == "LoadConfiguration") { //This is for admin var num_packets:String = params.NumberOfPackets.value; var num:String = params.PacketNumber.value; var packetData:String = params.PacketData.value; if (num == "1") { configXML = packetData; } else { configXML += packetData; } if (num == num_packets) { parseConfig(configXML); } } else if (action == "UpdateUserVariable") { var users = zone.users; var ob = params.UserVariable; var tmp_user = ob.User.value; var value = ob.Data.value; var name = ob.Name.value; for (var i = 0; i2) { var foo:Wddx = new Wddx(); var ob:Object = foo.deserialize(WDDXxml); } else { var ob:Object = new Object(); } tmp_rooms[i].Description = ob; //var room:Object = zone.rooms[i]; //room.data = room; } onRoomsInZoneLoaded(tmp_rooms, params.Zone.Name.value); } else if (action == "RoomList") { var myOldRoom = getRoom(); var oldUsers = getUserList(); zone = new Object(); zone.numUsers = Number(params.Zone.attributes.Users); zone.name = params.Zone.Name.value; zone.rooms = params.Zone.Rooms.Rooms; for (var i = 0; i2) { var foo:Wddx = new Wddx(); var ob:Object = foo.deserialize(WDDXxml); } else { var ob:Object = new Object(); } zone.rooms[i].Description = ob; var room:Object = zone.rooms[i]; room.label = room.Name.value; room.data = room; if (myOldRoom.Name.value == room.Name.value) { zone.rooms[i] = myOldRoom; zone.myRoom = myOldRoom; zone.users = oldUsers; } } zoneChanged(zone.name); roomListUpdated(zone.rooms, "all"); } else if (action == "UpdateUserList") { var minorAction:String = params.MinorAction.value; if (minorAction == "UserJoined") { var users = zone.users; var user = params.User; // var user_name = user.Name.value; var uv = user.UserVariables.UserVariables; user.userVariables = new Object(); for (var k = 0; k2) { var foo:Wddx = new Wddx(); var ob:Object = foo.deserialize(WDDXxml); } else { var ob:Object = new Object(); } roomOb.Description = ob; break; } } roomListUpdated(zone.rooms, "roomupdated", roomOb); } else if (minorAction == "CreateRoom") { //Add a new room to the room list var roomOb:Object = newRoomOb; roomOb.label = roomOb.Name.value; roomOb.data = roomOb; var WDDXxml:XML = roomOb.Description.value; var foo:Wddx = new Wddx(); var ob:Object = foo.deserialize(WDDXxml); roomOb.Description = ob; zone.rooms.push(roomOb); roomListUpdated(zone.rooms, "roomcreated", roomOb); } else if (minorAction == "DeleteRoom") { //Room that already exists was deleted for (var j = 0; j