diff --git a/SLProxy/ChatConsole.cs b/SLProxy/ChatConsole.cs
index 55fa76c2..b4af315b 100644
--- a/SLProxy/ChatConsole.cs
+++ b/SLProxy/ChatConsole.cs
@@ -1,83 +1,81 @@
-/*
- * ChatConsole.cs: sample SLProxy appliation that writes chat to the console.
- * Typing on the console will send chat to Second Life.
- *
- * Copyright (c) 2006 Austin Jennings
- * Modified by Andrew Ortman ("qode") on Decemeber 21, 2006 to work with the new pregen proxy.
- * 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 SLProxy;
+/*
+ * ChatConsole.cs: sample SLProxy appliation that writes chat to the console.
+ * Typing on the console will send chat to Second Life.
+ *
+ * Copyright (c) 2006 Austin Jennings
+ * Modified by Andrew Ortman ("qode") on Decemeber 21, 2006 to work with the new pregen proxy.
+ * 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 SLProxy;
using libsecondlife;
-using libsecondlife.Packets;
-using Nwc.XmlRpc;
-
-using System;
-using System.Collections;
-using System.Net;
-using System.Threading;
-
-public class ChatConsole {
- private static SecondLife client;
- private static ProtocolManager protocolManager;
- private static Proxy proxy;
- private static LLUUID agentID;
- private static LLUUID sessionID;
-
- public static void Main(string[] args) {
- // configure the proxy
- ProxyConfig proxyConfig = new ProxyConfig("ChatConsole V2", "Austin Jennings / Andrew Ortman", args);
- proxy = new Proxy(proxyConfig);
-
- // set a delegate for when the client logs in
- proxy.SetLoginResponseDelegate(new XmlRpcResponseDelegate(Login));
-
- // add a delegate for incoming chat
- proxy.AddDelegate(PacketType.ChatFromSimulator, Direction.Incoming, new PacketDelegate(ChatFromSimulator));
-
- // start the proxy
- proxy.Start();
- }
-
- private static void Login(XmlRpcResponse response) {
- Hashtable values = (Hashtable)response.Value;
- if (values.Contains("agent_id") && values.Contains("session_id")) {
- // remember our agentID and sessionID
- agentID = new LLUUID((string)values["agent_id"]);
- sessionID = new LLUUID((string)values["session_id"]);
-
- // start a new thread that reads lines from the console
- (new Thread(new ThreadStart(ReadFromConsole))).Start();
- }
- }
-
- private static void ReadFromConsole() {
- // send text from the console in an infinite loop
- for (;;) {
- // read a line from the console
- string message = Console.ReadLine();
-
+using libsecondlife.Packets;
+using Nwc.XmlRpc;
+
+using System;
+using System.Collections;
+using System.Net;
+using System.Threading;
+
+public class ChatConsole {
+ private static Proxy proxy;
+ private static LLUUID agentID;
+ private static LLUUID sessionID;
+
+ public static void Main(string[] args) {
+ // configure the proxy
+ ProxyConfig proxyConfig = new ProxyConfig("ChatConsole V2", "Austin Jennings / Andrew Ortman", args);
+ proxy = new Proxy(proxyConfig);
+
+ // set a delegate for when the client logs in
+ proxy.SetLoginResponseDelegate(new XmlRpcResponseDelegate(Login));
+
+ // add a delegate for incoming chat
+ proxy.AddDelegate(PacketType.ChatFromSimulator, Direction.Incoming, new PacketDelegate(ChatFromSimulator));
+
+ // start the proxy
+ proxy.Start();
+ }
+
+ private static void Login(XmlRpcResponse response) {
+ Hashtable values = (Hashtable)response.Value;
+ if (values.Contains("agent_id") && values.Contains("session_id")) {
+ // remember our agentID and sessionID
+ agentID = new LLUUID((string)values["agent_id"]);
+ sessionID = new LLUUID((string)values["session_id"]);
+
+ // start a new thread that reads lines from the console
+ (new Thread(new ThreadStart(ReadFromConsole))).Start();
+ }
+ }
+
+ private static void ReadFromConsole() {
+ // send text from the console in an infinite loop
+ for (;;) {
+ // read a line from the console
+ string message = Console.ReadLine();
+
// construct a ChatFromViewer packet
ChatFromViewerPacket chat = new ChatFromViewerPacket();
chat.ChatData.Channel = 0;
@@ -85,25 +83,25 @@ public class ChatConsole {
chat.ChatData.Type = (byte)1;
chat.AgentData.AgentID = agentID;
- chat.AgentData.SessionID = sessionID;
- // inject the packet
- proxy.InjectPacket((Packet)chat, Direction.Outgoing);
- }
- }
-
- private static Packet ChatFromSimulator(Packet packet, IPEndPoint sim) {
+ chat.AgentData.SessionID = sessionID;
+ // inject the packet
+ proxy.InjectPacket((Packet)chat, Direction.Outgoing);
+ }
+ }
+
+ private static Packet ChatFromSimulator(Packet packet, IPEndPoint sim) {
// deconstruct the packet
ChatFromSimulatorPacket chat = (ChatFromSimulatorPacket)packet;
string message = Helpers.FieldToString(chat.ChatData.Message);
string name = Helpers.FieldToString(chat.ChatData.FromName);
byte audible = chat.ChatData.Audible;
- byte type = chat.ChatData.ChatType;
-
- // if this was a normal, audible message, write it to the console
- if (audible != 0 && (type == 0 || type == 1 || type == 2))
- Console.WriteLine(name + ": " + message);
-
- // return the packet unmodified
- return packet;
- }
-}
+ byte type = chat.ChatData.ChatType;
+
+ // if this was a normal, audible message, write it to the console
+ if (audible != 0 && (type == 0 || type == 1 || type == 2))
+ Console.WriteLine(name + ": " + message);
+
+ // return the packet unmodified
+ return packet;
+ }
+}
diff --git a/applications/SLIRC/frmSLIRC.cs b/applications/SLIRC/frmSLIRC.cs
index 3a21d3c5..29d9ff6f 100644
--- a/applications/SLIRC/frmSLIRC.cs
+++ b/applications/SLIRC/frmSLIRC.cs
@@ -89,7 +89,7 @@ namespace SLIRC
try
{
client = new SecondLife();
- client.Self.OnChat += new ChatCallback(Avatar_OnChat);
+ client.Self.OnChat += new MainAvatar.ChatCallback(Avatar_OnChat);
grpLogin.Enabled = true;
}
catch (Exception error)
diff --git a/libsecondlife-cs/AssetSystem/ImageManager.cs b/libsecondlife-cs/AssetSystem/ImageManager.cs
index e1dbbee7..218f77d3 100644
--- a/libsecondlife-cs/AssetSystem/ImageManager.cs
+++ b/libsecondlife-cs/AssetSystem/ImageManager.cs
@@ -42,14 +42,14 @@ namespace libsecondlife.AssetSystem
public delegate void ImageRetrievedCallback(LLUUID id, byte[] data, bool cached, string statusmsg); //this delegate is called when an image completed.
///
- /// Manages the uploading and downloading of Images from SecondLife
- ///
- public class ImageManager
- {
+ /// Manages the uploading and downloading of Images from SecondLife
+ ///
+ public class ImageManager
+ {
private SecondLife slClient;
- public enum CacheTypes {None, Memory, Disk};
+ public enum CacheTypes { None, Memory, Disk };
private CacheTypes CacheType;
private string CacheDirectory = "ImageCache";
private Dictionary CacheTable = new Dictionary();
@@ -57,40 +57,40 @@ namespace libsecondlife.AssetSystem
private ImagePacketHelpers ImagePacketHelper;
- private Dictionary htDownloadRequests = new Dictionary();
+ private Dictionary htDownloadRequests = new Dictionary();
public ImageRetrievedCallback OnImageRetrieved;
- private class TransferRequest
- {
+ private class TransferRequest
+ {
public ManualResetEvent ReceivedHeaderPacket = new ManualResetEvent(false);
public ManualResetEvent Completed = new ManualResetEvent(false);
- public bool Status;
- public string StatusMsg;
+ public bool Status;
+ public string StatusMsg;
- public uint Size;
- public uint Received;
- public uint LastPacket;
- public byte[] AssetData;
+ public uint Size;
+ public uint Received;
+ public uint LastPacket;
+ public byte[] AssetData;
public int BaseDataReceived;
- public TransferRequest()
- {
- Status = false;
- StatusMsg = "";
+ public TransferRequest()
+ {
+ Status = false;
+ StatusMsg = "";
- AssetData = null;
+ AssetData = null;
BaseDataReceived = 0;
- }
- }
+ }
+ }
///
///
///
public ImageManager(SecondLife client)
- {
+ {
Init(client, CacheTypes.None, null);
}
@@ -251,7 +251,7 @@ namespace libsecondlife.AssetSystem
///
/// The Image's AssetID
public byte[] RequestImage(LLUUID ImageID)
- {
+ {
byte[] imgData = CachedImage(ImageID);
if (imgData != null)
{
@@ -280,17 +280,17 @@ namespace libsecondlife.AssetSystem
}
// Wait for transfer to complete.
- tr.Completed.WaitOne();
+ tr.Completed.WaitOne(20000, false);
- if( tr.Status == true )
- {
- return tr.AssetData;
- }
- else
- {
- throw new Exception( "RequestImage: " + tr.StatusMsg );
- }
- }
+ if (tr.Status == true)
+ {
+ return tr.AssetData;
+ }
+ else
+ {
+ throw new Exception("RequestImage: " + tr.StatusMsg);
+ }
+ }
///
/// Requests an image from SecondLife.
@@ -335,17 +335,17 @@ namespace libsecondlife.AssetSystem
///
///
public void ImageDataCallbackHandler(Packet packet, Simulator simulator)
- {
- #if DEBUG_PACKETS
+ {
+#if DEBUG_PACKETS
slClient.DebugLog(packet);
- #endif
+#endif
ImageDataPacket reply = (ImageDataPacket)packet;
- LLUUID ImageID = reply.ImageID.ID;
-// unused? ushort Packets = reply.ImageID.Packets;
- uint Size = reply.ImageID.Size;
- byte[] Data = reply.ImageData.Data;
+ LLUUID ImageID = reply.ImageID.ID;
+ // unused? ushort Packets = reply.ImageID.Packets;
+ uint Size = reply.ImageID.Size;
+ byte[] Data = reply.ImageData.Data;
// Lookup the request that this packet is for
TransferRequest tr;
@@ -363,28 +363,28 @@ namespace libsecondlife.AssetSystem
}
// Initialize the request so that it's data buffer is the right size for the image
- tr.Size = Size;
- tr.AssetData = new byte[tr.Size];
+ tr.Size = Size;
+ tr.AssetData = new byte[tr.Size];
tr.BaseDataReceived = Data.Length;
// Copy the first block of image data into the request.
- Array.Copy(Data, 0, tr.AssetData, tr.Received, Data.Length);
- tr.Received += (uint)Data.Length;
+ Array.Copy(Data, 0, tr.AssetData, tr.Received, Data.Length);
+ tr.Received += (uint)Data.Length;
// Mark that the TransferRequest has received this header packet
tr.ReceivedHeaderPacket.Set();
- // If we've gotten all the data, mark it completed.
- if( tr.Received >= tr.Size )
- {
- tr.Status = true;
+ // If we've gotten all the data, mark it completed.
+ if (tr.Received >= tr.Size)
+ {
+ tr.Status = true;
tr.Completed.Set();
// Fire off image downloaded event
CacheImage(ImageID, tr.AssetData);
FireImageRetrieved(ImageID, tr.AssetData, false);
- }
- }
+ }
+ }
///
/// Handles the remaining Image data that did not fit in the initial ImageData packet
@@ -392,13 +392,13 @@ namespace libsecondlife.AssetSystem
///
///
public void ImagePacketCallbackHandler(Packet packet, Simulator simulator)
- {
- #if DEBUG_PACKETS
+ {
+#if DEBUG_PACKETS
slClient.DebugLog(packet);
- #endif
+#endif
ImagePacketPacket reply = (ImagePacketPacket)packet;
-
+
LLUUID ImageID = reply.ImageID.ID;
// Lookup the request for this packet
@@ -407,11 +407,11 @@ namespace libsecondlife.AssetSystem
{
tr = (TransferRequest)htDownloadRequests[ImageID];
}
- if( tr == null )
- {
+ if (tr == null)
+ {
// Received a packet that doesn't belong to any requests in our queue, strange...
- return;
- }
+ return;
+ }
// TODO: Received data should probably be put into a temporary collection that's indected by ImageID.Packet
@@ -428,42 +428,42 @@ namespace libsecondlife.AssetSystem
Array.Copy(reply.ImageData.Data, 0, tr.AssetData, tr.BaseDataReceived + (1000 * (reply.ImageID.Packet - 1)), reply.ImageData.Data.Length);
tr.Received += (uint)reply.ImageData.Data.Length;
- // If we've gotten all the data, mark it completed.
- if( tr.Received >= tr.Size )
- {
+ // If we've gotten all the data, mark it completed.
+ if (tr.Received >= tr.Size)
+ {
tr.Status = true;
tr.Completed.Set();
// Fire off image downloaded event
CacheImage(ImageID, tr.AssetData);
FireImageRetrieved(ImageID, tr.AssetData, false);
- }
- }
+ }
+ }
///
///
///
public void ImageNotInDatabaseCallbackHandler(Packet packet, Simulator simulator)
{
- #if DEBUG_PACKETS
+#if DEBUG_PACKETS
slClient.DebugLog(packet);
- #endif
+#endif
ImageNotInDatabasePacket reply = (ImageNotInDatabasePacket)packet;
LLUUID ImageID = reply.ImageID.ID;
// Lookup the request for this packet
- TransferRequest tr;
+ TransferRequest tr;
lock (htDownloadRequests)
{
tr = (TransferRequest)htDownloadRequests[ImageID];
}
- if( tr == null )
- {
+ if (tr == null)
+ {
// Received a packet that doesn't belong to any requests in our queue, strange...
- return;
- }
+ return;
+ }
tr.Status = false;
tr.StatusMsg = "Image not in database";
diff --git a/libsecondlife-cs/Avatar.cs b/libsecondlife-cs/Avatar.cs
index f6fb8bfb..6181061d 100644
--- a/libsecondlife-cs/Avatar.cs
+++ b/libsecondlife-cs/Avatar.cs
@@ -71,17 +71,17 @@ namespace libsecondlife
AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX,
/// ORed with AGENT_CONTROL_UP_* if the keyboard is being used
AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX,
- ///
+ /// Fly
AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX,
///
AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX,
- ///
+ /// Finish our current animation
AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX,
- ///
+ /// Stand up from the ground or a prim seat
AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX,
- ///
+ /// Sit on the ground at our current location
AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX,
- ///
+ /// Whether mouselook is currently enabled
AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX,
/// Legacy, used if a key was pressed for less than a certain amount of time
AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX,
@@ -112,64 +112,104 @@ namespace libsecondlife
AGENT_CONTROL_ML_LBUTTON_UP = 0x1 << CONTROL_ML_LBUTTON_UP_INDEX
}
- /// The Avatar's UUID, asset server
- public LLUUID ID;
- /// Avatar ID in Region (sim) it is in
- public uint LocalID;
- /// Full Name of Avatar
- public string Name;
- /// Active Group of Avatar
- public string GroupName;
- /// Online Status of Avatar
- public bool Online;
- /// Location of Avatar (x,y,z probably)
- public LLVector3 Position;
- /// Rotational Position of Avatar
- public LLQuaternion Rotation;
- /// Region (aka sim) the Avatar is in
- public Region CurrentRegion;
- /// Date the Avatar was Born into Second Life
- public string BornOn;
- /// Key pointing to the Profile Image
- public LLUUID ProfileImage;
- /// Key of their Partner
- public LLUUID PartnerID;
- /// Text from the About field in the Profile
- public string AboutText;
- /// Bitmask representing Want To checkboxes
- public uint WantToMask;
- /// Text field for Want To
- public string WantToText;
- /// Bitmask representing Skills checkboxes
- public uint SkillsMask;
- /// Text field for Skills
- public string SkillsText;
- /// Text from the First Life field in the Profile
- public string FirstLifeText;
- /// Key pointing to the First Life picture
- public LLUUID FirstLifeImage;
- ///
- public bool Identified;
- ///
- public bool Transacted;
- ///
- public bool AllowPublish;
- ///
- public bool MaturePublish;
- /// Charter Member type, if applicable
- public string CharterMember;
- /// Rating for Behavior
- public float Behavior;
- /// Rating for Appearance
- public float Appearance;
- /// Rating for Building
- public float Building;
- /// Text from the Languages field in the Profile
- public string LanguagesText;
- ///
- public TextureEntry Textures;
- /// URL to load in Web Profile
- public string ProfileURL;
+ ///
+ /// Positive and negative ratings
+ ///
+ public struct Statistics
+ {
+ /// Positive ratings for Behavior
+ public int BehaviorPositive;
+ /// Negative ratings for Behavior
+ public int BehaviorNegative;
+ /// Positive ratings for Appearance
+ public int AppearancePositive;
+ /// Negative ratings for Appearance
+ public int AppearanceNegative;
+ /// Positive ratings for Building
+ public int BuildingPositive;
+ /// Negative ratings for Building
+ public int BuildingNegative;
+ }
+
+ ///
+ /// Avatar properties including about text, profile URL, image IDs and
+ /// publishing settings
+ ///
+ public struct Properties
+ {
+ /// Should this profile be published on the web
+ public bool AllowPublish;
+ /// First Life about text
+ public string FirstLifeText;
+ /// First Life image ID
+ public LLUUID FirstLifeImage;
+ ///
+ public LLUUID Partner;
+ ///
+ public string AboutText;
+ ///
+ public string BornOn;
+ ///
+ public string CharterMember;
+ /// Profile image ID
+ public LLUUID ProfileImage;
+ /// Is this a mature profile
+ public bool MaturePublish;
+ ///
+ public bool Identified;
+ ///
+ public bool Transacted;
+ /// Web URL for this profile
+ public string ProfileURL;
+ }
+
+ ///
+ /// Avatar interests including spoken languages, skills, and "want to"
+ /// choices
+ ///
+ public struct Interests
+ {
+ /// Languages profile field
+ public string LanguagesText;
+ ///
+ public uint SkillsMask;
+ ///
+ public string SkillsText;
+ ///
+ public uint WantToMask;
+ ///
+ public string WantToText;
+ }
+
+
+ /// UUID for this avatar
+ public LLUUID ID = LLUUID.Zero;
+ /// Temporary ID for this avatar, local to the current region
+ public uint LocalID = 0;
+ /// Full name
+ public string Name = String.Empty;
+ /// Active group
+ public string GroupName = String.Empty;
+ /// Online status
+ public bool Online = false;
+ /// Positive and negative ratings
+ public Statistics ProfileStatistics = new Statistics();
+ /// Avatar properties including about text, profile URL, image IDs and
+ /// publishing settings
+ public Properties ProfileProperties = new Properties();
+ /// Avatar interests including spoken languages, skills, and "want to"
+ /// choices
+ public Interests ProfileInterests = new Interests();
+ /// Local location, relative to the sim or what the avatar is
+ /// sitting on
+ public LLVector3 Position = LLVector3.Zero;
+ /// Rotational position, relative to the sim or what the avatar
+ /// is sitting on
+ public LLQuaternion Rotation = LLQuaternion.Identity;
+ /// Region the avatar is in
+ public Region CurrentRegion = null;
+ /// Textures for this avatars clothing
+ public TextureEntry Textures = new TextureEntry();
/// Gets the local ID of the prim the avatar is sitting on,
/// zero if the avatar is not currently sitting
@@ -180,39 +220,42 @@ namespace libsecondlife
internal uint sittingOn = 0;
- protected const int CONTROL_AT_POS_INDEX = 0;
- protected const int CONTROL_AT_NEG_INDEX = 1;
- protected const int CONTROL_LEFT_POS_INDEX = 2;
- protected const int CONTROL_LEFT_NEG_INDEX = 3;
- protected const int CONTROL_UP_POS_INDEX = 4;
- protected const int CONTROL_UP_NEG_INDEX = 5;
- protected const int CONTROL_PITCH_POS_INDEX = 6;
- protected const int CONTROL_PITCH_NEG_INDEX = 7;
- protected const int CONTROL_YAW_POS_INDEX = 8;
- protected const int CONTROL_YAW_NEG_INDEX = 9;
- protected const int CONTROL_FAST_AT_INDEX = 10;
- protected const int CONTROL_FAST_LEFT_INDEX = 11;
- protected const int CONTROL_FAST_UP_INDEX = 12;
- protected const int CONTROL_FLY_INDEX = 13;
- protected const int CONTROL_STOP_INDEX = 14;
- protected const int CONTROL_FINISH_ANIM_INDEX = 15;
- protected const int CONTROL_STAND_UP_INDEX = 16;
- protected const int CONTROL_SIT_ON_GROUND_INDEX = 17;
- protected const int CONTROL_MOUSELOOK_INDEX = 18;
- protected const int CONTROL_NUDGE_AT_POS_INDEX = 19;
- protected const int CONTROL_NUDGE_AT_NEG_INDEX = 20;
- protected const int CONTROL_NUDGE_LEFT_POS_INDEX = 21;
- protected const int CONTROL_NUDGE_LEFT_NEG_INDEX = 22;
- protected const int CONTROL_NUDGE_UP_POS_INDEX = 23;
- protected const int CONTROL_NUDGE_UP_NEG_INDEX = 24;
- protected const int CONTROL_TURN_LEFT_INDEX = 25;
- protected const int CONTROL_TURN_RIGHT_INDEX = 26;
- protected const int CONTROL_AWAY_INDEX = 27;
- protected const int CONTROL_LBUTTON_DOWN_INDEX = 28;
- protected const int CONTROL_LBUTTON_UP_INDEX = 29;
- protected const int CONTROL_ML_LBUTTON_DOWN_INDEX = 30;
- protected const int CONTROL_ML_LBUTTON_UP_INDEX = 31;
- protected const int TOTAL_CONTROLS = 32;
- }
+ private const int CONTROL_AT_POS_INDEX = 0;
+ private const int CONTROL_AT_NEG_INDEX = 1;
+ private const int CONTROL_LEFT_POS_INDEX = 2;
+ private const int CONTROL_LEFT_NEG_INDEX = 3;
+ private const int CONTROL_UP_POS_INDEX = 4;
+ private const int CONTROL_UP_NEG_INDEX = 5;
+ private const int CONTROL_PITCH_POS_INDEX = 6;
+ private const int CONTROL_PITCH_NEG_INDEX = 7;
+ private const int CONTROL_YAW_POS_INDEX = 8;
+ private const int CONTROL_YAW_NEG_INDEX = 9;
+ private const int CONTROL_FAST_AT_INDEX = 10;
+ private const int CONTROL_FAST_LEFT_INDEX = 11;
+ private const int CONTROL_FAST_UP_INDEX = 12;
+ private const int CONTROL_FLY_INDEX = 13;
+ private const int CONTROL_STOP_INDEX = 14;
+ private const int CONTROL_FINISH_ANIM_INDEX = 15;
+ private const int CONTROL_STAND_UP_INDEX = 16;
+ private const int CONTROL_SIT_ON_GROUND_INDEX = 17;
+ private const int CONTROL_MOUSELOOK_INDEX = 18;
+ private const int CONTROL_NUDGE_AT_POS_INDEX = 19;
+ private const int CONTROL_NUDGE_AT_NEG_INDEX = 20;
+ private const int CONTROL_NUDGE_LEFT_POS_INDEX = 21;
+ private const int CONTROL_NUDGE_LEFT_NEG_INDEX = 22;
+ private const int CONTROL_NUDGE_UP_POS_INDEX = 23;
+ private const int CONTROL_NUDGE_UP_NEG_INDEX = 24;
+ private const int CONTROL_TURN_LEFT_INDEX = 25;
+ private const int CONTROL_TURN_RIGHT_INDEX = 26;
+ private const int CONTROL_AWAY_INDEX = 27;
+ private const int CONTROL_LBUTTON_DOWN_INDEX = 28;
+ private const int CONTROL_LBUTTON_UP_INDEX = 29;
+ private const int CONTROL_ML_LBUTTON_DOWN_INDEX = 30;
+ private const int CONTROL_ML_LBUTTON_UP_INDEX = 31;
+ private const int TOTAL_CONTROLS = 32;
+ public Avatar()
+ {
+ }
+ }
}
diff --git a/libsecondlife-cs/AvatarManager.cs b/libsecondlife-cs/AvatarManager.cs
index b7d274a7..fc5f93b4 100644
--- a/libsecondlife-cs/AvatarManager.cs
+++ b/libsecondlife-cs/AvatarManager.cs
@@ -32,52 +32,63 @@ using libsecondlife.Packets;
namespace libsecondlife
{
///
- /// Class to manage multiple Avatars
+ /// Retrieve friend status notifications, and retrieve avatar names and
+ /// profiles
///
public class AvatarManager
{
///
/// Triggered after friend request packet is sent out
///
- ///
- ///
+ ///
+ ///
public delegate void FriendNotificationCallback(LLUUID agentID, bool online);
///
/// Triggered when a UUIDNameReply is received
///
///
- public delegate void AgentNamesCallback(Dictionary names);
+ public delegate void AvatarNamesCallback(Dictionary names);
///
- /// Triggered when Avatar properties are received (AvatarPropertiesReply)
+ /// Triggered when a response for avatar statistics (ratings) is returned
///
- ///
- public delegate void AvatarPropertiesCallback(Avatar avatar);
+ ///
+ ///
+ public delegate void AvatarStatisticsCallback(LLUUID avatarID, Avatar.Statistics statistics);
///
- ///
+ /// Triggered when a response for avatar interests is returned
///
- ///
- public delegate void AvatarNameCallback(Avatar avatar);
+ ///
+ ///
+ public delegate void AvatarInterestsCallback(LLUUID avatarID, Avatar.Interests interests);
///
- ///
+ /// Triggered when avatar properties are received (AvatarPropertiesReply)
///
- ///
- public delegate void AvatarStatisticsCallback(Avatar avatar);
+ ///
+ ///
+ public delegate void AvatarPropertiesCallback(LLUUID avatarID, Avatar.Properties properties);
///
- /// Triggered when a response for Avatar Interests is returned
+ /// Triggered when a name search reply is received (AvatarPickerReply)
///
- ///
- public delegate void AvatarInterestsCallback(Avatar avatar);
+ ///
+ ///
+ public delegate void AvatarNameSearchCallback(LLUUID queryID, Dictionary avatars);
+
/// Triggered whenever a friend comes online or goes offline
public event FriendNotificationCallback OnFriendNotification;
+ ///
+ public event AvatarNamesCallback OnAvatarNames;
+ ///
+ public event AvatarStatisticsCallback OnAvatarStatistics;
+ ///
+ public event AvatarInterestsCallback OnAvatarInterests;
+ ///
+ public event AvatarPropertiesCallback OnAvatarProperties;
+ ///
+ public event AvatarNameSearchCallback OnAvatarNameSearch;
- private Dictionary Avatars;
private SecondLife Client;
- private AgentNamesCallback OnAgentNames;
- private Dictionary AvatarPropertiesCallbacks;
- private Dictionary AvatarStatisticsCallbacks;
- private Dictionary AvatarInterestsCallbacks;
- private Dictionary ManualResetEvents;
+
///
/// Represents other avatars
///
@@ -85,386 +96,54 @@ namespace libsecondlife
public AvatarManager(SecondLife client)
{
Client = client;
- Avatars = new Dictionary();
- //Callback Dictionaries
- AvatarPropertiesCallbacks = new Dictionary();
- AvatarStatisticsCallbacks = new Dictionary();
- AvatarInterestsCallbacks = new Dictionary();
- //ManualResetEvent Dictionary
- ManualResetEvents = new Dictionary();
+
// Friend notification callback
NetworkManager.PacketCallback callback = new NetworkManager.PacketCallback(FriendNotificationHandler);
Client.Network.RegisterCallback(PacketType.OnlineNotification, callback);
Client.Network.RegisterCallback(PacketType.OfflineNotification, callback);
- Client.Network.RegisterCallback(PacketType.UUIDNameReply, new NetworkManager.PacketCallback(GetAgentNameHandler));
+
+ // Avatar profile callbacks
Client.Network.RegisterCallback(PacketType.AvatarPropertiesReply, new NetworkManager.PacketCallback(AvatarPropertiesHandler));
Client.Network.RegisterCallback(PacketType.AvatarStatisticsReply, new NetworkManager.PacketCallback(AvatarStatisticsHandler));
Client.Network.RegisterCallback(PacketType.AvatarInterestsReply, new NetworkManager.PacketCallback(AvatarInterestsHandler));
+
+ // Other callbacks
+ Client.Network.RegisterCallback(PacketType.UUIDNameReply, new NetworkManager.PacketCallback(AvatarNameHandler));
+ Client.Network.RegisterCallback(PacketType.AvatarPickerReply, new NetworkManager.PacketCallback(AvatarPickerReplyHandler));
}
-
///
- /// Add an Avatar into the Avatars Dictionary
+ /// Request a single avatar name
///
- /// Filled-out Avatar class to insert
- public void AddAvatar(Avatar avatar)
+ /// The avatar key to retrieve a name for
+ public void RequestAvatarName(LLUUID id)
{
- lock (Avatars)
- {
- Avatars[avatar.ID] = avatar;
- }
+ UUIDNameRequestPacket request = new UUIDNameRequestPacket();
+ request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[1];
+ request.UUIDNameBlock[0] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
+ request.UUIDNameBlock[0].ID = id;
+
+ Client.Network.SendPacket(request);
}
///
- /// Used to search all known Avatars for a particular Avatar Key
+ /// Request a list of avatar names
///
- ///
- ///
- public bool Contains(LLUUID id)
+ /// The avatar keys to retrieve names for
+ public void RequestAvatarNames(List ids)
{
- return Avatars.ContainsKey(id);
+ UUIDNameRequestPacket request = new UUIDNameRequestPacket();
+ request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[ids.Count];
+
+ for (int i = 0; i < ids.Count; i++)
+ {
+ request.UUIDNameBlock[i] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
+ request.UUIDNameBlock[i].ID = ids[i];
+ }
+
+ Client.Network.SendPacket(request);
}
- ///
- /// Refresh Avatar Profile information
- ///
- ///
- public void UpdateAvatar(Avatar a)
- {
- //Basic profile properties
- AvatarPropertiesUpdatePacket apup = new AvatarPropertiesUpdatePacket();
- AvatarPropertiesUpdatePacket.AgentDataBlock adb = new AvatarPropertiesUpdatePacket.AgentDataBlock();
- adb.AgentID = a.ID;
- adb.SessionID = Client.Network.SessionID;
- apup.AgentData = adb;
- AvatarPropertiesUpdatePacket.PropertiesDataBlock pdb = new AvatarPropertiesUpdatePacket.PropertiesDataBlock();
- pdb.AllowPublish = a.AllowPublish;
- pdb.FLAboutText = Helpers.StringToField(a.FirstLifeText);
- pdb.FLImageID = a.FirstLifeImage;
- pdb.ImageID = a.ProfileImage;
- pdb.MaturePublish = a.MaturePublish;
- pdb.ProfileURL = Helpers.StringToField(a.ProfileURL);
- apup.PropertiesData = pdb;
- //Intrests
- AvatarInterestsUpdatePacket aiup = new AvatarInterestsUpdatePacket();
- AvatarInterestsUpdatePacket.AgentDataBlock iadb = new AvatarInterestsUpdatePacket.AgentDataBlock();
- iadb.AgentID = a.ID;
- iadb.SessionID = Client.Network.SessionID;
- aiup.AgentData = iadb;
- AvatarInterestsUpdatePacket.PropertiesDataBlock ipdb = new AvatarInterestsUpdatePacket.PropertiesDataBlock();
- ipdb.LanguagesText = Helpers.StringToField(a.LanguagesText);
- ipdb.SkillsMask = a.SkillsMask;
- ipdb.SkillsText = Helpers.StringToField(a.SkillsText);
- ipdb.WantToMask = a.WantToMask;
- ipdb.WantToText = Helpers.StringToField(a.WantToText);
- aiup.PropertiesData = ipdb;
- //Send packets
- Client.Network.SendPacket(apup);
- Client.Network.SendPacket(aiup);
- }
-
- ///
- /// This function will only check if the avatar name exists locally,
- /// it will not do any networking calls to fetch the name
- ///
- /// The avatar name, or an empty string if it's not found
- public string LocalAvatarNameLookup(LLUUID id)
- {
- string name = "";
-
- lock (Avatars)
- {
- if (Avatars.ContainsKey(id))
- {
- name = Avatars[id].Name;
- }
- }
-
- return name;
- }
- ///
- /// Get an avatar's name, either from the cache or request it.
- /// This function does block.
- ///
- /// Key to look up
- ///
- public string GetAvatarName(LLUUID key)
- {
- //Short circuit the cache lookup in GetAvatarNames
- string name = LocalAvatarNameLookup(key);
- if (name != "") return name;
-
- //Add to the dictionary
- ManualResetEvents.Add(key, new ManualResetEvent(false));
-
- //Call function
- BeginGetAvatarName(key, null);
-
- //Start the blocking
- ManualResetEvents[key].WaitOne();
-
- //Clean up
- ManualResetEvents[key] = null;
-
- //Return
- return Avatars[key].Name;
- }
-
- ///
- ///
- ///
- ///
- public void BeginGetAvatarName(LLUUID id, AgentNamesCallback anc)
- {
- // TODO: BeginGetAvatarNames is pretty bulky, rewrite a simple version here
-
- List ids = new List();
- ids.Add(id);
- BeginGetAvatarNames(ids, anc);
- }
-
- ///
- ///
- ///
- ///
- public void BeginGetAvatarNames(List ids, AgentNamesCallback anc)
- {
- if (anc != null)
- {
- OnAgentNames = anc;
- }
-
- Dictionary havenames = new Dictionary();
- List neednames = new List();
-
- // Fire callbacks for the ones we already have cached
- foreach (LLUUID id in ids)
- {
- if (Avatars.ContainsKey(id))
- {
- havenames[id] = Avatars[id].Name;
- //Short circuit the lookup process
- if (ManualResetEvents.ContainsKey(id))
- {
- ManualResetEvents[id].Set();
- return;
- }
- }
- else
- {
- neednames.Add(id);
- }
- }
-
- if (havenames.Count > 0 && OnAgentNames != null)
- {
- OnAgentNames(havenames);
-
- }
-
- if (neednames.Count > 0)
- {
- UUIDNameRequestPacket request = new UUIDNameRequestPacket();
-
- request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[neednames.Count];
-
- for (int i = 0; i < neednames.Count; i++)
- {
- request.UUIDNameBlock[i] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
- request.UUIDNameBlock[i].ID = neednames[i];
- }
-
- Client.Network.SendPacket(request);
- }
- }
-
- ///
- /// Process an incoming UUIDNameReply Packet and insert Full Names into the Avatars Dictionary
- ///
- /// Incoming Packet to process
- /// Unused
- private void GetAgentNameHandler(Packet packet, Simulator simulator)
- {
- Dictionary names = new Dictionary();
- UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet;
-
- lock (Avatars)
- {
- foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock)
- {
- if (!Avatars.ContainsKey(block.ID))
- {
- Avatars[block.ID] = new Avatar();
- Avatars[block.ID].ID = block.ID;
- }
-
- Avatars[block.ID].Name = Helpers.FieldToString(block.FirstName) +
- " " + Helpers.FieldToString(block.LastName);
-
- names[block.ID] = Avatars[block.ID].Name;
- if (ManualResetEvents.ContainsKey(block.ID))
- {
- //Stop Blocking
- ManualResetEvents[block.ID].Set();
- }
- }
- }
-
- if (OnAgentNames != null)
- {
- OnAgentNames(names);
- }
- }
- ///
- /// Handle incoming friend notifications
- ///
- ///
- ///
- private void FriendNotificationHandler(Packet packet, Simulator simulator)
- {
- List requestids = new List();
-
- if (packet.Type == PacketType.OnlineNotification)
- {
- // If the agent is online...
- foreach (OnlineNotificationPacket.AgentBlockBlock block in ((OnlineNotificationPacket)packet).AgentBlock)
- {
- lock (Avatars)
- {
- if (!Avatars.ContainsKey(block.AgentID))
- {
- // Mark this avatar for a name request
- requestids.Add(block.AgentID);
-
- Avatars[block.AgentID] = new Avatar();
- Avatars[block.AgentID].ID = block.AgentID;
- }
-
- Avatars[block.AgentID].Online = true;
- }
-
- if (OnFriendNotification != null)
- {
- OnFriendNotification(block.AgentID, true);
- }
- }
- }
- else if (packet.Type == PacketType.OfflineNotification)
- {
- // If the agent is Offline...
- foreach (OfflineNotificationPacket.AgentBlockBlock block in ((OfflineNotificationPacket)packet).AgentBlock)
- {
- lock (Avatars)
- {
- if (!Avatars.ContainsKey(block.AgentID))
- {
- // Mark this avatar for a name request
- requestids.Add(block.AgentID);
-
- Avatars[block.AgentID] = new Avatar();
- Avatars[block.AgentID].ID = block.AgentID;
- }
-
- Avatars[block.AgentID].Online = false;
- }
-
- if (OnFriendNotification != null)
- {
- OnFriendNotification(block.AgentID, true);
- }
- }
- }
-
- if (requestids.Count > 0)
- {
- BeginGetAvatarNames(requestids, null);
- }
- }
- ///
- /// Handles incoming avatar statistics. What are those ?
- ///
- ///
- ///
- private void AvatarStatisticsHandler(Packet packet, Simulator simulator)
- {
- AvatarStatisticsReplyPacket asr = (AvatarStatisticsReplyPacket)packet;
- lock(Avatars)
- {
- Avatar av;
- if (!Avatars.ContainsKey(asr.AvatarData.AvatarID))
- {
- av = new Avatar();
- av.ID = asr.AvatarData.AvatarID;
- }
- else
- {
- av = Avatars[asr.AvatarData.AvatarID];
- }
-
- foreach(AvatarStatisticsReplyPacket.StatisticsDataBlock b in asr.StatisticsData)
- {
- string n = Helpers.FieldToString(b.Name);
- if(n.Equals("Behavior"))
- {
- av.Behavior = b.Positive;
- }
- else if(n.Equals("Appearance"))
- {
- av.Appearance = b.Positive;
- }
- else if(n.Equals("Building"))
- {
- av.Building = b.Positive;
- }
- }
-
- //Call it
- if (AvatarStatisticsCallbacks.ContainsKey(av.ID) && AvatarStatisticsCallbacks[av.ID] != null)
- AvatarStatisticsCallbacks[av.ID](av);
- }
- }
- ///
- /// Process incoming Avatar properties (profile data)
- ///
- ///
- ///
- private void AvatarPropertiesHandler(Packet packet, Simulator sim)
- {
- Avatar av;
- AvatarPropertiesReplyPacket reply = (AvatarPropertiesReplyPacket)packet;
- lock(Avatars)
- {
- if (!Avatars.ContainsKey(reply.AgentData.AvatarID))
- {
- //not in our "cache", create a new object
- av = new Avatar();
- }
- else
- {
- //Cache hit, modify existing avatar
- av = Avatars[reply.AgentData.AvatarID];
- }
- av.ID = reply.AgentData.AvatarID;
- av.ProfileImage = reply.PropertiesData.ImageID;
- av.FirstLifeImage = reply.PropertiesData.FLImageID;
- av.PartnerID = reply.PropertiesData.PartnerID;
- av.AboutText = Helpers.FieldToString(reply.PropertiesData.AboutText);
-
- av.FirstLifeText = Helpers.FieldToString(reply.PropertiesData.FLAboutText);
- av.BornOn = Helpers.FieldToString(reply.PropertiesData.BornOn);
- av.CharterMember = Helpers.FieldToString(reply.PropertiesData.CharterMember);
- av.AllowPublish = reply.PropertiesData.AllowPublish;
- av.MaturePublish = reply.PropertiesData.MaturePublish;
- av.Identified = reply.PropertiesData.Identified;
- av.Transacted = reply.PropertiesData.Transacted;
- av.ProfileURL = Helpers.FieldToString(reply.PropertiesData.ProfileURL);
- //reassign in the cache
- Avatars[av.ID] = av;
- //Heaven forbid that we actually get a packet we didn't ask for.
- if (AvatarPropertiesCallbacks.ContainsKey(av.ID) && AvatarPropertiesCallbacks[av.ID] != null)
- AvatarPropertiesCallbacks[av.ID](av);
- }
- }
///
/// Start a request for Avatar Properties
///
@@ -472,54 +151,184 @@ namespace libsecondlife
///
///
///
- public void BeginAvatarPropertiesRequest(LLUUID avatarid, AvatarPropertiesCallback apc, AvatarStatisticsCallback asc, AvatarInterestsCallback aic)
+ public void RequestAvatarProperties(LLUUID avatarid)
{
- //Set teh callback!
- AvatarPropertiesCallbacks[avatarid] = apc;
- AvatarStatisticsCallbacks[avatarid] = asc;
- AvatarInterestsCallbacks[avatarid] = aic;
- //Oh noes
- //Packet construction, good times
AvatarPropertiesRequestPacket aprp = new AvatarPropertiesRequestPacket();
- AvatarPropertiesRequestPacket.AgentDataBlock adb = new AvatarPropertiesRequestPacket.AgentDataBlock();
- adb.AgentID = Client.Network.AgentID;
- adb.SessionID = Client.Network.SessionID;
- adb.AvatarID = avatarid;
- aprp.AgentData = adb;
- //send the packet!
+
+ aprp.AgentData.AgentID = Client.Network.AgentID;
+ aprp.AgentData.SessionID = Client.Network.SessionID;
+ aprp.AgentData.AvatarID = avatarid;
+
Client.Network.SendPacket(aprp);
}
+
///
- /// Process incoming Avatar Interests information
+ /// Search for an avatar (first name, last name, and uuid)
+ ///
+ /// The name to search for
+ /// An ID to associate with this query
+ public void RequestAvatarNameSearch(string name, LLUUID queryID)
+ {
+ AvatarPickerRequestPacket aprp = new AvatarPickerRequestPacket();
+
+ aprp.AgentData.AgentID = Client.Network.AgentID;
+ aprp.AgentData.SessionID = Client.Network.SessionID;
+ aprp.AgentData.QueryID = queryID;
+ aprp.Data.Name = Helpers.StringToField(name);
+
+ Client.Network.SendPacket(aprp);
+ }
+
+ ///
+ /// Process an incoming UUIDNameReply Packet and insert Full Names into the Avatars Dictionary
+ ///
+ /// Incoming Packet to process
+ /// Unused
+ private void AvatarNameHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarNames != null)
+ {
+ Dictionary names = new Dictionary();
+ UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet;
+
+ foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock)
+ {
+ names[block.ID] = Helpers.FieldToString(block.FirstName) +
+ " " + Helpers.FieldToString(block.LastName);
+ }
+
+ OnAvatarNames(names);
+ }
+ }
+
+ ///
+ /// Handle incoming friend notifications
///
///
///
+ private void FriendNotificationHandler(Packet packet, Simulator simulator)
+ {
+ if (OnFriendNotification != null)
+ {
+ if (packet.Type == PacketType.OnlineNotification)
+ {
+ // If the agent is online
+ foreach (OnlineNotificationPacket.AgentBlockBlock block in ((OnlineNotificationPacket)packet).AgentBlock)
+ OnFriendNotification(block.AgentID, true);
+ }
+ else if (packet.Type == PacketType.OfflineNotification)
+ {
+ // If the agent is offline
+ foreach (OfflineNotificationPacket.AgentBlockBlock block in ((OfflineNotificationPacket)packet).AgentBlock)
+ OnFriendNotification(block.AgentID, true);
+ }
+ }
+ }
+
+ ///
+ /// Handles incoming avatar statistics, such as ratings
+ ///
+ ///
+ ///
+ private void AvatarStatisticsHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarStatistics != null)
+ {
+ AvatarStatisticsReplyPacket asr = (AvatarStatisticsReplyPacket)packet;
+ Avatar.Statistics stats = new Avatar.Statistics();
+
+ foreach (AvatarStatisticsReplyPacket.StatisticsDataBlock b in asr.StatisticsData)
+ {
+ string n = Helpers.FieldToString(b.Name);
+
+ switch (n)
+ {
+ case "Behavior":
+ stats.BehaviorPositive = b.Positive;
+ stats.BehaviorNegative = b.Negative;
+ break;
+ case "Appearance":
+ stats.AppearancePositive = b.Positive;
+ stats.AppearanceNegative = b.Negative;
+ break;
+ case "Building":
+ stats.AppearancePositive = b.Positive;
+ stats.AppearanceNegative = b.Negative;
+ break;
+ default:
+ Client.Log("Got an AvatarStatistics block with the name " + n, Helpers.LogLevel.Warning);
+ break;
+ }
+ }
+
+ OnAvatarStatistics(asr.AvatarData.AvatarID, stats);
+ }
+ }
+
+ ///
+ /// Process incoming avatar properties (profile data)
+ ///
+ ///
+ ///
+ private void AvatarPropertiesHandler(Packet packet, Simulator sim)
+ {
+ if (OnAvatarProperties != null)
+ {
+ AvatarPropertiesReplyPacket reply = (AvatarPropertiesReplyPacket)packet;
+ Avatar.Properties properties = new Avatar.Properties();
+
+ properties.ProfileImage = reply.PropertiesData.ImageID;
+ properties.FirstLifeImage = reply.PropertiesData.FLImageID;
+ properties.Partner = reply.PropertiesData.PartnerID;
+ properties.AboutText = Helpers.FieldToString(reply.PropertiesData.AboutText);
+ properties.FirstLifeText = Helpers.FieldToString(reply.PropertiesData.FLAboutText);
+ properties.BornOn = Helpers.FieldToString(reply.PropertiesData.BornOn);
+ properties.CharterMember = Helpers.FieldToString(reply.PropertiesData.CharterMember);
+ properties.AllowPublish = reply.PropertiesData.AllowPublish;
+ properties.MaturePublish = reply.PropertiesData.MaturePublish;
+ properties.Identified = reply.PropertiesData.Identified;
+ properties.Transacted = reply.PropertiesData.Transacted;
+ properties.ProfileURL = Helpers.FieldToString(reply.PropertiesData.ProfileURL);
+
+ OnAvatarProperties(reply.AgentData.AvatarID, properties);
+ }
+ }
+
+ ///
+ /// Process incoming Avatar Interests information
+ ///
private void AvatarInterestsHandler(Packet packet, Simulator simulator)
{
- AvatarInterestsReplyPacket airp = (AvatarInterestsReplyPacket)packet;
- Avatar av;
- lock (Avatars)
+ if (OnAvatarInterests != null)
{
- if (!Avatars.ContainsKey(airp.AgentData.AvatarID))
- {
- //not in our "cache", create a new object
- av = new Avatar();
- av.ID = airp.AgentData.AvatarID;
- }
- else
- {
- //Cache hit, modify existing avatar
- av = Avatars[airp.AgentData.AvatarID];
- }
- //The rest of the properties, thanks LL.
- av.WantToMask = airp.PropertiesData.WantToMask;
- av.WantToText = Helpers.FieldToString(airp.PropertiesData.WantToText);
- av.SkillsMask = airp.PropertiesData.SkillsMask;
- av.SkillsText = Helpers.FieldToString(airp.PropertiesData.SkillsText);
- av.LanguagesText = Helpers.FieldToString(airp.PropertiesData.LanguagesText);
+ AvatarInterestsReplyPacket airp = (AvatarInterestsReplyPacket)packet;
+ Avatar.Interests interests = new Avatar.Interests();
+
+ interests.WantToMask = airp.PropertiesData.WantToMask;
+ interests.WantToText = Helpers.FieldToString(airp.PropertiesData.WantToText);
+ interests.SkillsMask = airp.PropertiesData.SkillsMask;
+ interests.SkillsText = Helpers.FieldToString(airp.PropertiesData.SkillsText);
+ interests.LanguagesText = Helpers.FieldToString(airp.PropertiesData.LanguagesText);
+
+ OnAvatarInterests(airp.AgentData.AvatarID, interests);
+ }
+ }
+
+ private void AvatarPickerReplyHandler(Packet packet, Simulator simulator)
+ {
+ if (OnAvatarNameSearch != null)
+ {
+ AvatarPickerReplyPacket reply = (AvatarPickerReplyPacket)packet;
+ Dictionary avatars = new Dictionary();
+
+ foreach (AvatarPickerReplyPacket.DataBlock block in reply.Data)
+ {
+ avatars[block.AvatarID] = Helpers.FieldToString(block.FirstName) +
+ " " + Helpers.FieldToString(block.LastName);
+ }
+
+ OnAvatarNameSearch(reply.AgentData.QueryID, avatars);
}
- if (AvatarInterestsCallbacks.ContainsKey(airp.AgentData.AvatarID) && AvatarInterestsCallbacks[airp.AgentData.AvatarID] != null)
- AvatarInterestsCallbacks[airp.AgentData.AvatarID](av);
}
}
}
diff --git a/libsecondlife-cs/MainAvatar.cs b/libsecondlife-cs/MainAvatar.cs
index 6945c251..42d1c738 100644
--- a/libsecondlife-cs/MainAvatar.cs
+++ b/libsecondlife-cs/MainAvatar.cs
@@ -32,114 +32,136 @@ using libsecondlife.Packets;
namespace libsecondlife
{
- ///
- /// Triggered on incoming chat messages
- ///
- /// Text of chat message
- /// Is this normal audible chat or not.
- /// Type of chat (whisper,shout,status,etc)
- /// Type of source (Agent / Object / ???)
- /// Text name of sending Avatar/Object
- ///
- public delegate void ChatCallback(string message, byte audible, byte type, byte sourcetype,
- string fromName, LLUUID id, LLUUID ownerid, LLVector3 position);
-
- ///
- /// Triggered when a script pops up a dialog box
- ///
- /// The dialog box message
- /// Name of the object that sent the dialog
- /// Image to be displayed in the dialog
- /// ID of the object that sent the dialog
- /// First name of the object owner
- /// Last name of the object owner
- /// Chat channel that the object is communicating on
- /// List of button labels
- public delegate void ScriptDialogCallback(string message, string objectName, LLUUID imageID,
- LLUUID objectID, string firstName, string lastName, int chatChannel, List buttons);
-
- ///
- /// Triggered when the L$ account balance for this avatar changes
- ///
- /// The new account balance
- public delegate void BalanceCallback(int balance);
-
- ///
- /// Tiggered on incoming instant messages
- ///
- /// Key of sender
- /// Name of sender
- /// Key of destination Avatar
- /// ID of originating Estate
- /// Key of originating Region
- /// Coordinates in originating Region
- ///
- /// Group IM session toggle
- /// Key of IM Session
- /// Timestamp of message
- /// Text of message
- ///
- ///
- public delegate void InstantMessageCallback(LLUUID fromAgentID, string fromAgentName,
- LLUUID toAgentID, uint parentEstateID, LLUUID regionID, LLVector3 position,
- byte dialog, bool groupIM, LLUUID imSessionID, DateTime timestamp, string message,
- byte offline, byte[] binaryBucket);
-
- ///
- /// Triggered for any status updates of a teleport (progress, failed, succeeded)
- ///
- /// The simulator the avatar is currently residing in
- /// A message about the current teleport status
- /// The current status of the teleport
- public delegate void TeleportCallback(Simulator currentSim, string message, TeleportStatus status);
-
- ///
- /// Current teleport status
- ///
- public enum TeleportStatus
- {
- ///
- None,
- /// Teleport Start
- Start,
- /// Teleport in Progress
- Progress,
- /// Teleport Failed
- Failed,
- /// Teleport Completed
- Finished
- }
-
- ///
- /// Special commands used in Instant Messages
- ///
- public enum InstantMessageDialog
- {
- /// Indicates a regular IM from another agent
- MessageFromAgent = 0,
- /// Indicates that someone has given the user an object
- GiveInventory = 4,
- /// Indicates that someone has given the user a notecard
- GiveNotecard = 9,
- /// Indicates that the IM is from an object
- MessageFromObject = 19,
- /// Indicates that the IM is a teleport invitation
- RequestTeleport = 22,
- /// Response sent to the agent which inititiated a teleport invitation
- AcceptTeleport = 23,
- /// Response sent to the agent which inititiated a teleport invitation
- DenyTeleport = 24,
- /// Indicates that a user has started typing
- StartTyping = 41,
- /// Indicates that a user has stopped typing
- StopTyping = 42
- }
-
///
/// Class to hold Client Avatar's data
///
public partial class MainAvatar
{
+ ///
+ /// Current teleport status
+ ///
+ public enum TeleportStatus
+ {
+ ///
+ None,
+ /// Teleport Start
+ Start,
+ /// Teleport in Progress
+ Progress,
+ /// Teleport Failed
+ Failed,
+ /// Teleport Completed
+ Finished
+ }
+
+ ///
+ /// Special commands used in Instant Messages
+ ///
+ public enum InstantMessageDialog
+ {
+ /// Indicates a regular IM from another agent
+ MessageFromAgent = 0,
+ /// Indicates that someone has given the user an object
+ GiveInventory = 4,
+ /// Indicates that someone has given the user a notecard
+ GiveNotecard = 9,
+ /// Indicates that the IM is from an object
+ MessageFromObject = 19,
+ /// Indicates that the IM is a teleport invitation
+ RequestTeleport = 22,
+ /// Response sent to the agent which inititiated a teleport invitation
+ AcceptTeleport = 23,
+ /// Response sent to the agent which inititiated a teleport invitation
+ DenyTeleport = 24,
+ /// Indicates that a user has started typing
+ StartTyping = 41,
+ /// Indicates that a user has stopped typing
+ StopTyping = 42
+ }
+
+ ///
+ /// Conversion type to denote Chat Packet types in an easier-to-understand format
+ ///
+ public enum ChatType
+ {
+ /// Whispers (5m radius)
+ Whisper = 0,
+ /// Normal chat (10/20m radius), what the official viewer typically sends
+ Normal = 1,
+ /// Shouting! (100m radius)
+ Shout = 2,
+ /// Say chat (10/20m radius) - The official viewer will
+ /// print "[4:15] You say, hey" instead of "[4:15] You: hey"
+ Say = 3,
+ /// Event message when an Avatar has begun to type
+ StartTyping = 4,
+ /// Event message when an Avatar has stopped typing
+ StopTyping = 5
+ }
+
+
+ ///
+ /// Triggered on incoming chat messages
+ ///
+ /// Text of chat message
+ /// Is this normal audible chat or not.
+ /// Type of chat (whisper,shout,status,etc)
+ /// Type of source (Agent / Object / ???)
+ /// Text name of sending Avatar/Object
+ ///
+ public delegate void ChatCallback(string message, byte audible, byte type, byte sourcetype,
+ string fromName, LLUUID id, LLUUID ownerid, LLVector3 position);
+
+ ///
+ /// Triggered when a script pops up a dialog box
+ ///
+ /// The dialog box message
+ /// Name of the object that sent the dialog
+ /// Image to be displayed in the dialog
+ /// ID of the object that sent the dialog
+ /// First name of the object owner
+ /// Last name of the object owner
+ /// Chat channel that the object is communicating on
+ /// List of button labels
+ public delegate void ScriptDialogCallback(string message, string objectName, LLUUID imageID,
+ LLUUID objectID, string firstName, string lastName, int chatChannel, List buttons);
+
+ ///
+ /// Triggered when the L$ account balance for this avatar changes
+ ///
+ /// The new account balance
+ public delegate void BalanceCallback(int balance);
+
+ ///
+ /// Tiggered on incoming instant messages
+ ///
+ /// Key of sender
+ /// Name of sender
+ /// Key of destination Avatar
+ /// ID of originating Estate
+ /// Key of originating Region
+ /// Coordinates in originating Region
+ ///
+ /// Group IM session toggle
+ /// Key of IM Session
+ /// Timestamp of message
+ /// Text of message
+ ///
+ ///
+ public delegate void InstantMessageCallback(LLUUID fromAgentID, string fromAgentName,
+ LLUUID toAgentID, uint parentEstateID, LLUUID regionID, LLVector3 position,
+ byte dialog, bool groupIM, LLUUID imSessionID, DateTime timestamp, string message,
+ byte offline, byte[] binaryBucket);
+
+ ///
+ /// Triggered for any status updates of a teleport (progress, failed, succeeded)
+ ///
+ /// The simulator the avatar is currently residing in
+ /// A message about the current teleport status
+ /// The current status of the teleport
+ public delegate void TeleportCallback(Simulator currentSim, string message, TeleportStatus status);
+
+
/// Callback for incoming chat packets
public event ChatCallback OnChat;
/// Callback for pop-up dialogs from scripts
@@ -156,11 +178,23 @@ namespace libsecondlife
/// Your (client) Avatar ID, local to Region/sim
public uint LocalID;
/// Avatar First Name (i.e. Philip)
- public string FirstName = "";
+ public string FirstName = String.Empty;
/// Avatar Last Name (i.e. Linden)
- public string LastName = "";
- ///
- public string TeleportMessage;
+ public string LastName = String.Empty;
+ /// Positive and negative ratings
+ /// This information is read-only and any changes will not be
+ /// reflected on the server
+ public Avatar.Statistics ProfileStatistics = new Avatar.Statistics();
+ /// Avatar properties including about text, profile URL, image IDs and
+ /// publishing settings
+ /// If you change fields in this struct, the changes will not
+ /// be reflected on the server until you call SetAvatarInformation
+ public Avatar.Properties ProfileProperties = new Avatar.Properties();
+ /// Avatar interests including spoken languages, skills, and "want to"
+ /// choices
+ /// If you change fields in this struct, the changes will not
+ /// be reflected on the server until you call SetAvatarInformation
+ public Avatar.Interests ProfileInterests = new Avatar.Interests();
/// Current position of avatar
public LLVector3 Position = LLVector3.Zero;
/// Current rotation of avatar
@@ -198,6 +232,9 @@ namespace libsecondlife
get { return sittingOn; }
}
+ internal uint sittingOn = 0;
+ internal string teleportMessage = String.Empty;
+
private SecondLife Client;
private TeleportCallback OnBeginTeleport;
private TeleportStatus TeleportStat;
@@ -207,17 +244,14 @@ namespace libsecondlife
private float health = 0.0f;
private int balance = 0;
- internal uint sittingOn = 0;
-
///
- /// Constructor, aka 'CallBack Central' - Setup callbacks for packets related to our avatar
+ /// Constructor, setup callbacks for packets related to our avatar
///
///
public MainAvatar(SecondLife client)
{
NetworkManager.PacketCallback callback;
Client = client;
- TeleportMessage = "";
Status = new MainAvatarStatus(Client);
@@ -240,7 +274,8 @@ namespace libsecondlife
// Script dialog callback
Client.Network.RegisterCallback(PacketType.ScriptDialog, new NetworkManager.PacketCallback(ScriptDialogHandler));
- TeleportTimer = new Timer(18000);
+ // Teleport timeout timer
+ TeleportTimer = new Timer(Client.Settings.TELEPORT_TIMEOUT);
TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent);
TeleportTimeout = false;
@@ -292,17 +327,14 @@ namespace libsecondlife
}
///
- /// Generate an Instant Message (full arguments)
+ /// Send an Instant Message
///
- /// Client's Avatar
- /// SessionID of current connection to grid
+ /// The name this IM will show up as being from
+ /// Session ID of current connection to grid
/// Key of Avatar
- /// Text Message being sent.
+ /// Text message being sent
///
- /// IM Session ID
- ///
- /// TODO: Have fromName grabbed from elsewhere and remove argument, to prevent inadvertant spoofing.
- ///
+ /// IM session ID (to differentiate between IM windows)
public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message,
LLUUID[] conferenceIDs, LLUUID IMSessionID)
{
@@ -335,33 +367,51 @@ namespace libsecondlife
//TODO: Allow region id to be correctly set by caller or fetched from Client.*
im.MessageBlock.RegionID = LLUUID.Zero;
-
// Send the message
Client.Network.SendPacket(im);
}
///
- /// Conversion type to denote Chat Packet types in an easier-to-understand format
+ /// Synchronize the local profile and interests information to the server
///
- public enum ChatType
+ public void SetAvatarInformation()
{
- /// Whispers (5m radius)
- Whisper = 0,
- /// Normal chat (10/20m radius), what the official viewer typically sends
- Normal = 1,
- /// Shouting! (100m radius)
- Shout = 2,
- /// Say chat (10/20m radius) - The official viewer will
- /// print "[4:15] You say, hey" instead of "[4:15] You: hey"
- Say = 3,
- /// Event message when an Avatar has begun to type
- StartTyping = 4,
- /// Event message when an Avatar has stopped typing
- StopTyping = 5
+ // Basic profile properties
+ AvatarPropertiesUpdatePacket apup = new AvatarPropertiesUpdatePacket();
+
+ apup.AgentData = new AvatarPropertiesUpdatePacket.AgentDataBlock();
+ apup.AgentData.AgentID = this.ID;
+ apup.AgentData.SessionID = Client.Network.SessionID;
+
+ apup.PropertiesData = new AvatarPropertiesUpdatePacket.PropertiesDataBlock();
+ apup.PropertiesData.AllowPublish = this.ProfileProperties.AllowPublish;
+ apup.PropertiesData.FLAboutText = Helpers.StringToField(this.ProfileProperties.FirstLifeText);
+ apup.PropertiesData.FLImageID = this.ProfileProperties.FirstLifeImage;
+ apup.PropertiesData.ImageID = this.ProfileProperties.ProfileImage;
+ apup.PropertiesData.MaturePublish = this.ProfileProperties.MaturePublish;
+ apup.PropertiesData.ProfileURL = Helpers.StringToField(this.ProfileProperties.ProfileURL);
+
+ // Interests
+ AvatarInterestsUpdatePacket aiup = new AvatarInterestsUpdatePacket();
+
+ aiup.AgentData = new AvatarInterestsUpdatePacket.AgentDataBlock();
+ aiup.AgentData.AgentID = this.ID;
+ aiup.AgentData.SessionID = Client.Network.SessionID;
+
+ aiup.PropertiesData = new AvatarInterestsUpdatePacket.PropertiesDataBlock();
+ aiup.PropertiesData.LanguagesText = Helpers.StringToField(this.ProfileInterests.LanguagesText);
+ aiup.PropertiesData.SkillsMask = this.ProfileInterests.SkillsMask;
+ aiup.PropertiesData.SkillsText = Helpers.StringToField(this.ProfileInterests.SkillsText);
+ aiup.PropertiesData.WantToMask = this.ProfileInterests.WantToMask;
+ aiup.PropertiesData.WantToText = Helpers.StringToField(this.ProfileInterests.WantToText);
+
+ //Send packets
+ Client.Network.SendPacket(apup);
+ Client.Network.SendPacket(aiup);
}
///
- /// Send a Chat message.
+ /// Send a chat message
///
/// The Message you're sending out.
/// Channel number (0 would be default 'Say' message, other numbers
@@ -655,14 +705,14 @@ namespace libsecondlife
if (TeleportTimeout)
{
- TeleportMessage = "Teleport timed out.";
+ teleportMessage = "Teleport timed out.";
TeleportStat = TeleportStatus.Failed;
- if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, TeleportMessage, TeleportStat); }
+ if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); }
}
else
{
- if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, TeleportMessage, TeleportStat); }
+ if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); }
}
return (TeleportStat == TeleportStatus.Finished);
@@ -722,9 +772,9 @@ namespace libsecondlife
if (OnTeleport != null)
{
- TeleportMessage = "Unable to resolve name: " + simName;
+ teleportMessage = "Unable to resolve name: " + simName;
TeleportStat = TeleportStatus.Failed;
- OnTeleport(Client.Network.CurrentSim, TeleportMessage, TeleportStat);
+ OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat);
}
return false;
@@ -1035,36 +1085,36 @@ namespace libsecondlife
{
Client.DebugLog("TeleportStart received from " + simulator.ToString());
- TeleportMessage = "Teleport started";
+ teleportMessage = "Teleport started";
TeleportStat = TeleportStatus.Start;
if (OnBeginTeleport != null)
{
- OnBeginTeleport(Client.Network.CurrentSim, TeleportMessage, TeleportStat);
+ OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat);
}
}
else if (packet.Type == PacketType.TeleportProgress)
{
Client.DebugLog("TeleportProgress received from " + simulator.ToString());
- TeleportMessage = Helpers.FieldToString(((TeleportProgressPacket)packet).Info.Message);
+ teleportMessage = Helpers.FieldToString(((TeleportProgressPacket)packet).Info.Message);
TeleportStat = TeleportStatus.Progress;
if (OnBeginTeleport != null)
{
- OnBeginTeleport(Client.Network.CurrentSim, TeleportMessage, TeleportStat);
+ OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat);
}
}
else if (packet.Type == PacketType.TeleportFailed)
{
Client.DebugLog("TeleportFailed received from " + simulator.ToString());
- TeleportMessage = Helpers.FieldToString(((TeleportFailedPacket)packet).Info.Reason);
+ teleportMessage = Helpers.FieldToString(((TeleportFailedPacket)packet).Info.Reason);
TeleportStat = TeleportStatus.Failed;
if (OnBeginTeleport != null)
{
- OnBeginTeleport(Client.Network.CurrentSim, TeleportMessage, TeleportStat);
+ OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat);
}
OnBeginTeleport = null;
@@ -1082,7 +1132,7 @@ namespace libsecondlife
if (sim != null)
{
- TeleportMessage = "Teleport finished";
+ teleportMessage = "Teleport finished";
TeleportStat = TeleportStatus.Finished;
// Move the avatar in to the new sim
@@ -1099,7 +1149,7 @@ namespace libsecondlife
if (OnBeginTeleport != null)
{
- OnBeginTeleport(sim, TeleportMessage, TeleportStat);
+ OnBeginTeleport(sim, teleportMessage, TeleportStat);
}
else
{
@@ -1110,16 +1160,16 @@ namespace libsecondlife
}
else
{
- TeleportMessage = "Failed to connect to the new sim after a teleport";
+ teleportMessage = "Failed to connect to the new sim after a teleport";
TeleportStat = TeleportStatus.Failed;
// FIXME: Set the previous CurrentSim to the current simulator again
- Client.Log(TeleportMessage, Helpers.LogLevel.Warning);
+ Client.Log(teleportMessage, Helpers.LogLevel.Warning);
if (OnBeginTeleport != null)
{
- OnBeginTeleport(Client.Network.CurrentSim, TeleportMessage, TeleportStat);
+ OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat);
}
}
diff --git a/libsecondlife-cs/SecondLife.cs b/libsecondlife-cs/SecondLife.cs
index 7564068b..c166705f 100644
--- a/libsecondlife-cs/SecondLife.cs
+++ b/libsecondlife-cs/SecondLife.cs
@@ -32,14 +32,6 @@ using libsecondlife.InventorySystem;
namespace libsecondlife
{
- ///
- /// Callback used for client apps to receive log messages from
- /// libsecondlife
- ///
- ///
- ///
- public delegate void LogCallback(string message, Helpers.LogLevel level);
-
///
/// Main class to expose Second Life functionality to clients. All of the
/// classes needed for sending and receiving data are accessible through
@@ -47,6 +39,15 @@ namespace libsecondlife
///
public class SecondLife
{
+ ///
+ /// Callback used for client apps to receive log messages from
+ /// libsecondlife
+ ///
+ ///
+ ///
+ public delegate void LogCallback(string message, Helpers.LogLevel level);
+
+
/// Networking Subsystem
public NetworkManager Network;
/// Parcel (subdivided simulator lots) Subsystem
@@ -61,14 +62,12 @@ namespace libsecondlife
public ObjectManager Objects;
/// Group Subsystem
public GroupManager Groups;
-
/// Asset Subsystem
public AssetManager Assets;
/// Inventory Subsystem
public InventoryManager Inventory;
/// Image Subsystem
public ImageManager Images;
-
/// Throttling Subsystem
public AgentThrottle Throttle;
/// Settings Subsystem
@@ -86,20 +85,19 @@ namespace libsecondlife
///
public SecondLife()
{
+ // These are order-dependant
Network = new NetworkManager(this);
+ Settings = new Settings(this);
Parcels = new ParcelManager(this);
Self = new MainAvatar(this);
Avatars = new AvatarManager(this);
Grid = new GridManager(this);
Objects = new ObjectManager(this);
Groups = new GroupManager(this);
-
Assets = new AssetManager(this);
Images = new ImageManager(this);
Inventory = new InventoryManager(this);
-
Throttle = new AgentThrottle(this);
- Settings = new Settings(this);
}
///
diff --git a/libsecondlife-cs/Settings.cs b/libsecondlife-cs/Settings.cs
index 361cc325..2d5f7d82 100644
--- a/libsecondlife-cs/Settings.cs
+++ b/libsecondlife-cs/Settings.cs
@@ -47,6 +47,9 @@ namespace libsecondlife
/// we assume the sequence number just rolls over? Or maybe the
/// protocol isn't able to sustain a connection past that
public readonly int MAX_SEQUENCE = 0xFFFFFF;
+ /// Number of milliseconds before a teleport attempt will time
+ /// out
+ public readonly int TELEPORT_TIMEOUT = 18 * 1000;
/// The maximum size of the sequence number inbox, used to
/// check for resent and/or duplicate packets
diff --git a/libsecondlife-cs/examples/IA_InventoryManager/iManager.cs b/libsecondlife-cs/examples/IA_InventoryManager/iManager.cs
index 1bb409e8..32b7de0d 100644
--- a/libsecondlife-cs/examples/IA_InventoryManager/iManager.cs
+++ b/libsecondlife-cs/examples/IA_InventoryManager/iManager.cs
@@ -60,7 +60,7 @@ namespace IA_InventoryManager
{
_Client = new SecondLife();
_Client.Network.OnConnected += new NetworkManager.ConnectedCallback(Network_OnConnected);
- _Client.Self.OnTeleport += new TeleportCallback(Self_OnTeleport);
+ _Client.Self.OnTeleport += new MainAvatar.TeleportCallback(Self_OnTeleport);
}
catch (Exception e)
{
@@ -212,7 +212,7 @@ namespace IA_InventoryManager
}
- void Self_OnTeleport(Simulator currentSim, string message, TeleportStatus status)
+ void Self_OnTeleport(Simulator currentSim, string message, MainAvatar.TeleportStatus status)
{
Console.WriteLine("Teleport Completed");
StandUpStraight();
diff --git a/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs b/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs
index 1d32e162..87828249 100644
--- a/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs
+++ b/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs
@@ -57,19 +57,19 @@ namespace IA_TestAsyncImage
private void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
{
- if (avatar.FirstLifeImage != null)
+ if (avatar.ProfileProperties.FirstLifeImage != null)
{
- if (_Client.Images.isCachedImage(avatar.FirstLifeImage) == false)
+ if (_Client.Images.isCachedImage(avatar.ProfileProperties.FirstLifeImage) == false)
{
- _Client.Images.RequestImageAsync(avatar.FirstLifeImage);
+ _Client.Images.RequestImageAsync(avatar.ProfileProperties.FirstLifeImage);
}
}
- if (avatar.ProfileImage != null)
+ if (avatar.ProfileProperties.ProfileImage != null)
{
- if (_Client.Images.isCachedImage(avatar.FirstLifeImage) == false)
+ if (_Client.Images.isCachedImage(avatar.ProfileProperties.FirstLifeImage) == false)
{
- _Client.Images.RequestImageAsync(avatar.ProfileImage);
+ _Client.Images.RequestImageAsync(avatar.ProfileProperties.ProfileImage);
}
}
diff --git a/libsecondlife-cs/examples/Teleport/Teleport.cs b/libsecondlife-cs/examples/Teleport/Teleport.cs
index 2fa19810..2fd9ef8d 100644
--- a/libsecondlife-cs/examples/Teleport/Teleport.cs
+++ b/libsecondlife-cs/examples/Teleport/Teleport.cs
@@ -136,7 +136,7 @@ namespace Teleport
}
}
- Client.Self.OnTeleport += new TeleportCallback(Self_OnTeleport);
+ Client.Self.OnTeleport += new MainAvatar.TeleportCallback(Self_OnTeleport);
DoneTeleporting = false;
Client.Self.Teleport(RegionHandle, coords);
@@ -147,11 +147,11 @@ namespace Teleport
}
}
- void Self_OnTeleport(Simulator currentSim, string message, TeleportStatus status)
+ void Self_OnTeleport(Simulator currentSim, string message, MainAvatar.TeleportStatus status)
{
Console.WriteLine(message);
- if (status == TeleportStatus.Finished || status == TeleportStatus.Failed)
+ if (status == MainAvatar.TeleportStatus.Finished || status == MainAvatar.TeleportStatus.Failed)
{
DoneTeleporting = true;
}
diff --git a/libsecondlife-cs/examples/TestClient/Commands/EchoMasterCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/EchoMasterCommand.cs
index 319d2cbe..adfee52b 100644
--- a/libsecondlife-cs/examples/TestClient/Commands/EchoMasterCommand.cs
+++ b/libsecondlife-cs/examples/TestClient/Commands/EchoMasterCommand.cs
@@ -24,13 +24,13 @@ namespace libsecondlife.TestClient
if (!Active)
{
Active = true;
- Client.Self.OnChat += new ChatCallback(Self_OnChat);
+ Client.Self.OnChat += new MainAvatar.ChatCallback(Self_OnChat);
return "Echoing is now on.";
}
else
{
Active = false;
- Client.Self.OnChat -= new ChatCallback(Self_OnChat);
+ Client.Self.OnChat -= new MainAvatar.ChatCallback(Self_OnChat);
return "Echoing is now off.";
}
}
diff --git a/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs
index b3e06ff4..f165446d 100644
--- a/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs
+++ b/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs
@@ -99,6 +99,7 @@ namespace libsecondlife.TestClient
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(file, settings);
+
try
{
List prims = new List();
diff --git a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs
index 6632d334..75bdedbe 100644
--- a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs
+++ b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs
@@ -74,9 +74,9 @@ namespace libsecondlife.TestClient
prims.Add(prim.LocalID, prim);
}
}
- catch (Exception ex)
+ catch (Exception)
{
- return "Deserialize failed: " + ex.ToString();
+ return "Failed to import the object XML file, maybe it doesn't exist or is in the wrong format?";
}
if (!registeredCreateEvent)
diff --git a/libsecondlife-cs/examples/TestClient/Commands/ImportOutfitCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ImportOutfitCommand.cs
index 922d7d65..aee1ee15 100644
--- a/libsecondlife-cs/examples/TestClient/Commands/ImportOutfitCommand.cs
+++ b/libsecondlife-cs/examples/TestClient/Commands/ImportOutfitCommand.cs
@@ -32,6 +32,7 @@ namespace libsecondlife.TestClient
XmlReader reader = XmlReader.Create(args[0]);
XmlSerializer serializer = new XmlSerializer(typeof(Packet));
AvatarAppearancePacket appearance = (AvatarAppearancePacket)serializer.Deserialize(reader);
+ reader.Close();
AgentSetAppearancePacket set = new AgentSetAppearancePacket();
@@ -58,9 +59,9 @@ namespace libsecondlife.TestClient
Client.Network.SendPacket(set);
}
- catch (Exception e)
+ catch (Exception)
{
- return e.ToString();
+ return "Failed to import the appearance XML file, maybe it doesn't exist or is in the wrong format?";
}
return "Imported " + args[0] + " and sent an AgentSetAppearance packet";
diff --git a/libsecondlife-cs/examples/TestClient/TestClient.cs b/libsecondlife-cs/examples/TestClient/TestClient.cs
index 0ce32a6e..11ce5c0b 100644
--- a/libsecondlife-cs/examples/TestClient/TestClient.cs
+++ b/libsecondlife-cs/examples/TestClient/TestClient.cs
@@ -54,7 +54,7 @@ namespace libsecondlife.TestClient
Objects.OnObjectKilled += new ObjectManager.KillObjectCallback(Objects_OnObjectKilled);
Objects.OnNewAvatar += new ObjectManager.NewAvatarCallback(Objects_OnNewAvatar);
Objects.OnAvatarMoved += new ObjectManager.AvatarMovedCallback(Objects_OnAvatarMoved);
- Self.OnInstantMessage += new InstantMessageCallback(Self_OnInstantMessage);
+ Self.OnInstantMessage += new MainAvatar.InstantMessageCallback(Self_OnInstantMessage);
Network.RegisterCallback(PacketType.AvatarAppearance, new NetworkManager.PacketCallback(AvatarAppearanceHandler));
diff --git a/libsecondlife-cs/examples/groupmanager/frmGroupInfo.Designer.cs b/libsecondlife-cs/examples/groupmanager/frmGroupInfo.Designer.cs
index e09fe0a3..89bb4ee9 100644
--- a/libsecondlife-cs/examples/groupmanager/frmGroupInfo.Designer.cs
+++ b/libsecondlife-cs/examples/groupmanager/frmGroupInfo.Designer.cs
@@ -731,7 +731,6 @@ namespace groupmanager
this.ShowInTaskbar = false;
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.Text = "Group Information";
- this.Load += new System.EventHandler(this.frmGroupInfo_Load);
this.tabs.ResumeLayout(false);
this.tabGeneral.ResumeLayout(false);
this.tabGeneral.PerformLayout();
diff --git a/libsecondlife-cs/examples/groupmanager/frmGroupInfo.cs b/libsecondlife-cs/examples/groupmanager/frmGroupInfo.cs
index 054cfa21..b80da985 100644
--- a/libsecondlife-cs/examples/groupmanager/frmGroupInfo.cs
+++ b/libsecondlife-cs/examples/groupmanager/frmGroupInfo.cs
@@ -11,29 +11,44 @@ using libsecondlife.AssetSystem;
namespace groupmanager
{
+ public class GroupMemberData
+ {
+ public LLUUID ID;
+ public string Name;
+ public string Title;
+ public string LastOnline;
+ public ulong Powers;
+ public bool IsOwner;
+ public int Contribution;
+ }
+
public partial class frmGroupInfo : Form
{
Group Group;
SecondLife Client;
- GroupProfile Profile;
- Dictionary Members;
- Dictionary Titles;
- Dictionary MemberData;
- Dictionary Names;
+ GroupProfile Profile = new GroupProfile();
+ Dictionary Members = new Dictionary();
+ Dictionary Titles = new Dictionary();
+ Dictionary MemberData = new Dictionary();
+ Dictionary Names = new Dictionary();
public frmGroupInfo(Group group, SecondLife client)
{
+ InitializeComponent();
+
+ while (!IsHandleCreated)
+ {
+ // Force handle creation
+ IntPtr temp = Handle;
+ }
+
Group = group;
Client = client;
- Profile = new GroupProfile();
- MemberData = new Dictionary();
- Names = new Dictionary();
- InitializeComponent();
- }
+ Client.Avatars.OnAvatarNames += new AvatarManager.AvatarNamesCallback(AvatarNamesHandler);
+
+ // Request the group information
- private void frmGroupInfo_Load(object sender, EventArgs e)
- {
Client.Groups.BeginGetGroupProfile(Group.ID,
new GroupManager.GroupProfileCallback(GroupProfileHandler));
@@ -50,8 +65,6 @@ namespace groupmanager
Invoke(new MethodInvoker(UpdateProfile));
- // Waterdrop: new LLUUID("c77a1c21-e604-7d2c-2c89-5539ce853466")
-
byte[] j2cdata;
if (Group.InsigniaID != null)
{
@@ -59,7 +72,7 @@ namespace groupmanager
}
else
{
- // TODO: Add somekind of
+ // ???
j2cdata = Client.Images.RequestImage("c77a1c21-e604-7d2c-2c89-5539ce853466");
}
@@ -84,10 +97,10 @@ namespace groupmanager
numFee.Value = Profile.MembershipFee;
chkMature.Checked = Profile.MaturePublish;
- Client.Avatars.BeginGetAvatarName(Profile.FounderID, new AvatarManager.AgentNamesCallback(AgentNamesHandler));
+ Client.Avatars.RequestAvatarName(Profile.FounderID);
}
- private void AgentNamesHandler(Dictionary names)
+ private void AvatarNamesHandler(Dictionary names)
{
lock (Names)
{
@@ -102,8 +115,6 @@ namespace groupmanager
private void UpdateNames()
{
- GroupMemberData member;
-
lock (Names)
{
if (Profile.FounderID != null && Names.ContainsKey(Profile.FounderID))
@@ -120,8 +131,7 @@ namespace groupmanager
MemberData[name.Key] = new GroupMemberData();
}
- member = MemberData[name.Key];
- member.Name = name.Value;
+ MemberData[name.Key].Name = name.Value;
}
}
}
@@ -209,7 +219,7 @@ namespace groupmanager
}
}
- Client.Avatars.BeginGetAvatarNames(requestids, new AvatarManager.AgentNamesCallback(AgentNamesHandler));
+ Client.Avatars.RequestAvatarNames(requestids);
}
private void GroupTitlesHandler(Dictionary titles)
@@ -223,25 +233,5 @@ namespace groupmanager
{
;
}
-
- //private void BytesToFile(byte[] bytes, string filename)
- //{
- // FileStream filestream = new FileStream(filename, FileMode.Create);
- // BinaryWriter writer = new BinaryWriter(filestream);
- // writer.Write(bytes);
- // writer.Close();
- // filestream.Close();
- //}
- }
-
- public class GroupMemberData
- {
- public LLUUID ID;
- public string Name;
- public string Title;
- public string LastOnline;
- public ulong Powers;
- public bool IsOwner;
- public int Contribution;
}
}
diff --git a/libsecondlife-cs/examples/primexport/frmPrimExport.cs b/libsecondlife-cs/examples/primexport/frmPrimExport.cs
index 4a1cc851..f03b9deb 100644
--- a/libsecondlife-cs/examples/primexport/frmPrimExport.cs
+++ b/libsecondlife-cs/examples/primexport/frmPrimExport.cs
@@ -299,7 +299,7 @@ namespace primexport
InitializeComponent();
client = new SecondLife();
- client.OnLogMessage += new LogCallback(client_OnLogMessage);
+ client.OnLogMessage += new SecondLife.LogCallback(client_OnLogMessage);
client.Objects.RequestAllObjects = true;
client.Objects.OnNewPrim += new ObjectManager.NewPrimCallback(PrimSeen);
client.Objects.OnNewAvatar += new ObjectManager.NewAvatarCallback(AvatarSeen);
diff --git a/libsecondlife-cs/libsecondlife.Tests/Tests.cs b/libsecondlife-cs/libsecondlife.Tests/NetworkTests.cs
similarity index 76%
rename from libsecondlife-cs/libsecondlife.Tests/Tests.cs
rename to libsecondlife-cs/libsecondlife.Tests/NetworkTests.cs
index 1b0ba5b5..2dc5791a 100644
--- a/libsecondlife-cs/libsecondlife.Tests/Tests.cs
+++ b/libsecondlife-cs/libsecondlife.Tests/NetworkTests.cs
@@ -1,209 +1,225 @@
-using System;
-using System.Collections.Generic;
-using System.Net;
-using libsecondlife;
-using libsecondlife.Packets;
-using NUnit.Framework;
-
-namespace libsecondlife.Tests
-{
- [TestFixture]
- public class NetworkTests : Assert
- {
- SecondLife Client;
- ulong CurrentRegionHandle = 0;
- ulong AhernRegionHandle = 1096213093149184;
- ulong MorrisRegionHandle = 1096213093149183;
- bool DetectedObject = false;
- bool DoneTeleporting = false;
- TeleportStatus tpStatus = TeleportStatus.None;
- string tpMessage = "";
-
- public NetworkTests()
- {
- Client = new SecondLife();
-
- //string startLoc = NetworkManager.StartLocation("hooper", 128, 128, 32);
-
- // Register callbacks
- Client.Network.RegisterCallback(PacketType.ObjectUpdate, new NetworkManager.PacketCallback(ObjectUpdateHandler));
- Client.Self.OnTeleport += new TeleportCallback(OnTeleportHandler);
-
- Client.Network.Login("Testing", "Anvil", "testinganvil", "Unit Test Framework", //startLoc,
- "contact@libsecondlife.org"/*, false*/);
- }
-
- ~NetworkTests()
- {
- Client.Network.Logout();
- }
-
- [SetUp]
- public void Init()
- {
- Assert.IsTrue(Client.Network.Connected, "Client is not connected to the grid: " + Client.Network.LoginError);
-
- int start = Environment.TickCount;
- while (Client.Network.CurrentSim.Region.Name == "")
- {
- if (Environment.TickCount - start > 5000)
- {
- Assert.Fail("Timeout waiting for a RegionHandshake packet");
- }
- }
-
- //Assert.AreEqual("ahern", Client.Network.CurrentSim.Region.Name.ToLower(), "Logged in to sim " +
- // Client.Network.CurrentSim.Region.Name + " instead of Ahern");
- }
-
- [Test]
- public void DetectObjects()
- {
- int start = Environment.TickCount;
- while (!DetectedObject)
- {
- if (Environment.TickCount - start > 10000)
- {
- Assert.Fail("Timeout waiting for an ObjectUpdate packet");
- return;
- }
- }
- }
-
- [Test]
- public void U64Receive()
- {
- int start = Environment.TickCount;
- while (CurrentRegionHandle == 0)
- {
- if (Environment.TickCount - start > 10000)
- {
- Assert.Fail("Timeout waiting for an ObjectUpdate packet");
- return;
- }
- }
-
- Assert.IsTrue(CurrentRegionHandle == AhernRegionHandle, "Current region is " +
- CurrentRegionHandle + " when we were expecting " + AhernRegionHandle + ", possible endian issue");
- }
-
- [Test]
- public void Teleport()
- {
- DoneTeleporting = false;
- tpStatus = TeleportStatus.None;
-
- Client.Self.Teleport(MorrisRegionHandle, new LLVector3(128, 128, 32));
-
- int start = Environment.TickCount;
-
- while (!DoneTeleporting)
- {
- System.Threading.Thread.Sleep(100);
-
- if (Environment.TickCount - start > 10000)
- {
- Assert.Fail("Timeout waiting for the first teleport to finish");
- return;
- }
- }
-
- Assert.IsTrue(tpStatus == TeleportStatus.Finished, "Teleport status is " + tpStatus.ToString() +
- ", message=" + tpMessage);
-
- // Wait for the region information to come in
- start = Environment.TickCount;
- while (Client.Network.CurrentSim.Region.Name == "")
- {
- if (Environment.TickCount - start > 5000)
- {
- Assert.Fail("Timeout waiting for a RegionHandshake packet");
- }
- }
-
- // Assert that we really did make it to our scheduled destination
- Assert.AreEqual("morris", Client.Network.CurrentSim.Region.Name.ToLower(),
- "Expected to teleport to Morris, ended up in " + Client.Network.CurrentSim.Region.Name +
- ". Possibly region full or offline?");
-
- ///////////////////////////////////////////////////////////////////
-
- // TODO: Add a local region teleport
-
- ///////////////////////////////////////////////////////////////////
-
- DoneTeleporting = false;
- tpStatus = TeleportStatus.None;
-
- Client.Self.Teleport(AhernRegionHandle, new LLVector3(128, 128, 32));
-
- start = Environment.TickCount;
-
- while (!DoneTeleporting)
- {
- System.Threading.Thread.Sleep(100);
-
- if (Environment.TickCount - start > 10000)
- {
- Assert.Fail("Timeout waiting for the second teleport to finish");
- return;
- }
- }
-
- Assert.IsTrue(tpStatus == TeleportStatus.Finished, "Teleport status is " + tpStatus.ToString() +
- ", message=" + tpMessage);
-
- // Wait for the region information to come in
- start = Environment.TickCount;
- while (Client.Network.CurrentSim.Region.Name == "")
- {
- if (Environment.TickCount - start > 5000)
- {
- Assert.Fail("Timeout waiting for a RegionHandshake packet");
- }
- }
-
- // Assert that we really did make it to our scheduled destination
- Assert.AreEqual("ahern", Client.Network.CurrentSim.Region.Name.ToLower(),
- "Expected to teleport to Ahern, ended up in " + Client.Network.CurrentSim.Region.Name +
- ". Possibly region full or offline?");
- }
-
- private void ObjectUpdateHandler(Packet packet, Simulator sim)
- {
- ObjectUpdatePacket update = (ObjectUpdatePacket)packet;
-
- DetectedObject = true;
- CurrentRegionHandle = update.RegionData.RegionHandle;
- }
-
- private void OnTeleportHandler(Simulator currentSim, string message, TeleportStatus status)
- {
- switch (status)
- {
- case TeleportStatus.None:
- break;
- case TeleportStatus.Start:
- break;
- case TeleportStatus.Progress:
- break;
- case TeleportStatus.Failed:
- DoneTeleporting = true;
- break;
- case TeleportStatus.Finished:
- DoneTeleporting = true;
- break;
- }
-
- tpMessage = message;
- tpStatus = status;
- }
-
- [TearDown]
- public void Shutdown()
- {
- //Client.Network.Logout();
- //Client = null;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Net;
+using libsecondlife;
+using libsecondlife.Packets;
+using libsecondlife.Utilities;
+using NUnit.Framework;
+
+namespace libsecondlife.Tests
+{
+ [TestFixture]
+ public class NetworkTests : Assert
+ {
+ SecondLife Client;
+
+ ulong CurrentRegionHandle = 0;
+ ulong AhernRegionHandle = 1096213093149184;
+ ulong MorrisRegionHandle = 1096213093149183;
+ bool DetectedObject = false;
+ bool DoneTeleporting = false;
+ MainAvatar.TeleportStatus tpStatus = MainAvatar.TeleportStatus.None;
+ string tpMessage = "";
+
+ LLUUID LookupKey1 = new LLUUID("25472683cb324516904a6cd0ecabf128");
+ string LookupName1 = "Bot Ringo";
+
+ public NetworkTests()
+ {
+ Client = new SecondLife();
+
+ string startLoc = NetworkManager.StartLocation("hooper", 128, 128, 32);
+
+ // Register callbacks
+ Client.Network.RegisterCallback(PacketType.ObjectUpdate, new NetworkManager.PacketCallback(ObjectUpdateHandler));
+ Client.Self.OnTeleport += new MainAvatar.TeleportCallback(OnTeleportHandler);
+
+ Client.Network.Login("Testing", "Anvil", "testinganvil", "Unit Test Framework", startLoc,
+ "contact@libsecondlife.org", false);
+ }
+
+ ~NetworkTests()
+ {
+ Client.Network.Logout();
+ }
+
+ [SetUp]
+ public void Init()
+ {
+ Assert.IsTrue(Client.Network.Connected, "Client is not connected to the grid: " + Client.Network.LoginError);
+
+ int start = Environment.TickCount;
+ while (Client.Network.CurrentSim.Region.Name == "")
+ {
+ if (Environment.TickCount - start > 5000)
+ {
+ Assert.Fail("Timeout waiting for a RegionHandshake packet");
+ }
+ }
+
+ //Assert.AreEqual("ahern", Client.Network.CurrentSim.Region.Name.ToLower(), "Logged in to sim " +
+ // Client.Network.CurrentSim.Region.Name + " instead of Ahern");
+ }
+
+ [Test]
+ public void DetectObjects()
+ {
+ int start = Environment.TickCount;
+ while (!DetectedObject)
+ {
+ if (Environment.TickCount - start > 10000)
+ {
+ Assert.Fail("Timeout waiting for an ObjectUpdate packet");
+ return;
+ }
+ }
+ }
+
+ [Test]
+ public void U64Receive()
+ {
+ int start = Environment.TickCount;
+ while (CurrentRegionHandle == 0)
+ {
+ if (Environment.TickCount - start > 10000)
+ {
+ Assert.Fail("Timeout waiting for an ObjectUpdate packet");
+ return;
+ }
+ }
+
+ Assert.IsTrue(CurrentRegionHandle == AhernRegionHandle, "Current region is " +
+ CurrentRegionHandle + " when we were expecting " + AhernRegionHandle + ", possible endian issue");
+ }
+
+ [Test]
+ public void NameLookup()
+ {
+ AvatarTracker tracker = new AvatarTracker(Client);
+
+ string name = tracker.GetAvatarName(LookupKey1);
+
+ Assert.IsTrue(name == LookupName1, "AvatarTracker.GetAvatarName() returned " + name +
+ " instead of " + LookupName1);
+ }
+
+ [Test]
+ public void Teleport()
+ {
+ DoneTeleporting = false;
+ tpStatus = MainAvatar.TeleportStatus.None;
+
+ Client.Self.Teleport(MorrisRegionHandle, new LLVector3(128, 128, 32));
+
+ int start = Environment.TickCount;
+
+ while (!DoneTeleporting)
+ {
+ System.Threading.Thread.Sleep(100);
+
+ if (Environment.TickCount - start > 10000)
+ {
+ Assert.Fail("Timeout waiting for the first teleport to finish");
+ return;
+ }
+ }
+
+ Assert.IsTrue(tpStatus == MainAvatar.TeleportStatus.Finished,
+ "Teleport status is " + tpStatus.ToString() + ", message=" + tpMessage);
+
+ // Wait for the region information to come in
+ start = Environment.TickCount;
+ while (Client.Network.CurrentSim.Region.Name == "")
+ {
+ if (Environment.TickCount - start > 5000)
+ {
+ Assert.Fail("Timeout waiting for a RegionHandshake packet");
+ }
+ }
+
+ // Assert that we really did make it to our scheduled destination
+ Assert.AreEqual("morris", Client.Network.CurrentSim.Region.Name.ToLower(),
+ "Expected to teleport to Morris, ended up in " + Client.Network.CurrentSim.Region.Name +
+ ". Possibly region full or offline?");
+
+ ///////////////////////////////////////////////////////////////////
+
+ // TODO: Add a local region teleport
+
+ ///////////////////////////////////////////////////////////////////
+
+ DoneTeleporting = false;
+ tpStatus = MainAvatar.TeleportStatus.None;
+
+ Client.Self.Teleport(AhernRegionHandle, new LLVector3(128, 128, 32));
+
+ start = Environment.TickCount;
+
+ while (!DoneTeleporting)
+ {
+ System.Threading.Thread.Sleep(100);
+
+ if (Environment.TickCount - start > 10000)
+ {
+ Assert.Fail("Timeout waiting for the second teleport to finish");
+ return;
+ }
+ }
+
+ Assert.IsTrue(tpStatus == MainAvatar.TeleportStatus.Finished, "Teleport status is " +
+ tpStatus.ToString() + ", message=" + tpMessage);
+
+ // Wait for the region information to come in
+ start = Environment.TickCount;
+ while (Client.Network.CurrentSim.Region.Name == "")
+ {
+ if (Environment.TickCount - start > 5000)
+ {
+ Assert.Fail("Timeout waiting for a RegionHandshake packet");
+ }
+ }
+
+ // Assert that we really did make it to our scheduled destination
+ Assert.AreEqual("ahern", Client.Network.CurrentSim.Region.Name.ToLower(),
+ "Expected to teleport to Ahern, ended up in " + Client.Network.CurrentSim.Region.Name +
+ ". Possibly region full or offline?");
+ }
+
+ private void ObjectUpdateHandler(Packet packet, Simulator sim)
+ {
+ ObjectUpdatePacket update = (ObjectUpdatePacket)packet;
+
+ DetectedObject = true;
+ CurrentRegionHandle = update.RegionData.RegionHandle;
+ }
+
+ private void OnTeleportHandler(Simulator currentSim, string message, MainAvatar.TeleportStatus status)
+ {
+ switch (status)
+ {
+ case MainAvatar.TeleportStatus.None:
+ break;
+ case MainAvatar.TeleportStatus.Start:
+ break;
+ case MainAvatar.TeleportStatus.Progress:
+ break;
+ case MainAvatar.TeleportStatus.Failed:
+ DoneTeleporting = true;
+ break;
+ case MainAvatar.TeleportStatus.Finished:
+ DoneTeleporting = true;
+ break;
+ }
+
+ tpMessage = message;
+ tpStatus = status;
+ }
+
+ [TearDown]
+ public void Shutdown()
+ {
+ //Client.Network.Logout();
+ //Client = null;
+ }
+ }
+}
diff --git a/libsecondlife-cs/libsecondlife.Tests/libsecondlife.Tests.csproj b/libsecondlife-cs/libsecondlife.Tests/libsecondlife.Tests.csproj
index c6efde47..cecef19d 100644
--- a/libsecondlife-cs/libsecondlife.Tests/libsecondlife.Tests.csproj
+++ b/libsecondlife-cs/libsecondlife.Tests/libsecondlife.Tests.csproj
@@ -40,13 +40,17 @@
-
+
{D9CDEDFB-8169-4B03-B57F-0DF638F044EC}
libsecondlife
+
+ {CE5E06C2-2428-416B-ADC1-F1FE16A0FB27}
+ libsecondlife.Utilities
+
diff --git a/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs b/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs
index c0c79d61..f9705f69 100644
--- a/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs
+++ b/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using libsecondlife;
+using libsecondlife.Packets;
namespace libsecondlife.Utilities
{
@@ -8,19 +10,174 @@ namespace libsecondlife.Utilities
/// Keeps an up to date inventory of the currently seen objects in each
/// simulator
///
- public class ObjectTracker
- {
- private SecondLife Client;
- private Dictionary> SimPrims = new Dictionary>();
+ //public class ObjectTracker
+ //{
+ // private SecondLife Client;
+ // private Dictionary> SimPrims = new Dictionary>();
- ///
- /// Default constructor
- ///
- /// A reference to the SecondLife client to track
- /// objects for
- public ObjectTracker(SecondLife client)
+ // ///
+ // /// Default constructor
+ // ///
+ // /// A reference to the SecondLife client to track
+ // /// objects for
+ // public ObjectTracker(SecondLife client)
+ // {
+ // Client = client;
+ // }
+ //}
+
+ ///
+ /// Maintains a cache of avatars and does blocking lookups for avatar data
+ ///
+ public class AvatarTracker
+ {
+ protected SecondLife Client;
+ protected Dictionary avatars = new Dictionary();
+ protected Dictionary NameLookupEvents = new Dictionary();
+
+ public AvatarTracker(SecondLife client)
{
Client = client;
+
+ Client.Avatars.OnAvatarNames += new AvatarManager.AvatarNamesCallback(Avatars_OnAvatarNames);
+ }
+
+ ///
+ /// Check if a particular avatar is in the local cache
+ ///
+ ///
+ ///
+ public bool Contains(LLUUID id)
+ {
+ return avatars.ContainsKey(id);
+ }
+
+ ///
+ /// Get an avatar's name, either from the cache or request it.
+ /// This function is blocking
+ ///
+ /// Avatar key to look up
+ /// The avatar name, or String.Empty if the lookup failed
+ public string GetAvatarName(LLUUID id)
+ {
+ // Short circuit the cache lookup in GetAvatarNames
+ if (Contains(id))
+ return LocalAvatarNameLookup(id);
+
+ // Add to the dictionary
+ lock (NameLookupEvents)
+ NameLookupEvents.Add(id, new ManualResetEvent(false));
+
+ // Call function
+ Client.Avatars.RequestAvatarName(id);
+
+ // Start blocking while we wait for this name to be fetched
+ NameLookupEvents[id].WaitOne(5000, false);
+
+ // Clean up
+ lock (NameLookupEvents)
+ NameLookupEvents.Remove(id);
+
+ // Return
+ return LocalAvatarNameLookup(id);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ //public void BeginGetAvatarName(LLUUID id)
+ //{
+ // // TODO: BeginGetAvatarNames is pretty bulky, rewrite a simple version here
+
+ // List ids = new List();
+ // ids.Add(id);
+ // BeginGetAvatarNames(ids);
+ //}
+
+ ///
+ ///
+ ///
+ ///
+ //public void BeginGetAvatarNames(List ids)
+ //{
+ // Dictionary havenames = new Dictionary();
+ // List neednames = new List();
+
+ // // Fire callbacks for the ones we already have cached
+ // foreach (LLUUID id in ids)
+ // {
+ // if (Avatars.ContainsKey(id))
+ // {
+ // havenames[id] = Avatars[id].Name;
+ // //Short circuit the lookup process
+ // if (ManualResetEvents.ContainsKey(id))
+ // {
+ // ManualResetEvents[id].Set();
+ // return;
+ // }
+ // }
+ // else
+ // {
+ // neednames.Add(id);
+ // }
+ // }
+
+ // if (havenames.Count > 0 && OnAgentNames != null)
+ // {
+ // OnAgentNames(havenames);
+ // }
+
+ // if (neednames.Count > 0)
+ // {
+ // UUIDNameRequestPacket request = new UUIDNameRequestPacket();
+
+ // request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[neednames.Count];
+
+ // for (int i = 0; i < neednames.Count; i++)
+ // {
+ // request.UUIDNameBlock[i] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
+ // request.UUIDNameBlock[i].ID = neednames[i];
+ // }
+
+ // Client.Network.SendPacket(request);
+ // }
+ //}
+
+ ///
+ /// This function will only check if the avatar name exists locally,
+ /// it will not do any networking calls to fetch the name
+ ///
+ /// The avatar name, or an empty string if it's not found
+ protected string LocalAvatarNameLookup(LLUUID id)
+ {
+ lock (avatars)
+ {
+ if (avatars.ContainsKey(id))
+ return avatars[id].Name;
+ else
+ return String.Empty;
+ }
+ }
+
+ private void Avatars_OnAvatarNames(Dictionary names)
+ {
+ lock (avatars)
+ {
+ foreach (KeyValuePair kvp in names)
+ {
+ if (!avatars.ContainsKey(kvp.Key) || avatars[kvp.Key] == null)
+ avatars[kvp.Key] = new Avatar();
+
+ avatars[kvp.Key].Name = kvp.Value;
+
+ lock (NameLookupEvents)
+ {
+ if (NameLookupEvents.ContainsKey(kvp.Key))
+ NameLookupEvents[kvp.Key].Set();
+ }
+ }
+ }
}
}
}