From 2a29dc70eec95d871b9f11cd3be016feccc76bb9 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 30 Jan 2007 12:47:47 +0000 Subject: [PATCH] * Part two of the major object restructuring * Fixed a bug with the LogoutTimer not dying * Major surgery on how objects are decoded off the wire, handles more cases now * Added support for lots more object and primitive parameters * Added several new Helpers functions for converting bytes to values git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@923 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/Helpers.cs | 102 ++- libsecondlife-cs/LLObject.cs | 178 ++--- libsecondlife-cs/MainAvatar.cs | 8 + libsecondlife-cs/NetworkManager.cs | 2 + libsecondlife-cs/ObjectManager.cs | 654 ++++++++++-------- libsecondlife-cs/Prims.cs | 50 +- .../TestClient/Commands/ImportCommand.cs | 6 +- .../examples/TestClient/TestClient.cs | 54 +- .../examples/primexport/frmPrimExport.cs | 38 +- .../libsecondlife.Utilities/Utilities.cs | 11 +- 10 files changed, 684 insertions(+), 419 deletions(-) diff --git a/libsecondlife-cs/Helpers.cs b/libsecondlife-cs/Helpers.cs index 0f06aada..90208f64 100644 --- a/libsecondlife-cs/Helpers.cs +++ b/libsecondlife-cs/Helpers.cs @@ -46,12 +46,10 @@ namespace libsecondlife public const byte MSG_RELIABLE = 0x40; /// This header flag signals that the message is compressed using zerocoding public const byte MSG_ZEROCODED = 0x80; - /// Used for converting a byte to a variable range float - public const float ONE_OVER_BYTEMAX = 1.0f / (float)byte.MaxValue; /// Used for converting degrees to radians - public const double DEG_TO_RAD = Math.PI / 180; + public const double DEG_TO_RAD = Math.PI / 180.0d; /// Used for converting radians to degrees - public const double RAD_TO_DEG = 180 / Math.PI; + public const double RAD_TO_DEG = 180.0d / Math.PI; /// /// Passed to SecondLife.Log() to identify the severity of a log entry @@ -162,6 +160,34 @@ namespace libsecondlife return bytes; } + /// + /// Convert the first two bytes starting at the given position in + /// little endian ordering to an unsigned short + /// + /// Byte array containing the ushort + /// Position to start reading the ushort from + /// An unsigned short, will be zero if a ushort can't be read + /// at the given position + public static ushort BytesToUInt16(byte[] bytes, int pos) + { + if (bytes.Length <= pos + 1) return 0; + return (ushort)(bytes[pos + 1] + (bytes[pos] << 8)); + } + + /// + /// Convert the first four bytes starting at the given position in + /// little endian ordering to an unsigned integer + /// + /// Byte array containing the uint + /// Position to start reading the uint from + /// An unsigned integer, will be zero if a uint can't be read + /// at the given position + public static uint BytesToUInt(byte[] bytes, int pos) + { + if (bytes.Length <= pos + 4) return 0; + return (uint)(bytes[pos + 3] + (bytes[pos + 2] << 8) + (bytes[pos + 1] << 16) + (bytes[pos] << 24)); + } + /// /// Convert the first four bytes of the given array in little endian /// ordering to an unsigned integer @@ -175,6 +201,20 @@ namespace libsecondlife return (uint)(bytes[3] + (bytes[2] << 8) + (bytes[1] << 16) + (bytes[0] << 24)); } + /// + /// Convert the first four bytes starting at the given position in + /// big endian ordering to an unsigned integer + /// + /// Byte array containing the uint + /// Position to start reading the uint from + /// An unsigned integer, will be zero if a uint can't be read + /// at the given position + public static uint BytesToUIntBig(byte[] bytes, int pos) + { + if (bytes.Length <= pos + 4) return 0; + return (uint)(bytes[pos] + (bytes[pos + 1] << 8) + (bytes[pos + 2] << 16) + (bytes[pos + 3] << 24)); + } + /// /// Convert the first four bytes of the given array in big endian /// ordering to an unsigned integer @@ -253,6 +293,20 @@ namespace libsecondlife return (byte)Math.Floor(val * (float)byte.MaxValue); } + /// + /// Convert a byte to a float value given a minimum and maximum range + /// + /// Byte array to get the byte from + /// Position in the byte array the desired byte is at + /// Minimum value range + /// Maximum value range + /// A float value inclusively between lower and upper + public static float ByteToFloat(byte[] bytes, int pos, float lower, float upper) + { + if (bytes.Length <= pos) return 0; + return ByteToFloat(bytes[pos], lower, upper); + } + /// /// Convert a byte to a float value given a minimum and maximum range /// @@ -262,6 +316,8 @@ namespace libsecondlife /// A float value inclusively between lower and upper public static float ByteToFloat(byte val, float lower, float upper) { + const float ONE_OVER_BYTEMAX = 1.0f / (float)byte.MaxValue; + float fval = (float)val * ONE_OVER_BYTEMAX; float delta = (upper - lower); fval *= delta; @@ -275,6 +331,44 @@ namespace libsecondlife return fval; } + /// + /// + /// + /// + /// + /// + /// + /// + public static float UInt16ToFloat(byte[] bytes, int pos, float lower, float upper) + { + ushort val = BytesToUInt16(bytes, pos); + return UInt16ToFloat(val, lower, upper); + } + + /// + /// + /// + /// + /// + /// + /// + public static float UInt16ToFloat(ushort val, float lower, float upper) + { + const float ONE_OVER_U16_MAX = 1.0f / 65535.0f; + + float fval = (float)val * ONE_OVER_U16_MAX; + float delta = upper - lower; + fval *= delta; + fval += lower; + + // Make sure zeroes come through as zero + float maxError = delta * ONE_OVER_U16_MAX; + if (Math.Abs(fval) < maxError) + fval = 0.0f; + + return fval; + } + /// /// Clamp a given value between a range /// diff --git a/libsecondlife-cs/LLObject.cs b/libsecondlife-cs/LLObject.cs index f3728bd9..11d1f812 100644 --- a/libsecondlife-cs/LLObject.cs +++ b/libsecondlife-cs/LLObject.cs @@ -111,6 +111,77 @@ namespace libsecondlife #region Structs + /// + /// + /// + [Serializable] + public struct ObjectData + { + /// + [XmlAttribute("pathtwistbegin")] + public int PathTwistBegin; + /// + [XmlAttribute("pathend")] + public float PathEnd; + /// + [XmlAttribute("profilebegin")] + public float ProfileBegin; + /// + [XmlAttribute("pathradiusoffset")] + public float PathRadiusOffset; + /// + [XmlAttribute("pathskew")] + public float PathSkew; + /// + [XmlAttribute("profilecurve")] + public uint ProfileCurve; + /// + [XmlAttribute("pathscalex")] + public float PathScaleX; + /// + [XmlAttribute("pathscaley")] + public float PathScaleY; + /// + [XmlAttribute("material")] + public uint Material; + /// + [XmlAttribute("pathshearx")] + public float PathShearX; + /// + [XmlAttribute("pathsheary")] + public float PathShearY; + /// + [XmlAttribute("pathtaperx")] + public float PathTaperX; + /// + [XmlAttribute("pathtapery")] + public float PathTaperY; + /// + [XmlAttribute("profileend")] + public float ProfileEnd; + /// + [XmlAttribute("pathbegin")] + public float PathBegin; + /// + [XmlAttribute("pathcurve")] + public uint PathCurve; + /// + [XmlAttribute("pathtwist")] + public int PathTwist; + /// + [XmlAttribute("profilehollow")] + public uint ProfileHollow; + /// + [XmlAttribute("pathrevolutions")] + public float PathRevolutions; + /// + [XmlAttribute("state")] + public uint State; + /// + [XmlIgnore] + public ObjectManager.PCode PCode; + } + /// /// /// @@ -151,104 +222,47 @@ namespace libsecondlife #endregion Structs - #region Public Properties + #region Public Members /// - [XmlAttribute("pathtwistbegin")] - public int PathTwistBegin; - /// - [XmlAttribute("pathend")] - public float PathEnd; - /// - [XmlAttribute("profilebegin")] - public float ProfileBegin; - /// - [XmlAttribute("pathradiusoffset")] - public float PathRadiusOffset; - /// - [XmlAttribute("pathskew")] - public float PathSkew; - /// - [XmlAttribute("profilecurve")] - public uint ProfileCurve; - /// - [XmlAttribute("pathscalex")] - public float PathScaleX; - /// - [XmlAttribute("pathscaley")] - public float PathScaleY; - /// - [XmlAttribute("localid")] - public uint LocalID; - /// - [XmlAttribute("parentid")] - public uint ParentID; - /// - [XmlAttribute("material")] - public uint Material; - /// - [XmlAttribute("pathshearx")] - public float PathShearX; - /// - [XmlAttribute("pathsheary")] - public float PathShearY; - /// - [XmlAttribute("pathtaperx")] - public float PathTaperX; - /// - [XmlAttribute("pathtapery")] - public float PathTaperY; - /// - [XmlAttribute("profileend")] - public float ProfileEnd; - /// - [XmlAttribute("pathbegin")] - public float PathBegin; - /// - [XmlAttribute("pathcurve")] - public uint PathCurve; - /// - [XmlAttribute("pathtwist")] - public int PathTwist; - /// - [XmlAttribute("profilehollow")] - public uint ProfileHollow; - /// - [XmlAttribute("pathrevolutions")] - public float PathRevolutions; - /// - [XmlAttribute("state")] - public uint State; - /// - [XmlAttribute("text")] - public string Text = String.Empty; - /// - [XmlAttribute("regionhandle")] - public ulong RegionHandle; - /// - [XmlAttribute("flags")] - public ObjectFlags Flags; - /// - [XmlIgnore] - public ObjectManager.PCode PCode = ObjectManager.PCode.Prim; - /// - [XmlElement("id")] public LLUUID ID = LLUUID.Zero; /// - [XmlElement("groupid")] public LLUUID GroupID = LLUUID.Zero; /// + public uint LocalID; + /// + public uint ParentID; + /// + public ulong RegionHandle; + /// + public ObjectFlags Flags; + /// Unknown + public byte[] GenericData; + /// public LLVector3 Position = LLVector3.Zero; /// public LLVector3 Scale = LLVector3.Zero; /// public LLQuaternion Rotation = LLQuaternion.Identity; + + public LLVector3 Velocity; + public LLVector3 AngularVelocity; + public LLVector3 Acceleration; + public LLVector4 CollisionPlane; + /// public TextureEntry Textures = new TextureEntry(); /// public ObjectPropertiesFamily PropertiesFamily; - #endregion Public Properties + #endregion Public Members + + + /// + public ObjectData Data { get { return data; } } + + + internal ObjectData data = new ObjectData(); #region Static Methods diff --git a/libsecondlife-cs/MainAvatar.cs b/libsecondlife-cs/MainAvatar.cs index 24bae795..8f02a45b 100644 --- a/libsecondlife-cs/MainAvatar.cs +++ b/libsecondlife-cs/MainAvatar.cs @@ -466,6 +466,14 @@ namespace libsecondlife public LLVector3 Position = LLVector3.Zero; /// Current rotation of avatar public LLQuaternion Rotation = LLQuaternion.Identity; + /// + public LLVector4 CollisionPlane = LLVector4.Zero; + /// + public LLVector3 Velocity = LLVector3.Zero; + /// + public LLVector3 Acceleration = LLVector3.Zero; + /// + public LLVector3 AngularVelocity = LLVector3.Zero; /// The point the avatar is currently looking at /// (may not stay updated) public LLVector3 LookAt = LLVector3.Zero; diff --git a/libsecondlife-cs/NetworkManager.cs b/libsecondlife-cs/NetworkManager.cs index f1f250c7..2c70e7c0 100644 --- a/libsecondlife-cs/NetworkManager.cs +++ b/libsecondlife-cs/NetworkManager.cs @@ -1565,6 +1565,7 @@ namespace libsecondlife CurrentSim.SendPacket(logout, true); LogoutTimer = new System.Timers.Timer(Client.Settings.LOGOUT_TIMEOUT); + LogoutTimer.AutoReset = false; LogoutTimer.Elapsed += new ElapsedEventHandler(LogoutTimer_Elapsed); LogoutTimer.Start(); } @@ -1731,6 +1732,7 @@ namespace libsecondlife /// private void LogoutTimer_Elapsed(object sender, ElapsedEventArgs ev) { + LogoutTimer.Stop(); Client.Log("Logout due to timeout on server acknowledgement", Helpers.LogLevel.Debug); ForceLogout(); } diff --git a/libsecondlife-cs/ObjectManager.cs b/libsecondlife-cs/ObjectManager.cs index fd7b72d5..c3f892b6 100644 --- a/libsecondlife-cs/ObjectManager.cs +++ b/libsecondlife-cs/ObjectManager.cs @@ -33,16 +33,20 @@ using libsecondlife.Packets; namespace libsecondlife { /// - /// Contains the shared variables sent in an object update packet for - /// objects. Used to track position and movement of prims and avatars + /// Contains the variables sent in an object update packet for objects. + /// Used to track position and movement of prims and avatars /// - public abstract class ObjectUpdate + public struct ObjectUpdate { /// - public uint LocalID; + public bool Avatar; + /// + public LLVector4 CollisionPlane; /// public byte State; /// + public uint LocalID; + /// public LLVector3 Position; /// public LLVector3 Velocity; @@ -51,29 +55,11 @@ namespace libsecondlife /// public LLQuaternion Rotation; /// - public LLVector3 RotationVelocity; + public LLVector3 AngularVelocity; /// public LLObject.TextureEntry Textures; } - /// - /// Contains all of the variables sent in an object update packet for a - /// primitive. Used to track position and movement of primitives - /// - public class PrimUpdate : ObjectUpdate - { - } - - /// - /// Contains all of the variables sent in an object update packet for an - /// avatar. Used to track position and movement of avatars - /// - public class AvatarUpdate : ObjectUpdate - { - /// - public LLVector4 CollisionPlane; - } - /// /// Handles all network traffic related to prims and avatar positions and /// movement. @@ -127,19 +113,10 @@ namespace libsecondlife /// /// /// - /// + /// /// /// - public delegate void PrimMovedCallback(Simulator simulator, PrimUpdate prim, ulong regionHandle, - ushort timeDilation); - /// - /// - /// - /// - /// - /// - /// - public delegate void AvatarMovedCallback(Simulator simulator, AvatarUpdate avatar, ulong regionHandle, + public delegate void ObjectUpdatedCallback(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation); /// /// @@ -256,7 +233,7 @@ namespace libsecondlife public enum CompressedFlags { /// Hasn't been spotted in the wild yet - Unknown1 = 0x01, + ScratchPad = 0x01, /// This may be incorrect Tree = 0x02, /// Whether the object has floating text ala llSetText @@ -267,12 +244,14 @@ namespace libsecondlife HasSound = 0x10, /// Whether the object is attached to a root object or not HasParent = 0x20, - /// Semi-common flag, currently unknown - Unknown2 = 0x40, + /// Unknown + Unknown = 0x40, /// Whether the object has an angular velocity HasAngularVelocity = 0x80, - /// Whether the object is an attachment or not - Attachment = 0x100 + /// Whether the object has a name value pairs string + HasNameValues = 0x100, + /// Whether the object has a Media URL set + MediaURL = 0x200 } /// @@ -390,17 +369,11 @@ namespace libsecondlife /// public event NewFoliageCallback OnNewFoliage; /// - /// This event will be raised when a prim movement packet is received, - /// containing the updated position, rotation, and movement-related - /// vectors. - /// - public event PrimMovedCallback OnPrimMoved; - /// - /// This event will be raised when an avatar movement packet is + /// This event will be raised when a terse object update packet is /// received, containing the updated position, rotation, and - /// movement-related vectors. + /// movement-related vectors /// - public event AvatarMovedCallback OnAvatarMoved; + public event ObjectUpdatedCallback OnObjectUpdated; /// /// This event will be raised when the main avatar sits on an /// object or stands up, with a local ID of the current seat or @@ -525,27 +498,31 @@ namespace libsecondlife /// Create, or "rez" a new prim object in a simulator /// /// The target simulator - /// The prim object to rez + /// Data describing the prim object to rez + /// Group ID that this prim is set to, or LLUUID.Zero /// An approximation of the position at which to rez the prim + /// Scale vector to size this prim + /// Rotation quaternion to rotate this prim /// Due to the way client prim rezzing is done on the server, /// the requested position for an object is only close to where the prim /// actually ends up. If you desire exact placement you'll need to /// follow up by moving the object after it has been created. - public void AddPrim(Simulator simulator, Primitive prim, LLVector3 position) + public void AddPrim(Simulator simulator, LLObject.ObjectData prim, LLUUID groupID, LLVector3 position, + LLVector3 scale, LLQuaternion rotation) { ObjectAddPacket packet = new ObjectAddPacket(); packet.AgentData.AgentID = Client.Network.AgentID; packet.AgentData.SessionID = Client.Network.SessionID; - packet.AgentData.GroupID = prim.GroupID; + packet.AgentData.GroupID = groupID; packet.ObjectData.State = (byte)prim.State; packet.ObjectData.AddFlags = (uint)LLObject.ObjectFlags.CreateSelected; packet.ObjectData.PCode = (byte)PCode.Prim; packet.ObjectData.Material = (byte)prim.Material; - packet.ObjectData.Scale = prim.Scale; - packet.ObjectData.Rotation = prim.Rotation; + packet.ObjectData.Scale = scale; + packet.ObjectData.Rotation = rotation; packet.ObjectData.PathBegin = LLObject.PathBeginByte(prim.PathBegin); packet.ObjectData.PathCurve = (byte)prim.PathCurve; @@ -573,9 +550,7 @@ namespace libsecondlife packet.ObjectData.RayTargetID = LLUUID.Zero; packet.ObjectData.BypassRaycast = 1; - // TODO: This is no longer a field in ObjectAdd. Detect if there actually is - // texture information for this prim and send an ObjectUpdate - //packet.ObjectData.TextureEntry = prim.Textures.GetBytes(); + // TODO: Automatically send another packet to set the TextureEntry? Client.Network.SendPacket(packet, simulator); } @@ -919,66 +894,218 @@ namespace libsecondlife foreach (ObjectUpdatePacket.ObjectDataBlock block in update.ObjectData) { + LLVector4 collisionPlane = LLVector4.Zero; + LLVector3 position; + LLVector3 velocity; + LLVector3 acceleration; + LLQuaternion rotation; + LLVector3 angularVelocity; + + // TODO: Parse NameValue here + string nameValue = Helpers.FieldToString(block.NameValue); + + // Object (primitive) parameters + LLObject.ObjectData data = new LLObject.ObjectData(); + data.State = block.State; + data.Material = block.Material; + data.PathCurve = block.PathCurve; + data.ProfileCurve = block.ProfileCurve; + data.PathBegin = LLObject.PathBeginFloat(block.PathBegin); + data.PathEnd = LLObject.PathEndFloat(block.PathEnd); + data.PathScaleX = LLObject.PathScaleFloat(block.PathScaleX); + data.PathScaleY = LLObject.PathScaleFloat(block.PathScaleY); + data.PathShearX = LLObject.PathShearFloat(block.PathShearX); + data.PathShearY = LLObject.PathShearFloat(block.PathShearY); + data.PathTwist = block.PathTwist; + data.PathTwistBegin = block.PathTwistBegin; + data.PathRadiusOffset = LLObject.PathRadiusOffsetFloat(block.PathRadiusOffset); + data.PathTaperX = LLObject.PathTaperFloat(block.PathTaperX); + data.PathTaperY = LLObject.PathTaperFloat(block.PathTaperY); + data.PathRevolutions = LLObject.PathRevolutionsFloat(block.PathRevolutions); + data.PathSkew = LLObject.PathSkewFloat(block.PathSkew); + data.ProfileBegin = LLObject.ProfileBeginFloat(block.ProfileBegin); + data.ProfileEnd = LLObject.ProfileEndFloat(block.ProfileEnd); + data.ProfileHollow = block.ProfileHollow; + + // Additional packed parameters in ObjectData + int pos = 0; + switch (block.ObjectData.Length) + { + case 76: + // Collision normal for avatar + collisionPlane = new LLVector4(block.ObjectData, pos); + pos += 16; + + goto case 60; + case 60: + // Position + position = new LLVector3(block.ObjectData, pos); + pos += 12; + // Velocity + velocity = new LLVector3(block.ObjectData, pos); + pos += 12; + // Acceleration + acceleration = new LLVector3(block.ObjectData, pos); + pos += 12; + // Rotation (theta) + rotation = new LLQuaternion(block.ObjectData, pos, true); + pos += 12; + // Angular velocity (omega) + angularVelocity = new LLVector3(block.ObjectData, pos); + pos += 12; + + break; + case 48: + // Collision normal for avatar + collisionPlane = new LLVector4(block.ObjectData, pos); + pos += 16; + + goto case 32; + case 32: + // The data is an array of unsigned shorts + + // Position + position = new LLVector3( + Helpers.UInt16ToFloat(block.ObjectData, pos, -0.5f * 256.0f, 1.5f * 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 2, -0.5f * 256.0f, 1.5f * 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 4, -256.0f, 3.0f * 256.0f)); + pos += 6; + // Velocity + velocity = new LLVector3( + Helpers.UInt16ToFloat(block.ObjectData, pos, -256.0f, 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 4, -256.0f, 256.0f)); + pos += 6; + // Acceleration + acceleration = new LLVector3( + Helpers.UInt16ToFloat(block.ObjectData, pos, -256.0f, 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 4, -256.0f, 256.0f)); + pos += 6; + // Rotation (theta) + rotation = new LLQuaternion( + Helpers.UInt16ToFloat(block.ObjectData, pos, -1.0f, 1.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 2, -1.0f, 1.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 4, -1.0f, 1.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 6, -1.0f, 1.0f)); + pos += 8; + // Angular velocity (omega) + angularVelocity = new LLVector3( + Helpers.UInt16ToFloat(block.ObjectData, pos, -256.0f, 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f), + Helpers.UInt16ToFloat(block.ObjectData, pos + 4, -256.0f, 256.0f)); + pos += 6; + + break; + case 16: + // The data is an array of single bytes (8-bit numbers) + + // Position + position = new LLVector3( + Helpers.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f)); + pos += 3; + // Velocity + velocity = new LLVector3( + Helpers.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f)); + pos += 3; + // Accleration + acceleration = new LLVector3( + Helpers.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f)); + pos += 3; + // Rotation + rotation = new LLQuaternion( + Helpers.ByteToFloat(block.ObjectData, pos, -1.0f, 1.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 1, -1.0f, 1.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 2, -1.0f, 1.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 3, -1.0f, 1.0f)); + pos += 4; + // Angular Velocity + angularVelocity = new LLVector3( + Helpers.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f), + Helpers.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f)); + pos += 3; + + break; + default: + Client.Log("Got an ObjectUpdate block with ObjectUpdate field length of " + + block.ObjectData.Length, Helpers.LogLevel.Warning); + + continue; + } + + // Unknown + if (block.Data.Length != 0) + Client.DebugLog("Got an ObjectUpdate block with Data length " + block.Data.Length); + + // Determine the object type and create the appropriate class byte pcode = block.PCode; switch (pcode) { case (byte)PCode.Grass: case (byte)PCode.Tree: case (byte)PCode.Prim: - // New prim spotted Primitive prim = new Primitive(); + + prim.Flags = (LLObject.ObjectFlags)block.UpdateFlags; - prim.NameValue = Helpers.FieldToString(block.NameValue); + if ((prim.Flags & LLObject.ObjectFlags.ZlibCompressed) != 0) + { + Client.Log("Got a ZlibCompressed ObjectUpdate, implement me!", Helpers.LogLevel.Info); + continue; + } - prim.RegionHandle = update.RegionData.RegionHandle; - prim.Position = new LLVector3(block.ObjectData, 0); - prim.Rotation = new LLQuaternion(block.ObjectData, 36, true); - // TODO: Parse the rest of the ObjectData byte array fields + prim.NameValue = nameValue; prim.LocalID = block.ID; - prim.State = block.State; prim.ID = block.FullID; prim.ParentID = block.ParentID; - //block.OwnerID Sound-related - prim.Material = block.Material; - prim.PathCurve = block.PathCurve; - prim.ProfileCurve = block.ProfileCurve; - prim.PathBegin = LLObject.PathBeginFloat(block.PathBegin); - prim.PathEnd = LLObject.PathEndFloat(block.PathEnd); - prim.PathScaleX = LLObject.PathScaleFloat(block.PathScaleX); - prim.PathScaleY = LLObject.PathScaleFloat(block.PathScaleY); - prim.PathShearX = LLObject.PathShearFloat(block.PathShearX); - prim.PathShearY = LLObject.PathShearFloat(block.PathShearY); - prim.PathTwist = block.PathTwist; - prim.PathTwistBegin = block.PathTwistBegin; - prim.PathRadiusOffset = LLObject.PathRadiusOffsetFloat(block.PathRadiusOffset); - prim.PathTaperX = LLObject.PathTaperFloat(block.PathTaperX); - prim.PathTaperY = LLObject.PathTaperFloat(block.PathTaperY); - prim.PathRevolutions = LLObject.PathRevolutionsFloat(block.PathRevolutions); - prim.PathSkew = LLObject.PathSkewFloat(block.PathSkew); - prim.ProfileBegin = LLObject.ProfileBeginFloat(block.ProfileBegin); - prim.ProfileEnd = LLObject.ProfileEndFloat(block.ProfileEnd); - prim.ProfileHollow = block.ProfileHollow; + prim.RegionHandle = update.RegionData.RegionHandle; + prim.Scale = block.Scale; + prim.ClickAction = block.ClickAction; + prim.OwnerID = block.OwnerID; + prim.MediaURL = Helpers.FieldToUTF8String(block.MediaURL); + prim.Text = Helpers.FieldToUTF8String(block.Text); + prim.TextColor = new LLColor(block.TextColor, 0); + // Alpha is inversed to help zero encoding + prim.TextColor.A = (byte)(255 - prim.TextColor.A); + // Sound information + prim.Sound = block.Sound; + prim.SoundFlags = block.Flags; + prim.SoundGain = block.Gain; + prim.SoundRadius = block.Radius; - //block.Data ? - prim.Text = ASCIIEncoding.ASCII.GetString(block.Text); - //block.TextColor LLColor4U of the hovering text - //block.MediaURL Quicktime stream + // Joint information + prim.Joint = (Primitive.JointType)block.JointType; + prim.JointPivot = block.JointPivot; + prim.JointAxisOrAnchor = block.JointAxisOrAnchor; + + // Object parameters + prim.data = data; + + // Textures, texture animations, particle system, and extra params prim.Textures = new Primitive.TextureEntry(block.TextureEntry, 0, block.TextureEntry.Length); prim.TextureAnim = new Primitive.TextureAnimation(block.TextureAnim, 0); - //block.JointType ? - //block.JointPivot ? - //block.JointAxisOrAnchor ? prim.ParticleSys = new Primitive.ParticleSystem(block.PSBlock, 0); prim.SetExtraParamsFromBytes(block.ExtraParams, 0); - prim.Scale = block.Scale; - //block.Flags ? - prim.Flags = (LLObject.ObjectFlags)block.UpdateFlags; - //block.ClickAction ? - //block.Gain Sound-related - //block.Sound Sound-related - //block.Radius Sound-related + + // Unknown + prim.GenericData = block.Data; + + // Packed parameters + prim.CollisionPlane = collisionPlane; + prim.Position = position; + prim.Velocity = velocity; + prim.Acceleration = acceleration; + prim.Rotation = rotation; + prim.AngularVelocity = angularVelocity; if (prim.NameValue.StartsWith("AttachItemID")) { @@ -998,12 +1125,16 @@ namespace libsecondlife // Update some internals if this is our avatar if (block.FullID == Client.Network.AgentID) { - // Update our current position information + // Interesting to know Client.Self.LocalID = block.ID; - //avatar.CollisionPlane = new LLQuaternion(block.ObjectData, 0); - Client.Self.Position = new LLVector3(block.ObjectData, 16); - Client.Self.Rotation = new LLQuaternion(block.ObjectData, 52, true); - // TODO: Parse the rest of the ObjectData byte array fields + + // Packed parameters + Client.Self.CollisionPlane = collisionPlane; + Client.Self.Position = position; + Client.Self.Velocity = velocity; + Client.Self.Acceleration = acceleration; + Client.Self.Rotation = rotation; + Client.Self.AngularVelocity = angularVelocity; // Detect if we are sitting or standing uint oldSittingOn = Client.Self.SittingOn; @@ -1020,36 +1151,55 @@ namespace libsecondlife { Avatar avatar = new Avatar(); + avatar.ID = block.FullID; + avatar.LocalID = block.ID; + + // TODO: This will probably change with the NameValue parsing string FirstName = String.Empty; string LastName = String.Empty; string GroupName = String.Empty; - //avatar.CollisionPlane = new LLQuaternion(block.ObjectData, 0); - avatar.Position = new LLVector3(block.ObjectData, 16); - avatar.Rotation = new LLQuaternion(block.ObjectData, 52, true); - // TODO: Parse the rest of the ObjectData byte array fields + // Packed parameters + avatar.CollisionPlane = collisionPlane; + avatar.Position = position; + avatar.Velocity = velocity; + avatar.Acceleration = acceleration; + avatar.Rotation = rotation; + avatar.AngularVelocity = angularVelocity; SetAvatarSittingOn(avatar, block.ParentID); + // Object parameters + avatar.data = data; + + // Unknown + avatar.GenericData = block.Data; + + // TODO: Replace this ParseAvName(Helpers.FieldToString(block.NameValue), ref FirstName, ref LastName, ref GroupName); - avatar.ID = block.FullID; - avatar.LocalID = block.ID; + // Basic vitals avatar.Name = FirstName + " " + LastName; avatar.GroupName = GroupName; avatar.Online = true; avatar.CurrentRegion = simulator.Region; + // Texutres avatar.Textures = new Primitive.TextureEntry(block.TextureEntry, 0, block.TextureEntry.Length); FireOnNewAvatar(simulator, avatar, update.RegionData.RegionHandle, update.RegionData.TimeDilation); } + break; case (byte)PCode.ParticleSystem: // FIXME: Handle ParticleSystem Client.DebugLog("Got an ObjectUpdate block with a ParticleSystem PCode"); + break; default: + Client.DebugLog("Got an ObjectUpdate block with an unrecognized PCode " + + ((PCode)pcode).ToString()); + break; } } @@ -1063,120 +1213,68 @@ namespace libsecondlife /// protected void TerseUpdateHandler(Packet packet, Simulator simulator) { - float x, y, z, w; - uint localid; - LLVector4 CollisionPlane = LLVector4.Zero; - LLVector3 Position; - LLVector3 Velocity; - LLVector3 Acceleration; - LLQuaternion Rotation; - LLVector3 RotationVelocity; - - ImprovedTerseObjectUpdatePacket update = (ImprovedTerseObjectUpdatePacket)packet; - - UpdateDilation(simulator, update.RegionData.TimeDilation); + ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)packet; + UpdateDilation(simulator, terse.RegionData.TimeDilation); - foreach (ImprovedTerseObjectUpdatePacket.ObjectDataBlock block in update.ObjectData) + foreach (ImprovedTerseObjectUpdatePacket.ObjectDataBlock block in terse.ObjectData) { - int i = 0; - bool avatar; - - localid = (uint)(block.Data[i++] + (block.Data[i++] << 8) + - (block.Data[i++] << 16) + (block.Data[i++] << 24)); - - byte state = block.Data[i++]; - - avatar = Convert.ToBoolean(block.Data[i++]); - - if (avatar) + try { - if (OnAvatarMoved == null) return; + ObjectUpdate update = new ObjectUpdate(); + int pos = 0; - CollisionPlane = new LLVector4(block.Data, i); - i += 16; - } - else - { - if (OnPrimMoved == null) return; - } - - // Position - Position = new LLVector3(block.Data, i); - i += 12; - // Velocity - x = Dequantize(block.Data, i, -128.0F, 128.0F); - i += 2; - y = Dequantize(block.Data, i, -128.0F, 128.0F); - i += 2; - z = Dequantize(block.Data, i, -128.0F, 128.0F); - i += 2; - Velocity = new LLVector3(x, y, z); - // Acceleration - x = Dequantize(block.Data, i, -64.0F, 64.0F); - i += 2; - y = Dequantize(block.Data, i, -64.0F, 64.0F); - i += 2; - z = Dequantize(block.Data, i, -64.0F, 64.0F); - i += 2; - Acceleration = new LLVector3(x, y, z); - // Rotation - x = Dequantize(block.Data, i, -1.0F, 1.0F); - i += 2; - y = Dequantize(block.Data, i, -1.0F, 1.0F); - i += 2; - z = Dequantize(block.Data, i, -1.0F, 1.0F); - i += 2; - w = Dequantize(block.Data, i, -1.0F, 1.0F); - i += 2; - Rotation = new LLQuaternion(x, y, z, w); - // Rotation velocity - x = Dequantize(block.Data, i, -64.0F, 64.0F); - i += 2; - y = Dequantize(block.Data, i, -64.0F, 64.0F); - i += 2; - z = Dequantize(block.Data, i, -64.0F, 64.0F); - i += 2; - RotationVelocity = new LLVector3(x, y, z); - - if (avatar) - { - if (localid == Client.Self.LocalID) + // Local ID + update.LocalID = Helpers.BytesToUIntBig(block.Data, pos); + pos += 4; + // State + update.State = block.Data[pos++]; + // Avatar boolean + update.Avatar = (block.Data[pos++] != 0); + // Collision normal for avatar + if (update.Avatar) { - Client.Self.Position = Position; - Client.Self.Rotation = Rotation; + update.CollisionPlane = new LLVector4(block.Data, pos); + pos += 16; } + // Position + update.Position = new LLVector3(block.Data, pos); + pos += 12; + // Velocity + update.Velocity = new LLVector3( + Helpers.UInt16ToFloat(block.Data, pos, -128.0f, 128.0f), + Helpers.UInt16ToFloat(block.Data, pos + 2, -128.0f, 128.0f), + Helpers.UInt16ToFloat(block.Data, pos + 4, -128.0f, 128.0f)); + pos += 6; + // Acceleration + update.Velocity = new LLVector3( + Helpers.UInt16ToFloat(block.Data, pos, -64.0f, 64.0f), + Helpers.UInt16ToFloat(block.Data, pos + 2, -64.0f, 64.0f), + Helpers.UInt16ToFloat(block.Data, pos + 4, -64.0f, 64.0f)); + pos += 6; + // Rotation (theta) + update.Rotation = new LLQuaternion( + Helpers.UInt16ToFloat(block.Data, pos, -1.0f, 1.0f), + Helpers.UInt16ToFloat(block.Data, pos + 2, -1.0f, 1.0f), + Helpers.UInt16ToFloat(block.Data, pos + 4, -1.0f, 1.0f), + Helpers.UInt16ToFloat(block.Data, pos + 8, -1.0f, 1.0f)); + pos += 8; + // Angular velocity + update.AngularVelocity = new LLVector3( + Helpers.UInt16ToFloat(block.Data, pos, -64.0f, 64.0f), + Helpers.UInt16ToFloat(block.Data, pos + 2, -64.0f, 64.0f), + Helpers.UInt16ToFloat(block.Data, pos + 4, -64.0f, 64.0f)); + pos += 6; - AvatarUpdate avupdate = new AvatarUpdate(); - avupdate.LocalID = localid; - avupdate.State = state; - avupdate.Position = Position; - avupdate.CollisionPlane = CollisionPlane; - avupdate.Velocity = Velocity; - avupdate.Acceleration = Acceleration; - avupdate.Rotation = Rotation; - avupdate.RotationVelocity = RotationVelocity; - avupdate.Textures = new Primitive.TextureEntry(block.TextureEntry, 4, block.TextureEntry.Length - 4); + // Textures + // FIXME: Why are we ignoring the first four bytes here? + update.Textures = new LLObject.TextureEntry(block.TextureEntry, 4, block.TextureEntry.Length - 4); - FireOnAvatarMoved(simulator, avupdate, update.RegionData.RegionHandle, update.RegionData.TimeDilation); + // Fire the callback + FireOnObjectUpdated(simulator, update, terse.RegionData.RegionHandle, terse.RegionData.TimeDilation); } - else + catch (Exception e) { - // TODO: Is there an easy way to distinguish prims from trees in this packet, - // or would the client have to do it's own lookup to determine whether it's a - // prim or a tree? If the latter, we should rename this update to something - // less prim specific - - PrimUpdate primupdate = new PrimUpdate(); - primupdate.LocalID = localid; - primupdate.State = state; - primupdate.Position = Position; - primupdate.Velocity = Velocity; - primupdate.Acceleration = Acceleration; - primupdate.Rotation = Rotation; - primupdate.RotationVelocity = RotationVelocity; - primupdate.Textures = new Primitive.TextureEntry(block.TextureEntry, 4, block.TextureEntry.Length - 4); - - FireOnPrimMoved(simulator, primupdate, update.RegionData.RegionHandle, update.RegionData.TimeDilation); + Client.Log(e.ToString(), Helpers.LogLevel.Warning); } } } @@ -1208,30 +1306,35 @@ namespace libsecondlife (block.Data[i++] << 16) + (block.Data[i++] << 24)); byte pcode = block.Data[i++]; - if (pcode == (byte)PCode.Prim) + if (pcode == (byte)PCode.Prim || pcode == (byte)PCode.Grass || pcode == (byte)PCode.Tree || + pcode == (byte)PCode.NewTree) { #region Prim - prim.State = (uint)block.Data[i++]; - i += 4; // CRC - prim.Material = (uint)block.Data[i++]; - i++; // TODO: ClickAction + // State + prim.data.State = (uint)block.Data[i++]; + // CRC + i += 4; + // Material + prim.data.Material = (uint)block.Data[i++]; + // Click action + prim.ClickAction = block.Data[i++]; + // Scale prim.Scale = new LLVector3(block.Data, i); i += 12; + // Position prim.Position = new LLVector3(block.Data, i); i += 12; + // Rotation prim.Rotation = new LLQuaternion(block.Data, i, true); i += 12; - - int flagsValue = (int)block.Data[i++] + (int)(block.Data[i++] << 8) + - (int)(block.Data[i++] << 16) + (int)(block.Data[i++] << 24); - CompressedFlags flags = (CompressedFlags)flagsValue; + // Compressed flags + CompressedFlags flags = (CompressedFlags)Helpers.BytesToUIntBig(block.Data, i); + i += 4; if ((flags & CompressedFlags.Tree) != 0) { - // FIXME: I don't think this is even Tree data, as it would have - // a different PCode. Figure out what this flag is and how to - // decode it + // FIXME: Decode the tree data byte Unknown1 = block.Data[i++]; byte Unknown2 = block.Data[i++]; @@ -1251,8 +1354,7 @@ namespace libsecondlife if ((flags & CompressedFlags.HasAngularVelocity) != 0) { - // TODO: Use this - LLVector3 Omega = new LLVector3(block.Data, i); + prim.AngularVelocity = new LLVector3(block.Data, i); i += 12; } @@ -1261,14 +1363,17 @@ namespace libsecondlife string text = String.Empty; while (block.Data[i] != 0) { + // TODO: Make this UTF8 compliant? text += (char)block.Data[i]; i++; } i++; + // Floating text prim.Text = text; - // TODO: Text color + // Text color + prim.TextColor = new LLColor(block.Data, i); i += 4; } else @@ -1287,10 +1392,9 @@ namespace libsecondlife //Sound data if ((flags & CompressedFlags.HasSound) != 0) { - //TODO: use this data - LLUUID SoundUUID = new LLUUID(block.Data, i); + prim.Sound = new LLUUID(block.Data, i); i += 16; - LLUUID OwnerUUID = new LLUUID(block.Data, i); + prim.OwnerID = new LLUUID(block.Data, i); i += 16; if (!BitConverter.IsLittleEndian) @@ -1299,64 +1403,64 @@ namespace libsecondlife Array.Reverse(block.Data, i + 5, 4); } - float SoundGain = BitConverter.ToSingle(block.Data, i); + prim.SoundGain = BitConverter.ToSingle(block.Data, i); i += 4; - byte SoundFlags = block.Data[i++]; - float SoundRadius = BitConverter.ToSingle(block.Data, i); + prim.SoundFlags = block.Data[i++]; + prim.SoundRadius = BitConverter.ToSingle(block.Data, i); i += 4; } - // Indicates that this is an attachment - if ((flags & CompressedFlags.Attachment) != 0) + if ((flags & CompressedFlags.HasNameValues) != 0) { - // Get the attachment string - // Example: "AttachItemID STRING RW SV fa9a5ab8-1bad-b449-9873-cf5b803e664e" string text = String.Empty; while (block.Data[i] != 0) { + // TODO: Make UTF-8 compliant? text += (char)block.Data[i]; i++; } i++; + // FIXME: Parse the NameValue pairs + prim.NameValue = text; } - if ((flags & CompressedFlags.Unknown1) != 0) + if ((flags & CompressedFlags.ScratchPad) != 0) { - // TODO: Is this even a valid flag? - Client.DebugLog("Compressed object with Unknown1 flag set: " + Environment.NewLine + + // TODO: What is this? + Client.DebugLog("Compressed object with ScratchPad flag set: " + Environment.NewLine + "Flags: " + flags.ToString() + Environment.NewLine + Helpers.FieldToString(block.Data)); } - if ((flags & CompressedFlags.Unknown2) != 0) + if ((flags & CompressedFlags.Unknown) != 0) { - // FIXME: Implement CompressedFlags.Unknown2 - //Client.DebugLog("Compressed object with Unknown2 flag set: " + Environment.NewLine + - // "Flags: " + flags.ToString() + Environment.NewLine + - // Helpers.FieldToString(block.Data)); + // TODO: Implement CompressedFlags.Unknown2 + Client.DebugLog("Compressed object with Unknown flag set: " + Environment.NewLine + + "Flags: " + flags.ToString() + Environment.NewLine + + Helpers.FieldToString(block.Data)); } - prim.PathCurve = (uint)block.Data[i++]; - prim.PathBegin = LLObject.PathBeginFloat(block.Data[i++]); - prim.PathEnd = LLObject.PathEndFloat(block.Data[i++]); - prim.PathScaleX = LLObject.PathScaleFloat(block.Data[i++]); - prim.PathScaleY = LLObject.PathScaleFloat(block.Data[i++]); - prim.PathShearX = LLObject.PathShearFloat(block.Data[i++]); - prim.PathShearY = LLObject.PathShearFloat(block.Data[i++]); - prim.PathTwist = (int)block.Data[i++]; - prim.PathTwistBegin = (int)block.Data[i++]; - prim.PathRadiusOffset = LLObject.PathRadiusOffsetFloat((sbyte)block.Data[i++]); - prim.PathTaperX = LLObject.PathTaperFloat((sbyte)block.Data[i++]); - prim.PathTaperY = LLObject.PathTaperFloat((sbyte)block.Data[i++]); - prim.PathRevolutions = LLObject.PathRevolutionsFloat(block.Data[i++]); - prim.PathSkew = LLObject.PathSkewFloat((sbyte)block.Data[i++]); + prim.data.PathCurve = (uint)block.Data[i++]; + prim.data.PathBegin = LLObject.PathBeginFloat(block.Data[i++]); + prim.data.PathEnd = LLObject.PathEndFloat(block.Data[i++]); + prim.data.PathScaleX = LLObject.PathScaleFloat(block.Data[i++]); + prim.data.PathScaleY = LLObject.PathScaleFloat(block.Data[i++]); + prim.data.PathShearX = LLObject.PathShearFloat(block.Data[i++]); + prim.data.PathShearY = LLObject.PathShearFloat(block.Data[i++]); + prim.data.PathTwist = (int)block.Data[i++]; + prim.data.PathTwistBegin = (int)block.Data[i++]; + prim.data.PathRadiusOffset = LLObject.PathRadiusOffsetFloat((sbyte)block.Data[i++]); + prim.data.PathTaperX = LLObject.PathTaperFloat((sbyte)block.Data[i++]); + prim.data.PathTaperY = LLObject.PathTaperFloat((sbyte)block.Data[i++]); + prim.data.PathRevolutions = LLObject.PathRevolutionsFloat(block.Data[i++]); + prim.data.PathSkew = LLObject.PathSkewFloat((sbyte)block.Data[i++]); - prim.ProfileCurve = (uint)block.Data[i++]; - prim.ProfileBegin = Primitive.ProfileBeginFloat(block.Data[i++]); - prim.ProfileEnd = Primitive.ProfileEndFloat(block.Data[i++]); - prim.ProfileHollow = (uint)block.Data[i++]; + prim.data.ProfileCurve = (uint)block.Data[i++]; + prim.data.ProfileBegin = Primitive.ProfileBeginFloat(block.Data[i++]); + prim.data.ProfileEnd = Primitive.ProfileEndFloat(block.Data[i++]); + prim.data.ProfileHollow = (uint)block.Data[i++]; int textureEntryLength = (int)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); @@ -1375,8 +1479,9 @@ namespace libsecondlife } // Fire the appropriate callback - if ((flags & CompressedFlags.Attachment) != 0) + if ((flags & CompressedFlags.HasNameValues) != 0) { + // TODO: We should use a better check to see if this is actually an attachment FireOnNewAttachment(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); } else if ((flags & CompressedFlags.Tree) != 0) @@ -1387,13 +1492,9 @@ namespace libsecondlife { FireOnNewPrim(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); } + #endregion Prim } - else if (pcode == (byte)PCode.Grass || pcode == (byte)PCode.Tree || pcode == (byte)PCode.NewTree) - { - // FIXME: Implement this - //Client.DebugLog("######### Got an ObjectUpdateCompressed for grass/tree, implement this! #########"); - } else { Client.Log("######### Got an ObjectUpdateCompressed for PCode=" + pcode.ToString() + @@ -1592,20 +1693,11 @@ namespace libsecondlife } } - protected void FireOnPrimMoved(Simulator simulator, PrimUpdate primupdate, ulong RegionHandle, ushort TimeDilation) + protected void FireOnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong RegionHandle, ushort TimeDilation) { - if (OnPrimMoved != null) + if (OnObjectUpdated != null) { - try { OnPrimMoved(simulator, primupdate, RegionHandle, TimeDilation); } - catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } - } - } - - protected void FireOnAvatarMoved(Simulator simulator, AvatarUpdate avupdate, ulong RegionHandle, ushort TimeDilation) - { - if (OnAvatarMoved != null) - { - try { OnAvatarMoved(simulator, avupdate, RegionHandle, TimeDilation); } + try { OnObjectUpdated(simulator, update, RegionHandle, TimeDilation); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } diff --git a/libsecondlife-cs/Prims.cs b/libsecondlife-cs/Prims.cs index e009375d..0252ca15 100644 --- a/libsecondlife-cs/Prims.cs +++ b/libsecondlife-cs/Prims.cs @@ -48,6 +48,25 @@ namespace libsecondlife Light = 0x20 } + /// + /// + /// + public enum JointType : byte + { + /// + Invalid = 0, + /// + Hinge = 1, + /// + Point = 2, + /// + [Obsolete] + LPoint = 3, + /// + [Obsolete] + Wheel = 4 + } + #region Subclasses @@ -229,8 +248,31 @@ namespace libsecondlife public LightData Light = new LightData(); /// public ParticleSystem ParticleSys = new ParticleSystem(); + /// + public byte ClickAction; + /// + public LLUUID Sound = LLUUID.Zero; + /// Identifies the owner of the audio or particle system + public LLUUID OwnerID = LLUUID.Zero; + /// + public byte SoundFlags; + /// + public float SoundGain; + /// + public float SoundRadius; + /// + public string Text; + /// + public LLColor TextColor; + /// + public string MediaURL; + public JointType Joint; + public LLVector3 JointPivot; + public LLVector3 JointAxisOrAnchor; - + /// + /// Default constructor + /// public Primitive() { } @@ -280,9 +322,9 @@ namespace libsecondlife output += "ParentID: " + ParentID + ", "; output += "LocalID: " + LocalID + ", "; output += "Flags: " + Flags + ", "; - output += "State: " + State + ", "; - output += "PCode: " + PCode + ", "; - output += "Material: " + Material + ", "; + output += "State: " + data.State + ", "; + output += "PCode: " + data.PCode + ", "; + output += "Material: " + data.Material + ", "; return output; } diff --git a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs index 063c0bd1..08c2fa4d 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs @@ -124,7 +124,8 @@ namespace libsecondlife.TestClient rezzingRootPrim = true; currentPrim = linkset.RootPrim; - Client.Objects.AddPrim(Client.Network.CurrentSim, linkset.RootPrim, linkset.RootPrim.Position); + Client.Objects.AddPrim(Client.Network.CurrentSim, linkset.RootPrim.Data, LLUUID.Zero, + linkset.RootPrim.Position, linkset.RootPrim.Scale, linkset.RootPrim.Rotation); if (!primDone.WaitOne(10000, false)) return "Rez failed, timed out while creating a prim."; @@ -138,7 +139,8 @@ namespace libsecondlife.TestClient currentPrim = prim; currentPosition = prim.Position + linkset.RootPrim.Position; - Client.Objects.AddPrim(Client.Network.CurrentSim, prim, currentPosition); + Client.Objects.AddPrim(Client.Network.CurrentSim, prim.Data, LLUUID.Zero, + currentPosition, prim.Scale, prim.Rotation); if (!primDone.WaitOne(10000, false)) return "Rez failed, timed out while creating a prim."; diff --git a/libsecondlife-cs/examples/TestClient/TestClient.cs b/libsecondlife-cs/examples/TestClient/TestClient.cs index 1220644b..7f48e419 100644 --- a/libsecondlife-cs/examples/TestClient/TestClient.cs +++ b/libsecondlife-cs/examples/TestClient/TestClient.cs @@ -53,10 +53,9 @@ namespace libsecondlife.TestClient Network.RegisterCallback(PacketType.AgentDataUpdate, new NetworkManager.PacketCallback(AgentDataUpdateHandler)); Objects.OnNewPrim += new ObjectManager.NewPrimCallback(Objects_OnNewPrim); - Objects.OnPrimMoved += new ObjectManager.PrimMovedCallback(Objects_OnPrimMoved); + Objects.OnObjectUpdated += new ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated); Objects.OnObjectKilled += new ObjectManager.KillObjectCallback(Objects_OnObjectKilled); Objects.OnNewAvatar += new ObjectManager.NewAvatarCallback(Objects_OnNewAvatar); - Objects.OnAvatarMoved += new ObjectManager.AvatarMovedCallback(Objects_OnAvatarMoved); Self.OnInstantMessage += new MainAvatar.InstantMessageCallback(Self_OnInstantMessage); this.OnLogMessage += new LogCallback(TestClient_OnLogMessage); @@ -203,14 +202,41 @@ namespace libsecondlife.TestClient } } - private void Objects_OnPrimMoved(Simulator simulator, PrimUpdate prim, ulong regionHandle, ushort timeDilation) + private void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation) { - lock (SimPrims) + regionX = (int)(regionHandle >> 32); + regionY = (int)(regionHandle & 0xFFFFFFFF); + + if (update.Avatar) { - if (SimPrims.ContainsKey(simulator) && SimPrims[simulator].ContainsKey(prim.LocalID)) + lock (AvatarList) { - SimPrims[simulator][prim.LocalID].Position = prim.Position; - SimPrims[simulator][prim.LocalID].Rotation = prim.Rotation; + // TODO: We really need a solid avatar and object tracker in Utilities to use here + if (AvatarList.ContainsKey(update.LocalID)) + { + AvatarList[update.LocalID].CollisionPlane = update.CollisionPlane; + AvatarList[update.LocalID].Position = update.Position; + AvatarList[update.LocalID].Velocity = update.Velocity; + AvatarList[update.LocalID].Acceleration = update.Acceleration; + AvatarList[update.LocalID].Rotation = update.Rotation; + AvatarList[update.LocalID].AngularVelocity = update.AngularVelocity; + AvatarList[update.LocalID].Textures = update.Textures; + } + } + } + else + { + lock (SimPrims) + { + if (SimPrims.ContainsKey(simulator) && SimPrims[simulator].ContainsKey(update.LocalID)) + { + SimPrims[simulator][update.LocalID].Position = update.Position; + SimPrims[simulator][update.LocalID].Velocity = update.Velocity; + SimPrims[simulator][update.LocalID].Acceleration = update.Acceleration; + SimPrims[simulator][update.LocalID].Rotation = update.Rotation; + SimPrims[simulator][update.LocalID].AngularVelocity = update.AngularVelocity; + SimPrims[simulator][update.LocalID].Textures = update.Textures; + } } } } @@ -241,20 +267,6 @@ namespace libsecondlife.TestClient } } - private void Objects_OnAvatarMoved(Simulator simulator, AvatarUpdate avatar, ulong regionHandle, ushort timeDilation) - { - regionX = (int)(regionHandle >> 32); - regionY = (int)(regionHandle & 0xFFFFFFFF); - lock (AvatarList) - { - if (AvatarList.ContainsKey(avatar.LocalID)) - { - AvatarList[avatar.LocalID].Position = avatar.Position; - AvatarList[avatar.LocalID].Rotation = avatar.Rotation; - } - } - } - private void AvatarAppearanceHandler(Packet packet, Simulator simulator) { AvatarAppearancePacket appearance = (AvatarAppearancePacket)packet; diff --git a/libsecondlife-cs/examples/primexport/frmPrimExport.cs b/libsecondlife-cs/examples/primexport/frmPrimExport.cs index b8c7abd3..b3c36653 100644 --- a/libsecondlife-cs/examples/primexport/frmPrimExport.cs +++ b/libsecondlife-cs/examples/primexport/frmPrimExport.cs @@ -443,7 +443,9 @@ namespace primexport output += "" + Environment.NewLine + "" + Environment.NewLine; - switch (prim.ProfileCurve + prim.PathCurve) + LLObject.ObjectData data = prim.Data; + + switch (data.ProfileCurve + data.PathCurve) { case 17: // PRIM_TYPE_BOX @@ -475,7 +477,7 @@ namespace primexport break; default: Log("Not exporting an unhandled prim, ProfileCurve=" + - prim.ProfileCurve + ", PathCurve=" + prim.PathCurve + Environment.NewLine); + data.ProfileCurve + ", PathCurve=" + data.PathCurve + Environment.NewLine); continue; } @@ -493,27 +495,27 @@ namespace primexport if (type == 1) { - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; } else { - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; } - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; - output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; + output += "" + Environment.NewLine; // FIXME: Hollowshape. 16-21 = circle, 32-37 = square, 48-53 = triangle output += "" + Environment.NewLine; diff --git a/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs b/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs index d82c5928..dc4fe05f 100644 --- a/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs +++ b/libsecondlife-cs/libsecondlife.Utilities/Utilities.cs @@ -156,7 +156,7 @@ namespace libsecondlife.Utilities Client.Avatars.OnAvatarGroups += new AvatarManager.AvatarGroupsCallback(Avatars_OnAvatarGroups); Client.Objects.OnNewAvatar += new ObjectManager.NewAvatarCallback(Objects_OnNewAvatar); - Client.Objects.OnAvatarMoved += new ObjectManager.AvatarMovedCallback(Objects_OnAvatarMoved); + Client.Objects.OnObjectUpdated += new ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated); } /// @@ -359,17 +359,14 @@ namespace libsecondlife.Utilities } } - void Objects_OnAvatarMoved(Simulator simulator, AvatarUpdate avatar, ulong regionHandle, ushort timeDilation) + void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation) { // TODO: } - void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation) + void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation) { - lock (avatars) - { - avatars[avatar.ID] = avatar; - } + // TODO: } private void Avatars_OnAvatarNames(Dictionary names)