From 2283b85bcf4f3eb945ee1eaf4efca6bb59a449f6 Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Fri, 30 Apr 2010 08:13:02 -0700 Subject: [PATCH] Some updates for client manager --- .../ClientStack/LindenUDP/LLClientView.cs | 38 +- .../RegionSyncModule/RegionSyncAvatar.cs | 1 + .../RegionSyncModule/RegionSyncClient.cs | 344 ++++++++++++++---- .../RegionSyncClientModule.cs | 7 + .../RegionSyncModule/RegionSyncClientView.cs | 124 ++++++- .../RegionSyncModule/RegionSyncMessage.cs | 13 +- .../RegionSyncModule/RegionSyncServer.cs | 125 ++++--- .../RegionSyncServerModule.cs | 195 ++++++---- .../Interfaces/IRegionSyncClientModule.cs | 1 + OpenSim/Region/Framework/Scenes/Scene.cs | 109 ++++-- .../Framework/Scenes/SceneObjectGroup.cs | 2 +- .../Framework/Scenes/SceneObjectPart.cs | 11 +- .../Region/Framework/Scenes/ScenePresence.cs | 106 ++++-- .../BasicPhysicsPlugin/BasicPhysicsScene.cs | 17 +- .../Region/Physics/OdePlugin/ODECharacter.cs | 5 +- 15 files changed, 803 insertions(+), 295 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 28780ab454..48d4c2f7f3 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4901,32 +4901,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP int i = 0; x.ToBytes(xb, ref i); + // If there was a previous update and this update is exactly the same, skip it - if (m_lastAgentUpdate != null && Enumerable.SequenceEqual(xb, m_lastAgentUpdate)) + if (m_scene.IsSyncedClient() && m_lastAgentUpdate != null && Enumerable.SequenceEqual(xb, m_lastAgentUpdate)) + { + //m_log.Warn("LLClientView: HandleAgentUpdate (IDENTICAL TO LAST)"); return true; - - /* + } + // What is different?? (only interesting for client managers) + /* if(m_scene.IsSyncedClient() && m_lastAgentUpdate != null) { AgentUpdatePacket.AgentDataBlock lastarg = new AgentUpdatePacket.AgentDataBlock(); i = 0; lastarg.FromBytes(m_lastAgentUpdate, ref i); - - if(x.BodyRotation != lastarg.BodyRotation) m_log.Warn("BodyRotation"); - if(x.CameraAtAxis != lastarg.CameraAtAxis) m_log.Warn("CameraAtAxis"); - if(x.CameraCenter != lastarg.CameraCenter) m_log.Warn("CameraCenter"); - if(x.CameraLeftAxis != lastarg.CameraLeftAxis) m_log.Warn("CameraLeftAxis"); - if(x.CameraUpAxis != lastarg.CameraUpAxis) m_log.Warn("CameraUpAxis"); - if(x.ControlFlags != lastarg.ControlFlags) m_log.Warn("ControlFlags"); - if(x.Far != lastarg.Far) m_log.Warn("Far"); - if(x.Flags != lastarg.Flags) m_log.Warn("Flags"); - if(x.State != lastarg.State) m_log.Warn("State"); - if(x.HeadRotation != lastarg.HeadRotation) m_log.Warn("HeadRotation"); - if(x.SessionID != lastarg.SessionID) m_log.Warn("SessionID"); - if(x.AgentID != lastarg.AgentID) m_log.Warn("AgentID"); + m_log.WarnFormat("LLClientView: HandleAgentUpdate |{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}|", + (x.BodyRotation != lastarg.BodyRotation) ? "X" : " ", + (x.CameraAtAxis != lastarg.CameraAtAxis) ? "X" : " ", + (x.CameraCenter != lastarg.CameraCenter) ? "X" : " ", + (x.CameraLeftAxis != lastarg.CameraLeftAxis) ? "X" : " ", + (x.CameraUpAxis != lastarg.CameraUpAxis) ? "X" : " ", + (x.ControlFlags != lastarg.ControlFlags) ? "X" : " ", + (x.Far != lastarg.Far) ? "X" : " ", + (x.Flags != lastarg.Flags) ? "X" : " ", + (x.State != lastarg.State) ? "X" : " ", + (x.HeadRotation != lastarg.HeadRotation) ? "X" : " ", + (x.SessionID != lastarg.SessionID) ? "X" : " ", + (x.AgentID != lastarg.AgentID) ? "X" : " "); } * */ + + // save this set of arguments for next time m_lastAgentUpdate = xb; diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs index 80c1299544..c0f4dffeab 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs @@ -130,6 +130,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule public event Action OnRemoveAvatar; public event CreateNewInventoryItem OnCreateNewInventoryItem; + public event LinkInventoryItem OnLinkInventoryItem; public event CreateInventoryFolder OnCreateNewInventoryFolder; public event UpdateInventoryFolder OnUpdateInventoryFolder; public event MoveInventoryFolder OnMoveInventoryFolder; diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs index 939f594c22..dd60172763 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs @@ -20,6 +20,16 @@ namespace OpenSim.Region.Examples.RegionSyncModule // The RegionSyncClient has a receive thread to process messages from the RegionSyncServer. public class RegionSyncClient { + #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 + #region RegionSyncClient members // Set the addr and port of RegionSyncServer @@ -29,9 +39,15 @@ namespace OpenSim.Region.Examples.RegionSyncModule // A reference to the local scene private Scene m_scene; + // The avatars added to this client manager for clients on other client managers + Dictionary m_remoteAvatars = new Dictionary(); + Dictionary m_localAvatars = new Dictionary(); + // The logfile private ILog m_log; + private string LogHeader = "[REGION SYNC CLIENT]"; + // The listener and the thread which listens for connections from client managers private Thread m_rcvLoop; @@ -46,7 +62,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule public RegionSyncClient(Scene scene, string addr, int port) { m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - m_log.Warn("[REGION SYNC CLIENT] Constructed"); + m_log.WarnFormat("{0} Constructed", LogHeader); m_scene = scene; m_addr = IPAddress.Parse(addr); m_port = port; @@ -61,40 +77,63 @@ namespace OpenSim.Region.Examples.RegionSyncModule } catch (Exception e) { - m_log.WarnFormat("[REGION SYNC CLIENT] [Start] Could not connect to RegionSyncServer at {0}:{1}", m_addr, m_port); + m_log.WarnFormat("{0} [Start] Could not connect to RegionSyncServer at {1}:{2}", LogHeader, m_addr, m_port); m_log.Warn(e.Message); } - m_log.WarnFormat("[REGION SYNC CLIENT] Connected to RegionSyncServer at {0}:{1}", m_addr, m_port); + m_log.WarnFormat("{0} Connected to RegionSyncServer at {1}:{2}", LogHeader, m_addr, m_port); m_rcvLoop = new Thread(new ThreadStart(ReceiveLoop)); m_rcvLoop.Name = "RegionSyncClient ReceiveLoop"; - m_log.WarnFormat("[REGION SYNC CLIENT] Starting {0} thread", m_rcvLoop.Name); + m_log.WarnFormat("{0} Starting {1} thread", LogHeader, m_rcvLoop.Name); m_rcvLoop.Start(); - m_log.Warn("[REGION SYNC CLIENT] Started"); + m_log.WarnFormat("{0} Started", LogHeader); DoInitialSync(); } // Disconnect from the RegionSyncServer and close the RegionSyncClient public void Stop() { + // Can't abort the TCP connection until we let the remote scene know we are going away. + // Or, the remote scene will remove the CM's avatars automatically when it disconnects m_rcvLoop.Abort(); ShutdownClient(); } private void ShutdownClient() { - m_log.WarnFormat("[REGION SYNC CLIENT] Disconnected from RegionSyncServer. Shutting down."); - m_scene.ForEachClient(delegate(IClientAPI client) { client.OnAgentUpdateRaw -= HandleAgentUpdateRaw; }); + m_log.WarnFormat("{0} Disconnected from RegionSyncServer. Shutting down.", LogHeader); + + // Remove remote avatars from local scene + lock (m_remoteAvatars) + { + foreach (UUID id in m_remoteAvatars.Keys) + { + m_scene.RemoveClient(id); + } + } + // Remove local avatars from remote scene + lock (m_localAvatars) + { + foreach (KeyValuePair kvp in m_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; + } + } + // Close the connection m_client.Client.Close(); m_client.Close(); + } // Listen for messages from a RegionSyncClient // *** This is the main thread loop for each connected client private void ReceiveLoop() { - m_log.WarnFormat("[REGION SYNC CLIENT] Thread running: {0}", m_rcvLoop.Name); + m_log.WarnFormat("{0} Thread running: {1}", LogHeader, m_rcvLoop.Name); while (true && m_client.Connected) { RegionSyncMessage msg; @@ -102,7 +141,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule try { msg = new RegionSyncMessage(m_client.GetStream()); - //m_log.WarnFormat("[REGION SYNC CLIENT] Received: {0}", msg.ToString()); + //m_log.WarnFormat("{0} Received: {1}", LogHeader, msg.ToString()); } // If there is a problem reading from the client, shut 'er down. catch @@ -113,16 +152,32 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Try handling the message try { - HandleMessage(msg); + string message; + MsgHandlerStatus 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; + } } catch (Exception e) { - m_log.WarnFormat("[REGION SYNC CLIENT] Encountered an exception: {0}", e.Message); + m_log.WarnFormat("{0} Encountered an exception: {1}", LogHeader, e.Message); } } } - #region Send + #region SEND // Send a message to a single connected RegionSyncClient private void Send(string msg) { @@ -133,7 +188,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule private void Send(RegionSyncMessage msg) { Send(msg.ToBytes()); - //m_log.WarnFormat("[REGION SYNC CLIENT] Sent {0}", msg.ToString()); + //m_log.WarnFormat("{0} Sent {1}", LogHeader, msg.ToString()); } private void Send(byte[] data) @@ -153,59 +208,72 @@ namespace OpenSim.Region.Examples.RegionSyncModule } } #endregion - + + // Send coarse locations to locally connected clients + // This is a highly optimized version for lots of local and remote clients + // 99.9% faster for 1000 clients! + public void SendCoarseLocations() + { + List ids = new List(); + List locations = new List(); + m_scene.GetCoarseLocations(out ids, out locations); + lock (m_localAvatars) + { + foreach (IClientAPI client in m_localAvatars.Values) + { + client.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 bool HandleMessage(RegionSyncMessage msg) + private MsgHandlerStatus HandleMessage(RegionSyncMessage msg, out string result) { switch (msg.Type) { case RegionSyncMessage.MsgType.Terrain: { m_scene.Heightmap.LoadFromXmlString(Encoding.ASCII.GetString(msg.Data)); - return HandlerSuccess(msg, "Syncrhonized terrain"); + result = "Synchronized terrain"; + return MsgHandlerStatus.Success; } - case RegionSyncMessage.MsgType.AddObject: - case RegionSyncMessage.MsgType.UpdateObject: + case RegionSyncMessage.MsgType.NewObject: + case RegionSyncMessage.MsgType.UpdatedObject: { SceneObjectGroup sog = SceneObjectSerializer.FromXml2Format(Encoding.ASCII.GetString(msg.Data)); - if(sog.IsDeleted) - return HandlerFailure(msg, String.Format("Ignoring update on deleted LocalId {0}.", sog.LocalId.ToString())); + if (sog.IsDeleted) + { + result = String.Format("Ignoring update on deleted LocalId {0}.", sog.LocalId.ToString()); + return MsgHandlerStatus.Trivial; + } if (m_scene.AddNewSceneObject(sog, true)) - { - sog.ScheduleGroupForFullUpdate(); - return HandlerSuccess(msg, String.Format("LocalId {0} added or updated.", sog.LocalId.ToString())); - } - return HandlerFailure(msg, String.Format("Could not add or update LocalId {0}.", sog.LocalId.ToString())); - } - case RegionSyncMessage.MsgType.UpdateAvatarTerse: - { - - OSDMap data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data)) as OSDMap; - if( data != null ) - { - UUID agentID = data["id"].AsUUID(); - Vector3 pos = data["pos"].AsVector3(); - Vector3 vel = data["vel"].AsVector3(); - Quaternion rot = data["rot"].AsQuaternion(); - - // We get the UUID of the avatar to be updated, find it in the scene - if (agentID != UUID.Zero) - { - ScenePresence presence = m_scene.GetScenePresence(agentID); - if (presence != null) - { - presence.AbsolutePosition = pos; - presence.Velocity = vel; - presence.Rotation = rot; - presence.SendTerseUpdateToAllClients(); - return HandlerSuccess(msg, String.Format("agentID {0} updated", agentID.ToString())); - } - } - return HandlerFailure(msg, String.Format("agentID {0} not found.", agentID.ToString())); - } - return HandlerFailure(msg, "Could not parse AgentUpdate parameters"); + 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()); + sog.ScheduleGroupForFullUpdate(); + return MsgHandlerStatus.Success; } // return HandlerSuccess(msg, "Updated avatar"); /* @@ -228,9 +296,9 @@ namespace OpenSim.Region.Examples.RegionSyncModule } } * */ - case RegionSyncMessage.MsgType.RemoveObject: + case RegionSyncMessage.MsgType.RemovedObject: { - OSDMap data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data)) as OSDMap; + OSDMap data = DeserializeMessage(msg); if( data != null ) { ulong regionHandle = data["regionHandle"].AsULong(); @@ -245,15 +313,130 @@ namespace OpenSim.Region.Examples.RegionSyncModule if (sog != null) { m_scene.DeleteSceneObject(sog, false); - return HandlerSuccess(msg, String.Format("localID {0} deleted.", localID.ToString())); + result = String.Format("localID {0} deleted.", localID.ToString()); + return MsgHandlerStatus.Success; } } - return HandlerFailure(msg, String.Format("ignoring delete request for non-local regionHandle {0}.", regionHandle.ToString())); + result = String.Format("ignoring delete request for non-local regionHandle {0}.", regionHandle.ToString()); + return MsgHandlerStatus.Trivial; } - return HandlerFailure(msg, String.Format("localID {0} not found.", localID.ToString())); + result = String.Format("localID {0} not found.", localID.ToString()); + return MsgHandlerStatus.Warning; } - return HandlerFailure(msg, "Could not parse DeleteObject parameters"); + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; } + case RegionSyncMessage.MsgType.NewAvatar: + { + OSDMap data = DeserializeMessage(msg); + if (data != null) + { + UUID agentID = data["agentID"].AsUUID(); + string first = data["first"].AsString(); + string last = data["last"].AsString(); + Vector3 startPos = data["startPos"].AsVector3(); + + if (agentID != null && first != null && last != null && startPos != null) + { + if (m_remoteAvatars.ContainsKey(agentID)) + { + result = String.Format("Attempted to add duplicate avatar \"{0} {1}\" ({2})", first, last, agentID.ToString()); + return MsgHandlerStatus.Warning; + } + 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; + } + RegionSyncAvatar av = new RegionSyncAvatar(m_scene, agentID, first, last, startPos); + m_remoteAvatars.Add(agentID, av); + m_scene.AddNewClient(av); + result = String.Format("Handled NewAvatar for UUID {0}", agentID); + return MsgHandlerStatus.Success; + } + } + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; + } + case RegionSyncMessage.MsgType.UpdatedAvatar: + { + OSDMap data = DeserializeMessage(msg); + if (data != null) + { + 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(); + + // We get the UUID of the avatar to be updated, find it in the scene + if (agentID != UUID.Zero) + { + ScenePresence presence; + if (m_scene.TryGetScenePresence(agentID, out presence)) + { + presence.AbsolutePosition = pos; + presence.Velocity = vel; + presence.Rotation = rot; + presence.PhysicsActor.Flying = flying; + List locals; + lock (m_localAvatars) + locals = new List(m_localAvatars.Values); + presence.SendTerseUpdateToClientList(locals); + 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"); + return MsgHandlerStatus.Success; + } + } + result = String.Format("agentID {0} not found.", agentID.ToString()); + return MsgHandlerStatus.Warning; + } + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; + } + case RegionSyncMessage.MsgType.RemovedAvatar: + { + OSDMap data = DeserializeMessage(msg); + if (data != null) + { + UUID agentID = data["agentID"].AsUUID(); + + // We get the UUID of the avatar to be removed, find it in the scene + if (agentID != UUID.Zero) + { + ScenePresence presence; + // If this is a synced remote avatar and it's in the scene, then remove it + if (m_remoteAvatars.ContainsKey(agentID)) + { + if (m_scene.TryGetScenePresence(agentID, out presence)) + { + m_scene.RemoveClient(agentID); + result = String.Format("Avatar \"{0}\" removed from scene", presence.Name); + return MsgHandlerStatus.Success; + } + else + { + result = String.Format("agentID {0} not found.", agentID.ToString()); + return MsgHandlerStatus.Warning; + } + } + else + { + result = String.Format("agentID {0} is not a remote avatar.", agentID.ToString()); + return MsgHandlerStatus.Warning; + } + } + result = String.Format("agentID {0} was UUID.Zero!", agentID.ToString()); + return MsgHandlerStatus.Error; + } + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; + } + /* case RegionSyncMessage.MsgType.UpdateObject: { @@ -279,31 +462,27 @@ namespace OpenSim.Region.Examples.RegionSyncModule * */ default: { - m_log.WarnFormat("[REGION SYNC CLIENT] Unsupported message type"); - return false; + result = String.Format("{0} Unsupported message type: {1}", LogHeader, ((int)msg.Type).ToString()); + return MsgHandlerStatus.Error; } } } - private bool HandlerSuccess(RegionSyncMessage msg, string handlerMessage) + private bool HandlerDebug(RegionSyncMessage msg, string handlerMessage) { - //m_log.WarnFormat("[REGION SYNC CLIENT] Handled {0}: {1}", msg.ToString(), handlerMessage); + m_log.WarnFormat("{0} DBG ({1}): {2}", LogHeader, msg.ToString(), handlerMessage); return true; } - private bool HandlerFailure(RegionSyncMessage msg, string handlerMessage) - { - //m_log.WarnFormat("[REGION SYNC SERVER] Unable to handle {0}: {1}", msg.ToString(), handlerMessage); - return false; - } - private void DoInitialSync() { m_scene.DeleteAllSceneObjects(); 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); } public string GetServerAddressAndPort() @@ -311,11 +490,20 @@ namespace OpenSim.Region.Examples.RegionSyncModule return m_addr.ToString() + ":" + m_port.ToString(); } - - #region Event Handlers - + #region MESSAGES SENT FROM CLIENT MANAGER TO SIM public void EventManager_OnNewClient(IClientAPI client) { + // 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)) + return; + } + // Otherwise, it's a real local client connecting so track it locally + lock(m_localAvatars) + m_localAvatars.Add(client.AgentId, client); + 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); data["agentID"] = OSD.FromUUID(client.AgentId); @@ -330,6 +518,16 @@ namespace OpenSim.Region.Examples.RegionSyncModule client.OnChatFromClientRaw += HandleChatFromClientRaw; } + void RemoveLocalClient(UUID clientID, Scene scene) + { + 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))); + } + /// /// This is the event handler for client movement. If a client is moving, this event is triggering. /// @@ -342,8 +540,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule { Send(new RegionSyncMessage(RegionSyncMessage.MsgType.ChatFromClient, chatData)); } - #endregion + /* // Should be part of the RegionSyncClient public string ReceiveMsg() diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs index ab51b9377a..0b97a4fade 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs @@ -101,6 +101,11 @@ namespace OpenSim.Region.Examples.RegionSyncModule #endregion #region IRegionSyncClientModule members + public void SendCoarseLocations() + { + m_client.SendCoarseLocations(); + } + public bool Active { get { return m_active; } @@ -126,6 +131,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule private ILog m_log; private Object m_client_lock = new Object(); private RegionSyncClient m_client = null; + + #endregion #region Event Handlers diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs index 78a80d1174..9367cccbed 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs @@ -21,6 +21,13 @@ namespace OpenSim.Region.Examples.RegionSyncModule public class RegionSyncClientView { #region RegionSyncClientView members + + object stats = new object(); + private long msgsIn; + private long msgsOut; + private long bytesIn; + private long bytesOut; + // The TcpClient this view uses to communicate with its RegionSyncClient private TcpClient m_tcpclient; // Set the addr and port for TcpListener @@ -32,15 +39,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule Dictionary m_syncedAvatars = new Dictionary(); // A queue for incoming and outgoing traffic - private Queue m_inQ = new Queue(); - private Queue m_outQ = new Queue(); + private OpenMetaverse.BlockingQueue inbox = new OpenMetaverse.BlockingQueue(); private ILog m_log; private Thread m_receive_loop; - - // The last time the entire region was sent to this RegionSyncClient - private long m_archive_time; + private Thread m_handler; // A string of the format [REGION SYNC CLIENT VIEW #X] for use in log headers private string LogHeader @@ -54,6 +58,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule get { return String.Format("RegionSyncClientView #{0}", m_connection_number); } } + public string GetStats() + { + lock(stats) + return String.Format("{0},{1},{2},{3}", msgsIn, msgsOut, bytesIn, bytesOut); + } + // Check if the client is connected public bool Connected { get { return m_tcpclient.Connected; } } @@ -96,12 +106,17 @@ namespace OpenSim.Region.Examples.RegionSyncModule while (true) { RegionSyncMessage msg = GetMessage(); + lock (stats) + { + msgsIn++; + bytesIn += msg.Length; + } HandleMessage(msg); } } catch (Exception e) { - m_log.WarnFormat("{0} RegionSyncClient has disconnected.", LogHeader); + m_log.WarnFormat("{0} RegionSyncClient has disconnected: {1}", LogHeader, e.Message); } } @@ -110,10 +125,30 @@ namespace OpenSim.Region.Examples.RegionSyncModule { // Get a RegionSyncMessager from the incoming stream RegionSyncMessage msg = new RegionSyncMessage(m_tcpclient.GetStream()); - m_log.WarnFormat("{0} Received {1}", LogHeader, msg.ToString()); + //m_log.WarnFormat("{0} Received {1}", LogHeader, msg.ToString()); return msg; } + 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 // *** Perhaps this should not be synchronous with the receive // We could handle messages from an incoming Queue @@ -122,6 +157,11 @@ namespace OpenSim.Region.Examples.RegionSyncModule //string handlerMessage = ""; switch (msg.Type) { + case RegionSyncMessage.MsgType.GetTerrain: + { + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.Terrain, m_scene.Heightmap.SaveToXmlString())); + return HandlerSuccess(msg, "Terrain sent"); + } case RegionSyncMessage.MsgType.GetObjects: { List entities = m_scene.GetEntities(); @@ -130,19 +170,28 @@ namespace OpenSim.Region.Examples.RegionSyncModule if (e is SceneObjectGroup) { string sogxml = SceneObjectSerializer.ToXml2Format((SceneObjectGroup)e); - Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AddObject, sogxml)); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.NewObject, sogxml)); } } return HandlerSuccess(msg, "Sent all scene objects"); } - case RegionSyncMessage.MsgType.GetTerrain: + case RegionSyncMessage.MsgType.GetAvatars: { - Send(new RegionSyncMessage(RegionSyncMessage.MsgType.Terrain, m_scene.Heightmap.SaveToXmlString())); - return HandlerSuccess(msg, "Terrain sent"); + m_scene.ForEachScenePresence(delegate(ScenePresence presence) + { + // Let the client managers know about this avatar + OSDMap data = new OSDMap(1); + data["agentID"] = OSD.FromUUID(presence.ControllingClient.AgentId); + data["first"] = OSD.FromString(presence.ControllingClient.FirstName); + data["last"] = OSD.FromString(presence.ControllingClient.LastName); + data["startPos"] = OSD.FromVector3(presence.ControllingClient.StartPos); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.NewAvatar, OSDParser.SerializeJsonString(data))); + }); + return HandlerSuccess(msg, "Sent all scene avatars"); } case RegionSyncMessage.MsgType.AgentAdd: { - OSDMap data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data)) as OSDMap; + OSDMap data = DeserializeMessage(msg); if (data != null) { UUID agentID = data["agentID"].AsUUID(); @@ -165,7 +214,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule return HandlerSuccess(msg, String.Format("Handled AddAgent for UUID {0}", agentID)); } } - return HandlerFailure(msg, "Could not parse AddAgent parameters"); + return HandlerFailure(msg, "Could not deserialize JSON data."); } case RegionSyncMessage.MsgType.AgentUpdate: @@ -204,6 +253,42 @@ namespace OpenSim.Region.Examples.RegionSyncModule else return HandlerFailure(msg, String.Format("Could not handle AgentUpdate UUID {0}", agentID)); } + case RegionSyncMessage.MsgType.AgentRemove: + { + OSDMap data = DeserializeMessage(msg); + if (data != null) + { + UUID agentID = data["agentID"].AsUUID(); + + if (agentID != null && agentID != UUID.Zero) + { + lock (m_syncedAvatars) + { + if (m_syncedAvatars.ContainsKey(agentID)) + { + m_syncedAvatars.Remove(agentID); + ScenePresence presence; + if (m_scene.TryGetScenePresence(agentID, out presence)) + { + string name = presence.Name; + m_scene.RemoveClient(agentID); + return HandlerSuccess(msg, String.Format("Agent \"{0}\" ({1}) was removed from scene.", name, agentID)); + } + else + { + return HandlerFailure(msg, String.Format("Agent {0} not found in the scene.", agentID)); + } + } + else + { + return HandlerFailure(msg, String.Format("Agent {0} not in the list of synced avatars.", agentID)); + } + } + } + } + return HandlerFailure(msg, "Could not deserialize JSON data."); + + } default: { m_log.WarnFormat("{0} Unable to handle unsupported message type", LogHeader); @@ -212,9 +297,15 @@ namespace OpenSim.Region.Examples.RegionSyncModule } } + private bool HandlerDebug(RegionSyncMessage msg, string handlerMessage) + { + m_log.WarnFormat("{0} DBG ({1}): {2}", LogHeader, msg.ToString(), handlerMessage); + return true; + } + 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; } @@ -236,6 +327,11 @@ namespace OpenSim.Region.Examples.RegionSyncModule { try { + lock (stats) + { + msgsOut++; + bytesOut += data.Length; + } m_tcpclient.GetStream().Write(data, 0, data.Length); } catch (IOException e) diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs index 4911171811..3a963c3a8b 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncMessage.cs @@ -19,6 +19,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule // CM -> SIM AgentAdd, AgentUpdate, + AgentRemove, GetTerrain, GetObjects, SubscribeObjects, @@ -27,12 +28,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule ChatFromClient, // SIM -> CM Terrain, - AddObject, - UpdateObject, - RemoveObject, - AddAvatarResponse, - UpdateAvatarTerse, - RemoveAvatar, + NewObject, // objects + UpdatedObject, // objects + RemovedObject, // objects + NewAvatar, // avatars + UpdatedAvatar, // avatars + RemovedAvatar, // avatars ChatFromSim, // BIDIR EchoRequest, diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs index 99c77f4892..8f84dda87a 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs @@ -19,6 +19,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Set the addr and port for TcpListener private IPAddress m_addr; private Int32 m_port; + + private int clientCounter; // The local scene. private Scene m_scene; @@ -36,27 +38,50 @@ namespace OpenSim.Region.Examples.RegionSyncModule private Thread m_listenerThread; // The list of clients and the threads handling IO for each client - // Lock should be used when client managers connect or disconnect - // while modifying the client list. - private Object clientLock = new Object(); - private List m_client_views = new List(); + // The list is read most of the time and only updated when a new client manager + // connects, so we just replace the list when it changes. Iterators on this + // list need to be able to handle if an element is shutting down. + private HashSet m_client_views = new HashSet(); // Check if any of the client views are in a connected state public bool Synced { get { - lock (clientLock) - { - foreach (RegionSyncClientView cv in m_client_views) - { - if (cv.Connected) - return true; - } - return false; - } + return (m_client_views.Count > 0); } } + + // Add the client view to the list and increment synced client counter + public void AddSyncedClient(RegionSyncClientView rscv) + { + HashSet newlist = new HashSet(m_client_views); + newlist.Add(rscv); + // Threads holding the previous version of the list can keep using it since + // they will not hold it for long and get a new copy next time they need to iterate + Interlocked.Exchange>(ref m_client_views, newlist); + } + + // Remove the client view from the list and decrement synced client counter + public void RemoveSyncedClient(RegionSyncClientView rscv) + { + HashSet newlist = new HashSet(m_client_views); + newlist.Remove(rscv); + // Threads holding the previous version of the list can keep using it since + // they will not hold it for long and get a new copy next time they need to iterate + Interlocked.Exchange>(ref m_client_views, newlist); + } + + public void ReportStats() + { + // 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 + foreach (RegionSyncClientView rscv in m_client_views) + { + m_log.ErrorFormat("{0}: {1}", rscv.Description, rscv.GetStats()); + } + } + #endregion // Constructor @@ -82,19 +107,18 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Stop the server and disconnect all RegionSyncClients public void Shutdown() { - lock (clientLock) + // Stop the listener and listening thread so no new clients are accepted + m_listener.Stop(); + m_listenerThread.Abort(); + m_listenerThread = null; + + // Stop all existing client views and clear client list + HashSet list = new HashSet(m_client_views); + foreach (RegionSyncClientView rscv in list) { - // Stop the listener and listening thread so no new clients are accepted - m_listener.Stop(); - m_listenerThread.Abort(); - m_listenerThread = null; - // Stop all existing client views and clear client list - foreach (RegionSyncClientView cv in m_client_views) - { - // Each client view will clean up after itself - cv.Shutdown(); - } - m_client_views.Clear(); + // Each client view will clean up after itself + rscv.Shutdown(); + RemoveSyncedClient(rscv); } } @@ -117,16 +141,13 @@ namespace OpenSim.Region.Examples.RegionSyncModule TcpClient tcpclient = m_listener.AcceptTcpClient(); IPAddress addr = ((IPEndPoint)tcpclient.Client.RemoteEndPoint).Address; int port = ((IPEndPoint)tcpclient.Client.RemoteEndPoint).Port; - lock (clientLock) - { - // Add the RegionSyncClientView to the list of clients - // *** Need to work on the timing order of starting the client view and adding to the server list - // so that messages coming from the scene do not get lost before the client view is added but - // not sent before it is ready to process them. - RegionSyncClientView rscv = new RegionSyncClientView(m_client_views.Count, m_scene, tcpclient); - m_log.WarnFormat("[REGION SYNC SERVER] New connection from {0}", rscv.Description); - m_client_views.Add(rscv); - } + // Add the RegionSyncClientView to the list of clients + // *** Need to work on the timing order of starting the client view and adding to the server list + // so that messages coming from the scene do not get lost before the client view is added but + // not sent before it is ready to process them. + RegionSyncClientView rscv = new RegionSyncClientView(++clientCounter, m_scene, tcpclient); + m_log.WarnFormat("[REGION SYNC SERVER] New connection from {0}", rscv.Description); + AddSyncedClient(rscv); } } catch (SocketException e) @@ -138,22 +159,30 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Broadcast a message to all connected RegionSyncClients public void Broadcast(RegionSyncMessage msg) { - List clients = new List(); - lock (clientLock) - { - foreach (RegionSyncClientView client in m_client_views) - { - if (client.Connected) - clients.Add(client); - } - } - - if(clients.Count > 0 ) + List closed = null; + //lock (m_client_views) { //m_log.WarnFormat("[REGION SYNC SERVER] Broadcasting {0} to all connected RegionSyncClients", msg.ToString()); - foreach( RegionSyncClientView client in clients) + foreach (RegionSyncClientView client in m_client_views) { - client.Send(msg); + // If connected, send the message. + if (client.Connected) + { + client.Send(msg); + } + // 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 33e78cf384..d761f677cd 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs @@ -24,7 +24,8 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + +using System; using System.Collections.Generic; using System.Reflection; using Nini.Config; @@ -32,6 +33,7 @@ using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Framework.Client; +using OpenSim.Region.CoreModules.Framework.InterfaceCommander; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Serialization; @@ -42,7 +44,7 @@ using System.Threading; namespace OpenSim.Region.Examples.RegionSyncModule { - public class RegionSyncServerModule : IRegionModule, IRegionSyncServerModule + public class RegionSyncServerModule : IRegionModule, IRegionSyncServerModule, ICommandableModule { #region IRegionModule Members public void Initialise(Scene scene, IConfigSource config) @@ -60,6 +62,11 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_serverport = syncConfig.GetInt("ServerPort", 13000); m_scene = scene; m_scene.RegisterModuleInterface(this); + + // Setup the command line interface + m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; + InstallInterfaces(); + m_log.Warn("[REGION SYNC SERVER MODULE] Initialised"); } @@ -67,20 +74,21 @@ namespace OpenSim.Region.Examples.RegionSyncModule { if (!m_active) return; - + //m_scene.EventManager.OnObjectBeingRemovedFromScene += new EventManager.ObjectBeingRemovedFromScene(EventManager_OnObjectBeingRemovedFromScene); - //m_scene.EventManager.OnNewClient +=new EventManager.OnNewClientDelegate(EventManager_OnNewClient); - //m_scene.EventManager.OnNewPresence +=new EventManager.OnNewPresenceDelegate(EventManager_OnNewPresence); + //m_scene.EventManager.OnAvatarEnteringNewParcel += new EventManager.AvatarEnteringNewParcel(EventManager_OnAvatarEnteringNewParcel); //m_scene.EventManager.OnClientMovement += new EventManager.ClientMovement(EventManager_OnClientMovement); //m_scene.EventManager.OnLandObjectAdded += new EventManager.LandObjectAdded(EventManager_OnLandObjectAdded); //m_scene.EventManager.OnLandObjectRemoved += new EventManager.LandObjectRemoved(EventManager_OnLandObjectRemoved); + m_scene.EventManager.OnNewClient += new EventManager.OnNewClientDelegate(EventManager_OnNewClient); + //m_scene.EventManager.OnNewPresence += new EventManager.OnNewPresenceDelegate(EventManager_OnNewPresence); m_scene.EventManager.OnRemovePresence += new EventManager.OnRemovePresenceDelegate(EventManager_OnRemovePresence); m_scene.SceneGraph.OnObjectCreate += new ObjectCreateDelegate(SceneGraph_OnObjectCreate); m_scene.SceneGraph.OnObjectDuplicate += new ObjectDuplicateDelegate(SceneGraph_OnObjectDuplicate); //m_scene.SceneGraph.OnObjectRemove += new ObjectDeleteDelegate(SceneGraph_OnObjectRemove); //m_scene.StatsReporter.OnSendStatsResult += new SimStatsReporter.SendStatResult(StatsReporter_OnSendStatsResult); - + m_log.Warn("[REGION SYNC SERVER MODULE] Starting RegionSyncServer"); // Start the server and listen for RegionSyncClients m_server = new RegionSyncServer(m_scene, m_serveraddr, m_serverport); @@ -103,7 +111,15 @@ namespace OpenSim.Region.Examples.RegionSyncModule { get { return false; } } -#endregion + #endregion + + #region ICommandableModule Members + private readonly Commander m_commander = new Commander("sync"); + public ICommander CommandInterface + { + get { return m_commander; } + } + #endregion #region IRegionSyncServerModule members // Lock is used to synchronize access to the update status and both update queues @@ -116,7 +132,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule { if (!Active || !Synced) return; - lock (m_primUpdates) + lock (m_updateLock) { m_primUpdates[part.ParentGroup.UUID] = part.ParentGroup; } @@ -127,7 +143,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule { if (!Active || !Synced) return; - lock (m_presenceUpdates) + lock (m_updateLock) { m_presenceUpdates[presence.UUID] = presence; } @@ -148,10 +164,14 @@ namespace OpenSim.Region.Examples.RegionSyncModule List primUpdates; List presenceUpdates; - primUpdates = new List(m_primUpdates.Values); - presenceUpdates = new List(m_presenceUpdates.Values); - m_primUpdates.Clear(); - m_presenceUpdates.Clear(); + + lock (m_updateLock) + { + primUpdates = new List(m_primUpdates.Values); + presenceUpdates = new List(m_presenceUpdates.Values); + m_primUpdates.Clear(); + m_presenceUpdates.Clear(); + } // This could be another thread for sending outgoing messages or just have the Queue functions // create and queue the messages directly into the outgoing server thread. @@ -164,20 +184,27 @@ namespace OpenSim.Region.Examples.RegionSyncModule if (!sog.IsDeleted) { string sogxml = SceneObjectSerializer.ToXml2Format(sog); - m_server.Broadcast(new RegionSyncMessage(RegionSyncMessage.MsgType.UpdateObject, sogxml)); + m_server.Broadcast(new RegionSyncMessage(RegionSyncMessage.MsgType.UpdatedObject, sogxml)); } } foreach (ScenePresence presence in presenceUpdates) { if (!presence.IsDeleted) { - OSDMap data = new OSDMap(4); + OSDMap data = new OSDMap(5); data["id"] = OSD.FromUUID(presence.UUID); // Do not include offset for appearance height. That will be handled by RegionSyncClient before sending to viewers - data["pos"] = OSD.FromVector3(presence.AbsolutePosition); - data["vel"] = OSD.FromVector3(presence.Velocity); + 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); - RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.UpdateAvatarTerse, OSDParser.SerializeJsonString(data)); + data["fly"] = OSD.FromBoolean(presence.Flying); + RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.UpdatedAvatar, OSDParser.SerializeJsonString(data)); m_server.Broadcast(rsm); } } @@ -193,7 +220,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule OSDMap data = new OSDMap(2); data["regionHandle"] = OSD.FromULong(regionHandle); data["localID"] = OSD.FromUInteger(localID); - RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.RemoveObject, OSDParser.SerializeJsonString(data)); + RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.RemovedObject, OSDParser.SerializeJsonString(data)); m_server.Broadcast(rsm); } @@ -255,7 +282,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule * */ } #endif - #endregion + #endregion #endregion #region RegionSyncServerModule members @@ -272,10 +299,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule #region Event Handlers private void SceneGraph_OnObjectCreate(EntityBase entity) { + if (!Synced) + return; if (entity is SceneObjectGroup) { string sogxml = SceneObjectSerializer.ToXml2Format((SceneObjectGroup)entity); - RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.AddObject, sogxml); + RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.NewObject, sogxml); m_server.Broadcast(rsm); } else @@ -286,10 +315,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule private void SceneGraph_OnObjectDuplicate(EntityBase original, EntityBase copy) { + if (!Synced) + return; if (original is SceneObjectGroup && copy is SceneObjectGroup) { string sogxml = SceneObjectSerializer.ToXml2Format((SceneObjectGroup)copy); - RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.AddObject, sogxml); + RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.NewObject, sogxml); m_server.Broadcast(rsm); } else @@ -300,10 +331,12 @@ namespace OpenSim.Region.Examples.RegionSyncModule private void SceneGraph_OnObjectRemove(EntityBase entity) { + if (!Synced) + return; if (entity is SceneObjectGroup) { // No reason to send the entire object, just send the UUID to be deleted - RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.RemoveObject, entity.UUID.ToString()); + RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.RemovedObject, entity.UUID.ToString()); m_server.Broadcast(rsm); } else @@ -315,6 +348,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule // A ficticious event public void Scene_AddNewPrim(SceneObjectGroup sog) { + if (!Synced) + return; } /* @@ -384,48 +419,48 @@ namespace OpenSim.Region.Examples.RegionSyncModule } void EventManager_OnClientMovement(ScenePresence client) - { - + { m_moveCounter++; if (m_moveCounter % 100 == 0) { - string msg = (string.Format("EventManager_OnClientMovement - Event has been triggered 100 times")); m_server.Broadcast(msg); m_log.Warn("REGION SYNC SERVER MODULE] " + msg); } } - 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()); DebugSceneStats(); } + */ + private void EventManager_OnNewPresence(ScenePresence presence) + { + if (!Synced) + return; + m_log.WarnFormat("[REGION SYNC SERVER MODULE] (OneNewPresence) \"{0}\"", presence.Name); + } private void EventManager_OnNewClient(IClientAPI client) { - ScenePresence presence = m_scene.GetScenePresence(client.AgentId); - if (presence != null) - { - m_log.WarnFormat("[REGION SYNC SERVER MODULE] (OnNewClient) \"{0}\"", presence.Name); - DebugSceneStats(); - } - else - { - m_log.WarnFormat("[REGION SYNC SERVER MODULE] (OnNewClient) A new client connected but module could not get ScenePresence"); - } + if (!Synced) + return; + 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); + data["first"] = OSD.FromString(client.FirstName); + data["last"] = OSD.FromString(client.LastName); + data["startPos"] = OSD.FromVector3(client.StartPos); + m_server.Broadcast(new RegionSyncMessage(RegionSyncMessage.MsgType.NewAvatar, OSDParser.SerializeJsonString(data))); } - private void EventManager_OnNewPresence(ScenePresence presence) - { - m_log.WarnFormat("[REGION SYNC SERVER MODULE] Avatar \"{0}\" (1) {2} has joined the scene", presence.Firstname + " " + presence.Lastname, presence.ControllingClient.AgentId.ToString(), presence.UUID.ToString()); - DebugSceneStats(); - } - * */ - private void EventManager_OnRemovePresence(UUID agentID) { + if (!Synced) + return; + /* ScenePresence avatar; if (m_scene.TryGetScenePresence(agentID, out avatar)) { @@ -435,39 +470,61 @@ namespace OpenSim.Region.Examples.RegionSyncModule { m_log.WarnFormat("[REGION SYNC SERVER MODULE] Avatar \"unknown\" has left the scene"); } - OSDArray data = new OSDArray(); - data.Add(OSD.FromULong(avatar.RegionHandle)); - data.Add(OSD.FromUInteger(avatar.LocalId)); - RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.RemoveAvatar, data.ToString()); - m_server.Broadcast(rsm); + * */ + OSDMap data = new OSDMap(); + data["agentID"] = OSD.FromUUID(agentID); + m_server.Broadcast(new RegionSyncMessage(RegionSyncMessage.MsgType.RemovedAvatar, OSDParser.SerializeJsonString(data))); } #endregion - - /* - private void DebugSceneStats() + #region Console Command Interface + private void InstallInterfaces() { - List avatars = m_scene.GetAvatars(); - List entities = m_scene.GetEntities(); - m_log.WarnFormat("There are {0} avatars and {1} entities in the scene", avatars.Count, entities.Count); - } + Command cmdSyncStats = new Command("stats", CommandIntentions.COMMAND_HAZARDOUS, SyncStats, "Reports stats for the RegionSyncServer."); + m_commander.RegisterCommand("stats", cmdSyncStats); - public IClientAPI ClientAggregator - { - get {return m_clientAggregator;} - } - - private void AddAvatars() - { - for (int i = 0; i < 1; i++) + lock (m_scene) { - MyNpcCharacter m_character = new MyNpcCharacter(m_scene, this); - m_scene.AddNewClient(m_character); - - m_scene.AgentCrossing(m_character.AgentId, m_character.StartPos, false); + // Add this to our scene so scripts can call these functions + m_scene.RegisterModuleCommander(m_commander); } } - */ + + + /// + /// Processes commandline input. Do not call directly. + /// + /// Commandline arguments + private void EventManager_OnPluginConsole(string[] args) + { + if (args[0] == "sync") + { + if (args.Length == 1) + { + m_commander.ProcessConsoleCommand("help", new string[0]); + return; + } + + string[] tmpArgs = new string[args.Length - 2]; + int i; + for (i = 2; i < args.Length; i++) + tmpArgs[i - 2] = args[i]; + + m_commander.ProcessConsoleCommand(args[1], tmpArgs); + } + } + + private void SyncStats(Object[] args) + { + if (Synced) + m_server.ReportStats(); + else if (m_server != null) + m_log.Error("No RegionSyncClients connected"); + else + m_log.Error("The RegionSyncServer is not running!"); + } + #endregion } + } diff --git a/OpenSim/Region/Framework/Interfaces/IRegionSyncClientModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionSyncClientModule.cs index ba3a36abe1..962e83cb47 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionSyncClientModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionSyncClientModule.cs @@ -35,5 +35,6 @@ namespace OpenSim.Region.Framework.Interfaces { bool Active { get; } bool Synced { get; } + void SendCoarseLocations(); } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index cab9efd4c0..ef42e1be9c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1429,27 +1429,56 @@ namespace OpenSim.Region.Framework.Scenes // Run through all ScenePresences looking for updates // Presence updates and queued object updates for each presence are sent to clients - if (m_frame % m_update_presences == 0) - m_sceneGraph.UpdatePresences(); + // If it's a synced client, just send prim updates + // This will get fixed later to only send to locally logged in presences rather than all presences + // but requires pulling apart the concept of a client from the concept of a presence/avatar + if (IsSyncedClient()) + { + ForEachScenePresence(delegate(ScenePresence sp) { sp.SendPrimUpdates(); }); + RegionSyncClientModule.SendCoarseLocations(); + } + else + { + if (m_frame % m_update_presences == 0) + m_sceneGraph.UpdatePresences(); + } + // REGION SYNC + // If this is a synced server, send updates to client managers at this time + // This batches updates, but the client manager will forward on to clients without + // additional delay if (IsSyncedServer()) + { m_regionSyncServerModule.SendUpdates(); + } int tmpPhysicsMS2 = Util.EnvironmentTickCount(); - if ((m_frame % m_update_physics == 0) && m_physics_enabled) - m_sceneGraph.UpdatePreparePhysics(); + // Do not simulate physics locally if this is a synced client + if (!IsSyncedClient()) + { + if ((m_frame % m_update_physics == 0) && m_physics_enabled) + m_sceneGraph.UpdatePreparePhysics(); + } physicsMS2 = Util.EnvironmentTickCountSubtract(tmpPhysicsMS2); - if (m_frame % m_update_entitymovement == 0) - m_sceneGraph.UpdateScenePresenceMovement(); + // Do not simulate physics locally if this is a synced client + if (!IsSyncedClient()) + { + if (m_frame % m_update_entitymovement == 0) + m_sceneGraph.UpdateScenePresenceMovement(); + } int tmpPhysicsMS = Util.EnvironmentTickCount(); - if (m_frame % m_update_physics == 0) + // Do not simulate physics locally if this is a synced client + if (!IsSyncedClient()) { - if (m_physics_enabled) - physicsFPS = m_sceneGraph.UpdatePhysics(Math.Max(SinceLastFrame.TotalSeconds, m_timespan)); - if (SynchronizeScene != null) - SynchronizeScene(this); + if (m_frame % m_update_physics == 0) + { + if (m_physics_enabled) + physicsFPS = m_sceneGraph.UpdatePhysics(Math.Max(SinceLastFrame.TotalSeconds, m_timespan)); + if (SynchronizeScene != null) + SynchronizeScene(this); + } } physicsMS = Util.EnvironmentTickCountSubtract(tmpPhysicsMS); @@ -1562,7 +1591,46 @@ namespace OpenSim.Region.Framework.Scenes } } - + public void GetCoarseLocations(out List ids, out List locations) + { + List resultIds = new List(); + List resultLocations = new List(); + + ForEachScenePresence(delegate(ScenePresence sp) + { + if (sp.IsChildAgent) + return; + resultIds.Add(sp.UUID); + resultLocations.Add(sp.AbsolutePosition); + }); + ids = resultIds; + locations = resultLocations; + + /* + if (sp.ParentID != 0) + { + // sitting avatar + SceneObjectPart sop = GetSceneObjectPart(sp.ParentID); + if (sop != null) + { + locations.Add(sop.AbsolutePosition + sp.m_pos); + ids.Add(sp.UUID); + } + else + { + // we can't find the parent.. ! arg! + locations.Add(sp.m_pos); + ids.Add(sp.UUID); + } + } + else + { + locations.Add(sp.m_pos); + ids.Add(sp.UUID); + } + }); + */ + } public void AddGroupTarget(SceneObjectGroup grp) { @@ -3233,9 +3301,9 @@ namespace OpenSim.Region.Framework.Scenes m_eventManager.TriggerOnRemovePresence(agentID); - if(IsSyncedServer()) - RegionSyncServerModule.DeleteObject(avatar.RegionHandle, avatar.LocalId); - else + // 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()) { ForEachClient( delegate(IClientAPI client) @@ -3325,11 +3393,11 @@ namespace OpenSim.Region.Framework.Scenes } } + ForEachClient(delegate(IClientAPI client) { client.SendKillObject(m_regionHandle, localID); }); + // REGION SYNC if( IsSyncedServer() ) RegionSyncServerModule.DeleteObject(m_regionHandle, localID); - else - ForEachClient(delegate(IClientAPI client) { client.SendKillObject(m_regionHandle, localID); }); } #endregion @@ -4365,11 +4433,10 @@ namespace OpenSim.Region.Framework.Scenes /// Performs action on all scene presences. /// /// - static int s_ForEachPresenceCounter = 0; public void ForEachScenePresence(Action action) { - if (IsSyncedServer()) - return; + //if (IsSyncedServer()) + // return; // We don't want to try to send messages if there are no avatars. if (m_sceneGraph != null) { @@ -4450,7 +4517,7 @@ namespace OpenSim.Region.Framework.Scenes public void ForEachClient(Action action) { // REGION SYNC - if (IsSyncedServer()) + if (false)//IsSyncedServer()) return; m_clientManager.ForEachSync(action); } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 9f4f7851dc..2b8a1daea3 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1272,7 +1272,7 @@ namespace OpenSim.Region.Framework.Scenes if (Scene.IsSyncedServer()) { Scene.RegionSyncServerModule.DeleteObject(part.RegionHandle, part.LocalId); - return; + //return; } Scene.ForEachScenePresence(delegate(ScenePresence avatar) { diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 987e5e8652..17b6f56826 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1259,13 +1259,13 @@ namespace OpenSim.Region.Framework.Scenes /// public void AddFullUpdateToAllAvatars() { - // REGION SYNC - if (m_parentGroup.Scene.IsSyncedServer()) - m_parentGroup.Scene.RegionSyncServerModule.QueuePartForUpdate(this); m_parentGroup.Scene.ForEachScenePresence(delegate(ScenePresence avatar) { avatar.SceneViewer.QueuePartForUpdate(this); }); + // REGION SYNC + if (m_parentGroup.Scene.IsSyncedServer()) + m_parentGroup.Scene.RegionSyncServerModule.QueuePartForUpdate(this); } public void AddFullUpdateToAvatar(ScenePresence presence) @@ -1286,12 +1286,13 @@ namespace OpenSim.Region.Framework.Scenes /// Terse updates public void AddTerseUpdateToAllAvatars() { - if (m_parentGroup.Scene.IsSyncedServer()) - m_parentGroup.Scene.RegionSyncServerModule.QueuePartForUpdate(this); m_parentGroup.Scene.ForEachScenePresence(delegate(ScenePresence avatar) { avatar.SceneViewer.QueuePartForUpdate(this); }); + // REGION SYNC + if (m_parentGroup.Scene.IsSyncedServer()) + m_parentGroup.Scene.RegionSyncServerModule.QueuePartForUpdate(this); } public void AddTerseUpdateToAvatar(ScenePresence presence) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index bb948c9970..8279c803ae 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -383,6 +383,18 @@ namespace OpenSim.Region.Framework.Scenes set { m_allowMovement = value; } } + // Added for region sync module. + // Should not rely on a physics actor like so many of these params seem to + public bool Flying + { + get + { + if (PhysicsActor != null) + return PhysicsActor.Flying; + return false; + } + } + public bool SetAlwaysRun { get @@ -688,12 +700,9 @@ namespace OpenSim.Region.Framework.Scenes AbsolutePosition = posLastSignificantMove = m_CameraCenter = m_lastCameraCenter = m_controllingClient.StartPos; - if (!m_scene.IsSyncedServer()) - { - m_reprioritization_timer = new Timer(world.ReprioritizationInterval); - m_reprioritization_timer.Elapsed += new ElapsedEventHandler(Reprioritize); - m_reprioritization_timer.AutoReset = false; - } + m_reprioritization_timer = new Timer(world.ReprioritizationInterval); + m_reprioritization_timer.Elapsed += new ElapsedEventHandler(Reprioritize); + m_reprioritization_timer.AutoReset = false; AdjustKnownSeeds(); @@ -1196,6 +1205,39 @@ namespace OpenSim.Region.Framework.Scenes } } + // Move the avatar to a specific position, velocity and rotation and mark it as changed + // Mostly copied from HandleAgentUpdate + public void MoveTo(Vector3 pos, Vector3 vel, Quaternion rot) + { + ++m_movementUpdateCount; + if (m_movementUpdateCount < 1) + m_movementUpdateCount = 1; + #region Sanity Checking + + // This is irritating. Really. + if (!AbsolutePosition.IsFinite()) + { + RemoveFromPhysicalScene(); + m_log.Error("[AVATAR]: NonFinite Avatar position detected... Reset Position. Mantis this please. Error #9999902"); + + m_pos = m_LastFinitePos; + if (!m_pos.IsFinite()) + { + m_pos.X = 127f; + m_pos.Y = 127f; + m_pos.Z = 127f; + m_log.Error("[AVATAR]: NonFinite Avatar position detected... Reset Position. Mantis this please. Error #9999903"); + } + + AddToPhysicalScene(false); + } + else + { + m_LastFinitePos = m_pos; + } + #endregion Sanity Checking + } + /// /// This is the event handler for client movement. If a client is moving, this event is triggering. /// @@ -2324,6 +2366,10 @@ namespace OpenSim.Region.Framework.Scenes { SendTerseUpdateToAllClients(); + // REGION SYNC + if(m_scene.IsSyncedServer()) + m_scene.RegionSyncServerModule.QueuePresenceForTerseUpdate(this); + // Update the "last" values m_lastPosition = m_pos; m_lastRotation = m_bodyRot; @@ -2382,6 +2428,19 @@ 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) + { + m_perfMonMS = Util.EnvironmentTickCount(); + foreach( IClientAPI client in clients) + { + SendTerseUpdateToClient(client); + } + m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); + } + public void SendCoarseLocations() { SendCourseLocationsMethod d = m_sendCourseLocationsMethod; @@ -2400,39 +2459,12 @@ namespace OpenSim.Region.Framework.Scenes public void SendCoarseLocationsDefault(UUID sceneId, ScenePresence p) { m_perfMonMS = Util.EnvironmentTickCount(); - - List CoarseLocations = new List(); List AvatarUUIDs = new List(); - m_scene.ForEachScenePresence(delegate(ScenePresence sp) - { - if (sp.IsChildAgent) - return; - - if (sp.ParentID != 0) - { - // sitting avatar - SceneObjectPart sop = m_scene.GetSceneObjectPart(sp.ParentID); - if (sop != null) - { - CoarseLocations.Add(sop.AbsolutePosition + sp.m_pos); - AvatarUUIDs.Add(sp.UUID); - } - else - { - // we can't find the parent.. ! arg! - CoarseLocations.Add(sp.m_pos); - AvatarUUIDs.Add(sp.UUID); - } - } - else - { - CoarseLocations.Add(sp.m_pos); - AvatarUUIDs.Add(sp.UUID); - } - }); - + 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); m_controllingClient.SendCoarseLocationUpdate(AvatarUUIDs, CoarseLocations); - m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); } diff --git a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs index b6e1cb465c..639364c79f 100644 --- a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs +++ b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs @@ -24,10 +24,12 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + using System; using System.Collections.Generic; +using System.Reflection; using Nini.Config; +using log4net; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Physics.Manager; @@ -36,6 +38,8 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin { public class BasicScene : PhysicsScene { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private List _actors = new List(); private float[] _heightMap; @@ -129,7 +133,16 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin actorPosition.X = ((int)Constants.RegionSize - 0.1f); } - float height = _heightMap[(int)actor.Position.Y * Constants.RegionSize + (int)actor.Position.X] + actor.Size.Z; + float height = 25.0F; + try + { + height = _heightMap[(int)actor.Position.Y * Constants.RegionSize + (int)actor.Position.X] + actor.Size.Z; + } + catch (OverflowException) + { + m_log.WarnFormat("[BASICPHYSICS]: Actor out of range: {0}", actor.SOPName, actor.Position.ToString()); + } + if (actor.Flying) { if (actor.Position.Z + (actor.Velocity.Z*timeStep) < diff --git a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs index a2229e8aa5..fc3778cb78 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs @@ -906,10 +906,9 @@ namespace OpenSim.Region.Physics.OdePlugin d.Vector3 localpos = d.BodyGetPosition(Body); Vector3 localPos = new Vector3(localpos.X, localpos.Y, localpos.Z); - if (!localPos.IsFinite()) + if (!localPos.IsFinite() || localpos.X < 0.0 || localpos.X > 256.0 || localpos.Y < 0.0 || localpos.Y > 256.0 || localpos.Z < 0.0) { - - m_log.Warn("[PHYSICS]: Avatar Position is non-finite!"); + m_log.Warn("[PHYSICS]: Avatar Position is non-finite or out of bounds!"); defects.Add(this); // _parent_scene.RemoveCharacter(this);