From b4526a5a6d170e04655990c8edb8e355156a2061 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sun, 18 Oct 2009 02:00:42 -0700 Subject: * Big performance increase in loading prims from the region database with MySQL * Handle the AgentFOV packet * Bypass queuing and throttles for ping checks to make ping times more closely match network latency * Only track reliable bytes in LLUDPCLient.BytesSinceLastACK --- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 271 +++++++++++---------- OpenSim/Framework/IClientAPI.cs | 2 + .../Region/ClientStack/LindenUDP/LLClientView.cs | 24 +- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 51 ++-- 4 files changed, 190 insertions(+), 158 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index 6bc8bec..801d6b9 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -403,26 +403,23 @@ namespace OpenSim.Data.MySQL } } - public List LoadObjects(UUID regionUUID) + public List LoadObjects(UUID regionID) { - UUID lastGroupID = UUID.Zero; + const int ROWS_PER_QUERY = 5000; + + Dictionary prims = new Dictionary(ROWS_PER_QUERY); Dictionary objects = new Dictionary(); - Dictionary prims = new Dictionary(); - SceneObjectGroup grp = null; int count = 0; + #region Prim Loading + lock (m_Connection) { using (MySqlCommand cmd = m_Connection.CreateCommand()) { - cmd.CommandText = "select *, " + - "case when prims.UUID = SceneGroupID " + - "then 0 else 1 end as sort from prims " + - "left join primshapes on prims.UUID = primshapes.UUID " + - "where RegionUUID = ?RegionUUID " + - "order by SceneGroupID asc, sort asc, LinkNumber asc"; - - cmd.Parameters.AddWithValue("RegionUUID", regionUUID.ToString()); + cmd.CommandText = + "SELECT * FROM prims LEFT JOIN primshapes ON prims.UUID = primshapes.UUID WHERE RegionUUID = ?RegionUUID"; + cmd.Parameters.AddWithValue("RegionUUID", regionID.ToString()); using (IDataReader reader = ExecuteReader(cmd)) { @@ -434,56 +431,61 @@ namespace OpenSim.Data.MySQL else prim.Shape = BuildShape(reader); - prims[prim.UUID] = prim; - - UUID groupID = new UUID(reader["SceneGroupID"].ToString()); + UUID parentID = new UUID(reader["SceneGroupID"].ToString()); + if (parentID != prim.UUID) + prim.ParentUUID = parentID; - if (groupID != lastGroupID) // New SOG - { - if (grp != null) - objects[grp.UUID] = grp; + prims[prim.UUID] = prim; - lastGroupID = groupID; + ++count; + if (count % ROWS_PER_QUERY == 0) + m_log.Debug("[REGION DB]: Loaded " + count + " prims..."); + } + } + } + } - // There sometimes exist OpenSim bugs that 'orphan groups' so that none of the prims are - // recorded as the root prim (for which the UUID must equal the persisted group UUID). In - // this case, force the UUID to be the same as the group UUID so that at least these can be - // deleted (we need to change the UUID so that any other prims in the linkset can also be - // deleted). - if (prim.UUID != groupID && groupID != UUID.Zero) - { - m_log.WarnFormat( - "[REGION DB]: Found root prim {0} {1} at {2} where group was actually {3}. Forcing UUID to group UUID", - prim.Name, prim.UUID, prim.GroupPosition, groupID); + #endregion Prim Loading - prim.UUID = groupID; - } + #region SceneObjectGroup Creation - grp = new SceneObjectGroup(prim); - } - else - { - // Black magic to preserve link numbers - // - int link = prim.LinkNum; + // Create all of the SOGs from the root prims first + foreach (SceneObjectPart prim in prims.Values) + { + if (prim.ParentUUID == UUID.Zero) + objects[prim.UUID] = new SceneObjectGroup(prim); + } - grp.AddPart(prim); + // Add all of the children objects to the SOGs + foreach (SceneObjectPart prim in prims.Values) + { + SceneObjectGroup sog; + if (prim.UUID != prim.ParentUUID) + { + if (objects.TryGetValue(prim.ParentUUID, out sog)) + { + int originalLinkNum = prim.LinkNum; - if (link != 0) - prim.LinkNum = link; - } + sog.AddPart(prim); - ++count; - if (count % 5000 == 0) - m_log.Debug("[REGION DB]: Loaded " + count + " prims..."); - } + // SceneObjectGroup.AddPart() tries to be smart and automatically set the LinkNum. + // We override that here + if (originalLinkNum != 0) + prim.LinkNum = originalLinkNum; + } + else + { + m_log.Warn("[REGION DB]: Database contains an orphan child prim " + prim.UUID + " pointing to missing parent " + prim.ParentUUID); } - - if (grp != null) - objects[grp.UUID] = grp; } } + #endregion SceneObjectGroup Creation + + m_log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); + + #region Prim Inventory Loading + // Instead of attempting to LoadItems on every prim, // most of which probably have no items... get a // list from DB of all prims which have items and @@ -493,7 +495,7 @@ namespace OpenSim.Data.MySQL { using (MySqlCommand itemCmd = m_Connection.CreateCommand()) { - itemCmd.CommandText = "select distinct primID from primitems"; + itemCmd.CommandText = "SELECT DISTINCT primID FROM primitems"; using (IDataReader itemReader = ExecuteReader(itemCmd)) { while (itemReader.Read()) @@ -502,9 +504,7 @@ namespace OpenSim.Data.MySQL { UUID primID = new UUID(itemReader["primID"].ToString()); if (prims.ContainsKey(primID)) - { primsWithInventory.Add(prims[primID]); - } } } } @@ -512,9 +512,14 @@ namespace OpenSim.Data.MySQL } foreach (SceneObjectPart prim in primsWithInventory) + { LoadItems(prim); + } + + #endregion Prim Inventory Loading + + m_log.DebugFormat("[REGION DB]: Loaded inventory from {0} objects", primsWithInventory.Count); - m_log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); return new List(objects.Values); } @@ -811,137 +816,137 @@ namespace OpenSim.Data.MySQL private SceneObjectPart BuildPrim(IDataReader row) { SceneObjectPart prim = new SceneObjectPart(); - prim.UUID = new UUID((String) row["UUID"]); + prim.UUID = new UUID((string)row["UUID"]); // explicit conversion of integers is required, which sort // of sucks. No idea if there is a shortcut here or not. - prim.CreationDate = Convert.ToInt32(row["CreationDate"]); + prim.CreationDate = (int)row["CreationDate"]; if (row["Name"] != DBNull.Value) - prim.Name = (String)row["Name"]; + prim.Name = (string)row["Name"]; else - prim.Name = string.Empty; - // various text fields - prim.Text = (String) row["Text"]; - prim.Color = Color.FromArgb(Convert.ToInt32(row["ColorA"]), - Convert.ToInt32(row["ColorR"]), - Convert.ToInt32(row["ColorG"]), - Convert.ToInt32(row["ColorB"])); - prim.Description = (String) row["Description"]; - prim.SitName = (String) row["SitName"]; - prim.TouchName = (String) row["TouchName"]; - // permissions - prim.ObjectFlags = Convert.ToUInt32(row["ObjectFlags"]); - prim.CreatorID = new UUID((String) row["CreatorID"]); - prim.OwnerID = new UUID((String) row["OwnerID"]); - prim.GroupID = new UUID((String) row["GroupID"]); - prim.LastOwnerID = new UUID((String) row["LastOwnerID"]); - prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]); - prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]); - prim.GroupMask = Convert.ToUInt32(row["GroupMask"]); - prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]); - prim.BaseMask = Convert.ToUInt32(row["BaseMask"]); - // vectors + prim.Name = String.Empty; + // Various text fields + prim.Text = (string)row["Text"]; + prim.Color = Color.FromArgb((int)row["ColorA"], + (int)row["ColorR"], + (int)row["ColorG"], + (int)row["ColorB"]); + prim.Description = (string)row["Description"]; + prim.SitName = (string)row["SitName"]; + prim.TouchName = (string)row["TouchName"]; + // Permissions + prim.ObjectFlags = (uint)(int)row["ObjectFlags"]; + prim.CreatorID = new UUID((string)row["CreatorID"]); + prim.OwnerID = new UUID((string)row["OwnerID"]); + prim.GroupID = new UUID((string)row["GroupID"]); + prim.LastOwnerID = new UUID((string)row["LastOwnerID"]); + prim.OwnerMask = (uint)(int)row["OwnerMask"]; + prim.NextOwnerMask = (uint)(int)row["NextOwnerMask"]; + prim.GroupMask = (uint)(int)row["GroupMask"]; + prim.EveryoneMask = (uint)(int)row["EveryoneMask"]; + prim.BaseMask = (uint)(int)row["BaseMask"]; + // Vectors prim.OffsetPosition = new Vector3( - Convert.ToSingle(row["PositionX"]), - Convert.ToSingle(row["PositionY"]), - Convert.ToSingle(row["PositionZ"]) + (float)(double)row["PositionX"], + (float)(double)row["PositionY"], + (float)(double)row["PositionZ"] ); prim.GroupPosition = new Vector3( - Convert.ToSingle(row["GroupPositionX"]), - Convert.ToSingle(row["GroupPositionY"]), - Convert.ToSingle(row["GroupPositionZ"]) + (float)(double)row["GroupPositionX"], + (float)(double)row["GroupPositionY"], + (float)(double)row["GroupPositionZ"] ); prim.Velocity = new Vector3( - Convert.ToSingle(row["VelocityX"]), - Convert.ToSingle(row["VelocityY"]), - Convert.ToSingle(row["VelocityZ"]) + (float)(double)row["VelocityX"], + (float)(double)row["VelocityY"], + (float)(double)row["VelocityZ"] ); prim.AngularVelocity = new Vector3( - Convert.ToSingle(row["AngularVelocityX"]), - Convert.ToSingle(row["AngularVelocityY"]), - Convert.ToSingle(row["AngularVelocityZ"]) + (float)(double)row["AngularVelocityX"], + (float)(double)row["AngularVelocityY"], + (float)(double)row["AngularVelocityZ"] ); prim.Acceleration = new Vector3( - Convert.ToSingle(row["AccelerationX"]), - Convert.ToSingle(row["AccelerationY"]), - Convert.ToSingle(row["AccelerationZ"]) + (float)(double)row["AccelerationX"], + (float)(double)row["AccelerationY"], + (float)(double)row["AccelerationZ"] ); // quaternions prim.RotationOffset = new Quaternion( - Convert.ToSingle(row["RotationX"]), - Convert.ToSingle(row["RotationY"]), - Convert.ToSingle(row["RotationZ"]), - Convert.ToSingle(row["RotationW"]) + (float)(double)row["RotationX"], + (float)(double)row["RotationY"], + (float)(double)row["RotationZ"], + (float)(double)row["RotationW"] ); prim.SitTargetPositionLL = new Vector3( - Convert.ToSingle(row["SitTargetOffsetX"]), - Convert.ToSingle(row["SitTargetOffsetY"]), - Convert.ToSingle(row["SitTargetOffsetZ"]) + (float)(double)row["SitTargetOffsetX"], + (float)(double)row["SitTargetOffsetY"], + (float)(double)row["SitTargetOffsetZ"] ); prim.SitTargetOrientationLL = new Quaternion( - Convert.ToSingle(row["SitTargetOrientX"]), - Convert.ToSingle(row["SitTargetOrientY"]), - Convert.ToSingle(row["SitTargetOrientZ"]), - Convert.ToSingle(row["SitTargetOrientW"]) + (float)(double)row["SitTargetOrientX"], + (float)(double)row["SitTargetOrientY"], + (float)(double)row["SitTargetOrientZ"], + (float)(double)row["SitTargetOrientW"] ); - prim.PayPrice[0] = Convert.ToInt32(row["PayPrice"]); - prim.PayPrice[1] = Convert.ToInt32(row["PayButton1"]); - prim.PayPrice[2] = Convert.ToInt32(row["PayButton2"]); - prim.PayPrice[3] = Convert.ToInt32(row["PayButton3"]); - prim.PayPrice[4] = Convert.ToInt32(row["PayButton4"]); + prim.PayPrice[0] = (int)row["PayPrice"]; + prim.PayPrice[1] = (int)row["PayButton1"]; + prim.PayPrice[2] = (int)row["PayButton2"]; + prim.PayPrice[3] = (int)row["PayButton3"]; + prim.PayPrice[4] = (int)row["PayButton4"]; prim.Sound = new UUID(row["LoopedSound"].ToString()); - prim.SoundGain = Convert.ToSingle(row["LoopedSoundGain"]); + prim.SoundGain = (float)(double)row["LoopedSoundGain"]; prim.SoundFlags = 1; // If it's persisted at all, it's looped if (!(row["TextureAnimation"] is DBNull)) - prim.TextureAnimation = (Byte[])row["TextureAnimation"]; + prim.TextureAnimation = (byte[])row["TextureAnimation"]; if (!(row["ParticleSystem"] is DBNull)) - prim.ParticleSystem = (Byte[])row["ParticleSystem"]; + prim.ParticleSystem = (byte[])row["ParticleSystem"]; prim.RotationalVelocity = new Vector3( - Convert.ToSingle(row["OmegaX"]), - Convert.ToSingle(row["OmegaY"]), - Convert.ToSingle(row["OmegaZ"]) + (float)(double)row["OmegaX"], + (float)(double)row["OmegaY"], + (float)(double)row["OmegaZ"] ); prim.SetCameraEyeOffset(new Vector3( - Convert.ToSingle(row["CameraEyeOffsetX"]), - Convert.ToSingle(row["CameraEyeOffsetY"]), - Convert.ToSingle(row["CameraEyeOffsetZ"]) + (float)(double)row["CameraEyeOffsetX"], + (float)(double)row["CameraEyeOffsetY"], + (float)(double)row["CameraEyeOffsetZ"] )); prim.SetCameraAtOffset(new Vector3( - Convert.ToSingle(row["CameraAtOffsetX"]), - Convert.ToSingle(row["CameraAtOffsetY"]), - Convert.ToSingle(row["CameraAtOffsetZ"]) + (float)(double)row["CameraAtOffsetX"], + (float)(double)row["CameraAtOffsetY"], + (float)(double)row["CameraAtOffsetZ"] )); - if (Convert.ToInt16(row["ForceMouselook"]) != 0) + if ((sbyte)row["ForceMouselook"] != 0) prim.SetForceMouselook(true); - prim.ScriptAccessPin = Convert.ToInt32(row["ScriptAccessPin"]); + prim.ScriptAccessPin = (int)row["ScriptAccessPin"]; - if (Convert.ToInt16(row["AllowedDrop"]) != 0) + if ((sbyte)row["AllowedDrop"] != 0) prim.AllowedDrop = true; - if (Convert.ToInt16(row["DieAtEdge"]) != 0) + if ((sbyte)row["DieAtEdge"] != 0) prim.DIE_AT_EDGE = true; - prim.SalePrice = Convert.ToInt32(row["SalePrice"]); - prim.ObjectSaleType = unchecked((byte)Convert.ToSByte(row["SaleType"])); + prim.SalePrice = (int)row["SalePrice"]; + prim.ObjectSaleType = unchecked((byte)(sbyte)row["SaleType"]); - prim.Material = unchecked((byte)Convert.ToSByte(row["Material"])); + prim.Material = unchecked((byte)(sbyte)row["Material"]); if (!(row["ClickAction"] is DBNull)) - prim.ClickAction = unchecked((byte)Convert.ToSByte(row["ClickAction"])); + prim.ClickAction = unchecked((byte)(sbyte)row["ClickAction"]); prim.CollisionSound = new UUID(row["CollisionSound"].ToString()); - prim.CollisionSoundVolume = Convert.ToSingle(row["CollisionSoundVolume"]); + prim.CollisionSoundVolume = (float)(double)row["CollisionSoundVolume"]; - if (Convert.ToInt16(row["PassTouches"]) != 0) + if ((sbyte)row["PassTouches"] != 0) prim.PassTouches = true; - prim.LinkNum = Convert.ToInt32(row["LinkNumber"]); + prim.LinkNum = (int)row["LinkNumber"]; return prim; } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 03e7a25..1e03d4c 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -448,6 +448,8 @@ namespace OpenSim.Framework public delegate void AvatarInterestUpdate(IClientAPI client, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages); public delegate void PlacesQuery(UUID QueryID, UUID TransactionID, string QueryText, uint QueryFlags, byte Category, string SimName, IClientAPI client); + public delegate void AgentFOV(IClientAPI client, float verticalAngle); + public delegate double UpdatePriorityHandler(UpdatePriorityData data); #endregion diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 2773a5e..b93e905 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -295,6 +295,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public event MuteListRequest OnMuteListRequest; public event AvatarInterestUpdate OnAvatarInterestUpdate; public event PlacesQuery OnPlacesQuery; + public event AgentFOV OnAgentFOV; #endregion Events @@ -346,6 +347,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected ulong m_activeGroupPowers; protected Dictionary m_groupPowers = new Dictionary(); protected int m_terrainCheckerCount; + protected uint m_agentFOVCounter; // These numbers are guesses at a decent tradeoff between responsiveness // of the interest list and throughput. Lower is more responsive, higher @@ -8871,19 +8873,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion + case PacketType.AgentFOV: + AgentFOVPacket fovPacket = (AgentFOVPacket)Pack; - #region unimplemented handlers - - case PacketType.StartPingCheck: - StartPingCheckPacket pingStart = (StartPingCheckPacket)Pack; - CompletePingCheckPacket pingComplete = new CompletePingCheckPacket(); - pingComplete.PingID.PingID = pingStart.PingID.PingID; - m_udpServer.SendPacket(m_udpClient, pingComplete, ThrottleOutPacketType.Unknown, false); + if (fovPacket.FOVBlock.GenCounter > m_agentFOVCounter) + { + m_agentFOVCounter = fovPacket.FOVBlock.GenCounter; + AgentFOV handlerAgentFOV = OnAgentFOV; + if (handlerAgentFOV != null) + { + handlerAgentFOV(this, fovPacket.FOVBlock.VerticalAngle); + } + } break; - case PacketType.CompletePingCheck: - // TODO: Do stats tracking or something with these? - break; + #region unimplemented handlers case PacketType.ViewerStats: // TODO: handle this packet diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 7a403aa..4f3478b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -572,6 +572,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < ackPacket.Packets.Length; i++) AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent); } + + // We don't need to do anything else with PacketAck packets + return; } #endregion ACK Receiving @@ -579,20 +582,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region ACK Sending if (packet.Header.Reliable) + { udpClient.PendingAcks.Enqueue(packet.Header.Sequence); - // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, - // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove - // 2*MTU bytes from the value and send ACKs, and finally add the local value back to - // client.BytesSinceLastACK. Lockless thread safety - int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); - bytesSinceLastACK += buffer.DataLength; - if (bytesSinceLastACK > LLUDPServer.MTU * 2) - { - bytesSinceLastACK -= LLUDPServer.MTU * 2; - SendAcks(udpClient); + // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, + // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove + // 2*MTU bytes from the value and send ACKs, and finally add the local value back to + // client.BytesSinceLastACK. Lockless thread safety + int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); + bytesSinceLastACK += buffer.DataLength; + if (bytesSinceLastACK > LLUDPServer.MTU * 2) + { + bytesSinceLastACK -= LLUDPServer.MTU * 2; + SendAcks(udpClient); + } + Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); } - Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); #endregion ACK Sending @@ -612,12 +617,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Incoming Packet Accounting - // Don't bother clogging up the queue with PacketAck packets that are already handled here - if (packet.Type != PacketType.PacketAck) + #region Ping Check Handling + + if (packet.Type == PacketType.StartPingCheck) { - // Inbox insertion - packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); + // We don't need to do anything else with ping checks + StartPingCheckPacket startPing = (StartPingCheckPacket)packet; + + CompletePingCheckPacket completePing = new CompletePingCheckPacket(); + completePing.PingID.PingID = startPing.PingID.PingID; + SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); + return; } + else if (packet.Type == PacketType.CompletePingCheck) + { + // We don't currently track client ping times + return; + } + + #endregion Ping Check Handling + + // Inbox insertion + packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); } protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) -- cgit v1.1