OpenMetaverse.ObjectManager.ObjectUpdateHandler C# (CSharp) Method

ObjectUpdateHandler() protected method

Process an incoming packet and raise the appropriate events
protected ObjectUpdateHandler ( object sender, OpenMetaverse.PacketReceivedEventArgs e ) : void
sender object The sender
e OpenMetaverse.PacketReceivedEventArgs The EventArgs object containing the packet data
return void
        protected void ObjectUpdateHandler(object sender, PacketReceivedEventArgs e)
        {
            Packet packet = e.Packet;
            Simulator simulator = e.Simulator;

            ObjectUpdatePacket update = (ObjectUpdatePacket)packet;
            UpdateDilation(e.Simulator, update.RegionData.TimeDilation);

            for (int b = 0; b < update.ObjectData.Length; b++)
            {
                ObjectUpdatePacket.ObjectDataBlock block = update.ObjectData[b];

                ObjectMovementUpdate objectupdate = new ObjectMovementUpdate();
                //Vector4 collisionPlane = Vector4.Zero;
                //Vector3 position;
                //Vector3 velocity;
                //Vector3 acceleration;
                //Quaternion rotation;
                //Vector3 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:
                        case PCode.Prim:
                            if (m_ObjectUpdate == null) continue;
                            break;
                        case PCode.Avatar:
                            // Make an exception for updates about our own agent
                            if (block.FullID != Client.Self.AgentID && m_AvatarUpdate == null) continue;
                            break;
                        case PCode.ParticleSystem:
                            continue; // TODO: Do something with these
                    }
                }

                #endregion Relevance check

                #region NameValue parsing

                string nameValue = Utils.BytesToString(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
                Primitive.ConstructionData data = new Primitive.ConstructionData();
                data.State = block.State;
                data.Material = (Material)block.Material;
                data.PathCurve = (PathCurve)block.PathCurve;
                data.profileCurve = block.ProfileCurve;
                data.PathBegin = Primitive.UnpackBeginCut(block.PathBegin);
                data.PathEnd = Primitive.UnpackEndCut(block.PathEnd);
                data.PathScaleX = Primitive.UnpackPathScale(block.PathScaleX);
                data.PathScaleY = Primitive.UnpackPathScale(block.PathScaleY);
                data.PathShearX = Primitive.UnpackPathShear((sbyte)block.PathShearX);
                data.PathShearY = Primitive.UnpackPathShear((sbyte)block.PathShearY);
                data.PathTwist = Primitive.UnpackPathTwist(block.PathTwist);
                data.PathTwistBegin = Primitive.UnpackPathTwist(block.PathTwistBegin);
                data.PathRadiusOffset = Primitive.UnpackPathTwist(block.PathRadiusOffset);
                data.PathTaperX = Primitive.UnpackPathTaper(block.PathTaperX);
                data.PathTaperY = Primitive.UnpackPathTaper(block.PathTaperY);
                data.PathRevolutions = Primitive.UnpackPathRevolutions(block.PathRevolutions);
                data.PathSkew = Primitive.UnpackPathTwist(block.PathSkew);
                data.ProfileBegin = Primitive.UnpackBeginCut(block.ProfileBegin);
                data.ProfileEnd = Primitive.UnpackEndCut(block.ProfileEnd);
                data.ProfileHollow = Primitive.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
                        objectupdate.CollisionPlane = new Vector4(block.ObjectData, pos);
                        pos += 16;

                        goto case 60;
                    case 60:
                        // Position
                        objectupdate.Position = new Vector3(block.ObjectData, pos);
                        pos += 12;
                        // Velocity
                        objectupdate.Velocity = new Vector3(block.ObjectData, pos);
                        pos += 12;
                        // Acceleration
                        objectupdate.Acceleration = new Vector3(block.ObjectData, pos);
                        pos += 12;
                        // Rotation (theta)
                        objectupdate.Rotation = new Quaternion(block.ObjectData, pos, true);
                        pos += 12;
                        // Angular velocity (omega)
                        objectupdate.AngularVelocity = new Vector3(block.ObjectData, pos);
                        pos += 12;

                        break;
                    case 48:
                        // Collision normal for avatar
                        objectupdate.CollisionPlane = new Vector4(block.ObjectData, pos);
                        pos += 16;

                        goto case 32;
                    case 32:
                        // The data is an array of unsigned shorts

                        // Position
                        objectupdate.Position = new Vector3(
                            Utils.UInt16ToFloat(block.ObjectData, pos, -0.5f * 256.0f, 1.5f * 256.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 2, -0.5f * 256.0f, 1.5f * 256.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 4, -256.0f, 3.0f * 256.0f));
                        pos += 6;
                        // Velocity
                        objectupdate.Velocity = new Vector3(
                            Utils.UInt16ToFloat(block.ObjectData, pos, -256.0f, 256.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 4, -256.0f, 256.0f));
                        pos += 6;
                        // Acceleration
                        objectupdate.Acceleration = new Vector3(
                            Utils.UInt16ToFloat(block.ObjectData, pos, -256.0f, 256.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 4, -256.0f, 256.0f));
                        pos += 6;
                        // Rotation (theta)
                        objectupdate.Rotation = new Quaternion(
                            Utils.UInt16ToFloat(block.ObjectData, pos, -1.0f, 1.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 2, -1.0f, 1.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 4, -1.0f, 1.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 6, -1.0f, 1.0f));
                        pos += 8;
                        // Angular velocity (omega)
                        objectupdate.AngularVelocity = new Vector3(
                            Utils.UInt16ToFloat(block.ObjectData, pos, -256.0f, 256.0f),
                            Utils.UInt16ToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f),
                            Utils.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
                        objectupdate.Position = new Vector3(
                            Utils.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f));
                        pos += 3;
                        // Velocity
                        objectupdate.Velocity = new Vector3(
                            Utils.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f));
                        pos += 3;
                        // Accleration
                        objectupdate.Acceleration = new Vector3(
                            Utils.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f));
                        pos += 3;
                        // Rotation
                        objectupdate.Rotation = new Quaternion(
                            Utils.ByteToFloat(block.ObjectData, pos, -1.0f, 1.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 1, -1.0f, 1.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 2, -1.0f, 1.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 3, -1.0f, 1.0f));
                        pos += 4;
                        // Angular Velocity
                        objectupdate.AngularVelocity = new Vector3(
                            Utils.ByteToFloat(block.ObjectData, pos, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 1, -256.0f, 256.0f),
                            Utils.ByteToFloat(block.ObjectData, pos + 2, -256.0f, 256.0f));
                        pos += 3;

                        break;
                    default:
                        Logger.Log("Got an ObjectUpdate block with ObjectUpdate field length of " +
                            block.ObjectData.Length, Helpers.LogLevel.Warning, Client);

                        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:

                        bool isNewObject;
                        lock (simulator.ObjectsPrimitives.Dictionary)
                            isNewObject = !simulator.ObjectsPrimitives.ContainsKey(block.ID);

                        Primitive prim = GetPrimitive(simulator, block.ID, block.FullID);

                        // Textures
                        objectupdate.Textures = new Primitive.TextureEntry(block.TextureEntry, 0,
                            block.TextureEntry.Length);

                        OnObjectDataBlockUpdate(new ObjectDataBlockUpdateEventArgs(simulator, prim, data, block, objectupdate, nameValues));

                        #region Update Prim Info with decoded data
                        prim.Flags = (PrimFlags)block.UpdateFlags;

                        if ((prim.Flags & PrimFlags.ZlibCompressed) != 0)
                        {
                            Logger.Log("Got a ZlibCompressed ObjectUpdate, implement me!",
                                Helpers.LogLevel.Warning, Client);
                            continue;
                        }

                        // Automatically request ObjectProperties for prim if it was rezzed selected.
                        if ((prim.Flags & PrimFlags.CreateSelected) != 0)
                        {
                            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 = Utils.BytesToString(block.MediaURL);
                        prim.Text = Utils.BytesToString(block.Text);
                        prim.TextColor = new Color4(block.TextColor, 0, false, true);
                        prim.IsAttachment = attachment;

                        // Sound information
                        prim.Sound = block.Sound;
                        prim.SoundFlags = (SoundFlags)block.Flags;
                        prim.SoundGain = block.Gain;
                        prim.SoundRadius = block.Radius;

                        // Joint information
                        prim.Joint = (JointType)block.JointType;
                        prim.JointPivot = block.JointPivot;
                        prim.JointAxisOrAnchor = block.JointAxisOrAnchor;

                        // Object parameters
                        prim.PrimData = data;

                        // Textures, texture animations, particle system, and extra params
                        prim.Textures = objectupdate.Textures;

                        prim.TextureAnim = new Primitive.TextureAnimation(block.TextureAnim, 0);
                        prim.ParticleSys = new Primitive.ParticleSystem(block.PSBlock, 0);
                        prim.SetExtraParamsFromBytes(block.ExtraParams, 0);

                        // PCode-specific data
                        switch (pcode)
                        {
                            case PCode.Grass:
                            case PCode.Tree:
                            case PCode.NewTree:
                                if (block.Data.Length == 1)
                                    prim.TreeSpecies = (Tree)block.Data[0];
                                else
                                    Logger.Log("Got a foliage update with an invalid TreeSpecies field", Helpers.LogLevel.Warning);
                                //    prim.ScratchPad = Utils.EmptyBytes;
                                //    break;
                                //default:
                                //    prim.ScratchPad = new byte[block.Data.Length];
                                //    if (block.Data.Length > 0)
                                //        Buffer.BlockCopy(block.Data, 0, prim.ScratchPad, 0, prim.ScratchPad.Length);
                                break;
                        }
                        prim.ScratchPad = Utils.EmptyBytes;

                        // Packed parameters
                        prim.CollisionPlane = objectupdate.CollisionPlane;
                        prim.Position = objectupdate.Position;
                        prim.Velocity = objectupdate.Velocity;
                        prim.Acceleration = objectupdate.Acceleration;
                        prim.Rotation = objectupdate.Rotation;
                        prim.AngularVelocity = objectupdate.AngularVelocity;
                        #endregion

                        EventHandler<PrimEventArgs> handler = m_ObjectUpdate;
                        if (handler != null)
                        {
                            WorkPool.QueueUserWorkItem(delegate(object o)
                            { handler(this, new PrimEventArgs(simulator, prim, update.RegionData.TimeDilation, isNewObject, attachment)); });
                        }
                        //OnParticleUpdate handler replacing decode particles, PCode.Particle system appears to be deprecated this is a fix
                        if (prim.ParticleSys.PartMaxAge != 0) {
                            OnParticleUpdate(new ParticleUpdateEventArgs(simulator, prim.ParticleSys, prim));
                        }

                        break;
                    #endregion Prim and Foliage
                    #region Avatar
                    case PCode.Avatar:

                        bool isNewAvatar;
                        lock (simulator.ObjectsAvatars.Dictionary)
                            isNewAvatar = !simulator.ObjectsAvatars.ContainsKey(block.ID);

                        // Update some internals if this is our avatar
                        if (block.FullID == Client.Self.AgentID && simulator == Client.Network.CurrentSim)
                        {
                            #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 = objectupdate.CollisionPlane;
                            Client.Self.relativePosition = objectupdate.Position;
                            Client.Self.velocity = objectupdate.Velocity;
                            Client.Self.acceleration = objectupdate.Acceleration;
                            Client.Self.relativeRotation = objectupdate.Rotation;
                            Client.Self.angularVelocity = objectupdate.AngularVelocity;

                            #endregion
                        }

                        #region Create an Avatar from the decoded data

                        Avatar avatar = GetAvatar(simulator, block.ID, block.FullID);

                        objectupdate.Avatar = true;
                        // Textures
                        objectupdate.Textures = new Primitive.TextureEntry(block.TextureEntry, 0,
                            block.TextureEntry.Length);

                        OnObjectDataBlockUpdate(new ObjectDataBlockUpdateEventArgs(simulator, avatar, data, block, objectupdate, nameValues));

                        uint oldSeatID = avatar.ParentID;

                        avatar.ID = block.FullID;
                        avatar.LocalID = block.ID;
                        avatar.Scale = block.Scale;
                        avatar.CollisionPlane = objectupdate.CollisionPlane;
                        avatar.Position = objectupdate.Position;
                        avatar.Velocity = objectupdate.Velocity;
                        avatar.Acceleration = objectupdate.Acceleration;
                        avatar.Rotation = objectupdate.Rotation;
                        avatar.AngularVelocity = objectupdate.AngularVelocity;
                        avatar.NameValues = nameValues;
                        avatar.PrimData = data;
                        if (block.Data.Length > 0)
                        {
                            Logger.Log("Unexpected Data field for an avatar update, length " + block.Data.Length, Helpers.LogLevel.Warning);
                        }
                        avatar.ParentID = block.ParentID;
                        avatar.RegionHandle = update.RegionData.RegionHandle;

                        SetAvatarSittingOn(simulator, avatar, block.ParentID, oldSeatID);

                        // Textures
                        avatar.Textures = objectupdate.Textures;

                        #endregion Create an Avatar from the decoded data

                        OnAvatarUpdate(new AvatarUpdateEventArgs(simulator, avatar, update.RegionData.TimeDilation, isNewAvatar));

                        break;
                    #endregion Avatar
                    case PCode.ParticleSystem:
                        DecodeParticleUpdate(block);
                        break;
                    default:
                        Logger.DebugLog("Got an ObjectUpdate block with an unrecognized PCode " + pcode.ToString(), Client);
                        break;
                }
            }
        }