From 16d61c2cd2215497396ff7040e2a07c46ab5f910 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Mon, 5 May 2008 23:41:41 +0000 Subject: [PATCH] [libsecondlife] * Rewrote CapsBase to reimplement the needed functions in WebClient instead of inheriting * Fixed some bugs in CapsClient with the wrong callbacks being hooked up * InventoryManager.RequestCreateItemFromAsset() signature changed to add a progress callback * Updated example code to match new RequestCreateItemFromAsset() [primpreview] * Changed FRONT_AND_BACK rendering to FRONT only to speed things up * Only run picking code for left-clicks git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@1827 52acb1d6-8a22-11de-b505-999d5b087335 --- SLImageUpload/frmSLImageUpload.cs | 29 +- importprimscript/importprimscript.cs | 7 + libsecondlife/Capabilities/CapsBase.cs | 496 +++++++++++++++++- libsecondlife/Capabilities/CapsClient.cs | 33 +- .../Capabilities/EventQueueClient.cs | 8 +- libsecondlife/InventoryManager.cs | 15 +- libsecondlife/Login.cs | 17 +- .../Commands/Inventory/UploadImageCommand.cs | 11 +- libsecondlife/libsecondlife.csproj | 1 - 9 files changed, 556 insertions(+), 61 deletions(-) diff --git a/SLImageUpload/frmSLImageUpload.cs b/SLImageUpload/frmSLImageUpload.cs index 1f17e23b..67777725 100644 --- a/SLImageUpload/frmSLImageUpload.cs +++ b/SLImageUpload/frmSLImageUpload.cs @@ -7,6 +7,7 @@ using System.Text; using System.Windows.Forms; using System.Threading; using libsecondlife; +using libsecondlife.Capabilities; namespace SLImageUpload { @@ -29,21 +30,22 @@ namespace SLImageUpload { Client = new SecondLife(); Client.Network.OnEventQueueRunning += new NetworkManager.EventQueueRunningCallback(Network_OnEventQueueRunning); - Client.Assets.OnUploadProgress += new AssetManager.UploadProgressCallback(Assets_OnUploadProgress); Client.Network.OnLogin += new NetworkManager.LoginCallback(Network_OnLogin); // Turn almost everything off since we are only interested in uploading textures Client.Settings.ALWAYS_DECODE_OBJECTS = false; Client.Settings.ALWAYS_REQUEST_OBJECTS = false; + Client.Settings.SEND_AGENT_UPDATES = true; Client.Settings.CONTINUOUS_AGENT_UPDATES = false; Client.Settings.OBJECT_TRACKING = false; - Client.Settings.SEND_AGENT_UPDATES = true; Client.Settings.STORE_LAND_PATCHES = false; Client.Settings.MULTIPLE_SIMS = false; Client.Self.Movement.Camera.Far = 32.0f; Client.Throttle.Cloud = 0.0f; Client.Throttle.Land = 0.0f; Client.Throttle.Wind = 0.0f; + + Client.Throttle.Texture = 446000.0f; } private void EnableUpload() @@ -280,6 +282,16 @@ namespace SLImageUpload Client.Inventory.RequestCreateItemFromAsset(UploadData, name, "Uploaded with SL Image Upload", AssetType.Texture, InventoryType.Texture, Client.Inventory.FindFolderForType(AssetType.Texture), + + delegate(CapsClient client, long bytesReceived, long bytesSent, long totalBytesToReceive, long totalBytesToSend) + { + if (bytesSent > 0) + { + Transferred = (int)bytesSent; + BeginInvoke((MethodInvoker)delegate() { SetProgress(); }); + } + }, + delegate(bool success, string status, LLUUID itemID, LLUUID assetID) { if (this.InvokeRequired) @@ -295,6 +307,9 @@ namespace SLImageUpload // Fix the permissions on the new upload since they are fscked by default InventoryItem item = Client.Inventory.FetchItem(itemID, Client.Self.AgentID, 1000 * 15); + Transferred = UploadData.Length; + BeginInvoke((MethodInvoker)delegate() { SetProgress(); }); + if (item != null) { item.Permissions.EveryoneMask = PermissionMask.All; @@ -331,16 +346,6 @@ namespace SLImageUpload } } - private void Assets_OnUploadProgress(AssetUpload upload) - { - Transferred = upload.Transferred; - - if (this.InvokeRequired) - BeginInvoke(new MethodInvoker(SetProgress)); - else - SetProgress(); - } - private void SetProgress() { prgUpload.Value = Transferred; diff --git a/importprimscript/importprimscript.cs b/importprimscript/importprimscript.cs index b3407f0b..03852673 100644 --- a/importprimscript/importprimscript.cs +++ b/importprimscript/importprimscript.cs @@ -4,6 +4,7 @@ using System.Threading; using System.IO; using System.Drawing; using libsecondlife; +using libsecondlife.Capabilities; namespace importprimscript { @@ -214,6 +215,12 @@ namespace importprimscript AutoResetEvent uploadEvent = new AutoResetEvent(false); Client.Inventory.RequestCreateItemFromAsset(jp2data, Path.GetFileNameWithoutExtension(filename), "Uploaded with importprimscript", AssetType.Texture, InventoryType.Texture, UploadFolderID, + + delegate(CapsClient client, long bytesReceived, long bytesSent, long totalBytesToReceive, long totalBytesToSend) + { + // FIXME: Do something with progress? + }, + delegate(bool success, string status, LLUUID itemID, LLUUID assetID) { if (success) diff --git a/libsecondlife/Capabilities/CapsBase.cs b/libsecondlife/Capabilities/CapsBase.cs index 60a9a478..4b7a4b96 100644 --- a/libsecondlife/Capabilities/CapsBase.cs +++ b/libsecondlife/Capabilities/CapsBase.cs @@ -26,39 +26,509 @@ using System; using System.Net; +using System.IO; +using System.Threading; namespace libsecondlife.Capabilities { - public class CapsBase : WebClient + public class CapsBase { - public int Timeout; - public int ContentLength; + #region Callback Data Classes - public Uri Location { get { return _Location; } } + public class OpenWriteCompletedEventArgs + { + public Stream Result; + public Exception Error; + public bool Cancelled; + public object UserState; - protected Uri _Location; + public OpenWriteCompletedEventArgs(Stream result, Exception error, bool cancelled, object userState) + { + Result = result; + Error = error; + Cancelled = cancelled; + UserState = userState; + } + } + + public class UploadDataCompletedEventArgs + { + public byte[] Result; + public Exception Error; + public bool Cancelled; + public object UserState; + + public UploadDataCompletedEventArgs(byte[] result, Exception error, bool cancelled, object userState) + { + Result = result; + Error = error; + Cancelled = cancelled; + UserState = userState; + } + } + + public class DownloadDataCompletedEventArgs + { + public byte[] Result; + public Exception Error; + public bool Cancelled; + public object UserState; + } + + public class DownloadStringCompletedEventArgs + { + public Uri Address; + public string Result; + public Exception Error; + public bool Cancelled; + public object UserState; + + public DownloadStringCompletedEventArgs(Uri address, string result, Exception error, bool cancelled, object userState) + { + Address = address; + Result = result; + Error = error; + Cancelled = cancelled; + UserState = userState; + } + } + + public class DownloadProgressChangedEventArgs + { + public long BytesReceived; + public int ProgressPercentage; + public long TotalBytesToReceive; + public object UserState; + + public DownloadProgressChangedEventArgs(long bytesReceived, long totalBytesToReceive, object userToken) + { + BytesReceived = bytesReceived; + ProgressPercentage = (int)(((float)bytesReceived / (float)totalBytesToReceive) * 100f); + TotalBytesToReceive = totalBytesToReceive; + UserState = userToken; + } + } + + public class UploadProgressChangedEventArgs + { + public long BytesReceived; + public long BytesSent; + public int ProgressPercentage; + public long TotalBytesToReceive; + public long TotalBytesToSend; + public object UserState; + + public UploadProgressChangedEventArgs(long bytesReceived, long totalBytesToReceive, long bytesSent, long totalBytesToSend, object userState) + { + BytesReceived = bytesReceived; + TotalBytesToReceive = totalBytesToReceive; + ProgressPercentage = (int)(((float)bytesSent / (float)totalBytesToSend) * 100f); + BytesSent = bytesSent; + TotalBytesToSend = totalBytesToSend; + UserState = userState; + } + } + + #endregion Callback Data Classes + + public delegate void OpenWriteCompletedEventHandler(object sender, OpenWriteCompletedEventArgs e); + public delegate void UploadDataCompletedEventHandler(object sender, UploadDataCompletedEventArgs e); + public delegate void DownloadStringCompletedEventHandler(object sender, DownloadStringCompletedEventArgs e); + public delegate void DownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e); + public delegate void UploadProgressChangedEventHandler(object sender, UploadProgressChangedEventArgs e); + + public event OpenWriteCompletedEventHandler OpenWriteCompleted; + public event UploadDataCompletedEventHandler UploadDataCompleted; + public event DownloadStringCompletedEventHandler DownloadStringCompleted; + public event DownloadProgressChangedEventHandler DownloadProgressChanged; + public event UploadProgressChangedEventHandler UploadProgressChanged; + + public WebHeaderCollection Headers = new WebHeaderCollection(); + public IWebProxy Proxy; + + public Uri Location { get { return location; } } + public bool IsBusy { get { return isBusy; } } + public WebHeaderCollection ResponseHeaders { get { return responseHeaders; } } + + protected WebHeaderCollection responseHeaders; + protected Uri location; + protected bool isBusy; + protected Thread asyncThread; + protected System.Text.Encoding encoding = System.Text.Encoding.Default; public CapsBase(Uri location) { - _Location = location; + this.location = location; } - protected override WebRequest GetWebRequest(Uri address) + public void OpenWriteAsync(Uri address) { - HttpWebRequest request = base.GetWebRequest(address) as HttpWebRequest; + OpenWriteAsync(address, null); + } + + public void OpenWriteAsync(Uri address, string method) + { + OpenWriteAsync(address, method, null); + } + + public void OpenWriteAsync(Uri address, string method, object userToken) + { + if (address == null) + throw new ArgumentNullException("address"); + + SetBusy(); + + asyncThread = new Thread(delegate(object state) + { + object[] args = (object[])state; + WebRequest request = null; + + try + { + request = SetupRequest((Uri)args[0]); + Stream stream = request.GetRequestStream(); + + OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( + stream, null, false, args[2])); + } + catch (ThreadInterruptedException) + { + if (request != null) + request.Abort(); + + OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( + null, null, true, args[2])); + } + catch (Exception e) + { + OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( + null, e, false, args[2])); + } + }); + + object[] cbArgs = new object[] { address, method, userToken }; + asyncThread.Start(cbArgs); + } + + public void UploadDataAsync(Uri address, byte[] data) + { + UploadDataAsync(address, null, data, null); + } + + public void UploadDataAsync(Uri address, string method, byte[] data) + { + UploadDataAsync(address, method, data, null); + } + + public void UploadDataAsync(Uri address, string method, byte[] data, object userToken) + { + if (address == null) + throw new ArgumentNullException("address"); + if (data == null) + throw new ArgumentNullException("data"); + + SetBusy(); + + asyncThread = new Thread(delegate(object state) + { + object[] args = (object[])state; + byte[] data2; + + try + { + data2 = UploadDataCore((Uri)args[0], (string)args[1], (byte[])args[2], args[3]); + + OnUploadDataCompleted( + new UploadDataCompletedEventArgs(data2, null, false, args[3])); + } + catch (ThreadInterruptedException) + { + OnUploadDataCompleted( + new UploadDataCompletedEventArgs(null, null, true, args[3])); + } + catch (Exception e) + { + OnUploadDataCompleted( + new UploadDataCompletedEventArgs(null, e, false, args[3])); + } + }); + + object[] cbArgs = new object[] { address, method, data, userToken }; + asyncThread.Start(cbArgs); + } + + public void DownloadStringAsync (Uri address) + { + DownloadStringAsync (address, null); + } + + public void DownloadStringAsync(Uri address, object userToken) + { + if (address == null) + throw new ArgumentNullException("address"); + + SetBusy(); + + asyncThread = new Thread(delegate(object state) + { + object[] args = (object[])state; + + try + { + string data = encoding.GetString(DownloadDataCore((Uri)args[0], args[1])); + OnDownloadStringCompleted( + new DownloadStringCompletedEventArgs(location, data, null, false, args[1])); + } + catch (ThreadInterruptedException) + { + OnDownloadStringCompleted( + new DownloadStringCompletedEventArgs(location, null, null, true, args[1])); + } + catch (Exception e) + { + OnDownloadStringCompleted( + new DownloadStringCompletedEventArgs(location, null, e, false, args[1])); + } + }); + + object[] cbArgs = new object[] { address, userToken }; + asyncThread.Start(cbArgs); + } + + public void CancelAsync() + { + if (asyncThread == null) + return; + + Thread t = asyncThread; + CompleteAsync(); + t.Interrupt(); + } + + protected void CompleteAsync() + { + isBusy = false; + asyncThread = null; + } + + protected void SetBusy() + { + CheckBusy(); + isBusy = true; + } + + protected void CheckBusy() + { + if (isBusy) + throw new NotSupportedException("CapsBase does not support concurrent I/O operations."); + } + + protected Stream ProcessResponse(WebResponse response) + { + responseHeaders = response.Headers; + return response.GetResponseStream(); + } + + protected byte[] ReadAll(Stream stream, int length, object userToken, bool uploading) + { + MemoryStream ms = null; + + bool nolength = (length == -1); + int size = ((nolength) ? 8192 : length); + if (nolength) + ms = new MemoryStream(); + + long total = 0; + int nread = 0; + int offset = 0; + byte[] buffer = new byte[size]; + + while ((nread = stream.Read(buffer, offset, size)) != 0) + { + if (nolength) + { + ms.Write(buffer, 0, nread); + } + else + { + offset += nread; + size -= nread; + } + + if (uploading) + { + if (UploadProgressChanged != null) + { + total += nread; + UploadProgressChanged(this, new UploadProgressChangedEventArgs(nread, length, 0, 0, userToken)); + } + } + else + { + if (DownloadProgressChanged != null) + { + total += nread; + DownloadProgressChanged(this, new DownloadProgressChangedEventArgs(nread, length, userToken)); + } + } + } + + if (nolength) + return ms.ToArray(); + + return buffer; + } + + protected WebRequest SetupRequest(Uri uri) + { + HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); if (request == null) - throw new ArgumentException("CapsBase requires a Uri", "address"); + throw new ArgumentException("Could not create an HttpWebRequest from the given Uri", "address"); + + location = uri; + + if (Proxy != null) + request.Proxy = Proxy; + + request.Method = "POST"; + + if (Headers != null && Headers.Count != 0) + { + string expect = Headers["Expect"]; + string contentType = Headers["Content-Type"]; + string accept = Headers["Accept"]; + string connection = Headers["Connection"]; + string userAgent = Headers["User-Agent"]; + string referer = Headers["Referer"]; + + if (!String.IsNullOrEmpty(expect)) + request.Expect = expect; + + if (!String.IsNullOrEmpty(accept)) + request.Accept = accept; + + if (!String.IsNullOrEmpty(contentType)) + request.ContentType = contentType; + + if (!String.IsNullOrEmpty(connection)) + request.Connection = connection; + + if (!String.IsNullOrEmpty(userAgent)) + request.UserAgent = userAgent; + + if (!String.IsNullOrEmpty(referer)) + request.Referer = referer; + } // Disable keep-alive request.KeepAlive = false; + // Disable stupid Expect-100: Continue header request.ServicePoint.Expect100Continue = false; - - // Set the timeout - if (Timeout > 0) - request.Timeout = Timeout; + // Upload chunks directly instead of buffering to memory + request.AllowWriteStreamBuffering = false; return request; } + + protected WebRequest SetupRequest(Uri uri, string method) + { + WebRequest request = SetupRequest(uri); + request.Method = method; + return request; + } + + protected byte[] UploadDataCore(Uri address, string method, byte[] data, object userToken) + { + WebRequest request = SetupRequest(address); + + try + { + int contentLength = data.Length; + request.ContentLength = contentLength; + using (Stream stream = request.GetRequestStream()) + { + // Most uploads are very small chunks of data, use an optimized path for these + if (contentLength < 4096) + { + stream.Write(data, 0, contentLength); + } + else + { + MemoryStream ms = new MemoryStream(data); + + byte[] buffer = new byte[checked((uint)Math.Min(4096, (int)contentLength))]; + int bytesRead = 0; + + while ((bytesRead = ms.Read(buffer, 0, buffer.Length)) != 0) + { + stream.Write(buffer, 0, bytesRead); + + if (UploadProgressChanged != null) + { + UploadProgressChanged(this, new UploadProgressChangedEventArgs(0, 0, bytesRead, contentLength, userToken)); + } + } + + ms.Close(); + } + } + + WebResponse response = request.GetResponse(); + Stream st = ProcessResponse(response); + + return ReadAll(st, (int)response.ContentLength, userToken, true); + } + catch (ThreadInterruptedException) + { + if (request != null) + request.Abort(); + throw; + } + } + + protected byte[] DownloadDataCore(Uri address, object userToken) + { + WebRequest request = null; + + try + { + request = SetupRequest(address, "GET"); + WebResponse response = request.GetResponse(); + Stream st = ProcessResponse(response); + return ReadAll(st, (int)response.ContentLength, userToken, false); + } + catch (ThreadInterruptedException) + { + if (request != null) + request.Abort(); + throw; + } + catch (Exception ex) + { + throw new WebException("An error occurred performing a WebClient request.", ex); + } + } + + protected virtual void OnOpenWriteCompleted(OpenWriteCompletedEventArgs args) + { + CompleteAsync(); + if (OpenWriteCompleted != null) + OpenWriteCompleted(this, args); + } + + protected virtual void OnUploadDataCompleted(UploadDataCompletedEventArgs args) + { + CompleteAsync(); + if (UploadDataCompleted != null) + UploadDataCompleted(this, args); + } + + protected virtual void OnDownloadStringCompleted(DownloadStringCompletedEventArgs args) + { + CompleteAsync(); + if (DownloadStringCompleted != null) + DownloadStringCompleted(this, args); + } } } diff --git a/libsecondlife/Capabilities/CapsClient.cs b/libsecondlife/Capabilities/CapsClient.cs index 80aea4bb..b16940b8 100644 --- a/libsecondlife/Capabilities/CapsClient.cs +++ b/libsecondlife/Capabilities/CapsClient.cs @@ -49,10 +49,10 @@ namespace libsecondlife.Capabilities public CapsClient(Uri capability) { _Client = new CapsBase(capability); - _Client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Client_DownloadProgressChanged); - _Client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(Client_DownloadDataCompleted); - _Client.UploadProgressChanged += new UploadProgressChangedEventHandler(Client_UploadProgressChanged); - _Client.UploadDataCompleted += new UploadDataCompletedEventHandler(Client_UploadDataCompleted); + _Client.DownloadProgressChanged += new CapsBase.DownloadProgressChangedEventHandler(Client_DownloadProgressChanged); + _Client.UploadProgressChanged += new CapsBase.UploadProgressChangedEventHandler(Client_UploadProgressChanged); + _Client.UploadDataCompleted += new CapsBase.UploadDataCompletedEventHandler(Client_UploadDataCompleted); + _Client.DownloadStringCompleted += new CapsBase.DownloadStringCompletedEventHandler(Client_DownloadStringCompleted); } public void StartRequest() @@ -112,7 +112,7 @@ namespace libsecondlife.Capabilities #region Callback Handlers - private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + private void Client_DownloadProgressChanged(object sender, CapsBase.DownloadProgressChangedEventArgs e) { if (OnProgress != null) { @@ -121,7 +121,16 @@ namespace libsecondlife.Capabilities } } - private void Client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e) + private void Client_UploadProgressChanged(object sender, CapsBase.UploadProgressChangedEventArgs e) + { + if (OnProgress != null) + { + try { OnProgress(this, e.BytesReceived, e.BytesSent, e.TotalBytesToReceive, e.TotalBytesToSend); } + catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); } + } + } + + private void Client_UploadDataCompleted(object sender, CapsBase.UploadDataCompletedEventArgs e) { if (OnComplete != null && !e.Cancelled) { @@ -137,6 +146,7 @@ namespace libsecondlife.Capabilities if (Helpers.StringContains(e.Error.Message, "502")) { // These are normal, retry the request automatically + SecondLife.DebugLogStatic("502 error from capability " + _Client.Location); StartRequest(_PostData, _ContentType); } else @@ -146,18 +156,13 @@ namespace libsecondlife.Capabilities } } } - } - - private void Client_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e) - { - if (OnProgress != null) + else if (e.Cancelled) { - try { OnProgress(this, e.BytesReceived, e.BytesSent, e.TotalBytesToReceive, e.TotalBytesToSend); } - catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); } + SecondLife.DebugLogStatic("Capability action at " + _Client.Location + " cancelled"); } } - private void Client_UploadDataCompleted(object sender, UploadDataCompletedEventArgs e) + private void Client_DownloadStringCompleted(object sender, CapsBase.DownloadStringCompletedEventArgs e) { if (OnComplete != null && !e.Cancelled) { diff --git a/libsecondlife/Capabilities/EventQueueClient.cs b/libsecondlife/Capabilities/EventQueueClient.cs index d75603b9..8a433df4 100644 --- a/libsecondlife/Capabilities/EventQueueClient.cs +++ b/libsecondlife/Capabilities/EventQueueClient.cs @@ -60,8 +60,8 @@ namespace libsecondlife.Capabilities public EventQueueClient(Uri eventQueueLocation) { _Client = new CapsBase(eventQueueLocation); - _Client.OpenWriteCompleted += new OpenWriteCompletedEventHandler(Client_OpenWriteCompleted); - _Client.UploadDataCompleted += new UploadDataCompletedEventHandler(Client_UploadDataCompleted); + _Client.OpenWriteCompleted += new CapsBase.OpenWriteCompletedEventHandler(Client_OpenWriteCompleted); + _Client.UploadDataCompleted += new CapsBase.UploadDataCompletedEventHandler(Client_UploadDataCompleted); } public void Start() @@ -90,7 +90,7 @@ namespace libsecondlife.Capabilities #region Callback Handlers - private void Client_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e) + private void Client_OpenWriteCompleted(object sender, CapsBase.OpenWriteCompletedEventArgs e) { bool raiseEvent = false; @@ -124,7 +124,7 @@ namespace libsecondlife.Capabilities } } - private void Client_UploadDataCompleted(object sender, UploadDataCompletedEventArgs e) + private void Client_UploadDataCompleted(object sender, CapsBase.UploadDataCompletedEventArgs e) { LLSDArray events = null; int ack = 0; diff --git a/libsecondlife/InventoryManager.cs b/libsecondlife/InventoryManager.cs index a2685439..34c26483 100644 --- a/libsecondlife/InventoryManager.cs +++ b/libsecondlife/InventoryManager.cs @@ -1681,7 +1681,7 @@ namespace libsecondlife } public void RequestCreateItemFromAsset(byte[] data, string name, string description, AssetType assetType, - InventoryType invType, LLUUID folderID, ItemCreatedFromAssetCallback callback) + InventoryType invType, LLUUID folderID, CapsClient.ProgressCallback progCallback, ItemCreatedFromAssetCallback callback) { if (_Client.Network.CurrentSim == null || _Client.Network.CurrentSim.Caps == null) throw new Exception("NewFileAgentInventory capability is not currently available"); @@ -1702,7 +1702,7 @@ namespace libsecondlife // Make the request CapsClient request = new CapsClient(url); request.OnComplete += new CapsClient.CompleteCallback(CreateItemFromAssetResponse); - request.UserData = new KeyValuePair(callback, data); + request.UserData = new object[] { progCallback, callback, data }; request.StartRequest(postData); } else @@ -2751,10 +2751,12 @@ namespace libsecondlife private void CreateItemFromAssetResponse(CapsClient client, LLSD result, Exception error) { + object[] args = (object[])client.UserData; + CapsClient.ProgressCallback progCallback = (CapsClient.ProgressCallback)args[0]; + ItemCreatedFromAssetCallback callback = (ItemCreatedFromAssetCallback)args[1]; + byte[] itemData = (byte[])args[2]; + LLSDMap contents = (LLSDMap)result; - KeyValuePair kvp = (KeyValuePair)client.UserData; - ItemCreatedFromAssetCallback callback = kvp.Key; - byte[] itemData = (byte[])kvp.Value; if (result == null) { @@ -2772,8 +2774,9 @@ namespace libsecondlife // This makes the assumption that all uploads go to CurrentSim, to avoid // the problem of HttpRequestState not knowing anything about simulators CapsClient upload = new CapsClient(new Uri(uploadURL)); + upload.OnProgress += progCallback; upload.OnComplete += new CapsClient.CompleteCallback(CreateItemFromAssetResponse); - upload.UserData = kvp; + upload.UserData = new object[] { null, callback, itemData }; upload.StartRequest(itemData, "application/octet-stream"); } else if (status == "complete") diff --git a/libsecondlife/Login.cs b/libsecondlife/Login.cs index 0375171d..3ef5a1d8 100644 --- a/libsecondlife/Login.cs +++ b/libsecondlife/Login.cs @@ -129,15 +129,14 @@ namespace libsecondlife { try { - AgentID = ParseUUID("agent_id", reply); - SessionID = ParseUUID("session_id", reply); - SecureSessionID = ParseUUID("secure_session_id", reply); - FirstName = ParseString("first_name", reply).Trim('"'); - LastName = ParseString("last_name", reply).Trim('"'); - StartLocation = ParseString("start_location", reply); - AgentAccess = ParseString("agent_access", reply); - LookAt = ParseLLVector3("look_at", reply); - + AgentID = ParseUUID("agent_id", reply); + SessionID = ParseUUID("session_id", reply); + SecureSessionID = ParseUUID("secure_session_id", reply); + FirstName = ParseString("first_name", reply).Trim('"'); + LastName = ParseString("last_name", reply).Trim('"'); + StartLocation = ParseString("start_location", reply); + AgentAccess = ParseString("agent_access", reply); + LookAt = ParseLLVector3("look_at", reply); } catch (LLSDException e) { diff --git a/libsecondlife/examples/TestClient/Commands/Inventory/UploadImageCommand.cs b/libsecondlife/examples/TestClient/Commands/Inventory/UploadImageCommand.cs index 1aee1072..a2f59a09 100644 --- a/libsecondlife/examples/TestClient/Commands/Inventory/UploadImageCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/Inventory/UploadImageCommand.cs @@ -4,7 +4,7 @@ using System.IO; using System.Threading; using System.Drawing; using libsecondlife; -using libsecondlife.Packets; +using libsecondlife.Capabilities; namespace libsecondlife.TestClient { @@ -59,8 +59,15 @@ namespace libsecondlife.TestClient { string name = System.IO.Path.GetFileNameWithoutExtension(FileName); - Client.Inventory.RequestCreateItemFromAsset(UploadData, name, "Uploaded with SL Image Upload", + Client.Inventory.RequestCreateItemFromAsset(UploadData, name, "Uploaded with TestClient", AssetType.Texture, InventoryType.Texture, Client.Inventory.FindFolderForType(AssetType.Texture), + + delegate(CapsClient client, long bytesReceived, long bytesSent, long totalBytesToReceive, long totalBytesToSend) + { + if (bytesSent > 0) + Console.WriteLine(String.Format("Texture upload: {0} / {1}", bytesSent, totalBytesToSend)); + }, + delegate(bool success, string status, LLUUID itemID, LLUUID assetID) { Console.WriteLine(String.Format( diff --git a/libsecondlife/libsecondlife.csproj b/libsecondlife/libsecondlife.csproj index 9f483cec..df2932ed 100644 --- a/libsecondlife/libsecondlife.csproj +++ b/libsecondlife/libsecondlife.csproj @@ -121,7 +121,6 @@ - Component