Merge branch 'master' of ssh://opensimulator.org/var/git/opensim

bulletsim
Justin Clark-Casey (justincc) 2011-04-18 20:36:26 +01:00
commit 8533c63d89
14 changed files with 553 additions and 441 deletions

View File

@ -1337,12 +1337,12 @@ namespace OpenSim.Client.MXP.ClientStack
// Need to translate to MXP somehow // Need to translate to MXP somehow
} }
public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags)
{ {
//throw new System.NotImplementedException(); //throw new System.NotImplementedException();
} }
public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) public void SendObjectPropertiesReply(ISceneEntity entity)
{ {
//throw new System.NotImplementedException(); //throw new System.NotImplementedException();
} }

View File

@ -884,12 +884,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags)
{ {
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) public void SendObjectPropertiesReply(ISceneEntity entity)
{ {
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }

View File

@ -570,16 +570,35 @@ namespace OpenSim.Framework
public float dwell; public float dwell;
} }
public class EntityUpdate public class IEntityUpdate
{ {
public ISceneEntity Entity; public ISceneEntity Entity;
public PrimUpdateFlags Flags; public uint Flags;
public float TimeDilation;
public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) public virtual void Update(IEntityUpdate update)
{
this.Flags |= update.Flags;
}
public IEntityUpdate(ISceneEntity entity, uint flags)
{ {
Entity = entity; Entity = entity;
Flags = flags; Flags = flags;
}
}
public class EntityUpdate : IEntityUpdate
{
// public ISceneEntity Entity;
// public PrimUpdateFlags Flags;
public float TimeDilation;
public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation)
: base(entity,(uint)flags)
{
//Entity = entity;
// Flags = flags;
TimeDilation = timedilation; TimeDilation = timedilation;
} }
} }
@ -1211,20 +1230,9 @@ namespace OpenSim.Framework
/// <param name="stats"></param> /// <param name="stats"></param>
void SendSimStats(SimStats stats); void SendSimStats(SimStats stats);
void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags);
uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice,
uint Category,
UUID LastOwnerID, string ObjectName, string Description);
void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, void SendObjectPropertiesReply(ISceneEntity Entity);
UUID FromTaskUUID,
UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID,
UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle,
string ItemName,
string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask,
uint EveryoneMask,
uint BaseMask, byte saleType, int salePrice);
void SendAgentOffline(UUID[] agentIDs); void SendAgentOffline(UUID[] agentIDs);

View File

@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary> /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary>
private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
// First log file or time has expired, start writing to a new log file
//<MIC>
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// THIS IS DEBUGGING CODE & SHOULD BE REMOVED
// -----------------------------------------------------------------
// -----------------------------------------------------------------
public class QueueLogger
{
public Int32 start = 0;
public StreamWriter Log = null;
private Dictionary<UUID,int> m_idMap = new Dictionary<UUID,int>();
public QueueLogger()
{
DateTime now = DateTime.Now;
String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss"));
Log = new StreamWriter(fname);
start = Util.EnvironmentTickCount();
}
public int LookupID(UUID uuid)
{
int localid;
if (! m_idMap.TryGetValue(uuid,out localid))
{
localid = m_idMap.Count + 1;
m_idMap[uuid] = localid;
}
return localid;
}
}
public static QueueLogger QueueLog = null;
// -----------------------------------------------------------------
public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue)
{
if (QueueLog == null)
QueueLog = new QueueLogger();
Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
lock(QueueLog)
{
int cid = QueueLog.LookupID(client);
int aid = QueueLog.LookupID(avatar);
QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue);
}
}
// -----------------------------------------------------------------
public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup)
{
if (QueueLog == null)
QueueLog = new QueueLogger();
Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
lock(QueueLog)
{
int cid = QueueLog.LookupID(client);
QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString());
}
}
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// -----------------------------------------------------------------
//</MIC>
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
@ -386,6 +315,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_cachedTextureSerial; private int m_cachedTextureSerial;
private PriorityQueue m_entityUpdates; private PriorityQueue m_entityUpdates;
private PriorityQueue m_entityProps;
private Prioritizer m_prioritizer; private Prioritizer m_prioritizer;
private bool m_disableFacelights = false; private bool m_disableFacelights = false;
@ -433,9 +363,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected IAssetService m_assetService; protected IAssetService m_assetService;
private const bool m_checkPackets = true; private const bool m_checkPackets = true;
private Timer m_propertiesPacketTimer;
private List<ObjectPropertiesPacket.ObjectDataBlock> m_propertiesBlocks = new List<ObjectPropertiesPacket.ObjectDataBlock>();
#endregion Class Members #endregion Class Members
#region Properties #region Properties
@ -511,6 +438,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_scene = scene; m_scene = scene;
m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); m_entityUpdates = new PriorityQueue(m_scene.Entities.Count);
m_entityProps = new PriorityQueue(m_scene.Entities.Count);
m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>(); m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>();
m_killRecord = new HashSet<uint>(); m_killRecord = new HashSet<uint>();
// m_attachmentsSent = new HashSet<uint>(); // m_attachmentsSent = new HashSet<uint>();
@ -534,9 +462,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnQueueEmpty += HandleQueueEmpty;
m_udpClient.OnPacketStats += PopulateStats; m_udpClient.OnPacketStats += PopulateStats;
m_propertiesPacketTimer = new Timer(100);
m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket;
m_prioritizer = new Prioritizer(m_scene); m_prioritizer = new Prioritizer(m_scene);
RegisterLocalPacketHandlers(); RegisterLocalPacketHandlers();
@ -1610,7 +1535,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
else else
{ {
OutPacket(kill, ThrottleOutPacketType.State); // OutPacket(kill, ThrottleOutPacketType.State);
OutPacket(kill, ThrottleOutPacketType.Task);
} }
} }
@ -2440,7 +2366,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
packet.Effect = effectBlocks; packet.Effect = effectBlocks;
OutPacket(packet, ThrottleOutPacketType.State); // OutPacket(packet, ThrottleOutPacketType.State);
OutPacket(packet, ThrottleOutPacketType.Task);
} }
public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember,
@ -3634,9 +3561,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
} }
private Int32 m_LastQueueFill = 0;
private uint m_maxUpdates = 0;
private void ProcessEntityUpdates(int maxUpdates) private void ProcessEntityUpdates(int maxUpdates)
{ {
OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@ -3644,46 +3568,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
// Check to see if this is a flush
if (maxUpdates <= 0) if (maxUpdates <= 0)
{ {
m_maxUpdates = Int32.MaxValue; maxUpdates = Int32.MaxValue;
} }
else
{
if (m_maxUpdates == 0 || m_LastQueueFill == 0)
{
m_maxUpdates = (uint)maxUpdates;
}
else
{
if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
m_maxUpdates += 5;
else
m_maxUpdates = m_maxUpdates >> 1;
}
m_maxUpdates = Util.Clamp<uint>(m_maxUpdates,10,500);
}
m_LastQueueFill = Util.EnvironmentTickCount();
int updatesThisCall = 0; int updatesThisCall = 0;
//<MIC>
// DEBUGGING CODE... REMOVE
// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates);
//</MIC>
// We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
// condition where a kill can be processed before an out-of-date update for the same object. // condition where a kill can be processed before an out-of-date update for the same object.
lock (m_killRecord) lock (m_killRecord)
{ {
float avgTimeDilation = 1.0f; float avgTimeDilation = 1.0f;
EntityUpdate update; IEntityUpdate iupdate;
Int32 timeinqueue; // this is just debugging code & can be dropped later Int32 timeinqueue; // this is just debugging code & can be dropped later
while (updatesThisCall < m_maxUpdates) while (updatesThisCall < maxUpdates)
{ {
lock (m_entityUpdates.SyncRoot) lock (m_entityUpdates.SyncRoot)
if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue))
break; break;
EntityUpdate update = (EntityUpdate)iupdate;
avgTimeDilation += update.TimeDilation; avgTimeDilation += update.TimeDilation;
avgTimeDilation *= 0.5f; avgTimeDilation *= 0.5f;
@ -3723,7 +3631,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region UpdateFlags to packet type conversion #region UpdateFlags to packet type conversion
PrimUpdateFlags updateFlags = update.Flags; PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags;
bool canUseCompressed = true; bool canUseCompressed = true;
bool canUseImproved = true; bool canUseImproved = true;
@ -3803,6 +3711,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion Block Construction #endregion Block Construction
} }
#region Packet Sending #region Packet Sending
//const float TIME_DILATION = 1.0f; //const float TIME_DILATION = 1.0f;
@ -3902,12 +3811,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion Primitive Packet/Data Sending Methods #endregion Primitive Packet/Data Sending Methods
// These are used to implement an adaptive backoff in the number
// of updates converted to packets. Since we don't want packets
// to sit in the queue with old data, only convert enough updates
// to packets that can be sent in 200ms.
private Int32 m_LastQueueFill = 0;
private Int32 m_maxUpdates = 0;
void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories)
{ {
if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
{ {
if (m_maxUpdates == 0 || m_LastQueueFill == 0)
{
m_maxUpdates = m_udpServer.PrimUpdatesPerCallback;
}
else
{
if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
m_maxUpdates += 5;
else
m_maxUpdates = m_maxUpdates >> 1;
}
m_maxUpdates = Util.Clamp<Int32>(m_maxUpdates,10,500);
m_LastQueueFill = Util.EnvironmentTickCount();
if (m_entityUpdates.Count > 0) if (m_entityUpdates.Count > 0)
ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); ProcessEntityUpdates(m_maxUpdates);
if (m_entityProps.Count > 0)
ProcessEntityPropertyRequests(m_maxUpdates);
} }
if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
@ -4021,47 +3954,167 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(pack, ThrottleOutPacketType.Task); OutPacket(pack, ThrottleOutPacketType.Task);
} }
public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, private class ObjectPropertyUpdate : IEntityUpdate
uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category,
UUID LastOwnerID, string ObjectName, string Description)
{ {
ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); internal bool SendFamilyProps;
// TODO: don't create new blocks if recycling an old packet internal bool SendObjectProps;
ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj)
objPropDB.RequestFlags = RequestFlags; : base(entity,flags)
objPropDB.ObjectID = ObjectUUID; {
if (OwnerID == GroupID) SendFamilyProps = sendfam;
objPropDB.OwnerID = UUID.Zero; SendObjectProps = sendobj;
else }
objPropDB.OwnerID = OwnerID; public void Update(ObjectPropertyUpdate update)
objPropDB.GroupID = GroupID; {
objPropDB.BaseMask = BaseMask; SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
objPropDB.OwnerMask = OwnerMask; SendObjectProps = SendObjectProps || update.SendObjectProps;
objPropDB.GroupMask = GroupMask; Flags |= update.Flags;
objPropDB.EveryoneMask = EveryoneMask; }
objPropDB.NextOwnerMask = NextOwnerMask;
// TODO: More properties are needed in SceneObjectPart!
objPropDB.OwnershipCost = OwnershipCost;
objPropDB.SaleType = SaleType;
objPropDB.SalePrice = SalePrice;
objPropDB.Category = Category;
objPropDB.LastOwnerID = LastOwnerID;
objPropDB.Name = Util.StringToBytes256(ObjectName);
objPropDB.Description = Util.StringToBytes256(Description);
objPropFamilyPack.ObjectData = objPropDB;
objPropFamilyPack.Header.Zerocoded = true;
OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task);
} }
public void SendObjectPropertiesReply( public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags)
UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, {
UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, uint priority = 0; // time based ordering only
UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, lock (m_entityProps.SyncRoot)
string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false));
uint BaseMask, byte saleType, int salePrice) }
public void SendObjectPropertiesReply(ISceneEntity entity)
{
uint priority = 0; // time based ordering only
lock (m_entityProps.SyncRoot)
m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true));
}
private void ProcessEntityPropertyRequests(int maxUpdates)
{
OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>> objectFamilyBlocks =
new OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>>();
OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks =
new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>();
IEntityUpdate iupdate;
Int32 timeinqueue; // this is just debugging code & can be dropped later
int updatesThisCall = 0;
while (updatesThisCall < m_maxUpdates)
{
lock (m_entityProps.SyncRoot)
if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue))
break;
ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate;
if (update.SendFamilyProps)
{
if (update.Entity is SceneObjectPart)
{
SceneObjectPart sop = (SceneObjectPart)update.Entity;
ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags);
objectFamilyBlocks.Value.Add(objPropDB);
}
}
if (update.SendObjectProps)
{
if (update.Entity is SceneObjectPart)
{
SceneObjectPart sop = (SceneObjectPart)update.Entity;
ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop);
objectPropertiesBlocks.Value.Add(objPropDB);
}
}
updatesThisCall++;
}
Int32 ppcnt = 0;
Int32 pbcnt = 0;
if (objectPropertiesBlocks.IsValueCreated)
{
List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value;
ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count];
for (int i = 0; i < blocks.Count; i++)
packet.ObjectData[i] = blocks[i];
packet.Header.Zerocoded = true;
OutPacket(packet, ThrottleOutPacketType.Task, true);
pbcnt += blocks.Count;
ppcnt++;
}
Int32 fpcnt = 0;
Int32 fbcnt = 0;
if (objectFamilyBlocks.IsValueCreated)
{
List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value;
// ObjectPropertiesFamilyPacket objPropFamilyPack =
// (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
//
// objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count];
// for (int i = 0; i < blocks.Count; i++)
// objPropFamilyPack.ObjectData[i] = blocks[i];
//
// OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true);
// one packet per object block... uggh...
for (int i = 0; i < blocks.Count; i++)
{
ObjectPropertiesFamilyPacket packet =
(ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
packet.ObjectData = blocks[i];
packet.Header.Zerocoded = true;
OutPacket(packet, ThrottleOutPacketType.Task);
fpcnt++;
fbcnt++;
}
}
// m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt);
// m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt);
}
private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags)
{
ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock();
block.RequestFlags = requestFlags;
block.ObjectID = sop.UUID;
if (sop.OwnerID == sop.GroupID)
block.OwnerID = UUID.Zero;
else
block.OwnerID = sop.OwnerID;
block.GroupID = sop.GroupID;
block.BaseMask = sop.BaseMask;
block.OwnerMask = sop.OwnerMask;
block.GroupMask = sop.GroupMask;
block.EveryoneMask = sop.EveryoneMask;
block.NextOwnerMask = sop.NextOwnerMask;
// TODO: More properties are needed in SceneObjectPart!
block.OwnershipCost = sop.OwnershipCost;
block.SaleType = sop.ObjectSaleType;
block.SalePrice = sop.SalePrice;
block.Category = sop.Category;
block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right?
block.Name = Util.StringToBytes256(sop.Name);
block.Description = Util.StringToBytes256(sop.Description);
return block;
}
private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop)
{ {
//ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
// TODO: don't create new blocks if recycling an old packet // TODO: don't create new blocks if recycling an old packet
@ -4069,84 +4122,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ObjectPropertiesPacket.ObjectDataBlock block = ObjectPropertiesPacket.ObjectDataBlock block =
new ObjectPropertiesPacket.ObjectDataBlock(); new ObjectPropertiesPacket.ObjectDataBlock();
block.ItemID = ItemID; block.ObjectID = sop.UUID;
block.CreationDate = CreationDate; block.Name = Util.StringToBytes256(sop.Name);
block.CreatorID = CreatorUUID; block.Description = Util.StringToBytes256(sop.Description);
block.FolderID = FolderUUID;
block.FromTaskID = FromTaskUUID;
block.GroupID = GroupUUID;
block.InventorySerial = InventorySerial;
block.LastOwnerID = LastOwnerUUID; block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds
// proper.ObjectData[0].LastOwnerID = UUID.Zero; block.CreatorID = sop.CreatorID;
block.GroupID = sop.GroupID;
block.ObjectID = ObjectUUID; block.LastOwnerID = sop.LastOwnerID;
if (OwnerUUID == GroupUUID) if (sop.OwnerID == sop.GroupID)
block.OwnerID = UUID.Zero; block.OwnerID = UUID.Zero;
else else
block.OwnerID = OwnerUUID; block.OwnerID = sop.OwnerID;
block.TouchName = Util.StringToBytes256(TouchTitle);
block.TextureID = TextureID;
block.SitName = Util.StringToBytes256(SitTitle);
block.Name = Util.StringToBytes256(ItemName);
block.Description = Util.StringToBytes256(ItemDescription);
block.OwnerMask = OwnerMask;
block.NextOwnerMask = NextOwnerMask;
block.GroupMask = GroupMask;
block.EveryoneMask = EveryoneMask;
block.BaseMask = BaseMask;
// proper.ObjectData[0].AggregatePerms = 53;
// proper.ObjectData[0].AggregatePermTextures = 0;
// proper.ObjectData[0].AggregatePermTexturesOwner = 0;
block.SaleType = saleType;
block.SalePrice = salePrice;
lock (m_propertiesPacketTimer) block.ItemID = sop.FromUserInventoryItemID;
{ block.FolderID = UUID.Zero; // sop.FromFolderID ??
m_propertiesBlocks.Add(block); block.FromTaskID = UUID.Zero; // ???
block.InventorySerial = (short)sop.InventorySerial;
int length = 0; SceneObjectPart root = sop.ParentGroup.RootPart;
foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks)
{
length += b.Length;
}
if (length > 1100) // FIXME: use real MTU
{
ProcessObjectPropertiesPacket(null, null);
m_propertiesPacketTimer.Stop();
return;
}
m_propertiesPacketTimer.Stop(); block.TouchName = Util.StringToBytes256(root.TouchName);
m_propertiesPacketTimer.Start(); block.TextureID = new byte[0]; // TextureID ???
} block.SitName = Util.StringToBytes256(root.SitName);
block.OwnerMask = root.OwnerMask;
block.NextOwnerMask = root.NextOwnerMask;
block.GroupMask = root.GroupMask;
block.EveryoneMask = root.EveryoneMask;
block.BaseMask = root.BaseMask;
block.SaleType = root.ObjectSaleType;
block.SalePrice = root.SalePrice;
//proper.Header.Zerocoded = true; return block;
//OutPacket(proper, ThrottleOutPacketType.Task);
}
private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e)
{
ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
lock (m_propertiesPacketTimer)
{
m_propertiesPacketTimer.Stop();
proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count];
int index = 0;
foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks)
{
proper.ObjectData[index++] = b;
}
m_propertiesBlocks.Clear();
}
proper.Header.Zerocoded = true;
OutPacket(proper, ThrottleOutPacketType.Task);
} }
#region Estate Data Sending Methods #region Estate Data Sending Methods
@ -4487,6 +4494,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public void SendForceClientSelectObjects(List<uint> ObjectIDs) public void SendForceClientSelectObjects(List<uint> ObjectIDs)
{ {
m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count);
bool firstCall = true; bool firstCall = true;
const int MAX_OBJECTS_PER_PACKET = 251; const int MAX_OBJECTS_PER_PACKET = 251;
ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect);

View File

@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_nextOnQueueEmpty = 1; private int m_nextOnQueueEmpty = 1;
/// <summary>Throttle bucket for this agent's connection</summary> /// <summary>Throttle bucket for this agent's connection</summary>
private readonly TokenBucket m_throttle; private readonly TokenBucket m_throttleClient;
/// <summary>Throttle bucket for this agent's connection</summary>
private readonly TokenBucket m_throttleCategory;
/// <summary>Throttle buckets for each packet category</summary> /// <summary>Throttle buckets for each packet category</summary>
private readonly TokenBucket[] m_throttleCategories; private readonly TokenBucket[] m_throttleCategories;
/// <summary>Outgoing queues for throttled packets</summary> /// <summary>Outgoing queues for throttled packets</summary>
@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_maxRTO = maxRTO; m_maxRTO = maxRTO;
// Create a token bucket throttle for this client that has the scene token bucket as a parent // Create a token bucket throttle for this client that has the scene token bucket as a parent
m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit);
// Create a token bucket throttle for the total categary with the client bucket as a throttle
m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit);
// Create an array of token buckets for this clients different throttle categories // Create an array of token buckets for this clients different throttle categories
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Initialize the packet outboxes, where packets sit while they are waiting for tokens // Initialize the packet outboxes, where packets sit while they are waiting for tokens
m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
// Initialize the token buckets that control the throttling for each category // Initialize the token buckets that control the throttling for each category
m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type));
} }
// Default the retransmission timeout to three seconds // Default the retransmission timeout to three seconds
@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_packetOutboxes[i].Clear(); m_packetOutboxes[i].Clear();
m_nextPackets[i] = null; m_nextPackets[i] = null;
} }
// pull the throttle out of the scene throttle
m_throttleClient.Parent.UnregisterRequest(m_throttleClient);
OnPacketStats = null; OnPacketStats = null;
OnQueueEmpty = null; OnQueueEmpty = null;
} }
@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <returns>Information about the client connection</returns> /// <returns>Information about the client connection</returns>
public ClientInfo GetClientInfo() public ClientInfo GetClientInfo()
{ {
///<mic>
TokenBucket tb;
tb = m_throttleClient.Parent;
m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT");
tb = m_throttleClient;
m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT");
tb = m_throttleCategory;
m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY");
for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
{
tb = m_throttleCategories[i];
m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET");
}
///</mic>
// TODO: This data structure is wrong in so many ways. Locking and copying the entire lists // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
// of pending and needed ACKs for every client every time some method wants information about // of pending and needed ACKs for every client every time some method wants information about
// this connection is a recipe for poor performance // this connection is a recipe for poor performance
@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
info.pendingAcks = new Dictionary<uint, uint>(); info.pendingAcks = new Dictionary<uint, uint>();
info.needAck = new Dictionary<uint, byte[]>(); info.needAck = new Dictionary<uint, byte[]>();
info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
info.taskThrottle + info.assetThrottle + info.textureThrottle; info.taskThrottle + info.assetThrottle + info.textureThrottle;
@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
// State is a subcategory of task that we allocate a percentage to // State is a subcategory of task that we allocate a percentage to
int state = (int)((float)task * STATE_TASK_PERCENTAGE); int state = 0;
task -= state; // int state = (int)((float)task * STATE_TASK_PERCENTAGE);
// task -= state;
// Make sure none of the throttles are set below our packet MTU, // Make sure none of the throttles are set below our packet MTU,
// otherwise a throttle could become permanently clogged // otherwise a throttle could become permanently clogged
@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Update the token buckets with new throttle values // Update the token buckets with new throttle values
TokenBucket bucket; TokenBucket bucket;
bucket = m_throttle; bucket = m_throttleCategory;
bucket.MaxBurst = total; bucket.RequestedDripRate = total;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
bucket.DripRate = resend; bucket.RequestedDripRate = resend;
bucket.MaxBurst = resend;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
bucket.DripRate = land; bucket.RequestedDripRate = land;
bucket.MaxBurst = land;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
bucket.DripRate = wind; bucket.RequestedDripRate = wind;
bucket.MaxBurst = wind;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
bucket.DripRate = cloud; bucket.RequestedDripRate = cloud;
bucket.MaxBurst = cloud;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
bucket.DripRate = asset; bucket.RequestedDripRate = asset;
bucket.MaxBurst = asset;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
bucket.DripRate = task + state; bucket.RequestedDripRate = task;
bucket.MaxBurst = task + state;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State];
bucket.DripRate = state; bucket.RequestedDripRate = state;
bucket.MaxBurst = state;
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
bucket.DripRate = texture; bucket.RequestedDripRate = texture;
bucket.MaxBurst = texture;
// Reset the packed throttles cached data // Reset the packed throttles cached data
m_packedThrottles = null; m_packedThrottles = null;
@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
data = new byte[7 * 4]; data = new byte[7 * 4];
int i = 0; int i = 0;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4;
m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
m_packedThrottles = data; m_packedThrottles = data;
} }
@ -420,6 +440,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category]; OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
TokenBucket bucket = m_throttleCategories[category]; TokenBucket bucket = m_throttleCategories[category];
// Don't send this packet if there is already a packet waiting in the queue
// even if we have the tokens to send it, tokens should go to the already
// queued packets
if (queue.Count > 0)
{
queue.Enqueue(packet);
return true;
}
if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength))
{ {
// Enough tokens were removed from the bucket, the packet will not be queued // Enough tokens were removed from the bucket, the packet will not be queued

View File

@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
#endregion BinaryStats #endregion BinaryStats
m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); m_throttle = new TokenBucket(null, sceneThrottleBps);
ThrottleRates = new ThrottleRates(configSource); ThrottleRates = new ThrottleRates(configSource);
} }

View File

@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
public bool Enqueue(uint pqueue, EntityUpdate value) public bool Enqueue(uint pqueue, IEntityUpdate value)
{ {
LookupItem lookup; LookupItem lookup;
@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (m_lookupTable.TryGetValue(localid, out lookup)) if (m_lookupTable.TryGetValue(localid, out lookup))
{ {
entry = lookup.Heap[lookup.Handle].EntryOrder; entry = lookup.Heap[lookup.Handle].EntryOrder;
value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; value.Update(lookup.Heap[lookup.Handle].Value);
lookup.Heap.Remove(lookup.Handle); lookup.Heap.Remove(lookup.Handle);
} }
@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return true; return true;
} }
internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
{ {
for (int i = 0; i < m_numberOfQueues; ++i) for (int i = 0; i < m_numberOfQueues; ++i)
{ {
@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
timeinqueue = 0; timeinqueue = 0;
value = default(EntityUpdate); value = default(IEntityUpdate);
return false; return false;
} }
@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region MinHeapItem #region MinHeapItem
private struct MinHeapItem : IComparable<MinHeapItem> private struct MinHeapItem : IComparable<MinHeapItem>
{ {
private EntityUpdate value; private IEntityUpdate value;
internal EntityUpdate Value { internal IEntityUpdate Value {
get { get {
return this.value; return this.value;
} }
@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
this.pqueue = pqueue; this.pqueue = pqueue;
} }
internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value)
{ {
this.entrytime = Util.EnvironmentTickCount(); this.entrytime = Util.EnvironmentTickCount();
this.entryorder = entryorder; this.entryorder = entryorder;

View File

@ -26,6 +26,10 @@
*/ */
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using log4net;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Region.ClientStack.LindenUDP
{ {
@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary> /// </summary>
public class TokenBucket public class TokenBucket
{ {
/// <summary>Parent bucket to this bucket, or null if this is a root private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// bucket</summary> private static Int32 m_counter = 0;
TokenBucket parent;
/// <summary>Size of the bucket in bytes. If zero, the bucket has
/// infinite capacity</summary>
int maxBurst;
/// <summary>Rate that the bucket fills, in bytes per millisecond. If
/// zero, the bucket always remains full</summary>
int tokensPerMS;
/// <summary>Number of tokens currently in the bucket</summary>
int content;
/// <summary>Time of the last drip, in system ticks</summary>
int lastDrip;
#region Properties private Int32 m_identifier;
/// <summary>
/// Number of ticks (ms) per quantum, drip rate and max burst
/// are defined over this interval.
/// </summary>
private const Int32 m_ticksPerQuantum = 1000;
/// <summary>
/// This is the number of quantums worth of packets that can
/// be accommodated during a burst
/// </summary>
private const Double m_quantumsPerBurst = 1.5;
/// <summary>
/// </summary>
private const Int32 m_minimumDripRate = 1400;
/// <summary>Time of the last drip, in system ticks</summary>
private Int32 m_lastDrip;
/// <summary>
/// The number of bytes that can be sent at this moment. This is the
/// current number of tokens in the bucket
/// </summary>
private Int64 m_tokenCount;
/// <summary>
/// Map of children buckets and their requested maximum burst rate
/// </summary>
private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
#region Properties
/// <summary> /// <summary>
/// The parent bucket of this bucket, or null if this bucket has no /// The parent bucket of this bucket, or null if this bucket has no
/// parent. The parent bucket will limit the aggregate bandwidth of all /// parent. The parent bucket will limit the aggregate bandwidth of all
/// of its children buckets /// of its children buckets
/// </summary> /// </summary>
private TokenBucket m_parent;
public TokenBucket Parent public TokenBucket Parent
{ {
get { return parent; } get { return m_parent; }
set { m_parent = value; }
} }
/// <summary> /// <summary>
/// Maximum burst rate in bytes per second. This is the maximum number /// Maximum burst rate in bytes per second. This is the maximum number
/// of tokens that can accumulate in the bucket at any one time /// of tokens that can accumulate in the bucket at any one time. This
/// also sets the total request for leaf nodes
/// </summary> /// </summary>
public int MaxBurst private Int64 m_burstRate;
public Int64 RequestedBurstRate
{ {
get { return maxBurst; } get { return m_burstRate; }
set { maxBurst = (value >= 0 ? value : 0); } set { m_burstRate = (value < 0 ? 0 : value); }
}
public Int64 BurstRate
{
get {
double rate = RequestedBurstRate * BurstRateModifier();
if (rate < m_minimumDripRate * m_quantumsPerBurst)
rate = m_minimumDripRate * m_quantumsPerBurst;
return (Int64) rate;
}
} }
/// <summary> /// <summary>
/// The speed limit of this bucket in bytes per second. This is the /// The speed limit of this bucket in bytes per second. This is the
/// number of tokens that are added to the bucket per second /// number of tokens that are added to the bucket per quantum
/// </summary> /// </summary>
/// <remarks>Tokens are added to the bucket any time /// <remarks>Tokens are added to the bucket any time
/// <seealso cref="RemoveTokens"/> is called, at the granularity of /// <seealso cref="RemoveTokens"/> is called, at the granularity of
/// the system tick interval (typically around 15-22ms)</remarks> /// the system tick interval (typically around 15-22ms)</remarks>
public int DripRate private Int64 m_dripRate;
public Int64 RequestedDripRate
{ {
get { return tokensPerMS * 1000; } get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
set set {
{ m_dripRate = (value < 0 ? 0 : value);
if (value == 0) m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
tokensPerMS = 0; m_totalDripRequest = m_dripRate;
else if (m_parent != null)
{ m_parent.RegisterRequest(this,m_dripRate);
int bpms = (int)((float)value / 1000.0f);
if (bpms <= 0)
tokensPerMS = 1; // 1 byte/ms is the minimum granularity
else
tokensPerMS = bpms;
} }
} }
public Int64 DripRate
{
get {
if (m_parent == null)
return Math.Min(RequestedDripRate,TotalDripRequest);
double rate = (double)RequestedDripRate * m_parent.DripRateModifier();
if (rate < m_minimumDripRate)
rate = m_minimumDripRate;
return (Int64)rate;
}
} }
/// <summary> /// <summary>
/// The speed limit of this bucket in bytes per millisecond /// The current total of the requested maximum burst rates of
/// this bucket's children buckets.
/// </summary> /// </summary>
public int DripPerMS private Int64 m_totalDripRequest;
public Int64 TotalDripRequest
{ {
get { return tokensPerMS; } get { return m_totalDripRequest; }
set { m_totalDripRequest = value; }
} }
/// <summary> #endregion Properties
/// The number of bytes that can be sent at this moment. This is the
/// current number of tokens in the bucket
/// <remarks>If this bucket has a parent bucket that does not have
/// enough tokens for a request, <seealso cref="RemoveTokens"/> will
/// return false regardless of the content of this bucket</remarks>
/// </summary>
public int Content
{
get { return content; }
}
#endregion Properties #region Constructor
/// <summary> /// <summary>
/// Default constructor /// Default constructor
@ -128,12 +169,76 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// zero if this bucket has no maximum capacity</param> /// zero if this bucket has no maximum capacity</param>
/// <param name="dripRate">Rate that the bucket fills, in bytes per /// <param name="dripRate">Rate that the bucket fills, in bytes per
/// second. If zero, the bucket always remains full</param> /// second. If zero, the bucket always remains full</param>
public TokenBucket(TokenBucket parent, int maxBurst, int dripRate) public TokenBucket(TokenBucket parent, Int64 dripRate)
{ {
this.parent = parent; m_identifier = m_counter++;
MaxBurst = maxBurst;
DripRate = dripRate; Parent = parent;
lastDrip = Environment.TickCount & Int32.MaxValue; RequestedDripRate = dripRate;
// TotalDripRequest = dripRate; // this will be overwritten when a child node registers
// MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
m_lastDrip = Environment.TickCount & Int32.MaxValue;
}
#endregion Constructor
/// <summary>
/// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
/// no modification if the requested bandwidth is less than the
/// max burst bandwidth all the way to the root of the throttle
/// hierarchy. However, if any of the parents is over-booked, then
/// the modifier will be less than 1.
/// </summary>
private double DripRateModifier()
{
Int64 driprate = DripRate;
return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
}
/// <summary>
/// </summary>
private double BurstRateModifier()
{
// for now... burst rate is always m_quantumsPerBurst (constant)
// larger than drip rate so the ratio of burst requests is the
// same as the drip ratio
return DripRateModifier();
}
/// <summary>
/// Register drip rate requested by a child of this throttle. Pass the
/// changes up the hierarchy.
/// </summary>
public void RegisterRequest(TokenBucket child, Int64 request)
{
m_children[child] = request;
// m_totalDripRequest = m_children.Values.Sum();
m_totalDripRequest = 0;
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
m_totalDripRequest += cref.Value;
// Pass the new values up to the parent
if (m_parent != null)
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
}
/// <summary>
/// Remove the rate requested by a child of this throttle. Pass the
/// changes up the hierarchy.
/// </summary>
public void UnregisterRequest(TokenBucket child)
{
m_children.Remove(child);
// m_totalDripRequest = m_children.Values.Sum();
m_totalDripRequest = 0;
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
m_totalDripRequest += cref.Value;
// Pass the new values up to the parent
if (m_parent != null)
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
} }
/// <summary> /// <summary>
@ -142,42 +247,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="amount">Number of tokens to remove from the bucket</param> /// <param name="amount">Number of tokens to remove from the bucket</param>
/// <returns>True if the requested number of tokens were removed from /// <returns>True if the requested number of tokens were removed from
/// the bucket, otherwise false</returns> /// the bucket, otherwise false</returns>
public bool RemoveTokens(int amount) public bool RemoveTokens(Int64 amount)
{ {
bool dummy; // Deposit tokens for this interval
return RemoveTokens(amount, out dummy); Drip();
// If we have enough tokens then remove them and return
if (m_tokenCount - amount >= 0)
{
// we don't have to remove from the parent, the drip rate is already
// reflective of the drip rate limits in the parent
m_tokenCount -= amount;
return true;
}
return false;
} }
/// <summary> /// <summary>
/// Remove a given number of tokens from the bucket /// Deposit tokens into the bucket from a child bucket that did
/// not use all of its available tokens
/// </summary> /// </summary>
/// <param name="amount">Number of tokens to remove from the bucket</param> private void Deposit(Int64 count)
/// <param name="dripSucceeded">True if tokens were added to the bucket
/// during this call, otherwise false</param>
/// <returns>True if the requested number of tokens were removed from
/// the bucket, otherwise false</returns>
public bool RemoveTokens(int amount, out bool dripSucceeded)
{ {
if (maxBurst == 0) m_tokenCount += count;
{
dripSucceeded = true;
return true;
}
dripSucceeded = Drip(); // Deposit the overflow in the parent bucket, this is how we share
// unused bandwidth
if (content - amount >= 0) Int64 burstrate = BurstRate;
{ if (m_tokenCount > burstrate)
if (parent != null && !parent.RemoveTokens(amount)) m_tokenCount = burstrate;
return false;
content -= amount;
return true;
}
else
{
return false;
}
} }
/// <summary> /// <summary>
@ -186,37 +285,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// call to Drip /// call to Drip
/// </summary> /// </summary>
/// <returns>True if tokens were added to the bucket, otherwise false</returns> /// <returns>True if tokens were added to the bucket, otherwise false</returns>
public bool Drip() private void Drip()
{ {
if (tokensPerMS == 0) // This should never happen... means we are a leaf node and were created
// with no drip rate...
if (DripRate == 0)
{ {
content = maxBurst; m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0");
return true; return;
} }
else
{
int now = Environment.TickCount & Int32.MaxValue;
int deltaMS = now - lastDrip;
// Determine the interval over which we are adding tokens, never add
// more than a single quantum of tokens
Int32 now = Environment.TickCount & Int32.MaxValue;
Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum);
m_lastDrip = now;
// This can be 0 in the very unusual case that the timer wrapped
// It can be 0 if we try add tokens at a sub-tick rate
if (deltaMS <= 0) if (deltaMS <= 0)
{ return;
if (deltaMS < 0)
lastDrip = now;
return false;
}
int dripAmount = deltaMS * tokensPerMS; Deposit(deltaMS * DripRate / m_ticksPerQuantum);
content = Math.Min(content + dripAmount, maxBurst);
lastDrip = now;
if (dripAmount < 0 || content < 0)
// sim has been idle for too long, integer has overflown
// previous calculation is meaningless, let's put it at correct max
content = maxBurst;
return true;
}
} }
} }
} }

View File

@ -702,18 +702,12 @@ namespace OpenSim.Region.Examples.SimpleModule
{ {
} }
public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags)
uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category,
UUID LastOwnerID, string ObjectName, string Description)
{ {
} }
public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, public void SendObjectPropertiesReply(ISceneEntity entity)
UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID,
UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName,
string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask,
uint BaseMask, byte saleType, int salePrice)
{ {
} }

View File

@ -1772,10 +1772,12 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="part"></param> /// <param name="part"></param>
public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags)
{ {
remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags);
RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask,
RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, // remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask,
RootPart.CreatorID, RootPart.Name, RootPart.Description); // RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask,
// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category,
// RootPart.CreatorID, RootPart.Name, RootPart.Description);
} }
public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID)

View File

@ -2055,15 +2055,7 @@ namespace OpenSim.Region.Framework.Scenes
public void GetProperties(IClientAPI client) public void GetProperties(IClientAPI client)
{ {
//Viewer wants date in microseconds so multiply it by 1,000,000. client.SendObjectPropertiesReply(this);
client.SendObjectPropertiesReply(
m_fromUserInventoryItemID, (ulong)_creationDate*(ulong)1e6, _creatorID, UUID.Zero, UUID.Zero,
_groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID,
ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description,
ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask,
ParentGroup.RootPart._baseMask,
ParentGroup.RootPart.ObjectSaleType,
ParentGroup.RootPart.SalePrice);
} }
public UUID GetRootPartUUID() public UUID GetRootPartUUID()

View File

@ -1332,14 +1332,13 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
} }
public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags)
{ {
} }
public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) public void SendObjectPropertiesReply(ISceneEntity entity)
{ {
} }
public void SendAgentOffline(UUID[] agentIDs) public void SendAgentOffline(UUID[] agentIDs)

View File

@ -786,18 +786,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{ {
} }
public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags)
uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category,
UUID LastOwnerID, string ObjectName, string Description)
{ {
} }
public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, public void SendObjectPropertiesReply(ISceneEntity entity)
UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID,
UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName,
string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask,
uint BaseMask, byte saleType, int salePrice)
{ {
} }

View File

@ -816,18 +816,11 @@ namespace OpenSim.Tests.Common.Mock
{ {
} }
public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags)
uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category,
UUID LastOwnerID, string ObjectName, string Description)
{ {
} }
public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, public void SendObjectPropertiesReply(ISceneEntity entity)
UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID,
UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName,
string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask,
uint BaseMask, byte saleType, int salePrice)
{ {
} }