Improve prim sending by combining multiple prim updates into a single packet

0.6.5-rc1
Melanie Thielker 2009-05-01 16:29:15 +00:00
parent 5ede445bdf
commit 55a69f3f2d
4 changed files with 209 additions and 75 deletions

View File

@ -62,6 +62,8 @@ namespace OpenSim.Framework
RegionStatus RegionStatus { get; set; } RegionStatus RegionStatus { get; set; }
ClientManager ClientManager { get; } ClientManager ClientManager { get; }
float TimeDilation { get; }
event restart OnRestart; event restart OnRestart;
void AddNewClient(IClientAPI client); void AddNewClient(IClientAPI client);

View File

@ -69,10 +69,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_cachedTextureSerial; private int m_cachedTextureSerial;
private Timer m_clientPingTimer; private Timer m_clientPingTimer;
private Timer m_terseUpdateTimer; private Timer m_avatarTerseUpdateTimer;
private Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_terseUpdates = new Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); private Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_avatarTerseUpdates = new Dictionary<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
private ushort m_terseTimeDilationLast = 0; private ushort m_terseTimeDilationLast = 0;
private Timer m_primTerseUpdateTimer;
private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_primTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
private Timer m_primFullUpdateTimer;
private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates =
new List<ObjectUpdatePacket.ObjectDataBlock>();
private bool m_clientBlocked; private bool m_clientBlocked;
private int m_probesWithNoIngressPackets; private int m_probesWithNoIngressPackets;
@ -121,8 +127,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected string m_activeGroupName = String.Empty; protected string m_activeGroupName = String.Empty;
protected ulong m_activeGroupPowers; protected ulong m_activeGroupPowers;
protected Dictionary<UUID,ulong> m_groupPowers = new Dictionary<UUID, ulong>(); protected Dictionary<UUID,ulong> m_groupPowers = new Dictionary<UUID, ulong>();
protected int m_terseUpdateRate = 50; protected int m_avatarTerseUpdateRate = 50;
protected int m_terseUpdatesPerPacket = 5; protected int m_avatarTerseUpdatesPerPacket = 5;
// LL uses these limits, apparently. Compressed terse would be
// 23, but we don't have that yet
//
protected int m_primTerseUpdatesPerPacket = 10;
protected int m_primFullUpdatesPerPacket = 14;
protected int m_primTerseUpdateRate = 10;
protected int m_primFullUpdateRate = 14;
// LLClientView Only // LLClientView Only
public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args); public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args);
@ -528,7 +543,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Shut down timers // Shut down timers
m_clientPingTimer.Stop(); m_clientPingTimer.Stop();
m_terseUpdateTimer.Stop(); m_avatarTerseUpdateTimer.Stop();
m_primTerseUpdateTimer.Stop();
m_primFullUpdateTimer.Stop();
// This is just to give the client a reasonable chance of // This is just to give the client a reasonable chance of
// flushing out all it's packets. There should probably // flushing out all it's packets. There should probably
@ -609,7 +626,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
// Shut down timers // Shut down timers
m_clientPingTimer.Stop(); m_clientPingTimer.Stop();
m_terseUpdateTimer.Stop(); m_avatarTerseUpdateTimer.Stop();
m_primTerseUpdateTimer.Stop();
m_primFullUpdateTimer.Stop();
} }
public void Restart() public void Restart()
@ -621,9 +640,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_clientPingTimer.Elapsed += CheckClientConnectivity; m_clientPingTimer.Elapsed += CheckClientConnectivity;
m_clientPingTimer.Enabled = true; m_clientPingTimer.Enabled = true;
m_terseUpdateTimer = new Timer(m_terseUpdateRate); m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate);
m_terseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates);
m_terseUpdateTimer.AutoReset = false; m_avatarTerseUpdateTimer.AutoReset = false;
m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate);
m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates);
m_primTerseUpdateTimer.AutoReset = false;
m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
m_primFullUpdateTimer.AutoReset = false;
} }
public void Terminate() public void Terminate()
@ -845,10 +872,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_clientPingTimer.Elapsed += CheckClientConnectivity; m_clientPingTimer.Elapsed += CheckClientConnectivity;
m_clientPingTimer.Enabled = true; m_clientPingTimer.Enabled = true;
m_terseUpdateTimer = new Timer(m_terseUpdateRate); m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate);
m_terseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates);
m_terseUpdateTimer.AutoReset = false; m_avatarTerseUpdateTimer.AutoReset = false;
m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate);
m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates);
m_primTerseUpdateTimer.AutoReset = false;
m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
m_primFullUpdateTimer.AutoReset = false;
m_scene.AddNewClient(this); m_scene.AddNewClient(this);
RefreshGroupMembership(); RefreshGroupMembership();
@ -2724,20 +2758,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
CreateAvatarImprovedBlock(localID, position, velocity, rotation); CreateAvatarImprovedBlock(localID, position, velocity, rotation);
bool sendpacketnow = false; bool sendpacketnow = false;
lock (m_terseUpdates) lock (m_avatarTerseUpdates)
{ {
// Only one update per avatar per packet. No need to send old ones so just overwrite them. // Only one update per avatar per packet. No need to send old ones so just overwrite them.
m_terseUpdates[localID] = terseBlock; m_avatarTerseUpdates[localID] = terseBlock;
m_terseTimeDilationLast = timeDilation; m_terseTimeDilationLast = timeDilation;
// If packet is full or own movement packet, send it. // If packet is full or own movement packet, send it.
if (agentid == m_agentId || m_terseUpdates.Count >= m_terseUpdatesPerPacket) if (agentid == m_agentId || m_avatarTerseUpdates.Count >= m_avatarTerseUpdatesPerPacket)
{ {
m_terseUpdateTimer.Stop(); m_avatarTerseUpdateTimer.Stop();
sendpacketnow = true; sendpacketnow = true;
} }
else if (m_terseUpdates.Count == 1) else if (m_avatarTerseUpdates.Count == 1)
m_terseUpdateTimer.Start(); m_avatarTerseUpdateTimer.Start();
} }
// Call ProcessAvatarTerseUpdates outside the lock // Call ProcessAvatarTerseUpdates outside the lock
if (sendpacketnow) if (sendpacketnow)
@ -2746,14 +2780,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e) private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e)
{ {
lock (m_terseUpdates) lock (m_avatarTerseUpdates)
{ {
ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle;
terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[m_terseUpdates.Count]; terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[m_avatarTerseUpdates.Count];
int i = 0; int i = 0;
foreach (KeyValuePair<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> dbe in m_terseUpdates) foreach (KeyValuePair<uint, ImprovedTerseObjectUpdatePacket.ObjectDataBlock> dbe in m_avatarTerseUpdates)
{ {
terse.ObjectData[i] = dbe.Value; terse.ObjectData[i] = dbe.Value;
i++; i++;
@ -2764,7 +2798,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
terse.Header.Zerocoded = true; terse.Header.Zerocoded = true;
OutPacket(terse, ThrottleOutPacketType.Task); OutPacket(terse, ThrottleOutPacketType.Task);
m_terseUpdates.Clear(); m_avatarTerseUpdates.Clear();
} }
} }
@ -2859,81 +2893,124 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0)
rotation = Quaternion.Identity; rotation = Quaternion.Identity;
ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); ObjectUpdatePacket.ObjectDataBlock objectData =
new ObjectUpdatePacket.ObjectDataBlock();
objectData = CreatePrimUpdateBlock(primShape, flags);
objectData.ID = localID;
objectData.FullID = objectID;
objectData.OwnerID = ownerID;
// TODO: don't create new blocks if recycling an old packet objectData.Text = LLUtil.StringToPacketBytes(text);
outPacket.RegionData.RegionHandle = regionHandle; objectData.TextColor[0] = color[0];
outPacket.RegionData.TimeDilation = timeDilation; objectData.TextColor[1] = color[1];
outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; objectData.TextColor[2] = color[2];
objectData.TextColor[3] = color[3];
outPacket.ObjectData[0] = CreatePrimUpdateBlock(primShape, flags); objectData.ParentID = parentID;
objectData.PSBlock = particleSystem;
outPacket.ObjectData[0].ID = localID; objectData.ClickAction = clickAction;
outPacket.ObjectData[0].FullID = objectID; objectData.Material = material;
outPacket.ObjectData[0].OwnerID = ownerID; objectData.Flags = 0;
outPacket.ObjectData[0].Text = LLUtil.StringToPacketBytes(text);
outPacket.ObjectData[0].TextColor[0] = color[0];
outPacket.ObjectData[0].TextColor[1] = color[1];
outPacket.ObjectData[0].TextColor[2] = color[2];
outPacket.ObjectData[0].TextColor[3] = color[3];
outPacket.ObjectData[0].ParentID = parentID;
outPacket.ObjectData[0].PSBlock = particleSystem;
outPacket.ObjectData[0].ClickAction = clickAction;
outPacket.ObjectData[0].Material = material;
outPacket.ObjectData[0].Flags = 0;
if (attachment) if (attachment)
{ {
// Necessary??? // Necessary???
outPacket.ObjectData[0].JointAxisOrAnchor = new Vector3(0, 0, 2); objectData.JointAxisOrAnchor = new Vector3(0, 0, 2);
outPacket.ObjectData[0].JointPivot = new Vector3(0, 0, 0); objectData.JointPivot = new Vector3(0, 0, 0);
// Item from inventory??? // Item from inventory???
outPacket.ObjectData[0].NameValue = objectData.NameValue =
Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid); Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid);
outPacket.ObjectData[0].State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16)); objectData.State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16));
} }
// Xantor 20080528: Send sound info as well // Xantor 20080528: Send sound info as well
// Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again
outPacket.ObjectData[0].Sound = SoundId; objectData.Sound = SoundId;
if (SoundId == UUID.Zero) if (SoundId == UUID.Zero)
{ {
outPacket.ObjectData[0].OwnerID = UUID.Zero; objectData.OwnerID = UUID.Zero;
outPacket.ObjectData[0].Gain = 0.0f; objectData.Gain = 0.0f;
outPacket.ObjectData[0].Radius = 0.0f; objectData.Radius = 0.0f;
outPacket.ObjectData[0].Flags = 0; objectData.Flags = 0;
} }
else else
{ {
outPacket.ObjectData[0].OwnerID = ownerID; objectData.OwnerID = ownerID;
outPacket.ObjectData[0].Gain = (float)SoundGain; objectData.Gain = (float)SoundGain;
outPacket.ObjectData[0].Radius = (float)SoundRadius; objectData.Radius = (float)SoundRadius;
outPacket.ObjectData[0].Flags = SoundFlags; objectData.Flags = SoundFlags;
} }
byte[] pb = pos.GetBytes(); byte[] pb = pos.GetBytes();
Array.Copy(pb, 0, outPacket.ObjectData[0].ObjectData, 0, pb.Length); Array.Copy(pb, 0, objectData.ObjectData, 0, pb.Length);
byte[] vel = velocity.GetBytes(); byte[] vel = velocity.GetBytes();
Array.Copy(vel, 0, outPacket.ObjectData[0].ObjectData, pb.Length, vel.Length); Array.Copy(vel, 0, objectData.ObjectData, pb.Length, vel.Length);
byte[] rot = rotation.GetBytes(); byte[] rot = rotation.GetBytes();
Array.Copy(rot, 0, outPacket.ObjectData[0].ObjectData, 36, rot.Length); Array.Copy(rot, 0, objectData.ObjectData, 36, rot.Length);
byte[] rvel = rotational_velocity.GetBytes(); byte[] rvel = rotational_velocity.GetBytes();
Array.Copy(rvel, 0, outPacket.ObjectData[0].ObjectData, 36 + rot.Length, rvel.Length); Array.Copy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length);
if (textureanim.Length > 0) if (textureanim.Length > 0)
{ {
outPacket.ObjectData[0].TextureAnim = textureanim; objectData.TextureAnim = textureanim;
} }
outPacket.Header.Zerocoded = true;
lock (m_primFullUpdates)
{
if (m_primFullUpdates.Count == 0)
m_primFullUpdateTimer.Start();
m_primFullUpdates.Add(objectData);
if (m_primFullUpdates.Count >= m_primFullUpdatesPerPacket)
ProcessPrimFullUpdates(this, null);
}
}
void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e)
{
lock (m_primFullUpdates)
{
if (m_primFullUpdates.Count == 0)
{
m_primFullUpdateTimer.Stop();
return;
}
ObjectUpdatePacket outPacket =
(ObjectUpdatePacket)PacketPool.Instance.GetPacket(
PacketType.ObjectUpdate);
outPacket.RegionData.RegionHandle =
Scene.RegionInfo.RegionHandle;
outPacket.RegionData.TimeDilation =
(ushort)(Scene.TimeDilation * ushort.MaxValue);
int count = m_primFullUpdates.Count;
if (count > m_primFullUpdatesPerPacket)
count = m_primFullUpdatesPerPacket;
outPacket.ObjectData =
new ObjectUpdatePacket.ObjectDataBlock[count];
for (int index = 0 ; index < count ; index++)
{
outPacket.ObjectData[index] = m_primFullUpdates[0];
m_primFullUpdates.RemoveAt(0);
}
outPacket.Header.Zerocoded = true;
OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority);
if (m_primFullUpdates.Count == 0)
m_primFullUpdateTimer.Stop();
}
} }
/// <summary> /// <summary>
@ -2947,15 +3024,65 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0)
rotation = Quaternion.Identity; rotation = Quaternion.Identity;
ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
// TODO: don't create new blocks if recycling an old packet ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData =
terse.RegionData.RegionHandle = regionHandle; CreatePrimImprovedBlock(localID, position, rotation,
terse.RegionData.TimeDilation = timeDilation; velocity, rotationalvelocity, state);
terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
terse.ObjectData[0] = CreatePrimImprovedBlock(localID, position, rotation, velocity, rotationalvelocity, state); // AssetID should fall into here probably somehow... lock (m_primTerseUpdates)
terse.Header.Reliable = false; {
terse.Header.Zerocoded = true; if (m_primTerseUpdates.Count == 0)
OutPacket(terse, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); m_primTerseUpdateTimer.Start();
m_primTerseUpdates.Add(objectData);
if (m_primTerseUpdates.Count >= m_primTerseUpdatesPerPacket)
ProcessPrimTerseUpdates(this, null);
}
}
void ProcessPrimTerseUpdates(object sender, ElapsedEventArgs e)
{
lock (m_primTerseUpdates)
{
if (m_primTerseUpdates.Count == 0)
{
m_primTerseUpdateTimer.Stop();
return;
}
ImprovedTerseObjectUpdatePacket outPacket =
(ImprovedTerseObjectUpdatePacket)
PacketPool.Instance.GetPacket(
PacketType.ImprovedTerseObjectUpdate);
outPacket.RegionData.RegionHandle =
Scene.RegionInfo.RegionHandle;
outPacket.RegionData.TimeDilation =
(ushort)(Scene.TimeDilation * ushort.MaxValue);
int count = m_primTerseUpdates.Count;
if (count > m_primTerseUpdatesPerPacket)
count = m_primTerseUpdatesPerPacket;
outPacket.ObjectData =
new ImprovedTerseObjectUpdatePacket.
ObjectDataBlock[count];
for (int index = 0 ; index < count ; index++)
{
outPacket.ObjectData[index] = m_primTerseUpdates[0];
m_primTerseUpdates.RemoveAt(0);
}
outPacket.Header.Reliable = false;
outPacket.Header.Zerocoded = true;
OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority);
if (m_primTerseUpdates.Count == 0)
m_primTerseUpdateTimer.Stop();
}
} }
public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID)

View File

@ -97,6 +97,11 @@ namespace OpenSim.Region.Framework.Scenes
get { return m_clientManager; } get { return m_clientManager; }
} }
public float TimeDilation
{
get { return 1.0f; }
}
protected ulong m_regionHandle; protected ulong m_regionHandle;
protected string m_regionName; protected string m_regionName;
protected RegionInfo m_regInfo; protected RegionInfo m_regInfo;

View File

@ -745,7 +745,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
while (m_pendingObjects != null && m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < 60) while (m_pendingObjects != null && m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < 120)
{ {
SceneObjectGroup g = m_pendingObjects.Dequeue(); SceneObjectGroup g = m_pendingObjects.Dequeue();
@ -834,7 +834,7 @@ namespace OpenSim.Region.Framework.Scenes
updateCount++; updateCount++;
} }
if (updateCount > 60) if (updateCount > 300)
break; break;
} }