/// <summary> /// /// </summary> /// <param name="packet"></param> /// <param name="simulator"></param> protected void CompressedUpdateHandler(Packet packet, Simulator simulator) { ObjectUpdateCompressedPacket update = (ObjectUpdateCompressedPacket)packet; for (int b = 0; b < update.ObjectData.Length; b++) { ObjectUpdateCompressedPacket.ObjectDataBlock block = update.ObjectData[b]; int i = 0; try { // UUID LLUUID FullID = new LLUUID(block.Data, 0); i += 16; // Local ID uint LocalID = (uint)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); // PCode PCode pcode = (PCode)block.Data[i++]; #region Relevance check if (!Client.Settings.ALWAYS_DECODE_OBJECTS) { switch (pcode) { case PCode.Grass: case PCode.Tree: case PCode.NewTree: if (OnNewFoliage == null) continue; break; case PCode.Prim: if (OnNewPrim == null) continue; break; } } #endregion Relevance check Primitive prim = GetPrimitive(simulator, LocalID, FullID); prim.LocalID = LocalID; prim.ID = FullID; prim.Flags = (LLObject.ObjectFlags)block.UpdateFlags; prim.Data.PCode = pcode; switch (pcode) { case PCode.Grass: case PCode.Tree: case PCode.NewTree: #region Foliage Decoding // State prim.Data.State = block.Data[i++]; // CRC i += 4; // Material prim.Data.Material = (LLObject.MaterialType)block.Data[i++]; // Click action prim.ClickAction = (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; #endregion Foliage Decoding // FIXME: We are leaving a lot of data left undecoded here, including the // tree species. Need to understand what is going on with these packets // and fix it soon! FireOnNewFoliage(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); break; case PCode.Prim: #region Decode block and update Prim // State prim.Data.State = block.Data[i++]; // CRC i += 4; // Material prim.Data.Material = (LLObject.MaterialType)block.Data[i++]; // Click action prim.ClickAction = (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; // Compressed flags CompressedFlags flags = (CompressedFlags)Helpers.BytesToUIntBig(block.Data, i); i += 4; prim.OwnerID = new LLUUID(block.Data, i); i += 16; // Angular velocity if ((flags & CompressedFlags.HasAngularVelocity) != 0) { prim.AngularVelocity = new LLVector3(block.Data, i); i += 12; } // Parent ID if ((flags & CompressedFlags.HasParent) != 0) { prim.ParentID = (uint)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); } else { prim.ParentID = 0; } // Tree data if ((flags & CompressedFlags.Tree) != 0) { prim.GenericData = new byte[1]; prim.GenericData[0] = block.Data[i++]; } // Scratch pad else if ((flags & CompressedFlags.ScratchPad) != 0) { int size = block.Data[i++]; prim.GenericData = new byte[size]; Buffer.BlockCopy(block.Data, i, prim.GenericData, 0, size); i += size; } // Floating text if ((flags & CompressedFlags.HasText) != 0) { string text = String.Empty; while (block.Data[i] != 0) { text += (char)block.Data[i]; i++; } i++; // Floating text prim.Text = text; // Text color prim.TextColor = new LLColor(block.Data, i, false); // FIXME: Is alpha inversed here as well? i += 4; } else { prim.Text = String.Empty; } // Media URL if ((flags & CompressedFlags.MediaURL) != 0) { string text = String.Empty; while (block.Data[i] != 0) { text += (char)block.Data[i]; i++; } i++; prim.MediaURL = text; } // Particle system if ((flags & CompressedFlags.HasParticles) != 0) { prim.ParticleSys = new Primitive.ParticleSystem(block.Data, i); i += 86; } // Extra parameters i += prim.SetExtraParamsFromBytes(block.Data, i); //Sound data if ((flags & CompressedFlags.HasSound) != 0) { prim.Sound = new LLUUID(block.Data, i); i += 16; if (!BitConverter.IsLittleEndian) { Array.Reverse(block.Data, i, 4); Array.Reverse(block.Data, i + 5, 4); } prim.SoundGain = BitConverter.ToSingle(block.Data, i); i += 4; prim.SoundFlags = block.Data[i++]; prim.SoundRadius = BitConverter.ToSingle(block.Data, i); i += 4; } // Name values if ((flags & CompressedFlags.HasNameValues) != 0) { string text = String.Empty; while (block.Data[i] != 0) { text += (char)block.Data[i]; i++; } i++; // Parse the name values if (text.Length > 0) { string[] lines = text.Split('\n'); prim.NameValues = new NameValue[lines.Length]; for (int j = 0; j < lines.Length; j++) { if (!String.IsNullOrEmpty(lines[j])) { NameValue nv = new NameValue(lines[j]); prim.NameValues[j] = nv; } } } } prim.Data.PathCurve = (LLObject.PathCurve)block.Data[i++]; ushort pathBegin = Helpers.BytesToUInt16(block.Data, i); i += 2; prim.Data.PathBegin = LLObject.UnpackBeginCut(pathBegin); ushort pathEnd = Helpers.BytesToUInt16(block.Data, i); i += 2; prim.Data.PathEnd = LLObject.UnpackEndCut(pathEnd); prim.Data.PathScaleX = LLObject.UnpackPathScale(block.Data[i++]); prim.Data.PathScaleY = LLObject.UnpackPathScale(block.Data[i++]); prim.Data.PathShearX = LLObject.UnpackPathShear((sbyte)block.Data[i++]); prim.Data.PathShearY = LLObject.UnpackPathShear((sbyte)block.Data[i++]); prim.Data.PathTwist = LLObject.UnpackPathTwist((sbyte)block.Data[i++]); prim.Data.PathTwistBegin = LLObject.UnpackPathTwist((sbyte)block.Data[i++]); prim.Data.PathRadiusOffset = LLObject.UnpackPathTwist((sbyte)block.Data[i++]); prim.Data.PathTaperX = LLObject.UnpackPathTaper((sbyte)block.Data[i++]); prim.Data.PathTaperY = LLObject.UnpackPathTaper((sbyte)block.Data[i++]); prim.Data.PathRevolutions = LLObject.UnpackPathRevolutions(block.Data[i++]); prim.Data.PathSkew = LLObject.UnpackPathTwist((sbyte)block.Data[i++]); prim.Data.profileCurve = block.Data[i++]; ushort profileBegin = Helpers.BytesToUInt16(block.Data, i); i += 2; prim.Data.ProfileBegin = LLObject.UnpackBeginCut(profileBegin); ushort profileEnd = Helpers.BytesToUInt16(block.Data, i); i += 2; prim.Data.ProfileEnd = LLObject.UnpackEndCut(profileEnd); ushort profileHollow = Helpers.BytesToUInt16(block.Data, i); i += 2; prim.Data.ProfileHollow = LLObject.UnpackProfileHollow(profileHollow); // TextureEntry int textureEntryLength = (int)Helpers.BytesToUIntBig(block.Data, i); i += 4; prim.Textures = new LLObject.TextureEntry(block.Data, i, textureEntryLength); i += textureEntryLength; // Texture animation if ((flags & CompressedFlags.TextureAnimation) != 0) { //int textureAnimLength = (int)Helpers.BytesToUIntBig(block.Data, i); i += 4; prim.TextureAnim = new Primitive.TextureAnimation(block.Data, i); } #endregion #region Fire Events // Fire the appropriate callback if ((flags & CompressedFlags.HasNameValues) != 0 && prim.ParentID != 0) FireOnNewAttachment(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); else if ((flags & CompressedFlags.Tree) != 0) FireOnNewFoliage(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); else FireOnNewPrim(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); #endregion break; default: Client.DebugLog("Got an ObjectUpdateCompressed for PCode " + pcode.ToString() + ", implement this!"); break; } } catch (IndexOutOfRangeException e) { Client.Log("Had a problem decoding an ObjectUpdateCompressed packet: " + e.ToString(), Helpers.LogLevel.Warning); Client.Log(block.ToString(), Helpers.LogLevel.Warning); } } }
/// <summary> /// Used for new prims, or significant changes to existing prims /// </summary> /// <param name="packet"></param> /// <param name="simulator"></param> protected void UpdateHandler(Packet packet, Simulator simulator) { ObjectUpdatePacket update = (ObjectUpdatePacket)packet; UpdateDilation(simulator, update.RegionData.TimeDilation); for (int b = 0; b < update.ObjectData.Length; b++) { ObjectUpdatePacket.ObjectDataBlock block = update.ObjectData[b]; LLVector4 collisionPlane = LLVector4.Zero; LLVector3 position; LLVector3 velocity; LLVector3 acceleration; LLQuaternion rotation; LLVector3 angularVelocity; NameValue[] nameValues; bool attachment = false; PCode pcode = (PCode)block.PCode; #region Relevance check // Check if we are interested in this object if (!Client.Settings.ALWAYS_DECODE_OBJECTS) { switch (pcode) { case PCode.Grass: case PCode.Tree: case PCode.NewTree: if (OnNewFoliage == null) continue; break; case PCode.Prim: if (OnNewPrim == null) continue; break; case PCode.Avatar: // Make an exception for updates about our own agent if (block.FullID != Client.Self.AgentID && OnNewAvatar == null) continue; break; case PCode.ParticleSystem: continue; // TODO: Do something with these } } #endregion Relevance check #region NameValue parsing string nameValue = Helpers.FieldToUTF8String(block.NameValue); if (nameValue.Length > 0) { string[] lines = nameValue.Split('\n'); nameValues = new NameValue[lines.Length]; for (int i = 0; i < lines.Length; i++) { if (!String.IsNullOrEmpty(lines[i])) { NameValue nv = new NameValue(lines[i]); if (nv.Name == "AttachItemID") attachment = true; nameValues[i] = nv; } } } else { nameValues = new NameValue[0]; } #endregion NameValue parsing #region Decode Object (primitive) parameters LLObject.ObjectData data = new LLObject.ObjectData(); data.State = block.State; data.Material = (LLObject.MaterialType)block.Material; data.PathCurve = (LLObject.PathCurve)block.PathCurve; data.profileCurve = block.ProfileCurve; data.PathBegin = LLObject.UnpackBeginCut(block.PathBegin); data.PathEnd = LLObject.UnpackEndCut(block.PathEnd); data.PathScaleX = LLObject.UnpackPathScale(block.PathScaleX); data.PathScaleY = LLObject.UnpackPathScale(block.PathScaleY); data.PathShearX = LLObject.UnpackPathShear((sbyte)block.PathShearX); data.PathShearY = LLObject.UnpackPathShear((sbyte)block.PathShearY); data.PathTwist = LLObject.UnpackPathTwist(block.PathTwist); data.PathTwistBegin = LLObject.UnpackPathTwist(block.PathTwistBegin); data.PathRadiusOffset = LLObject.UnpackPathTwist(block.PathRadiusOffset); data.PathTaperX = LLObject.UnpackPathTaper(block.PathTaperX); data.PathTaperY = LLObject.UnpackPathTaper(block.PathTaperY); data.PathRevolutions = LLObject.UnpackPathRevolutions(block.PathRevolutions); data.PathSkew = LLObject.UnpackPathTwist(block.PathSkew); data.ProfileBegin = LLObject.UnpackBeginCut(block.ProfileBegin); data.ProfileEnd = LLObject.UnpackEndCut(block.ProfileEnd); data.ProfileHollow = LLObject.UnpackProfileHollow(block.ProfileHollow); data.PCode = pcode; #endregion #region Decode 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; } #endregion // Determine the object type and create the appropriate class switch (pcode) { #region Prim and Foliage case PCode.Grass: case PCode.Tree: case PCode.NewTree: case PCode.Prim: Primitive prim = GetPrimitive(simulator, block.ID, block.FullID); #region Update Prim Info with decoded data prim.Flags = (LLObject.ObjectFlags)block.UpdateFlags; if ((prim.Flags & LLObject.ObjectFlags.ZlibCompressed) != 0) { Client.Log("Got a ZlibCompressed ObjectUpdate, implement me!", Helpers.LogLevel.Warning); continue; } // Automatically request ObjectProperties for prim if it was rezzed selected. if ((prim.Flags & LLObject.ObjectFlags.CreateSelected) == LLObject.ObjectFlags.CreateSelected) SelectObject(simulator, prim.LocalID); prim.NameValues = nameValues; prim.LocalID = block.ID; prim.ID = block.FullID; prim.ParentID = block.ParentID; prim.RegionHandle = update.RegionData.RegionHandle; prim.Scale = block.Scale; prim.ClickAction = (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, false); // Only alpha is inversed prim.TextColor.A = (byte)(1.0f - prim.TextColor.A); // Sound information prim.Sound = block.Sound; prim.SoundFlags = block.Flags; prim.SoundGain = block.Gain; prim.SoundRadius = block.Radius; // 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 LLObject.TextureEntry(block.TextureEntry, 0, block.TextureEntry.Length); prim.TextureAnim = new Primitive.TextureAnimation(block.TextureAnim, 0); prim.ParticleSys = new Primitive.ParticleSystem(block.PSBlock, 0); prim.SetExtraParamsFromBytes(block.ExtraParams, 0); // PCode-specific data prim.GenericData = block.Data; // Packed parameters prim.CollisionPlane = collisionPlane; prim.Position = position; prim.Velocity = velocity; prim.Acceleration = acceleration; prim.Rotation = rotation; prim.AngularVelocity = angularVelocity; #endregion if (attachment) FireOnNewAttachment(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); else if (pcode == PCode.Prim) FireOnNewPrim(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); else FireOnNewFoliage(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); break; #endregion Prim and Foliage #region Avatar case PCode.Avatar: // Update some internals if this is our avatar if (block.FullID == Client.Self.AgentID) { #region Update Client.Self // We need the local ID to recognize terse updates for our agent Client.Self.localID = block.ID; // Packed parameters Client.Self.collisionPlane = collisionPlane; Client.Self.relativePosition = position; Client.Self.velocity = velocity; Client.Self.acceleration = acceleration; Client.Self.relativeRotation = rotation; Client.Self.angularVelocity = angularVelocity; #endregion } #region Create an Avatar from the decoded data Avatar avatar = GetAvatar(simulator, block.ID, block.FullID); uint oldSeatID = avatar.sittingOn; avatar.ID = block.FullID; avatar.LocalID = block.ID; avatar.CollisionPlane = collisionPlane; avatar.Position = position; avatar.Velocity = velocity; avatar.Acceleration = acceleration; avatar.Rotation = rotation; avatar.AngularVelocity = angularVelocity; avatar.NameValues = nameValues; avatar.Data = data; avatar.GenericData = block.Data; avatar.sittingOn = block.ParentID; SetAvatarSittingOn(simulator, avatar, block.ParentID, oldSeatID); // Set the current simulator for this avatar avatar.CurrentSim = simulator; // Textures avatar.Textures = new Primitive.TextureEntry(block.TextureEntry, 0, block.TextureEntry.Length); #endregion Create an Avatar from the decoded data FireOnNewAvatar(simulator, avatar, update.RegionData.RegionHandle, update.RegionData.TimeDilation); break; #endregion Avatar case PCode.ParticleSystem: DecodeParticleUpdate(block); // TODO: Create a callback for particle updates break; default: Client.DebugLog("Got an ObjectUpdate block with an unrecognized PCode " + pcode.ToString()); break; } } }