From 3ebfbf4b790e847843451f129a7b45f0c61697a9 Mon Sep 17 00:00:00 2001 From: Michael Cortez Date: Sat, 13 Jan 2007 00:23:00 +0000 Subject: [PATCH] Asset System work... Refactoring, documentation, making more sub-class friendly, and oh yeah -- Asset Downloading no longer requires blocking, and timeouts have been added through-out. git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@840 52acb1d6-8a22-11de-b505-999d5b087335 --- .../AssetSystem/AppearanceManager.cs | 160 ++++++++------- libsecondlife-cs/AssetSystem/AssetManager.cs | 175 ++++++++++------- libsecondlife-cs/AssetSystem/AssetRequest.cs | 167 ++++++++++++++++ .../AssetSystem/AssetRequestDownload.cs | 71 ------- .../AssetSystem/AssetRequestUpload.cs | 182 ++++++++---------- .../InventorySystem/InventoryImage.cs | 8 +- .../InventorySystem/InventoryItem.cs | 6 +- .../InventorySystem/InventoryManager.cs | 7 +- .../InventorySystem/InventoryNotecard.cs | 8 +- .../InventorySystem/InventoryScript.cs | 6 +- .../InventorySystem/InventoryWearable.cs | 1 - libsecondlife-cs/libsecondlife.csproj | 2 +- 12 files changed, 468 insertions(+), 325 deletions(-) create mode 100644 libsecondlife-cs/AssetSystem/AssetRequest.cs delete mode 100644 libsecondlife-cs/AssetSystem/AssetRequestDownload.cs diff --git a/libsecondlife-cs/AssetSystem/AppearanceManager.cs b/libsecondlife-cs/AssetSystem/AppearanceManager.cs index 072c3e1f..dd3ec57e 100644 --- a/libsecondlife-cs/AssetSystem/AppearanceManager.cs +++ b/libsecondlife-cs/AssetSystem/AppearanceManager.cs @@ -32,12 +32,12 @@ namespace libsecondlife.AssetSystem Invalid = 255 }; - private SecondLife Client; - private AssetManager AManager; + protected SecondLife Client; + protected AssetManager AManager; - private uint SerialNum = 1; + protected uint SerialNum = 1; - private ManualResetEvent AgentWearablesSignal = null; + protected ManualResetEvent AgentWearablesSignal = null; // This data defines all appearance info for an avatar public AgentWearablesUpdatePacket.WearableDataBlock[] AgentWearablesData; @@ -45,21 +45,21 @@ namespace libsecondlife.AssetSystem public TextureEntry AgentTextureEntry = new TextureEntry("C228D1CF4B5D4BA884F4899A0796AA97"); // if this isn't valid, blame JH ;-) - + /// + /// + /// + /// public AppearanceManager(SecondLife client) { Client = client; AManager = client.Assets; - RegisterCallbacks(); - - } - - private void RegisterCallbacks() - { Client.Network.RegisterCallback(libsecondlife.Packets.PacketType.AgentWearablesUpdate, new NetworkManager.PacketCallback(AgentWearablesUpdateCallbackHandler)); + } + #region Wear Stuff + /// /// Add a single wearable to your outfit, replacing if nessesary. /// @@ -159,10 +159,13 @@ namespace libsecondlife.AssetSystem SendAgentSetAppearance(); } + #endregion + + /// /// Creates and sends an AgentIsNowWearing packet based on the local cached AgentWearablesData array. /// - private void SendAgentIsNowWearing() + protected void SendAgentIsNowWearing() { AgentIsNowWearingPacket nowWearing = new AgentIsNowWearingPacket(); nowWearing.AgentData.AgentID = Client.Network.AgentID; @@ -182,7 +185,7 @@ namespace libsecondlife.AssetSystem /// Request from the server what wearables we're currently wearing. Update cached info. /// /// The wearable info for what we're currently wearing - public AgentWearablesUpdatePacket.WearableDataBlock[] GetWearables() + protected AgentWearablesUpdatePacket.WearableDataBlock[] GetWearables() { AgentWearablesSignal = new ManualResetEvent(false); @@ -199,7 +202,7 @@ namespace libsecondlife.AssetSystem /// /// Update the local Avatar Appearance information based on the contents of the assets as defined in the cached wearable data info. /// - public void GetAvatarAppearanceInfoFromWearableAssets() + protected void GetAvatarAppearanceInfoFromWearableAssets() { // Only request wearable data, if we have to. if ((AgentWearablesData == null) || (AgentWearablesData.Length == 0)) @@ -230,8 +233,12 @@ namespace libsecondlife.AssetSystem break; } - - AManager.GetInventoryAsset(wearableAsset); + AssetRequestDownload request = Client.Assets.RequestInventoryAsset(wearableAsset); + if (request.Wait(AssetManager.DefaultTimeout) != AssetRequestDownload.RequestStatus.Success) + { + throw new Exception("Asset (" + wearableAsset.AssetID.ToStringHyphenated() + ") unavailable (" + request.StatusMsg + ")"); + } + if ((wearableAsset.AssetData == null) || (wearableAsset.AssetData.Length == 0)) { Client.Log("Asset retrieval failed for AssetID: " + wearableAsset.AssetID, Helpers.LogLevel.Warning); @@ -312,60 +319,6 @@ namespace libsecondlife.AssetSystem AgentTextureEntry = te2; } - /// - /// Convert the morph params as they are stored in assets, to the byte values needed for - /// AgentSetAppearance packet - /// - /// Visual Param information for AgentSetAppearance packets - private Dictionary GetAssetParamsAsVisualParams() - { - Dictionary VisualParams = new Dictionary(); - - float maxVal = 0; - float minVal = 0; - uint packetIdx = 0; - float range = 0; - float percentage = 0; - byte packetVal = 0; - - foreach (KeyValuePair kvp in AgentAppearanceParams) - { - packetIdx = AppearanceManager.GetAgentSetAppearanceIndex(kvp.Key) - 1; //TODO/FIXME: this should be zero indexed, not 1 based. - maxVal = BodyShapeParams.GetValueMax(kvp.Key); - minVal = BodyShapeParams.GetValueMin(kvp.Key); - - range = maxVal - minVal; - - percentage = (kvp.Value - minVal) / range; - - packetVal = (byte)(percentage * (byte)255); - - VisualParams[packetIdx] = packetVal; - - } - - return VisualParams; - } - - /// - /// Determine agent size for AgentSetAppearance based on Visual Param data. - /// - /// - /// - private LLVector3 GetAgentSizeFromVisualParams(Dictionary VisualParams) - { - if (VisualParams.ContainsKey(25)) - { - float AV_Height_Range = 2.025506f - 1.50856f; - float AV_Height = 1.50856f + (((float)VisualParams[25] / 255.0f) * AV_Height_Range); - return new LLVector3(0.45f, 0.6f, AV_Height); - } - else - { - return new LLVector3(0.45f, 0.6f, 1.0f); - } - } - /// /// Send an AgentSetAppearance packet to the server to update your appearance. /// @@ -420,6 +373,63 @@ namespace libsecondlife.AssetSystem } + /// + /// Convert the morph params as they are stored in assets, to the byte values needed for + /// AgentSetAppearance packet + /// + /// Visual Param information for AgentSetAppearance packets + protected Dictionary GetAssetParamsAsVisualParams() + { + Dictionary VisualParams = new Dictionary(); + + float maxVal = 0; + float minVal = 0; + uint packetIdx = 0; + float range = 0; + float percentage = 0; + byte packetVal = 0; + + foreach (KeyValuePair kvp in AgentAppearanceParams) + { + packetIdx = AppearanceManager.GetAgentSetAppearanceIndex(kvp.Key) - 1; //TODO/FIXME: this should be zero indexed, not 1 based. + maxVal = BodyShapeParams.GetValueMax(kvp.Key); + minVal = BodyShapeParams.GetValueMin(kvp.Key); + + range = maxVal - minVal; + + percentage = (kvp.Value - minVal) / range; + + packetVal = (byte)(percentage * (byte)255); + + VisualParams[packetIdx] = packetVal; + + } + + return VisualParams; + } + + /// + /// Determine agent size for AgentSetAppearance based on Visual Param data. + /// + /// + /// + protected LLVector3 GetAgentSizeFromVisualParams(Dictionary VisualParams) + { + if (VisualParams.ContainsKey(25)) + { + float AV_Height_Range = 2.025506f - 1.50856f; + float AV_Height = 1.50856f + (((float)VisualParams[25] / 255.0f) * AV_Height_Range); + return new LLVector3(0.45f, 0.6f, AV_Height); + } + else + { + return new LLVector3(0.45f, 0.6f, 1.0f); + } + } + + + + #region Callback Handlers private void AgentWearablesUpdateCallbackHandler(Packet packet, Simulator simulator) { @@ -429,12 +439,16 @@ namespace libsecondlife.AssetSystem AgentWearablesSignal.Set(); } + #endregion + + #region Lookup Tables + /// /// Convert a Visual Params index number from the ParamID provided in Assets and from avatar_lad.xml /// /// /// - private static uint GetAgentSetAppearanceIndex(uint AssetParamID) + protected static uint GetAgentSetAppearanceIndex(uint AssetParamID) { switch (AssetParamID) { @@ -667,7 +681,7 @@ namespace libsecondlife.AssetSystem /// /// /// - public static uint GetParamID(uint VisualParamIdx) + protected static uint GetParamID(uint VisualParamIdx) { switch (VisualParamIdx) { @@ -894,5 +908,7 @@ namespace libsecondlife.AssetSystem throw new Exception("Unknown Visual Param (AgentSetApperance) index: " + VisualParamIdx); } } + + #endregion } } diff --git a/libsecondlife-cs/AssetSystem/AssetManager.cs b/libsecondlife-cs/AssetSystem/AssetManager.cs index 11a80642..6c636835 100644 --- a/libsecondlife-cs/AssetSystem/AssetManager.cs +++ b/libsecondlife-cs/AssetSystem/AssetManager.cs @@ -73,10 +73,19 @@ namespace libsecondlife.AssetSystem /// public class AssetManager { - private SecondLife slClient; + protected SecondLife slClient; - private AssetRequestUpload curUploadRequest = null; - private Dictionary htDownloadRequests = new Dictionary(); + protected AssetRequestUpload curUploadRequest = null; + protected Dictionary htDownloadRequests = new Dictionary(); + + public readonly static int DefaultTimeout = 15000; + + /// + /// Event singaling an asset transfer request has completed. + /// + /// + public delegate void On_TransferRequestCompleted(AssetRequest request); + public event On_TransferRequestCompleted TransferRequestCompletedEvent; /// /// @@ -97,31 +106,27 @@ namespace libsecondlife.AssetSystem // XFer packets for uploading large assets slClient.Network.RegisterCallback(PacketType.ConfirmXferPacket, new NetworkManager.PacketCallback(ConfirmXferPacketCallbackHandler)); slClient.Network.RegisterCallback(PacketType.RequestXfer, new NetworkManager.PacketCallback(RequestXferCallbackHandler)); - } + } - void Network_OnConnected(object sender) + #region State Handling + private void Network_OnConnected(object sender) { ClearState(); } - void Network_OnDisconnected(NetworkManager.DisconnectType reason, string message) + private void Network_OnDisconnected(NetworkManager.DisconnectType reason, string message) { ClearState(); } - private void ClearState() + protected void ClearState() { htDownloadRequests.Clear(); curUploadRequest = null; } + #endregion - /// - /// Handle the appropriate sink fee associated with an asset upload - /// - public void SinkFee() - { - slClient.Self.GiveMoney(LLUUID.Zero, slClient.Settings.UPLOAD_COST, "Image Upload"); - } + #region Asset Uploading /// /// Upload an asset to Second Life @@ -150,13 +155,25 @@ namespace libsecondlife.AssetSystem { curUploadRequest = null; } - } + } + + /// + /// Handle the appropriate sink fee associated with an asset upload + /// + protected void SinkFee() + { + slClient.Self.GiveMoney(LLUUID.Zero, slClient.Settings.UPLOAD_COST, "Image Upload"); + } + + #endregion + + #region Download Assets /// /// Get the Asset data for an item, must be used when requesting a Notecard /// /// - public void GetInventoryAsset( InventoryItem item ) + public AssetRequestDownload RequestInventoryAsset(InventoryItem item) { if ( (item.OwnerMask & (uint)AssetPermission.Copy) == 0 ) throw new AssetPermissionException(item, slClient, "Asset data refused, Copy permission needed."); @@ -165,9 +182,7 @@ namespace libsecondlife.AssetSystem LLUUID TransferID = LLUUID.Random(); - AssetRequestDownload request = new AssetRequestDownload(TransferID); - request.Size = int.MaxValue; // Number of bytes expected - request.Received = 0; // Number of bytes received + AssetRequestDownload request = new AssetRequestDownload(slClient.Assets, TransferID, item._Asset); request.UpdateLastPacketTime(); // last time we recevied a packet for this request htDownloadRequests[TransferID] = request; @@ -189,25 +204,21 @@ namespace libsecondlife.AssetSystem slClient.Network.SendPacket(packet); #if DEBUG_PACKETS - slClient.DebugLog(packet); + slClient.Log(packet.ToString(), Helpers.LogLevel.Info); #endif - request.Completed.WaitOne(); - - item.SetAssetData(request.AssetData); - } + return request; + } /// /// Get Asset data, works with BodyShapes (type 13) but does not work with Notecards(type 7) /// /// - public void GetInventoryAsset(Asset asset) + public AssetRequestDownload RequestInventoryAsset(Asset asset) { LLUUID TransferID = LLUUID.Random(); - AssetRequestDownload request = new AssetRequestDownload(TransferID); - request.Size = int.MaxValue; // Number of bytes expected - request.Received = 0; // Number of bytes received + AssetRequestDownload request = new AssetRequestDownload(slClient.Assets, TransferID, asset); request.UpdateLastPacketTime(); // last time we recevied a packet for this request htDownloadRequests[TransferID] = request; @@ -216,31 +227,69 @@ namespace libsecondlife.AssetSystem slClient.Network.SendPacket(packet); #if DEBUG_PACKETS - slClient.DebugLog(packet); + slClient.Log(packet.ToString(), Helpers.LogLevel.Info); #endif - request.Completed.WaitOne(); + return request; - asset.SetAssetData(request.AssetData); - - } + } + #endregion + + #region Event Generation + internal void FireTransferRequestCompletedEvent(AssetRequest request) + { + TransferRequestCompletedEvent(request); + } + #endregion + + #region Deprecated Methods + /// + /// Get the Asset data for an item, must be used when requesting a Notecard + /// + /// + [Obsolete("Use RequestInventoryAsset instead.", false)] + public void GetInventoryAsset(InventoryItem item) + { + RequestInventoryAsset(item).Wait(-1); + } + + /// + /// Get Asset data, works with BodyShapes (type 13) but does not work with Notecards(type 7) + /// + /// + [Obsolete("Use RequestInventoryAsset instead.", false)] + public void GetInventoryAsset(Asset asset) + { + RequestInventoryAsset(asset).Wait(-1); + } + #endregion + + #region Callback Handlers private void AssetUploadCompleteCallbackHandler(Packet packet, Simulator simulator) { #if DEBUG_PACKETS - slClient.DebugLog(packet); + slClient.Log(packet.ToString(), Helpers.LogLevel.Info); #endif Packets.AssetUploadCompletePacket reply = (AssetUploadCompletePacket)packet; + if (reply.AssetBlock.Success) + { + curUploadRequest.UploadComplete(reply.AssetBlock.UUID, AssetRequest.RequestStatus.Success); + } + else + { + curUploadRequest.UploadComplete(reply.AssetBlock.UUID, AssetRequest.RequestStatus.Failure); + } - curUploadRequest.UploadComplete(reply.AssetBlock.UUID, reply.AssetBlock.Success); + TransferRequestCompletedEvent(curUploadRequest); } private void RequestXferCallbackHandler(Packet packet, Simulator simulator) { #if DEBUG_PACKETS - slClient.DebugLog(packet); + slClient.Log(packet.ToString(), Helpers.LogLevel.Info); #endif RequestXferPacket reply = (RequestXferPacket)packet; @@ -254,7 +303,7 @@ namespace libsecondlife.AssetSystem private void ConfirmXferPacketCallbackHandler(Packet packet, Simulator simulator) { #if DEBUG_PACKETS - slClient.DebugLog(packet); + slClient.Log(packet.ToString(), Helpers.LogLevel.Info); #endif ConfirmXferPacketPacket reply = (ConfirmXferPacketPacket)packet; @@ -263,10 +312,11 @@ namespace libsecondlife.AssetSystem } + // Download stuff private void TransferInfoCallbackHandler(Packet packet, Simulator simulator) { #if DEBUG_PACKETS - slClient.DebugLog(packet); + slClient.Log(packet.ToString(), Helpers.LogLevel.Info); #endif TransferInfoPacket reply = (TransferInfoPacket)packet; @@ -276,33 +326,29 @@ namespace libsecondlife.AssetSystem int Status = reply.TransferInfo.Status; // Lookup the request for this packet - AssetRequestDownload request = htDownloadRequests[TransferID]; - if (request == null) + if (!htDownloadRequests.ContainsKey(TransferID)) { + slClient.Log("Received unexpected TransferInfo packet." + Environment.NewLine + packet.ToString(), Helpers.LogLevel.Warning); return; } + AssetRequestDownload request = htDownloadRequests[TransferID]; // Mark it as either not found or update the request information if (Status == -2) { - request.Status = false; - request.StatusMsg = "Asset Status -2 :: Likely Status Not Found"; - - request.Size = 0; - request.AssetData = new byte[0]; - request.Completed.Set(); + request.SetExpectedSize(Size); + request.Fail("Asset Status -2 :: Likely Status Not Found"); } else { - request.Size = Size; - request.AssetData = new byte[Size]; + request.SetExpectedSize(Size); } } private void TransferPacketCallbackHandler(Packet packet, Simulator simulator) { #if DEBUG_PACKETS - slClient.DebugLog(packet); + slClient.Log(packet.ToString(), Helpers.LogLevel.Info); #endif TransferPacketPacket reply = (TransferPacketPacket)packet; @@ -311,31 +357,18 @@ namespace libsecondlife.AssetSystem byte[] Data = reply.TransferData.Data; - // Append data to data received. - AssetRequestDownload request = htDownloadRequests[TransferID]; - if (request == null) + // Lookup the request for this packet + if (!htDownloadRequests.ContainsKey(TransferID)) { + slClient.Log("Received unexpected TransferPacket packet." + Environment.NewLine + packet.ToString(), Helpers.LogLevel.Warning); return; } + AssetRequestDownload request = htDownloadRequests[TransferID]; - // Add data to data dictionary. - request.AssetDataReceived[reply.TransferData.Packet] = Data; - request.Received += Data.Length; - - - - // If we've gotten all the data, mark it completed. - if (request.Received >= request.Size) - { - int curPos = 0; - foreach (KeyValuePair kvp in request.AssetDataReceived) - { - Array.Copy(kvp.Value, 0, request.AssetData, curPos, kvp.Value.Length); - curPos += kvp.Value.Length; - } - - request.Completed.Set(); - } + // Append data to data received. + request.AddDownloadedData(reply.TransferData.Packet, Data); } - } + + #endregion + } } diff --git a/libsecondlife-cs/AssetSystem/AssetRequest.cs b/libsecondlife-cs/AssetSystem/AssetRequest.cs new file mode 100644 index 00000000..994aa1e2 --- /dev/null +++ b/libsecondlife-cs/AssetSystem/AssetRequest.cs @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006, Second Life Reverse Engineering Team + * 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 Second Life Reverse Engineering Team 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. + */ + +//#define DEBUG_PACKETS + +using System; +using System.Collections.Generic; + +using libsecondlife; + +using libsecondlife.InventorySystem; + +using libsecondlife.Packets; +using System.Threading; + +namespace libsecondlife.AssetSystem +{ + public class AssetRequest + { + public enum RequestStatus { Success, Failure }; + + protected AssetManager _AssetManager; + + protected LLUUID _TransactionID; + public Asset AssetBeingTransferd; + + + protected ManualResetEvent _Completed = new ManualResetEvent(false); + + protected RequestStatus _Status; + public RequestStatus Status + { + get { return _Status; } + } + + protected string _StatusMsg = ""; + public string StatusMsg + { + get { return _StatusMsg; } + } + + protected int _Size; + protected byte[] _AssetData; + + protected uint _LastPacketTime; + public uint LastPacketTime { get { return _LastPacketTime; } } + + public uint SecondsSinceLastPacket { get { return Helpers.GetUnixTime() - _LastPacketTime; } } + + public AssetRequest(AssetManager Manager, LLUUID TransID, Asset Asset) + { + _AssetManager = Manager; + _TransactionID = TransID; + AssetBeingTransferd = Asset; + + _Size = int.MaxValue; + + UpdateLastPacketTime(); + } + + public void UpdateLastPacketTime() + { + _LastPacketTime = Helpers.GetUnixTime(); + } + + /// + /// Wait for this Request to be completed. + /// + /// milliseconds to wait, -1 to wait indefinitely + /// + public RequestStatus Wait(int timeout) + { + if (!_Completed.WaitOne(timeout, false)) + { + _StatusMsg += "Timeout Failure"; + return RequestStatus.Failure; + } + else + { + return _Status; + } + } + + protected void MarkCompleted(RequestStatus status, string status_msg) + { + _StatusMsg += status_msg; + _Status = status; + + _Completed.Set(); + + _AssetManager.FireTransferRequestCompletedEvent(this); + } + + internal void Fail(string status_msg) + { + MarkCompleted(RequestStatus.Failure, status_msg); + } + + } + + + public class AssetRequestDownload : AssetRequest + { + protected int _Received; + protected SortedList _AssetDataReceived = new SortedList(); + + public AssetRequestDownload(AssetManager Manager, LLUUID TransID, Asset Asset2Update) + : base(Manager, TransID, Asset2Update) + { + _Received = 0; + } + + internal void AddDownloadedData(int packetNum, byte[] data) + { + if (!_AssetDataReceived.ContainsKey(packetNum)) + { + _AssetDataReceived[packetNum] = data; + _Received += data.Length; + } + + // If we've gotten all the data, mark it completed. + if (_Received >= _Size) + { + int curPos = 0; + foreach (KeyValuePair kvp in _AssetDataReceived) + { + Array.Copy(kvp.Value, 0, _AssetData, curPos, kvp.Value.Length); + curPos += kvp.Value.Length; + } + + AssetBeingTransferd.SetAssetData(_AssetData); + + MarkCompleted(AssetRequestDownload.RequestStatus.Success, "Download Completed"); + } + + } + + internal void SetExpectedSize(int size) + { + _Size = size; + _AssetData = new byte[_Size]; + } + } +} diff --git a/libsecondlife-cs/AssetSystem/AssetRequestDownload.cs b/libsecondlife-cs/AssetSystem/AssetRequestDownload.cs deleted file mode 100644 index e0aaed21..00000000 --- a/libsecondlife-cs/AssetSystem/AssetRequestDownload.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2006, Second Life Reverse Engineering Team - * 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 Second Life Reverse Engineering Team 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. - */ - -//#define DEBUG_PACKETS - -using System; -using System.Collections.Generic; - -using libsecondlife; - -using libsecondlife.InventorySystem; - -using libsecondlife.Packets; -using System.Threading; - -namespace libsecondlife.AssetSystem -{ - class AssetRequestDownload - { - public LLUUID TransactionID; - - public ManualResetEvent Completed = new ManualResetEvent(false); - public bool Status; - public string StatusMsg; - - public int Size; - public int Received; - public byte[] AssetData; - public SortedList AssetDataReceived = new SortedList(); - - private uint _LastPacketTime; - public uint LastPacketTime { get { return _LastPacketTime; } } - - public uint SecondsSinceLastPacket { get { return Helpers.GetUnixTime() - _LastPacketTime; } } - - public AssetRequestDownload(LLUUID TransID) - { - - TransactionID = TransID; - UpdateLastPacketTime(); - } - - public void UpdateLastPacketTime() - { - _LastPacketTime = Helpers.GetUnixTime(); - } - } -} diff --git a/libsecondlife-cs/AssetSystem/AssetRequestUpload.cs b/libsecondlife-cs/AssetSystem/AssetRequestUpload.cs index 9f6c237c..ec8a0db6 100644 --- a/libsecondlife-cs/AssetSystem/AssetRequestUpload.cs +++ b/libsecondlife-cs/AssetSystem/AssetRequestUpload.cs @@ -39,109 +39,87 @@ using System.Threading; namespace libsecondlife.AssetSystem { - class AssetRequestUpload + public class AssetRequestUpload : AssetRequest { - public ManualResetEvent Completed = new ManualResetEvent(false); - public bool Status; - public string StatusMsg; + protected SecondLife _Client; + protected readonly int _MaxResendAttempts = 20; - public Asset MyAsset; + protected ulong _XferID; - public LLUUID TransactionID; - public ulong XferID; + protected int _ResendCount; + protected uint _CurrentPacket; - SecondLife slClient; + protected int _NumPackets2Send; - public int resendCount; - public uint CurrentPacket; - private uint _LastPacketTime; - public uint LastPacketTime + public AssetRequestUpload(SecondLife Client, LLUUID TransID, Asset Asset2Upload) : base(Client.Assets, TransID, Asset2Upload) { - get { return _LastPacketTime; } - } - - public uint SecondsSinceLastPacket - { - get { return Helpers.GetUnixTime() - _LastPacketTime; } - } - - private int _NumPackets; - public int NumPackets { get { return _NumPackets; } } - - public AssetRequestUpload(SecondLife slClient, LLUUID TransID, Asset asset) - { - this.slClient = slClient; - TransactionID = TransID; - UpdateLastPacketTime(); - - MyAsset = asset; - - CurrentPacket = 0; - resendCount = 0; - _NumPackets = asset._AssetData.Length / 1000; - if (_NumPackets < 1) + if ((AssetBeingTransferd._AssetData == null) || (AssetBeingTransferd._AssetData.Length == 0)) { - _NumPackets = 1; + throw new Exception("Asset data cannot be null."); + } + + _Client = Client; + + _CurrentPacket = 0; + _ResendCount = 0; + _NumPackets2Send = AssetBeingTransferd._AssetData.Length / 1000; + if (_NumPackets2Send < 1) + { + _NumPackets2Send = 1; } } internal LLUUID DoUpload() { - this.SendFirstPacket(); + SendFirstPacket(); - while (this.Completed.WaitOne(1000, true) == false && this.resendCount < 20) // only resend 20 times + while ((_Completed.WaitOne(1000, true) == false) && (_ResendCount < _MaxResendAttempts)) { if (this.SecondsSinceLastPacket > 2) { - slClient.Log("Resending Packet (more than 2 seconds since last confirm)", Helpers.LogLevel.Info); + _Client.Log("Resending Packet (more than 2 seconds since last confirm)", Helpers.LogLevel.Info); this.SendCurrentPacket(); - resendCount++; + _ResendCount++; } } - if (this.Status == false) + if (_Status == RequestStatus.Failure) { - throw new Exception(this.StatusMsg); + throw new Exception(_StatusMsg); } else { - return this.TransactionID; + return _TransactionID; } } - public void UpdateLastPacketTime() - { - _LastPacketTime = Helpers.GetUnixTime(); - } - - - internal void SendFirstPacket() + protected void SendFirstPacket() { Packet packet; - if (this.MyAsset._AssetData.Length > 1000) + if (AssetBeingTransferd._AssetData.Length > 1000) { - packet = AssetPacketHelpers.AssetUploadRequestHeaderOnly(this.MyAsset, this.TransactionID); + packet = AssetPacketHelpers.AssetUploadRequestHeaderOnly(AssetBeingTransferd, _TransactionID); } else { - packet = AssetPacketHelpers.AssetUploadRequest(this.MyAsset, this.TransactionID); + packet = AssetPacketHelpers.AssetUploadRequest(AssetBeingTransferd, _TransactionID); } - slClient.Network.SendPacket(packet); + _Client.Network.SendPacket(packet); #if DEBUG_PACKETS slClient.DebugLog(packet); #endif #if DEBUG_HEADERS - slClient.DebugLog(packet.Header.ToString()); + _Client.DebugLog(packet.Header.ToString()); #endif } internal void RequestXfer(ulong XferID) { - this.XferID = XferID; + _XferID = XferID; // Setup to send the first packet SendCurrentPacket(); } @@ -151,11 +129,11 @@ namespace libsecondlife.AssetSystem // TODO should check that this is the same transfer? this.UpdateLastPacketTime(); - if (PacketNumConfirmed == CurrentPacket) + if (PacketNumConfirmed == _CurrentPacket) { // Increment Packet # - this.CurrentPacket++; - this.resendCount = 0; + this._CurrentPacket++; + this._ResendCount = 0; SendCurrentPacket(); } else @@ -164,72 +142,72 @@ namespace libsecondlife.AssetSystem } } - private void SendCurrentPacket() + protected void SendCurrentPacket() { Packet uploadPacket; - // technically we don't need this lock, because no state is updated here! - // lock (this) + // THREADING: snapshot this num so we use a consistent value throughout + uint packetNum = _CurrentPacket; + if (packetNum == 0) { - // THREADING: snapshot this num so we use a consistent value throughout - uint packetNum = CurrentPacket; - if (packetNum == 0) + if (AssetBeingTransferd._AssetData.Length <= 1000) { - if (MyAsset._AssetData.Length <= 1000) - throw new Exception("Should not use xfer for small assets"); - int dataSize = 1000; - - byte[] packetData = new byte[dataSize + 4]; // Extra space is for leading data length bytes - - // Prefix the first Xfer packet with the data length - // FIXME: Apply endianness patch - Array.Copy(BitConverter.GetBytes((int)MyAsset._AssetData.Length), 0, packetData, 0, 4); - Array.Copy(MyAsset._AssetData, 0, packetData, 4, dataSize); - - uploadPacket = AssetPacketHelpers.SendXferPacket(XferID, packetData, packetNum); + throw new Exception("Should not use xfer for small assets"); } - else if (packetNum < this.NumPackets) - { - byte[] packetData = new byte[1000]; - Array.Copy(this.MyAsset._AssetData, packetNum * 1000, packetData, 0, 1000); + int dataSize = 1000; - uploadPacket = AssetPacketHelpers.SendXferPacket(this.XferID, packetData, packetNum); - } - else - { - // The last packet has to be handled slightly differently - int lastLen = this.MyAsset._AssetData.Length - (this.NumPackets * 1000); - byte[] packetData = new byte[lastLen]; - Array.Copy(this.MyAsset._AssetData, this.NumPackets * 1000, packetData, 0, lastLen); + byte[] packetData = new byte[dataSize + 4]; // Extra space is for leading data length bytes - uint lastPacket = (uint)int.MaxValue + (uint)this.NumPackets + (uint)1; - uploadPacket = AssetPacketHelpers.SendXferPacket(this.XferID, packetData, lastPacket); - } + // Prefix the first Xfer packet with the data length + // FIXME: Apply endianness patch + Array.Copy(BitConverter.GetBytes((int)AssetBeingTransferd._AssetData.Length), 0, packetData, 0, 4); + Array.Copy(AssetBeingTransferd._AssetData, 0, packetData, 4, dataSize); + + uploadPacket = AssetPacketHelpers.SendXferPacket(_XferID, packetData, packetNum); + } + else if (packetNum < _NumPackets2Send) + { + byte[] packetData = new byte[1000]; + Array.Copy(AssetBeingTransferd._AssetData, packetNum * 1000, packetData, 0, 1000); + + uploadPacket = AssetPacketHelpers.SendXferPacket(_XferID, packetData, packetNum); + } + else + { + // The last packet has to be handled slightly differently + int lastLen = this.AssetBeingTransferd._AssetData.Length - (_NumPackets2Send * 1000); + byte[] packetData = new byte[lastLen]; + Array.Copy(this.AssetBeingTransferd._AssetData, _NumPackets2Send * 1000, packetData, 0, lastLen); + + uint lastPacket = (uint)int.MaxValue + (uint)_NumPackets2Send + (uint)1; + uploadPacket = AssetPacketHelpers.SendXferPacket(_XferID, packetData, lastPacket); } - slClient.Network.SendPacket(uploadPacket); + _Client.Network.SendPacket(uploadPacket); #if DEBUG_PACKETS slClient.DebugLog(uploadPacket); #endif #if DEBUG_HEADERS - slClient.DebugLog(uploadPacket.Header.ToString()); + _Client.DebugLog(uploadPacket.Header.ToString()); #endif } - internal void UploadComplete(LLUUID assetID, bool success) + internal void UploadComplete(LLUUID assetID, RequestStatus success) { - MyAsset.AssetID = assetID; - this.Status = success; + AssetBeingTransferd.AssetID = assetID; UpdateLastPacketTime(); + _Client.Log("Upload complete", Helpers.LogLevel.Info); - if (Status) - StatusMsg = "Success"; + if (_Status == RequestStatus.Success) + { + MarkCompleted(success, "Success"); + } else - StatusMsg = "Server returned failed"; + { + MarkCompleted(success, "Server returned failed"); + } - slClient.Log("Upload complete", Helpers.LogLevel.Info); - Completed.Set(); } } } diff --git a/libsecondlife-cs/InventorySystem/InventoryImage.cs b/libsecondlife-cs/InventorySystem/InventoryImage.cs index 0016bf18..877ae610 100644 --- a/libsecondlife-cs/InventorySystem/InventoryImage.cs +++ b/libsecondlife-cs/InventorySystem/InventoryImage.cs @@ -22,8 +22,12 @@ namespace libsecondlife.InventorySystem { if ((AssetID != null) && (AssetID != LLUUID.Zero)) { - base.iManager.AssetManager.GetInventoryAsset( this ); - return ((AssetImage)Asset).J2CData; + AssetRequestDownload request = base.iManager.AssetManager.RequestInventoryAsset(this); + if (request.Wait(AssetManager.DefaultTimeout) != AssetRequestDownload.RequestStatus.Success) + { + throw new Exception("Asset (" + AssetID.ToStringHyphenated() + ") unavailable (" + request.StatusMsg + ") for " + this.Name); + } + return ((AssetImage)Asset).J2CData; } } diff --git a/libsecondlife-cs/InventorySystem/InventoryItem.cs b/libsecondlife-cs/InventorySystem/InventoryItem.cs index 146aa7bd..1974fa86 100644 --- a/libsecondlife-cs/InventorySystem/InventoryItem.cs +++ b/libsecondlife-cs/InventorySystem/InventoryItem.cs @@ -130,7 +130,11 @@ namespace libsecondlife.InventorySystem { if ((AssetID != null) && (AssetID != LLUUID.Zero)) { - base.iManager.AssetManager.GetInventoryAsset(this); + AssetRequestDownload request = base.iManager.AssetManager.RequestInventoryAsset(this); + if (request.Wait(AssetManager.DefaultTimeout) != AssetRequestDownload.RequestStatus.Success) + { + throw new Exception("Asset (" + AssetID.ToStringHyphenated() + ") unavailable (" + request.StatusMsg + ") for " + this.Name); + } return Asset; } } diff --git a/libsecondlife-cs/InventorySystem/InventoryManager.cs b/libsecondlife-cs/InventorySystem/InventoryManager.cs index b5c0fd3d..992a1d60 100644 --- a/libsecondlife-cs/InventorySystem/InventoryManager.cs +++ b/libsecondlife-cs/InventorySystem/InventoryManager.cs @@ -697,6 +697,11 @@ namespace libsecondlife.InventorySystem } } + protected void FireRequestDownloadFinishedEvent(object o, EventArgs e) + { + RequestDownloadFinishedEvent(o, e); + } + #endregion #region libsecondlife callback handlers @@ -948,7 +953,7 @@ namespace libsecondlife.InventorySystem { DownloadRequest_EventArgs e = new DownloadRequest_EventArgs(); e.DownloadRequest = dr; - RequestDownloadFinishedEvent(InvFolderUpdating, e); + FireRequestDownloadFinishedEvent(InvFolderUpdating, e); } } } diff --git a/libsecondlife-cs/InventorySystem/InventoryNotecard.cs b/libsecondlife-cs/InventorySystem/InventoryNotecard.cs index 47883dd0..77684e18 100644 --- a/libsecondlife-cs/InventorySystem/InventoryNotecard.cs +++ b/libsecondlife-cs/InventorySystem/InventoryNotecard.cs @@ -20,8 +20,12 @@ namespace libsecondlife.InventorySystem } else { if ((AssetID != null) && (AssetID != LLUUID.Zero)) { - base.iManager.AssetManager.GetInventoryAsset( this ); - return ((AssetNotecard)Asset).Body; + AssetRequestDownload request = base.iManager.AssetManager.RequestInventoryAsset(this); + if (request.Wait(AssetManager.DefaultTimeout) != AssetRequestDownload.RequestStatus.Success) + { + throw new Exception("Asset (" + AssetID.ToStringHyphenated() + ") unavailable (" + request.StatusMsg + ") for " + this.Name); + } + return ((AssetNotecard)Asset).Body; } } diff --git a/libsecondlife-cs/InventorySystem/InventoryScript.cs b/libsecondlife-cs/InventorySystem/InventoryScript.cs index 7c0b8228..9438f70d 100644 --- a/libsecondlife-cs/InventorySystem/InventoryScript.cs +++ b/libsecondlife-cs/InventorySystem/InventoryScript.cs @@ -19,7 +19,11 @@ namespace libsecondlife.InventorySystem { if ( (AssetID != null) && (AssetID != LLUUID.Zero) ) { - base.iManager.AssetManager.GetInventoryAsset(this); + AssetRequestDownload request = base.iManager.AssetManager.RequestInventoryAsset(this); + if (request.Wait(AssetManager.DefaultTimeout) != AssetRequestDownload.RequestStatus.Success) + { + throw new Exception("Asset (" + AssetID.ToStringHyphenated() + ") unavailable (" + request.StatusMsg + ") for " + this.Name); + } return ((AssetScript)Asset).Source; } } diff --git a/libsecondlife-cs/InventorySystem/InventoryWearable.cs b/libsecondlife-cs/InventorySystem/InventoryWearable.cs index e141a68b..48d23d28 100644 --- a/libsecondlife-cs/InventorySystem/InventoryWearable.cs +++ b/libsecondlife-cs/InventorySystem/InventoryWearable.cs @@ -11,7 +11,6 @@ namespace libsecondlife.InventorySystem public class InventoryWearable : InventoryItem { // Wearable Inventory/Asset constants - public enum WearableAssetType { Shape = 0, Skin = 1, Hair = 2, Eyes = 3, Socks = 7, Gloves = 9, Pants = 5, Shirt = 4, Shoes = 6, Jacket = 8, Skirt = 12, Underpants = 11 }; public enum WearableType : sbyte { Clothing = 5, Body = 13 }; diff --git a/libsecondlife-cs/libsecondlife.csproj b/libsecondlife-cs/libsecondlife.csproj index ce92fef9..88f92109 100644 --- a/libsecondlife-cs/libsecondlife.csproj +++ b/libsecondlife-cs/libsecondlife.csproj @@ -97,12 +97,12 @@ - +