From ed6b88a0170cbb780f14d1173554eb8bfb8ea206 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 30 Nov 2006 03:35:36 +0000 Subject: [PATCH] libsecondlife: * Added a SerializableDictionary class, implements a generic Dictionary<,> that can be serialized to xml * Added all possible values for ObjectFlags to allow serializing * Added a few initial xml serialization attributes to prim-related classes and structures * Created ToXml() and FromXml() functions for PrimObject TestClient: * ExportCommand exports a single prim to an xml file * quit and login commands now work when no avatars are logged in git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@662 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/Avatar.cs | 13 +- libsecondlife-cs/ObjectManager.cs | 12 +- libsecondlife-cs/ParticleSystem.cs | 89 ++++--- libsecondlife-cs/Prims.cs | 210 ++++++++++------- libsecondlife-cs/SerializableDictionary.cs | 101 ++++++++ libsecondlife-cs/Textures.cs | 220 ++++++++++++++++-- libsecondlife-cs/Types.cs | 6 + .../TestClient/Commands/ExportCommand.cs | 63 +++++ .../examples/TestClient/TestClient.cs | 62 +++-- .../examples/TestClient/TestClient.csproj | 1 + libsecondlife-cs/libsecondlife.csproj | 1 + 11 files changed, 621 insertions(+), 157 deletions(-) create mode 100644 libsecondlife-cs/SerializableDictionary.cs create mode 100644 libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs diff --git a/libsecondlife-cs/Avatar.cs b/libsecondlife-cs/Avatar.cs index e4880499..25f6dba2 100644 --- a/libsecondlife-cs/Avatar.cs +++ b/libsecondlife-cs/Avatar.cs @@ -78,21 +78,32 @@ namespace libsecondlife public delegate void TeleportCallback(string message, TeleportStatus status); /// - /// Name Conversion for Teleport Status flag/bit + /// Current teleport status /// public enum TeleportStatus { + /// None, + /// Start, + /// Progress, + /// Failed, + /// Finished } + /// + /// Special commands used in Instant Messages + /// public enum InstantMessageDialog { + /// RequestTeleport = 22, + /// AcceptTeleport = 23, + /// DenyTeleport = 24 } diff --git a/libsecondlife-cs/ObjectManager.cs b/libsecondlife-cs/ObjectManager.cs index ee7b13b9..1c17bf61 100644 --- a/libsecondlife-cs/ObjectManager.cs +++ b/libsecondlife-cs/ObjectManager.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; +using System.Xml.Serialization; using System.Text; using libsecondlife.Packets; @@ -151,19 +152,26 @@ namespace libsecondlife /// /// /// + [Serializable] public enum PCode { /// + [XmlEnum("Prim")] Prim = 9, /// + [XmlEnum("Avatar")] Avatar = 47, /// + [XmlEnum("Grass")] Grass = 95, /// + [XmlEnum("NewTree")] NewTree = 111, /// + [XmlEnum("ParticleSystem")] ParticleSystem = 143, /// + [XmlEnum("Tree")] Tree = 255 } @@ -774,7 +782,7 @@ namespace libsecondlife string name = Helpers.FieldToString(block.NameValue); // New prim spotted - PrimObject prim = new PrimObject(Client); + PrimObject prim = new PrimObject(); prim.Name = name; @@ -1035,7 +1043,7 @@ namespace libsecondlife foreach (ObjectUpdateCompressedPacket.ObjectDataBlock block in update.ObjectData) { int i = 0; - prim = new PrimObject(Client); + prim = new PrimObject(); prim.Flags = (ObjectFlags)block.UpdateFlags; diff --git a/libsecondlife-cs/ParticleSystem.cs b/libsecondlife-cs/ParticleSystem.cs index 3cb67f13..567c557a 100644 --- a/libsecondlife-cs/ParticleSystem.cs +++ b/libsecondlife-cs/ParticleSystem.cs @@ -1,37 +1,45 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Xml.Serialization; namespace libsecondlife { /// /// /// + [Serializable] public class ParticleSystem { /// /// /// + [Serializable] public enum SourcePattern : byte { /// + [XmlEnum("None")] None = 0, /// + [XmlEnum("Drop")] Drop = 0x01, /// + [XmlEnum("Explode")] Explode = 0x02, /// + [XmlEnum("Angle")] Angle = 0x04, /// + [XmlEnum("AngleCone")] AngleCone = 0x08, /// + [XmlEnum("AngleConeEmpty")] AngleConeEmpty = 0x10 } /// /// /// - [Flags] + [Flags, Serializable] public enum ParticleFlags : ushort { /// @@ -57,48 +65,63 @@ namespace libsecondlife } /// + [XmlAttribute] public uint PartStartRGBA; /// + [XmlAttribute] public uint PartEndRGBA; /// + [XmlAttribute] + public float PartMaxAge; + /// + [XmlAttribute] + public float SrcMaxAge; + /// + [XmlAttribute] + public float SrcAngleBegin; + /// + [XmlAttribute] + public float SrcAngleEnd; + /// + [XmlAttribute] + public int SrcBurstPartCount; + /// + [XmlAttribute] + public float SrcBurstRadius; + /// + [XmlAttribute] + public float SrcBurstRate; + /// + [XmlAttribute] + public float SrcBurstSpeedMin; + /// + [XmlAttribute] + public float SrcBurstSpeedMax; + /// Unknown + [XmlAttribute] + public uint Version; + /// Unknown + [XmlAttribute] + public uint StartTick; + /// + [XmlAttribute] + public SourcePattern SrcPattern; + /// Various options that describe the behavior of this system + [XmlAttribute] + public ParticleFlags PartFlags; + /// public LLVector3 PartStartScale = LLVector3.Zero; /// public LLVector3 PartEndScale = LLVector3.Zero; /// - public float PartMaxAge; - /// - public float SrcMaxAge; - /// public LLVector3 SrcAccel = LLVector3.Zero; /// - public float SrcAngleBegin; - /// - public float SrcAngleEnd; - /// - public int SrcBurstPartCount; - /// - public float SrcBurstRadius; - /// - public float SrcBurstRate; - /// - public float SrcBurstSpeedMin; - /// - public float SrcBurstSpeedMax; - /// public LLVector3 SrcOmega = LLVector3.Zero; /// public LLUUID SrcTargetKey = LLUUID.Zero; /// Texture that will be applied to the particles public LLUUID SrcTexture = LLUUID.Zero; - /// - public SourcePattern SrcPattern; - /// Various options that describe the behavior of this system - public ParticleFlags PartFlags; - /// Unknown - public uint Version; - /// Unknown - public uint StartTick; - + /// /// /// @@ -116,6 +139,10 @@ namespace libsecondlife FromBytes(data, pos); } + /// + /// + /// + /// public byte[] GetBytes() { byte[] bytes = new byte[0]; @@ -145,7 +172,7 @@ namespace libsecondlife SrcMaxAge = (data[i++] + (data[i++] << 8)) / 256.0f; - //Unknown + // TODO: Unknown i += 2; SrcAngleBegin = (data[i++] / 100.0f) * (float)Math.PI; @@ -176,7 +203,7 @@ namespace libsecondlife PartMaxAge = (data[i++] + (data[i++] << 8)) / 256.0f; - //Unknown + // TODO: Unknown i += 2; PartStartRGBA = (uint)(data[i++] + (data[i++] << 8) + diff --git a/libsecondlife-cs/Prims.cs b/libsecondlife-cs/Prims.cs index fd2ff007..5aaf3645 100644 --- a/libsecondlife-cs/Prims.cs +++ b/libsecondlife-cs/Prims.cs @@ -26,13 +26,14 @@ using System; using System.Xml; +using System.Xml.Serialization; namespace libsecondlife { /// /// Primary parameters for primitives such as Physics Enabled or Phantom /// - [Flags] + [Flags, Serializable] public enum ObjectFlags { /// None of the primary flags are enabled @@ -41,16 +42,41 @@ namespace libsecondlife Physics = 1 << 0, /// CreateSelected = 1 << 1, - /// + Unknown1 = 1 << 2, + Unknown2 = 1 << 3, + Unknown3 = 1 << 4, + Unknown4 = 1 << 5, Script = 1 << 6, /// Whether this object contains an active touch script Touch = 1 << 7, + Unknown5 = 1 << 8, /// Whether this object can receive payments Money = 1 << 9, /// Whether this object is phantom (no collisions) Phantom = 1 << 10, + Unknown6 = 1 << 11, + Unknown7 = 1 << 12, + Unknown8 = 1 << 13, + Unknown9 = 1 << 14, + Unknown10 = 1 << 15, + Unknown11 = 1 << 16, + Unknown12 = 1 << 17, + Unknown13 = 1 << 18, + Unknown14 = 1 << 19, + Unknown15 = 1 << 20, + Unknown16 = 1 << 21, + Unknown17 = 1 << 22, + Unknown18 = 1 << 23, + Unknown19 = 1 << 24, + Unknown20 = 1 << 25, + Unknown21 = 1 << 26, + Unknown22 = 1 << 27, + Unknown23 = 1 << 28, + Unknown24 = 1 << 29, /// Whether this object is temporary - Temp = 1 << 30 + Temp = 1 << 30, + Unknown25 = 1 << 31, + Unknown26 = 1 << 32 } /// @@ -58,132 +84,134 @@ namespace libsecondlife /// been added after the original ObjectFlags that has all eight bits /// reserved already /// + [Serializable] public enum ExtraParamType : ushort { /// Whether this object has flexible parameters + [XmlEnum("Flexible")] Flexible = 0x10, /// Whether this object has light parameters + [XmlEnum("Light")] Light = 0x20 } /// /// /// - public class PrimObject + [Serializable] + public class PrimObject { /// + [XmlAttribute] public int PathTwistBegin = 0; /// + [XmlAttribute] public float PathEnd = 0; /// - public float ProfileBegin = 0; + [XmlAttribute] + public float ProfileBegin = 0; /// - public float PathRadiusOffset = 0; + [XmlAttribute] + public float PathRadiusOffset = 0; /// - public float PathSkew = 0; + [XmlAttribute] + public float PathSkew = 0; /// - public LLVector3 Position = LLVector3.Zero; - /// - public ulong RegionHandle; + [XmlAttribute] + public uint ProfileCurve = 0; /// - public uint ProfileCurve = 0; + [XmlAttribute] + public float PathScaleX = 0; /// - public float PathScaleX = 0; + [XmlAttribute] + public float PathScaleY = 0; /// - public float PathScaleY = 0; + [XmlAttribute] + public uint LocalID = 0; + /// + [XmlAttribute] + public uint ParentID = 0; + /// + [XmlAttribute] + public uint Material = 0; + /// + [XmlAttribute] + public string Name = ""; + /// + [XmlAttribute] + public string Description = ""; + /// + [XmlAttribute] + public float PathShearX = 0; + /// + [XmlAttribute] + public float PathShearY = 0; + /// + [XmlAttribute] + public float PathTaperX = 0; + /// + [XmlAttribute] + public float PathTaperY = 0; + /// + [XmlAttribute] + public float ProfileEnd = 0; + /// + [XmlAttribute] + public float PathBegin = 0; + /// + [XmlAttribute] + public uint PathCurve = 0; + /// + [XmlAttribute] + public int PathTwist = 0; + /// + [XmlAttribute] + public uint ProfileHollow = 0; + /// + [XmlAttribute] + public float PathRevolutions = 0; + /// + [XmlAttribute] + public uint State; + /// + [XmlAttribute] + public string Text; + /// + [XmlAttribute] + public ObjectManager.PCode PCode = ObjectManager.PCode.Prim; + /// + [XmlAttribute] + public ulong RegionHandle; /// public LLUUID ID = LLUUID.Zero; - /// - public uint LocalID = 0; - /// - public uint ParentID = 0; /// public LLUUID GroupID = LLUUID.Zero; /// - public uint Material = 0; - /// - public string Name = ""; - /// - public string Description = ""; - /// - public float PathShearX = 0; - /// - public float PathShearY = 0; - /// - public float PathTaperX = 0; - /// - public float PathTaperY = 0; - /// - public float ProfileEnd = 0; - /// - public float PathBegin = 0; - /// - public uint PathCurve = 0; + public LLVector3 Position = LLVector3.Zero; /// public LLVector3 Scale = LLVector3.Zero; - /// - public int PathTwist = 0; - /// - public uint ProfileHollow = 0; - /// - public float PathRevolutions = 0; /// public LLQuaternion Rotation = LLQuaternion.Identity; /// - public uint State; - /// - public string Text; - /// - public ObjectManager.PCode PCode; - /// public ObjectFlags Flags; /// - public TextureEntry Textures; + public TextureEntry Textures = new TextureEntry(); /// - public TextureAnimation TextureAnim; + public TextureAnimation TextureAnim = new TextureAnimation(); /// - public PrimFlexibleData Flexible; + public PrimFlexibleData Flexible = new PrimFlexibleData(); /// - public PrimLightData Light; + public PrimLightData Light = new PrimLightData(); /// - public ParticleSystem ParticleSys; - - private SecondLife Client; + public ParticleSystem ParticleSys = new ParticleSystem(); /// - /// + /// Default constructor /// - public PrimObject(SecondLife client) + public PrimObject() { - Client = client; - PCode = ObjectManager.PCode.Prim; - Flexible = new PrimFlexibleData(); - Light = new PrimLightData(); - ParticleSys = new ParticleSystem(); - Textures = new TextureEntry(); - TextureAnim = new TextureAnimation(); - Flexible = new PrimFlexibleData(); - Light = new PrimLightData(); - ParticleSys = new ParticleSystem(); } - /// - /// - /// - /// - public PrimObject(SecondLife client, LLUUID texture) - { - Client = client; - PCode = ObjectManager.PCode.Prim; - Textures = new TextureEntry(); - Textures.DefaultTexture.TextureID = texture; - } - - public GridRegion GridRegion - { - get { return Client.Grid.RegionsByHandle[RegionHandle]; } - } - /// /// /// @@ -470,11 +498,24 @@ namespace libsecondlife return output; } + + public void ToXml(XmlWriter xmlWriter) + { + XmlSerializer serializer = new XmlSerializer(typeof(PrimObject)); + serializer.Serialize(xmlWriter, this); + } + + public static PrimObject FromXml(XmlReader xmlReader) + { + XmlSerializer serializer = new XmlSerializer(typeof(PrimObject)); + return (PrimObject)serializer.Deserialize(xmlReader); + } } /// /// /// + [Serializable] public class PrimFlexibleData { /// @@ -546,6 +587,7 @@ namespace libsecondlife /// /// /// + [Serializable] public class PrimLightData { /// diff --git a/libsecondlife-cs/SerializableDictionary.cs b/libsecondlife-cs/SerializableDictionary.cs new file mode 100644 index 00000000..07371eeb --- /dev/null +++ b/libsecondlife-cs/SerializableDictionary.cs @@ -0,0 +1,101 @@ +/* + * Adapted from code by Paul Welter at + * http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the Second Life Reverse Engineering Team nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml.Serialization; + +/// +/// +/// +/// +/// +[XmlRoot("dictionary")] +public class SerializableDictionary : Dictionary, IXmlSerializable +{ + #region IXmlSerializable Members + + public System.Xml.Schema.XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(System.Xml.XmlReader reader) + { + XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); + XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); + + bool wasEmpty = reader.IsEmptyElement; + reader.Read(); + + if (wasEmpty) + return; + + while (reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + reader.ReadStartElement("item"); + + reader.ReadStartElement("key"); + TKey key = (TKey)keySerializer.Deserialize(reader); + reader.ReadEndElement(); + + reader.ReadStartElement("value"); + TValue value = (TValue)valueSerializer.Deserialize(reader); + reader.ReadEndElement(); + + this.Add(key, value); + + reader.ReadEndElement(); + reader.MoveToContent(); + } + + reader.ReadEndElement(); + } + + public void WriteXml(System.Xml.XmlWriter writer) + { + XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); + XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); + + foreach (TKey key in this.Keys) + { + writer.WriteStartElement("item"); + + writer.WriteStartElement("key"); + keySerializer.Serialize(writer, key); + writer.WriteEndElement(); + + writer.WriteStartElement("value"); + TValue value = this[key]; + valueSerializer.Serialize(writer, value); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + } + + #endregion +} diff --git a/libsecondlife-cs/Textures.cs b/libsecondlife-cs/Textures.cs index 1a62f9ee..9982a836 100644 --- a/libsecondlife-cs/Textures.cs +++ b/libsecondlife-cs/Textures.cs @@ -26,7 +26,7 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Xml.Serialization; using System.IO; namespace libsecondlife @@ -36,23 +36,59 @@ namespace libsecondlife /// public enum Bumpiness { + /// + [XmlEnum("None")] None = 0, + /// + [XmlEnum("Brightness")] Brightness = 1, + /// + [XmlEnum("Darkness")] Darkness = 2, + /// + [XmlEnum("Woodgrain")] Woodgrain = 3, + /// + [XmlEnum("Bark")] Bark = 4, + /// + [XmlEnum("Bricks")] Bricks = 5, + /// + [XmlEnum("Checker")] Checker = 6, + /// + [XmlEnum("Concrete")] Concrete = 7, + /// + [XmlEnum("Crustytile")] Crustytile = 8, + /// + [XmlEnum("Cutstone")] Cutstone = 9, + /// + [XmlEnum("Discs")] Discs = 10, + /// + [XmlEnum("Gravel")] Gravel = 11, + /// + [XmlEnum("Petridish")] Petridish = 12, + /// + [XmlEnum("Siding")] Siding = 13, + /// + [XmlEnum("Stonetile")] Stonetile = 14, + /// + [XmlEnum("Stucco")] Stucco = 15, + /// + [XmlEnum("Suction")] Suction = 16, + /// + [XmlEnum("Weave")] Weave = 17 } @@ -61,9 +97,17 @@ namespace libsecondlife /// public enum Shininess { + /// + [XmlEnum("None")] None = 0, + /// + [XmlEnum("Low")] Low = 0x40, + /// + [XmlEnum("Medium")] Medium = 0x80, + /// + [XmlEnum("High")] High = 0xC0 } @@ -72,33 +116,35 @@ namespace libsecondlife /// public enum Mapping { + /// + [XmlEnum("Default")] Default = 0, + /// + [XmlEnum("Planar")] Planar = 2 } /// /// /// + [Serializable] public class TextureEntry { /// - public TextureEntryFace DefaultTexture; + public TextureEntryFace DefaultTexture = null; /// - public Dictionary FaceTextures; + public SerializableDictionary FaceTextures = + new SerializableDictionary(); /// /// /// public TextureEntry() { - FaceTextures = new Dictionary(); - //DefaultTexture = new TextureEntryFace(null); - DefaultTexture = null; } public TextureEntry(LLUUID textureID) { - FaceTextures = new Dictionary(); DefaultTexture = new TextureEntryFace(null); DefaultTexture.TextureID = textureID; } @@ -367,7 +413,7 @@ namespace libsecondlife { short value = (short)(byteArray[pos] | (byteArray[pos + 1] << 8)); float QV = (float)value; - float QF = upper / 32767.0F; + float QF = upper / 32767.0f; return (float)(QV * QF); } @@ -379,37 +425,37 @@ namespace libsecondlife private short RepeatShort(float value) { - return QuantizeSigned(value - 1.0F, 101.0F); + return QuantizeSigned(value - 1.0f, 101.0f); } private short OffsetShort(float value) { - return QuantizeSigned(value, 1.0F); + return QuantizeSigned(value, 1.0f); } private short RotationShort(float value) { - return QuantizeSigned(value, 359.995F); + return QuantizeSigned(value, 359.995f); } private float RepeatFloat(byte[] data, int pos) { - return DequantizeSigned(data, pos, 101.0F) + 1.0F; + return DequantizeSigned(data, pos, 101.0f) + 1.0f; } private float OffsetFloat(byte[] data, int pos) { - return DequantizeSigned(data, pos, 1.0F); + return DequantizeSigned(data, pos, 1.0f); } private float RotationFloat(byte[] data, int pos) { - return DequantizeSigned(data, pos, 359.995F); + return DequantizeSigned(data, pos, 359.995f); } private void FromBytes(byte[] data, int pos, int length) { - FaceTextures = new Dictionary(); + FaceTextures = new SerializableDictionary(); DefaultTexture = new TextureEntryFace(null); if (length <= 0) @@ -542,6 +588,7 @@ namespace libsecondlife /// /// /// + [Serializable] public class TextureEntryFace { [Flags] @@ -560,6 +607,13 @@ namespace libsecondlife All = 0xFFFFFFFF } + /// + /// + /// + public TextureEntryFace() + { + } + /// /// /// @@ -757,10 +811,10 @@ namespace libsecondlife private byte _Flags2; } - /// /// /// + [Serializable] public class TextureAnimation { /// @@ -830,4 +884,138 @@ namespace libsecondlife Rate = BitConverter.ToSingle(data, i + 8); } } + + /// + /// A serializable dictionary of TextureEntryFace objects, indexed by + /// the prim face they are mapped to + /// + [Serializable] + public class Faces : System.Collections.DictionaryBase, System.Xml.Serialization.IXmlSerializable + { + private const string NS = "http://www.libsecondlife.org/"; + + /// + /// Default constructor + /// + public Faces() + { + } + + public virtual TextureEntryFace this[uint key] + { + get + { + return (TextureEntryFace)this.Dictionary[key]; + } + set + { + this.Dictionary[key] = value; + } + } + + public virtual void Add(uint key, TextureEntryFace value) + { + this.Dictionary.Add(key, value); + } + + public virtual bool Contains(uint key) + { + return this.Dictionary.Contains(key); + } + + public virtual bool ContainsKey(uint key) + { + return this.Dictionary.Contains(key); + } + + public virtual bool ContainsValue(TextureEntryFace value) + { + foreach (TextureEntryFace item in this.Dictionary.Values) + { + if (item == value) + return true; + } + return false; + } + + public virtual void Remove(uint key) + { + this.Dictionary.Remove(key); + } + + public virtual System.Collections.ICollection Keys + { + get + { + return this.Dictionary.Keys; + } + } + + public virtual System.Collections.ICollection Values + { + get + { + return this.Dictionary.Values; + } + } + + void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter w) + { + System.Xml.Serialization.XmlSerializer keySer = + new System.Xml.Serialization.XmlSerializer(typeof(uint)); + System.Xml.Serialization.XmlSerializer valueSer = + new System.Xml.Serialization.XmlSerializer(typeof(TextureEntryFace)); + w.WriteStartElement("dictionary", NS); + foreach (object key in Dictionary.Keys) + { + w.WriteStartElement("item", NS); + + w.WriteStartElement("key", NS); + keySer.Serialize(w, key); + w.WriteEndElement(); + + w.WriteStartElement("value", NS); + object value = Dictionary[key]; + valueSer.Serialize(w, value); + w.WriteEndElement(); + + w.WriteEndElement(); + } + w.WriteEndElement(); + } + + void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader r) + { + System.Xml.Serialization.XmlSerializer keySer = + new System.Xml.Serialization.XmlSerializer(typeof(string)); + System.Xml.Serialization.XmlSerializer valueSer = + new System.Xml.Serialization.XmlSerializer(typeof(TextureEntryFace)); + + r.Read(); + r.ReadStartElement("dictionary", NS); + while (r.NodeType != System.Xml.XmlNodeType.EndElement) + { + r.ReadStartElement("item", NS); + + r.ReadStartElement("key", NS); + object key = keySer.Deserialize(r); + r.ReadEndElement(); + + r.ReadStartElement("value", NS); + object value = valueSer.Deserialize(r); + r.ReadEndElement(); + + Dictionary.Add(key, value); + + r.ReadEndElement(); + r.MoveToContent(); + } + r.ReadEndElement(); + } + + System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() + { + return null; + } + } } diff --git a/libsecondlife-cs/Types.cs b/libsecondlife-cs/Types.cs index 8b6de1b2..ba4ffeb5 100644 --- a/libsecondlife-cs/Types.cs +++ b/libsecondlife-cs/Types.cs @@ -26,6 +26,7 @@ using System; using System.Net; +using System.Xml.Serialization; namespace libsecondlife { @@ -33,6 +34,7 @@ namespace libsecondlife /// A 128-bit Universally Unique Identifier, used throughout the Second /// Life networking protocol /// + [XmlRoot("uuid")] public class LLUUID { /// Get a byte array of the 16 raw bytes making up the UUID @@ -265,6 +267,7 @@ namespace libsecondlife /// /// /// + [XmlRoot("vector3")] public class LLVector3 { /// @@ -451,6 +454,7 @@ namespace libsecondlife /// /// /// + [XmlRoot("vector3d")] public class LLVector3d { /// @@ -538,6 +542,7 @@ namespace libsecondlife /// /// /// + [XmlRoot("vector4")] public class LLVector4 { /// @@ -618,6 +623,7 @@ namespace libsecondlife /// /// /// + [XmlRoot("quaternion")] public class LLQuaternion { /// diff --git a/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs new file mode 100644 index 00000000..fb0d4e06 --- /dev/null +++ b/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using libsecondlife; +using libsecondlife.Packets; + +namespace libsecondlife.TestClient +{ + public class ExportCommand : Command + { + public ExportCommand() + { + Name = "export"; + Description = "Exports an object to an xml file. Usage: export uuid outputfile.xml"; + } + + public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) + { + if (args.Length != 2) + return "Usage: export uuid outputfile.xml"; + + LLUUID id; + string file = args[1]; + + try + { + id = new LLUUID(args[0]); + } + catch (Exception) + { + return "Usage: export uuid outputfile.xml"; + } + + foreach (PrimObject prim in TestClient.Prims.Values) + { + if (prim.ID == id) + { + try + { + XmlWriter writer = XmlWriter.Create(file); + prim.ToXml(writer); + writer.Close(); + } + catch (Exception e) + { + string ret = "Failed to write to " + file + ":" + e.ToString(); + if (ret.Length > 1000) + { + ret = ret.Remove(1000); + } + return ret; + } + + return ""; + } + } + + return "Couldn't find UUID " + id.ToString() + " in the " + TestClient.Prims.Count + + "objects currently indexed"; + } + } +} diff --git a/libsecondlife-cs/examples/TestClient/TestClient.cs b/libsecondlife-cs/examples/TestClient/TestClient.cs index 5e2435e6..c88fe3df 100644 --- a/libsecondlife-cs/examples/TestClient/TestClient.cs +++ b/libsecondlife-cs/examples/TestClient/TestClient.cs @@ -186,33 +186,49 @@ Begin: if (Commands.ContainsKey(firstToken)) { - foreach (SecondLife client in Clients.Values) + if (firstToken == "login") { - if (client.Network.Connected) + // Special login case: Only call it once, and allow it with + // no logged in avatars + string[] args = new string[tokens.Length - 1]; + Array.Copy(tokens, 1, args, 0, args.Length); + string response = Commands["login"].Execute(null, args, null); + + if (response.Length > 0) { - string[] args = new string[tokens.Length - 1]; - Array.Copy(tokens, 1, args, 0, args.Length); - string response = Commands[firstToken].Execute(client, args, fromAgentID); - - if (response.Length > 0) - { - if (fromAgentID != null && client.Network.Connected) - client.Self.InstantMessage(fromAgentID, response, imSessionID); - Console.WriteLine(response); - } - - if (firstToken == "login") - { - // Special login case: Only call it once - break; - } + Console.WriteLine(response); } - - if (avatars != Clients.Count) + } + else if (firstToken == "quit") + { + // Special quit case: This allows us to quit even when there + // are zero avatars logged in + Commands["quit"].Execute(null, null, null); + } + else + { + foreach (SecondLife client in Clients.Values) { - // The dictionary size changed, start over since the - // foreach is shot - goto Begin; + if (client.Network.Connected) + { + string[] args = new string[tokens.Length - 1]; + Array.Copy(tokens, 1, args, 0, args.Length); + string response = Commands[firstToken].Execute(client, args, fromAgentID); + + if (response.Length > 0) + { + if (fromAgentID != null && client.Network.Connected) + client.Self.InstantMessage(fromAgentID, response, imSessionID); + Console.WriteLine(response); + } + } + + if (avatars != Clients.Count) + { + // The dictionary size changed, start over since the + // foreach is shot + goto Begin; + } } } } diff --git a/libsecondlife-cs/examples/TestClient/TestClient.csproj b/libsecondlife-cs/examples/TestClient/TestClient.csproj index fcefb433..e52af961 100644 --- a/libsecondlife-cs/examples/TestClient/TestClient.csproj +++ b/libsecondlife-cs/examples/TestClient/TestClient.csproj @@ -36,6 +36,7 @@ + diff --git a/libsecondlife-cs/libsecondlife.csproj b/libsecondlife-cs/libsecondlife.csproj index 79274c7e..0064e8e6 100644 --- a/libsecondlife-cs/libsecondlife.csproj +++ b/libsecondlife-cs/libsecondlife.csproj @@ -157,6 +157,7 @@ Code + Code