Hopefully improved texture downloading (including the speed at which they download), but still a few problems, it seems that the client has a quite short timeout for receiving a texture and if the whole texture isn't sent within this time, the client will request the texture again, With quite small textures this is fine, but it seems that with larger textures we can't send them fast enough and a infinite loop develops where the client keeps requesting a texture and we keep trying to send it, but are never fast enough. So I've for now put code in that so that the server will try to send a texture only once and then after that will ignore future requests from that client for that texture.

afrisby
MW 2007-08-08 18:55:58 +00:00
parent 01f4aeb520
commit be483bc697
1 changed files with 83 additions and 35 deletions

View File

@ -28,12 +28,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using libsecondlife; using libsecondlife;
using libsecondlife.Packets; using libsecondlife.Packets;
using OpenSim.Framework.Interfaces; using OpenSim.Framework.Interfaces;
using OpenSim.Framework.Types; using OpenSim.Framework.Types;
using OpenSim.Framework.Utilities;
namespace OpenSim.Framework.Communications.Caches namespace OpenSim.Framework.Communications.Caches
{ {
@ -54,9 +56,15 @@ namespace OpenSim.Framework.Communications.Caches
public Dictionary<LLUUID, AssetRequest> RequestedTextures = new Dictionary<LLUUID, AssetRequest>(); //Textures requested from the asset server public Dictionary<LLUUID, AssetRequest> RequestedTextures = new Dictionary<LLUUID, AssetRequest>(); //Textures requested from the asset server
public Dictionary<LLUUID, TextureSender> SendingTextures = new Dictionary<LLUUID, TextureSender>(); public Dictionary<LLUUID, TextureSender> SendingTextures = new Dictionary<LLUUID, TextureSender>();
private BlockingQueue<TextureSender> QueueTextures = new BlockingQueue<TextureSender>();
private Dictionary<LLUUID, List<LLUUID>> AvatarRecievedTextures = new Dictionary<LLUUID,List<LLUUID>>();
private IAssetServer _assetServer; private IAssetServer _assetServer;
private Thread _assetCacheThread; private Thread _assetCacheThread;
private Thread TextureSenderThread;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@ -71,6 +79,10 @@ namespace OpenSim.Framework.Communications.Caches
this._assetCacheThread.IsBackground = true; this._assetCacheThread.IsBackground = true;
this._assetCacheThread.Start(); this._assetCacheThread.Start();
this.TextureSenderThread = new Thread(new ThreadStart(this.ProcessTextureSenders));
this.TextureSenderThread.IsBackground = true;
this.TextureSenderThread.Start();
} }
public AssetCache(string assetServerDLLName, string assetServerURL, string assetServerKey) public AssetCache(string assetServerDLLName, string assetServerURL, string assetServerKey)
@ -85,6 +97,9 @@ namespace OpenSim.Framework.Communications.Caches
this._assetCacheThread.IsBackground = true; this._assetCacheThread.IsBackground = true;
this._assetCacheThread.Start(); this._assetCacheThread.Start();
this.TextureSenderThread = new Thread(new ThreadStart(this.ProcessTextureSenders));
this.TextureSenderThread.IsBackground = true;
this.TextureSenderThread.Start();
} }
/// <summary> /// <summary>
@ -165,12 +180,14 @@ namespace OpenSim.Framework.Communications.Caches
req = (AssetRequest)this.TextureRequests[i]; req = (AssetRequest)this.TextureRequests[i];
if (!this.SendingTextures.ContainsKey(req.ImageInfo.FullID)) if (!this.SendingTextures.ContainsKey(req.ImageInfo.FullID))
{ {
//Console.WriteLine("new texture to send");
TextureSender sender = new TextureSender(req); TextureSender sender = new TextureSender(req);
sender.OnComplete += this.TextureSent; //sender.OnComplete += this.TextureSent;
lock (this.SendingTextures) lock (this.SendingTextures)
{ {
this.SendingTextures.Add(req.ImageInfo.FullID, sender); this.SendingTextures.Add(req.ImageInfo.FullID, sender);
} }
this.QueueTextures.Enqueue(sender);
} }
} }
@ -178,6 +195,25 @@ namespace OpenSim.Framework.Communications.Caches
this.TextureRequests.Clear(); this.TextureRequests.Clear();
} }
public void ProcessTextureSenders()
{
while (true)
{
TextureSender sender = this.QueueTextures.Dequeue();
bool finished = sender.SendTexture();
if (finished)
{
this.TextureSent(sender);
}
else
{
// Console.WriteLine("readding texture");
this.QueueTextures.Enqueue(sender);
}
}
}
/// <summary> /// <summary>
/// Event handler, called by a TextureSender object to say that texture has been sent /// Event handler, called by a TextureSender object to say that texture has been sent
/// </summary> /// </summary>
@ -189,6 +225,7 @@ namespace OpenSim.Framework.Communications.Caches
lock (this.SendingTextures) lock (this.SendingTextures)
{ {
this.SendingTextures.Remove(sender.request.ImageInfo.FullID); this.SendingTextures.Remove(sender.request.ImageInfo.FullID);
this.AvatarRecievedTextures[sender.request.RequestUser.AgentId].Add(sender.request.ImageInfo.FullID);
} }
} }
} }
@ -201,8 +238,10 @@ namespace OpenSim.Framework.Communications.Caches
//then add to the correct cache list //then add to the correct cache list
//then check for waiting requests for this asset/texture (in the Requested lists) //then check for waiting requests for this asset/texture (in the Requested lists)
//and move those requests into the Requests list. //and move those requests into the Requests list.
if (IsTexture) if (IsTexture)
{ {
// Console.WriteLine("asset recieved from asset server");
TextureImage image = new TextureImage(asset); TextureImage image = new TextureImage(asset);
this.Textures.Add(image.FullID, image); this.Textures.Add(image.FullID, image);
if (this.RequestedTextures.ContainsKey(image.FullID)) if (this.RequestedTextures.ContainsKey(image.FullID))
@ -212,7 +251,7 @@ namespace OpenSim.Framework.Communications.Caches
if (image.Data.LongLength > 600) if (image.Data.LongLength > 600)
{ {
//over 600 bytes so split up file //over 600 bytes so split up file
req.NumPackets = 1 + (int)(image.Data.Length - 600 + 999) / 1000; req.NumPackets = 1 + (int)(image.Data.Length - 600 ) / 1000;
} }
else else
{ {
@ -444,8 +483,17 @@ namespace OpenSim.Framework.Communications.Caches
/// <param name="imageID"></param> /// <param name="imageID"></param>
public void AddTextureRequest(IClientAPI userInfo, LLUUID imageID) public void AddTextureRequest(IClientAPI userInfo, LLUUID imageID)
{ {
//Console.WriteLine("texture request for " + imageID.ToStringHyphenated()); // Console.WriteLine("texture request for " + imageID.ToStringHyphenated());
//check to see if texture is in local cache, if not request from asset server //check to see if texture is in local cache, if not request from asset server
if(!this.AvatarRecievedTextures.ContainsKey(userInfo.AgentId))
{
this.AvatarRecievedTextures.Add(userInfo.AgentId, new List<LLUUID>());
}
if(this.AvatarRecievedTextures[userInfo.AgentId].Contains(imageID))
{
//Console.WriteLine(userInfo.AgentId +" is requesting a image( "+ imageID+" that has already been sent to them");
return;
}
if (!this.Textures.ContainsKey(imageID)) if (!this.Textures.ContainsKey(imageID))
{ {
if (!this.RequestedTextures.ContainsKey(imageID)) if (!this.RequestedTextures.ContainsKey(imageID))
@ -472,7 +520,8 @@ namespace OpenSim.Framework.Communications.Caches
if (imag.Data.LongLength > 600) if (imag.Data.LongLength > 600)
{ {
//over 600 bytes so split up file //over 600 bytes so split up file
req.NumPackets = 1 + (int)(imag.Data.Length - 600 + 999) / 1000; req.NumPackets = 1 + (int)(imag.Data.Length - 600 ) / 1000;
//Console.WriteLine("texture is " + imag.Data.Length + " which we will send in " +req.NumPackets +" packets");
} }
else else
{ {
@ -582,37 +631,22 @@ namespace OpenSim.Framework.Communications.Caches
public class TextureSender public class TextureSender
{ {
public AssetRequest request; public AssetRequest request;
public event DownloadComplete OnComplete;
Thread m_thread;
public TextureSender(AssetRequest req) public TextureSender(AssetRequest req)
{ {
request = req; request = req;
//Console.WriteLine("creating worker thread for texture " + req.ImageInfo.FullID.ToStringHyphenated());
//Console.WriteLine("texture data length is " + req.ImageInfo.Data.Length);
// Console.WriteLine("in " + req.NumPackets + " packets");
//ThreadPool.QueueUserWorkItem(new WaitCallback(SendTexture), new object());
//need some sort of custom threadpool here, as using the .net one, overloads it and stops the handling of incoming packets etc
//but don't really want to create a thread for every texture download
m_thread = new Thread(new ThreadStart(SendTexture));
m_thread.IsBackground = true;
m_thread.Start();
} }
public void SendTexture() public bool SendTexture()
{
//Console.WriteLine("starting to send sending texture " + request.ImageInfo.FullID.ToStringHyphenated());
while (request.PacketCounter != request.NumPackets)
{ {
SendPacket(); SendPacket();
Thread.Sleep(500);
}
//Console.WriteLine("finished sending texture " + request.ImageInfo.FullID.ToStringHyphenated()); if ((request.PacketCounter > request.NumPackets) |(request.NumPackets ==1))
if (OnComplete != null)
{ {
OnComplete(this); return true;
} }
return false;
} }
public void SendPacket() public void SendPacket()
@ -643,13 +677,14 @@ namespace OpenSim.Framework.Communications.Caches
{ {
//more than one packet so split file up //more than one packet so split file up
ImageDataPacket im = new ImageDataPacket(); ImageDataPacket im = new ImageDataPacket();
im.ImageID.Packets = (ushort)req.NumPackets; im.ImageID.Packets = (ushort)(req.NumPackets);
im.ImageID.ID = req.ImageInfo.FullID; im.ImageID.ID = req.ImageInfo.FullID;
im.ImageID.Size = (uint)req.ImageInfo.Data.Length; im.ImageID.Size = (uint)req.ImageInfo.Data.Length;
im.ImageData.Data = new byte[600]; im.ImageData.Data = new byte[600];
Array.Copy(req.ImageInfo.Data, 0, im.ImageData.Data, 0, 600); Array.Copy(req.ImageInfo.Data, 0, im.ImageData.Data, 0, 600);
im.ImageID.Codec = 2; im.ImageID.Codec = 2;
req.RequestUser.OutPacket(im); req.RequestUser.OutPacket(im);
//this.SaveAssetToFile("packetheader.dat", im.ImageData.Data);
req.PacketCounter++; req.PacketCounter++;
//req.ImageInfo.last_used = time; //req.ImageInfo.last_used = time;
//System.Console.WriteLine("sent first packet of texture: //System.Console.WriteLine("sent first packet of texture:
@ -658,23 +693,36 @@ namespace OpenSim.Framework.Communications.Caches
} }
else else
{ {
//Console.WriteLine("sending packet" + req.PacketCounter + "for " + req.ImageInfo.FullID.ToStringHyphenated()); // Console.WriteLine("sending packet" + req.PacketCounter + "for " + req.ImageInfo.FullID.ToStringHyphenated());
//send imagepacket //send imagepacket
//more than one packet so split file up //more than one packet so split file up
ImagePacketPacket im = new ImagePacketPacket(); ImagePacketPacket im = new ImagePacketPacket();
im.ImageID.Packet = (ushort)req.PacketCounter; im.ImageID.Packet = (ushort)(req.PacketCounter);
im.ImageID.ID = req.ImageInfo.FullID; im.ImageID.ID = req.ImageInfo.FullID;
int size = req.ImageInfo.Data.Length - 600 - 1000 * (req.PacketCounter - 1); int size = req.ImageInfo.Data.Length - 600 - (1000 * (req.PacketCounter - 1));
if (size > 1000) size = 1000; if (size > 1000) size = 1000;
im.ImageData.Data = new byte[size]; im.ImageData.Data = new byte[size];
Array.Copy(req.ImageInfo.Data, 600 + 1000 * (req.PacketCounter - 1), im.ImageData.Data, 0, size); Array.Copy(req.ImageInfo.Data, 600 + (1000 * (req.PacketCounter - 1)), im.ImageData.Data, 0, size);
req.RequestUser.OutPacket(im); req.RequestUser.OutPacket(im);
//if (req.PacketCounter == req.NumPackets)
// {
// this.SaveAssetToFile("packet"+req.PacketCounter+".dat", im.ImageData.Data);
//}
req.PacketCounter++; req.PacketCounter++;
//req.ImageInfo.last_used = time; //req.ImageInfo.last_used = time;
//System.Console.WriteLine("sent a packet of texture: "+req.image_info.FullID); //System.Console.WriteLine("sent a packet of texture: "+req.image_info.FullID);
} }
} }
private void SaveAssetToFile(string filename, byte[] data)
{
FileStream fs = File.Create(filename);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(data);
bw.Close();
fs.Close();
}
} }
} }
} }