* 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
prioritization
John Hurliman 2009-10-18 02:00:42 -07:00
parent a3f93cffb4
commit b4526a5a6d
4 changed files with 194 additions and 162 deletions

View File

@ -403,26 +403,23 @@ namespace OpenSim.Data.MySQL
}
}
public List<SceneObjectGroup> LoadObjects(UUID regionUUID)
public List<SceneObjectGroup> LoadObjects(UUID regionID)
{
UUID lastGroupID = UUID.Zero;
const int ROWS_PER_QUERY = 5000;
Dictionary<UUID, SceneObjectPart> prims = new Dictionary<UUID, SceneObjectPart>(ROWS_PER_QUERY);
Dictionary<UUID, SceneObjectGroup> objects = new Dictionary<UUID, SceneObjectGroup>();
Dictionary<UUID, SceneObjectPart> prims = new Dictionary<UUID, SceneObjectPart>();
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);
UUID parentID = new UUID(reader["SceneGroupID"].ToString());
if (parentID != prim.UUID)
prim.ParentUUID = parentID;
prims[prim.UUID] = prim;
UUID groupID = new UUID(reader["SceneGroupID"].ToString());
if (groupID != lastGroupID) // New SOG
{
if (grp != null)
objects[grp.UUID] = grp;
lastGroupID = groupID;
// 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);
prim.UUID = groupID;
}
grp = new SceneObjectGroup(prim);
}
else
{
// Black magic to preserve link numbers
//
int link = prim.LinkNum;
grp.AddPart(prim);
if (link != 0)
prim.LinkNum = link;
}
++count;
if (count % 5000 == 0)
if (count % ROWS_PER_QUERY == 0)
m_log.Debug("[REGION DB]: Loaded " + count + " prims...");
}
}
if (grp != null)
objects[grp.UUID] = grp;
}
}
#endregion Prim Loading
#region SceneObjectGroup Creation
// 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);
}
// 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;
sog.AddPart(prim);
// 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);
}
}
}
#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<SceneObjectGroup>(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;
}

View File

@ -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

View File

@ -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<UUID,ulong> m_groupPowers = new Dictionary<UUID, ulong>();
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,20 +8873,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion
case PacketType.AgentFOV:
AgentFOVPacket fovPacket = (AgentFOVPacket)Pack;
if (fovPacket.FOVBlock.GenCounter > m_agentFOVCounter)
{
m_agentFOVCounter = fovPacket.FOVBlock.GenCounter;
AgentFOV handlerAgentFOV = OnAgentFOV;
if (handlerAgentFOV != null)
{
handlerAgentFOV(this, fovPacket.FOVBlock.VerticalAngle);
}
}
break;
#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);
break;
case PacketType.CompletePingCheck:
// TODO: Do stats tracking or something with these?
break;
case PacketType.ViewerStats:
// TODO: handle this packet
//m_log.Warn("[CLIENT]: unhandled ViewerStats packet");

View File

@ -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)