2008-07-21 21:12:59 +00:00
/ *
* Copyright ( c ) 2007 - 2008 , openmetaverse . org
* All rights reserved .
*
* - Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* - Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
* - Neither the name of the openmetaverse . org nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
* /
2007-07-13 14:49:36 +00:00
using System ;
using System.Collections.Generic ;
using System.Threading ;
2008-03-28 00:28:02 +00:00
using System.IO ;
2010-08-20 10:45:09 +00:00
using System.Net ;
2008-07-21 21:12:59 +00:00
using OpenMetaverse ;
using OpenMetaverse.Packets ;
2009-05-12 00:07:35 +00:00
using OpenMetaverse.Assets ;
2009-07-31 17:43:01 +00:00
using OpenMetaverse.Http ;
using OpenMetaverse.StructuredData ;
using OpenMetaverse.Messages.Linden ;
2007-07-13 14:49:36 +00:00
2008-07-21 21:12:59 +00:00
namespace OpenMetaverse
2007-07-13 14:49:36 +00:00
{
2007-09-10 10:20:30 +00:00
#region Enums
2008-08-20 22:46:28 +00:00
public enum EstateAssetType : int
2008-07-29 21:36:53 +00:00
{
2008-08-20 22:46:28 +00:00
None = - 1 ,
Covenant = 0
2008-07-29 21:36:53 +00:00
}
2007-07-13 14:49:36 +00:00
/// <summary>
///
/// </summary>
public enum StatusCode
{
/// <summary>OK</summary>
OK = 0 ,
/// <summary>Transfer completed</summary>
Done = 1 ,
/// <summary></summary>
Skip = 2 ,
/// <summary></summary>
Abort = 3 ,
/// <summary>Unknown error occurred</summary>
Error = - 1 ,
/// <summary>Equivalent to a 404 error</summary>
UnknownSource = - 2 ,
/// <summary>Client does not have permission for that resource</summary>
2009-07-10 16:23:26 +00:00
InsufficientPermissions = - 3 ,
2007-07-13 14:49:36 +00:00
/// <summary>Unknown status</summary>
Unknown = - 4
}
/// <summary>
///
/// </summary>
public enum ChannelType : int
{
/// <summary></summary>
Unknown = 0 ,
/// <summary>Unknown</summary>
Misc = 1 ,
/// <summary>Virtually all asset transfers use this channel</summary>
Asset = 2
}
/// <summary>
///
/// </summary>
public enum SourceType : int
{
/// <summary></summary>
Unknown = 0 ,
/// <summary>Asset from the asset server</summary>
Asset = 2 ,
/// <summary>Inventory item</summary>
SimInventoryItem = 3 ,
2008-08-20 22:46:28 +00:00
/// <summary>Estate asset, such as an estate covenant</summary>
2007-07-13 14:49:36 +00:00
SimEstate = 4
}
/// <summary>
///
/// </summary>
public enum TargetType : int
{
/// <summary></summary>
Unknown = 0 ,
/// <summary></summary>
2008-08-20 22:46:28 +00:00
File = 1 ,
2007-07-13 14:49:36 +00:00
/// <summary></summary>
2008-08-20 22:46:28 +00:00
VFile = 2
2007-07-13 14:49:36 +00:00
}
/// <summary>
2012-12-24 05:38:48 +01:00
/// When requesting image download, type of the image requested
2007-07-13 14:49:36 +00:00
/// </summary>
public enum ImageType : byte
{
2012-12-24 05:38:48 +01:00
/// <summary>Normal in-world object texture</summary>
2007-07-13 14:49:36 +00:00
Normal = 0 ,
2012-12-24 05:38:48 +01:00
/// <summary>Avatar texture</summary>
Baked = 1 ,
/// <summary>Server baked avatar texture</summary>
2013-02-02 04:02:13 +01:00
ServerBaked = 2
2007-07-13 14:49:36 +00:00
}
2008-09-10 18:42:11 +00:00
/// <summary>
/// Image file format
/// </summary>
public enum ImageCodec : byte
{
Invalid = 0 ,
RGB = 1 ,
J2C = 2 ,
BMP = 3 ,
TGA = 4 ,
JPEG = 5 ,
DXT = 6 ,
PNG = 7
}
2008-08-20 22:46:28 +00:00
public enum TransferError : int
{
None = 0 ,
Failed = - 1 ,
AssetNotFound = - 3 ,
AssetNotFoundInDatabase = - 4 ,
InsufficientPermissions = - 5 ,
EOF = - 39 ,
CannotOpenFile = - 42 ,
FileNotFound = - 43 ,
FileIsEmpty = - 44 ,
TCPTimeout = - 23016 ,
CircuitGone = - 23017
}
2007-09-10 10:20:30 +00:00
#endregion Enums
2008-08-20 22:46:28 +00:00
2007-09-10 10:20:30 +00:00
#region Transfer Classes
2007-07-13 14:49:36 +00:00
/// <summary>
///
/// </summary>
public class Transfer
{
2008-07-25 05:15:05 +00:00
public UUID ID ;
2007-11-06 09:26:10 +00:00
public int Size ;
2009-03-06 01:32:02 +00:00
public byte [ ] AssetData = Utils . EmptyBytes ;
2007-11-06 09:26:10 +00:00
public int Transferred ;
public bool Success ;
public AssetType AssetType ;
2008-11-10 18:37:25 +00:00
2008-07-11 19:05:30 +00:00
private int transferStart ;
2007-11-06 09:26:10 +00:00
2008-07-11 19:05:30 +00:00
/// <summary>Number of milliseconds passed since the last transfer
/// packet was received</summary>
public int TimeSinceLastPacket
{
get { return Environment . TickCount - transferStart ; }
internal set { transferStart = Environment . TickCount + value ; }
}
2007-09-10 10:20:30 +00:00
public Transfer ( )
{
2009-03-06 01:32:02 +00:00
AssetData = Utils . EmptyBytes ;
2007-11-06 09:26:10 +00:00
transferStart = Environment . TickCount ;
2007-09-10 10:20:30 +00:00
}
2007-07-13 14:49:36 +00:00
}
/// <summary>
///
/// </summary>
public class AssetDownload : Transfer
{
2008-07-25 05:15:05 +00:00
public UUID AssetID ;
2007-11-06 09:26:10 +00:00
public ChannelType Channel ;
public SourceType Source ;
public TargetType Target ;
public StatusCode Status ;
public float Priority ;
2007-07-13 14:49:36 +00:00
public Simulator Simulator ;
2009-07-19 03:38:27 +00:00
public AssetManager . AssetReceivedCallback Callback ;
2012-06-02 15:01:27 -07:00
public int nextPacket ;
public InternalDictionary < int , byte [ ] > outOfOrderPackets ;
2009-08-24 21:49:33 +00:00
internal ManualResetEvent HeaderReceivedEvent = new ManualResetEvent ( false ) ;
2008-12-19 03:39:29 +00:00
public AssetDownload ( )
: base ( )
{
2012-06-02 15:01:27 -07:00
nextPacket = 0 ;
outOfOrderPackets = new InternalDictionary < int , byte [ ] > ( ) ;
2008-12-19 03:39:29 +00:00
}
2007-07-13 14:49:36 +00:00
}
2009-03-26 22:56:44 +00:00
/// <summary>
///
/// </summary>
2007-11-06 09:26:10 +00:00
public class XferDownload : Transfer
{
public ulong XferID ;
2008-07-25 05:15:05 +00:00
public UUID VFileID ;
2007-11-06 09:26:10 +00:00
public uint PacketNum ;
public string Filename = String . Empty ;
2009-07-09 19:44:02 +00:00
public TransferError Error = TransferError . None ;
2008-12-19 03:39:29 +00:00
public XferDownload ( )
: base ( )
{
}
2007-11-06 09:26:10 +00:00
}
2007-07-13 14:49:36 +00:00
/// <summary>
///
/// </summary>
public class ImageDownload : Transfer
{
2007-11-06 09:26:10 +00:00
public ushort PacketCount ;
2008-09-10 18:42:11 +00:00
public ImageCodec Codec ;
2007-07-13 14:49:36 +00:00
public Simulator Simulator ;
2008-07-11 19:05:30 +00:00
public SortedList < ushort , ushort > PacketsSeen ;
2008-09-16 00:31:55 +00:00
public ImageType ImageType ;
public int DiscardLevel ;
2008-09-16 01:48:27 +00:00
public float Priority ;
2007-11-06 09:26:10 +00:00
internal int InitialDataSize ;
2009-08-24 21:49:33 +00:00
internal ManualResetEvent HeaderReceivedEvent = new ManualResetEvent ( false ) ;
2008-12-19 03:39:29 +00:00
public ImageDownload ( )
: base ( )
{
}
2007-07-13 14:49:36 +00:00
}
/// <summary>
///
/// </summary>
public class AssetUpload : Transfer
{
2008-07-25 05:15:05 +00:00
public UUID AssetID ;
2007-11-06 09:26:10 +00:00
public AssetType Type ;
public ulong XferID ;
public uint PacketNum ;
2008-12-19 03:39:29 +00:00
public AssetUpload ( )
: base ( )
{
}
2007-07-13 14:49:36 +00:00
}
2008-12-19 03:39:29 +00:00
2009-05-07 16:10:52 +00:00
/// <summary>
///
/// </summary>
2007-11-10 03:21:32 +00:00
public class ImageRequest
{
2009-05-07 16:10:52 +00:00
public UUID ImageID ;
public ImageType Type ;
public float Priority ;
public int DiscardLevel ;
/// <summary>
///
/// </summary>
/// <param name="imageid"></param>
/// <param name="type"></param>
/// <param name="priority"></param>
/// <param name="discardLevel"></param>
2008-07-25 05:15:05 +00:00
public ImageRequest ( UUID imageid , ImageType type , float priority , int discardLevel )
2007-11-10 03:21:32 +00:00
{
ImageID = imageid ;
Type = type ;
Priority = priority ;
DiscardLevel = discardLevel ;
}
2010-03-17 14:00:36 +00:00
2007-11-10 03:21:32 +00:00
}
2007-09-10 10:20:30 +00:00
#endregion Transfer Classes
2007-07-13 14:49:36 +00:00
/// <summary>
///
/// </summary>
public class AssetManager
{
2009-07-27 19:54:08 +00:00
/// <summary>Number of milliseconds to wait for a transfer header packet if out of order data was received</summary>
const int TRANSFER_HEADER_TIMEOUT = 1000 * 15 ;
2007-09-10 10:20:30 +00:00
#region Delegates
2007-07-13 14:49:36 +00:00
/// <summary>
2010-03-17 14:00:36 +00:00
/// Callback used for various asset download requests
2007-07-13 14:49:36 +00:00
/// </summary>
2010-03-17 14:00:36 +00:00
/// <param name="transfer">Transfer information</param>
/// <param name="asset">Downloaded asset, null on fail</param>
2007-07-15 06:36:27 +00:00
public delegate void AssetReceivedCallback ( AssetDownload transfer , Asset asset ) ;
2007-07-13 14:49:36 +00:00
/// <summary>
2010-03-17 14:00:36 +00:00
/// Callback used upon competition of baked texture upload
2007-11-06 09:26:10 +00:00
/// </summary>
2010-03-17 14:00:36 +00:00
/// <param name="newAssetID">Asset UUID of the newly uploaded baked texture</param>
2009-07-31 17:43:01 +00:00
public delegate void BakedTextureUploadedCallback ( UUID newAssetID ) ;
2010-10-15 00:01:17 +00:00
/// <summary>
/// A callback that fires upon the completition of the RequestMesh call
/// </summary>
/// <param name="success">Was the download successfull</param>
/// <param name="assetMesh">Resulting mesh or null on problems</param>
public delegate void MeshDownloadCallback ( bool success , AssetMesh assetMesh ) ;
2010-03-17 14:00:36 +00:00
2007-09-10 10:20:30 +00:00
#endregion Delegates
#region Events
2010-03-17 14:00:36 +00:00
#region XferReceived
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler < XferReceivedEventArgs > m_XferReceivedEvent ;
/// <summary>Raises the XferReceived event</summary>
/// <param name="e">A XferReceivedEventArgs object containing the
/// data returned from the simulator</param>
protected virtual void OnXferReceived ( XferReceivedEventArgs e )
{
EventHandler < XferReceivedEventArgs > handler = m_XferReceivedEvent ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>Thread sync lock object</summary>
private readonly object m_XferReceivedLock = new object ( ) ;
/// <summary>Raised when the simulator responds sends </summary>
public event EventHandler < XferReceivedEventArgs > XferReceived
{
add { lock ( m_XferReceivedLock ) { m_XferReceivedEvent + = value ; } }
remove { lock ( m_XferReceivedLock ) { m_XferReceivedEvent - = value ; } }
}
#endregion
#region AssetUploaded
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler < AssetUploadEventArgs > m_AssetUploadedEvent ;
/// <summary>Raises the AssetUploaded event</summary>
/// <param name="e">A AssetUploadedEventArgs object containing the
/// data returned from the simulator</param>
protected virtual void OnAssetUploaded ( AssetUploadEventArgs e )
{
EventHandler < AssetUploadEventArgs > handler = m_AssetUploadedEvent ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>Thread sync lock object</summary>
private readonly object m_AssetUploadedLock = new object ( ) ;
/// <summary>Raised during upload completes</summary>
public event EventHandler < AssetUploadEventArgs > AssetUploaded
{
add { lock ( m_AssetUploadedLock ) { m_AssetUploadedEvent + = value ; } }
remove { lock ( m_AssetUploadedLock ) { m_AssetUploadedEvent - = value ; } }
}
#endregion
#region UploadProgress
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler < AssetUploadEventArgs > m_UploadProgressEvent ;
/// <summary>Raises the UploadProgress event</summary>
/// <param name="e">A UploadProgressEventArgs object containing the
/// data returned from the simulator</param>
protected virtual void OnUploadProgress ( AssetUploadEventArgs e )
{
EventHandler < AssetUploadEventArgs > handler = m_UploadProgressEvent ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>Thread sync lock object</summary>
private readonly object m_UploadProgressLock = new object ( ) ;
/// <summary>Raised during upload with progres update</summary>
public event EventHandler < AssetUploadEventArgs > UploadProgress
{
add { lock ( m_UploadProgressLock ) { m_UploadProgressEvent + = value ; } }
remove { lock ( m_UploadProgressLock ) { m_UploadProgressEvent - = value ; } }
}
#endregion UploadProgress
#region InitiateDownload
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler < InitiateDownloadEventArgs > m_InitiateDownloadEvent ;
/// <summary>Raises the InitiateDownload event</summary>
/// <param name="e">A InitiateDownloadEventArgs object containing the
/// data returned from the simulator</param>
protected virtual void OnInitiateDownload ( InitiateDownloadEventArgs e )
{
EventHandler < InitiateDownloadEventArgs > handler = m_InitiateDownloadEvent ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>Thread sync lock object</summary>
private readonly object m_InitiateDownloadLock = new object ( ) ;
2009-03-25 21:39:24 +00:00
/// <summary>Fired when the simulator sends an InitiateDownloadPacket, used to download terrain .raw files</summary>
2010-03-17 14:00:36 +00:00
public event EventHandler < InitiateDownloadEventArgs > InitiateDownload
{
add { lock ( m_InitiateDownloadLock ) { m_InitiateDownloadEvent + = value ; } }
remove { lock ( m_InitiateDownloadLock ) { m_InitiateDownloadEvent - = value ; } }
}
#endregion InitiateDownload
#region ImageReceiveProgress
/// <summary>The event subscribers. null if no subcribers</summary>
private EventHandler < ImageReceiveProgressEventArgs > m_ImageReceiveProgressEvent ;
/// <summary>Raises the ImageReceiveProgress event</summary>
/// <param name="e">A ImageReceiveProgressEventArgs object containing the
/// data returned from the simulator</param>
protected virtual void OnImageReceiveProgress ( ImageReceiveProgressEventArgs e )
{
EventHandler < ImageReceiveProgressEventArgs > handler = m_ImageReceiveProgressEvent ;
if ( handler ! = null )
handler ( this , e ) ;
}
/// <summary>Thread sync lock object</summary>
private readonly object m_ImageReceiveProgressLock = new object ( ) ;
/// <summary>Fired when a texture is in the process of being downloaded by the TexturePipeline class</summary>
public event EventHandler < ImageReceiveProgressEventArgs > ImageReceiveProgress
{
add { lock ( m_ImageReceiveProgressLock ) { m_ImageReceiveProgressEvent + = value ; } }
remove { lock ( m_ImageReceiveProgressLock ) { m_ImageReceiveProgressEvent - = value ; } }
}
#endregion ImageReceiveProgress
2007-09-10 10:20:30 +00:00
#endregion Events
2008-04-11 19:10:38 +00:00
/// <summary>Texture download cache</summary>
2009-07-19 03:38:27 +00:00
public AssetCache Cache ;
2008-04-11 19:10:38 +00:00
2009-05-07 16:10:52 +00:00
private TexturePipeline Texture ;
2010-08-20 10:45:09 +00:00
private DownloadManager HttpDownloads ;
2008-07-21 21:12:59 +00:00
private GridClient Client ;
2009-05-07 16:10:52 +00:00
2008-07-25 05:15:05 +00:00
private Dictionary < UUID , Transfer > Transfers = new Dictionary < UUID , Transfer > ( ) ;
2009-05-07 16:10:52 +00:00
2007-11-06 09:26:10 +00:00
private AssetUpload PendingUpload ;
2008-07-21 08:51:49 +00:00
private object PendingUploadLock = new object ( ) ;
private volatile bool WaitingForUploadConfirm = false ;
2010-03-17 14:00:36 +00:00
2007-07-13 14:49:36 +00:00
/// <summary>
/// Default constructor
/// </summary>
2008-07-21 21:12:59 +00:00
/// <param name="client">A reference to the GridClient object</param>
public AssetManager ( GridClient client )
2007-07-13 14:49:36 +00:00
{
Client = client ;
2009-07-19 03:38:27 +00:00
Cache = new AssetCache ( client ) ;
2009-05-07 16:10:52 +00:00
Texture = new TexturePipeline ( client ) ;
2010-08-20 10:45:09 +00:00
HttpDownloads = new DownloadManager ( ) ;
2007-07-13 14:49:36 +00:00
// Transfer packets for downloading large assets
2009-10-28 08:01:52 +00:00
Client . Network . RegisterCallback ( PacketType . TransferInfo , TransferInfoHandler ) ;
Client . Network . RegisterCallback ( PacketType . TransferPacket , TransferPacketHandler ) ;
2007-07-13 14:49:36 +00:00
// Xfer packets for uploading large assets
2009-10-28 08:01:52 +00:00
Client . Network . RegisterCallback ( PacketType . RequestXfer , RequestXferHandler ) ;
Client . Network . RegisterCallback ( PacketType . ConfirmXferPacket , ConfirmXferPacketHandler ) ;
Client . Network . RegisterCallback ( PacketType . AssetUploadComplete , AssetUploadCompleteHandler ) ;
2007-11-06 09:26:10 +00:00
2009-07-09 19:44:02 +00:00
// Xfer packets for downloading misc assets
2009-10-28 08:01:52 +00:00
Client . Network . RegisterCallback ( PacketType . SendXferPacket , SendXferPacketHandler ) ;
Client . Network . RegisterCallback ( PacketType . AbortXfer , AbortXferHandler ) ;
2008-06-06 23:21:23 +00:00
2009-03-25 21:39:24 +00:00
// Simulator is responding to a request to download a file
2009-10-28 08:01:52 +00:00
Client . Network . RegisterCallback ( PacketType . InitiateDownload , InitiateDownloadPacketHandler ) ;
2009-03-25 21:39:24 +00:00
2007-07-13 14:49:36 +00:00
}
2010-03-17 14:00:36 +00:00
2007-07-13 14:49:36 +00:00
/// <summary>
2007-07-30 07:35:15 +00:00
/// Request an asset download
2007-07-13 14:49:36 +00:00
/// </summary>
2007-07-30 07:35:15 +00:00
/// <param name="assetID">Asset UUID</param>
/// <param name="type">Asset type, must be correct for the transfer to succeed</param>
/// <param name="priority">Whether to give this transfer an elevated priority</param>
2009-07-22 06:57:52 +00:00
/// <param name="callback">The callback to fire when the simulator responds with the asset data</param>
2009-07-19 03:38:27 +00:00
public void RequestAsset ( UUID assetID , AssetType type , bool priority , AssetReceivedCallback callback )
2009-07-10 16:23:26 +00:00
{
2009-07-31 23:23:14 +00:00
RequestAsset ( assetID , type , priority , SourceType . Asset , UUID . Random ( ) , callback ) ;
2009-07-10 16:23:26 +00:00
}
/// <summary>
/// Request an asset download
/// </summary>
/// <param name="assetID">Asset UUID</param>
/// <param name="type">Asset type, must be correct for the transfer to succeed</param>
/// <param name="priority">Whether to give this transfer an elevated priority</param>
/// <param name="sourceType">Source location of the requested asset</param>
2009-07-22 06:57:52 +00:00
/// <param name="callback">The callback to fire when the simulator responds with the asset data</param>
2009-07-19 03:38:27 +00:00
public void RequestAsset ( UUID assetID , AssetType type , bool priority , SourceType sourceType , AssetReceivedCallback callback )
2009-07-31 23:23:14 +00:00
{
RequestAsset ( assetID , type , priority , sourceType , UUID . Random ( ) , callback ) ;
}
/// <summary>
/// Request an asset download
/// </summary>
/// <param name="assetID">Asset UUID</param>
/// <param name="type">Asset type, must be correct for the transfer to succeed</param>
/// <param name="priority">Whether to give this transfer an elevated priority</param>
/// <param name="sourceType">Source location of the requested asset</param>
2009-08-19 07:25:30 +00:00
/// <param name="transactionID">UUID of the transaction</param>
2009-07-31 23:23:14 +00:00
/// <param name="callback">The callback to fire when the simulator responds with the asset data</param>
public void RequestAsset ( UUID assetID , AssetType type , bool priority , SourceType sourceType , UUID transactionID , AssetReceivedCallback callback )
2007-07-13 14:49:36 +00:00
{
AssetDownload transfer = new AssetDownload ( ) ;
2009-07-31 23:23:14 +00:00
transfer . ID = transactionID ;
2007-07-13 14:49:36 +00:00
transfer . AssetID = assetID ;
2007-07-15 06:36:27 +00:00
//transfer.AssetType = type; // Set in TransferInfoHandler.
2007-07-13 14:49:36 +00:00
transfer . Priority = 100.0f + ( priority ? 1.0f : 0.0f ) ;
transfer . Channel = ChannelType . Asset ;
2009-07-10 16:23:26 +00:00
transfer . Source = sourceType ;
2007-07-13 14:49:36 +00:00
transfer . Simulator = Client . Network . CurrentSim ;
2009-07-19 03:38:27 +00:00
transfer . Callback = callback ;
// Check asset cache first
if ( callback ! = null & & Cache . HasAsset ( assetID ) )
{
byte [ ] data = Cache . GetCachedAssetBytes ( assetID ) ;
transfer . AssetData = data ;
transfer . Success = true ;
transfer . Status = StatusCode . OK ;
Asset asset = CreateAssetWrapper ( type ) ;
asset . AssetData = data ;
asset . AssetID = assetID ;
try { callback ( transfer , asset ) ; }
catch ( Exception e ) { Logger . Log ( e . Message , Helpers . LogLevel . Error , Client , e ) ; }
return ;
}
2007-07-13 14:49:36 +00:00
// Add this transfer to the dictionary
lock ( Transfers ) Transfers [ transfer . ID ] = transfer ;
// Build the request packet and send it
TransferRequestPacket request = new TransferRequestPacket ( ) ;
request . TransferInfo . ChannelType = ( int ) transfer . Channel ;
request . TransferInfo . Priority = transfer . Priority ;
request . TransferInfo . SourceType = ( int ) transfer . Source ;
request . TransferInfo . TransferID = transfer . ID ;
byte [ ] paramField = new byte [ 20 ] ;
2008-11-05 01:50:24 +00:00
Buffer . BlockCopy ( assetID . GetBytes ( ) , 0 , paramField , 0 , 16 ) ;
Buffer . BlockCopy ( Utils . IntToBytes ( ( int ) type ) , 0 , paramField , 16 , 4 ) ;
2007-07-13 14:49:36 +00:00
request . TransferInfo . Params = paramField ;
Client . Network . SendPacket ( request , transfer . Simulator ) ;
}
2007-11-06 09:26:10 +00:00
/// <summary>
/// Request an asset download through the almost deprecated Xfer system
/// </summary>
/// <param name="filename">Filename of the asset to request</param>
/// <param name="deleteOnCompletion">Whether or not to delete the asset
/// off the server after it is retrieved</param>
/// <param name="useBigPackets">Use large transfer packets or not</param>
/// <param name="vFileID">UUID of the file to request, if filename is
/// left empty</param>
/// <param name="vFileType">Asset type of <code>vFileID</code>, or
/// <code>AssetType.Unknown</code> if filename is not empty</param>
2008-11-11 22:20:50 +00:00
/// <param name="fromCache">Sets the FilePath in the request to Cache
/// (4) if true, otherwise Unknown (0) is used</param>
2007-11-06 09:26:10 +00:00
/// <returns></returns>
2008-11-11 22:20:50 +00:00
public ulong RequestAssetXfer ( string filename , bool deleteOnCompletion , bool useBigPackets , UUID vFileID , AssetType vFileType ,
bool fromCache )
2007-11-06 09:26:10 +00:00
{
2008-07-25 05:15:05 +00:00
UUID uuid = UUID . Random ( ) ;
2007-12-25 02:37:35 +00:00
ulong id = uuid . GetULong ( ) ;
2007-11-06 09:26:10 +00:00
XferDownload transfer = new XferDownload ( ) ;
transfer . XferID = id ;
2008-07-28 22:23:48 +00:00
transfer . ID = new UUID ( id ) ; // Our dictionary tracks transfers with UUIDs, so convert the ulong back
2007-11-06 09:26:10 +00:00
transfer . Filename = filename ;
transfer . VFileID = vFileID ;
transfer . AssetType = vFileType ;
// Add this transfer to the dictionary
lock ( Transfers ) Transfers [ transfer . ID ] = transfer ;
RequestXferPacket request = new RequestXferPacket ( ) ;
request . XferID . ID = id ;
2008-08-12 22:38:02 +00:00
request . XferID . Filename = Utils . StringToBytes ( filename ) ;
2008-11-11 22:20:50 +00:00
request . XferID . FilePath = fromCache ? ( byte ) 4 : ( byte ) 0 ;
2007-11-06 09:26:10 +00:00
request . XferID . DeleteOnCompletion = deleteOnCompletion ;
request . XferID . UseBigPackets = useBigPackets ;
request . XferID . VFileID = vFileID ;
request . XferID . VFileType = ( short ) vFileType ;
Client . Network . SendPacket ( request ) ;
return id ;
}
2007-07-13 14:49:36 +00:00
/// <summary>
///
/// </summary>
2008-07-28 22:23:48 +00:00
/// <param name="assetID">Use UUID.Zero if you do not have the
2007-07-13 14:49:36 +00:00
/// asset ID but have all the necessary permissions</param>
/// <param name="itemID">The item ID of this asset in the inventory</param>
2008-07-28 22:23:48 +00:00
/// <param name="taskID">Use UUID.Zero if you are not requesting an
2007-07-13 14:49:36 +00:00
/// asset from an object inventory</param>
/// <param name="ownerID">The owner of this asset</param>
/// <param name="type">Asset type</param>
/// <param name="priority">Whether to prioritize this asset download or not</param>
2009-07-24 08:25:28 +00:00
/// <param name="callback"></param>
2009-07-19 03:38:27 +00:00
public void RequestInventoryAsset ( UUID assetID , UUID itemID , UUID taskID , UUID ownerID , AssetType type , bool priority , AssetReceivedCallback callback )
2007-07-13 14:49:36 +00:00
{
AssetDownload transfer = new AssetDownload ( ) ;
2008-07-25 05:15:05 +00:00
transfer . ID = UUID . Random ( ) ;
2007-07-13 14:49:36 +00:00
transfer . AssetID = assetID ;
2007-07-15 06:36:27 +00:00
//transfer.AssetType = type; // Set in TransferInfoHandler.
2007-07-13 14:49:36 +00:00
transfer . Priority = 100.0f + ( priority ? 1.0f : 0.0f ) ;
transfer . Channel = ChannelType . Asset ;
transfer . Source = SourceType . SimInventoryItem ;
transfer . Simulator = Client . Network . CurrentSim ;
2009-07-19 03:38:27 +00:00
transfer . Callback = callback ;
// Check asset cache first
if ( callback ! = null & & Cache . HasAsset ( assetID ) )
{
byte [ ] data = Cache . GetCachedAssetBytes ( assetID ) ;
transfer . AssetData = data ;
transfer . Success = true ;
transfer . Status = StatusCode . OK ;
Asset asset = CreateAssetWrapper ( type ) ;
asset . AssetData = data ;
asset . AssetID = assetID ;
try { callback ( transfer , asset ) ; }
catch ( Exception e ) { Logger . Log ( e . Message , Helpers . LogLevel . Error , Client , e ) ; }
2010-03-17 14:00:36 +00:00
2009-07-19 03:38:27 +00:00
return ;
}
2007-07-13 14:49:36 +00:00
// Add this transfer to the dictionary
lock ( Transfers ) Transfers [ transfer . ID ] = transfer ;
// Build the request packet and send it
TransferRequestPacket request = new TransferRequestPacket ( ) ;
request . TransferInfo . ChannelType = ( int ) transfer . Channel ;
request . TransferInfo . Priority = transfer . Priority ;
request . TransferInfo . SourceType = ( int ) transfer . Source ;
request . TransferInfo . TransferID = transfer . ID ;
byte [ ] paramField = new byte [ 100 ] ;
2007-11-06 09:26:10 +00:00
Buffer . BlockCopy ( Client . Self . AgentID . GetBytes ( ) , 0 , paramField , 0 , 16 ) ;
Buffer . BlockCopy ( Client . Self . SessionID . GetBytes ( ) , 0 , paramField , 16 , 16 ) ;
Buffer . BlockCopy ( ownerID . GetBytes ( ) , 0 , paramField , 32 , 16 ) ;
Buffer . BlockCopy ( taskID . GetBytes ( ) , 0 , paramField , 48 , 16 ) ;
Buffer . BlockCopy ( itemID . GetBytes ( ) , 0 , paramField , 64 , 16 ) ;
Buffer . BlockCopy ( assetID . GetBytes ( ) , 0 , paramField , 80 , 16 ) ;
2008-10-06 22:34:38 +00:00
Buffer . BlockCopy ( Utils . IntToBytes ( ( int ) type ) , 0 , paramField , 96 , 4 ) ;
2007-07-13 14:49:36 +00:00
request . TransferInfo . Params = paramField ;
Client . Network . SendPacket ( request , transfer . Simulator ) ;
}
2009-07-19 03:38:27 +00:00
public void RequestInventoryAsset ( InventoryItem item , bool priority , AssetReceivedCallback callback )
2007-08-25 09:36:33 +00:00
{
2009-07-19 03:38:27 +00:00
RequestInventoryAsset ( item . AssetUUID , item . UUID , UUID . Zero , item . OwnerID , item . AssetType , priority , callback ) ;
2007-08-25 09:36:33 +00:00
}
2007-07-13 14:49:36 +00:00
public void RequestEstateAsset ( )
{
throw new Exception ( "This function is not implemented yet!" ) ;
}
2009-03-26 22:56:44 +00:00
/// <summary>
/// Used to force asset data into the PendingUpload property, ie: for raw terrain uploads
/// </summary>
/// <param name="assetData">An AssetUpload object containing the data to upload to the simulator</param>
internal void SetPendingAssetUploadData ( AssetUpload assetData )
{
2010-03-17 14:00:36 +00:00
lock ( PendingUploadLock )
2009-03-26 22:56:44 +00:00
PendingUpload = assetData ;
}
/// <summary>
/// Request an asset be uploaded to the simulator
/// </summary>
/// <param name="asset">The <seealso cref="Asset"/> Object containing the asset data</param>
/// <param name="storeLocal">If True, the asset once uploaded will be stored on the simulator
/// in which the client was connected in addition to being stored on the asset server</param>
/// <returns>The <seealso cref="UUID"/> of the transfer, can be used to correlate the upload with
/// events being fired</returns>
2008-07-25 05:15:05 +00:00
public UUID RequestUpload ( Asset asset , bool storeLocal )
2007-08-25 09:36:33 +00:00
{
if ( asset . AssetData = = null )
throw new ArgumentException ( "Can't upload an asset with no data (did you forget to call Encode?)" ) ;
2008-07-25 05:15:05 +00:00
UUID assetID ;
UUID transferID = RequestUpload ( out assetID , asset . AssetType , asset . AssetData , storeLocal ) ;
2007-08-25 09:36:33 +00:00
asset . AssetID = assetID ;
return transferID ;
}
2010-03-17 14:00:36 +00:00
2009-03-26 22:56:44 +00:00
/// <summary>
/// Request an asset be uploaded to the simulator
/// </summary>
/// <param name="type">The <seealso cref="AssetType"/> of the asset being uploaded</param>
/// <param name="data">A byte array containing the encoded asset data</param>
/// <param name="storeLocal">If True, the asset once uploaded will be stored on the simulator
/// in which the client was connected in addition to being stored on the asset server</param>
/// <returns>The <seealso cref="UUID"/> of the transfer, can be used to correlate the upload with
/// events being fired</returns>
2008-07-25 05:15:05 +00:00
public UUID RequestUpload ( AssetType type , byte [ ] data , bool storeLocal )
2007-07-30 08:10:05 +00:00
{
2008-07-25 05:15:05 +00:00
UUID assetID ;
2008-07-22 19:12:32 +00:00
return RequestUpload ( out assetID , type , data , storeLocal ) ;
2007-07-30 08:10:05 +00:00
}
2009-03-26 22:56:44 +00:00
/// <summary>
/// Request an asset be uploaded to the simulator
/// </summary>
/// <param name="assetID"></param>
2009-03-27 17:33:54 +00:00
/// <param name="type">Asset type to upload this data as</param>
2009-03-26 22:56:44 +00:00
/// <param name="data">A byte array containing the encoded asset data</param>
/// <param name="storeLocal">If True, the asset once uploaded will be stored on the simulator
/// in which the client was connected in addition to being stored on the asset server</param>
/// <returns>The <seealso cref="UUID"/> of the transfer, can be used to correlate the upload with
/// events being fired</returns>
2009-02-18 19:07:06 +00:00
public UUID RequestUpload ( out UUID assetID , AssetType type , byte [ ] data , bool storeLocal )
2010-03-17 14:00:36 +00:00
{
return RequestUpload ( out assetID , type , data , storeLocal , UUID . Random ( ) ) ;
}
2007-07-13 14:49:36 +00:00
/// <summary>
2007-07-30 07:35:15 +00:00
/// Initiate an asset upload
2007-07-13 14:49:36 +00:00
/// </summary>
2008-03-06 05:57:16 +00:00
/// <param name="assetID">The ID this asset will have if the
2007-07-30 08:10:05 +00:00
/// upload succeeds</param>
2007-07-30 07:35:15 +00:00
/// <param name="type">Asset type to upload this data as</param>
/// <param name="data">Raw asset data to upload</param>
/// <param name="storeLocal">Whether to store this asset on the local
/// simulator or the grid-wide asset server</param>
2009-02-18 19:07:06 +00:00
/// <param name="transactionID">The tranaction id for the upload <see cref="RequestCreateItem"/></param>
2007-07-30 08:10:05 +00:00
/// <returns>The transaction ID of this transfer</returns>
2009-02-18 19:07:06 +00:00
public UUID RequestUpload ( out UUID assetID , AssetType type , byte [ ] data , bool storeLocal , UUID transactionID )
2007-07-13 14:49:36 +00:00
{
2007-07-30 08:10:05 +00:00
AssetUpload upload = new AssetUpload ( ) ;
upload . AssetData = data ;
upload . AssetType = type ;
2009-02-18 19:07:06 +00:00
assetID = UUID . Combine ( transactionID , Client . Self . SecureSessionID ) ;
2007-07-30 08:10:05 +00:00
upload . AssetID = assetID ;
upload . Size = data . Length ;
upload . XferID = 0 ;
2010-03-17 14:00:36 +00:00
upload . ID = transactionID ;
2007-07-30 08:10:05 +00:00
// Build and send the upload packet
AssetUploadRequestPacket request = new AssetUploadRequestPacket ( ) ;
request . AssetBlock . StoreLocal = storeLocal ;
2008-07-22 19:12:32 +00:00
request . AssetBlock . Tempfile = false ; // This field is deprecated
2009-02-18 19:07:06 +00:00
request . AssetBlock . TransactionID = transactionID ;
2007-07-30 08:10:05 +00:00
request . AssetBlock . Type = ( sbyte ) type ;
2010-05-05 05:48:57 +00:00
bool isMultiPacketUpload ;
2007-07-30 08:10:05 +00:00
if ( data . Length + 100 < Settings . MAX_PACKET_SIZE )
2007-07-13 14:49:36 +00:00
{
2010-05-05 05:48:57 +00:00
isMultiPacketUpload = false ;
2008-05-06 23:57:26 +00:00
Logger . Log (
2007-07-30 08:10:05 +00:00
String . Format ( "Beginning asset upload [Single Packet], ID: {0}, AssetID: {1}, Size: {2}" ,
2008-05-06 23:57:26 +00:00
upload . ID . ToString ( ) , upload . AssetID . ToString ( ) , upload . Size ) , Helpers . LogLevel . Info , Client ) ;
2007-07-30 08:10:05 +00:00
2010-03-17 14:00:36 +00:00
Transfers [ upload . ID ] = upload ;
2007-07-30 08:10:05 +00:00
// The whole asset will fit in this packet, makes things easy
request . AssetBlock . AssetData = data ;
upload . Transferred = data . Length ;
2007-07-13 14:49:36 +00:00
}
else
{
2010-05-05 05:48:57 +00:00
isMultiPacketUpload = true ;
2008-05-06 23:57:26 +00:00
Logger . Log (
2007-07-30 08:10:05 +00:00
String . Format ( "Beginning asset upload [Multiple Packets], ID: {0}, AssetID: {1}, Size: {2}" ,
2008-05-06 23:57:26 +00:00
upload . ID . ToString ( ) , upload . AssetID . ToString ( ) , upload . Size ) , Helpers . LogLevel . Info , Client ) ;
2007-07-30 07:35:15 +00:00
2007-07-30 08:10:05 +00:00
// Asset is too big, send in multiple packets
2009-03-06 01:32:02 +00:00
request . AssetBlock . AssetData = Utils . EmptyBytes ;
2007-07-13 14:49:36 +00:00
}
2007-07-30 08:10:05 +00:00
2007-11-06 09:26:10 +00:00
// Wait for the previous upload to receive a RequestXferPacket
2008-07-21 08:51:49 +00:00
lock ( PendingUploadLock )
2007-11-06 09:26:10 +00:00
{
2009-08-15 01:44:52 +00:00
const int UPLOAD_CONFIRM_TIMEOUT = 20 * 1000 ;
2008-07-21 08:51:49 +00:00
const int SLEEP_INTERVAL = 50 ;
int t = 0 ;
while ( WaitingForUploadConfirm & & t < UPLOAD_CONFIRM_TIMEOUT )
{
System . Threading . Thread . Sleep ( SLEEP_INTERVAL ) ;
t + = SLEEP_INTERVAL ;
}
2007-11-06 09:26:10 +00:00
2008-07-21 08:51:49 +00:00
if ( t < UPLOAD_CONFIRM_TIMEOUT )
{
2010-05-05 05:48:57 +00:00
if ( isMultiPacketUpload )
{
WaitingForUploadConfirm = true ;
}
2008-07-21 08:51:49 +00:00
PendingUpload = upload ;
Client . Network . SendPacket ( request ) ;
2007-11-06 09:26:10 +00:00
2008-07-21 08:51:49 +00:00
return upload . ID ;
}
else
{
throw new Exception ( "Timeout waiting for previous asset upload to begin" ) ;
}
2007-11-06 09:26:10 +00:00
}
}
2007-07-30 08:10:05 +00:00
2009-07-31 17:43:01 +00:00
public void RequestUploadBakedTexture ( byte [ ] textureData , BakedTextureUploadedCallback callback )
{
Uri url = null ;
Caps caps = Client . Network . CurrentSim . Caps ;
if ( caps ! = null )
url = caps . CapabilityURI ( "UploadBakedTexture" ) ;
if ( url ! = null )
{
// Fetch the uploader capability
CapsClient request = new CapsClient ( url ) ;
request . OnComplete + =
delegate ( CapsClient client , OSD result , Exception error )
{
if ( error = = null & & result is OSDMap )
{
UploadBakedTextureMessage message = new UploadBakedTextureMessage ( ) ;
message . Deserialize ( ( OSDMap ) result ) ;
if ( message . Request . State = = "upload" )
{
Uri uploadUrl = ( ( UploaderRequestUpload ) message . Request ) . Url ;
if ( uploadUrl ! = null )
{
// POST the asset data
CapsClient upload = new CapsClient ( uploadUrl ) ;
upload . OnComplete + =
delegate ( CapsClient client2 , OSD result2 , Exception error2 )
{
if ( error2 = = null & & result2 is OSDMap )
{
UploadBakedTextureMessage message2 = new UploadBakedTextureMessage ( ) ;
message2 . Deserialize ( ( OSDMap ) result2 ) ;
if ( message2 . Request . State = = "complete" )
{
callback ( ( ( UploaderRequestComplete ) message2 . Request ) . AssetID ) ;
return ;
}
}
Logger . Log ( "Bake upload failed during asset upload" , Helpers . LogLevel . Warning , Client ) ;
callback ( UUID . Zero ) ;
} ;
upload . BeginGetResponse ( textureData , "application/octet-stream" , Client . Settings . CAPS_TIMEOUT ) ;
return ;
}
}
}
Logger . Log ( "Bake upload failed during uploader retrieval" , Helpers . LogLevel . Warning , Client ) ;
callback ( UUID . Zero ) ;
} ;
request . BeginGetResponse ( new OSDMap ( ) , OSDFormat . Xml , Client . Settings . CAPS_TIMEOUT ) ;
}
else
{
Logger . Log ( "UploadBakedTexture not available, falling back to UDP method" , Helpers . LogLevel . Info , Client ) ;
2009-07-31 21:53:32 +00:00
2013-07-18 20:00:11 +02:00
WorkPool . QueueUserWorkItem (
2009-07-31 21:53:32 +00:00
delegate ( object o )
{
UUID transactionID = UUID . Random ( ) ;
BakedTextureUploadedCallback uploadCallback = ( BakedTextureUploadedCallback ) o ;
AutoResetEvent uploadEvent = new AutoResetEvent ( false ) ;
2010-03-17 14:00:36 +00:00
EventHandler < AssetUploadEventArgs > udpCallback =
delegate ( object sender , AssetUploadEventArgs e )
2009-07-31 21:53:32 +00:00
{
2010-03-17 14:00:36 +00:00
if ( e . Upload . ID = = transactionID )
2009-07-31 21:53:32 +00:00
{
uploadEvent . Set ( ) ;
2010-03-17 14:00:36 +00:00
uploadCallback ( e . Upload . Success ? e . Upload . AssetID : UUID . Zero ) ;
2009-07-31 21:53:32 +00:00
}
} ;
2010-03-17 14:00:36 +00:00
AssetUploaded + = udpCallback ;
2009-07-31 21:53:32 +00:00
UUID assetID ;
2009-08-15 12:28:59 +00:00
bool success ;
2009-07-31 21:53:32 +00:00
2009-08-15 12:28:59 +00:00
try
{
RequestUpload ( out assetID , AssetType . Texture , textureData , true , transactionID ) ;
success = uploadEvent . WaitOne ( Client . Settings . TRANSFER_TIMEOUT , false ) ;
}
catch ( Exception )
{
success = false ;
}
2009-07-31 21:53:32 +00:00
2010-03-17 14:00:36 +00:00
AssetUploaded - = udpCallback ;
2009-07-31 21:53:32 +00:00
if ( ! success )
uploadCallback ( UUID . Zero ) ;
} , callback
) ;
2009-07-31 17:43:01 +00:00
}
}
2010-08-20 10:45:09 +00:00
#region Texture Downloads
2009-05-07 16:10:52 +00:00
/// <summary>
/// Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
/// manage the requests and re-assemble the image from the packets received from the simulator
/// </summary>
/// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
/// <param name="imageType">The <see cref="ImageType"/> of the texture asset.
/// Use <see cref="ImageType.Normal"/> for most textures, or <see cref="ImageType.Baked"/> for baked layer texture assets</param>
/// <param name="priority">A float indicating the requested priority for the transfer. Higher priority values tell the simulator
/// to prioritize the request before lower valued requests. An image already being transferred using the <see cref="TexturePipeline"/> can have
/// its priority changed by resending the request with the new priority value</param>
/// <param name="discardLevel">Number of quality layers to discard.
2009-09-30 23:15:15 +00:00
/// This controls the end marker of the data sent. Sending with value -1 combined with priority of 0 cancels an in-progress
/// transfer.</param>
/// <remarks>A bug exists in the Linden Simulator where a -1 will occasionally be sent with a non-zero priority
/// indicating an off-by-one error.</remarks>
2009-05-07 16:10:52 +00:00
/// <param name="packetStart">The packet number to begin the request at. A value of 0 begins the request
/// from the start of the asset texture</param>
/// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
/// will contain the result of the request and the texture asset data</param>
/// <param name="progress">If true, the callback will be fired for each chunk of the downloaded image.
/// The callback asset parameter will contain all previously received chunks of the texture asset starting
/// from the beginning of the request</param>
/// <example>
/// Request an image and fire a callback when the request is complete
/// <code>
/// Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, TextureDownloader_OnDownloadFinished);
///
/// private void TextureDownloader_OnDownloadFinished(TextureRequestState state, AssetTexture asset)
/// {
/// if(state == TextureRequestState.Finished)
/// {
/// Console.WriteLine("Texture {0} ({1} bytes) has been successfully downloaded",
/// asset.AssetID,
/// asset.AssetData.Length);
/// }
/// }
/// </code>
/// Request an image and use an inline anonymous method to handle the downloaded texture data
/// <code>
/// Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, delegate(TextureRequestState state, AssetTexture asset)
/// {
/// if(state == TextureRequestState.Finished)
/// {
/// Console.WriteLine("Texture {0} ({1} bytes) has been successfully downloaded",
/// asset.AssetID,
/// asset.AssetData.Length);
/// }
/// }
/// );
/// </code>
/// Request a texture, decode the texture to a bitmap image and apply it to a imagebox
/// <code>
/// Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, TextureDownloader_OnDownloadFinished);
///
/// private void TextureDownloader_OnDownloadFinished(TextureRequestState state, AssetTexture asset)
/// {
/// if(state == TextureRequestState.Finished)
/// {
/// ManagedImage imgData;
/// Image bitmap;
///
/// if (state == TextureRequestState.Finished)
/// {
/// OpenJPEG.DecodeToImage(assetTexture.AssetData, out imgData, out bitmap);
/// picInsignia.Image = bitmap;
/// }
/// }
/// }
/// </code>
/// </example>
2010-03-17 14:00:36 +00:00
public void RequestImage ( UUID textureID , ImageType imageType , float priority , int discardLevel ,
2009-05-07 16:10:52 +00:00
uint packetStart , TextureDownloadCallback callback , bool progress )
2010-03-17 14:00:36 +00:00
{
2010-08-20 10:45:09 +00:00
if ( Client . Settings . USE_HTTP_TEXTURES & &
Client . Network . CurrentSim . Caps ! = null & &
Client . Network . CurrentSim . Caps . CapabilityURI ( "GetTexture" ) ! = null )
{
HttpRequestTexture ( textureID , imageType , priority , discardLevel , packetStart , callback , progress ) ;
}
else
{
Texture . RequestTexture ( textureID , imageType , priority , discardLevel , packetStart , callback , progress ) ;
}
2009-05-07 16:10:52 +00:00
}
/// <summary>
/// Overload: Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
/// manage the requests and re-assemble the image from the packets received from the simulator
/// </summary>
/// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
/// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
/// will contain the result of the request and the texture asset data</param>
public void RequestImage ( UUID textureID , TextureDownloadCallback callback )
{
RequestImage ( textureID , ImageType . Normal , 101300.0f , 0 , 0 , callback , false ) ;
}
/// <summary>
/// Overload: Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
/// manage the requests and re-assemble the image from the packets received from the simulator
/// </summary>
/// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
/// <param name="imageType">The <see cref="ImageType"/> of the texture asset.
/// Use <see cref="ImageType.Normal"/> for most textures, or <see cref="ImageType.Baked"/> for baked layer texture assets</param>
/// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
/// will contain the result of the request and the texture asset data</param>
public void RequestImage ( UUID textureID , ImageType imageType , TextureDownloadCallback callback )
{
RequestImage ( textureID , imageType , 101300.0f , 0 , 0 , callback , false ) ;
}
/// <summary>
/// Overload: Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
/// manage the requests and re-assemble the image from the packets received from the simulator
/// </summary>
/// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
/// <param name="imageType">The <see cref="ImageType"/> of the texture asset.
/// Use <see cref="ImageType.Normal"/> for most textures, or <see cref="ImageType.Baked"/> for baked layer texture assets</param>
/// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
/// will contain the result of the request and the texture asset data</param>
/// <param name="progress">If true, the callback will be fired for each chunk of the downloaded image.
/// The callback asset parameter will contain all previously received chunks of the texture asset starting
/// from the beginning of the request</param>
public void RequestImage ( UUID textureID , ImageType imageType , TextureDownloadCallback callback , bool progress )
{
RequestImage ( textureID , imageType , 101300.0f , 0 , 0 , callback , progress ) ;
}
/// <summary>
/// Cancel a texture request
/// </summary>
/// <param name="textureID">The texture assets <see cref="UUID"/></param>
public void RequestImageCancel ( UUID textureID )
{
Texture . AbortTextureRequest ( textureID ) ;
}
2010-10-15 00:01:17 +00:00
/// <summary>
/// Requests download of a mesh asset
/// </summary>
/// <param name="meshID">UUID of the mesh asset</param>
/// <param name="callback">Callback when the request completes</param>
public void RequestMesh ( UUID meshID , MeshDownloadCallback callback )
{
if ( meshID = = UUID . Zero | | callback = = null )
return ;
if ( Client . Network . CurrentSim . Caps ! = null & &
Client . Network . CurrentSim . Caps . CapabilityURI ( "GetMesh" ) ! = null )
{
// Do we have this mesh asset in the cache?
if ( Client . Assets . Cache . HasAsset ( meshID ) )
{
callback ( true , new AssetMesh ( meshID , Client . Assets . Cache . GetCachedAssetBytes ( meshID ) ) ) ;
return ;
}
Uri url = Client . Network . CurrentSim . Caps . CapabilityURI ( "GetMesh" ) ;
DownloadRequest req = new DownloadRequest (
new Uri ( string . Format ( "{0}/?mesh_id={1}" , url . ToString ( ) , meshID . ToString ( ) ) ) ,
Client . Settings . CAPS_TIMEOUT ,
null ,
null ,
( HttpWebRequest request , HttpWebResponse response , byte [ ] responseData , Exception error ) = >
{
if ( error = = null & & responseData ! = null ) // success
{
callback ( true , new AssetMesh ( meshID , responseData ) ) ;
Client . Assets . Cache . SaveAssetToCache ( meshID , responseData ) ;
}
else // download failed
{
Logger . Log (
string . Format ( "Failed to fetch mesh asset {0}: {1}" ,
meshID ,
( error = = null ) ? "" : error . Message
) ,
Helpers . LogLevel . Warning , Client ) ;
}
}
) ;
HttpDownloads . QueueDownlad ( req ) ;
}
else
{
Logger . Log ( "GetMesh capability not available" , Helpers . LogLevel . Error , Client ) ;
callback ( false , null ) ;
}
}
2012-12-24 05:38:48 +01:00
/// <summary>
/// Fetach avatar texture on a grid capable of server side baking
/// </summary>
/// <param name="avatarID">ID of the avatar</param>
/// <param name="textureID">ID of the texture</param>
/// <param name="bakeName">Name of the part of the avatar texture applies to</param>
/// <param name="callback">Callback invoked on operation completion</param>
public void RequestServerBakedImage ( UUID avatarID , UUID textureID , string bakeName , TextureDownloadCallback callback )
{
if ( avatarID = = UUID . Zero | | textureID = = UUID . Zero | | callback = = null )
return ;
if ( string . IsNullOrEmpty ( Client . Network . AgentAppearanceServiceURL ) )
{
callback ( TextureRequestState . NotFound , null ) ;
return ;
}
byte [ ] assetData ;
// Do we have this image in the cache?
if ( Client . Assets . Cache . HasAsset ( textureID )
& & ( assetData = Client . Assets . Cache . GetCachedAssetBytes ( textureID ) ) ! = null )
{
ImageDownload image = new ImageDownload ( ) ;
image . ID = textureID ;
image . AssetData = assetData ;
image . Size = image . AssetData . Length ;
image . Transferred = image . AssetData . Length ;
image . ImageType = ImageType . ServerBaked ;
image . AssetType = AssetType . Texture ;
image . Success = true ;
callback ( TextureRequestState . Finished , new AssetTexture ( image . ID , image . AssetData ) ) ;
FireImageProgressEvent ( image . ID , image . Transferred , image . Size ) ;
return ;
}
CapsBase . DownloadProgressEventHandler progressHandler = null ;
Uri url = new Uri ( string . Format ( "{0}texture/{1}/{2}/{3}" , Client . Network . AgentAppearanceServiceURL , avatarID , bakeName , textureID ) ) ;
DownloadRequest req = new DownloadRequest (
url ,
Client . Settings . CAPS_TIMEOUT ,
"image/x-j2c" ,
progressHandler ,
( HttpWebRequest request , HttpWebResponse response , byte [ ] responseData , Exception error ) = >
{
if ( error = = null & & responseData ! = null ) // success
{
ImageDownload image = new ImageDownload ( ) ;
image . ID = textureID ;
image . AssetData = responseData ;
image . Size = image . AssetData . Length ;
image . Transferred = image . AssetData . Length ;
image . ImageType = ImageType . ServerBaked ;
image . AssetType = AssetType . Texture ;
image . Success = true ;
callback ( TextureRequestState . Finished , new AssetTexture ( image . ID , image . AssetData ) ) ;
Client . Assets . Cache . SaveAssetToCache ( textureID , responseData ) ;
}
else // download failed
{
Logger . Log (
string . Format ( "Failed to fetch server bake {0}: {1}" ,
textureID ,
( error = = null ) ? "" : error . Message
) ,
Helpers . LogLevel . Warning , Client ) ;
callback ( TextureRequestState . Timeout , null ) ;
}
}
) ;
HttpDownloads . QueueDownlad ( req ) ;
}
2009-05-08 21:26:14 +00:00
/// <summary>
/// Lets TexturePipeline class fire the progress event
/// </summary>
/// <param name="texureID">The texture ID currently being downloaded</param>
/// <param name="transferredBytes">the number of bytes transferred</param>
/// <param name="totalBytes">the total number of bytes expected</param>
internal void FireImageProgressEvent ( UUID texureID , int transferredBytes , int totalBytes )
{
2010-03-17 14:00:36 +00:00
try { OnImageReceiveProgress ( new ImageReceiveProgressEventArgs ( texureID , transferredBytes , totalBytes ) ) ; }
catch ( Exception e ) { Logger . Log ( e . Message , Helpers . LogLevel . Error , Client , e ) ; }
2009-05-08 21:26:14 +00:00
}
2010-08-20 10:45:09 +00:00
// Helper method for downloading textures via GetTexture cap
// Same signature as the UDP variant since we need all the params to
// pass to the UDP TexturePipeline in case we need to fall back to it
// (Linden servers currently (1.42) don't support bakes downloads via HTTP)
private void HttpRequestTexture ( UUID textureID , ImageType imageType , float priority , int discardLevel ,
uint packetStart , TextureDownloadCallback callback , bool progress )
{
if ( textureID = = UUID . Zero | | callback = = null )
return ;
2011-07-09 11:32:09 +00:00
byte [ ] assetData ;
2010-08-20 10:45:09 +00:00
// Do we have this image in the cache?
2011-07-09 11:32:09 +00:00
if ( Client . Assets . Cache . HasAsset ( textureID )
& & ( assetData = Client . Assets . Cache . GetCachedAssetBytes ( textureID ) ) ! = null )
2010-08-20 10:45:09 +00:00
{
ImageDownload image = new ImageDownload ( ) ;
image . ID = textureID ;
2011-07-09 11:32:09 +00:00
image . AssetData = assetData ;
2010-08-20 10:45:09 +00:00
image . Size = image . AssetData . Length ;
image . Transferred = image . AssetData . Length ;
image . ImageType = imageType ;
image . AssetType = AssetType . Texture ;
image . Success = true ;
callback ( TextureRequestState . Finished , new AssetTexture ( image . ID , image . AssetData ) ) ;
FireImageProgressEvent ( image . ID , image . Transferred , image . Size ) ;
return ;
}
CapsBase . DownloadProgressEventHandler progressHandler = null ;
if ( progress )
{
progressHandler = ( HttpWebRequest request , HttpWebResponse response , int bytesReceived , int totalBytesToReceive ) = >
{
FireImageProgressEvent ( textureID , bytesReceived , totalBytesToReceive ) ;
} ;
}
Uri url = Client . Network . CurrentSim . Caps . CapabilityURI ( "GetTexture" ) ;
DownloadRequest req = new DownloadRequest (
new Uri ( string . Format ( "{0}/?texture_id={1}" , url . ToString ( ) , textureID . ToString ( ) ) ) ,
Client . Settings . CAPS_TIMEOUT ,
"image/x-j2c" ,
progressHandler ,
( HttpWebRequest request , HttpWebResponse response , byte [ ] responseData , Exception error ) = >
{
if ( error = = null & & responseData ! = null ) // success
{
ImageDownload image = new ImageDownload ( ) ;
image . ID = textureID ;
image . AssetData = responseData ;
image . Size = image . AssetData . Length ;
image . Transferred = image . AssetData . Length ;
image . ImageType = imageType ;
image . AssetType = AssetType . Texture ;
image . Success = true ;
callback ( TextureRequestState . Finished , new AssetTexture ( image . ID , image . AssetData ) ) ;
FireImageProgressEvent ( image . ID , image . Transferred , image . Size ) ;
Client . Assets . Cache . SaveAssetToCache ( textureID , responseData ) ;
}
else // download failed
{
Logger . Log (
string . Format ( "Failed to fetch texture {0} over HTTP, falling back to UDP: {1}" ,
textureID ,
( error = = null ) ? "" : error . Message
) ,
Helpers . LogLevel . Warning , Client ) ;
Texture . RequestTexture ( textureID , imageType , priority , discardLevel , packetStart , callback , progress ) ;
}
}
) ;
HttpDownloads . QueueDownlad ( req ) ;
}
#endregion Texture Downloads
2007-11-06 09:26:10 +00:00
#region Helpers
2012-05-30 14:16:06 -07:00
public Asset CreateAssetWrapper ( AssetType type )
2007-11-06 09:26:10 +00:00
{
Asset asset ;
switch ( type )
{
case AssetType . Notecard :
asset = new AssetNotecard ( ) ;
break ;
case AssetType . LSLText :
asset = new AssetScriptText ( ) ;
break ;
case AssetType . LSLBytecode :
asset = new AssetScriptBinary ( ) ;
break ;
case AssetType . Texture :
asset = new AssetTexture ( ) ;
break ;
2008-04-07 16:46:04 +00:00
case AssetType . Object :
2007-11-06 09:26:10 +00:00
asset = new AssetPrim ( ) ;
break ;
case AssetType . Clothing :
asset = new AssetClothing ( ) ;
break ;
case AssetType . Bodypart :
asset = new AssetBodypart ( ) ;
break ;
2008-09-13 03:16:21 +00:00
case AssetType . Animation :
asset = new AssetAnimation ( ) ;
break ;
2008-12-19 03:39:29 +00:00
case AssetType . Sound :
asset = new AssetSound ( ) ;
break ;
2009-04-10 21:43:15 +00:00
case AssetType . Landmark :
asset = new AssetLandmark ( ) ;
break ;
2009-07-14 16:16:29 +00:00
case AssetType . Gesture :
asset = new AssetGesture ( ) ;
break ;
2012-05-30 14:16:06 -07:00
case AssetType . CallingCard :
asset = new AssetCallingCard ( ) ;
break ;
2007-11-06 09:26:10 +00:00
default :
2012-05-30 14:16:06 -07:00
asset = new AssetMutable ( type ) ;
Logger . Log ( "Unimplemented asset type: " + type , Helpers . LogLevel . Error ) ;
break ;
2007-11-06 09:26:10 +00:00
}
return asset ;
}
private Asset WrapAsset ( AssetDownload download )
{
Asset asset = CreateAssetWrapper ( download . AssetType ) ;
2008-12-19 03:39:29 +00:00
if ( asset ! = null )
{
asset . AssetID = download . AssetID ;
asset . AssetData = download . AssetData ;
return asset ;
}
else
{
return null ;
}
2007-07-13 14:49:36 +00:00
}
private void SendNextUploadPacket ( AssetUpload upload )
{
SendXferPacketPacket send = new SendXferPacketPacket ( ) ;
send . XferID . ID = upload . XferID ;
send . XferID . Packet = upload . PacketNum + + ;
if ( send . XferID . Packet = = 0 )
{
// The first packet reserves the first four bytes of the data for the
// total length of the asset and appends 1000 bytes of data after that
send . DataPacket . Data = new byte [ 1004 ] ;
2008-10-06 22:34:38 +00:00
Buffer . BlockCopy ( Utils . IntToBytes ( upload . Size ) , 0 , send . DataPacket . Data , 0 , 4 ) ;
2007-07-13 14:49:36 +00:00
Buffer . BlockCopy ( upload . AssetData , 0 , send . DataPacket . Data , 4 , 1000 ) ;
upload . Transferred + = 1000 ;
2007-09-10 10:20:30 +00:00
lock ( Transfers )
{
Transfers . Remove ( upload . AssetID ) ;
Transfers [ upload . ID ] = upload ;
}
2007-07-13 14:49:36 +00:00
}
else if ( ( send . XferID . Packet + 1 ) * 1000 < upload . Size )
{
// This packet is somewhere in the middle of the transfer, or a perfectly
// aligned packet at the end of the transfer
send . DataPacket . Data = new byte [ 1000 ] ;
Buffer . BlockCopy ( upload . AssetData , upload . Transferred , send . DataPacket . Data , 0 , 1000 ) ;
upload . Transferred + = 1000 ;
}
else
{
// Special handler for the last packet which will be less than 1000 bytes
int lastlen = upload . Size - ( ( int ) send . XferID . Packet * 1000 ) ;
send . DataPacket . Data = new byte [ lastlen ] ;
Buffer . BlockCopy ( upload . AssetData , ( int ) send . XferID . Packet * 1000 , send . DataPacket . Data , 0 , lastlen ) ;
send . XferID . Packet | = ( uint ) 0x80000000 ; // This signals the final packet
upload . Transferred + = lastlen ;
}
Client . Network . SendPacket ( send ) ;
}
2007-11-06 09:26:10 +00:00
private void SendConfirmXferPacket ( ulong xferID , uint packetNum )
2007-09-10 10:20:30 +00:00
{
2007-11-06 09:26:10 +00:00
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket ( ) ;
confirm . XferID . ID = xferID ;
confirm . XferID . Packet = packetNum ;
2007-09-10 10:20:30 +00:00
2007-11-06 09:26:10 +00:00
Client . Network . SendPacket ( confirm ) ;
}
2007-09-10 10:20:30 +00:00
2007-11-06 09:26:10 +00:00
#endregion Helpers
2007-09-10 10:20:30 +00:00
2007-11-06 09:26:10 +00:00
#region Transfer Callbacks
2007-09-10 10:20:30 +00:00
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void TransferInfoHandler ( object sender , PacketReceivedEventArgs e )
2007-07-13 14:49:36 +00:00
{
2009-10-28 08:01:52 +00:00
TransferInfoPacket info = ( TransferInfoPacket ) e . Packet ;
2009-07-19 03:38:27 +00:00
Transfer transfer ;
AssetDownload download ;
if ( Transfers . TryGetValue ( info . TransferInfo . TransferID , out transfer ) )
2007-07-13 14:49:36 +00:00
{
2009-07-19 03:38:27 +00:00
download = ( AssetDownload ) transfer ;
if ( download . Callback = = null ) return ;
2007-07-13 14:49:36 +00:00
2009-07-19 03:38:27 +00:00
download . Channel = ( ChannelType ) info . TransferInfo . ChannelType ;
download . Status = ( StatusCode ) info . TransferInfo . Status ;
download . Target = ( TargetType ) info . TransferInfo . TargetType ;
download . Size = info . TransferInfo . Size ;
// TODO: Once we support mid-transfer status checking and aborting this
// will need to become smarter
if ( download . Status ! = StatusCode . OK )
2007-07-13 14:49:36 +00:00
{
2009-07-19 03:38:27 +00:00
Logger . Log ( "Transfer failed with status code " + download . Status , Helpers . LogLevel . Warning , Client ) ;
2007-07-13 14:49:36 +00:00
2009-07-19 03:38:27 +00:00
lock ( Transfers ) Transfers . Remove ( download . ID ) ;
2007-07-13 14:49:36 +00:00
2009-07-19 03:38:27 +00:00
// No data could have been received before the TransferInfo packet
download . AssetData = null ;
2007-07-13 14:49:36 +00:00
2009-07-19 03:38:27 +00:00
// Fire the event with our transfer that contains Success = false;
try { download . Callback ( download , null ) ; }
2009-10-28 08:01:52 +00:00
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2009-07-19 03:38:27 +00:00
}
else
{
download . AssetData = new byte [ download . Size ] ;
2007-07-13 14:49:36 +00:00
2009-07-19 03:38:27 +00:00
if ( download . Source = = SourceType . Asset & & info . TransferInfo . Params . Length = = 20 )
{
download . AssetID = new UUID ( info . TransferInfo . Params , 0 ) ;
download . AssetType = ( AssetType ) ( sbyte ) info . TransferInfo . Params [ 16 ] ;
2007-07-13 14:49:36 +00:00
2009-07-19 03:38:27 +00:00
//Client.DebugLog(String.Format("TransferInfo packet received. AssetID: {0} Type: {1}",
// transfer.AssetID, type));
}
else if ( download . Source = = SourceType . SimInventoryItem & & info . TransferInfo . Params . Length = = 100 )
{
// TODO: Can we use these?
//UUID agentID = new UUID(info.TransferInfo.Params, 0);
//UUID sessionID = new UUID(info.TransferInfo.Params, 16);
//UUID ownerID = new UUID(info.TransferInfo.Params, 32);
//UUID taskID = new UUID(info.TransferInfo.Params, 48);
//UUID itemID = new UUID(info.TransferInfo.Params, 64);
download . AssetID = new UUID ( info . TransferInfo . Params , 80 ) ;
download . AssetType = ( AssetType ) ( sbyte ) info . TransferInfo . Params [ 96 ] ;
//Client.DebugLog(String.Format("TransferInfo packet received. AgentID: {0} SessionID: {1} " +
// "OwnerID: {2} TaskID: {3} ItemID: {4} AssetID: {5} Type: {6}", agentID, sessionID,
// ownerID, taskID, itemID, transfer.AssetID, type));
2007-07-13 14:49:36 +00:00
}
else
{
2009-07-19 03:38:27 +00:00
Logger . Log ( "Received a TransferInfo packet with a SourceType of " + download . Source . ToString ( ) +
" and a Params field length of " + info . TransferInfo . Params . Length ,
Helpers . LogLevel . Warning , Client ) ;
2007-07-13 14:49:36 +00:00
}
}
2009-08-24 21:49:33 +00:00
download . HeaderReceivedEvent . Set ( ) ;
2009-07-19 03:38:27 +00:00
}
else
{
Logger . Log ( "Received a TransferInfo packet for an asset we didn't request, TransferID: " +
info . TransferInfo . TransferID , Helpers . LogLevel . Warning , Client ) ;
2007-07-13 14:49:36 +00:00
}
}
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void TransferPacketHandler ( object sender , PacketReceivedEventArgs e )
2007-07-13 14:49:36 +00:00
{
2009-10-28 08:01:52 +00:00
TransferPacketPacket asset = ( TransferPacketPacket ) e . Packet ;
2007-09-10 10:20:30 +00:00
Transfer transfer ;
2007-07-13 14:49:36 +00:00
2007-09-10 10:20:30 +00:00
if ( Transfers . TryGetValue ( asset . TransferData . TransferID , out transfer ) )
2007-07-13 14:49:36 +00:00
{
2012-06-02 15:01:27 -07:00
AssetDownload download = ( AssetDownload ) transfer ;
2007-07-13 14:49:36 +00:00
2007-09-10 10:20:30 +00:00
if ( download . Size = = 0 )
2007-07-13 14:49:36 +00:00
{
2008-05-06 23:57:26 +00:00
Logger . DebugLog ( "TransferPacket received ahead of the transfer header, blocking..." , Client ) ;
2007-07-13 14:49:36 +00:00
// We haven't received the header yet, block until it's received or times out
2009-07-27 19:54:08 +00:00
download . HeaderReceivedEvent . WaitOne ( TRANSFER_HEADER_TIMEOUT , false ) ;
2007-07-13 14:49:36 +00:00
2007-09-10 10:20:30 +00:00
if ( download . Size = = 0 )
2007-07-13 14:49:36 +00:00
{
2008-05-06 23:57:26 +00:00
Logger . Log ( "Timed out while waiting for the asset header to download for " +
download . ID . ToString ( ) , Helpers . LogLevel . Warning , Client ) ;
2007-07-13 14:49:36 +00:00
// Abort the transfer
TransferAbortPacket abort = new TransferAbortPacket ( ) ;
2007-09-10 10:20:30 +00:00
abort . TransferInfo . ChannelType = ( int ) download . Channel ;
abort . TransferInfo . TransferID = download . ID ;
Client . Network . SendPacket ( abort , download . Simulator ) ;
2007-07-13 14:49:36 +00:00
2007-09-10 10:20:30 +00:00
download . Success = false ;
lock ( Transfers ) Transfers . Remove ( download . ID ) ;
2007-07-13 14:49:36 +00:00
// Fire the event with our transfer that contains Success = false
2009-07-19 03:38:27 +00:00
if ( download . Callback ! = null )
2007-07-13 14:49:36 +00:00
{
2009-07-19 03:38:27 +00:00
try { download . Callback ( download , null ) ; }
2009-10-28 08:01:52 +00:00
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2007-07-13 14:49:36 +00:00
}
return ;
}
}
2012-06-02 15:01:27 -07:00
// If packets arrive out of order, we add them to the out of order packet directory
// until all previous packets have arrived
2008-12-19 03:39:29 +00:00
try
{
2012-06-02 15:01:27 -07:00
if ( download . nextPacket = = asset . TransferData . Packet )
{
byte [ ] data = asset . TransferData . Data ;
do
{
Buffer . BlockCopy ( data , 0 , download . AssetData , download . Transferred , data . Length ) ;
download . Transferred + = data . Length ;
download . nextPacket + + ;
} while ( download . outOfOrderPackets . TryGetValue ( download . nextPacket , out data ) ) ;
}
else
{
//Logger.Log(string.Format("Fixing out of order packet {0} when expecting {1}!", asset.TransferData.Packet, download.nextPacket), Helpers.LogLevel.Debug);
download . outOfOrderPackets . Add ( asset . TransferData . Packet , asset . TransferData . Data ) ;
}
2008-12-19 03:39:29 +00:00
}
catch ( ArgumentException )
{
Logger . Log ( String . Format ( "TransferPacket handling failed. TransferData.Data.Length={0}, AssetData.Length={1}, TransferData.Packet={2}" ,
asset . TransferData . Data . Length , download . AssetData . Length , asset . TransferData . Packet ) , Helpers . LogLevel . Error ) ;
return ;
}
2008-11-10 18:37:25 +00:00
//Client.DebugLog(String.Format("Transfer packet {0}, received {1}/{2}/{3} bytes for asset {4}",
// asset.TransferData.Packet, asset.TransferData.Data.Length, transfer.Transferred, transfer.Size,
// transfer.AssetID.ToString()));
2007-07-13 14:49:36 +00:00
// Check if we downloaded the full asset
2007-09-10 10:20:30 +00:00
if ( download . Transferred > = download . Size )
2007-07-13 14:49:36 +00:00
{
2008-05-06 23:57:26 +00:00
Logger . DebugLog ( "Transfer for asset " + download . AssetID . ToString ( ) + " completed" , Client ) ;
2008-11-10 18:37:25 +00:00
2007-09-10 10:20:30 +00:00
download . Success = true ;
lock ( Transfers ) Transfers . Remove ( download . ID ) ;
2007-07-13 14:49:36 +00:00
2009-07-19 03:38:27 +00:00
// Cache successful asset download
Cache . SaveAssetToCache ( download . AssetID , download . AssetData ) ;
if ( download . Callback ! = null )
2007-07-13 14:49:36 +00:00
{
2009-07-19 03:38:27 +00:00
try { download . Callback ( download , WrapAsset ( download ) ) ; }
2009-10-28 08:01:52 +00:00
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2007-07-13 14:49:36 +00:00
}
}
}
}
2007-11-06 09:26:10 +00:00
#endregion Transfer Callbacks
2007-08-25 09:36:33 +00:00
2007-11-06 09:26:10 +00:00
#region Xfer Callbacks
2007-07-15 06:36:27 +00:00
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void InitiateDownloadPacketHandler ( object sender , PacketReceivedEventArgs e )
2009-03-25 21:39:24 +00:00
{
2010-03-17 14:00:36 +00:00
InitiateDownloadPacket request = ( InitiateDownloadPacket ) e . Packet ;
try
2009-03-25 21:39:24 +00:00
{
2010-03-17 14:00:36 +00:00
OnInitiateDownload ( new InitiateDownloadEventArgs ( Utils . BytesToString ( request . FileData . SimFilename ) ,
Utils . BytesToString ( request . FileData . ViewerFilename ) ) ) ;
}
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2009-03-25 21:39:24 +00:00
}
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void RequestXferHandler ( object sender , PacketReceivedEventArgs e )
2007-07-13 14:49:36 +00:00
{
2007-11-06 09:26:10 +00:00
if ( PendingUpload = = null )
2008-05-06 23:57:26 +00:00
Logger . Log ( "Received a RequestXferPacket for an unknown asset upload" , Helpers . LogLevel . Warning , Client ) ;
2007-11-06 09:26:10 +00:00
else
2007-07-13 14:49:36 +00:00
{
2007-11-06 09:26:10 +00:00
AssetUpload upload = PendingUpload ;
PendingUpload = null ;
2008-07-21 08:51:49 +00:00
WaitingForUploadConfirm = false ;
2009-10-28 08:01:52 +00:00
RequestXferPacket request = ( RequestXferPacket ) e . Packet ;
2007-07-13 14:49:36 +00:00
2007-11-06 09:26:10 +00:00
upload . XferID = request . XferID . ID ;
upload . Type = ( AssetType ) request . XferID . VFileType ;
2007-07-13 14:49:36 +00:00
2008-07-25 05:15:05 +00:00
UUID transferID = new UUID ( upload . XferID ) ;
2007-11-06 09:26:10 +00:00
Transfers [ transferID ] = upload ;
2007-07-13 14:49:36 +00:00
2007-11-06 09:26:10 +00:00
// Send the first packet containing actual asset data
SendNextUploadPacket ( upload ) ;
2007-07-13 14:49:36 +00:00
}
}
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void ConfirmXferPacketHandler ( object sender , PacketReceivedEventArgs e )
2007-07-13 14:49:36 +00:00
{
2009-10-28 08:01:52 +00:00
ConfirmXferPacketPacket confirm = ( ConfirmXferPacketPacket ) e . Packet ;
2007-07-13 14:49:36 +00:00
// Building a new UUID every time an ACK is received for an upload is a horrible
// thing, but this whole Xfer system is horrible
2008-07-25 05:15:05 +00:00
UUID transferID = new UUID ( confirm . XferID . ID ) ;
2007-09-10 10:20:30 +00:00
Transfer transfer ;
2007-07-13 14:49:36 +00:00
AssetUpload upload = null ;
2007-09-10 10:20:30 +00:00
if ( Transfers . TryGetValue ( transferID , out transfer ) )
2007-07-13 14:49:36 +00:00
{
2007-09-10 10:20:30 +00:00
upload = ( AssetUpload ) transfer ;
2007-07-13 14:49:36 +00:00
2007-09-10 10:20:30 +00:00
//Client.DebugLog(String.Format("ACK for upload {0} of asset type {1} ({2}/{3})",
2007-11-30 13:15:31 +00:00
// upload.AssetID.ToString(), upload.Type, upload.Transferred, upload.Size));
2007-07-13 14:49:36 +00:00
2010-03-17 14:00:36 +00:00
try { OnUploadProgress ( new AssetUploadEventArgs ( upload ) ) ; }
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2007-09-10 10:20:30 +00:00
if ( upload . Transferred < upload . Size )
SendNextUploadPacket ( upload ) ;
2007-07-13 14:49:36 +00:00
}
}
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void AssetUploadCompleteHandler ( object sender , PacketReceivedEventArgs e )
2007-07-13 14:49:36 +00:00
{
2009-10-28 08:01:52 +00:00
AssetUploadCompletePacket complete = ( AssetUploadCompletePacket ) e . Packet ;
2007-07-13 14:49:36 +00:00
2008-07-21 08:51:49 +00:00
// If we uploaded an asset in a single packet, RequestXferHandler()
// will never be called so we need to set this here as well
WaitingForUploadConfirm = false ;
2010-03-17 14:00:36 +00:00
if ( m_AssetUploadedEvent ! = null )
2007-07-13 14:49:36 +00:00
{
bool found = false ;
2008-07-25 05:15:05 +00:00
KeyValuePair < UUID , Transfer > foundTransfer = new KeyValuePair < UUID , Transfer > ( ) ;
2007-07-13 14:49:36 +00:00
// Xfer system sucks really really bad. Where is the damn XferID?
lock ( Transfers )
{
2008-07-25 05:15:05 +00:00
foreach ( KeyValuePair < UUID , Transfer > transfer in Transfers )
2007-07-13 14:49:36 +00:00
{
if ( transfer . Value . GetType ( ) = = typeof ( AssetUpload ) )
{
AssetUpload upload = ( AssetUpload ) transfer . Value ;
if ( ( upload ) . AssetID = = complete . AssetBlock . UUID )
{
found = true ;
foundTransfer = transfer ;
upload . Success = complete . AssetBlock . Success ;
upload . Type = ( AssetType ) complete . AssetBlock . Type ;
break ;
}
}
}
}
if ( found )
{
lock ( Transfers ) Transfers . Remove ( foundTransfer . Key ) ;
2010-03-17 14:00:36 +00:00
try { OnAssetUploaded ( new AssetUploadEventArgs ( ( AssetUpload ) foundTransfer . Value ) ) ; }
2009-10-28 08:01:52 +00:00
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2007-07-13 14:49:36 +00:00
}
2008-07-21 08:51:49 +00:00
else
{
Logger . Log ( String . Format (
"Got an AssetUploadComplete on an unrecognized asset, AssetID: {0}, Type: {1}, Success: {2}" ,
complete . AssetBlock . UUID , ( AssetType ) complete . AssetBlock . Type , complete . AssetBlock . Success ) ,
Helpers . LogLevel . Warning ) ;
}
2007-07-13 14:49:36 +00:00
}
}
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void SendXferPacketHandler ( object sender , PacketReceivedEventArgs e )
2007-11-06 09:26:10 +00:00
{
2009-10-28 08:01:52 +00:00
SendXferPacketPacket xfer = ( SendXferPacketPacket ) e . Packet ;
2007-11-06 09:26:10 +00:00
2008-07-28 22:23:48 +00:00
// Lame ulong to UUID conversion, please go away Xfer system
2008-07-25 05:15:05 +00:00
UUID transferID = new UUID ( xfer . XferID . ID ) ;
2007-11-06 09:26:10 +00:00
Transfer transfer ;
XferDownload download = null ;
if ( Transfers . TryGetValue ( transferID , out transfer ) )
{
download = ( XferDownload ) transfer ;
// Apply a mask to get rid of the "end of transfer" bit
uint packetNum = xfer . XferID . Packet & 0x0FFFFFFF ;
// Check for out of order packets, possibly indicating a resend
if ( packetNum ! = download . PacketNum )
{
if ( packetNum = = download . PacketNum - 1 )
{
2008-05-06 23:57:26 +00:00
Logger . DebugLog ( "Resending Xfer download confirmation for packet " + packetNum , Client ) ;
2007-11-06 09:26:10 +00:00
SendConfirmXferPacket ( download . XferID , packetNum ) ;
}
else
{
2008-05-06 23:57:26 +00:00
Logger . Log ( "Out of order Xfer packet in a download, got " + packetNum + " expecting " + download . PacketNum ,
Helpers . LogLevel . Warning , Client ) ;
2008-11-11 23:55:33 +00:00
// Re-confirm the last packet we actually received
SendConfirmXferPacket ( download . XferID , download . PacketNum - 1 ) ;
2007-11-06 09:26:10 +00:00
}
return ;
}
if ( packetNum = = 0 )
{
2008-11-11 22:28:36 +00:00
// This is the first packet received in the download, the first four bytes are a size integer
2008-11-11 23:07:43 +00:00
// in little endian ordering
2008-08-21 21:51:20 +00:00
byte [ ] bytes = xfer . DataPacket . Data ;
2008-11-11 23:07:43 +00:00
download . Size = ( bytes [ 0 ] + ( bytes [ 1 ] < < 8 ) + ( bytes [ 2 ] < < 16 ) + ( bytes [ 3 ] < < 24 ) ) ;
2007-11-06 09:26:10 +00:00
download . AssetData = new byte [ download . Size ] ;
2008-11-11 22:28:36 +00:00
Logger . DebugLog ( "Received first packet in an Xfer download of size " + download . Size ) ;
2007-11-06 09:26:10 +00:00
Buffer . BlockCopy ( xfer . DataPacket . Data , 4 , download . AssetData , 0 , xfer . DataPacket . Data . Length - 4 ) ;
download . Transferred + = xfer . DataPacket . Data . Length - 4 ;
}
else
{
Buffer . BlockCopy ( xfer . DataPacket . Data , 0 , download . AssetData , 1000 * ( int ) packetNum , xfer . DataPacket . Data . Length ) ;
download . Transferred + = xfer . DataPacket . Data . Length ;
}
// Increment the packet number to the packet we are expecting next
download . PacketNum + + ;
// Confirm receiving this packet
SendConfirmXferPacket ( download . XferID , packetNum ) ;
if ( ( xfer . XferID . Packet & 0x80000000 ) ! = 0 )
{
// This is the last packet in the transfer
if ( ! String . IsNullOrEmpty ( download . Filename ) )
2008-05-06 23:57:26 +00:00
Logger . DebugLog ( "Xfer download for asset " + download . Filename + " completed" , Client ) ;
2007-11-06 09:26:10 +00:00
else
2008-05-06 23:57:26 +00:00
Logger . DebugLog ( "Xfer download for asset " + download . VFileID . ToString ( ) + " completed" , Client ) ;
2007-11-06 09:26:10 +00:00
download . Success = true ;
lock ( Transfers ) Transfers . Remove ( download . ID ) ;
2010-03-17 14:00:36 +00:00
try { OnXferReceived ( new XferReceivedEventArgs ( download ) ) ; }
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2007-11-06 09:26:10 +00:00
}
}
}
2009-10-28 08:01:52 +00:00
/// <summary>Process an incoming packet and raise the appropriate events</summary>
/// <param name="sender">The sender</param>
/// <param name="e">The EventArgs object containing the packet data</param>
protected void AbortXferHandler ( object sender , PacketReceivedEventArgs e )
2009-07-09 19:44:02 +00:00
{
2009-10-28 08:01:52 +00:00
AbortXferPacket abort = ( AbortXferPacket ) e . Packet ;
2009-07-09 22:24:04 +00:00
XferDownload download = null ;
2009-07-09 19:44:02 +00:00
// Lame ulong to UUID conversion, please go away Xfer system
UUID transferID = new UUID ( abort . XferID . ID ) ;
lock ( Transfers )
{
2009-07-09 22:24:04 +00:00
Transfer transfer ;
2009-07-09 19:44:02 +00:00
if ( Transfers . TryGetValue ( transferID , out transfer ) )
2009-07-09 22:24:04 +00:00
{
download = ( XferDownload ) transfer ;
2009-07-09 19:44:02 +00:00
Transfers . Remove ( transferID ) ;
2009-07-09 22:24:04 +00:00
}
2009-07-09 19:44:02 +00:00
}
2010-03-17 14:00:36 +00:00
if ( download ! = null & & m_XferReceivedEvent ! = null )
2009-07-09 19:44:02 +00:00
{
download . Success = false ;
download . Error = ( TransferError ) abort . XferID . Result ;
2010-03-17 14:00:36 +00:00
try { OnXferReceived ( new XferReceivedEventArgs ( download ) ) ; }
2009-10-28 08:01:52 +00:00
catch ( Exception ex ) { Logger . Log ( ex . Message , Helpers . LogLevel . Error , Client , ex ) ; }
2009-07-09 19:44:02 +00:00
}
}
2007-11-06 09:26:10 +00:00
#endregion Xfer Callbacks
2007-07-13 14:49:36 +00:00
}
2010-03-17 14:00:36 +00:00
#region EventArg classes
// <summary>Provides data for XferReceived event</summary>
public class XferReceivedEventArgs : EventArgs
{
private readonly XferDownload m_Xfer ;
/// <summary>Xfer data</summary>
public XferDownload Xfer { get { return m_Xfer ; } }
public XferReceivedEventArgs ( XferDownload xfer )
{
this . m_Xfer = xfer ;
}
}
// <summary>Provides data for AssetUploaded event</summary>
public class AssetUploadEventArgs : EventArgs
{
private readonly AssetUpload m_Upload ;
/// <summary>Upload data</summary>
public AssetUpload Upload { get { return m_Upload ; } }
public AssetUploadEventArgs ( AssetUpload upload )
{
this . m_Upload = upload ;
}
}
// <summary>Provides data for InitiateDownloaded event</summary>
public class InitiateDownloadEventArgs : EventArgs
{
private readonly string m_SimFileName ;
private readonly string m_ViewerFileName ;
/// <summary>Filename used on the simulator</summary>
public string SimFileName { get { return m_SimFileName ; } }
/// <summary>Filename used by the client</summary>
public string ViewerFileName { get { return m_ViewerFileName ; } }
public InitiateDownloadEventArgs ( string simFilename , string viewerFilename )
{
this . m_SimFileName = simFilename ;
this . m_ViewerFileName = viewerFilename ;
}
}
// <summary>Provides data for ImageReceiveProgress event</summary>
public class ImageReceiveProgressEventArgs : EventArgs
{
private readonly UUID m_ImageID ;
private readonly int m_Received ;
private readonly int m_Total ;
/// <summary>UUID of the image that is in progress</summary>
public UUID ImageID { get { return m_ImageID ; } }
/// <summary>Number of bytes received so far</summary>
public int Received { get { return m_Received ; } }
/// <summary>Image size in bytes</summary>
public int Total { get { return m_Total ; } }
public ImageReceiveProgressEventArgs ( UUID imageID , int received , int total )
{
this . m_ImageID = imageID ;
this . m_Received = received ;
this . m_Total = total ;
}
}
#endregion
2007-07-13 14:49:36 +00:00
}