/* * Copyright (c) 2006, Second Life Reverse Engineering Team * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the Second Life Reverse Engineering Team nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; namespace libsecondlife { public delegate void NewPrimCallback(Simulator simulator, PrimObject prim); public delegate void NewAvatarCallback(Simulator simulator, Avatar avatar); public delegate void PrimMovedCallback(Simulator simulator, PrimObject prim); public delegate void AvatarMovedCallback(Simulator simulator, Avatar avatar); /// /// /// public struct PrimUpdate { public uint LocalID; public byte State; public LLVector3 Position; public LLVector3 Velocity; public LLVector3 Acceleration; public LLQuaternion Rotation; public LLVector3 RotationVelocity; } /// /// /// public struct AvatarUpdate { public uint LocalID; public byte State; public LLVector4 CollisionPlane; public LLVector3 Position; public LLVector3 Velocity; public LLVector3 Acceleration; public LLQuaternion Rotation; public LLVector3 RotationVelocity; } /// /// Tracks all the objects (avatars and prims) in a region /// public class ObjectManager { private SecondLife Client; public ObjectManager(SecondLife client) { Client = client; Client.Network.RegisterCallback("ObjectUpdate", new PacketCallback(UpdateHandler)); Client.Network.RegisterCallback("ImprovedTerseObjectUpdate", new PacketCallback(TerseUpdateHandler)); } private void UpdateHandler(Packet packet, Simulator simulator) { PrimObject prim = new PrimObject(); // Create a PrimObject or Avatar and fire the callback Client.Log("ObjectUpdate", Helpers.LogLevel.Info); foreach (Block block in packet.Blocks()) { foreach (Field field in block.Fields) { switch (field.Layout.Name) { case "ID": prim.ID = (UInt32)field.Data; break; case "State": prim.State = (byte)field.Data; break; case "FullID": prim.UUID = (LLUUID)field.Data; break; case "ParentID": //prim.ParentID = (uint)field.Data; break; case "OwnerID": //prim.OwnerID = (LLUUID)field.Data; break; case "Material": prim.Material = (byte)(field.Data); break; case "PathCurve": prim.PathCurve = (byte)field.Data; break; case "ProfileCurve": prim.ProfileCurve = (byte)field.Data; break; case "PathBegin": prim.PathBegin = (byte)field.Data; break; case "PathEnd": prim.PathEnd = (byte)field.Data; break; case "PathScaleX": prim.PathScaleX = (float)((byte)field.Data); break; case "PathScaleY": prim.PathScaleY = (float)((byte)field.Data); break; case "PathShearX": prim.PathShearX = (float)((byte)field.Data); break; case "PathShearY": prim.PathShearY = (float)((byte)field.Data); break; case "PathTwist": prim.PathTwist = (sbyte)field.Data; break; case "PathRadiusOffset": prim.PathRadiusOffset = (float)((sbyte)field.Data); break; case "PathTaperX": prim.PathTaperX = (float)((sbyte)field.Data); break; case "PathTaperY": prim.PathTaperY = (float)((sbyte)field.Data); break; case "PathRevolutions": prim.PathRevolutions = (float)((byte)field.Data); break; case "PathSkew": prim.PathSkew = (float)((sbyte)field.Data); break; case "ProfileBegin": prim.ProfileBegin = (float)((byte)field.Data); break; case "ProfileEnd": prim.ProfileEnd = (float)((byte)field.Data); break; case "ProfileHollow": prim.ProfileHollow = (byte)field.Data; break; case "NameValue": Console.WriteLine("[debug] Name: " + Helpers.FieldToString(field.Data)); prim.Name = Helpers.FieldToString(field.Data); break; case "Data": //prim.Sound = (LLUUID)field.Data; break; case "Text": // break; case "TextColor": // LLColor4U break; case "MediaURL": Console.WriteLine("[debug] MediaURL: " + Helpers.FieldToString(field.Data)); // break; case "TextureEntry": byte[] bytes = (byte[])field.Data; prim.Texture = new LLUUID(bytes, 0); break; case "TextureAnim": break; case "JointType": //prim.JointType = (byte)field.Data; break; case "JointPivot": // break; case "JointAxisOrAnchor": // break; case "PCode": //prim.PCode = (byte)field.Data; break; case "PSBlock": // break; case "ExtraParams": // break; case "Scale": prim.Scale = (LLVector3)field.Data; break; case "Flags": //prim.Flags = (byte)field.Data; break; case "UpdateFlags": //prim.UpdateFlags = (uint)field.Data; break; case "PathTwistBegin": prim.PathTwistBegin = (sbyte)field.Data; break; case "CRC": //prim.CRC = (uint)field.Data; break; case "ClickAction": //prim.ClickAction = (byte)field.Data; break; case "Gain": //prim.Gain = (Single)field.Data; break; case "Sound": //prim.Sound = (LLUUID)field.Data; break; case "Radius": //prim.Radius = (Single)field.Data; break; case "ObjectData": // break; case "TimeDilation": // break; case "RegionHandle": // break; default: Console.WriteLine("Field Not Handled: " + field.Layout.Name + " " + field.Data.GetType().ToString()); break; } } } // If an event handler is registered call it } private void TerseUpdateHandler(Packet packet, Simulator simulator) { bool avatar = false; int i; byte[] data; uint localid = 0; byte state = 0; float x, y, z, s; LLVector4 CollisionPlane = null; LLVector3 Position = null, Velocity = null, Acceleration = null, RotationVelocity = null; LLQuaternion Rotation = null; // Create an AvatarUpdate or PrimUpdate and fire the callback Client.Log("ImprovedTerseObjectUpdate", Helpers.LogLevel.Info); foreach (Block block in packet.Blocks()) { foreach (Field field in block.Fields) { switch (field.Layout.Name) { case "Data": i = 0; data = (byte[])field.Data; // Region ID localid = (uint)(data[i] + (data[i + 1] << 8) + (data[i + 2] << 16) + (data[i + 3] << 24)); i += 4; // Object state state = data[i++]; // Avatar boolean avatar = Convert.ToBoolean(data[i]); i++; if (avatar) { CollisionPlane = new LLVector4(data, i); i += 16; } // Position Position = new LLVector3(data, i); i += 12; // Velocity x = Dequantize(data, i, -128.0F, 128.0F); i += 2; y = Dequantize(data, i, -128.0F, 128.0F); i += 2; z = Dequantize(data, i, -128.0F, 128.0F); i += 2; Velocity = new LLVector3(x, y, z); // Acceleration x = Dequantize(data, i, -64.0F, 64.0F); i += 2; y = Dequantize(data, i, -64.0F, 64.0F); i += 2; z = Dequantize(data, i, -64.0F, 64.0F); i += 2; Acceleration = new LLVector3(x, y, z); // Rotation x = Dequantize(data, i, -1.0F, 1.0F); i += 2; y = Dequantize(data, i, -1.0F, 1.0F); i += 2; z = Dequantize(data, i, -1.0F, 1.0F); i += 2; s = Dequantize(data, i, -1.0F, 1.0F); i += 2; Rotation = new LLQuaternion(x, y, z, s); // Rotation velocity x = Dequantize(data, i, -64.0F, 64.0F); i += 2; y = Dequantize(data, i, -64.0F, 64.0F); i += 2; z = Dequantize(data, i, -64.0F, 64.0F); i += 2; RotationVelocity = new LLVector3(x, y, z); break; case "RegionHandle": // break; case "TimeDilation": // break; case "TextureEntry": // break; } } } if (avatar) { 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; Client.Log("LocalID: " + localid + ", State: " + state + ", Position: " + Position.ToString() + ", CollisionPlane: " + CollisionPlane.ToString() + ", Velocity: " + Velocity.ToString() + ", Acceleration: " + Acceleration.ToString() + ", Rotation: " + Rotation.ToString() + ", RotationVelocity: " + RotationVelocity.ToString(), Helpers.LogLevel.Info); } else { ; } // If an event handler is registered call it } /// /// Takes a quantized value and its quantization range and returns a float /// representation of the continuous value. For example, a value of 32767 /// and a range of -128.0 to 128.0 would return 0.0. The endian conversion /// from the 16-bit little endian to the native platform will also be handled. /// /// The byte array containing the short value /// The beginning position of the short (quantized) value /// The lower quantization range /// The upper quantization range /// A 32-bit floating point representation of the dequantized value public static float Dequantize(byte[] byteArray, int pos, float lower, float upper) { ushort value = (ushort)(byteArray[pos] + (byteArray[pos + 1] << 8)); float QV = (float)value; float range = upper - lower; float QF = range / 65536.0F; return (float)((QV * QF - (0.5F * range)) + QF); } } }