using System.Collections; using System.Collections.Generic; using System.Net; using System.Reflection; using System.Threading; using log4net; using Nwc.XmlRpc; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Communications.Cache; using OpenSim.Region.ClientStack; using OpenSim.Region.ClientStack.LindenUDP; namespace ModularRex.RexNetwork { public delegate void RexAppearanceDelegate(RexClientView sender); public delegate void RexFaceExpressionDelegate(RexClientView sender, List parameters); public delegate void RexAvatarProperties(RexClientView sender, List parameters); public delegate void ReceiveRexStartUp(RexClientView remoteClient, UUID agentID, string status); public delegate void ReceiveRexClientScriptCmd(RexClientView remoteClient, UUID agentID, List parameters); /// /// Inherits from LLClientView the majority of functionality /// Overrides and extends for Rex-specific functionality. /// /// In the case whereby functionality uses the same packets but differs /// between Rex and LL, you can use a override on those specific functions /// to overload the request. /// public class RexClientView : LLClientView, IClientRexFaceExpression, IClientRexAppearance { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private string m_rexAccountID; private string m_rexAvatarURL; private string m_rexAuthURL; private string m_rexSkypeURL; public string AvatarStorageOverride; public float RexCharacterSpeedMod = 1.0f; public float RexMovementSpeedMod = 1.0f; public float RexVertMovementSpeedMod = 1.0f; public bool RexWalkDisabled = false; public bool RexFlyDisabled = false; public bool RexSitDisabled = false; public event RexAppearanceDelegate OnRexAppearance; public event RexFaceExpressionDelegate OnRexFaceExpression; public event RexAvatarProperties OnRexAvatarProperties; public event ReceiveRexStartUp OnReceiveRexStartUp; public event ReceiveRexClientScriptCmd OnReceiveRexClientScriptCmd; public RexClientView(EndPoint remoteEP, IScene scene, AssetCache assetCache, LLPacketServer packServer, AuthenticateResponse authenSessions, UUID agentId, UUID sessionId, uint circuitCode, EndPoint proxyEP, ClientStackUserSettings userSettings) : base(remoteEP, scene, assetCache, packServer, authenSessions, agentId, sessionId, circuitCode, proxyEP, userSettings) { // Rex communication now occurs via GenericMessage // We have a special handler here below. OnGenericMessage += RealXtendClientView_OnGenericMessage; } public RexClientView(EndPoint remoteEP, IScene scene, AssetCache assetCache, LLPacketServer packServer, AuthenticateResponse authenSessions, UUID agentId, UUID sessionId, uint circuitCode, EndPoint proxyEP, string rexAvatarURL, string rexAuthURL, ClientStackUserSettings userSettings) : base(remoteEP, scene, assetCache, packServer, authenSessions, agentId, sessionId, circuitCode, proxyEP, userSettings) { // Rex communication now occurs via GenericMessage // We have a special handler here below. OnGenericMessage += RealXtendClientView_OnGenericMessage; RexAvatarURL = rexAvatarURL; RexAuthURL = rexAuthURL; } /// /// Registers interfaces for IClientCore, /// every time you make a new Rex-specific /// Interface. Make sure to register it here. /// protected override void RegisterInterfaces() { RegisterInterface(this); RegisterInterface(this); RegisterInterface(this); base.RegisterInterfaces(); } /// /// The avatar URL for this avatar /// Eg: http://avatar.com:10000/uuid/ /// public string RexAvatarURL { get { return m_rexAvatarURL; } set { m_rexAvatarURL = value; if (OnRexAppearance != null) { OnRexAppearance(this); return; } } } /// /// Skype username of the avatar /// eg: Skypeuser /// public string RexSkypeURL { get { return m_rexSkypeURL; } set { m_rexSkypeURL = value; } } /// /// The full Rex Username of this account /// Eg: user@hostname.com:10001 /// /// Note: This is not filled immedietely on /// creation. This property is filled in /// via Login and may not be availible /// immedietely upon connect. /// /// The above glitch is scheduled to be /// fixed by a new RexCommsManager which /// will allow this to be set at spawn in /// login. /// public string RexAccount { get { return m_rexAccountID; } set { // Todo: More solid data checking here. m_rexAccountID = value; RexAuthURL = m_rexAccountID.Split('@')[1]; } } /// /// The URL of the Avatar's Authentication Server /// Eg: http://authentication.com:10001/ /// public string RexAuthURL { get { return m_rexAuthURL; } set { if (value.Contains("@")) { m_rexAuthURL = "http://" + value.Split('@')[1]; } else { m_rexAuthURL = value; } // Request Agent Properties Asynchronously ThreadPool.QueueUserWorkItem(RequestProperties); } } /// /// Special - used to convert GenericMessage packets /// to their appropriate Rex equivilents. /// /// Eg: GenericMessage(RexAppearance) -> /// OnRexAppearance(...) /// void RealXtendClientView_OnGenericMessage(object sender, string method, List args) { //TODO: Convert to Dictionary if (method == "RexAppearance") if (OnRexAppearance != null) { OnRexAppearance(this); return; } if (method == "RexFaceExpression") { if (OnRexFaceExpression != null) { OnRexFaceExpression(this, args); return; } } if (method == "RexAvatarProp") { if(OnRexAvatarProperties != null) { OnRexAvatarProperties(this, args); return; } } if (method == "rexscr") { if (OnReceiveRexClientScriptCmd != null) { OnReceiveRexClientScriptCmd(this, AgentId, args); } } if (method == "RexStartup") { if (OnReceiveRexStartUp != null) { OnReceiveRexStartUp(this, AgentId, args[0]); } } m_log.Warn("[REXCLIENTVIEW] Unhandled GenericMessage (" + method + ") {"); foreach (string s in args) { m_log.Warn("\t" + s); } m_log.Warn("}"); } /// /// Sends a Rex Script Command to the viewer /// attached to this ClientView. /// /// If you are coding something, try use /// SendRex*** instead, as many of them /// will trigger this instead with type /// and parameter checking. /// public void SendRexScriptCommand(string unit, string command, string parameters) { List pack = new List(); pack.Add(unit); pack.Add(command); if (!string.IsNullOrEmpty(parameters)) pack.Add(parameters); SendGenericMessage("RexScr", pack); } public void SendRexInventoryMessage(string message) { SendRexScriptCommand("hud", "ShowInventoryMessage(\"" + message + "\")", ""); } public void SendRexScrollMessage(string message, double time) { SendRexScriptCommand("hud", "ShowScrollMessage(\"" + message + "\", \"" + time + "\")", ""); } public void SendRexTutorialMessage(string message, double time) { SendRexScriptCommand("hud", "ShowScrollMessage(\"" + message + "\", \"" + time + "\")", ""); } public void SendRexFadeInAndOut(string message, double between, double time) { SendRexScriptCommand("hud", "ShowInventoryMessage(\"" + message + "\"," + " \"" + between + "\", \"" + time + "\")", ""); } public void SendRexFaceExpression(List expressionData) { expressionData.Insert(0, AgentId.ToString()); SendGenericMessage("RexFaceExpression", expressionData); } public void SendRexAppearance(UUID agentID, string avatarURL) { List pack = new List(); pack.Add(avatarURL); pack.Add(agentID.ToString()); SendGenericMessage("RexAppearance", pack); } /// /// Requests properties about this agent from their /// authentication server. This should be run in /// an async thread. /// /// Note that this particular function may set the /// avatars appearance which may in turn call /// additional modules and functions elsewhere. /// /// private void RequestProperties(object o) { m_log.Info("[REXCLIENT] Resolving avatar..."); Hashtable ReqVals = new Hashtable(); ReqVals["avatar_uuid"] = AgentId.ToString(); ReqVals["AuthenticationAddress"] = RexAuthURL; ArrayList SendParams = new ArrayList(); SendParams.Add(ReqVals); XmlRpcRequest req = new XmlRpcRequest("get_user_by_uuid", SendParams); m_log.Info("[REXCLIENT] Sending XMLRPC Request to " + RexAuthURL); XmlRpcResponse authreply = req.Send(RexAuthURL, 9000); if (!((Hashtable)authreply.Value).ContainsKey("error_type")) { string rexAsAddress = ((Hashtable)authreply.Value)["as_address"].ToString(); string rexSkypeURL = ((Hashtable)authreply.Value)["skype_url"].ToString(); UUID userID = new UUID(((Hashtable)authreply.Value)["uuid"].ToString()); // Sanity check if (userID == AgentId) { RexAvatarURL = rexAsAddress; RexSkypeURL = rexSkypeURL; } } else { //Error has occurred m_log.Warn("[REXCLIENT]: User not found"); } } /// /// Sends Fog parameters to client. Only works underwater. /// /// meters from camera where the fog starts /// meters from camera where the fog ends /// redness in fog /// greeness in fog /// blueness in fog public void SendRexFog(float start, float end, float red, float green, float blue) { List pack = new List(); pack.Add(start.ToString()); pack.Add(end.ToString()); pack.Add(red.ToString()); pack.Add(green.ToString()); pack.Add(blue.ToString()); SendGenericMessage("RexFog", pack); } /// /// Sends water height to client. Usually used when changing water height on the fly with scripting. /// /// Water height in meters public void SendRexWaterHeight(float height) { List pack = new List(); pack.Add(height.ToString()); SendGenericMessage("RexWaterHeight", pack); } /// /// Sends post postprosessing effect toggle to client. /// /// /// Id of the effect. See documentation for the effect ids /// True to set effect on. False to set effect off. public void SendRexPostProcess(int effectId, bool toggle) { List pack = new List(); pack.Add(effectId.ToString()); pack.Add(toggle.ToString()); SendGenericMessage("RexPostP", pack); } /// /// Creates client side rtt camera /// /// 0 to remove existing rtt camera (by name), 1 to add new rtt camera /// Unique identifier for the camera /// UUID of the texture that gets rendered to /// Position of the camera in the world /// Point in the world the camera will look at /// Width of the texture /// Height of the texture public void SendRexRttCamera(int command, string name, UUID assetId, Vector3 pos, Vector3 lookat, int width, int height) { List pack = new List(); pack.Add(command.ToString()); pack.Add(name); pack.Add(assetId.ToString()); pack.Add(pos.ToString()); pack.Add(lookat.ToString()); pack.Add(width.ToString()); pack.Add(height.ToString()); SendGenericMessage("RexRttCam", pack); } /// /// Sends a viewport to client /// /// 0 to remove existing viewport (by name), 1 to add new viewport. /// Unique identifier for the viewport /// screen relative position of the left edge of the viewport /// screen relative position of the top edge of the viewport /// screen relative width of the viewport /// screen relative height of the viewport public void SendRexViewport(int command, string name, float posX, float posY, float width, float height) { List pack = new List(); pack.Add(command.ToString()); pack.Add(name); pack.Add(posX.ToString()); pack.Add(posY.ToString()); pack.Add(width.ToString()); pack.Add(height.ToString()); SendGenericMessage("RexSetViewport", pack); } /// /// Toggles the wind sound on client /// /// public void SendRexToggleWindSound(bool toggle) { List pack = new List(); pack.Add(toggle.ToString()); SendGenericMessage("RexToggleWindSound", pack); } /// /// Sends Rex clientside camera effects, particle script attached to camera etc. /// /// True to enable the effect, False to disable /// Id of the effect /// Offset position from the camera /// Offset rotation from the camera public void SendRexCameraClientSideEffect(bool enable, UUID assetId, Vector3 pos, Quaternion rot) { List pack = new List(); pack.Add(assetId.ToString()); pack.Add(pos.ToString()); pack.Add(rot.ToString()); pack.Add(enable.ToString()); SendGenericMessage("RexSCSEffect", pack); } /// /// Overrides default lighting conditions and ambient light in the world. /// /// Note that this override is a hard one. The user will be unable to change the lighting /// conditions in any way after they are overridden. /// /// Direction of the global light (sun) /// Colour of the global light /// Colour of the ambient light (the light that is always present) public void SendRexSetAmbientLight(Vector3 direction, Vector3 colour, Vector3 ambientColour) { List pack = new List(); pack.Add(direction.ToString()); pack.Add(colour.ToString()); pack.Add(ambientColour.ToString()); SendGenericMessage("RexAmbientL", pack); } /// /// Lauch flash animation to play on client /// /// Id of the flash animation (swf) to play /// left border of the rectangle /// top border of the rectangle /// right border of the rectangle /// bottom border of the rectangle /// time in seconds from start of animation playback until the flash control is destroyed public void SendRexPlayFlashAnimation(UUID assetId, float left, float top, float right, float bottom, float timeToDeath) { List pack = new List(); pack.Add(assetId.ToString()); pack.Add(left.ToString()); pack.Add(top.ToString()); pack.Add(right.ToString()); pack.Add(bottom.ToString()); pack.Add(timeToDeath.ToString()); SendGenericMessage("RexFlashAnim", pack); } internal void SendRexPreloadAvatarAssets(List vAssetsList) { throw new System.NotImplementedException(); } internal void SendRexForceFOV(float fov, bool enable) { throw new System.NotImplementedException(); } internal void SendRexForceCamera(int forceMode, float minZoom, float maxZoom) { throw new System.NotImplementedException(); } internal void SendRexSky(int type, string images, float curvature, float tiling) { throw new System.NotImplementedException(); } internal void SendRexPreloadAssets(Dictionary tempassetlist) { throw new System.NotImplementedException(); } internal void SendMediaURL(UUID assetId, string mediaURL, byte vRefreshRate) { throw new System.NotImplementedException(); } internal void RexIKSendLimbTarget(UUID vAgentID, int vLimbId, Vector3 vDest, float vTimeToTarget, float vStayTime, float vConstraintAngle, string vStartAnim, string vTargetAnim, string vEndAnim) { throw new System.NotImplementedException(); } public void SendRexAvatarAnimation(UUID agentID, string vAnimName, float vRate, float vFadeIn, float vFadeOut, int nRepeats, bool vbStopAnim) //rex { throw new System.NotImplementedException(); } internal void SendRexAvatarMorph(UUID uUID, string vMorphName, float vWeight, float vTime) { throw new System.NotImplementedException(); } internal void SendRexMeshAnimation(UUID uUID, string vAnimName, float vRate, bool vbLooped, bool vbStopAnim) { throw new System.NotImplementedException(); } internal void SendRexClientSideEffect(string assetId, float vTimeUntilLaunch, float vTimeUntilDeath, Vector3 pos, Quaternion rot, float vSpeed) { throw new System.NotImplementedException(); } } }