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;
}
}
}