diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index 098d73e062..75fc5be5c2 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -802,6 +802,11 @@ namespace OpenSim.Client.MXP.ClientStack set { } } + public uint MaxCoarseLocations + { + get { return 0; } + } + public uint CircuitCode { get { return m_sessionID.CRC(); } diff --git a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs index 072be411c4..93c727ec53 100644 --- a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs +++ b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs @@ -203,6 +203,11 @@ namespace OpenSim.Client.Sirikata.ClientStack set { sendLogoutPacketWhenClosing = value; } } + public uint MaxCoarseLocations + { + get { return 0; } + } + public uint CircuitCode { get { return circuitCode; } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index 4711035b20..ec08f47c80 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -206,6 +206,11 @@ namespace OpenSim.Client.VWoHTTP.ClientStack set { throw new System.NotImplementedException(); } } + public uint MaxCoarseLocations + { + get { return 0; } + } + public uint CircuitCode { get { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs index 0c600f6146..15595006d6 100644 --- a/OpenSim/Framework/AvatarAppearance.cs +++ b/OpenSim/Framework/AvatarAppearance.cs @@ -29,6 +29,8 @@ using System; using System.Collections; using System.Collections.Generic; using OpenMetaverse; +using System.Reflection; +using log4net; namespace OpenSim.Framework { @@ -361,7 +363,7 @@ namespace OpenSim.Framework // This sets Visual Params with *less* weirder values then default. Instead of a ugly alien, it looks like a fat scientist SetDefaultParams(m_visualparams); SetDefaultWearables(); - m_texture = GetDefaultTexture(); + m_texture = null;//GetDefaultTexture(); } public AvatarAppearance(UUID avatarID, AvatarWearable[] wearables, byte[] visualParams) @@ -370,14 +372,17 @@ namespace OpenSim.Framework m_serial = 1; m_wearables = wearables; m_visualparams = visualParams; - m_texture = GetDefaultTexture(); + m_texture = null;// GetDefaultTexture(); } + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// /// Set up appearance textures and avatar parameters, including a height calculation /// public virtual void SetAppearance(Primitive.TextureEntry textureEntry, byte[] visualParams) { + //m_log.WarnFormat("[APPEARANCE] SetAppearance called ({0})", textureEntry == null ? " " : "te"); if (textureEntry != null) m_texture = textureEntry; if (visualParams != null) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 21a2bf589e..6022cb9260 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -677,6 +677,8 @@ namespace OpenSim.Framework IPEndPoint RemoteEndPoint { get; } + uint MaxCoarseLocations { get; } + event GenericMessage OnGenericMessage; // [Obsolete("LLClientView Specific - Replace with more bare-bones arguments.")] diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index a6e00c2251..0a401e7295 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -310,7 +310,7 @@ namespace OpenSim.Framework.Servers.HttpServer } catch (Exception e) { - m_log.Error(string.Format("[BASE HTTP SERVER]: OnRequest() failed with "), e); + m_log.Error(String.Format("[BASE HTTP SERVER]: OnRequest() failed with {0}", e.Message)); } } diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index a09b903bfc..a01e8a4e61 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -866,6 +866,7 @@ namespace OpenSim String.Format("{0,-16}{1,-16}{2,-37}{3,-11}{4,-16}{5,-30}", "Firstname", "Lastname", "Agent ID", "Root/Child", "Region", "Position")); + bool missingTextures = false; foreach (ScenePresence presence in agents) { RegionInfo regionInfo = presence.Scene.RegionInfo; @@ -880,17 +881,25 @@ namespace OpenSim regionName = regionInfo.RegionName; } + string te = "TE"; + if (presence.Appearance.Texture == null) + { + te = ""; + missingTextures = true; + } MainConsole.Instance.Output( String.Format( - "{0,-16}{1,-16}{2,-37}{3,-11}{4,-16}{5,-30}", + "{0,-16}{1,-16}{2,-37}{3,-11}{4,-16}{5,-30}{6,2}", presence.Firstname, presence.Lastname, presence.UUID, presence.IsChildAgent ? "Child" : "Root", regionName, - presence.AbsolutePosition.ToString())); + presence.AbsolutePosition.ToString(), + te)); } + MainConsole.Instance.Output(String.Empty); break; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 61f2002be3..c92e15129c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -365,6 +365,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Timer m_propertiesPacketTimer; private List m_propertiesBlocks = new List(); + private uint m_maxCoarseLocations = 60; + #endregion Class Members #region Properties @@ -412,6 +414,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool SendLogoutPacketWhenClosing { set { m_SendLogoutPacketWhenClosing = value; } } + public uint MaxCoarseLocations { get { return m_maxCoarseLocations; } } + #endregion Properties /// @@ -3321,6 +3325,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendWearables(AvatarWearable[] wearables, int serial) { + //m_log.WarnFormat("[LLCLIENTVIEW] Sending wearables to {0}", Name); AgentWearablesUpdatePacket aw = (AgentWearablesUpdatePacket)PacketPool.Instance.GetPacket(PacketType.AgentWearablesUpdate); aw.AgentData.AgentID = AgentId; aw.AgentData.SerialNum = (uint)serial; @@ -3347,6 +3352,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry) { + ScenePresence sp; + string forName = ""; + if (m_scene.TryGetScenePresence(agentID, out sp)) + forName = sp.Name; + //m_log.WarnFormat("[LLCLIENTVIEW] Sending {0} appearance to {1}", forName, Name); AvatarAppearancePacket avp = (AvatarAppearancePacket)PacketPool.Instance.GetPacket(PacketType.AvatarAppearance); // TODO: don't create new blocks if recycling an old packet avp.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218]; @@ -3426,7 +3436,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Each packet can only hold around 62 avatar positions and the client clears the mini-map each time // a CoarseLocationUpdate packet is received. Oh well. - int total = Math.Min(CoarseLocations.Count, 60); + int total = Math.Min(CoarseLocations.Count, (int)MaxCoarseLocations); CoarseLocationUpdatePacket.IndexBlock ib = new CoarseLocationUpdatePacket.IndexBlock(); @@ -5556,6 +5566,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private bool HandlerAgentWearablesRequest(IClientAPI sender, Packet Pack) { + //m_log.WarnFormat("[LLCLIENTVIEW] Received AgentWearablesRequest from {0}", sender.Name); GenericCall2 handlerRequestWearables = OnRequestWearables; if (handlerRequestWearables != null) @@ -5595,7 +5606,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // for the client session anyway, in order to protect ourselves against bad code in plugins try { - byte[] visualparams = new byte[appear.VisualParam.Length]; for (int i = 0; i < appear.VisualParam.Length; i++) visualparams[i] = appear.VisualParam[i].ParamValue; @@ -5604,10 +5614,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (appear.ObjectData.TextureEntry.Length > 1) te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length); - if (handlerSetAppearance != null) - handlerSetAppearance(te, visualparams); + // What would happen if we ignore any SetAppearance packet that does not include textures? + if (te == null) + { + //m_log.WarnFormat("[LLCLIENTVIEW] Received SetAppearance from {0} ( )", Name); + return true; + } + //m_log.WarnFormat("[LLCLIENTVIEW] Received SetAppearance from {0} (TE)", Name); if (handlerSetAppearanceRaw != null) handlerSetAppearanceRaw(this, AgentId, visualparams, te); + if (handlerSetAppearance != null) + handlerSetAppearance(te, visualparams); + } catch (Exception e) { diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index ef37f63622..8e44e2029f 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -856,7 +856,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // m_scene.SendKillObject(m_localId); - agent.Scene.NotifyMyCoarseLocationChange(); // the user may change their profile information in other region, // so the userinfo in UserProfileCache is not reliable any more, delete it // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs index 6b097a7e8c..567d01c7e7 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs @@ -33,7 +33,7 @@ using OpenMetaverse.Packets; using OpenSim.Framework; using OpenSim.Region.Framework.Scenes; -namespace OpenSim.Region.Examples.RegionSyncModule +namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { public class RegionSyncAvatar : IClientAPI { @@ -419,6 +419,11 @@ namespace OpenSim.Region.Examples.RegionSyncModule set { } } + public uint MaxCoarseLocations + { + get { return 0; } + } + public virtual void ActivateGesture(UUID assetId, UUID gestureId) { } diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs index 24f8e37152..ce541980a2 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs @@ -16,7 +16,7 @@ using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes.Types; using log4net; -namespace OpenSim.Region.Examples.RegionSyncModule +namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { // The RegionSyncClient has a receive thread to process messages from the RegionSyncServer. public class RegionSyncClient @@ -45,6 +45,16 @@ namespace OpenSim.Region.Examples.RegionSyncModule Dictionary m_remoteAvatars = new Dictionary(); Dictionary m_localAvatars = new Dictionary(); + private Dictionary RemoteAvatars + { + get { return m_remoteAvatars; } + } + + private Dictionary LocalAvatars + { + get { return m_localAvatars; } + } + // The logfile private ILog m_log; @@ -56,18 +66,20 @@ namespace OpenSim.Region.Examples.RegionSyncModule // The client connection to the RegionSyncServer private TcpClient m_client = new TcpClient(); - // The queue of incoming messages which need handling - //private Queue m_inQ = new Queue(); + private string m_regionName; + + private System.Timers.Timer m_statsTimer = new System.Timers.Timer(30000); #endregion // Constructor public RegionSyncClient(Scene scene, string addr, int port) { m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - //m_log.WarnFormat("{0} Constructed", LogHeader); m_scene = scene; m_addr = IPAddress.Parse(addr); m_port = port; + + m_statsTimer.Elapsed += new System.Timers.ElapsedEventHandler(StatsTimerElapsed); } // Start the RegionSyncClient @@ -91,6 +103,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_rcvLoop.Start(); //m_log.WarnFormat("{0} Started", LogHeader); DoInitialSync(); + m_statsTimer.Start(); } // Disconnect from the RegionSyncServer and close the RegionSyncClient @@ -101,43 +114,66 @@ namespace OpenSim.Region.Examples.RegionSyncModule ShutdownClient(); } + public void ReportStatus() + { + Dictionary locals = LocalAvatars; + Dictionary remotes = RemoteAvatars; + + m_log.WarnFormat("{0}: {1,4} Local avatars", LogHeader, locals.Count); + foreach (KeyValuePair kvp in locals) + { + ScenePresence sp; + bool inScene = m_scene.TryGetScenePresence(kvp.Value.AgentId, out sp); + m_log.WarnFormat("{0}: {1} {2} {3}", LogHeader, kvp.Value.AgentId, kvp.Value.Name, inScene ? "" : "(NOT IN SCENE)"); + } + m_log.WarnFormat("{0}: {1,4} Remote avatars", LogHeader, remotes.Count); + foreach (KeyValuePair kvp in remotes) + { + ScenePresence sp; + bool inScene = m_scene.TryGetScenePresence(kvp.Value.AgentId, out sp); + m_log.WarnFormat("{0}: {1} {2} {3}", LogHeader, kvp.Value.AgentId, kvp.Value.Name, inScene ? "" : "(NOT IN SCENE)"); + } + m_log.WarnFormat("{0}: ===================================================", LogHeader); + m_log.WarnFormat("{0} Synchronized to RegionSyncServer at {1}:{2}", LogHeader, m_addr, m_port); + m_log.WarnFormat("{0}: {1,4} Local avatars", LogHeader, locals.Count); + m_log.WarnFormat("{0}: {1,4} Remote avatars", LogHeader, remotes.Count); + } + private void ShutdownClient() { m_log.WarnFormat("{0} Disconnected from RegionSyncServer. Shutting down.", LogHeader); - lock (m_syncRoot) + // Remove remote avatars from local scene + try { - // Remove remote avatars from local scene - try + foreach (KeyValuePair kvp in RemoteAvatars) { - List remoteAvatars = new List(m_remoteAvatars.Keys); - foreach (UUID id in remoteAvatars) - { - m_scene.RemoveClient(id); - } - } - catch (Exception e) - { - m_log.ErrorFormat("Caught exception while removing remote avatars on Shutdown: {0}", e.Message); - } - try - { - // Remove local avatars from remote scene - List> localAvatars = new List>(m_localAvatars); - foreach (KeyValuePair kvp in localAvatars) - { - // Tell the remote scene to remove this client - RemoveLocalClient(kvp.Key, m_scene); - // Remove the agent update handler from the client - kvp.Value.OnAgentUpdateRaw -= HandleAgentUpdateRaw; - kvp.Value.OnSetAppearanceRaw -= HandleSetAppearanceRaw; - } - } - catch (Exception e) - { - m_log.ErrorFormat("Caught exception while removing local avatars on Shutdown: {0}", e.Message); + // This will cause a callback into RemoveLocalClient + m_scene.RemoveClient(kvp.Key); } } + catch (Exception e) + { + m_log.ErrorFormat("Caught exception while removing remote avatars on Shutdown: {0}", e.Message); + } + + try + { + // Remove local avatars from remote scene + foreach (KeyValuePair kvp in LocalAvatars) + { + // Tell the remote scene to remove this client + RemoveLocalClient(kvp.Key, m_scene); + // Remove the agent update handler from the client + kvp.Value.OnAgentUpdateRaw -= HandleAgentUpdateRaw; + kvp.Value.OnSetAppearanceRaw -= HandleSetAppearanceRaw; + } + } + catch (Exception e) + { + m_log.ErrorFormat("Caught exception while removing local avatars on Shutdown: {0}", e.Message); + } + // Close the connection m_client.Client.Close(); m_client.Close(); @@ -167,29 +203,11 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Try handling the message try { - string message; - MsgHandlerStatus status; - lock(m_syncRoot) - status = HandleMessage(msg, out message); - switch (status) - { - case MsgHandlerStatus.Success: - //m_log.WarnFormat("{0} Handled {1}: {2}", LogHeader, msg.ToString(), message); - break; - case MsgHandlerStatus.Trivial: - m_log.WarnFormat("{0} Issue handling {1}: {2}", LogHeader, msg.ToString(), message); - break; - case MsgHandlerStatus.Warning: - m_log.WarnFormat("{0} Warning handling {1}: {2}", LogHeader, msg.ToString(), message); - break; - case MsgHandlerStatus.Error: - m_log.WarnFormat("{0} Error handling {1}: {2}", LogHeader, msg.ToString(), message); - break; - } + HandleMessage(msg); } catch (Exception e) { - m_log.WarnFormat("{0} Encountered an exception: {1}", LogHeader, e.Message); + m_log.WarnFormat("{0} Encountered an exception: {1} (MSGTYPE = {2})", LogHeader, e.Message, msg.ToString()); } } } @@ -234,155 +252,116 @@ namespace OpenSim.Region.Examples.RegionSyncModule List ids = new List(); List locations = new List(); m_scene.GetCoarseLocations(out ids, out locations); - lock (m_syncRoot) + foreach (KeyValuePair kvp in LocalAvatars) { - foreach (IClientAPI client in m_localAvatars.Values) - { - client.SendCoarseLocationUpdate(ids, locations); - } + kvp.Value.SendCoarseLocationUpdate(ids, locations); } } - HashSet exceptions = new HashSet(); - private OSDMap DeserializeMessage(RegionSyncMessage msg) - { - OSDMap data = null; - try - { - data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data)) as OSDMap; - } - catch (Exception e) - { - lock (exceptions) - // If this is a new message, then print the underlying data that caused it - if (!exceptions.Contains(e.Message)) - m_log.Error(LogHeader + " " + Encoding.ASCII.GetString(msg.Data)); - data = null; - } - return data; - } - // Handle an incoming message - // Returns true if the message was processed // TODO: This should not be synchronous with the receive! - // Instead, handle messages from the incoming Queue - private MsgHandlerStatus HandleMessage(RegionSyncMessage msg, out string result) + // Instead, handle messages from an incoming Queue so server doesn't block sending + private void HandleMessage(RegionSyncMessage msg) { switch (msg.Type) { + case RegionSyncMessage.MsgType.RegionName: + { + m_regionName = Encoding.ASCII.GetString(msg.Data, 0, msg.Length); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Syncing to region \"{0}\"", m_regionName)); + return; + } case RegionSyncMessage.MsgType.Terrain: { - m_scene.Heightmap.LoadFromXmlString(Encoding.ASCII.GetString(msg.Data)); - result = "Synchronized terrain"; - return MsgHandlerStatus.Success; + m_scene.Heightmap.LoadFromXmlString(Encoding.ASCII.GetString(msg.Data, 0, msg.Length)); + RegionSyncMessage.HandleSuccess(LogHeader, msg, "Synchronized terrain"); + return; } case RegionSyncMessage.MsgType.NewObject: case RegionSyncMessage.MsgType.UpdatedObject: { - SceneObjectGroup sog = SceneObjectSerializer.FromXml2Format(Encoding.ASCII.GetString(msg.Data)); + SceneObjectGroup sog = SceneObjectSerializer.FromXml2Format(Encoding.ASCII.GetString(msg.Data, 0, msg.Length)); if (sog.IsDeleted) { - result = String.Format("Ignoring update on deleted LocalId {0}.", sog.LocalId.ToString()); - return MsgHandlerStatus.Trivial; + RegionSyncMessage.HandleTrivial(LogHeader, msg, String.Format("Ignoring update on deleted LocalId {0}.", sog.LocalId.ToString())); + return; } - if (m_scene.AddNewSceneObject(sog, true)) - result = String.Format("Object \"{0}\" ({1}) ({1}) updated.", sog.Name, sog.UUID.ToString(), sog.LocalId.ToString()); - else - result = String.Format("Object \"{0}\" ({1}) ({1}) added.", sog.Name, sog.UUID.ToString(), sog.LocalId.ToString()); + + if (m_scene.AddNewSceneObject(sog, true)); + //RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Object \"{0}\" ({1}) ({1}) updated.", sog.Name, sog.UUID.ToString(), sog.LocalId.ToString())); + //else + //RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Object \"{0}\" ({1}) ({1}) added.", sog.Name, sog.UUID.ToString(), sog.LocalId.ToString())); sog.ScheduleGroupForFullUpdate(); - return MsgHandlerStatus.Success; + return; } - // return HandlerSuccess(msg, "Updated avatar"); - /* - case RegionSyncMessage.MsgType.SetObjectPosition: Attributes! - { - if (part.ParentGroup == null) - { - part.UpdateOffSet(new Vector3((float)real_vec.x, (float)real_vec.y, (float)real_vec.z)); - } - else if (part.ParentGroup.RootPart == part) - { - part.parent.UpdateGroupPosition(new Vector3((float)real_vec.x, (float)real_vec.y, (float)real_vec.z)); - } - else - { - part.OffsetPosition = new Vector3((float)targetPos.x, (float)targetPos.y, (float)targetPos.z); - SceneObjectGroup parent = part.ParentGroup; - parent.HasGroupChanged = true; - parent.ScheduleGroupForTerseUpdate(); - } - } - * */ case RegionSyncMessage.MsgType.RemovedObject: { - OSDMap data = DeserializeMessage(msg); - if( data != null ) - { - ulong regionHandle = data["regionHandle"].AsULong(); - uint localID = data["localID"].AsUInteger(); - - // We get the UUID of the object to be deleted, find it in the scene - if (regionHandle != 0 && localID != 0 ) - { - if (regionHandle == m_scene.RegionInfo.RegionHandle) - { - SceneObjectGroup sog = m_scene.SceneGraph.GetGroupByPrim(localID); - if (sog != null) - { - m_scene.DeleteSceneObject(sog, false); - result = String.Format("localID {0} deleted.", localID.ToString()); - return MsgHandlerStatus.Success; - } - } - result = String.Format("ignoring delete request for non-local regionHandle {0}.", regionHandle.ToString()); - return MsgHandlerStatus.Trivial; - } - result = String.Format("localID {0} not found.", localID.ToString()); - return MsgHandlerStatus.Warning; - } - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; - } - case RegionSyncMessage.MsgType.NewAvatar: - { + // Get the data from message and error check OSDMap data = DeserializeMessage(msg); if (data == null) { - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } + // Get the parameters from data + //ulong regionHandle = data["regionHandle"].AsULong(); + uint localID = data["localID"].AsUInteger(); + + // Find the object in the scene + SceneObjectGroup sog = m_scene.SceneGraph.GetGroupByPrim(localID); + if (sog == null) + { + //RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("localID {0} not found.", localID.ToString())); + return; + } + + // Delete the object from the scene + m_scene.DeleteSceneObject(sog, false); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("localID {0} deleted.", localID.ToString())); + return; + } + case RegionSyncMessage.MsgType.NewAvatar: + { + // Get the data from message and error check + OSDMap data = DeserializeMessage(msg); + if (data == null) + { + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; + } + + // Get the parameters from data and error check UUID agentID = data["agentID"].AsUUID(); string first = data["first"].AsString(); string last = data["last"].AsString(); Vector3 startPos = data["startPos"].AsVector3(); - if (agentID == null || agentID == UUID.Zero || first == null || last == null || startPos == null) { - result = "Missing or invalid JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Missing or invalid JSON data."); + return; } if (m_remoteAvatars.ContainsKey(agentID)) { - result = String.Format("Attempted to add duplicate avatar \"{0} {1}\" ({2})", first, last, agentID.ToString()); - return MsgHandlerStatus.Warning; + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Attempted to add duplicate avatar \"{0} {1}\" ({2})", first, last, agentID.ToString())); + return; } ScenePresence sp; if (m_scene.TryGetScenePresence(agentID, out sp)) { - result = String.Format("Confirmation of new avatar \"{0}\" ({1})", sp.Name, sp.UUID.ToString()); - HandlerDebug(msg, result); - return MsgHandlerStatus.Success; + //RegionSyncMessage.HandlerDebug(LogHeader, msg, String.Format("Confirmation of new avatar \"{0}\" ({1})", sp.Name, sp.UUID.ToString())); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Confirmation of new avatar \"{0}\" ({1})", sp.Name, sp.UUID.ToString())); + return; } RegionSyncAvatar av = new RegionSyncAvatar(m_scene, agentID, first, last, startPos); m_remoteAvatars.Add(agentID, av); m_scene.AddNewClient(av); - result = String.Format("Added new remote avatar \"{0}\" ({1})", first + " " + last, agentID); - HandlerDebug(msg, result); - return MsgHandlerStatus.Success; + //RegionSyncMessage.HandlerDebug(LogHeader, msg, String.Format("Added new remote avatar \"{0}\" ({1})", first + " " + last, agentID)); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Added new remote avatar \"{0}\" ({1})", first + " " + last, agentID)); + return; } case RegionSyncMessage.MsgType.UpdatedAvatar: @@ -391,21 +370,37 @@ namespace OpenSim.Region.Examples.RegionSyncModule OSDMap data = DeserializeMessage(msg); if (data == null) { - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } // Get the parameters from data and error check - UUID agentID = data["id"].AsUUID(); - Vector3 pos = data["pos"].AsVector3(); - Vector3 vel = data["vel"].AsVector3(); - Quaternion rot = data["rot"].AsQuaternion(); - bool flying = data["fly"].AsBoolean(); - uint flags = data["flags"].AsUInteger(); + UUID agentID = UUID.Zero; + Vector3 pos = Vector3.Zero; + Vector3 vel = Vector3.Zero; + Quaternion rot = Quaternion.Identity; + bool flying = false; + string anim = ""; + uint flags = 0; + + try + { + agentID = data["id"].AsUUID(); + pos = data["pos"].AsVector3(); + vel = data["vel"].AsVector3(); + rot = data["rot"].AsQuaternion(); + flying = data["fly"].AsBoolean(); + anim = data["anim"].AsString(); + flags = data["flags"].AsUInteger(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (Decoding JSON): {1}", LogHeader, e.Message); + } if (agentID == null || agentID == UUID.Zero ) { - result = "Missing or invalid JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Missing or invalid JSON data."); + return; } // Find the presence in the scene and error check @@ -413,12 +408,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_scene.TryGetScenePresence(agentID, out presence); if (presence == null) { - result = String.Format("agentID {0} not found.", agentID.ToString()); - return MsgHandlerStatus.Warning; + //RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("agentID {0} not found.", agentID.ToString())); + return; } + /** // Update the scene presence from parameters - /* bool updateAnimations = ((presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY ) || (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_POS ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_POS ) || (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG ) || @@ -426,27 +421,56 @@ namespace OpenSim.Region.Examples.RegionSyncModule (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG ) || (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT ) || (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT )); - * */ - presence.AgentControlFlags = flags; - presence.AbsolutePosition = pos; - presence.Velocity = vel; - presence.Rotation = rot; - bool updateAnimations = - presence.PhysicsActor.Flying = flying; - List locals; - lock (m_syncRoot) - locals = new List(m_localAvatars.Values); - presence.SendTerseUpdateToClientList(locals); - if(updateAnimations) - presence.Animator.UpdateMovementAnimations(); + */ + try + { + presence.AgentControlFlags = flags; + presence.AbsolutePosition = pos; + presence.Velocity = vel; + presence.Rotation = rot; + // It seems the physics scene can drop an avatar if the avatar makes it angry + if (presence.PhysicsActor != null) + { + presence.PhysicsActor.Flying = flying; + presence.PhysicsActor.CollidingGround = !flying; + } + } + catch(Exception e) + { + m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (setting presence values) for {1}: {2}", LogHeader, presence.Name, e.Message); + } + try + { + presence.SendTerseUpdateToAllClients(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (SendTerseUpdateToAllClients): {1}", LogHeader, e.Message); + } + /* + foreach (KeyValuePair kvp in LocalAvatars) + { + presence.SendTerseUpdateToClient(kvp.Value); + }*/ + + try + { + // If the animation has changed, set the new animation + if (!presence.Animator.CurrentMovementAnimation.Equals(anim)) + presence.Animator.TrySetMovementAnimation(anim); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (TrySetMovementAnimation): {1}", LogHeader, e.Message); + } /* * result = String.Format("Avatar \"{0}\" ({1}) ({2}) updated (pos:{3}, vel:{4}, rot:{5}, fly:{6})", presence.Name, presence.UUID.ToString(), presence.LocalId.ToString(), presence.AbsolutePosition.ToString(), presence.Velocity.ToString(), presence.Rotation.ToString(), presence.PhysicsActor.Flying ? "Y" : "N"); + HandleSuccess(msg, result); * */ - result = ""; // For performance, skip creating that long string unless debugging - return MsgHandlerStatus.Success; + return; } case RegionSyncMessage.MsgType.RemovedAvatar: { @@ -454,44 +478,74 @@ namespace OpenSim.Region.Examples.RegionSyncModule OSDMap data = DeserializeMessage(msg); if (data == null) { - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } // Get the agentID from data and error check UUID agentID = data["agentID"].AsUUID(); if (agentID == UUID.Zero) { - result = "Missing or invalid JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Missing or invalid JSON data."); + return; } - // Find the presence in the scene and error check + // Try to get the presence from the scene + + ScenePresence presence; m_scene.TryGetScenePresence(agentID, out presence); - if(presence == null) - { - result = String.Format("agentID {0} not found.", agentID.ToString()); - return MsgHandlerStatus.Success; - } - // Remove the presence from the scene and if it's remote, then from the remote list too - m_scene.RemoveClient(agentID); - if (m_remoteAvatars.ContainsKey(agentID)) + // Local and Remote avatar dictionaries will only be modified under this lock + // They can be read without locking as we are just going to swap refs atomically + lock (m_syncRoot) { - result = String.Format("Remote avatar \"{0}\" removed from scene", presence.Name); - HandlerDebug(msg, result); - return MsgHandlerStatus.Success; + + if (RemoteAvatars.ContainsKey(agentID)) + { + Dictionary newremotes = new Dictionary(RemoteAvatars); + string name = newremotes[agentID].Name; + // Remove from list of remote avatars + newremotes.Remove(agentID); + m_remoteAvatars = newremotes; + + // If this presence is in out remote list, then this message tells us to remove it from the + // local scene so we should find it there. + if (presence == null) + { + RegionSyncMessage.HandleError(LogHeader, msg, String.Format("Couldn't remove remote avatar \"{0}\" because it's not in local scene", name)); + return; + } + m_scene.RemoveClient(agentID); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Remote avatar \"{0}\" removed from scene", name)); + return; + } + else if (LocalAvatars.ContainsKey(agentID)) + { + Dictionary newlocals = new Dictionary(LocalAvatars); + string name = newlocals[agentID].Name; + // Remove from list of local avatars + newlocals.Remove(agentID); + m_localAvatars = newlocals; + + // If this presence is in our local list, then this message is a confirmation from the server + // and the presence should not be in our scene at this time. + if (presence != null) + { + RegionSyncMessage.HandleError(LogHeader, msg, String.Format("Remove avatar confirmation received for \"{0}\" still in the local scene.", name)); + return; + } + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Received confirmation of removed avatar \"{0}\"", name)); + return; + } } - else if(m_localAvatars.ContainsKey(agentID)) - { - result = String.Format("Received confirmation of removed avatar \"{0}\" ({1})", presence.Name, presence.UUID.ToString()); - HandlerDebug(msg, result); - return MsgHandlerStatus.Success; - } - result = String.Format("Avatar is not local OR remote but was found in scene: \"{0}\" ({1})", presence.Name, presence.UUID.ToString()); - HandlerDebug(msg, result); - return MsgHandlerStatus.Error; + // Getting here is always an error because we have been asked to remove and avatar + // so it should have been in either the local or remote collections + if (presence == null) + RegionSyncMessage.HandleError(LogHeader, msg, String.Format("Avatar is not local OR remote and was not found in scene: \"{0}\" {1}", presence.Name, agentID.ToString())); + else + RegionSyncMessage.HandleError(LogHeader, msg, String.Format("Avatar is not local OR remote but was found in scene: {1}", agentID.ToString())); + return; } case RegionSyncMessage.MsgType.ChatFromClient: { @@ -499,8 +553,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule OSDMap data = DeserializeMessage(msg); if (data == null) { - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } OSChatMessage args = new OSChatMessage(); args.Channel = data["channel"].AsInteger(); @@ -519,26 +573,27 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_scene.EventManager.TriggerOnChatBroadcast(sp.ControllingClient, args); } - result = String.Format("Received chat from \"{0}\"", args.From); - HandlerDebug(msg, result); - return MsgHandlerStatus.Success; + //RegionSyncMessage.HandlerDebug(LogHeader, msg, String.Format("Received chat from \"{0}\"", args.From)); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Received chat from \"{0}\"", args.From)); + return; } case RegionSyncMessage.MsgType.AvatarAppearance: { + //m_log.WarnFormat("{0} START of AvatarAppearance handler", LogHeader); // Get the data from message and error check OSDMap data = DeserializeMessage(msg); if (data == null) { - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } // Get the parameters from data and error check UUID agentID = data["id"].AsUUID(); if (agentID == null || agentID == UUID.Zero) { - result = "Missing or invalid JSON data."; - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, "Missing or invalid JSON data."); + return; } // Find the presence in the scene @@ -549,67 +604,70 @@ namespace OpenSim.Region.Examples.RegionSyncModule Primitive.TextureEntry te = Primitive.TextureEntry.FromOSD(data["te"]); byte[] vp = data["vp"].AsBinary(); + bool missingBakes = false; byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; for (int i = 0; i < BAKE_INDICES.Length; i++) { int j = BAKE_INDICES[i]; Primitive.TextureEntryFace face = te.FaceTextures[j]; if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) + { if (m_scene.AssetService.Get(face.TextureID.ToString()) == null) - HandlerDebug(msg, "Missing baked texture " + face.TextureID + " (" + j + ") for avatar " + name); + { + RegionSyncMessage.HandlerDebug(LogHeader, msg, "Missing baked texture " + face.TextureID + " (" + j + ") for avatar " + name); + missingBakes = true; + } + } } + //m_log.WarnFormat("{0} Calling presence.SetAppearance for {1} (\"{2}\") {3}", LogHeader, agentID, presence.Name, missingBakes ? "MISSING BAKES" : "GOT BAKES"); presence.SetAppearance(te, vp); - result = String.Format("Agent \"{0}\" ({1}) updated their appearance.", name, agentID); - return MsgHandlerStatus.Success; + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Set appearance for {0}", name)); } - result = String.Format("Agent {0} not found in the scene.", agentID); - return MsgHandlerStatus.Warning; - } - - /* - case RegionSyncMessage.MsgType.UpdateObject: - { - UUID uuid; - if (UUID.TryParse(Encoding.ASCII.GetString(msg.Data), out uuid)) + else { - // We get the UUID of the object to be updated, find it in the scene - SceneObjectGroup sog = m_scene.SceneGraph.GetGroupByPrim(uuid); - Vector3 v = new Vector3(); - v.ToString(); - if (sog != null) - { - //m_scene.DeleteSceneObject(sog, false); - return HandlerSuccess(msg, String.Format("UUID {0} updated.", uuid.ToString())); - } - else - { - return HandlerFailure(msg, String.Format("UUID {0} not found.", uuid.ToString())); - } + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Agent {0} not found in the scene.", agentID)); } - return HandlerFailure(msg, "Could not parse UUID"); + //m_log.WarnFormat("{0} END of AvatarAppearance handler", LogHeader); + return; } - * */ default: { - result = String.Format("{0} Unsupported message type: {1}", LogHeader, ((int)msg.Type).ToString()); - return MsgHandlerStatus.Error; + RegionSyncMessage.HandleError(LogHeader, msg, String.Format("{0} Unsupported message type: {1}", LogHeader, ((int)msg.Type).ToString())); + return; } } } - private bool HandlerDebug(RegionSyncMessage msg, string handlerMessage) + + HashSet exceptions = new HashSet(); + private OSDMap DeserializeMessage(RegionSyncMessage msg) { - m_log.WarnFormat("{0} DBG ({1}): {2}", LogHeader, msg.ToString(), handlerMessage); - return true; + OSDMap data = null; + try + { + data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data, 0, msg.Length)) as OSDMap; + } + catch (Exception e) + { + lock (exceptions) + // If this is a new message, then print the underlying data that caused it + if (!exceptions.Contains(e.Message)) + m_log.Error(LogHeader + " " + Encoding.ASCII.GetString(msg.Data, 0, msg.Length)); + data = null; + } + return data; } private void DoInitialSync() { m_scene.DeleteAllSceneObjects(); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.RegionName, m_scene.RegionInfo.RegionName)); + m_log.WarnFormat("Sending region name: \"{0}\"", m_scene.RegionInfo.RegionName); Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetTerrain)); Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetObjects)); Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetAvatars)); + // Register for events which will be forwarded to authoritative scene m_scene.EventManager.OnNewClient += EventManager_OnNewClient; m_scene.EventManager.OnClientClosed += new EventManager.ClientClosed(RemoveLocalClient); @@ -625,17 +683,18 @@ namespace OpenSim.Region.Examples.RegionSyncModule { // If this client was added in response to NewAvatar message from a synced server, // don't subscribe to events or send back to server - lock (m_remoteAvatars) - { - if (m_remoteAvatars.ContainsKey(client.AgentId)) + if (RemoteAvatars.ContainsKey(client.AgentId)) return; - } // Otherwise, it's a real local client connecting so track it locally - lock(m_syncRoot) - m_localAvatars.Add(client.AgentId, client); + lock (m_syncRoot) + { + Dictionary newlocals = new Dictionary(LocalAvatars); + newlocals.Add(client.AgentId, client); + m_localAvatars = newlocals; + } m_log.WarnFormat("{0} New local client \"{1}\" ({2}) being added to remote scene.", LogHeader, client.Name, client.AgentId.ToString()); // Let the auth sim know that a new agent has connected - OSDMap data = new OSDMap(1); + OSDMap data = new OSDMap(4); data["agentID"] = OSD.FromUUID(client.AgentId); data["first"] = OSD.FromString(client.FirstName); data["last"] = OSD.FromString(client.LastName); @@ -654,14 +713,24 @@ namespace OpenSim.Region.Examples.RegionSyncModule lock (m_syncRoot) { // If the client to be removed is a local client, then send a message to the remote scene - if (m_localAvatars.ContainsKey(clientID)) + if (LocalAvatars.ContainsKey(clientID)) { - m_log.WarnFormat("{0} Local client ({1}) being removed from remote scene.", LogHeader, clientID.ToString()); - m_localAvatars.Remove(clientID); - // Let the auth sim know that an agent has disconnected - OSDMap data = new OSDMap(1); - data["agentID"] = OSD.FromUUID(clientID); - Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentRemove, OSDParser.SerializeJsonString(data))); + // If we are still connected, let the auth sim know that an agent has disconnected + if (m_client.Connected) + { + m_log.WarnFormat("{0} Local client ({1}) has left the scene. Notifying remote scene.", LogHeader, clientID.ToString()); + OSDMap data = new OSDMap(1); + data["agentID"] = OSD.FromUUID(clientID); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentRemove, OSDParser.SerializeJsonString(data))); + } + else + { + m_log.WarnFormat("{0} Local client ({1}) has left the scene.", LogHeader, clientID.ToString()); + // Remove it from our list of local avatars (we will never receive a confirmation since we are disconnected) + Dictionary newlocals = new Dictionary(LocalAvatars); + newlocals.Remove(clientID); + m_localAvatars = newlocals; + } } } } @@ -676,11 +745,25 @@ namespace OpenSim.Region.Examples.RegionSyncModule public void HandleSetAppearanceRaw(object sender, UUID agentID, byte[] vp, Primitive.TextureEntry te) { + // Try to find the scene presence we want to set the appearance for + ScenePresence sp; + string name = "NOT FOUND"; + if (m_scene.TryGetScenePresence(agentID, out sp)) + name = sp.Name; + m_log.WarnFormat("{0} Received LLClientView.SetAppearance ({1,3},{2,2}) for {3} (\"{4}\")", LogHeader, vp.Length.ToString(), (te == null) ? "" : "te", agentID.ToString(), sp.Name); + if (sp == null) + return; + + // Set the appearance on the presence. This will generate the needed exchange with the client if rebakes need to take place. + m_log.WarnFormat("{0} Setting appearance on ScenePresence {1} \"{2}\"", LogHeader, sp.UUID, sp.Name); + sp.SetAppearance(te, vp); + if (te != null) { - OSDMap data = new OSDMap(2); - data["id"] = OSDUUID.FromUUID(agentID); - data["vp"] = new OSDBinary(vp); + //m_log.WarnFormat("{0} Sending appearance to server for {1} \"{2}\"", LogHeader, sp.UUID, sp.Name); + OSDMap data = new OSDMap(3); + data["id"] = OSDUUID.FromUUID(sp.UUID); + data["vp"] = new OSDBinary(sp.Appearance.VisualParams); data["te"] = te.GetOSD(); Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AvatarAppearance, OSDParser.SerializeJsonString(data))); } @@ -714,19 +797,23 @@ namespace OpenSim.Region.Examples.RegionSyncModule } #endregion - /* - // Should be part of the RegionSyncClient - public string ReceiveMsg() + + private void StatsTimerElapsed(object source, System.Timers.ElapsedEventArgs e) { - lock (m_outQ) + // Let the auth sim know about this client's POV Re: Client counts + int t, l, r; + lock (m_syncRoot) { - if (m_outQ.Count > 0) - { - return m_outQ.Dequeue(); - } + t = m_scene.SceneGraph.GetRootAgentCount(); + l = LocalAvatars.Count; + r = RemoteAvatars.Count; } - return null; + OSDMap data = new OSDMap(3); + data["total"] = OSD.FromInteger(t); + data["local"] = OSD.FromInteger(l); + data["remote"] = OSD.FromInteger(r); + //m_log.WarnFormat("{0}: Sent stats: {1},{2},{3}", LogHeader, t, l, r); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.RegionStatus, OSDParser.SerializeJsonString(data))); } - * */ } } diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs index 56af895b3b..b245dcef99 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs @@ -39,7 +39,7 @@ using log4net; using System.Net; using System.Net.Sockets; -namespace OpenSim.Region.Examples.RegionSyncModule +namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { public class RegionSyncClientModule : IRegionModule, IRegionSyncClientModule, ICommandableModule { @@ -72,8 +72,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule { if (!m_active) return; - - //m_log.Warn("[REGION SYNC CLIENT MODULE] Post-Initialised"); + // Go ahead and try to sync right away + Start(); } public void Close() @@ -159,8 +159,11 @@ namespace OpenSim.Region.Examples.RegionSyncModule //cmdSyncStop.AddArgument("server_address", "The IP address of the server to synchronize with", "String"); //cmdSyncStop.AddArgument("server_port", "The port of the server to synchronize with", "Integer"); + Command cmdSyncStatus = new Command("status", CommandIntentions.COMMAND_HAZARDOUS, SyncStatus, "Displays synchronization status."); + m_commander.RegisterCommand("start", cmdSyncStart); m_commander.RegisterCommand("stop", cmdSyncStop); + m_commander.RegisterCommand("status", cmdSyncStatus); lock (m_scene) { @@ -195,6 +198,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule private void SyncStart(Object[] args) { + Start(); + } + + private void Start() + { + lock (m_client_lock) { if (m_client != null) @@ -224,6 +233,20 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_log.Warn("[REGION SYNC CLIENT MODULE] Stopping synchronization"); } } + + private void SyncStatus(Object[] args) + { + lock (m_client_lock) + { + if (m_client == null) + { + m_log.WarnFormat("[REGION SYNC CLIENT MODULE] Not currently synchronized"); + return; + } + m_log.WarnFormat("[REGION SYNC CLIENT MODULE] Synchronized"); + m_client.ReportStatus(); + } + } #endregion } } diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs index 9916ed61af..37273ff50e 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs @@ -14,8 +14,18 @@ using OpenSim.Region.Framework.Scenes.Serialization; using OpenSim.Region.Framework.Interfaces; using log4net; -namespace OpenSim.Region.Examples.RegionSyncModule +namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { + #region MsgHandlerStatus Enum + public enum MsgHandlerStatus + { + Success, // Everything went as expected + Trivial, // Minor issue, nothing to worry about + Warning, // Something went wrong, we can continue + Error // This should certainly not have happened! (A bug) + } + #endregion + // The RegionSyncClientView acts as a thread on the RegionSyncServer to handle incoming // messages from RegionSyncClients. public class RegionSyncClientView @@ -23,10 +33,19 @@ namespace OpenSim.Region.Examples.RegionSyncModule #region RegionSyncClientView members object stats = new object(); + private DateTime lastStatTime; + private long queuedUpdates; + private long dequeuedUpdates; private long msgsIn; private long msgsOut; private long bytesIn; private long bytesOut; + private long pollBlocks; + private int lastTotalCount; + private int lastLocalCount; + private int lastRemoteCount; + + private int msgCount = 0; // The TcpClient this view uses to communicate with its RegionSyncClient private TcpClient m_tcpclient; @@ -36,32 +55,66 @@ namespace OpenSim.Region.Examples.RegionSyncModule private int m_connection_number; private Scene m_scene; + object m_syncRoot = new object(); Dictionary m_syncedAvatars = new Dictionary(); // A queue for incoming and outgoing traffic - private OpenMetaverse.BlockingQueue inbox = new OpenMetaverse.BlockingQueue(); + //private OpenMetaverse.BlockingQueue inbox = new OpenMetaverse.BlockingQueue(); + //private OpenMetaverse.BlockingQueue outbox = new OpenMetaverse.BlockingQueue(); + + private BlockingUpdateQueue m_outQ = new BlockingUpdateQueue(); private ILog m_log; private Thread m_receive_loop; - private Thread m_handler; + private Thread m_send_loop; + private string m_regionName; - // A string of the format [REGION SYNC CLIENT VIEW #X] for use in log headers + // A string of the format [REGION SYNC CLIENT VIEW (regionname)] for use in log headers private string LogHeader { - get { return String.Format("[REGION SYNC CLIENT VIEW #{0}]", m_connection_number); } + get + { + if(m_regionName == null) + return String.Format("[REGION SYNC CLIENT VIEW #{0}]", m_connection_number); + return String.Format("[REGION SYNC CLIENT VIEW #{0} ({1:10})]", m_connection_number, m_regionName); + } } // A string of the format "RegionSyncClientView #X" for use in describing the object itself public string Description { - get { return String.Format("RegionSyncClientView #{0}", m_connection_number); } + get + { + if(m_regionName == null) + return String.Format("RegionSyncClientView #{0}", m_connection_number); + return String.Format("RegionSyncClientView #{0} ({1:10})", m_connection_number, m_regionName); + } } public string GetStats() { - lock(stats) - return String.Format("{0},{1},{2},{3}", msgsIn, msgsOut, bytesIn, bytesOut); + int syncedAvCount; + string ret; + lock (m_syncRoot) + syncedAvCount = m_syncedAvatars.Count; + lock (stats) + { + double secondsSinceLastStats = DateTime.Now.Subtract(lastStatTime).TotalSeconds; + lastStatTime = DateTime.Now; + int totalAvCount = m_scene.SceneGraph.GetRootAgentCount(); + ret = String.Format("[{0,4}/{1,4}], [{2,4}/{3,4}], [{4,4}/{5,4}], [{6,4} ({7,4})], [{8,8} ({9,8:00.00})], [{10,4} ({11,4})], [{12,8} ({13,8:00.00})], [{14,8},{15,8},{16,8}]", + lastTotalCount, totalAvCount, // TOTAL AVATARS + lastLocalCount, syncedAvCount, // LOCAL TO THIS CLIENT VIEW + lastRemoteCount, totalAvCount - syncedAvCount, // REMOTE (SHOULD = TOTAL - LOCAL) + msgsIn, (int)(msgsIn / secondsSinceLastStats), + bytesIn, 8 * (bytesIn / secondsSinceLastStats / 1000000), // IN + msgsOut, (int)(msgsOut / secondsSinceLastStats), + bytesOut, 8 * (bytesOut / secondsSinceLastStats / 1000000), // OUT + m_outQ.Count, (int)(queuedUpdates / secondsSinceLastStats), (int)(dequeuedUpdates/secondsSinceLastStats)); // QUEUE ACTIVITY + msgsIn = msgsOut = bytesIn = bytesOut = pollBlocks = queuedUpdates = dequeuedUpdates = 0; + } + return ret; } // Check if the client is connected @@ -69,6 +122,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule { get { return m_tcpclient.Connected; } } #endregion + // Constructor public RegionSyncClientView(int num, Scene scene, TcpClient client) { @@ -83,21 +137,23 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Create a thread for the receive loop m_receive_loop = new Thread(new ThreadStart(delegate() { ReceiveLoop(); })); - m_receive_loop.Name = Description; + m_receive_loop.Name = Description + " (ReceiveLoop)"; //m_log.WarnFormat("{0} Started thread: {1}", LogHeader, m_receive_loop.Name); m_receive_loop.Start(); + + m_send_loop = new Thread(new ThreadStart(delegate() { SendLoop(); })); + m_send_loop.Name = Description + " (SendLoop)"; + m_send_loop.Start(); } // Stop the RegionSyncClientView, disconnecting the RegionSyncClient public void Shutdown() { m_scene.EventManager.OnChatFromClient -= EventManager_OnChatFromClient; - // Abort ReceiveLoop Thread, close Socket and TcpClient - m_receive_loop.Abort(); m_tcpclient.Client.Close(); m_tcpclient.Close(); //Logout any synced avatars - lock (m_syncedAvatars) + lock (m_syncRoot) { foreach (UUID agentID in m_syncedAvatars.Keys) { @@ -117,10 +173,15 @@ namespace OpenSim.Region.Examples.RegionSyncModule } // Listen for messages from a RegionSyncClient - // *** This is the main thread loop for each connected client + // *** This is the main receive loop thread for each connected client private void ReceiveLoop() { m_scene.EventManager.OnChatFromClient += new EventManager.ChatFromClientEvent(EventManager_OnChatFromClient); + + // Reset stats and time + lastStatTime = DateTime.Now; + msgsIn = msgsOut = bytesIn = bytesOut = 0; + try { while (true) @@ -131,16 +192,49 @@ namespace OpenSim.Region.Examples.RegionSyncModule msgsIn++; bytesIn += msg.Length; } - HandleMessage(msg); + lock (m_syncRoot) + HandleMessage(msg); } } catch (Exception e) { - m_log.WarnFormat("{0} RegionSyncClient has disconnected: {1}", LogHeader, e.Message); + m_log.ErrorFormat("{0} RegionSyncClient has disconnected: {1} (ReceiveLoop)", LogHeader, e.Message); + } + Shutdown(); + // Thread exits here + } + + // Send messages from the update Q as fast as we can DeQueue them + // *** This is the main send loop thread for each connected client + private void SendLoop() + { + try + { + while (true) + { + // Dequeue is thread safe + byte[] update = m_outQ.Dequeue(); + lock (stats) + dequeuedUpdates++; + Send(update); + } + } + catch (Exception e) + { + m_log.ErrorFormat("{0} RegionSyncClient has disconnected: {1} (SendLoop)", LogHeader, e.Message); } Shutdown(); } + public void EnqueuePresenceUpdate(UUID id, byte[] update) + { + lock (stats) + queuedUpdates++; + // Enqueue is thread safe + m_outQ.Enqueue(id, update); + } + + void EventManager_OnChatFromClient(object sender, OSChatMessage chat) { OSDMap data = new OSDMap(5); @@ -167,32 +261,42 @@ namespace OpenSim.Region.Examples.RegionSyncModule OSDMap data = null; try { - data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data)) as OSDMap; + data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data, 0, msg.Length)) as OSDMap; } catch(Exception e) { lock(exceptions) // If this is a new message, then print the underlying data that caused it if(!exceptions.Contains(e.Message)) - m_log.Error(LogHeader + " " + Encoding.ASCII.GetString(msg.Data)); + m_log.Error(LogHeader + " " + Encoding.ASCII.GetString(msg.Data, 0, msg.Length)); data = null; } return data; } + private Dictionary m_appearanceTimers = new Dictionary(); // Handle an incoming message // *** Perhaps this should not be synchronous with the receive // We could handle messages from an incoming Queue - private bool HandleMessage(RegionSyncMessage msg) + private void HandleMessage(RegionSyncMessage msg) { + msgCount++; //string handlerMessage = ""; switch (msg.Type) { + case RegionSyncMessage.MsgType.RegionName: + { + m_regionName = Encoding.ASCII.GetString(msg.Data, 0, msg.Length); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.RegionName, m_scene.RegionInfo.RegionName)); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Syncing to region \"{0}\"", m_regionName)); + return; + } case RegionSyncMessage.MsgType.GetTerrain: { Send(new RegionSyncMessage(RegionSyncMessage.MsgType.Terrain, m_scene.Heightmap.SaveToXmlString())); - return HandlerSuccess(msg, "Terrain sent"); + RegionSyncMessage.HandleSuccess(LogHeader, msg, "Terrain sent"); + return; } case RegionSyncMessage.MsgType.GetObjects: { @@ -205,7 +309,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule Send(new RegionSyncMessage(RegionSyncMessage.MsgType.NewObject, sogxml)); } } - return HandlerSuccess(msg, "Sent all scene objects"); + RegionSyncMessage.HandleSuccess(LogHeader, msg, "Sent all scene objects"); + return; } case RegionSyncMessage.MsgType.GetAvatars: { @@ -219,7 +324,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule data["startPos"] = OSD.FromVector3(presence.ControllingClient.StartPos); Send(new RegionSyncMessage(RegionSyncMessage.MsgType.NewAvatar, OSDParser.SerializeJsonString(data))); }); - return HandlerSuccess(msg, "Sent all scene avatars"); + RegionSyncMessage.HandleSuccess(LogHeader, msg, "Sent all scene avatars"); + return; } case RegionSyncMessage.MsgType.AgentAdd: { @@ -234,19 +340,22 @@ namespace OpenSim.Region.Examples.RegionSyncModule if (agentID != null && first != null && last != null && startPos != null) { RegionSyncAvatar av = new RegionSyncAvatar(m_scene, agentID, first, last, startPos); - lock (m_syncedAvatars) + lock (m_syncRoot) { if (m_syncedAvatars.ContainsKey(agentID)) { - return HandlerFailure(msg, String.Format( "Attempted to add duplicate avatar with agentID {0}", agentID)); + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Attempted to add duplicate avatar with agentID {0}", agentID)); + return; } m_syncedAvatars.Add(agentID, av); } m_scene.AddNewClient(av); - return HandlerSuccess(msg, String.Format("Handled AddAgent for UUID {0}", agentID)); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Handled AddAgent for UUID {0}", agentID)); + return; } } - return HandlerFailure(msg, "Could not deserialize JSON data."); + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } case RegionSyncMessage.MsgType.AgentUpdate: @@ -259,12 +368,15 @@ namespace OpenSim.Region.Examples.RegionSyncModule RegionSyncAvatar av; bool found; - lock (m_syncedAvatars) + lock (m_syncRoot) { found = m_syncedAvatars.TryGetValue(agentData.AgentID, out av); } if(!found) - return HandlerFailure(msg, String.Format("Received agent update for non-existent avatar with UUID {0}", agentData.AgentID)); + { + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Received agent update for non-existent avatar with UUID {0}", agentData.AgentID)); + return; + } AgentUpdateArgs arg = new AgentUpdateArgs(); arg.AgentID = agentData.AgentID; @@ -281,9 +393,15 @@ namespace OpenSim.Region.Examples.RegionSyncModule arg.State = agentData.State; if( av.AgentUpdate(arg) ) - return HandlerSuccess(msg, String.Format("Handled AgentUpdate for UUID {0}", agentID)); + { + //RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Handled AgentUpdate for UUID {0}", agentID)); + return; + } else - return HandlerFailure(msg, String.Format("Could not handle AgentUpdate UUID {0}", agentID)); + { + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Could not handle AgentUpdate UUID {0}", agentID)); + return; + } } case RegionSyncMessage.MsgType.AgentRemove: { @@ -291,17 +409,19 @@ namespace OpenSim.Region.Examples.RegionSyncModule OSDMap data = DeserializeMessage(msg); if (data == null) { - return HandlerFailure(msg, "Could not deserialize JSON data."); + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } // Get the parameters from data and error check UUID agentID = data["agentID"].AsUUID(); if (agentID == null || agentID == UUID.Zero) { - return HandlerFailure(msg, "Missing or invalid JSON data."); + RegionSyncMessage.HandleError(LogHeader, msg, "Missing or invalid JSON data."); + return; } - lock (m_syncedAvatars) + lock (m_syncRoot) { if (m_syncedAvatars.ContainsKey(agentID)) { @@ -311,58 +431,99 @@ namespace OpenSim.Region.Examples.RegionSyncModule if (m_scene.TryGetScenePresence(agentID, out presence)) { string name = presence.Name; + m_scene.SceneGraph.DeleteSceneObject(UUID.Zero, true); m_scene.RemoveClient(agentID); - return HandlerSuccess(msg, String.Format("Agent \"{0}\" ({1}) was removed from scene.", name, agentID)); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Agent \"{0}\" was removed from scene.", name)); + return; } else { - return HandlerFailure(msg, String.Format("Agent {0} not found in the scene.", agentID)); + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Agent {0} not found in the scene.", agentID)); + return; } } else { - return HandlerFailure(msg, String.Format("Agent {0} not in the list of synced avatars.", agentID)); + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Agent {0} not in the list of synced avatars.", agentID)); + return; } } } case RegionSyncMessage.MsgType.AvatarAppearance: { + int msgID = msgCount; + //m_log.WarnFormat("{0} START of AvatarAppearance handler <{1}>", LogHeader, msgID); // Get the data from message and error check OSDMap data = DeserializeMessage(msg); if (data == null) { - return HandlerFailure(msg, "Could not deserialize JSON data."); + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } // Get the parameters from data and error check UUID agentID = data["id"].AsUUID(); if (agentID == null || agentID == UUID.Zero) { - return HandlerFailure(msg, "Missing or invalid JSON data."); + RegionSyncMessage.HandleError(LogHeader, msg, "Missing or invalid JSON data."); + return; } - // Find the presence in the scene ScenePresence presence; if (m_scene.TryGetScenePresence(agentID, out presence)) { + int delay = 5000; string name = presence.Name; - Primitive.TextureEntry te = Primitive.TextureEntry.FromOSD(data["te"]); - byte[] vp = data["vp"].AsBinary(); + //m_log.WarnFormat("{0} Waiting {1}ms before setting appearance on presence {2} <{3}>", LogHeader, delay, name, msgID); + Timer appearanceSetter = new Timer(delegate(object obj) + { + //m_log.WarnFormat("{0} Ready to set appearance on presence {1} <{2}>", LogHeader, name, msgID); + Primitive.TextureEntry te = Primitive.TextureEntry.FromOSD(data["te"]); + byte[] vp = data["vp"].AsBinary(); - byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; - for (int i = 0; i < BAKE_INDICES.Length; i++) - { - int j = BAKE_INDICES[i]; - Primitive.TextureEntryFace face = te.FaceTextures[j]; - if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) - if (m_scene.AssetService.Get(face.TextureID.ToString()) == null) - HandlerDebug(msg, "Missing baked texture " + face.TextureID + " (" + j + ") for avatar " + name); - } + bool missingBakes = false; + byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; + for (int i = 0; i < BAKE_INDICES.Length; i++) + { + int j = BAKE_INDICES[i]; + Primitive.TextureEntryFace face = te.FaceTextures[j]; + if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) + { + if (m_scene.AssetService.Get(face.TextureID.ToString()) == null) + { + RegionSyncMessage.HandlerDebug(LogHeader, msg, "Missing baked texture " + face.TextureID + " (" + j + ") for avatar " + name); + missingBakes = true; + } + } + } - presence.SetAppearance(te, vp); - return HandlerDebug(msg, String.Format("Agent \"{0}\" ({1}) updated their appearance.", name, agentID)); + //m_log.WarnFormat("{0} {1} Calling presence.SetAppearance {2} <{3}>", LogHeader, name, (missingBakes ? "MISSING BAKES" : "GOT BAKES"), msgID); + try + { + presence.SetAppearance(te, vp); + } + catch (Exception e) + { + m_log.WarnFormat("{0} Caught exception setting appearance for {1} (probably was removed from scene): {2}", LogHeader, name, e.Message); + } + if (!missingBakes) + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Set appearance for {0} <{1}>", name, msgID)); + else + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Set appearance for {0} but has missing bakes. <{1}>", name, msgID)); + //m_log.WarnFormat("{0} Calling RegionsSyncServerModule.SendAppearance for {1} {2} <{3}>", LogHeader, name, (missingBakes ? "MISSING BAKES" : "GOT BAKES"), msgID); + m_scene.RegionSyncServerModule.SendAppearance(presence.UUID, presence.Appearance.VisualParams, presence.Appearance.Texture); + lock (m_appearanceTimers) + m_appearanceTimers.Remove(agentID); + }, null, delay, Timeout.Infinite); + lock (m_appearanceTimers) + m_appearanceTimers[agentID] = appearanceSetter; } - return HandlerFailure(msg, String.Format("Agent {0} not found in the scene.", agentID)); + else + { + RegionSyncMessage.HandleWarning(LogHeader, msg, String.Format("Presence not found in the scene: {0} <{1}>", agentID, msgID)); + } + //m_log.WarnFormat("{0} END of AvatarAppearance handler <{1}>", LogHeader, msgID); + return; } case RegionSyncMessage.MsgType.ChatFromClient: { @@ -370,7 +531,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule OSDMap data = DeserializeMessage(msg); if (data == null) { - return HandlerFailure(msg, "Could not deserialize JSON data."); + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; } OSChatMessage args = new OSChatMessage(); args.Channel = data["channel"].AsInteger(); @@ -388,12 +550,31 @@ namespace OpenSim.Region.Examples.RegionSyncModule args.SenderUUID = id; m_scene.EventManager.TriggerOnChatFromClient(sp.ControllingClient,args); } - return HandlerSuccess(msg, String.Format("Received chat from \"{0}\"", args.From)); + RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Received chat from \"{0}\"", args.From)); + return; + } + case RegionSyncMessage.MsgType.RegionStatus: + { + // Get the data from message and error check + OSDMap data = DeserializeMessage(msg); + if (data == null) + { + RegionSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; + } + int t = data["total"].AsInteger(); + int l = data["local"].AsInteger(); + int r = data["remote"].AsInteger(); + lastTotalCount = t; + lastLocalCount = l; + lastRemoteCount = r; + //RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Received stats: {0},{1},{2}", t, l, r)); + return; } default: { m_log.WarnFormat("{0} Unable to handle unsupported message type", LogHeader); - return false; + return; } } } @@ -406,7 +587,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule private bool HandlerSuccess(RegionSyncMessage msg, string handlerMessage) { - //m_log.WarnFormat("{0} Handled {1}: {2}", LogHeader, msg.ToString(), handlerMessage); + m_log.WarnFormat("{0} Handled {1}: {2}", LogHeader, msg.ToString(), handlerMessage); return true; } @@ -418,8 +599,9 @@ namespace OpenSim.Region.Examples.RegionSyncModule public void Send(RegionSyncMessage msg) { + //if (msg.Type == RegionSyncMessage.MsgType.AvatarAppearance) + //m_log.WarnFormat("{0} Sending AvatarAppearance to client manager", LogHeader); Send(msg.ToBytes()); - //m_log.WarnFormat("{0} Sent {1}", LogHeader, msg.ToString()); } private void Send(byte[] data) @@ -433,31 +615,46 @@ namespace OpenSim.Region.Examples.RegionSyncModule msgsOut++; bytesOut += data.Length; } - m_tcpclient.GetStream().Write(data, 0, data.Length); + m_tcpclient.GetStream().BeginWrite(data, 0, data.Length, ar => + { + if(m_tcpclient.Connected) + { + try + { + m_tcpclient.GetStream().EndWrite(ar); + } + catch(Exception) + {} + } + }, null); } - catch (IOException e) + catch (IOException) { m_log.WarnFormat("{0} RegionSyncClient has disconnected.", LogHeader); } } } - #region crud - // Should be part of the RegionSyncClient - /* - public string ReceiveMsg() + public void ReportStatus() { - lock (m_outQ) + int syncedAvCount; + lock (m_syncRoot) + syncedAvCount = m_syncedAvatars.Count; + lock (stats) { - if (m_outQ.Count > 0) - { - return m_outQ.Dequeue(); - } + bool localcheck = true; + bool remotecheck = true; + bool totalcheck = true; + if (syncedAvCount != lastLocalCount) + localcheck = false; + if (m_scene.SceneGraph.GetRootAgentCount() != lastTotalCount) + totalcheck = false; + if (m_scene.SceneGraph.GetRootAgentCount() - syncedAvCount != lastRemoteCount) + remotecheck = false; + m_log.ErrorFormat("{0} Syncing {1,4} remote presences. Remote scene reporting {2,4} locals, {3,4} remotes, {4,4} total ({5},{6},{7})", + LogHeader, m_syncedAvatars.Count, lastLocalCount, lastRemoteCount, lastTotalCount, localcheck ? " " : "!", remotecheck ? " " : "!", totalcheck ? " " : "!"); } - return null; } - * */ - #endregion } } diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs index 282f8a427e..6d9b6e07cb 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs @@ -3,7 +3,7 @@ using System.IO; using OpenMetaverse; using log4net; -namespace OpenSim.Region.Examples.RegionSyncModule +namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { /// /// A message for synchonization message between scenes @@ -39,13 +39,16 @@ namespace OpenSim.Region.Examples.RegionSyncModule ChatFromSim, // BIDIR EchoRequest, - EchoResponse + EchoResponse, + RegionName, + RegionStatus } #endregion #region Member Data private MsgType m_type; private byte[] m_data; + static ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); #endregion #region Constructors @@ -58,7 +61,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule public RegionSyncMessage(MsgType type, string msg) { m_type = type; - m_data = System.Text.Encoding.ASCII.GetBytes(msg + System.Environment.NewLine); + m_data = System.Text.Encoding.ASCII.GetBytes(msg); } public RegionSyncMessage(MsgType type) @@ -131,5 +134,32 @@ namespace OpenSim.Region.Examples.RegionSyncModule return String.Format("{0} ({1} bytes)", m_type.ToString(), m_data.Length.ToString()); } #endregion + + public static void HandleSuccess(string header, RegionSyncMessage msg, string message) + { + m_log.WarnFormat("{0} Handled {1}: {2}", header, msg.ToString(), message); + } + + public static void HandleTrivial(string header, RegionSyncMessage msg, string message) + { + m_log.WarnFormat("{0} Issue handling {1}: {2}", header, msg.ToString(), message); + } + + public static void HandleWarning(string header, RegionSyncMessage msg, string message) + { + m_log.WarnFormat("{0} Warning handling {1}: {2}", header, msg.ToString(), message); + } + + public static void HandleError(string header, RegionSyncMessage msg, string message) + { + m_log.WarnFormat("{0} Error handling {1}: {2}", header, msg.ToString(), message); + } + + public static bool HandlerDebug(string header, RegionSyncMessage msg, string message) + { + m_log.WarnFormat("{0} DBG ({1}): {2}", header, msg.ToString(), message); + return true; + } + } } diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs index 886b3619fe..9def96e88f 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs @@ -7,9 +7,10 @@ using System.Collections.Generic; using System.Threading; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; +using OpenMetaverse; using log4net; -namespace OpenSim.Region.Examples.RegionSyncModule +namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { // The RegionSyncServer has a listener thread which accepts connections from RegionSyncClients // and an additional thread to process updates to/from each RegionSyncClient. @@ -24,13 +25,6 @@ namespace OpenSim.Region.Examples.RegionSyncModule // The local scene. private Scene m_scene; - - // A queue for incoming and outgoing traffic - // Incoming stuff can be from any client - // Outgoing stuff will be multicast to all clients - private Queue m_inQ = new Queue(); - private Queue m_outQ = new Queue(); - private ILog m_log; // The listener and the thread which listens for connections from client managers @@ -81,17 +75,34 @@ namespace OpenSim.Region.Examples.RegionSyncModule } } - public void ReportStats() + public void ReportStats(System.IO.TextWriter tw) { // We should be able to safely iterate over our reference to the list since // the only places which change it will replace it with an updated version - m_log.Error("SERVER, MSGIN, MSGOUT, BYTESIN, BYTESOUT"); - foreach (RegionSyncClientView rscv in m_client_views) + HashSet cvs = m_client_views; + tw.WriteLine("{0}: [REGION SYNC SERVER] TOTAL LOCAL REMOTE TO_SCENE FROM_SCENE", DateTime.Now.ToLongTimeString()); + tw.WriteLine("{0}: [REGION SYNC SERVER] MSGS ( /s ) BYTES ( Mbps ) MSGS ( /s ) BYTES ( Mbps ) QUEUE", DateTime.Now.ToLongTimeString()); + foreach (RegionSyncClientView cv in cvs) { - m_log.ErrorFormat("{0}: {1}", rscv.Description, rscv.GetStats()); + tw.WriteLine("{0}: [{1}] {2}", DateTime.Now.ToLongTimeString(), cv.Description, cv.GetStats()); + } + tw.Flush(); + } + + public void ReportStatus() + { + lock (m_clientview_lock) + { + m_log.ErrorFormat("[REGION SYNC SERVER] Connected to {0} remote client managers", m_client_views.Count); + m_log.ErrorFormat("[REGION SYNC SERVER] Local scene contains {0} presences", m_scene.SceneGraph.GetRootAgentCount()); + foreach (RegionSyncClientView rscv in m_client_views) + { + rscv.ReportStatus(); + } } } + #endregion // Constructor @@ -114,6 +125,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule //m_log.Warn("[REGION SYNC SERVER] Started"); } + + // Stop the server and disconnect all RegionSyncClients public void Shutdown() { @@ -190,5 +203,32 @@ namespace OpenSim.Region.Examples.RegionSyncModule //m_client_views.Remove(rscv); } } + + // Broadcast a message to all connected RegionSyncClients + public void EnqueuePresenceUpdate(UUID id, byte[] update) + { + List closed = null; + foreach (RegionSyncClientView client in m_client_views) + { + // If connected, send the message. + if (client.Connected) + { + client.EnqueuePresenceUpdate(id, update); + } + // Else, remove the client view from the list + else + { + if (closed == null) + closed = new List(); + closed.Add(client); + } + } + if (closed != null) + { + foreach (RegionSyncClientView rscv in closed) + RemoveSyncedClient(rscv); + //m_client_views.Remove(rscv); + } + } } } diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs index 002f474b41..bf45e706ca 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs @@ -38,11 +38,12 @@ using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Serialization; using log4net; +using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; -namespace OpenSim.Region.Examples.RegionSyncModule +namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { public class RegionSyncServerModule : IRegionModule, IRegionSyncServerModule, ICommandableModule { @@ -93,9 +94,20 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Start the server and listen for RegionSyncClients m_server = new RegionSyncServer(m_scene, m_serveraddr, m_serverport); m_server.Start(); + m_statsTimer.Elapsed += new System.Timers.ElapsedEventHandler(StatsTimerElapsed); + m_statsTimer.Start(); //m_log.Warn("[REGION SYNC SERVER MODULE] Post-Initialised"); } + private void StatsTimerElapsed(object source, System.Timers.ElapsedEventArgs e) + { + if (Synced) + { + TextWriter tw = File.AppendText("syncstats.txt"); + m_server.ReportStats(tw); + tw.Close(); + } + } void IRegionModule.Close() { @@ -128,6 +140,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule private Dictionary m_primUpdates = new Dictionary(); private Dictionary m_presenceUpdates = new Dictionary(); + private System.Timers.Timer m_statsTimer = new System.Timers.Timer(1000); + public void QueuePartForUpdate(SceneObjectPart part) { if (!Active || !Synced) @@ -189,24 +203,32 @@ namespace OpenSim.Region.Examples.RegionSyncModule } foreach (ScenePresence presence in presenceUpdates) { - if (!presence.IsDeleted) + try { - OSDMap data = new OSDMap(6); - data["id"] = OSD.FromUUID(presence.UUID); - // Do not include offset for appearance height. That will be handled by RegionSyncClient before sending to viewers - if(presence.AbsolutePosition.IsFinite()) - data["pos"] = OSD.FromVector3(presence.AbsolutePosition); - else - data["pos"] = OSD.FromVector3(Vector3.Zero); - if(presence.Velocity.IsFinite()) - data["vel"] = OSD.FromVector3(presence.Velocity); - else - data["vel"] = OSD.FromVector3(Vector3.Zero); - data["rot"] = OSD.FromQuaternion(presence.Rotation); - data["fly"] = OSD.FromBoolean(presence.Flying); - data["flags"] = OSD.FromUInteger((uint)presence.AgentControlFlags); - RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.UpdatedAvatar, OSDParser.SerializeJsonString(data)); - m_server.Broadcast(rsm); + if (!presence.IsDeleted) + { + OSDMap data = new OSDMap(7); + data["id"] = OSD.FromUUID(presence.UUID); + // Do not include offset for appearance height. That will be handled by RegionSyncClient before sending to viewers + if (presence.AbsolutePosition.IsFinite()) + data["pos"] = OSD.FromVector3(presence.AbsolutePosition); + else + data["pos"] = OSD.FromVector3(Vector3.Zero); + if (presence.Velocity.IsFinite()) + data["vel"] = OSD.FromVector3(presence.Velocity); + else + data["vel"] = OSD.FromVector3(Vector3.Zero); + data["rot"] = OSD.FromQuaternion(presence.Rotation); + data["fly"] = OSD.FromBoolean(presence.Flying); + data["flags"] = OSD.FromUInteger((uint)presence.AgentControlFlags); + data["anim"] = OSD.FromString(presence.Animator.CurrentMovementAnimation); + RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.UpdatedAvatar, OSDParser.SerializeJsonString(data)); + m_server.EnqueuePresenceUpdate(presence.UUID, rsm.ToBytes()); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[REGION SYNC SERVER MODULE] Caught exception sending presence updates for {0}", presence.Name); } } // Indicate that the current batch of updates has been completed @@ -214,16 +236,35 @@ namespace OpenSim.Region.Examples.RegionSyncModule }); } + private Dictionary m_appearanceTimers = new Dictionary(); + public void SendAppearance(UUID agentID, byte[] vp, Primitive.TextureEntry te) { - if (te != null) + ScenePresence sp; + if (!m_scene.TryGetScenePresence(agentID, out sp)) { - OSDMap data = new OSDMap(2); - data["id"] = OSDUUID.FromUUID(agentID); - data["vp"] = new OSDBinary(vp); - data["te"] = te.GetOSD(); - m_server.Broadcast(new RegionSyncMessage(RegionSyncMessage.MsgType.AvatarAppearance, OSDParser.SerializeJsonString(data))); + m_log.WarnFormat("[REGION SYNC SERVER MODULE] <{0}> {1} SendAppearance could not locate presence!", " ", agentID); + return; } + //m_log.WarnFormat("[REGION SYNC SERVER MODULE] <{0}> {1} ScenePresence called SendAppearance ({2})", sp.Name, agentID, te == null ? " " : "te"); + if(te == null) + return; + int delay = 1000; + //m_log.WarnFormat("[REGION SYNC SERVER MODULE] <{0}> {1} Waiting {2}ms before sending appearance to all client managers", sp.Name, agentID, delay); + OSDMap data = new OSDMap(3); + data["id"] = OSDUUID.FromUUID(agentID); + data["vp"] = new OSDBinary(vp); + data["te"] = te.GetOSD(); + Timer appearanceSetter = new Timer(delegate(object obj) + { + //m_log.WarnFormat("[REGION SYNC SERVER MODULE] <{0}> {1} Broadcasting appearance to all client managers", sp.Name, agentID); + m_server.Broadcast(new RegionSyncMessage(RegionSyncMessage.MsgType.AvatarAppearance, OSDParser.SerializeJsonString(data))); + lock (m_appearanceTimers) + m_appearanceTimers.Remove(agentID); + }, null, delay, Timeout.Infinite); + // Just keeps a reference to this timer + lock (m_appearanceTimers) + m_appearanceTimers[agentID] = appearanceSetter; } public void DeleteObject(ulong regionHandle, uint localID) @@ -443,7 +484,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule void EventManager_OnAvatarEnteringNewParcel(ScenePresence avatar, int localLandID, UUID regionID) { - m_log.WarnFormat("[REGION SYNC SERVER MODULE] (OnAvatarEnteringNewParcel) Avatar \"{0}\" has joined the scene (1) {2} {3} {4}", avatar.Name, avatar.ControllingClient.AgentId.ToString(), avatar.UUID.ToString(), localLandID, regionID.ToString()); + m_log.WarnFormat("[REGION SYNC SERVER MODULE] (OnAvatarEnteringNewParcel) Avatar \"{0}\" has joined the scene {1} {2} {3} {4}", avatar.Name, avatar.ControllingClient.AgentId.ToString(), avatar.UUID.ToString(), localLandID, regionID.ToString()); DebugSceneStats(); } @@ -459,7 +500,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule { if (!Synced) return; - m_log.WarnFormat("[REGION SYNC SERVER MODULE] Agent \"{0}\" (1) has joined the scene", client.FirstName + " " + client.LastName, client.AgentId.ToString()); + m_log.WarnFormat("[REGION SYNC SERVER MODULE] Agent \"{0}\" {1} has joined the scene", client.FirstName + " " + client.LastName, client.AgentId.ToString()); // Let the client managers know that a new agent has connected OSDMap data = new OSDMap(1); data["agentID"] = OSD.FromUUID(client.AgentId); @@ -477,7 +518,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule ScenePresence avatar; if (m_scene.TryGetScenePresence(agentID, out avatar)) { - m_log.WarnFormat("[REGION SYNC SERVER MODULE] Avatar \"{0}\" (1) {2} has left the scene", avatar.Firstname + " " + avatar.Lastname, agentID.ToString(), avatar.UUID.ToString()); + m_log.WarnFormat("[REGION SYNC SERVER MODULE] Avatar \"{0}\" {1} {2} has left the scene", avatar.Firstname + " " + avatar.Lastname, agentID.ToString(), avatar.UUID.ToString()); } else { @@ -494,8 +535,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule #region Console Command Interface private void InstallInterfaces() { - Command cmdSyncStats = new Command("stats", CommandIntentions.COMMAND_HAZARDOUS, SyncStats, "Reports stats for the RegionSyncServer."); - m_commander.RegisterCommand("stats", cmdSyncStats); + Command cmdSyncStatus = new Command("status", CommandIntentions.COMMAND_HAZARDOUS, SyncStatus, "Reports current status of the RegionSyncServer."); + m_commander.RegisterCommand("status", cmdSyncStatus); lock (m_scene) { @@ -528,16 +569,34 @@ namespace OpenSim.Region.Examples.RegionSyncModule } } - private void SyncStats(Object[] args) + private void SyncStatus(Object[] args) { if (Synced) - m_server.ReportStats(); - else if (m_server != null) - m_log.Error("No RegionSyncClients connected"); + m_server.ReportStatus(); else - m_log.Error("The RegionSyncServer is not running!"); + m_log.Error("No RegionSyncClients connected"); } #endregion + + + private static readonly Vector3 CenterOfRegion = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f),20); + // ----------------------------------------------------------------- + // ----------------------------------------------------------------- + internal void LocalChat(string msg, int channel) + { + OSChatMessage osm = new OSChatMessage(); + osm.From = "RegionSyncServerModule"; + osm.Message = msg; + osm.Type = ChatTypeEnum.Region; + osm.Position = CenterOfRegion; + osm.Sender = null; + osm.SenderUUID = OpenMetaverse.UUID.Zero; // Hmph! Still? + + osm.Channel = channel; + + m_log.DebugFormat("[REGION SYNC SERVER MODULE] LocalChat({0},{1})", msg, channel); + m_scene.EventManager.TriggerOnChatBroadcast(this, osm); + } } } diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index eeb9e31f60..4590efdef5 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -403,6 +403,11 @@ namespace OpenSim.Region.Examples.SimpleModule set { } } + public uint MaxCoarseLocations + { + get { return 0; } + } + public virtual void ActivateGesture(UUID assetId, UUID gestureId) { } diff --git a/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs index 7d5d72eb69..9d6f240f2d 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs @@ -44,12 +44,5 @@ namespace OpenSim.Region.Framework.Interfaces void DeleteObject(ulong regionHandle, uint localID); void SendAppearance(UUID agentID, byte[] vp, Primitive.TextureEntry te); - //void SendPartFullUpdate(SceneObjectPart part); - - //void SendPartTerseUpdate(SceneObjectPart part); - - //void SendShutdownConnectionNotice(Scene scene); - - } } diff --git a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs index de4c5fb053..05c47edc51 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs @@ -52,6 +52,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation public string CurrentMovementAnimation { get { return m_movementAnimation; } + set { m_movementAnimation = value; } } protected string m_movementAnimation = "DEFAULT"; diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 18fff3d4b7..0a1add4c89 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -375,6 +375,7 @@ namespace OpenSim.Region.Framework.Scenes private int m_update_backup = 200; private int m_update_terrain = 50; private int m_update_land = 1; + private int m_update_coarse_locations = 25; private int frameMS; private int physicsMS2; @@ -1311,8 +1312,9 @@ namespace OpenSim.Region.Framework.Scenes while (m_regInfo.EstateSettings.EstateOwner == UUID.Zero && MainConsole.Instance != null) { MainConsole.Instance.Output("The current estate has no owner set."); - string first = MainConsole.Instance.CmdPrompt("Estate owner first name", "Test"); - string last = MainConsole.Instance.CmdPrompt("Estate owner last name", "User"); + string first = "Test";// MainConsole.Instance.CmdPrompt("Estate owner first name", "Test"); + string last = "User";// MainConsole.Instance.CmdPrompt("Estate owner last name", "User"); + MainConsole.Instance.Output(String.Format("Setting estate owner to {0} {1}.", first, last)); UserAccount account = UserAccountService.GetUserAccount(m_regInfo.ScopeID, first, last); @@ -1472,6 +1474,23 @@ namespace OpenSim.Region.Framework.Scenes m_regionSyncServerModule.SendUpdates(); } + // The authoritative sim should not try to send coarse locations + // Leave this up to the client managers + if (!IsSyncedServer()) + { + if (m_frame % m_update_coarse_locations == 0) + { + List coarseLocations; + List avatarUUIDs; + SceneGraph.GetCoarseLocations(out coarseLocations, out avatarUUIDs, 60); + // Send coarse locations to clients + ForEachScenePresence(delegate(ScenePresence presence) + { + presence.SendCoarseLocations(coarseLocations, avatarUUIDs); + }); + } + } + int tmpPhysicsMS2 = Util.EnvironmentTickCount(); // Do not simulate physics locally if this is a synced client if (!IsSyncedClient()) @@ -3350,7 +3369,11 @@ namespace OpenSim.Region.Framework.Scenes (childagentYN ? "child" : "root"), agentID, RegionInfo.RegionName); m_sceneGraph.removeUserCount(!childagentYN); - CapsModule.RemoveCapsHandler(agentID); + + // If there is a CAPS handler, remove it now. + // A Synced server region will not have a CAPS handler for its presences + if(CapsModule.GetCapsHandlerForUser(agentID) != null) + CapsModule.RemoveCapsHandler(agentID); // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever // this method is doing is HORRIBLE!!! @@ -3381,7 +3404,7 @@ namespace OpenSim.Region.Framework.Scenes // Don't try to send kills to clients if this is a synced server. // The client closed event will trigger the broadcast to client managers - //if(!IsSyncedServer()) + if(!IsSyncedServer()) { ForEachClient( delegate(IClientAPI client) @@ -3392,9 +3415,6 @@ namespace OpenSim.Region.Framework.Scenes }); - ForEachScenePresence( - delegate(ScenePresence presence) { presence.CoarseLocationChange(); }); - IAgentAssetTransactions agentTransactions = this.RequestModuleInterface(); if (agentTransactions != null) { @@ -3447,15 +3467,6 @@ namespace OpenSim.Region.Framework.Scenes } } - /// - /// Inform all other ScenePresences on this Scene that someone else has changed position on the minimap. - /// - public void NotifyMyCoarseLocationChange() - { - // REGION SYNC (Need a better plan for coarse locations) - //ForEachScenePresence(delegate(ScenePresence presence) { presence.CoarseLocationChange(); }); - } - #endregion #region Entities diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 0abcbca89e..ecbf5e6813 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -206,6 +206,43 @@ namespace OpenSim.Region.Framework.Scenes }); } + public void GetCoarseLocations(out List coarseLocations, out List avatarUUIDs, uint maxLocations) + { + coarseLocations = new List(); + avatarUUIDs = new List(); + + List presences = GetScenePresences(); + for (int i = 0; i < Math.Min(presences.Count, maxLocations); ++i) + { + ScenePresence sp = presences[i]; + // If this presence is a child agent, we don't want its coarse locations + if (sp.IsChildAgent) + return; + + if (sp.ParentID != 0) + { + // sitting avatar + SceneObjectPart sop = m_parentScene.GetSceneObjectPart(sp.ParentID); + if (sop != null) + { + coarseLocations.Add(sop.AbsolutePosition + sp.AbsolutePosition); + avatarUUIDs.Add(sp.UUID); + } + else + { + // we can't find the parent.. ! arg! + coarseLocations.Add(sp.AbsolutePosition); + avatarUUIDs.Add(sp.UUID); + } + } + else + { + coarseLocations.Add(sp.AbsolutePosition); + avatarUUIDs.Add(sp.UUID); + } + } + } + #endregion #region Entity Methods diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index ec607abc9d..6f0081e2ac 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -65,7 +65,7 @@ namespace OpenSim.Region.Framework.Scenes public ScriptControlled eventControls; } - public delegate void SendCourseLocationsMethod(UUID scene, ScenePresence presence); + public delegate void SendCourseLocationsMethod(UUID scene, ScenePresence presence, List coarseLocations, List avatarUUIDs); public class ScenePresence : EntityBase, ISceneEntity { @@ -173,8 +173,6 @@ namespace OpenSim.Region.Framework.Scenes public string JID = String.Empty; - // Agent moves with a PID controller causing a force to be exerted. - private bool m_newCoarseLocations = true; private float m_health = 100f; // Default AV Height @@ -683,10 +681,12 @@ namespace OpenSim.Region.Framework.Scenes CreateSceneViewer(); m_animator = new ScenePresenceAnimator(this); + /* Primitive.TextureEntry te = AvatarAppearance.GetDefaultTexture(); byte[] vp = AvatarAppearance.GetDefaultVisualParams(); m_appearance = new AvatarAppearance(UUID); m_appearance.SetAppearance(te, vp); + * */ } private ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo) : this() @@ -722,7 +722,7 @@ namespace OpenSim.Region.Framework.Scenes AdjustKnownSeeds(); // TODO: I think, this won't send anything, as we are still a child here... - Animator.TrySetMovementAnimation("STAND"); + //Animator.TrySetMovementAnimation("STAND"); // we created a new ScenePresence (a new child agent) in a fresh region. // Request info about all the (root) agents in this region @@ -737,13 +737,13 @@ namespace OpenSim.Region.Framework.Scenes AvatarWearable[] wearables) : this(client, world, reginfo) { - //m_appearance = new AvatarAppearance(m_uuid, wearables, visualParams); + m_appearance = new AvatarAppearance(m_uuid, wearables, visualParams); } public ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo, AvatarAppearance appearance) : this(client, world, reginfo) { - //m_appearance = appearance; + m_appearance = appearance; } private void CreateSceneViewer() @@ -756,10 +756,12 @@ namespace OpenSim.Region.Framework.Scenes // REGION SYNC if (!m_scene.IsSyncedClient()) { + // These client messages will not be handled by client managers but instead + // they are caught by the RegionSyncClient module and passed up to the auth sim m_controllingClient.OnAgentUpdate += HandleAgentUpdate; + m_controllingClient.OnSetAppearance += SetAppearance; } - m_controllingClient.OnSetAppearance += SetAppearance; m_controllingClient.OnRequestWearables += SendWearables; m_controllingClient.OnCompleteMovementToRegion += CompleteMovement; //m_controllingClient.OnCompleteMovementToRegion += SendInitialData; @@ -2352,12 +2354,6 @@ namespace OpenSim.Region.Framework.Scenes SendPrimUpdates(); - if (m_newCoarseLocations) - { - SendCoarseLocations(); - m_newCoarseLocations = false; - } - if (m_isChildAgent == false) { // PhysicsActor actor = m_physicsActor; @@ -2428,6 +2424,10 @@ namespace OpenSim.Region.Framework.Scenes /// public void SendTerseUpdateToAllClients() { + // REGION SYNC + // The server should not be doing anything via the ForEachScenePresence method + if (m_scene.IsSyncedServer()) + return; m_perfMonMS = Util.EnvironmentTickCount(); m_scene.ForEachClient(SendTerseUpdateToClient); @@ -2435,29 +2435,13 @@ namespace OpenSim.Region.Framework.Scenes m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); } - /// - /// Send a location/velocity/accelleration update to all agents in a list - /// - public void SendTerseUpdateToClientList(List clients) + public void SendCoarseLocations(List coarseLocations, List avatarUUIDs) { - m_perfMonMS = Util.EnvironmentTickCount(); - foreach( IClientAPI client in clients) - { - SendTerseUpdateToClient(client); - } - m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); - } - - public void SendCoarseLocations() - { - SendCoarseLocationsDefault(m_scene.RegionInfo.originRegionID, this); - /* SendCourseLocationsMethod d = m_sendCourseLocationsMethod; if (d != null) { - d.Invoke(m_scene.RegionInfo.originRegionID, this); + d.Invoke(m_scene.RegionInfo.originRegionID, this, coarseLocations, avatarUUIDs); } - * */ } public void SetSendCourseLocationMethod(SendCourseLocationsMethod d) @@ -2466,23 +2450,12 @@ namespace OpenSim.Region.Framework.Scenes m_sendCourseLocationsMethod = d; } - public void SendCoarseLocationsDefault(UUID sceneId, ScenePresence p) + public void SendCoarseLocationsDefault(UUID sceneId, ScenePresence p, List coarseLocations, List avatarUUIDs) { m_perfMonMS = Util.EnvironmentTickCount(); - List AvatarUUIDs = new List(); - List CoarseLocations = new List(); - // This is not cheap to compile this list of locations. - // It should ideally be done once and then sent to every client rather than compiled for each client - //m_scene.GetCoarseLocations(out AvatarUUIDs, out CoarseLocations); - AvatarUUIDs.Add(UUID); - CoarseLocations.Add(AbsolutePosition); - m_controllingClient.SendCoarseLocationUpdate(AvatarUUIDs, CoarseLocations); + m_controllingClient.SendCoarseLocationUpdate(avatarUUIDs, coarseLocations); m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); - } - public void CoarseLocationChange() - { - m_newCoarseLocations = true; } /// @@ -2512,6 +2485,10 @@ namespace OpenSim.Region.Framework.Scenes /// public void SendInitialFullUpdateToAllClients() { + // REGION SYNC + // The server should not be doing anything via the ForEachScenePresence method + if (m_scene.IsSyncedServer()) + return; m_perfMonMS = Util.EnvironmentTickCount(); int avUpdates = 0; m_scene.ForEachScenePresence(delegate(ScenePresence avatar) @@ -2542,6 +2519,10 @@ namespace OpenSim.Region.Framework.Scenes public void SendFullUpdateToAllClients() { + // REGION SYNC + // The server should not be doing anything via the ForEachScenePresence method + if (m_scene.IsSyncedServer()) + return; m_perfMonMS = Util.EnvironmentTickCount(); // only send update from root agents to other clients; children are only "listening posts" @@ -2592,16 +2573,23 @@ namespace OpenSim.Region.Framework.Scenes /// public void SendAppearanceToAllOtherAgents() { + // REGION SYNC + // The server should not be doing anything via the ForEachScenePresence method + if (m_scene.IsSyncedServer()) + return; + if (Appearance.Texture == null) + return; m_perfMonMS = Util.EnvironmentTickCount(); // REGION SYNC - if(m_scene.IsSyncedServer()) - m_scene.RegionSyncServerModule.SendAppearance(UUID, Appearance.VisualParams, Appearance.Texture); + //if(m_scene.IsSyncedServer()) + //m_scene.RegionSyncServerModule.SendAppearance(UUID, Appearance.VisualParams, Appearance.Texture); m_appearance.Owner = UUID; // Why does this need to be here? m_scene.ForEachClient(delegate(IClientAPI client) { if(client.AgentId != ControllingClient.AgentId) { + //m_log.WarnFormat("[SCENE PRESENCE] Sending {0} appearance to {1} (SendAppearanceToAllOtherAgents)", Name, client.Name); client.SendAppearance(m_appearance.Owner, m_appearance.VisualParams, m_appearance.Texture.GetBytes()); } }); @@ -2627,7 +2615,10 @@ namespace OpenSim.Region.Framework.Scenes /// public void SendAppearanceToOtherAgent(ScenePresence avatar) { + if (Appearance.Texture == null) + return; //m_log.WarnFormat("{0} sending appearance to {1}, owner={2}", UUID, avatar.UUID, m_appearance.Owner); + //m_log.WarnFormat("[SCENE PRESENCE] Sending {0} appearance to {1} (SendAppearanceToOtherAgent)", Name, avatar.Name); m_appearance.Owner = UUID; avatar.ControllingClient.SendAppearance( m_appearance.Owner, m_appearance.VisualParams, m_appearance.Texture.GetBytes()); @@ -2640,6 +2631,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void SetAppearance(Primitive.TextureEntry textureEntry, byte[] visualParams) { + //m_log.WarnFormat("[SCENE PRESENCE] SetAppearance called for {0} ({1})", Name, textureEntry == null ? " " : "te"); if (m_physicsActor != null) { if (!IsChildAgent) @@ -2704,6 +2696,7 @@ namespace OpenSim.Region.Framework.Scenes public void SetWearable(int wearableId, AvatarWearable wearable) { + m_log.WarnFormat("[SCENE PRESENCE] SetWearable called for \"{0}\" (wearableID = {1})", Name, wearableId); m_appearance.SetWearable(wearableId, wearable); AvatarData adata = new AvatarData(m_appearance); m_scene.AvatarService.SetAvatar(m_controllingClient.AgentId, adata); @@ -2737,7 +2730,6 @@ namespace OpenSim.Region.Framework.Scenes { posLastSignificantMove = AbsolutePosition; m_scene.EventManager.TriggerSignificantClientMovement(m_controllingClient); - m_scene.NotifyMyCoarseLocationChange(); } // Minimum Draw distance is 64 meters, the Radius of the draw distance sphere is 32m diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 039bedbf67..a320a5053a 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -638,6 +638,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server set { } } + public uint MaxCoarseLocations + { + get { return 0; } + } + public uint CircuitCode { get { return (uint)Util.RandomClass.Next(0,int.MaxValue); } diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 9ac828b3ff..42374dbae0 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -131,6 +131,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC set { } } + public uint MaxCoarseLocations { get { return 0; } } + #region Internal Functions private void SendOnChatFromClient(string message, ChatTypeEnum chatType) diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs index 9cb349a1e9..ff0e743113 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs @@ -696,7 +696,9 @@ namespace OpenSim.Region.RegionCombinerModule presence.SetSendCourseLocationMethod(SendCourseLocationUpdates); } - private void SendCourseLocationUpdates(UUID sceneId, ScenePresence presence) + // This delegate was refactored for non-combined regions. + // This combined region version will not use the pre-compiled lists of locations and ids + private void SendCourseLocationUpdates(UUID sceneId, ScenePresence presence, List coarseLocations, List avatarUUIDs) { RegionConnections connectiondata = null; lock (m_regions) diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index a8f1b20abb..bb48490daf 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -417,6 +417,11 @@ namespace OpenSim.Tests.Common.Mock set { } } + public uint MaxCoarseLocations + { + get { return 0; } + } + private uint m_circuitCode; public uint CircuitCode diff --git a/bin/OpenSim.32BitLaunch.exe b/bin/OpenSim.32BitLaunch.exe index cc73c8bc98..cd6b7b5964 100755 Binary files a/bin/OpenSim.32BitLaunch.exe and b/bin/OpenSim.32BitLaunch.exe differ diff --git a/prebuild.xml b/prebuild.xml index 0a2abaa3c5..c23770502b 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -1378,6 +1378,7 @@ ../../../bin/ +