From b4accdddfc8c8574cafbbce6b21e3014f247c2c2 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sat, 1 Jul 2006 03:42:53 +0000 Subject: [PATCH] * Added JSON library to parse LLSD fields in the login reply * Added fields in Avatar to hold the parsed data from login * CoarseLocationUpdate packets handled internally * Added Network.LoginValues hashtable, removed second parameter from .Login() * Updated examples to reflect LoginValues / Login() change git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@39 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/AssemblyInfo.cs | 6 +- libsecondlife-cs/Avatar.cs | 58 +- libsecondlife-cs/JSONlib/JSONArray.cs | 629 +++++++++++++ libsecondlife-cs/JSONlib/JSONFacade.cs | 36 + libsecondlife-cs/JSONlib/JSONObject.cs | 882 ++++++++++++++++++ libsecondlife-cs/JSONlib/JSONTokener.cs | 463 +++++++++ libsecondlife-cs/JSONlib/JSONUtils.cs | 69 ++ libsecondlife-cs/JSONlib/Nii.JSON.csproj | 115 +++ libsecondlife-cs/JSONlib/json.doc.xml | 878 +++++++++++++++++ libsecondlife-cs/NetworkManager.cs | 79 +- libsecondlife-cs/Packet.cs | 2 +- libsecondlife-cs/Types.cs | 20 +- .../examples/name2key/name2key.cs | 3 +- .../examples/name2key/name2key.csproj | 1 + .../examples/slaccountant/frmSLAccountant.cs | 6 +- .../examples/slaccountant/slaccountant.csproj | 1 + libsecondlife-cs/examples/sldump/sldump.cs | 6 +- .../examples/sldump/sldump.csproj | 1 + libsecondlife-cs/libsecondlife.csproj | 5 + libsecondlife-cs/libsecondlife.sln | 8 + 20 files changed, 3224 insertions(+), 44 deletions(-) create mode 100644 libsecondlife-cs/JSONlib/JSONArray.cs create mode 100644 libsecondlife-cs/JSONlib/JSONFacade.cs create mode 100644 libsecondlife-cs/JSONlib/JSONObject.cs create mode 100644 libsecondlife-cs/JSONlib/JSONTokener.cs create mode 100644 libsecondlife-cs/JSONlib/JSONUtils.cs create mode 100644 libsecondlife-cs/JSONlib/Nii.JSON.csproj create mode 100644 libsecondlife-cs/JSONlib/json.doc.xml diff --git a/libsecondlife-cs/AssemblyInfo.cs b/libsecondlife-cs/AssemblyInfo.cs index f6f2d402..88c74788 100644 --- a/libsecondlife-cs/AssemblyInfo.cs +++ b/libsecondlife-cs/AssemblyInfo.cs @@ -7,11 +7,11 @@ using System.Runtime.CompilerServices; // associated with an assembly. // [assembly: AssemblyTitle("libsecondlife")] -[assembly: AssemblyDescription("")] +[assembly: AssemblyDescription("Networking layer for the Second Life world")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Second Life Reverse Engineering Team")] [assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("")] +[assembly: AssemblyCopyright("(c) 2006 Second Life Reverse Engineering Team")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -26,7 +26,7 @@ using System.Runtime.CompilerServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("0.0.3.0")] +[assembly: AssemblyVersion("0.0.4.0")] // // In order to sign your assembly you must specify a key to use. Refer to the diff --git a/libsecondlife-cs/Avatar.cs b/libsecondlife-cs/Avatar.cs index 15a8feab..5dd95bfe 100644 --- a/libsecondlife-cs/Avatar.cs +++ b/libsecondlife-cs/Avatar.cs @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2006, Second Life Reverse Engineering Team * All rights reserved. @@ -53,6 +50,10 @@ namespace libsecondlife public string FirstName; public string LastName; public string TeleportMessage; + public LLVector3d Position; + public LLVector3d LookAt; + public LLVector3d HomePosition; + public LLVector3d HomeLookAt; private SecondLife Client; private int TeleportStatus; @@ -67,27 +68,56 @@ namespace libsecondlife Client = client; TeleportMessage = ""; - // Setup the callbacks - PacketCallback callback = new PacketCallback(TeleportHandler); + // Create emtpy vectors for now + HomeLookAt = HomePosition = LookAt = Position = new LLVector3d(); + + // Location callback + PacketCallback callback = new PacketCallback(LocationHandler); + Client.Network.InternalCallbacks["CoarseLocationUpdate"] = callback; + + // Teleport callbacks + callback = new PacketCallback(TeleportHandler); Client.Network.InternalCallbacks["TeleportStart"] = callback; Client.Network.InternalCallbacks["TeleportProgress"] = callback; Client.Network.InternalCallbacks["TeleportFailed"] = callback; Client.Network.InternalCallbacks["TeleportFinish"] = callback; // Instant Message Callback - PacketCallback IMCallback = new PacketCallback(InstantMessageHandler); - Client.Network.InternalCallbacks["ImprovedInstantMessage"] = IMCallback; + callback = new PacketCallback(InstantMessageHandler); + Client.Network.InternalCallbacks["ImprovedInstantMessage"] = callback; // Chat Callback - PacketCallback ChatCallback = new PacketCallback(ChatHandler); - Client.Network.InternalCallbacks["ChatFromSimulator"] = ChatCallback; + callback = new PacketCallback(ChatHandler); + Client.Network.InternalCallbacks["ChatFromSimulator"] = callback; TeleportTimer = new Timer(8000); TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent); TeleportTimeout = false; } - private void InstantMessageHandler(Packet packet, Circuit circuit) + private void LocationHandler(Packet packet, Circuit circuit) + { + foreach (Block block in packet.Blocks()) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "X") + { + Position.X = Convert.ToDouble((byte)field.Data); + } + else if (field.Layout.Name == "Y") + { + Position.Y = Convert.ToDouble((byte)field.Data); + } + else if (field.Layout.Name == "Z") + { + Position.Z = Convert.ToDouble((byte)field.Data); + } + } + } + } + + private void InstantMessageHandler(Packet packet, Circuit circuit) { if (packet.Layout.Name == "ImprovedInstantMessage") { @@ -251,13 +281,13 @@ namespace libsecondlife Client.Network.SendPacket(packet); } - public void Chat(string message) + public void Chat(string message, byte type) { LLUUID CommandID = new LLUUID(); LLVector3 Position = new LLVector3(0.0F,0.0F,0.0F); - Packet packet = PacketBuilder.ChatOut(Client.Protocol,Client.Avatar.ID,Client.Network.SessionID, - message, 0, 0, 0, CommandID,20,Position); + Packet packet = PacketBuilder.Chat(Client.Protocol, Client.Avatar.ID, Client.Network.SessionID, + message, type, 0, 0, CommandID, 20, Position); Client.Network.SendPacket(packet); } @@ -267,7 +297,7 @@ namespace libsecondlife LLUUID CommandID = new LLUUID(); LLVector3 Position = new LLVector3(0.0F,0.0F,0.0F); - Packet packet = PacketBuilder.ChatOut(Client.Protocol,Client.Avatar.ID,Client.Network.SessionID, + Packet packet = PacketBuilder.Chat(Client.Protocol,Client.Avatar.ID,Client.Network.SessionID, message, 0, 0, 0, CommandID,100,Position); Client.Network.SendPacket(packet); diff --git a/libsecondlife-cs/JSONlib/JSONArray.cs b/libsecondlife-cs/JSONlib/JSONArray.cs new file mode 100644 index 00000000..58b968ff --- /dev/null +++ b/libsecondlife-cs/JSONlib/JSONArray.cs @@ -0,0 +1,629 @@ +using System; +using System.Collections; +using System.Text; + +namespace Nii.JSON +{ + /// + /// + /// A JSONArray is an ordered sequence of values. Its external form is a string + /// wrapped in square brackets with commas between the values. The internal form + /// is an object having get() and opt() methods for accessing the values by + /// index, and put() methods for adding or replacing values. The values can be + /// any of these types: Boolean, JSONArray, JSONObject, Number, String, or the + /// JSONObject.NULL object. + /// + /// + /// The constructor can convert a JSON external form string into an + /// internal form Java object. The toString() method creates an external + /// form string. + /// + /// + /// A get() method returns a value if one can be found, and throws an exception + /// if one cannot be found. An opt() method returns a default value instead of + /// throwing an exception, and so is useful for obtaining optional values. + /// + /// + /// The generic get() and opt() methods return an object which you can cast or + /// query for type. There are also typed get() and opt() methods that do typing + /// checking and type coersion for you. + /// + /// + /// The texts produced by the toString() methods are very strict. + /// The constructors are more forgiving in the texts they will accept. + /// + /// + /// + /// An extra comma may appear just before the closing bracket. + /// Strings may be quoted with single quotes. + /// Strings do not need to be quoted at all if they do not contain leading + /// or trailing spaces, and if they do not contain any of these characters: + /// { } [ ] / \ : , + /// Numbers may have the 0- (octal) or 0x- (hex) prefix. + /// + /// + /// + /// Public Domain 2002 JSON.org + /// @author JSON.org + /// @version 0.1 + /// + /// Ported to C# by Are Bjolseth, teleplan.no + /// TODO: + /// 1. Implement Custom exceptions + /// 2. Add indexer JSONObject[i] = object, and object = JSONObject[i]; + /// 3. Add indexer JSONObject["key"] = object, and object = JSONObject["key"] + /// 4. Add unit testing + /// 5. Add log4net + /// 6. Make get/put methods private, to force use of indexer instead? + /// + public class JSONArray + { + /// The ArrayList where the JSONArray's properties are kept. + private ArrayList myArrayList; + + #region JSONArray Constructors + /// + /// Construct an empty JSONArray + /// + public JSONArray() + { + myArrayList = new ArrayList(); + } + + /// + /// Construct a JSONArray from a JSONTokener. + /// + /// A JSONTokener + public JSONArray(JSONTokener x) : this() + { + if (x.nextClean() != '[') + { + throw new Exception("A JSONArray must start with '['"); + } + if (x.nextClean() == ']') + { + return; + } + x.back(); + while (true) + { + myArrayList.Add(x.nextObject()); + switch (x.nextClean()) + { + case ',': + if (x.nextClean() == ']') + { + return; + } + x.back(); + break; + case ']': + return; + default: + throw new Exception("Expected a ',' or ']'"); + } + } + } + + /// + /// Construct a JSONArray from a source string. + /// + /// A string that begins with '[' and ends with ']'. + public JSONArray(string s) : this(new JSONTokener(s)) + { + } + + /// + /// Construct a JSONArray from a Collection. + /// + /// A Collection. + public JSONArray(ICollection collection) + { + myArrayList = new ArrayList(collection); + } + #endregion + + #region C# extensions. Indexer and property + /// + /// Alternate to Java get/put method, by using indexer + /// + public object this[int i] + { + get + { + return opt(i); + } + set + { + //myArrayList[i] = value; + put(i, value); + } + } + + /// + /// Alternativ to Java, getArrayList, by using propery + /// + public IList List + { + get + { + return myArrayList; + } + } + #endregion + + #region Getters for a value associated with an index. + /// + /// Get the object value associated with an index. + /// Use indexer instead!!! Added to be true to the original Java implementation + /// + /// index subscript. The index must be between 0 and length()-1 + /// An object value. + public object getValue(int i) + { + object obj = opt(i); + if (obj == null) + { + string msg = string.Format("JSONArray[{0}] not found", i); + throw new Exception(msg); + } + return obj; + //return myArrayList[i]; + } + + /// + /// Get the ArrayList which is holding the elements of the JSONArray. + /// Use the indexer instead!! Added to be true to the orignal Java src + /// + /// The ArrayList + public IList getArrayList() + { + return myArrayList; + } + + /// + /// Get the boolean value associated with an index. + /// The string values "true" and "false" are converted to boolean. + /// + /// index subscript + /// The truth + public bool getBoolean(int i) + { + object obj = getValue(i); + if (obj is bool) + { + return (bool)obj; + } + string msg = string.Format("JSONArray[{0}]={1} not a Boolean", i, obj); + throw new Exception(msg); + } + + /// + /// Get the double value associated with an index. + /// + /// index subscript + /// A double value + public double getDouble(int i) + { + object obj = getValue(i); + if (obj is double) + return (double)obj; + if (obj is float) + return (double)obj; + if (obj is string) + { + return Convert.ToDouble(obj); + } + string msg = string.Format("JSONArray[{0}]={1} not a double", i, obj); + throw new Exception(msg); + } + + /// + /// Get the int value associated with an index. + /// + /// index subscript + /// The int value + public int getInt(int i) + { + object obj = getValue(i); + if (obj is int) + return (int)obj; + if (obj is string) + { + return Convert.ToInt32(obj); + } + string msg = string.Format("JSONArray[{0}]={1} not a int", i, obj); + throw new Exception(msg); + } + + /// + /// Get the JSONArray associated with an index. + /// + /// index subscript + /// A JSONArray value + public JSONArray getJSONArray(int i) + { + object obj = getValue(i); + if (obj is JSONArray) + { + return (JSONArray)obj; + } + string msg = string.Format("JSONArray[{0}]={1} not a JSONArray", i, obj); + throw new Exception(msg); + } + + /// + /// Get the JSONObject associated with an index. + /// + /// index subscript + /// A JSONObject value + public JSONObject getJSONObject(int i) + { + object obj = getValue(i); + if (obj is JSONObject) + { + return (JSONObject)obj; + } + string msg = string.Format("JSONArray[{0}]={1} not a JSONObject", i, obj); + throw new Exception(msg); + } + + /// + /// Get the string associated with an index. + /// + /// index subscript + /// A string value. + public string getString(int i) + { + object obj = getValue(i); + if (obj is string) + { + return (string)obj; + } + string msg = string.Format("JSONArray[{0}]={1} not a string", i, obj); + throw new Exception(msg); + } + #endregion + + /// + /// Determine if the value is null. + /// + /// index subscript + /// true if the value at the index is null, or if there is no value. + public bool isNull(int i) + { + object obj = opt(i); + return (obj == null || obj.Equals(null)); + } + + /// + /// Make a string from the contents of this JSONArray. The separator string + /// is inserted between each element. + /// Warning: This method assumes that the data structure is acyclical. + /// + /// separator A string that will be inserted between the elements. + /// A string. + public string join(string separator) + { + object obj; + StringBuilder sb = new StringBuilder(); + for (int i=0; i 0) + { + sb.Append(separator); + } + obj = myArrayList[i]; + + if (obj == null) + { + sb.Append(""); + } + else if (obj is string) + { + sb.Append(JSONUtils.Enquote((string)obj)); + } + else if (obj is int) + { + sb.Append(((int)obj).ToString()); + } + else + { + sb.Append(obj.ToString()); + } + } + return sb.ToString(); + } + + /// + /// Get the length of the JSONArray. + /// Added to be true to the original Java implementation + /// + /// Number of JSONObjects in array + public int Length() + { + return myArrayList.Count; + } + /// + /// Get the length of the JSONArray. + /// Using a propery instead of method + /// + public int Count + { + get + { + return myArrayList.Count; + } + } + + + #region Get the optional value associated with an index. + /// + /// Get the optional object value associated with an index. + /// + /// index subscript + /// object at that index. + public object opt(int i) + { + if (i < 0 || i >= myArrayList.Count) + throw new ArgumentOutOfRangeException("int i", i, "Index out of bounds!"); + + return myArrayList[i]; + } + + /// + /// Get the optional boolean value associated with an index. + /// + /// index subscript + /// The truth + public bool optBoolean(int i) + { + return optBoolean(i,false); + } + + /// + /// Get the optional boolean value associated with an index. + /// It returns the defaultValue if there is no value at that index or if it is not + /// a Boolean or the String "true" or "false". + /// + /// index subscript + /// + /// The truth. + public bool optBoolean(int i, bool defaultValue) + { + object obj = opt(i); + if (obj != null) + { + return (bool)obj; + } + return defaultValue; + } + + /// + /// Get the optional double value associated with an index. + /// NaN is returned if the index is not found, + /// or if the value is not a number and cannot be converted to a number. + /// + /// index subscript + /// The double value object + public double optDouble(int i) + { + return optDouble(i,double.NaN); + } + + /// + /// Get the optional double value associated with an index. + /// NaN is returned if the index is not found, + /// or if the value is not a number and cannot be converted to a number. + /// + /// index subscript + /// + /// The double value object + public double optDouble(int i, double defaultValue) + { + object obj = opt(i); + if (obj != null) + { + if (obj is double) + return (double)obj; + if (obj is float) + return (float)obj; + if (obj is string) + { + return Convert.ToDouble(obj); + } + string msg = string.Format("JSONArray[{0}]={1} not a double", i, obj); + throw new Exception(msg); + } + return defaultValue; + } + + /// + /// Get the optional int value associated with an index. + /// Zero is returned if the index is not found, + /// or if the value is not a number and cannot be converted to a number. + /// + /// index subscript + /// The int value object + public int optInt(int i) + { + return optInt(i,0); + } + + /// + /// Get the optional int value associated with an index. + /// The defaultValue is returned if the index is not found, + /// or if the value is not a number and cannot be converted to a number. + /// + /// index subscript + /// The default value + /// The int value object + public int optInt(int i, int defaultValue) + { + object obj = opt(i); + if (obj != null) + { + if (obj is int) + return (int)obj; + if (obj is string) + return Convert.ToInt32(obj); + string msg = string.Format("JSONArray[{0}]={1} not a int", i, obj); + throw new Exception(msg); + } + return defaultValue; + } + + /// + /// Get the optional JSONArray associated with an index. + /// + /// index subscript + /// A JSONArray value, or null if the index has no value, or if the value is not a JSONArray. + public JSONArray optJSONArray(int i) + { + object obj = opt(i); + if (obj is JSONArray) + return (JSONArray)obj; + return null; + } + + /// + /// Get the optional JSONObject associated with an index. + /// Null is returned if the key is not found, or null if the index has + /// no value, or if the value is not a JSONObject. + /// + /// index subscript + /// A JSONObject value + public JSONObject optJSONObject(int i) + { + object obj = opt(i); + if (obj is JSONObject) + { + return (JSONObject)obj; + } + return null; + } + + /// + /// Get the optional string value associated with an index. It returns an + /// empty string if there is no value at that index. If the value + /// is not a string and is not null, then it is coverted to a string. + /// + /// index subscript + /// A String value + public string optString(int i) + { + return optString(i, ""); + } + + /// + /// Get the optional string associated with an index. + /// The defaultValue is returned if the key is not found. + /// + /// index subscript + /// The default value + /// A string value + public string optString(int i, string defaultValue) + { + object obj = opt(i); + if (obj != null) + { + return obj.ToString(); + } + return defaultValue; + } + + #endregion + + #region Put methods - use indexer instead +/** + * OMITTED: + * public JSONArray put(bool val) + * public JSONArray put(double val) + * public JSONArray put(int val) +*/ + /// + /// Append an object value. + /// + /// An object value. The value should be a Boolean, Double, Integer, JSONArray, JSObject, or String, or the JSONObject.NULL object + /// this (JSONArray) + public JSONArray put(object val) + { + myArrayList.Add(val); + return this; + } + +/* + * OMITTED: + * public JSONArray put(int index, boolean value) + * public JSONArray put(int index, double value) + * public JSONArray put(int index, int value) + */ + /// + /// Put or replace a boolean value in the JSONArray. + /// + /// + /// The subscript. If the index is greater than the length of + /// the JSONArray, then null elements will be added as necessary to pad it out. + /// + /// An object value. + /// this (JSONArray) + public JSONArray put(int i, object val) + { + if (i < 0) + { + throw new ArgumentOutOfRangeException("i", i, "Negative indexes illegal"); + } + else if (val == null) + { + throw new ArgumentNullException("val", "Object cannt be null"); + } + else if (i < myArrayList.Count) + { + myArrayList.Insert(i, val); + } + // NOTE! Since i is >= Count, fill null vals before index i, then append new object at i + else + { + while (i != myArrayList.Count) + { + myArrayList.Add(null); + } + myArrayList.Add(val); + } + return this; + } + #endregion + + /// + /// Produce a JSONObject by combining a JSONArray of names with the values + /// of this JSONArray. + /// + /// + /// A JSONArray containing a list of key strings. These will be paired with the values. + /// + /// A JSONObject, or null if there are no names or if this JSONArray + public JSONObject toJSONObject(JSONArray names) + { + if (names == null || names.Length() == 0 || this.Length() == 0) + { + return null; + } + JSONObject jo = new JSONObject(); + for (int i=0; i + /// Make an JSON external form string of this JSONArray. For compactness, no + /// unnecessary whitespace is added. + /// + /// a printable, displayable, transmittable representation of the array. + public override string ToString() + { + return '['+ join(",") + ']'; + } + } +} diff --git a/libsecondlife-cs/JSONlib/JSONFacade.cs b/libsecondlife-cs/JSONlib/JSONFacade.cs new file mode 100644 index 00000000..45cc4b4e --- /dev/null +++ b/libsecondlife-cs/JSONlib/JSONFacade.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Reflection; +using System.Text; + +namespace Nii.JSON +{ + /// + /// Summary description for JsonFacade. + /// + public sealed class JsonFacade + { + /// + /// Parse a Hashtable and return a JSON formatted string + /// + /// + /// + public static string toJSON(IDictionary idict) + { + JSONObject jsob = new JSONObject(idict); + return jsob.ToString(); + } + /// + /// Parse JSON formatted string and return a Hashtable + /// + /// + /// + public static IDictionary fromJSON(string sJSON) + { + JSONObject jsob = new JSONObject(sJSON); + IDictionary idict = jsob.getDictionary(); + return idict; + } + } +} diff --git a/libsecondlife-cs/JSONlib/JSONObject.cs b/libsecondlife-cs/JSONlib/JSONObject.cs new file mode 100644 index 00000000..bd1f8521 --- /dev/null +++ b/libsecondlife-cs/JSONlib/JSONObject.cs @@ -0,0 +1,882 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Text; +using System.Globalization; + +/* + * A JSONObject is an unordered collection of name/value pairs. Its + * external form is a string wrapped in curly braces with colons between the + * names and values, and commas between the values and names. The internal form + * is an object having get() and opt() methods for accessing the values by name, + * and put() methods for adding or replacing values by name. The values can be + * any of these types: Boolean, JSONArray, JSONObject, Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert an external form string into an internal form + * Java object. The toString() method creates an external form string. + *

+ * A get() method returns a value if one can be found, and throws an exception + * if one cannot be found. An opt() method returns a default value instead of + * throwing an exception, and so is useful for obtaining optional values. + *

+ * The generic get() and opt() methods return an object, which you can cast or + * query for type. There are also typed get() and opt() methods that do typing + * checking and type coersion for you. + *

+ * The texts produced by the toString() methods are very strict. + * The constructors are more forgiving in the texts they will accept. + *

    + *
  • An extra comma may appear just before the closing brace.
  • + *
  • Strings may be quoted with single quotes.
  • + *
  • Strings do not need to be quoted at all if they do not contain leading + * or trailing spaces, and if they do not contain any of these characters: + * { } [ ] / \ : ,
  • + *
  • Numbers may have the 0- (octal) or 0x- (hex) prefix.
  • + *
+ *

+ * Public Domain 2002 JSON.org + * @author JSON.org + * @version 0.1 + *

+ * Ported to C# by Are Bjolseth, teleplan.no + * TODO: + * 1. Implement Custom exceptions + * 2. Add indexer JSONObject[i] = object, and object = JSONObject[i]; + * 3. Add indexer JSONObject["key"] = object, and object = JSONObject["key"] + * 4. Add unit testing + * 5. Add log4net + */ +namespace Nii.JSON +{ + ///

+ /// + /// A JSONArray is an ordered sequence of values. Its external form is a string + /// wrapped in square brackets with commas between the values. The internal form + /// is an object having get() and opt() methods for accessing the values by + /// index, and put() methods for adding or replacing values. The values can be + /// any of these types: Boolean, JSONArray, JSONObject, Number, String, or the + /// JSONObject.NULL object. + /// + /// + /// The constructor can convert a JSON external form string into an + /// internal form Java object. The toString() method creates an external + /// form string. + /// + /// + /// A get() method returns a value if one can be found, and throws an exception + /// if one cannot be found. An opt() method returns a default value instead of + /// throwing an exception, and so is useful for obtaining optional values. + /// + /// + /// The generic get() and opt() methods return an object which you can cast or + /// query for type. There are also typed get() and opt() methods that do typing + /// checking and type coersion for you. + /// + /// + /// The texts produced by the toString() methods are very strict. + /// The constructors are more forgiving in the texts they will accept. + /// + /// + /// + /// An extra comma may appear just before the closing bracket. + /// Strings may be quoted with single quotes. + /// Strings do not need to be quoted at all if they do not contain leading + /// or trailing spaces, and if they do not contain any of these characters: + /// { } [ ] / \ : , + /// Numbers may have the 0- (octal) or 0x- (hex) prefix. + /// + /// + /// + /// Public Domain 2002 JSON.org + /// @author JSON.org + /// @version 0.1 + /// + /// Ported to C# by Are Bjolseth, teleplan.no + /// TODO: + /// 1. Implement Custom exceptions + /// 2. Add indexer JSONObject[i] = object, and object = JSONObject[i]; + /// 3. Add indexer JSONObject["key"] = object, and object = JSONObject["key"] + /// 4. Add unit testing + /// 5. Add log4net + /// 6. Make get/put methods private, to force use of indexer instead? + /// + public class JSONObject + { + #region Local struct JSONNull + /// + /// Make a Null object + /// JSONObject.NULL is equivalent to the value that JavaScript calls null, + /// whilst C#'s null is equivalent to the value that JavaScript calls undefined. + /// + public struct JSONNull + { + /* + public object clone() + { + return this; + } + */ + /* + public bool equals(object obj) + { + return (obj == null) || (obj == this); + } + */ + /// + /// Overriden to return "null" + /// + /// null + public override string ToString() + { + //return base.ToString (); + return "null"; + } + } + #endregion + + ///The hash map where the JSONObject's properties are kept. + private Hashtable myHashMap; + + ///A shadow list of keys to enable access by sequence of insertion + private ArrayList myKeyIndexList; + + /// + /// It is sometimes more convenient and less ambiguous to have a NULL + /// object than to use C#'s null value. + /// JSONObject.NULL.toString() returns "null". + /// + public static readonly JSONNull NULL = new JSONNull(); + + #region Constructors for JSONObject + /// + /// Construct an empty JSONObject. + /// + public JSONObject() + { + myHashMap = new Hashtable(); + myKeyIndexList = new ArrayList(); + } + + /// + /// Construct a JSONObject from a JSONTokener. + /// + /// A JSONTokener object containing the source string. + public JSONObject(JSONTokener x) : this() + { + char c; + string key; + if (x.next() == '%') + { + x.unescape(); + } + x.back(); + if (x.nextClean() != '{') + { + throw new Exception("A JSONObject must begin with '{'"); + } + while (true) + { + c = x.nextClean(); + switch (c) + { + case (char)0: + throw new Exception("A JSONObject must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextObject().ToString(); + break; + } + if (x.nextClean() != ':') + { + throw new Exception("Expected a ':' after a key"); + } + object obj = x.nextObject(); + myHashMap.Add(key, obj); + myKeyIndexList.Add(key); + switch (x.nextClean()) + { + case ',': + if (x.nextClean() == '}') + { + return; + } + x.back(); + break; + case '}': + return; + default: + throw new Exception("Expected a ',' or '}'"); + } + } + } + + + /// + /// Construct a JSONObject from a string. + /// + /// A string beginning with '{' and ending with '}'. + public JSONObject(string sJSON) : this(new JSONTokener(sJSON)) + { + + } + + // public JSONObject(Hashtable map) + // By changing to arg to interface, all classes that implements IDictionary can be used + // public interface IDictionary : ICollection, IEnumerable + // Classes that implements IDictionary + // 1. BaseChannelObjectWithProperties - Provides a base implementation of a channel object that wants to provide a dictionary interface to its properties. + // 2. DictionaryBase - Provides the abstract (MustInherit in Visual Basic) base class for a strongly typed collection of key-and-value pairs. + // 3. Hashtable - Represents a collection of key-and-value pairs that are organized based on the hash code of the key. + // 4. HybridDictionary - Implements IDictionary by using a ListDictionary while the collection is small, and then switching to a Hashtable when the collection gets large. + // 5. ListDictionary - Implements IDictionary using a singly linked list. Recommended for collections that typically contain 10 items or less. + // 6. PropertyCollection - Contains the properties of a DirectoryEntry. + // 7. PropertyDescriptorCollection - Represents a collection of PropertyDescriptor objects. + // 8. SortedList - Represents a collection of key-and-value pairs that are sorted by the keys and are accessible by key and by index. + // 9. StateBag - Manages the view state of ASP.NET server controls, including pages. This class cannot be inherited. + // See ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1033/cpref/html/frlrfsystemcollectionsidictionaryclasstopic.htm + + + /// + /// Construct a JSONObject from a IDictionary + /// + /// + public JSONObject(IDictionary map) + { + myHashMap = new Hashtable(map); + myKeyIndexList = new ArrayList(map); + } + + #endregion + + /// + /// Accumulate values under a key. It is similar to the put method except + /// that if there is already an object stored under the key then a + /// JSONArray is stored under the key to hold all of the accumulated values. + /// If there is already a JSONArray, then the new value is appended to it. + /// In contrast, the put method replaces the previous value. + /// + /// A key string. + /// An object to be accumulated under the key. + /// this + public JSONObject accumulate(string key, object val) + { + JSONArray a; + object obj = opt(key); + if (obj == null) + { + put(key, val); + } + else if (obj is JSONArray) + { + a = (JSONArray)obj; + a.put(val); + } + else + { + a = new JSONArray(); + a.put(obj); + a.put(val); + put(key,a); + } + return this; + } + + + #region C# specific extensions + /// + /// Return the key for the associated index + /// + public string this[int i] + { + get + { + DictionaryEntry de = (DictionaryEntry)myKeyIndexList[i]; + return de.Key.ToString(); + } + } + + /// + /// Get/Add an object with the associated key + /// + public object this[string key] + { + get + { + return getValue(key); + } + set + { + put(key,value); + } + } + + /// + /// Return the number of JSON items in hashtable + /// + public int Count + { + get + { + return myHashMap.Count; + } + } + /// + /// C# convenience method + /// + /// The Hashtable + public IDictionary getDictionary() + { + return myHashMap; + } + #endregion + + + #region Gettes for a value associated with a key - use indexer instead + /// + /// Alias to Java get method + /// Get the value object associated with a key. + /// + /// A key string. + /// The object associated with the key. + public object getValue(string key) + { + //return myHashMap[key]; + object obj = opt(key); + if (obj == null) + { + throw new Exception("No such element"); + } + return obj; + } + + /// + /// Get the boolean value associated with a key. + /// + /// A key string. + /// The truth. + public bool getBool(string key) + { + object o = getValue(key); + if (o is bool) + { + bool b = (bool)o; + return b; + } + string msg = string.Format("JSONObject[{0}] is not a Boolean",JSONUtils.Enquote(key)); + throw new Exception(msg); + } + + /// + /// Get the double value associated with a key. + /// + /// A key string. + /// The double value + public double getDouble(string key) + { + object o = getValue(key); + if (o is double) + return (double)o; + if (o is float) + return (double)o; + + if (o is string) + { + return Convert.ToDouble(o); + } + string msg = string.Format("JSONObject[{0}] is not a double",JSONUtils.Enquote(key)); + throw new Exception(msg); + } + + /// + /// Get the int value associated with a key. + /// + /// A key string + /// The integer value. + public int getInt(string key) + { + object o = getValue(key); + if (o is int) + { + return (int)o; + } + + if (o is string) + { + return Convert.ToInt32(o); + } + string msg = string.Format("JSONObject[{0}] is not a int",JSONUtils.Enquote(key)); + throw new Exception(msg); + } + + /// + /// Get the JSONArray value associated with a key. + /// + /// A key string + /// A JSONArray which is the value + public JSONArray getJSONArray(string key) + { + object o = getValue(key); + if (o is JSONArray) + { + return (JSONArray)o; + } + string msg = string.Format("JSONObject[{0}]={1} is not a JSONArray",JSONUtils.Enquote(key),o); + throw new Exception(msg); + } + + /// + /// Get the JSONObject value associated with a key. + /// + /// A key string. + /// A JSONObject which is the value. + public JSONObject getJSONObject(string key) + { + object o = getValue(key); + if (o is JSONObject) + { + return (JSONObject)o; + } + string msg = string.Format("JSONObject[{0}]={1} is not a JSONArray",JSONUtils.Enquote(key),o); + throw new Exception(msg); + } + + /// + /// Get the string associated with a key. + /// + /// A key string. + /// A string which is the value. + public string getString(string key) + { + return getValue(key).ToString(); + } + #endregion + + + /// + /// Determine if the JSONObject contains a specific key. + /// + /// A key string. + /// true if the key exists in the JSONObject. + public bool has(string key) + { + return myHashMap.ContainsKey(key); + } + + + /// + /// Get an enumeration of the keys of the JSONObject. + /// Added to be true to orginal Java implementation + /// Indexers are easier to use + /// + /// + public IEnumerator keys() + { + return myHashMap.Keys.GetEnumerator(); + } + + /// + /// Determine if the value associated with the key is null or if there is no value. + /// + /// A key string + /// true if there is no value associated with the key or if the valus is the JSONObject.NULL object + public bool isNull(string key) + { + return JSONObject.NULL.Equals(opt(key)); + } + + /// + /// Get the number of keys stored in the JSONObject. + /// + /// The number of keys in the JSONObject. + public int Length() + { + return myHashMap.Count; + } + + /// + /// Produce a JSONArray containing the names of the elements of this JSONObject + /// + /// A JSONArray containing the key strings, or null if the JSONObject + public JSONArray names() + { + JSONArray ja = new JSONArray(); + + //NOTE!! I choose to use keys stored in the ArrayList, to maintain sequence order + foreach (string key in myKeyIndexList) + { + ja.put(key); + } + if (ja.Length() == 0) + { + return null; + } + return ja; + } + + /// + /// Produce a string from a number. + /// + /// Number value type object + /// String representation of the number + public string numberToString(object number) + { + if (number is float && ((float)number) == float.NaN) + { + string msg = string.Format(""); + throw new ArgumentException("object must be a valid number", "number"); + } + if (number is double && ((double)number) == double.NaN) + { + string msg = string.Format(""); + throw new ArgumentException("object must be a valid number", "number"); + } + + // Shave off trailing zeros and decimal point, if possible + + string s = ((double) number).ToString(NumberFormatInfo.InvariantInfo).ToLower(); + + if (s.IndexOf('e') < 0 && s.IndexOf('.') > 0) + { + while(s.EndsWith("0")) + { + s.Substring(0,s.Length-1); + } + if (s.EndsWith(".")) + { + s.Substring(0,s.Length-1); + } + } + return s; + } + + #region Get an optional value associated with a key. + /// + /// Get an optional value associated with a key. + /// + /// A key string + /// An object which is the value, or null if there is no value. + public object opt(string key) + { + if (key == null) + { + throw new ArgumentNullException("key", "Null key"); + } + return myHashMap[key]; + } + + /// + /// Get an optional value associated with a key. + /// It returns false if there is no such key, or if the value is not + /// Boolean.TRUE or the String "true". + /// + /// A key string. + /// bool value object + public bool optBoolean(string key) + { + return optBoolean(key, false); + } + + /// + /// Get an optional value associated with a key. + /// It returns false if there is no such key, or if the value is not + /// Boolean.TRUE or the String "true". + /// + /// A key string. + /// The preferred return value if conversion fails + /// bool value object + public bool optBoolean(string key, bool defaultValue) + { + object obj = opt(key); + if (obj != null) + { + if (obj is bool) + return (bool)obj; + if (obj is string) + { + return Convert.ToBoolean(obj); + } + } + return defaultValue; + } + + /// + /// Get an optional double associated with a key, + /// or NaN if there is no such key or if its value is not a number. + /// If the value is a string, an attempt will be made to evaluate it as + /// a number. + /// + /// A string which is the key. + /// A double value object + public double optDouble(string key) + { + return optDouble(key, double.NaN); + } + + /// + /// Get an optional double associated with a key, + /// or NaN if there is no such key or if its value is not a number. + /// If the value is a string, an attempt will be made to evaluate it as + /// a number. + /// + /// A string which is the key. + /// The default + /// A double value object + public double optDouble(string key, double defaultValue) + { + object obj = opt(key); + if (obj != null) + { + if (obj is double) + return (double)obj; + if (obj is float) + return (double)obj; + if (obj is string) + { + return Convert.ToDouble(obj); + } + } + return defaultValue; + } + + /// + /// Get an optional double associated with a key, or the + /// defaultValue if there is no such key or if its value is not a number. + /// If the value is a string, an attempt will be made to evaluate it as + /// number. + /// + /// A key string. + /// An int object value + public int optInt(string key) + { + return optInt(key, 0); + } + + /// + /// Get an optional double associated with a key, or the + /// defaultValue if there is no such key or if its value is not a number. + /// If the value is a string, an attempt will be made to evaluate it as + /// number. + /// + /// A key string. + /// The default value + /// An int object value + public int optInt(string key, int defaultValue) + { + object obj = opt(key); + if (obj != null) + { + if (obj is int) + return (int)obj; + if (obj is string) + return Convert.ToInt32(obj); + } + return defaultValue; + } + + /// + /// Get an optional JSONArray associated with a key. + /// It returns null if there is no such key, or if its value is not a JSONArray + /// + /// A key string + /// A JSONArray which is the value + public JSONArray optJSONArray(string key) + { + object obj = opt(key); + if (obj is JSONArray) + { + return (JSONArray)obj; + } + return null; + } + + /// + /// Get an optional JSONObject associated with a key. + /// It returns null if there is no such key, or if its value is not a JSONObject. + /// + /// A key string. + /// A JSONObject which is the value + public JSONObject optJSONObject(string key) + { + object obj = opt(key); + if (obj is JSONObject) + { + return (JSONObject)obj; + } + return null; + } + + /// + /// Get an optional string associated with a key. + /// It returns an empty string if there is no such key. If the value is not + /// a string and is not null, then it is coverted to a string. + /// + /// A key string. + /// A string which is the value. + public string optString(string key) + { + object obj = opt(key); + + return optString(key, ""); + } + + /// + /// Get an optional string associated with a key. + /// It returns the defaultValue if there is no such key. + /// + /// A key string. + /// The default + /// A string which is the value. + public string optString(string key, string defaultValue) + { + object obj = opt(key); + if (obj != null) + { + return obj.ToString(); + } + return defaultValue; + } + #endregion + + #region Put methods for adding key/value pairs + // OMITTED - all put methods can be replaced by a indexer in C# + // - =================================================== + // public JSONObject put(String key, boolean value) + // public JSONObject put(String key, double value) + // public JSONObject put(String key, int value) + + /// + /// Put a key/value pair in the JSONObject. If the value is null, + /// then the key will be removed from the JSONObject if it is present. + /// + /// A key string. + /// + /// An object which is the value. It should be of one of these + /// types: Boolean, Double, Integer, JSONArray, JSONObject, String, or the + /// JSONObject.NULL object. + /// + /// JSONObject + public JSONObject put(string key, object val) + { + if (key == null) + { + throw new ArgumentNullException("key", "key cannot be null"); + } + if (val != null) + { + if (!myHashMap.ContainsKey(key)) + { + string test = key; + myHashMap.Add(key,val); + myKeyIndexList.Add(key); + } + else + { + myHashMap[key]=val; + } + } + else + { + remove(key); + } + return this; + } + + /// + /// Add a key value pair + /// + /// + /// + /// + public JSONObject putOpt(string key, object val) + { + if (val != null) + { + put(key,val); + } + return this; + } + #endregion + + /// + /// Remove a object assosiateted with the given key + /// + /// + /// + public object remove(string key) + { + if (myHashMap.ContainsKey(key)) + { + // TODO - does it really work ??? + object obj = myHashMap[key]; + myHashMap.Remove(key); + myKeyIndexList.Remove(key); + return obj; + } + return null; + } + + /// + /// Append an array of JSONObjects to current object + /// + /// + /// + public JSONArray toJSONArray(JSONArray names) + { + if (names == null | names.Length() == 0) + return null; + + JSONArray ja = new JSONArray(); + for (int i=0; i + /// Overridden to return a JSON formattet object as a string + /// + /// JSON object as formatted string + public override string ToString() + { + object obj = null; + //string s; + StringBuilder sb = new StringBuilder(); + + sb.Append('{'); + foreach (string key in myHashMap.Keys) //NOTE! Could also use myKeyIndexList !!! + { + if (obj != null) + sb.Append(','); + obj = myHashMap[key]; + if (obj != null) + { + sb.Append(JSONUtils.Enquote(key)); + sb.Append(':'); + + if (obj is string) + { + sb.Append(JSONUtils.Enquote((string)obj)); + } + else if (obj is float || obj is double) + { + sb.Append(numberToString(obj)); + } + else if (obj is bool) + { + sb.Append(obj.ToString().ToLower()); + } + else + { + sb.Append(obj.ToString()); + } + } + } + sb.Append('}'); + return sb.ToString(); + } + } +} diff --git a/libsecondlife-cs/JSONlib/JSONTokener.cs b/libsecondlife-cs/JSONlib/JSONTokener.cs new file mode 100644 index 00000000..a4c7f7b1 --- /dev/null +++ b/libsecondlife-cs/JSONlib/JSONTokener.cs @@ -0,0 +1,463 @@ +using System; +using System.Text; +using System.Globalization; + +namespace Nii.JSON +{ + /// + /// + /// A JSONTokener takes a source string and extracts characters and tokens from + /// it. It is used by the JSONObject and JSONArray constructors to parse + /// JSON source strings. + /// + /// + /// Public Domain 2002 JSON.org + /// @author JSON.org + /// @version 0.1 + /// + /// Ported to C# by Are Bjolseth, teleplan.no + /// + /// + /// Implement Custom exceptions + /// Add unit testing + /// Add log4net + /// + /// + /// + public class JSONTokener + { + /// The index of the next character. + private int myIndex; + /// The source string being tokenized. + private string mySource; + + /// + /// Construct a JSONTokener from a string. + /// + /// A source string. + public JSONTokener(string s) + { + myIndex = 0; + mySource = s; + } + + /// + /// Back up one character. This provides a sort of lookahead capability, + /// so that you can test for a digit or letter before attempting to parse + /// the next number or identifier. + /// + public void back() + { + if (myIndex > 0) + myIndex -= 1; + } + + /// + /// Get the hex value of a character (base16). + /// + /// + /// A character between '0' and '9' or between 'A' and 'F' or + /// between 'a' and 'f'. + /// + /// An int between 0 and 15, or -1 if c was not a hex digit. + public static int dehexchar(char c) + { + if (c >= '0' && c <= '9') + { + return c - '0'; + } + if (c >= 'A' && c <= 'F') + { + return c + 10 - 'A'; + } + if (c >= 'a' && c <= 'f') + { + return c + 10 - 'a'; + } + return -1; + } + + /// + /// Determine if the source string still contains characters that next() can consume. + /// + /// true if not yet at the end of the source. + public bool more() + { + return myIndex < mySource.Length; + } + + /// + /// Get the next character in the source string. + /// + /// The next character, or 0 if past the end of the source string. + public char next() + { + char c = more() ? mySource[myIndex] : (char)0; + myIndex +=1; + return c; + } + + /// + /// Consume the next character, and check that it matches a specified character + /// + /// The character to match. + /// The character. + public char next(char c) + { + char n = next(); + if (n != c) + { + string msg = "Expected '" + c + "' and instead saw '" + n + "'."; + throw (new Exception(msg)); + } + return n; + } + + /// + /// Get the next n characters. + /// + /// The number of characters to take. + /// A string of n characters. + public string next(int n) + { + int i = myIndex; + int j = i + n; + if (j >= mySource.Length) + { + string msg = "Substring bounds error"; + throw (new Exception(msg)); + } + myIndex += n; + return mySource.Substring(i,j); + } + + /// + /// Get the next char in the string, skipping whitespace + /// and comments (slashslash and slashstar). + /// + /// A character, or 0 if there are no more characters. + public char nextClean() + { + while (true) + { + char c = next(); + if (c == '/') + { + switch (next()) + { + case '/': + do + { + c = next(); + } while (c != '\n' && c != '\r' && c != 0); + break; + case '*': + while (true) + { + c = next(); + if (c == 0) + { + throw (new Exception("Unclosed comment.")); + } + if (c == '*') + { + if (next() == '/') + { + break; + } + back(); + } + } + break; + default: + back(); + return '/'; + } + } + else if (c == 0 || c > ' ') + { + return c; + } + } + } + + /// + /// Return the characters up to the next close quote character. + /// Backslash processing is done. The formal JSON format does not + /// allow strings in single quotes, but an implementation is allowed to + /// accept them. + /// + /// The quoting character, either " or ' + /// A String. + public string nextString(char quote) + { + char c; + StringBuilder sb = new StringBuilder(); + while (true) + { + c = next(); + if ((c == 0x00) || (c == 0x0A) || (c == 0x0D)) + { + throw (new Exception("Unterminated string")); + } + // CTRL chars + if (c == '\\') + { + c = next(); + switch (c) + { + case 'b': //Backspace + sb.Append('\b'); + break; + case 't': //Horizontal tab + sb.Append('\t'); + break; + case 'n': //newline + sb.Append('\n'); + break; + case 'f': //Form feed + sb.Append('\f'); + break; + case 'r': // Carriage return + sb.Append('\r'); + break; + case 'u': + //sb.append((char)Integer.parseInt(next(4), 16)); // 16 == radix, ie. hex + int iascii = int.Parse(next(4),System.Globalization.NumberStyles.HexNumber); + sb.Append((char)iascii); + break; + default: + sb.Append(c); + break; + } + } + else + { + if (c == quote) + { + return sb.ToString(); + } + sb.Append(c); + } + }//END-while + } + + /// + /// Get the text up but not including the specified character or the + /// end of line, whichever comes first. + /// + /// A delimiter character. + /// A string. + public string nextTo(char d) + { + StringBuilder sb = new StringBuilder(); + while (true) + { + char c = next(); + if (c == d || c == (char)0 || c == '\n' || c == '\r') + { + if (c != (char)0) + { + back(); + } + return sb.ToString().Trim(); + } + sb.Append(c); + } + } + + /// + /// Get the text up but not including one of the specified delimeter + /// characters or the end of line, which ever comes first. + /// + /// A set of delimiter characters. + /// A string, trimmed. + public string nextTo(string delimiters) + { + char c; + StringBuilder sb = new StringBuilder(); + while (true) + { + c = next(); + if ((delimiters.IndexOf(c) >= 0) || (c == (char)0 ) || (c == '\n') || (c == '\r')) + { + if (c != (char)0) + { + back(); + } + return sb.ToString().Trim(); + } + sb.Append(c); + } + } + + /// + /// Get the next value as object. The value can be a Boolean, Double, Integer, + /// JSONArray, JSONObject, or String, or the JSONObject.NULL object. + /// + /// An object. + public object nextObject() + { + char c = nextClean(); + string s; + + if (c == '"' || c == '\'') + { + return nextString(c); + } + // Object + if (c == '{') + { + back(); + return new JSONObject(this); + } + + // JSON Array + if (c == '[') + { + back(); + return new JSONArray(this); + } + + StringBuilder sb = new StringBuilder(); + + char b = c; + while (c >= ' ' && c != ':' && c != ',' && c != ']' && c != '}' && c != '/') + { + sb.Append(c); + c = next(); + } + back(); + + s = sb.ToString().Trim(); + if (s == "true") + return bool.Parse("true"); + if (s == "false") + return bool.Parse("false"); + if (s == "null") + return JSONObject.NULL; + + if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') + { + try + { + return Convert.ToInt32(s); + } + catch (Exception e) + { + string msg = e.Message; + } + try + { + return Convert.ToDouble(s, NumberFormatInfo.InvariantInfo); + } + catch (Exception e) + { + string msg = e.Message; + } + } + if (s == "") + { + throw (new Exception("Missing value")); + } + return s; + } + + /// + /// Skip characters until the next character is the requested character. + /// If the requested character is not found, no characters are skipped. + /// + /// A character to skip to. + /// + /// The requested character, or zero if the requested character is not found. + /// + public char skipTo(char to) + { + char c; + int i = myIndex; + do + { + c = next(); + if (c == (char)0) + { + myIndex = i; + return c; + } + }while (c != to); + + back(); + return c; + } + + /// + /// Skip characters until past the requested string. + /// If it is not found, we are left at the end of the source. + /// + /// A string to skip past. + public void skipPast(string to) + { + myIndex = mySource.IndexOf(to, myIndex); + if (myIndex < 0) + { + myIndex = mySource.Length; + } + else + { + myIndex += to.Length; + } + } + + // TODO implement exception SyntaxError + + + /// + /// Make a printable string of this JSONTokener. + /// + /// " at character [myIndex] of [mySource]" + public override string ToString() + { + return " at charachter " + myIndex + " of " + mySource; + } + + /// + /// Unescape the source text. Convert %hh sequences to single characters, + /// and convert plus to space. There are Web transport systems that insist on + /// doing unnecessary URL encoding. This provides a way to undo it. + /// + public void unescape() + { + mySource = unescape(mySource); + } + + /// + /// Convert %hh sequences to single characters, and convert plus to space. + /// + /// A string that may contain plus and %hh sequences. + /// The unescaped string. + public static string unescape(string s) + { + int len = s.Length; + StringBuilder sb = new StringBuilder(); + for (int i=0; i < len; i++) + { + char c = s[i]; + if (c == '+') + { + c = ' '; + } + else if (c == '%' && (i + 2 < len)) + { + int d = dehexchar(s[i+1]); + int e = dehexchar(s[i+2]); + if (d >= 0 && e >= 0) + { + c = (char)(d*16 + e); + i += 2; + } + } + sb.Append(c); + } + return sb.ToString(); + } + } +} diff --git a/libsecondlife-cs/JSONlib/JSONUtils.cs b/libsecondlife-cs/JSONlib/JSONUtils.cs new file mode 100644 index 00000000..6e5af027 --- /dev/null +++ b/libsecondlife-cs/JSONlib/JSONUtils.cs @@ -0,0 +1,69 @@ +using System; +using System.Text; + +namespace Nii.JSON +{ + /// + /// Public Domain 2002 JSON.org + /// @author JSON.org + /// @version 0.1 + /// Ported to C# by Are Bjolseth, teleplan.no + /// + public sealed class JSONUtils + { + /// + /// Produce a string in double quotes with backslash sequences in all the right places. + /// + /// A String + /// A String correctly formatted for insertion in a JSON message. + public static string Enquote(string s) + { + if (s == null || s.Length == 0) + { + return "\"\""; + } + char c; + int i; + int len = s.Length; + StringBuilder sb = new StringBuilder(len + 4); + string t; + + sb.Append('"'); + for (i = 0; i < len; i += 1) + { + c = s[i]; + if ((c == '\\') || (c == '"') || (c == '>')) + { + sb.Append('\\'); + sb.Append(c); + } + else if (c == '\b') + sb.Append("\\b"); + else if (c == '\t') + sb.Append("\\t"); + else if (c == '\n') + sb.Append("\\n"); + else if (c == '\f') + sb.Append("\\f"); + else if (c == '\r') + sb.Append("\\r"); + else + { + if (c < ' ') + { + //t = "000" + Integer.toHexString(c); + string tmp = new string(c,1); + t = "000" + int.Parse(tmp,System.Globalization.NumberStyles.HexNumber); + sb.Append("\\u" + t.Substring(t.Length - 4)); + } + else + { + sb.Append(c); + } + } + } + sb.Append('"'); + return sb.ToString(); + } + } +} diff --git a/libsecondlife-cs/JSONlib/Nii.JSON.csproj b/libsecondlife-cs/JSONlib/Nii.JSON.csproj new file mode 100644 index 00000000..8c9be8a8 --- /dev/null +++ b/libsecondlife-cs/JSONlib/Nii.JSON.csproj @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libsecondlife-cs/JSONlib/json.doc.xml b/libsecondlife-cs/JSONlib/json.doc.xml new file mode 100644 index 00000000..0d0006f1 --- /dev/null +++ b/libsecondlife-cs/JSONlib/json.doc.xml @@ -0,0 +1,878 @@ + + + + JSON + + + + + + A JSONArray is an ordered sequence of values. Its external form is a string + wrapped in square brackets with commas between the values. The internal form + is an object having get() and opt() methods for accessing the values by + index, and put() methods for adding or replacing values. The values can be + any of these types: Boolean, JSONArray, JSONObject, Number, String, or the + JSONObject.NULL object. + + + The constructor can convert a JSON external form string into an + internal form Java object. The toString() method creates an external + form string. + + + A get() method returns a value if one can be found, and throws an exception + if one cannot be found. An opt() method returns a default value instead of + throwing an exception, and so is useful for obtaining optional values. + + + The generic get() and opt() methods return an object which you can cast or + query for type. There are also typed get() and opt() methods that do typing + checking and type coersion for you. + + + The texts produced by the toString() methods are very strict. + The constructors are more forgiving in the texts they will accept. + + + + An extra comma may appear just before the closing bracket. + Strings may be quoted with single quotes. + Strings do not need to be quoted at all if they do not contain leading + or trailing spaces, and if they do not contain any of these characters: + { } [ ] / \ : , + Numbers may have the 0- (octal) or 0x- (hex) prefix. + + + + Public Domain 2002 JSON.org + @author JSON.org + @version 0.1 + + Ported to C# by Are Bjolseth, teleplan.no + TODO: + 1. Implement Custom exceptions + 2. Add indexer JSONObject[i] = object, and object = JSONObject[i]; + 3. Add indexer JSONObject["key"] = object, and object = JSONObject["key"] + 4. Add unit testing + 5. Add log4net + 6. Make get/put methods private, to force use of indexer instead? + + + + The ArrayList where the JSONArray's properties are kept. + + + + Construct an empty JSONArray + + + + + Construct a JSONArray from a JSONTokener. + + A JSONTokener + + + + Construct a JSONArray from a source string. + + A string that begins with '[' and ends with ']'. + + + + Construct a JSONArray from a Collection. + + A Collection. + + + + Get the object value associated with an index. + Use indexer instead!!! Added to be true to the original Java implementation + + index subscript. The index must be between 0 and length()-1 + An object value. + + + + Get the ArrayList which is holding the elements of the JSONArray. + Use the indexer instead!! Added to be true to the orignal Java src + + The ArrayList + + + + Get the boolean value associated with an index. + The string values "true" and "false" are converted to boolean. + + index subscript + The truth + + + + Get the double value associated with an index. + + index subscript + A double value + + + + Get the int value associated with an index. + + index subscript + The int value + + + + Get the JSONArray associated with an index. + + index subscript + A JSONArray value + + + + Get the JSONObject associated with an index. + + index subscript + A JSONObject value + + + + Get the string associated with an index. + + index subscript + A string value. + + + + Determine if the value is null. + + index subscript + true if the value at the index is null, or if there is no value. + + + + Make a string from the contents of this JSONArray. The separator string + is inserted between each element. + Warning: This method assumes that the data structure is acyclical. + + separator A string that will be inserted between the elements. + A string. + + + + Get the length of the JSONArray. + Added to be true to the original Java implementation + + Number of JSONObjects in array + + + + Get the optional object value associated with an index. + + index subscript + object at that index. + + + + Get the optional boolean value associated with an index. + + index subscript + The truth + + + + Get the optional boolean value associated with an index. + It returns the defaultValue if there is no value at that index or if it is not + a Boolean or the String "true" or "false". + + index subscript + + The truth. + + + + Get the optional double value associated with an index. + NaN is returned if the index is not found, + or if the value is not a number and cannot be converted to a number. + + index subscript + The double value object + + + + Get the optional double value associated with an index. + NaN is returned if the index is not found, + or if the value is not a number and cannot be converted to a number. + + index subscript + + The double value object + + + + Get the optional int value associated with an index. + Zero is returned if the index is not found, + or if the value is not a number and cannot be converted to a number. + + index subscript + The int value object + + + + Get the optional int value associated with an index. + The defaultValue is returned if the index is not found, + or if the value is not a number and cannot be converted to a number. + + index subscript + The default value + The int value object + + + + Get the optional JSONArray associated with an index. + + index subscript + A JSONArray value, or null if the index has no value, or if the value is not a JSONArray. + + + + Get the optional JSONObject associated with an index. + Null is returned if the key is not found, or null if the index has + no value, or if the value is not a JSONObject. + + index subscript + A JSONObject value + + + + Get the optional string value associated with an index. It returns an + empty string if there is no value at that index. If the value + is not a string and is not null, then it is coverted to a string. + + index subscript + A String value + + + + Get the optional string associated with an index. + The defaultValue is returned if the key is not found. + + index subscript + The default value + A string value + + + OMITTED: + public JSONArray put(bool val) + public JSONArray put(double val) + public JSONArray put(int val) + + Append an object value. + + An object value. The value should be a Boolean, Double, Integer, JSONArray, JSObject, or String, or the JSONObject.NULL object + this (JSONArray) + + + + Put or replace a boolean value in the JSONArray. + + + The subscript. If the index is greater than the length of + the JSONArray, then null elements will be added as necessary to pad it out. + + An object value. + this (JSONArray) + + + + Produce a JSONObject by combining a JSONArray of names with the values + of this JSONArray. + + + A JSONArray containing a list of key strings. These will be paired with the values. + + A JSONObject, or null if there are no names or if this JSONArray + + + + Make an JSON external form string of this JSONArray. For compactness, no + unnecessary whitespace is added. + + a printable, displayable, transmittable representation of the array. + + + + Alternate to Java get/put method, by using indexer + + + + + Alternativ to Java, getArrayList, by using propery + + + + + Get the length of the JSONArray. + Using a propery instead of method + + + + + Summary description for JsonFacade. + + + + + Parse a Hashtable and return a JSON formatted string + + + + + + + Parse JSON formatted string and return a Hashtable + + + + + + + + A JSONArray is an ordered sequence of values. Its external form is a string + wrapped in square brackets with commas between the values. The internal form + is an object having get() and opt() methods for accessing the values by + index, and put() methods for adding or replacing values. The values can be + any of these types: Boolean, JSONArray, JSONObject, Number, String, or the + JSONObject.NULL object. + + + The constructor can convert a JSON external form string into an + internal form Java object. The toString() method creates an external + form string. + + + A get() method returns a value if one can be found, and throws an exception + if one cannot be found. An opt() method returns a default value instead of + throwing an exception, and so is useful for obtaining optional values. + + + The generic get() and opt() methods return an object which you can cast or + query for type. There are also typed get() and opt() methods that do typing + checking and type coersion for you. + + + The texts produced by the toString() methods are very strict. + The constructors are more forgiving in the texts they will accept. + + + + An extra comma may appear just before the closing bracket. + Strings may be quoted with single quotes. + Strings do not need to be quoted at all if they do not contain leading + or trailing spaces, and if they do not contain any of these characters: + { } [ ] / \ : , + Numbers may have the 0- (octal) or 0x- (hex) prefix. + + + + Public Domain 2002 JSON.org + @author JSON.org + @version 0.1 + + Ported to C# by Are Bjolseth, teleplan.no + TODO: + 1. Implement Custom exceptions + 2. Add indexer JSONObject[i] = object, and object = JSONObject[i]; + 3. Add indexer JSONObject["key"] = object, and object = JSONObject["key"] + 4. Add unit testing + 5. Add log4net + 6. Make get/put methods private, to force use of indexer instead? + + + + The hash map where the JSONObject's properties are kept. + + + A shadow list of keys to enable access by sequence of insertion + + + + It is sometimes more convenient and less ambiguous to have a NULL + object than to use C#'s null value. + JSONObject.NULL.toString() returns "null". + + + + + Construct an empty JSONObject. + + + + + Construct a JSONObject from a JSONTokener. + + A JSONTokener object containing the source string. + + + + Construct a JSONObject from a string. + + A string beginning with '{' and ending with '}'. + + + + Construct a JSONObject from a IDictionary + + + + + + Accumulate values under a key. It is similar to the put method except + that if there is already an object stored under the key then a + JSONArray is stored under the key to hold all of the accumulated values. + If there is already a JSONArray, then the new value is appended to it. + In contrast, the put method replaces the previous value. + + A key string. + An object to be accumulated under the key. + this + + + + C# convenience method + + The Hashtable + + + + Alias to Java get method + Get the value object associated with a key. + + A key string. + The object associated with the key. + + + + Get the boolean value associated with a key. + + A key string. + The truth. + + + + Get the double value associated with a key. + + A key string. + The double value + + + + Get the int value associated with a key. + + A key string + The integer value. + + + + Get the JSONArray value associated with a key. + + A key string + A JSONArray which is the value + + + + Get the JSONObject value associated with a key. + + A key string. + A JSONObject which is the value. + + + + Get the string associated with a key. + + A key string. + A string which is the value. + + + + Determine if the JSONObject contains a specific key. + + A key string. + true if the key exists in the JSONObject. + + + + Get an enumeration of the keys of the JSONObject. + Added to be true to orginal Java implementation + Indexers are easier to use + + + + + + Determine if the value associated with the key is null or if there is no value. + + A key string + true if there is no value associated with the key or if the valus is the JSONObject.NULL object + + + + Get the number of keys stored in the JSONObject. + + The number of keys in the JSONObject. + + + + Produce a JSONArray containing the names of the elements of this JSONObject + + A JSONArray containing the key strings, or null if the JSONObject + + + + Produce a string from a number. + + Number value type object + String representation of the number + + + + Get an optional value associated with a key. + + A key string + An object which is the value, or null if there is no value. + + + + Get an optional value associated with a key. + It returns false if there is no such key, or if the value is not + Boolean.TRUE or the String "true". + + A key string. + bool value object + + + + Get an optional value associated with a key. + It returns false if there is no such key, or if the value is not + Boolean.TRUE or the String "true". + + A key string. + The preferred return value if conversion fails + bool value object + + + + Get an optional double associated with a key, + or NaN if there is no such key or if its value is not a number. + If the value is a string, an attempt will be made to evaluate it as + a number. + + A string which is the key. + A double value object + + + + Get an optional double associated with a key, + or NaN if there is no such key or if its value is not a number. + If the value is a string, an attempt will be made to evaluate it as + a number. + + A string which is the key. + The default + A double value object + + + + Get an optional double associated with a key, or the + defaultValue if there is no such key or if its value is not a number. + If the value is a string, an attempt will be made to evaluate it as + number. + + A key string. + An int object value + + + + Get an optional double associated with a key, or the + defaultValue if there is no such key or if its value is not a number. + If the value is a string, an attempt will be made to evaluate it as + number. + + A key string. + The default value + An int object value + + + + Get an optional JSONArray associated with a key. + It returns null if there is no such key, or if its value is not a JSONArray + + A key string + A JSONArray which is the value + + + + Get an optional JSONObject associated with a key. + It returns null if there is no such key, or if its value is not a JSONObject. + + A key string. + A JSONObject which is the value + + + + Get an optional string associated with a key. + It returns an empty string if there is no such key. If the value is not + a string and is not null, then it is coverted to a string. + + A key string. + A string which is the value. + + + + Get an optional string associated with a key. + It returns the defaultValue if there is no such key. + + A key string. + The default + A string which is the value. + + + + Put a key/value pair in the JSONObject. If the value is null, + then the key will be removed from the JSONObject if it is present. + + A key string. + + An object which is the value. It should be of one of these + types: Boolean, Double, Integer, JSONArray, JSONObject, String, or the + JSONObject.NULL object. + + JSONObject + + + + Add a key value pair + + + + + + + + Remove a object assosiateted with the given key + + + + + + + Append an array of JSONObjects to current object + + + + + + + Overridden to return a JSON formattet object as a string + + JSON object as formatted string + + + + Return the key for the associated index + + + + + Get/Add an object with the associated key + + + + + Return the number of JSON items in hashtable + + + + + Make a Null object + JSONObject.NULL is equivalent to the value that JavaScript calls null, + whilst C#'s null is equivalent to the value that JavaScript calls undefined. + + + + + Overriden to return "null" + + null + + + + + A JSONTokener takes a source string and extracts characters and tokens from + it. It is used by the JSONObject and JSONArray constructors to parse + JSON source strings. + + + Public Domain 2002 JSON.org + @author JSON.org + @version 0.1 + + Ported to C# by Are Bjolseth, teleplan.no + + + Implement Custom exceptions + Add unit testing + Add log4net + + + + + + The index of the next character. + + + The source string being tokenized. + + + + Construct a JSONTokener from a string. + + A source string. + + + + Back up one character. This provides a sort of lookahead capability, + so that you can test for a digit or letter before attempting to parse + the next number or identifier. + + + + + Get the hex value of a character (base16). + + + A character between '0' and '9' or between 'A' and 'F' or + between 'a' and 'f'. + + An int between 0 and 15, or -1 if c was not a hex digit. + + + + Determine if the source string still contains characters that next() can consume. + + true if not yet at the end of the source. + + + + Get the next character in the source string. + + The next character, or 0 if past the end of the source string. + + + + Consume the next character, and check that it matches a specified character + + The character to match. + The character. + + + + Get the next n characters. + + The number of characters to take. + A string of n characters. + + + + Get the next char in the string, skipping whitespace + and comments (slashslash and slashstar). + + A character, or 0 if there are no more characters. + + + + Return the characters up to the next close quote character. + Backslash processing is done. The formal JSON format does not + allow strings in single quotes, but an implementation is allowed to + accept them. + + The quoting character, either " or ' + A String. + + + + Get the text up but not including the specified character or the + end of line, whichever comes first. + + A delimiter character. + A string. + + + + Get the text up but not including one of the specified delimeter + characters or the end of line, which ever comes first. + + A set of delimiter characters. + A string, trimmed. + + + + Get the next value as object. The value can be a Boolean, Double, Integer, + JSONArray, JSONObject, or String, or the JSONObject.NULL object. + + An object. + + + + Skip characters until the next character is the requested character. + If the requested character is not found, no characters are skipped. + + A character to skip to. + + The requested character, or zero if the requested character is not found. + + + + + Skip characters until past the requested string. + If it is not found, we are left at the end of the source. + + A string to skip past. + + + + Make a printable string of this JSONTokener. + + " at character [myIndex] of [mySource]" + + + + Unescape the source text. Convert %hh sequences to single characters, + and convert plus to space. There are Web transport systems that insist on + doing unnecessary URL encoding. This provides a way to undo it. + + + + + Convert %hh sequences to single characters, and convert plus to space. + + A string that may contain plus and %hh sequences. + The unescaped string. + + + + Public Domain 2002 JSON.org + @author JSON.org + @version 0.1 + Ported to C# by Are Bjolseth, teleplan.no + + + + + Produce a string in double quotes with backslash sequences in all the right places. + + A String + A String correctly formatted for insertion in a JSON message. + + + diff --git a/libsecondlife-cs/NetworkManager.cs b/libsecondlife-cs/NetworkManager.cs index 54b4ff8f..8fb42bbb 100644 --- a/libsecondlife-cs/NetworkManager.cs +++ b/libsecondlife-cs/NetworkManager.cs @@ -33,6 +33,7 @@ using System.Net.Sockets; using System.Threading; using System.Security.Cryptography; using Nwc.XmlRpc; +using Nii.JSON; namespace libsecondlife { @@ -608,6 +609,7 @@ namespace libsecondlife public Hashtable UserCallbacks; public Hashtable InternalCallbacks; public Circuit CurrentCircuit; + public Hashtable LoginValues; private SecondLife Client; private ProtocolManager Protocol; @@ -623,6 +625,7 @@ namespace libsecondlife UserCallbacks = new Hashtable(); InternalCallbacks = new Hashtable(); CurrentCircuit = null; + LoginValues = null; // Register the internal callbacks InternalCallbacks["RegionHandshake"] = new PacketCallback(RegionHandshakeHandler); @@ -680,12 +683,12 @@ namespace libsecondlife return values; } - public bool Login(Hashtable loginParams, out Hashtable values) + public bool Login(Hashtable loginParams) { - return Login(loginParams, "https://login.agni.lindenlab.com/cgi-bin/login.cgi", out values); + return Login(loginParams, "https://login.agni.lindenlab.com/cgi-bin/login.cgi"); } - public bool Login(Hashtable loginParams, string url, out Hashtable values) + public bool Login(Hashtable loginParams, string url) { XmlRpcResponse result; XmlRpcRequest xmlrpc = new XmlRpcRequest(); @@ -701,7 +704,7 @@ namespace libsecondlife { Helpers.Log(e.ToString(), Helpers.LogLevel.Error); LoginError = e.Message; - values = null; + LoginValues = null; return false; } @@ -709,28 +712,74 @@ namespace libsecondlife { Helpers.Log("Fault " + result.FaultCode + ": " + result.FaultString, Helpers.LogLevel.Error); LoginError = "Fault " + result.FaultCode + ": " + result.FaultString; - values = null; + LoginValues = null; return false; } - values = (Hashtable)result.Value; + LoginValues = (Hashtable)result.Value; - if ((string)values["login"] == "false") + // Replace LLSD variables with object representations + System.Text.RegularExpressions.Regex LLSDtoJSON = + new System.Text.RegularExpressions.Regex(@"('|r([0-9]))"); + + // Convert LLSD string to JSON + string json = "{vector:" + LLSDtoJSON.Replace((string)LoginValues["look_at"], "$2") + "}"; + + // Convert JSON string to a JSON object + IDictionary jsonObject = JsonFacade.fromJSON(json); + JSONArray jsonVector = (JSONArray)jsonObject["vector"]; + + // Convert the JSON object to an LLVector3d + LLVector3d vector = new LLVector3d((double)jsonVector[0], (double)jsonVector[1], + (double)jsonVector[2]); + + LoginValues["look_at"] = vector; + + // Convert LLSD string to JSON + json = LLSDtoJSON.Replace((string)LoginValues["home"], "$2"); + + // Convert JSON string to an object + jsonObject = JsonFacade.fromJSON(json); + + // Create the position vector + JSONArray array = (JSONArray)jsonObject["position"]; + LLVector3d posVector = new LLVector3d((double)array[0], (double)array[1], (double)array[2]); + + // Create the look_at vector + array = (JSONArray)jsonObject["look_at"]; + LLVector3d lookatVector = new LLVector3d((double)array[0], (double)array[1], (double)array[2]); + + // Create the regionhandle U64 + array = (JSONArray)jsonObject["region_handle"]; + U64 regionhandle = new U64((int)array[0], (int)array[1]); + + // Create a hashtable to hold the home values + Hashtable home = new Hashtable(); + home["position"] = posVector; + home["look_at"] = lookatVector; + home["region_handle"] = regionhandle; + + LoginValues["home"] = home; + + if ((string)LoginValues["login"] == "false") { - LoginError = values["reason"] + ": " + values["message"]; + LoginError = LoginValues["reason"] + ": " + LoginValues["message"]; return false; } - AgentID = new LLUUID((string)values["agent_id"]); - SessionID = new LLUUID((string)values["session_id"]); - Client.Avatar.ID = new LLUUID((string)values["agent_id"]); - Client.Avatar.FirstName = (string)values["first_name"]; - Client.Avatar.LastName = (string)values["last_name"]; - uint circuitCode = (uint)(int)values["circuit_code"]; + AgentID = new LLUUID((string)LoginValues["agent_id"]); + SessionID = new LLUUID((string)LoginValues["session_id"]); + Client.Avatar.ID = new LLUUID((string)LoginValues["agent_id"]); + Client.Avatar.FirstName = (string)LoginValues["first_name"]; + Client.Avatar.LastName = (string)LoginValues["last_name"]; + Client.Avatar.LookAt = vector; + Client.Avatar.HomePosition = posVector; + Client.Avatar.HomeLookAt = lookatVector; + uint circuitCode = (uint)(int)LoginValues["circuit_code"]; // Connect to the sim given in the login reply Circuit circuit = new Circuit(Protocol, this, circuitCode); - if (!circuit.Open((string)values["sim_ip"], (int)values["sim_port"])) + if (!circuit.Open((string)LoginValues["sim_ip"], (int)LoginValues["sim_port"])) { return false; } diff --git a/libsecondlife-cs/Packet.cs b/libsecondlife-cs/Packet.cs index d41618b6..324aee11 100644 --- a/libsecondlife-cs/Packet.cs +++ b/libsecondlife-cs/Packet.cs @@ -1059,7 +1059,7 @@ namespace libsecondlife return packet; } - public static Packet ChatOut(ProtocolManager protocol, LLUUID myAgentID, LLUUID mySessionID, string message, + public static Packet Chat(ProtocolManager protocol, LLUUID myAgentID, LLUUID mySessionID, string message, byte type, int channel, byte command, LLUUID commandID, float radius, LLVector3 position) { Hashtable blocks = new Hashtable(); diff --git a/libsecondlife-cs/Types.cs b/libsecondlife-cs/Types.cs index bdebc787..735a243f 100644 --- a/libsecondlife-cs/Types.cs +++ b/libsecondlife-cs/Types.cs @@ -43,8 +43,17 @@ namespace libsecondlife public U64(uint left, uint right) { Data = new uint[2]; - Data[0] = right; //left; - Data[1] = left; //right; + // Backwards... don't ask me, it works + Data[0] = right; + Data[1] = left; + } + + public U64(int left, int right) + { + Data = new uint[2]; + // Backwards... don't ask me, it works + Data[0] = (uint)right; + Data[1] = (uint)left; } public U64(byte[] byteArray, int pos) @@ -368,6 +377,13 @@ namespace libsecondlife X = Y = Z = 0.0D; } + public LLVector3d(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + public LLVector3d(byte[] byteArray, int pos) { X = BitConverter.ToDouble(byteArray, pos); diff --git a/libsecondlife-cs/examples/name2key/name2key.cs b/libsecondlife-cs/examples/name2key/name2key.cs index 0b2b5ece..4923ebd3 100644 --- a/libsecondlife-cs/examples/name2key/name2key.cs +++ b/libsecondlife-cs/examples/name2key/name2key.cs @@ -97,9 +97,8 @@ namespace name2key Hashtable loginParams = NetworkManager.DefaultLoginValues(args[0], args[1], args[2], "00:00:00:00:00:00", "last", 1, 10, 10, 10, "Win", "0", "name2key", "jhurliman@wsu.edu"); - Hashtable loginReply = new Hashtable(); - if (!client.Network.Login(loginParams, out loginReply)) + if (!client.Network.Login(loginParams)) { // Login failed Console.WriteLine("ERROR: " + client.Network.LoginError); diff --git a/libsecondlife-cs/examples/name2key/name2key.csproj b/libsecondlife-cs/examples/name2key/name2key.csproj index 4ac978bf..ac11480d 100644 --- a/libsecondlife-cs/examples/name2key/name2key.csproj +++ b/libsecondlife-cs/examples/name2key/name2key.csproj @@ -83,6 +83,7 @@ Name = "libsecondlife" Project = "{D9CDEDFB-8169-4B03-B57F-0DF638F044EC}" Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + Private = "False" /> diff --git a/libsecondlife-cs/examples/slaccountant/frmSLAccountant.cs b/libsecondlife-cs/examples/slaccountant/frmSLAccountant.cs index 995440bc..374695de 100644 --- a/libsecondlife-cs/examples/slaccountant/frmSLAccountant.cs +++ b/libsecondlife-cs/examples/slaccountant/frmSLAccountant.cs @@ -473,11 +473,11 @@ namespace SLAccountant Hashtable loginParams = NetworkManager.DefaultLoginValues(txtFirstName.Text, txtLastName.Text, txtPassword.Text, "00:00:00:00:00:00", "last", 1, 10, 10, 0, "Win", "0", "accountant", "jhurliman@wsu.edu"); - Hashtable loginValues = new Hashtable(); - if (client.Network.Login(loginParams, out loginValues)) + if (client.Network.Login(loginParams)) { - lblName.Text = loginValues["first_name"] + " " + loginValues["last_name"]; + lblName.Text = client.Network.LoginValues["first_name"] + " " + + client.Network.LoginValues["last_name"]; // AgentHeightWidth Hashtable blocks = new Hashtable(); diff --git a/libsecondlife-cs/examples/slaccountant/slaccountant.csproj b/libsecondlife-cs/examples/slaccountant/slaccountant.csproj index b9f5e3b3..f201319d 100644 --- a/libsecondlife-cs/examples/slaccountant/slaccountant.csproj +++ b/libsecondlife-cs/examples/slaccountant/slaccountant.csproj @@ -93,6 +93,7 @@ Name = "libsecondlife" Project = "{D9CDEDFB-8169-4B03-B57F-0DF638F044EC}" Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + Private = "False" /> diff --git a/libsecondlife-cs/examples/sldump/sldump.cs b/libsecondlife-cs/examples/sldump/sldump.cs index 045ea39e..516f4976 100644 --- a/libsecondlife-cs/examples/sldump/sldump.cs +++ b/libsecondlife-cs/examples/sldump/sldump.cs @@ -168,9 +168,7 @@ namespace sldump optionsArray.Add("inventory-skeleton"); loginParams["options"] = optionsArray; - Hashtable loginReply = new Hashtable(); - - if (!client.Network.Login(loginParams, out loginReply)) + if (!client.Network.Login(loginParams)) { // Login failed Console.WriteLine("Error logging in: " + client.Network.LoginError); @@ -178,7 +176,7 @@ namespace sldump } // Login was successful - Console.WriteLine("Message of the day: " + loginReply["message"]); + Console.WriteLine("Message of the day: " + client.Network.LoginValues["message"]); while (true) { diff --git a/libsecondlife-cs/examples/sldump/sldump.csproj b/libsecondlife-cs/examples/sldump/sldump.csproj index 4b5c775e..02b5700f 100644 --- a/libsecondlife-cs/examples/sldump/sldump.csproj +++ b/libsecondlife-cs/examples/sldump/sldump.csproj @@ -83,6 +83,7 @@ Name = "libsecondlife" Project = "{D9CDEDFB-8169-4B03-B57F-0DF638F044EC}" Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + Private = "False" /> diff --git a/libsecondlife-cs/libsecondlife.csproj b/libsecondlife-cs/libsecondlife.csproj index 923d31e7..1a1dffcb 100644 --- a/libsecondlife-cs/libsecondlife.csproj +++ b/libsecondlife-cs/libsecondlife.csproj @@ -84,6 +84,11 @@ Project = "{410F8877-F1E1-4696-ABC7-4339189EECC3}" Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" /> + diff --git a/libsecondlife-cs/libsecondlife.sln b/libsecondlife-cs/libsecondlife.sln index 22a8ad2a..44408737 100644 --- a/libsecondlife-cs/libsecondlife.sln +++ b/libsecondlife-cs/libsecondlife.sln @@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlRpcCS", "XmlRpcCS\XmlRpc ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nii.JSON", "JSONlib\Nii.JSON.csproj", "{C40EB7FD-F957-4659-A184-A1C28908D748}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "slaccountant", "examples\slaccountant\slaccountant.csproj", "{FC19D5F6-076E-4923-8456-9B0E00E22896}" ProjectSection(ProjectDependencies) = postProject EndProjectSection @@ -41,6 +45,10 @@ Global {410F8877-F1E1-4696-ABC7-4339189EECC3}.Debug.Build.0 = Debug|.NET {410F8877-F1E1-4696-ABC7-4339189EECC3}.Release.ActiveCfg = Release|.NET {410F8877-F1E1-4696-ABC7-4339189EECC3}.Release.Build.0 = Release|.NET + {C40EB7FD-F957-4659-A184-A1C28908D748}.Debug.ActiveCfg = Debug|.NET + {C40EB7FD-F957-4659-A184-A1C28908D748}.Debug.Build.0 = Debug|.NET + {C40EB7FD-F957-4659-A184-A1C28908D748}.Release.ActiveCfg = Release|.NET + {C40EB7FD-F957-4659-A184-A1C28908D748}.Release.Build.0 = Release|.NET {FC19D5F6-076E-4923-8456-9B0E00E22896}.Debug.ActiveCfg = Debug|.NET {FC19D5F6-076E-4923-8456-9B0E00E22896}.Debug.Build.0 = Debug|.NET {FC19D5F6-076E-4923-8456-9B0E00E22896}.Release.ActiveCfg = Release|.NET