diff --git a/VoiceTest/VoiceTest.cs b/VoiceTest/VoiceTest.cs
index 67deb227..e7fe597b 100644
--- a/VoiceTest/VoiceTest.cs
+++ b/VoiceTest/VoiceTest.cs
@@ -88,7 +88,7 @@ namespace VoiceTest
Console.WriteLine("Voice connector handle: " + connectorHandle);
// Wait for the simulator capabilities to show up
- client.Network.CurrentSim.Caps.CapsReceivedEvent.WaitOne(45 * 1000, false);
+ // FIXME: Use client.Network.OnEventQueueRunning to continue here
Console.WriteLine("Asking the current simulator to create a provisional account...");
diff --git a/libsecondlife/AgentManager.cs b/libsecondlife/AgentManager.cs
index 62b660a5..13153a41 100644
--- a/libsecondlife/AgentManager.cs
+++ b/libsecondlife/AgentManager.cs
@@ -30,6 +30,8 @@ using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Reflection;
+using libsecondlife.StructuredData;
+using libsecondlife.Capabilities;
using libsecondlife.Packets;
namespace libsecondlife
@@ -920,7 +922,7 @@ namespace libsecondlife
Client.Network.RegisterCallback(PacketType.MeanCollisionAlert, new NetworkManager.PacketCallback(MeanCollisionAlertHandler));
// CAPS callbacks
- Client.Network.RegisterEventCallback("EstablishAgentCommunication", new Capabilities.EventQueueCallback(EstablishAgentCommunicationEventHandler));
+ Client.Network.RegisterEventCallback("EstablishAgentCommunication", new Caps.EventQueueCallback(EstablishAgentCommunicationEventHandler));
// Login
Client.Network.RegisterLoginResponseCallback(new NetworkManager.LoginResponseCallback(Network_OnLoginResponse));
@@ -1784,7 +1786,9 @@ namespace libsecondlife
/// Target to look at
public void RequestTeleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt)
{
- if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.Caps != null && Client.Network.CurrentSim.Caps.IsEventQueueRunning)
+ if (Client.Network.CurrentSim != null &&
+ Client.Network.CurrentSim.Caps != null &&
+ Client.Network.CurrentSim.Caps.IsEventQueueRunning)
{
TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket();
teleport.AgentData.AgentID = Client.Self.AgentID;
@@ -2167,7 +2171,7 @@ namespace libsecondlife
}
}
- private void EstablishAgentCommunicationEventHandler(string message, StructuredData.LLSD llsd, CapsEventQueue caps)
+ private void EstablishAgentCommunicationEventHandler(string message, LLSD llsd, Simulator simulator)
{
StructuredData.LLSDMap body = (StructuredData.LLSDMap)llsd;
diff --git a/libsecondlife/Capabilities/CapsBase.cs b/libsecondlife/Capabilities/CapsBase.cs
new file mode 100644
index 00000000..4ba7ca23
--- /dev/null
+++ b/libsecondlife/Capabilities/CapsBase.cs
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2007, 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.
+ */
+
+using System;
+using System.Net;
+
+namespace libsecondlife.Capabilities
+{
+ public class CapsBase : WebClient
+ {
+ public int Timeout;
+ public int ContentLength;
+
+ public Uri Location { get { return _Location; } }
+
+ protected Uri _Location;
+
+ public CapsBase(Uri location)
+ {
+ _Location = location;
+ }
+
+ protected override WebRequest GetWebRequest(Uri address)
+ {
+ HttpWebRequest request = base.GetWebRequest(address) as HttpWebRequest;
+
+ if (request == null)
+ throw new ArgumentException("CapsBase requires a Uri", "address");
+
+ // Disable keep-alive
+ request.KeepAlive = false;
+
+ // Set the timeout
+ if (Timeout > 0)
+ request.Timeout = Timeout;
+
+ return request;
+ }
+ }
+}
diff --git a/libsecondlife/Capabilities/CapsClient.cs b/libsecondlife/Capabilities/CapsClient.cs
new file mode 100644
index 00000000..1b0f2b21
--- /dev/null
+++ b/libsecondlife/Capabilities/CapsClient.cs
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2007, 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.
+ */
+
+using System;
+using System.Net;
+using libsecondlife.StructuredData;
+
+namespace libsecondlife.Capabilities
+{
+ public class CapsClient
+ {
+ public delegate void ProgressCallback(CapsClient client, long bytesReceived, long bytesSent,
+ long totalBytesToReceive, long totalBytesToSend);
+ public delegate void CompleteCallback(CapsClient client, LLSD result, Exception error);
+
+ public ProgressCallback OnProgress;
+ public CompleteCallback OnComplete;
+
+ public IWebProxy Proxy;
+ public object UserData;
+
+ protected CapsBase _Client;
+ protected byte[] _PostData;
+ protected string _ContentType;
+
+ 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);
+ }
+
+ public void StartRequest()
+ {
+ StartRequest(null, null);
+ }
+
+ public void StartRequest(LLSD llsd)
+ {
+ byte[] postData = LLSDParser.SerializeXmlBytes(llsd);
+ StartRequest(postData, null);
+ }
+
+ public void StartRequest(byte[] postData)
+ {
+ StartRequest(postData, null);
+ }
+
+ public void StartRequest(byte[] postData, string contentType)
+ {
+ _PostData = postData;
+ _ContentType = contentType;
+
+ if (_Client.IsBusy)
+ {
+ SecondLife.LogStatic("New CAPS request initiated, closing previous request",
+ Helpers.LogLevel.Warning);
+ _Client.CancelAsync();
+ }
+
+ // Proxy
+ if (Proxy != null)
+ _Client.Proxy = Proxy;
+
+ // Content-Type
+ _Client.Headers.Clear();
+ if (!String.IsNullOrEmpty(contentType))
+ _Client.Headers.Add(HttpRequestHeader.ContentType, contentType);
+ else
+ _Client.Headers.Add(HttpRequestHeader.ContentType, "application/xml");
+
+ if (postData == null)
+ {
+ _Client.DownloadStringAsync(_Client.Location);
+ }
+ else
+ {
+ _Client.UploadDataAsync(_Client.Location, postData);
+
+ //if (postData.Length == 292)
+ //{
+ // CapsRequest request = new CapsRequest(_Client.Location.AbsoluteUri);
+ // request.OnCapsResponse += new CapsRequest.CapsResponseCallback(request_OnCapsResponse);
+ // request.MakeRequest(postData, "application/xml", null);
+
+ // //HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(_Client.Location);
+ // //request.KeepAlive = false;
+ // //request.Method = "POST";
+ // //request.ContentLength = postData.Length;
+ // //request.ContentType = "application/xml";
+ // //System.IO.Stream stream = request.GetRequestStream();
+
+ // //stream = _Client.OpenWrite(_Client.Location);
+ // //stream.Write(postData, 0, 292);
+ // //stream.Close();
+
+ // //WebResponse response = request.GetResponse();
+ // //SecondLife.LogStatic(response.ContentLength.ToString(), Helpers.LogLevel.Info);
+
+ // //_Client.Timeout = 10000;
+ // //byte[] lol = _Client.UploadData(_Client.Location, postData);
+ // //Console.WriteLine("Done?");
+ //}
+ }
+ }
+
+ public void Cancel()
+ {
+ if (_Client.IsBusy)
+ _Client.CancelAsync();
+ }
+
+ #region Callback Handlers
+
+ private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
+ {
+ if (OnProgress != null)
+ {
+ try { OnProgress(this, e.BytesReceived, 0, e.TotalBytesToReceive, 0); }
+ catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+
+ private void Client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
+ {
+ if (OnComplete != null && !e.Cancelled)
+ {
+ if (e.Error == null)
+ {
+ LLSD result = LLSDParser.DeserializeXml(e.Result);
+
+ try { OnComplete(this, result, e.Error); }
+ catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); }
+ }
+ else
+ {
+ if (Helpers.StringContains(e.Error.Message, "502"))
+ {
+ // These are normal, retry the request automatically
+ StartRequest(_PostData, _ContentType);
+ }
+ else
+ {
+ try { OnComplete(this, null, e.Error); }
+ catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+ }
+ }
+
+ private void Client_UploadProgressChanged(object sender, 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, UploadDataCompletedEventArgs e)
+ {
+ if (OnComplete != null && !e.Cancelled)
+ {
+ LLSD result = LLSDParser.DeserializeXml(e.Result);
+
+ try { OnComplete(this, result, e.Error); }
+ catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+
+ #endregion Callback Handlers
+ }
+}
diff --git a/libsecondlife/HttpBase.cs b/libsecondlife/Capabilities/CapsEventQueue.cs
similarity index 57%
rename from libsecondlife/HttpBase.cs
rename to libsecondlife/Capabilities/CapsEventQueue.cs
index bcf29feb..a2c618b2 100644
--- a/libsecondlife/HttpBase.cs
+++ b/libsecondlife/Capabilities/CapsEventQueue.cs
@@ -25,16 +25,15 @@
*/
using System;
+using System.Collections.Generic;
using System.Net;
-using System.IO;
using System.Text;
+using System.IO;
using System.Threading;
+using libsecondlife.StructuredData;
-namespace libsecondlife
+namespace libsecondlife.Capabilities
{
- ///
- /// Stores the current state of an HTTP request
- ///
public class HttpRequestState
{
const int BUFFER_SIZE = 1024;
@@ -55,42 +54,52 @@ namespace libsecondlife
}
}
- public abstract class HttpBase
+ public class CapsEventQueue
{
- protected abstract void RequestReply(HttpRequestState state, bool success, WebException exception);
- protected abstract void RequestSent(HttpRequestState request);
+ public const int HTTP_TIMEOUT = 1 * 30 * 1000;
+ public const int BUFFER_SIZE = 1024;
- /// Buffer size for reading incoming responses
- protected const int BUFFER_SIZE = 1024;
- /// A default timeout for HTTP connections
- protected const int HTTP_TIMEOUT = 1 * 30 * 1000;
+ public Simulator Simulator;
+
+ protected bool _Running = false;
+ protected bool _Dead = false;
protected HttpRequestState _RequestState;
protected string _RequestURL;
protected string _ProxyURL;
protected bool _Aborted = false;
- public HttpBase(string requestURL)
- : this(requestURL, String.Empty)
- { }
+ public bool Running { get { return _Running; } }
- public HttpBase(string requestURL, string proxyURL)
+ public CapsEventQueue(Simulator simulator, string eventQueueURI)
+ : this(simulator, eventQueueURI, String.Empty)
{
- _RequestURL = requestURL;
- _ProxyURL = proxyURL;
+ }
+
+ public CapsEventQueue(Simulator simulator, string eventQueueURI, string proxy)
+ {
+ Simulator = simulator;
+ _RequestURL = eventQueueURI;
+ _ProxyURL = proxy;
}
public void MakeRequest()
{
- MakeRequest(null, null, 0, null);
+ // Create an EventQueueGet request
+ LLSDMap request = new LLSDMap();
+ request["ack"] = new LLSD();
+ request["done"] = LLSD.FromBoolean(false);
+
+ byte[] postData = LLSDParser.SerializeXmlBytes(request);
+
+ SendRequest(postData, "application/xml", null);
}
- public void MakeRequest(byte[] postData, string contentType, int udpListeningPort, object state)
+ private void SendRequest(byte[] postData, string contentType, object state)
{
// Create a new HttpWebRequest
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(_RequestURL);
_RequestState = new HttpRequestState(httpRequest);
- _RequestState.State = state;
if (_ProxyURL != String.Empty)
{
@@ -116,55 +125,15 @@ namespace libsecondlife
// POST request
_RequestState.WebRequest.Method = "POST";
_RequestState.WebRequest.ContentLength = postData.Length;
- if (udpListeningPort > 0)
- _RequestState.WebRequest.Headers.Add("X-SecondLife-UDP-Listen-Port", udpListeningPort.ToString());
- if (String.IsNullOrEmpty(contentType))
- _RequestState.WebRequest.ContentType = "application/xml";
- else
- _RequestState.WebRequest.ContentType = contentType;
+ _RequestState.WebRequest.ContentType = "application/xml";
_RequestState.RequestData = postData;
IAsyncResult result = (IAsyncResult)_RequestState.WebRequest.BeginGetRequestStream(
- new AsyncCallback(RequestStreamCallback), _RequestState);
-
- // If there is a timeout, the callback fires and the request becomes aborted
-#if PocketPC
- Thread thread = new Thread(
- delegate()
- {
- if (!result.AsyncWaitHandle.WaitOne(HTTP_TIMEOUT, false))
- TimeoutCallback(_RequestState, true);
- }
- );
- thread.Start();
-#else
- ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback),
- _RequestState, HTTP_TIMEOUT, true);
-#endif
+ new AsyncCallback(EventRequestStreamCallback), _RequestState);
}
else
{
- // GET request
- IAsyncResult result = (IAsyncResult)_RequestState.WebRequest.BeginGetResponse(
- new AsyncCallback(ResponseCallback), _RequestState);
-
- // If there is a timeout, the callback fires and the request becomes aborted
-#if PocketPC
- Thread thread = new Thread(
- delegate()
- {
- if (!result.AsyncWaitHandle.WaitOne(HTTP_TIMEOUT, false))
- TimeoutCallback(_RequestState, true);
- }
- );
- thread.Start();
-#else
- ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback),
- _RequestState, HTTP_TIMEOUT, true);
-#endif
-
- // If we get here the request has been initialized, so fire the callback for a request being started
- RequestSent(_RequestState);
+ throw new ArgumentException("postData cannot be null for the event queue", "postData");
}
}
catch (WebException e)
@@ -173,14 +142,14 @@ namespace libsecondlife
}
catch (Exception e)
{
- Log("HttpBase.MakeRequest(): " + e.ToString(), Helpers.LogLevel.Warning);
+ SecondLife.LogStatic("CapsEventQueue.MakeRequest(): " + e.ToString(), Helpers.LogLevel.Warning);
Abort(false, null);
}
}
public void Abort()
{
- Abort(false, null);
+ Stop(true);
}
protected void TimeoutCallback(object state, bool timedOut)
@@ -289,7 +258,7 @@ namespace libsecondlife
_RequestState.ResponseStream = responseStream;
// Begin reading of the contents of the response
- IAsyncResult asynchronousInputRead = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
+ IAsyncResult asynchronousInputRead = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
new AsyncCallback(ReadCallback), _RequestState);
// If there is a timeout, the callback fires and the request becomes aborted
@@ -313,6 +282,80 @@ namespace libsecondlife
}
}
+ public void Stop(bool immediate)
+ {
+ Simulator.Client.Log(String.Format("Event queue for {0} is {1}", Simulator,
+ (immediate ? "aborting" : "disconnecting")), Helpers.LogLevel.Info);
+
+ _Dead = true;
+
+ if (immediate)
+ {
+ _Running = false;
+
+ // Abort the callback if it hasn't been already
+ _RequestState.WebRequest.Abort();
+ }
+ }
+
+ protected void EventRequestStreamCallback(IAsyncResult result)
+ {
+ bool raiseEvent = false;
+
+ if (!_Dead)
+ {
+ if (!_Running) raiseEvent = true;
+
+ // We are connected to the event queue
+ _Running = true;
+ }
+
+ try
+ {
+ _RequestState = (HttpRequestState)result.AsyncState;
+ Stream reqStream = _RequestState.WebRequest.EndGetRequestStream(result);
+
+ reqStream.Write(_RequestState.RequestData, 0, _RequestState.RequestData.Length);
+ reqStream.Close();
+
+ IAsyncResult newResult = _RequestState.WebRequest.BeginGetResponse(new AsyncCallback(EventResponseCallback), _RequestState);
+ }
+ catch (WebException e)
+ {
+ Abort(false, e);
+ return;
+ }
+
+ if (raiseEvent)
+ {
+ Simulator.Client.DebugLog("Capabilities event queue connected for " + Simulator.ToString());
+
+ // The event queue is starting up for the first time
+ Simulator.Client.Network.RaiseConnectedEvent(Simulator);
+ }
+ }
+
+ protected void EventResponseCallback(IAsyncResult result)
+ {
+ try
+ {
+ _RequestState = (HttpRequestState)result.AsyncState;
+ _RequestState.WebResponse = (HttpWebResponse)_RequestState.WebRequest.EndGetResponse(result);
+
+ // Read the response into a Stream object
+ Stream responseStream = _RequestState.WebResponse.GetResponseStream();
+ _RequestState.ResponseStream = responseStream;
+
+ // Begin reading of the contents of the response
+ IAsyncResult asynchronousInputRead = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
+ new AsyncCallback(ReadCallback), _RequestState);
+ }
+ catch (WebException e)
+ {
+ Abort(false, e);
+ }
+ }
+
protected void ReadCallback(IAsyncResult result)
{
try
@@ -334,7 +377,7 @@ namespace libsecondlife
_RequestState.ResponseDataPos += read;
// Continue reading the response until EndRead() returns 0
- IAsyncResult asynchronousResult = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
+ IAsyncResult asynchronousResult = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
new AsyncCallback(ReadCallback), _RequestState);
return;
@@ -353,5 +396,95 @@ namespace libsecondlife
Abort(false, e);
}
}
+
+ protected void RequestReply(HttpRequestState state, bool success, WebException exception)
+ {
+ LLSDArray events = null;
+ int ack = 0;
+
+ #region Exception Handling
+
+ if (exception != null)
+ {
+ string message = exception.Message.ToLower();
+
+ // Check what kind of exception happened
+ if (Helpers.StringContains(message, "404") || Helpers.StringContains(message, "410"))
+ {
+ Simulator.Client.Log("Closing event queue for " + Simulator.ToString() + " due to missing caps URI",
+ Helpers.LogLevel.Info);
+
+ _Running = false;
+ _Dead = true;
+ }
+ else if (!Helpers.StringContains(message, "aborted") && !Helpers.StringContains(message, "502"))
+ {
+ Simulator.Client.Log(String.Format("Unrecognized caps exception for {0}: {1}", Simulator, exception.Message),
+ Helpers.LogLevel.Warning);
+ }
+ }
+
+ #endregion Exception Handling
+
+ #region Reply Decoding
+
+ // Decode successful replies from the event queue
+ if (success)
+ {
+ LLSDMap response = (LLSDMap)LLSDParser.DeserializeXml(state.ResponseData);
+
+ if (response != null)
+ {
+ // Parse any events returned by the event queue
+ events = (LLSDArray)response["events"];
+ ack = response["id"].AsInteger();
+ }
+ }
+
+ #endregion Reply Decoding
+
+ #region Make New Request
+
+ if (_Running)
+ {
+ LLSDMap request = new LLSDMap();
+ if (ack != 0) request["ack"] = LLSD.FromInteger(ack);
+ else request["ack"] = new LLSD();
+ request["done"] = LLSD.FromBoolean(_Dead);
+
+ byte[] postData = LLSDParser.SerializeXmlBytes(request);
+
+ SendRequest(postData, "application/xml", null);
+
+ // If the event queue is dead at this point, turn it off since
+ // that was the last thing we want to do
+ if (_Dead)
+ {
+ _Running = false;
+ SecondLife.DebugLogStatic("Sent event queue shutdown message");
+ }
+ }
+
+ #endregion Make New Request
+
+ #region Callbacks
+
+ if (events != null && events.Count > 0)
+ {
+ // Fire callbacks for each event received
+ foreach (LLSDMap evt in events)
+ {
+ string msg = evt["message"].AsString();
+ LLSDMap body = (LLSDMap)evt["body"];
+
+ if (Simulator.Client.Settings.SYNC_PACKETCALLBACKS)
+ Simulator.Client.Network.CapsEvents.RaiseEvent(msg, body, Simulator);
+ else
+ Simulator.Client.Network.CapsEvents.BeginRaiseEvent(msg, body, Simulator);
+ }
+ }
+
+ #endregion Callbacks
+ }
}
}
diff --git a/libsecondlife/Capabilities/CapsListener.cs b/libsecondlife/Capabilities/CapsListener.cs
new file mode 100644
index 00000000..33b824bf
--- /dev/null
+++ b/libsecondlife/Capabilities/CapsListener.cs
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2007, 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.
+ */
+
+using System;
+using System.Net;
+
+#if !PocketPC
+
+namespace libsecondlife.Capabilities
+{
+ public class CapsListener
+ {
+ }
+}
+
+#endif
diff --git a/libsecondlife/Capabilities/EventQueueClient.cs b/libsecondlife/Capabilities/EventQueueClient.cs
new file mode 100644
index 00000000..3204799e
--- /dev/null
+++ b/libsecondlife/Capabilities/EventQueueClient.cs
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2007, 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.
+ */
+
+using System;
+using System.Net;
+using libsecondlife.StructuredData;
+
+namespace libsecondlife.Capabilities
+{
+ public class EventQueueClient
+ {
+ ///
+ ///
+ ///
+ public delegate void ConnectedCallback();
+ ///
+ ///
+ ///
+ ///
+ ///
+ public delegate void EventCallback(string eventName, LLSDMap body);
+
+ ///
+ public ConnectedCallback OnConnected;
+ ///
+ public EventCallback OnEvent;
+
+ public IWebProxy Proxy;
+
+ public bool Running { get { return _Running && _Client.IsBusy; } }
+
+ protected CapsBase _Client;
+ protected bool _Dead;
+ protected bool _Running;
+
+ public EventQueueClient(Uri eventQueueLocation)
+ {
+ _Client = new CapsBase(eventQueueLocation);
+ _Client.OpenWriteCompleted += new OpenWriteCompletedEventHandler(Client_OpenWriteCompleted);
+ _Client.UploadDataCompleted += new UploadDataCompletedEventHandler(Client_UploadDataCompleted);
+ }
+
+ public void Start()
+ {
+ _Dead = false;
+ _Client.OpenWriteAsync(_Client.Location);
+ }
+
+ public void Stop(bool immediate)
+ {
+ _Dead = true;
+
+ if (immediate)
+ _Running = false;
+
+ if (_Client.IsBusy)
+ {
+ SecondLife.DebugLogStatic("Stopping a running event queue");
+ _Client.CancelAsync();
+ }
+ else
+ {
+ SecondLife.DebugLogStatic("Stopping an already dead event queue");
+ }
+ }
+
+ #region Callback Handlers
+
+ private void Client_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e)
+ {
+ bool raiseEvent = false;
+
+ if (!_Dead)
+ {
+ if (!_Running) raiseEvent = true;
+
+ // We are connected to the event queue
+ _Running = true;
+ }
+
+ // Create an EventQueueGet request
+ LLSDMap request = new LLSDMap();
+ request["ack"] = new LLSD();
+ request["done"] = LLSD.FromBoolean(false);
+
+ byte[] postData = LLSDParser.SerializeXmlBytes(request);
+
+ _Client.UploadDataAsync(_Client.Location, postData);
+
+ if (raiseEvent)
+ {
+ SecondLife.DebugLogStatic("Capabilities event queue connected");
+
+ // The event queue is starting up for the first time
+ if (OnConnected != null)
+ {
+ try { OnConnected(); }
+ catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+ }
+
+ private void Client_UploadDataCompleted(object sender, UploadDataCompletedEventArgs e)
+ {
+ LLSDArray events = null;
+ int ack = 0;
+
+ if (e.Error != null)
+ {
+ string message = e.Error.Message.ToLower();
+
+ // Check what kind of exception happened
+ if (Helpers.StringContains(message, "404") || Helpers.StringContains(message, "410"))
+ {
+ SecondLife.LogStatic("Closing event queue due to missing caps URI", Helpers.LogLevel.Info);
+
+ _Running = false;
+ _Dead = true;
+ }
+ else if (!e.Cancelled && !Helpers.StringContains(message, "502"))
+ {
+ SecondLife.LogStatic("Unrecognized caps exception: " + e.Error.Message, Helpers.LogLevel.Warning);
+ }
+ }
+ else if (!e.Cancelled && e.Result != null)
+ {
+ LLSD result = LLSDParser.DeserializeXml(e.Result);
+ if (result != null && result.Type == LLSDType.Map)
+ {
+ // Parse any events returned by the event queue
+ LLSDMap map = (LLSDMap)result;
+
+ events = (LLSDArray)map["events"];
+ ack = map["id"].AsInteger();
+ }
+ }
+
+ if (_Running)
+ {
+ LLSDMap request = new LLSDMap();
+ if (ack != 0) request["ack"] = LLSD.FromInteger(ack);
+ else request["ack"] = new LLSD();
+ request["done"] = LLSD.FromBoolean(_Dead);
+
+ byte[] postData = LLSDParser.SerializeXmlBytes(request);
+
+ _Client.UploadDataAsync(_Client.Location, postData);
+
+ // If the event queue is dead at this point, turn it off since
+ // that was the last thing we want to do
+ if (_Dead)
+ {
+ _Running = false;
+ SecondLife.DebugLogStatic("Sent event queue shutdown message");
+ }
+ }
+
+ if (OnEvent != null && events != null && events.Count > 0)
+ {
+ // Fire callbacks for each event received
+ foreach (LLSDMap evt in events)
+ {
+ string msg = evt["message"].AsString();
+ LLSDMap body = (LLSDMap)evt["body"];
+
+ try { OnEvent(msg, body); }
+ catch (Exception ex) { SecondLife.LogStatic(ex.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+ }
+
+ #endregion Callback Handlers
+ }
+}
diff --git a/libsecondlife/Capabilities/EventQueueListener.cs b/libsecondlife/Capabilities/EventQueueListener.cs
new file mode 100644
index 00000000..87e21920
--- /dev/null
+++ b/libsecondlife/Capabilities/EventQueueListener.cs
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2007, 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.
+ */
+
+using System;
+using System.Net;
+
+#if !PocketPC
+
+namespace libsecondlife.Capabilities
+{
+ public class EventQueueListener
+ {
+ }
+}
+
+#endif
diff --git a/libsecondlife/Capabilities/HttpBase.cs b/libsecondlife/Capabilities/HttpBase.cs
deleted file mode 100644
index a90e5b81..00000000
--- a/libsecondlife/Capabilities/HttpBase.cs
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (c) 2007, 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.
- */
-
-using System;
-using System.Net;
-using System.IO;
-using System.Threading;
-
-namespace libsecondlife.Caps
-{
- ///
- /// Stores the current state of an HTTP request
- ///
- public class HttpRequestState
- {
- private const int BUFFER_SIZE = 1024;
-
- public byte[] RequestData;
- public byte[] ResponseData;
- public byte[] BufferRead;
- public HttpWebRequest WebRequest;
- public HttpWebResponse WebResponse;
- public Stream ResponseStream;
- public object State;
-
- internal int ResponseDataPos = 0;
-
- public HttpRequestState(HttpWebRequest webRequest)
- {
- WebRequest = webRequest;
- BufferRead = new byte[BUFFER_SIZE];
- }
- }
-
- public abstract class HttpBase
- {
- protected abstract void RequestReply(HttpRequestState state, bool success, WebException exception);
- protected abstract void RequestSent(HttpRequestState request);
-
- /// Buffer size for reading incoming responses
- protected const int BUFFER_SIZE = 1024;
- /// A default timeout for HTTP connections
- protected const int HTTP_TIMEOUT = 1 * 30 * 1000;
-
- protected HttpRequestState _RequestState;
- protected string _RequestURL;
- protected string _ProxyURL;
- protected string _ContentType;
- protected byte[] _PostData;
- protected object _State;
- protected bool _Aborted = false;
-
- public HttpBase(string requestURL)
- : this(requestURL, null, null, null, null)
- { }
-
- public HttpBase(string requestURL, string proxyURL, string contentType, byte[] postData, object state)
- {
- if (String.IsNullOrEmpty(_RequestURL))
- throw new ArgumentException("requestURL cannot be null or emtpy");
-
- _RequestURL = requestURL;
- _ProxyURL = proxyURL;
- _ContentType = contentType;
- _PostData = postData;
- _State = state;
- }
-
- public void Start()
- {
- // Client mode
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(_RequestURL);
- IAsyncResult result;
-
- // Always disable keep-alive for our purposes
- httpRequest.KeepAlive = false;
-
- // Create a state object to track this request in async callbacks
- _RequestState = new HttpRequestState(httpRequest);
- _RequestState.State = _State;
-
- if (!String.IsNullOrEmpty(_ProxyURL))
- {
- // Create a proxy object
- WebProxy proxy = new WebProxy();
-
- // Associate a new Uri object to the _wProxy object, using the proxy address
- // selected by the user
- proxy.Address = new Uri(_ProxyURL);
-
- // Finally, initialize the Web request object proxy property with the _wProxy
- // object
- httpRequest.Proxy = proxy;
- }
-
- try
- {
- if (_PostData != null)
- {
- // POST request
- _RequestState.WebRequest.Method = "POST";
- _RequestState.WebRequest.ContentLength = _PostData.Length;
- if (!String.IsNullOrEmpty(_ContentType))
- _RequestState.WebRequest.ContentType = _ContentType;
- _RequestState.RequestData = _PostData;
-
- result = (IAsyncResult)_RequestState.WebRequest.BeginGetRequestStream(
- new AsyncCallback(RequestStreamCallback), _RequestState);
- }
- else
- {
- // GET request
- result = (IAsyncResult)_RequestState.WebRequest.BeginGetResponse(
- new AsyncCallback(ResponseCallback), _RequestState);
- }
-
- // If there is a timeout, the callback fires and the request becomes aborted
- ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback),
- _RequestState, HTTP_TIMEOUT, true);
- }
- catch (WebException e)
- {
- Stop(false, e);
- return;
- }
-
- // If we get here the request has been initialized, so fire the callback for a request being started
- RequestSent(_RequestState);
- }
-
- public void Stop()
- {
- Stop(false, null);
- }
-
- protected void Stop(bool fromTimeout, WebException exception)
- {
- if (fromTimeout)
- {
- SecondLife.DebugLogStatic("HttpBase.Abort(): HTTP request timed out");
- }
- else
- {
- if (exception == null)
- {
- _Aborted = true;
- }
- else if (exception.Message.Contains("404") || exception.Message.Contains("410"))
- {
- _Aborted = true;
- SecondLife.DebugLogStatic("HttpBase.Abort(): HTTP request target is missing");
- }
- else if (exception.Message.Contains("Aborted") || exception.Message.Contains("aborted"))
- {
- // A callback threw an exception because the request is aborting, return to
- // avoid circular problems
- return;
- }
- else if (exception.Message.Contains("502"))
- {
- // Don't log anything since 502 errors are so common
- }
- else
- {
- SecondLife.LogStatic(String.Format("HttpBase.Abort(): {0} (Status: {1})", exception.Message, exception.Status),
- Helpers.LogLevel.Warning);
- }
- }
-
- // Abort the callback if it hasn't been already
- _RequestState.WebRequest.Abort();
-
- // Fire the callback for the request completing
- try { RequestReply(_RequestState, false, exception); }
- catch (Exception e) { SecondLife.LogStatic(e.ToString(), Helpers.LogLevel.Error); }
- }
-
- protected void TimeoutCallback(object state, bool timedOut)
- {
- if (timedOut) Stop(true, null);
- }
-
- protected void RequestStreamCallback(IAsyncResult result)
- {
- try
- {
- _RequestState = (HttpRequestState)result.AsyncState;
- Stream reqStream = _RequestState.WebRequest.EndGetRequestStream(result);
-
- reqStream.Write(_RequestState.RequestData, 0, _RequestState.RequestData.Length);
- reqStream.Close();
-
- IAsyncResult newResult = _RequestState.WebRequest.BeginGetResponse(new AsyncCallback(ResponseCallback), _RequestState);
-
- // If there is a timeout, the callback fires and the request becomes aborted
- ThreadPool.RegisterWaitForSingleObject(newResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback),
- _RequestState, HTTP_TIMEOUT, true);
- }
- catch (WebException e)
- {
- Stop(false, e);
- }
- }
-
- private void ResponseCallback(IAsyncResult result)
- {
- try
- {
- _RequestState = (HttpRequestState)result.AsyncState;
- _RequestState.WebResponse = (HttpWebResponse)_RequestState.WebRequest.EndGetResponse(result);
-
- // Read the response into a Stream object
- Stream responseStream = _RequestState.WebResponse.GetResponseStream();
- _RequestState.ResponseStream = responseStream;
-
- // Begin reading of the contents of the response
- IAsyncResult asynchronousInputRead = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
- new AsyncCallback(ReadCallback), _RequestState);
-
- // If there is a timeout, the callback fires and the request becomes aborted
- ThreadPool.RegisterWaitForSingleObject(asynchronousInputRead.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback),
- _RequestState, HTTP_TIMEOUT, true);
- }
- catch (WebException e)
- {
- Stop(false, e);
- }
- }
-
- protected void ReadCallback(IAsyncResult result)
- {
- try
- {
- _RequestState = (HttpRequestState)result.AsyncState;
- Stream responseStream = _RequestState.ResponseStream;
- int read = responseStream.EndRead(result);
-
- // Check if we have read the entire response
- if (read > 0)
- {
- // Create the byte array if it hasn't been created yet
- if (_RequestState.ResponseData == null || _RequestState.ResponseData.Length != _RequestState.WebResponse.ContentLength)
- _RequestState.ResponseData = new byte[_RequestState.WebResponse.ContentLength];
-
- // Copy the current buffer data in to the response variable
- Buffer.BlockCopy(_RequestState.BufferRead, 0, _RequestState.ResponseData, _RequestState.ResponseDataPos, read);
- // Increment our writing position in the response variable
- _RequestState.ResponseDataPos += read;
-
- // Continue reading the response until EndRead() returns 0
- IAsyncResult asynchronousResult = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
- new AsyncCallback(ReadCallback), _RequestState);
-
- return;
- }
- else
- {
- // Fire the callback for receiving a response
- try { RequestReply(_RequestState, true, null); }
- catch (Exception e) { SecondLife.LogStatic(e.ToString(), Helpers.LogLevel.Error); }
-
- responseStream.Close();
- }
- }
- catch (WebException e)
- {
- Stop(false, e);
- }
- }
- }
-}
diff --git a/libsecondlife/Capabilities.cs b/libsecondlife/Caps.cs
similarity index 72%
rename from libsecondlife/Capabilities.cs
rename to libsecondlife/Caps.cs
index fd70e9d4..5969c58b 100644
--- a/libsecondlife/Capabilities.cs
+++ b/libsecondlife/Caps.cs
@@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.Text;
using System.Threading;
using libsecondlife.StructuredData;
+using libsecondlife.Capabilities;
namespace libsecondlife
{
@@ -37,7 +38,7 @@ namespace libsecondlife
/// Second Life uses to communicate transactions such as teleporting or
/// group messaging
///
- public class Capabilities
+ public class Caps
{
///
/// Triggered when an event is received via the EventQueueGet
@@ -45,28 +46,21 @@ namespace libsecondlife
///
/// Event name
/// Decoded event data
- /// The CAPS system that made the call
- public delegate void EventQueueCallback(string message, StructuredData.LLSD body, CapsEventQueue eventQueue);
- ///
- /// Triggered when an HTTP call in the queue is executed and a response
- /// is received
- ///
- /// Decoded response
- /// Original capability request
- public delegate void CapsResponseCallback(StructuredData.LLSD body, HttpRequestState request);
+ /// The simulator that generated the event
+ public delegate void EventQueueCallback(string message, StructuredData.LLSD body, Simulator simulator);
/// Reference to the simulator this system is connected to
public Simulator Simulator;
internal string _SeedCapsURI;
- internal Dictionary _Caps = new Dictionary();
+ internal Dictionary _Caps = new Dictionary();
- private CapsRequest _SeedRequest;
+ private CapsClient _SeedRequest;
+ //private Capabilities2.EventQueueClient _EventQueueCap = null;
private CapsEventQueue _EventQueueCap = null;
/// Capabilities URI this system was initialized with
public string SeedCapsURI { get { return _SeedCapsURI; } }
- public ManualResetEvent CapsReceivedEvent = new ManualResetEvent(false);
/// Whether the capabilities event queue is connected and
/// listening for incoming events
@@ -88,7 +82,7 @@ namespace libsecondlife
///
///
///
- internal Capabilities(Simulator simulator, string seedcaps)
+ internal Caps(Simulator simulator, string seedcaps)
{
Simulator = simulator;
_SeedCapsURI = seedcaps;
@@ -102,14 +96,10 @@ namespace libsecondlife
(immediate ? "aborting" : "disconnecting")), Helpers.LogLevel.Info);
if (_SeedRequest != null)
- {
- _SeedRequest.Abort();
- }
+ _SeedRequest.Cancel();
if (_EventQueueCap != null)
- {
- _EventQueueCap.Disconnect(immediate);
- }
+ _EventQueueCap.Stop(immediate);
}
///
@@ -118,14 +108,14 @@ namespace libsecondlife
/// Name of the capability to request
/// The URI of the requested capability, or String.Empty if
/// the capability does not exist
- public string CapabilityURI(string capability)
+ public Uri CapabilityURI(string capability)
{
- string cap;
+ Uri cap;
if (_Caps.TryGetValue(capability, out cap))
return cap;
else
- return String.Empty;
+ return null;
}
private void MakeSeedRequest()
@@ -160,16 +150,16 @@ namespace libsecondlife
Simulator.Client.DebugLog("Making initial capabilities connection for " + Simulator.ToString());
- _SeedRequest = new CapsRequest(_SeedCapsURI, String.Empty, null);
- _SeedRequest.OnCapsResponse += new CapsRequest.CapsResponseCallback(seedRequest_OnCapsResponse);
- _SeedRequest.MakeRequest(postData, "application/xml", 0, null);
+ _SeedRequest = new CapsClient(new Uri(_SeedCapsURI));
+ _SeedRequest.OnComplete += new CapsClient.CompleteCallback(SeedRequestCompleteHandler);
+ _SeedRequest.StartRequest(postData);
}
- private void seedRequest_OnCapsResponse(LLSD response, HttpRequestState state)
+ private void SeedRequestCompleteHandler(CapsClient client, LLSD result, Exception error)
{
- if (response != null && response.Type == LLSDType.Map)
+ if (result != null && result.Type == LLSDType.Map)
{
- LLSDMap respTable = (LLSDMap)response;
+ LLSDMap respTable = (LLSDMap)result;
StringBuilder capsList = new StringBuilder();
@@ -178,20 +168,23 @@ namespace libsecondlife
capsList.Append(cap);
capsList.Append(' ');
- _Caps[cap] = respTable[cap].AsString();
+ _Caps[cap] = respTable[cap].AsUri();
}
Simulator.Client.DebugLog("Got capabilities: " + capsList.ToString());
- // Signal that we have connected to the CAPS server and received a list of capability URIs
- CapsReceivedEvent.Set();
-
if (_Caps.ContainsKey("EventQueueGet"))
{
- _EventQueueCap = new CapsEventQueue(Simulator, _Caps["EventQueueGet"]);
Simulator.Client.DebugLog("Starting event queue for " + Simulator.ToString());
+ _EventQueueCap = new CapsEventQueue(Simulator, _Caps["EventQueueGet"].AbsoluteUri);
_EventQueueCap.MakeRequest();
+
+ // FIXME: Get the new event queue client working
+ //_EventQueueCap = new Capabilities2.EventQueueClient(_Caps["EventQueueGet"]);
+ //_EventQueueCap.OnConnected += new Capabilities2.EventQueueClient.ConnectedCallback(EventQueueConnectedHandler);
+ //_EventQueueCap.OnEvent += new Capabilities2.EventQueueClient.EventCallback(EventQueueEventHandler);
+ //_EventQueueCap.Start();
}
}
else
@@ -200,5 +193,18 @@ namespace libsecondlife
MakeSeedRequest();
}
}
+
+ private void EventQueueConnectedHandler()
+ {
+ Simulator.Client.Network.RaiseConnectedEvent(Simulator);
+ }
+
+ private void EventQueueEventHandler(string eventName, LLSDMap body)
+ {
+ if (Simulator.Client.Settings.SYNC_PACKETCALLBACKS)
+ Simulator.Client.Network.CapsEvents.RaiseEvent(eventName, body, Simulator);
+ else
+ Simulator.Client.Network.CapsEvents.BeginRaiseEvent(eventName, body, Simulator);
+ }
}
}
diff --git a/libsecondlife/CapsEventQueue.cs b/libsecondlife/CapsEventQueue.cs
deleted file mode 100644
index b714a02c..00000000
--- a/libsecondlife/CapsEventQueue.cs
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (c) 2007, 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.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Text;
-using System.IO;
-using System.Threading;
-using libsecondlife.StructuredData;
-
-namespace libsecondlife
-{
- public class CapsEventQueue : HttpBase
- {
- public Simulator Simulator;
-
- protected bool _Running = false;
- protected bool _Dead = false;
-
- public bool Running { get { return _Running; } }
-
- public CapsEventQueue(Simulator simulator, string eventQueueURI)
- : this(simulator, eventQueueURI, String.Empty)
- {
- }
-
- public CapsEventQueue(Simulator simulator, string eventQueueURI, string proxy)
- : base(eventQueueURI, proxy)
- {
- Simulator = simulator;
- }
-
- protected override void RequestSent(HttpRequestState request)
- {
- ;
- }
-
- public new void MakeRequest()
- {
- // Create an EventQueueGet request
- LLSDMap request = new LLSDMap();
- request["ack"] = new LLSD();
- request["done"] = LLSD.FromBoolean(false);
-
- byte[] postData = LLSDParser.SerializeXmlBytes(request);
-
- // Create a new HttpWebRequest
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(_RequestURL);
- _RequestState = new HttpRequestState(httpRequest);
-
- if (_ProxyURL != String.Empty)
- {
- // Create a proxy object
- WebProxy proxy = new WebProxy();
-
- // Associate a new Uri object to the _wProxy object, using the proxy address
- // selected by the user
- proxy.Address = new Uri(_ProxyURL);
-
- // Finally, initialize the Web request object proxy property with the _wProxy
- // object
- httpRequest.Proxy = proxy;
- }
-
- // Always disable keep-alive for our purposes
- httpRequest.KeepAlive = false;
-
- // POST request
- _RequestState.WebRequest.Method = "POST";
- _RequestState.WebRequest.ContentLength = postData.Length;
- _RequestState.WebRequest.Headers.Add("X-SecondLife-UDP-Listen-Port", Simulator.udpPort.ToString());
- _RequestState.WebRequest.ContentType = "application/xml";
- _RequestState.RequestData = postData;
-
- IAsyncResult result = (IAsyncResult)_RequestState.WebRequest.BeginGetRequestStream(
- new AsyncCallback(EventRequestStreamCallback), _RequestState);
- }
-
- public new void MakeRequest(byte[] postData, string contentType, int udpListeningPort, object state)
- {
- // Create a new HttpWebRequest
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(_RequestURL);
- _RequestState = new HttpRequestState(httpRequest);
-
- if (_ProxyURL != String.Empty)
- {
- // Create a proxy object
- WebProxy proxy = new WebProxy();
-
- // Associate a new Uri object to the _wProxy object, using the proxy address
- // selected by the user
- proxy.Address = new Uri(_ProxyURL);
-
- // Finally, initialize the Web request object proxy property with the _wProxy
- // object
- httpRequest.Proxy = proxy;
- }
-
- // Always disable keep-alive for our purposes
- httpRequest.KeepAlive = false;
-
- try
- {
- if (postData != null)
- {
- // POST request
- _RequestState.WebRequest.Method = "POST";
- _RequestState.WebRequest.ContentLength = postData.Length;
- _RequestState.WebRequest.Headers.Add("X-SecondLife-UDP-Listen-Port", Simulator.udpPort.ToString());
- _RequestState.WebRequest.ContentType = "application/xml";
- _RequestState.RequestData = postData;
-
- IAsyncResult result = (IAsyncResult)_RequestState.WebRequest.BeginGetRequestStream(
- new AsyncCallback(EventRequestStreamCallback), _RequestState);
- }
- else
- {
- throw new ArgumentException("postData cannot be null for the event queue", "postData");
- }
- }
- catch (WebException e)
- {
- Abort(false, e);
- }
- catch (Exception e)
- {
- SecondLife.LogStatic("CapsEventQueue.MakeRequest(): " + e.ToString(), Helpers.LogLevel.Warning);
- Abort(false, null);
- }
- }
-
- public new void Abort()
- {
- Disconnect(true);
- }
-
- public void Disconnect(bool immediate)
- {
- Simulator.Client.Log(String.Format("Event queue for {0} is {1}", Simulator,
- (immediate ? "aborting" : "disconnecting")), Helpers.LogLevel.Info);
-
- _Dead = true;
-
- if (immediate)
- {
- _Running = false;
-
- // Abort the callback if it hasn't been already
- _RequestState.WebRequest.Abort();
- }
- }
-
- protected void EventRequestStreamCallback(IAsyncResult result)
- {
- bool raiseEvent = false;
-
- if (!_Dead)
- {
- if (!_Running) raiseEvent = true;
-
- // We are connected to the event queue
- _Running = true;
- }
-
- try
- {
- _RequestState = (HttpRequestState)result.AsyncState;
- Stream reqStream = _RequestState.WebRequest.EndGetRequestStream(result);
-
- reqStream.Write(_RequestState.RequestData, 0, _RequestState.RequestData.Length);
- reqStream.Close();
-
- IAsyncResult newResult = _RequestState.WebRequest.BeginGetResponse(new AsyncCallback(EventResponseCallback), _RequestState);
- }
- catch (WebException e)
- {
- Abort(false, e);
- return;
- }
-
- if (raiseEvent)
- {
- Simulator.Client.DebugLog("Capabilities event queue connected for " + Simulator.ToString());
-
- // The event queue is starting up for the first time
- Simulator.Client.Network.RaiseConnectedEvent(Simulator);
- }
- }
-
- protected void EventResponseCallback(IAsyncResult result)
- {
- try
- {
- _RequestState = (HttpRequestState)result.AsyncState;
- _RequestState.WebResponse = (HttpWebResponse)_RequestState.WebRequest.EndGetResponse(result);
-
- // Read the response into a Stream object
- Stream responseStream = _RequestState.WebResponse.GetResponseStream();
- _RequestState.ResponseStream = responseStream;
-
- // Begin reading of the contents of the response
- IAsyncResult asynchronousInputRead = responseStream.BeginRead(_RequestState.BufferRead, 0, BUFFER_SIZE,
- new AsyncCallback(ReadCallback), _RequestState);
- }
- catch (WebException e)
- {
- Abort(false, e);
- }
- }
-
- protected override void RequestReply(HttpRequestState state, bool success, WebException exception)
- {
- LLSDArray events = null;
- int ack = 0;
-
- #region Exception Handling
-
- if (exception != null)
- {
- string message = exception.Message.ToLower();
-
- // Check what kind of exception happened
- if (Helpers.StringContains(message, "404") || Helpers.StringContains(message, "410"))
- {
- Simulator.Client.Log("Closing event queue for " + Simulator.ToString() + " due to missing caps URI",
- Helpers.LogLevel.Info);
-
- _Running = false;
- _Dead = true;
- }
- else if (!Helpers.StringContains(message, "aborted") && !Helpers.StringContains(message, "502"))
- {
- Simulator.Client.Log(String.Format("Unrecognized caps exception for {0}: {1}", Simulator, exception.Message),
- Helpers.LogLevel.Warning);
- }
- }
-
- #endregion Exception Handling
-
- #region Reply Decoding
-
- // Decode successful replies from the event queue
- if (success)
- {
- LLSDMap response = (LLSDMap)LLSDParser.DeserializeXml(state.ResponseData);
-
- if (response != null)
- {
- // Parse any events returned by the event queue
- events = (LLSDArray)response["events"];
- ack = response["id"].AsInteger();
- }
- }
-
- #endregion Reply Decoding
-
- #region Make New Request
-
- if (_Running)
- {
- LLSDMap request = new LLSDMap();
- if (ack != 0) request["ack"] = LLSD.FromInteger(ack);
- else request["ack"] = new LLSD();
- request["done"] = LLSD.FromBoolean(_Dead);
-
- byte[] postData = LLSDParser.SerializeXmlBytes(request);
-
- MakeRequest(postData, "application/xml", 0, null);
-
- // If the event queue is dead at this point, turn it off since
- // that was the last thing we want to do
- if (_Dead)
- {
- _Running = false;
- SecondLife.DebugLogStatic("Sent event queue shutdown message");
- }
- }
-
- #endregion Make New Request
-
- #region Callbacks
-
- if (events != null && events.Count > 0)
- {
- // Fire callbacks for each event received
- foreach (LLSDMap evt in events)
- {
- string msg = evt["message"].AsString();
- LLSDMap body = (LLSDMap)evt["body"];
-
- //Simulator.Client.DebugLog(
- // String.Format("[{0}] Event {1}: {2}{3}", Simulator, msg, Helpers.NewLine, LLSD.LLSDDump(body, 0)));
-
- if (Simulator.Client.Settings.SYNC_PACKETCALLBACKS)
- Simulator.Client.Network.CapsEvents.RaiseEvent(msg, body, this);
- else
- Simulator.Client.Network.CapsEvents.BeginRaiseEvent(msg, body, this);
- }
- }
-
- #endregion Callbacks
- }
- }
-}
diff --git a/libsecondlife/CapsRequest.cs b/libsecondlife/CapsRequest.cs
deleted file mode 100644
index b7e05f0a..00000000
--- a/libsecondlife/CapsRequest.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2007, 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.
- */
-
-using System;
-using System.Collections;
-using System.Net;
-using System.Text;
-using libsecondlife.StructuredData;
-
-namespace libsecondlife
-{
- public class CapsRequest : HttpBase
- {
- public delegate void CapsResponseCallback(LLSD response, HttpRequestState state);
-
- public event CapsResponseCallback OnCapsResponse;
-
- public Simulator Simulator;
-
- public CapsRequest(string capsURI)
- : this(capsURI, String.Empty, null)
- {
- }
-
- public CapsRequest(string capsURI, string proxy)
- : this(capsURI, proxy, null)
- {
- }
-
- public CapsRequest(string capsURI, Simulator simulator)
- : this(capsURI, String.Empty, simulator)
- {
- }
-
- public CapsRequest(string capsURI, string proxy, Simulator simulator)
- : base(capsURI, proxy)
- {
- Simulator = simulator;
- }
-
- public new void MakeRequest()
- {
- base.MakeRequest(new byte[0], null, 0, null);
- }
-
- protected override void RequestSent(HttpRequestState request)
- {
- ;
- }
-
- protected override void RequestReply(HttpRequestState state, bool success, WebException exception)
- {
- LLSD response = null;
-
- if (success)
- {
- response = LLSDParser.DeserializeXml(state.ResponseData);
- }
- else if (exception != null && Helpers.StringContains(exception.Message, "502"))
- {
- // These are normal, retry the request automatically
- MakeRequest(state.RequestData, "application/xml", 0, null);
-
- return;
- }
-
- // Only fire the callback if there is response data or the call has
- // not been aborted. Timeouts and 502 errors don't count as aborting,
- // although 502 errors are already handled above
- if (response != null || !_Aborted)
- {
- if (OnCapsResponse != null)
- {
- try { OnCapsResponse(response, state); }
- catch (Exception e) { SecondLife.LogStatic(e.ToString(), Helpers.LogLevel.Error); }
- }
- }
- }
- }
-}
diff --git a/libsecondlife/EventDictionary.cs b/libsecondlife/EventDictionary.cs
index f84747f1..d826826c 100644
--- a/libsecondlife/EventDictionary.cs
+++ b/libsecondlife/EventDictionary.cs
@@ -159,20 +159,20 @@ namespace libsecondlife
private struct CapsCallbackWrapper
{
/// Callback to fire for this packet
- public Capabilities.EventQueueCallback Callback;
+ public Caps.EventQueueCallback Callback;
/// Name of the CAPS event
public string CapsEvent;
/// Decoded body of the CAPS event
public StructuredData.LLSD Body;
- /// Reference to the event queue that generated this event
- public CapsEventQueue EventQueue;
+ /// Reference to the simulator that generated this event
+ public Simulator Simulator;
}
/// Reference to the SecondLife client
public SecondLife Client;
- private Dictionary _EventTable =
- new Dictionary();
+ private Dictionary _EventTable =
+ new Dictionary();
private WaitCallback _ThreadPoolCallback;
///
@@ -192,7 +192,7 @@ namespace libsecondlife
/// Capability event name to register the
/// handler for
/// Callback to fire
- public void RegisterEvent(string capsEvent, Capabilities.EventQueueCallback eventHandler)
+ public void RegisterEvent(string capsEvent, Caps.EventQueueCallback eventHandler)
{
lock (_EventTable)
{
@@ -209,7 +209,7 @@ namespace libsecondlife
/// Capability event name unregister the
/// handler for
/// Callback to unregister
- public void UnregisterEvent(string capsEvent, Capabilities.EventQueueCallback eventHandler)
+ public void UnregisterEvent(string capsEvent, Caps.EventQueueCallback eventHandler)
{
lock (_EventTable)
{
@@ -223,19 +223,19 @@ namespace libsecondlife
///
/// Capability name
/// Decoded event body
- /// Reference to the event queue that
+ /// Reference to the simulator that
/// generated this event
- internal void RaiseEvent(string capsEvent, StructuredData.LLSD body, CapsEventQueue eventQueue)
+ internal void RaiseEvent(string capsEvent, StructuredData.LLSD body, Simulator simulator)
{
bool specialHandler = false;
- Capabilities.EventQueueCallback callback;
+ Caps.EventQueueCallback callback;
// Default handler first, if one exists
if (_EventTable.TryGetValue(capsEvent, out callback))
{
if (callback != null)
{
- try { callback(capsEvent, body, eventQueue); }
+ try { callback(capsEvent, body, simulator); }
catch (Exception ex) { Client.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error); }
}
}
@@ -248,7 +248,7 @@ namespace libsecondlife
if (packet != null)
{
NetworkManager.IncomingPacket incomingPacket;
- incomingPacket.Simulator = eventQueue.Simulator;
+ incomingPacket.Simulator = simulator;
incomingPacket.Packet = packet;
Client.DebugLog("Serializing " + packet.Type.ToString() + " capability with generic handler");
@@ -261,7 +261,7 @@ namespace libsecondlife
// Explicit handler next
if (_EventTable.TryGetValue(capsEvent, out callback) && callback != null)
{
- try { callback(capsEvent, body, eventQueue); }
+ try { callback(capsEvent, body, simulator); }
catch (Exception ex) { Client.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error); }
specialHandler = true;
@@ -276,12 +276,12 @@ namespace libsecondlife
///
/// Capability name
/// Decoded event body
- /// Reference to the event queue that
+ /// Reference to the simulator that
/// generated this event
- internal void BeginRaiseEvent(string capsEvent, StructuredData.LLSD body, CapsEventQueue eventQueue)
+ internal void BeginRaiseEvent(string capsEvent, StructuredData.LLSD body, Simulator simulator)
{
bool specialHandler = false;
- Capabilities.EventQueueCallback callback;
+ Caps.EventQueueCallback callback;
// Default handler first, if one exists
if (_EventTable.TryGetValue(String.Empty, out callback))
@@ -292,7 +292,7 @@ namespace libsecondlife
wrapper.Callback = callback;
wrapper.CapsEvent = capsEvent;
wrapper.Body = body;
- wrapper.EventQueue = eventQueue;
+ wrapper.Simulator = simulator;
ThreadPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper);
}
}
@@ -305,7 +305,7 @@ namespace libsecondlife
if (packet != null)
{
NetworkManager.IncomingPacket incomingPacket;
- incomingPacket.Simulator = eventQueue.Simulator;
+ incomingPacket.Simulator = simulator;
incomingPacket.Packet = packet;
Client.DebugLog("Serializing " + packet.Type.ToString() + " capability with generic handler");
@@ -322,7 +322,7 @@ namespace libsecondlife
wrapper.Callback = callback;
wrapper.CapsEvent = capsEvent;
wrapper.Body = body;
- wrapper.EventQueue = eventQueue;
+ wrapper.Simulator = simulator;
ThreadPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper);
specialHandler = true;
@@ -338,7 +338,7 @@ namespace libsecondlife
try
{
- wrapper.Callback(wrapper.CapsEvent, wrapper.Body, wrapper.EventQueue);
+ wrapper.Callback(wrapper.CapsEvent, wrapper.Body, wrapper.Simulator);
}
catch (Exception ex)
{
diff --git a/libsecondlife/GridManager.cs b/libsecondlife/GridManager.cs
index f3d42e12..6c45975d 100644
--- a/libsecondlife/GridManager.cs
+++ b/libsecondlife/GridManager.cs
@@ -29,6 +29,8 @@ using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
+using libsecondlife.StructuredData;
+using libsecondlife.Capabilities;
using libsecondlife.Packets;
namespace libsecondlife
@@ -236,15 +238,16 @@ namespace libsecondlife
///
public void RequestMapLayer(GridLayerType layer)
{
- string url = Client.Network.CurrentSim.Caps.CapabilityURI("MapLayer");
+ Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("MapLayer");
- if (!String.IsNullOrEmpty(url))
+ if (url != null)
{
- Hashtable body = new Hashtable();
- body["Flags"] = (int)layer;
+ LLSDMap body = new LLSDMap();
+ body["Flags"] = LLSD.FromInteger((int)layer);
- Client.Network.SendCapsRequest(url, body,
- new CapsRequest.CapsResponseCallback(MapLayerResponseHandler));
+ CapsClient request = new CapsClient(url);
+ request.OnComplete += new CapsClient.CompleteCallback(MapLayerResponseHandler);
+ request.StartRequest(body);
}
}
@@ -417,23 +420,23 @@ namespace libsecondlife
}
}
- private void MapLayerResponseHandler(object response, HttpRequestState state)
+ private void MapLayerResponseHandler(CapsClient client, LLSD result, Exception error)
{
- Dictionary body = (Dictionary)response;
- List