//Vector3 acceleration;
//Quaternion rotation;
//Vector3 angularVelocity;
NameValue[] nameValues;
boolean attachment = false;
PCode pcode = PCode.get(block.PCode);
//region Relevance check
// Check if we are interested in this object
if (!Client.settings.ALWAYS_DECODE_OBJECTS)
{
switch (pcode)
{
case Grass:
case Tree:
case NewTree:
case Prim:
JLogger.debug("Got a Grass, Tree, NewTree or Prim....");
if (onObjectUpdate == null) continue;
break;
case Avatar:
JLogger.debug("Got an Avatar...");
// Make an exception for updates about our own agent
if (!block.FullID.equals(Client.self.getAgentID()) && onAvatarUpdate == null) continue;
break;
case ParticleSystem:
JLogger.debug("Got an Partical System ... Going to Next Block");
continue; // TODO: Do something with these
}
}
//endregion Relevance check
//region NameValue parsing
String nameValue = Utils.bytesWithTrailingNullByteToString(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 (!Utils.isNullOrEmpty(lines[i]))
{
NameValue nv = new NameValue(lines[i]);
if (nv.Name.equals("AttachItemID")) attachment = true;
nameValues[i] = nv;
}
}
}
else
{
nameValues = new NameValue[0];
}
//JLogger.debug("Got NameValue String: " + nameValue + " $Parsed Values$ " + NameValue.NameValuesToString(nameValues));
//endregion NameValue parsing
//region Decode Object (primitive) parameters
ConstructionData data = new ConstructionData();
data.State = block.State;
data.Material = Material.get(block.Material);
data.PathCurve = PathCurve.get(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((byte)block.PathShearX);
data.PathShearY = Primitive.UnpackPathShear((byte)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;
int i = block.ObjectData.length;
//TODO following comment is only for debugging
JLogger.debug("ObjectUpdateHandler Got Object Length : " + i);
boolean search = true;
while(search)
{
search = false;
switch (i)
{
case 76:
// Collision normal for avatar
objectupdate.CollisionPlane = new Vector4(block.ObjectData, pos);
pos += 16;
//Keep on decoding
i = 60;
search = true;
break;
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;
//Keep on decoding
i = 32;
search = true;
break;
case 32:
// The data is an array of unsigned shorts
//TODO need to handle Little Endian Data
// 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)
//TODO need to handle Little Endian Data
// 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:
JLogger.warn("Unhandled: Got an ObjectUpdate block with ObjectUpdate field length of " +
block.ObjectData.length);
continue;
} //switch
}
//endregion
if(objectupdate.Position == null)
JLogger.warn("ObjectUpdateHandler: Object Position found to be null after update");
// Determine the object type and create the appropriate class
switch (pcode)
{
//region Prim and Foliage
case Grass:
case Tree:
case NewTree:
case Prim:
boolean isNewObject;
// lock (simulator.ObjectsPrimitives.Dictionary)
isNewObject = !simulator.ObjectsPrimitives.containsKey(block.ID);
final Primitive prim = GetPrimitive(simulator, block.ID, block.FullID);
// Textures
objectupdate.Textures = new TextureEntry(block.TextureEntry, 0,
block.TextureEntry.length);
onObjectDataBlockUpdate.raiseEvent(new ObjectDataBlockUpdateEventArgs(simulator, prim, data, block, objectupdate, nameValues));
//region Update Prim Info with decoded data
prim.Flags = block.UpdateFlags;
//JLogger.debug("Block UpdateFlags: " + block.UpdateFlags + Utils.bytesToHexDebugString(Utils.int64ToBytes(block.UpdateFlags), ""));
if ((prim.Flags & PrimFlags.ZlibCompressed.getIndex()) != 0)
{
JLogger.warn("Got a ZlibCompressed ObjectUpdate, implement me!");
continue;
}
// Automatically request ObjectProperties for prim if it was rezzed selected.
if (( prim.Flags & PrimFlags.CreateSelected.getIndex()) != 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.get(block.ClickAction);
prim.OwnerID = block.OwnerID;
prim.MediaURL = Utils.bytesWithTrailingNullByteToString(block.MediaURL);
prim.Text = Utils.bytesWithTrailingNullByteToString(block.Text);
prim.TextColor = new Color4(block.TextColor, 0, false, true);
prim.IsAttachment = attachment;
// Sound information
prim.Sound = block.Sound;
prim.SoundFlags = block.Flags;
prim.SoundGain = block.Gain;
prim.SoundRadius = block.Radius;
// Joint information
prim.Joint = JointType.get(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 TextureAnimation(block.TextureAnim, 0);
//FIXME need to fix the following exception
try{
prim.ParticleSys = new ParticleSystem(block.PSBlock, 0);
}
catch(Exception ex)
{
JLogger.warn("Error while decoding ParticleSystem from packet: "
+ Utils.getExceptionStackTraceAsString(ex));
}
prim.SetExtraParamsFromBytes(block.ExtraParams, 0);
// PCode-specific data
switch (pcode)
{
case Grass:
case Tree:
case NewTree:
if (block.Data.length == 1)
prim.TreeSpecies = Tree.get(block.Data[0]);
else
JLogger.warn("Got a foliage update with an invalid TreeSpecies field");
// 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;
JLogger.debug("Primitive " + prim.LocalID + " Position " + prim.Position.toString());
//endregion
final boolean isNewObject2 = isNewObject;
// EventHandler<PrimEventArgs> handler = m_ObjectUpdate;
if (onObjectUpdate != null)
{
final boolean attachment2 = attachment;
threadPool.execute(new Runnable(){
public void run()
{
onObjectUpdate.raiseEvent(new PrimEventArgs(simulator, prim, update.RegionData.TimeDilation, isNewObject2, attachment2));
}
});
// ThreadPool.QueueUserWorkItem(delegate(object o)
// { handler(this, new PrimEventArgs(simulator, prim, update.RegionData.TimeDilation, isNewObject, attachment)); });
}
break;
//endregion Prim and Foliage
//region Avatar
case Avatar:
boolean isNewAvatar;
// lock (simulator.ObjectsAvatars.Dictionary)
isNewAvatar = !simulator.ObjectsAvatars.containsKey(block.ID);
if(block.FullID.equals(UUID.Zero))
JLogger.warn(String.format("Received Avatar with Zero ID PCcode %d LocalID %d", block.PCode, block.ID));
// Update some internals if this is our avatar
if (block.FullID.equals(Client.self.getAgentID()) && simulator.equals(Client.network.getCurrentSim()))
{
//region Update Client.Self
// We need the local ID to recognize terse updates for our agent
Client.self.setLocalID(block.ID);
// Packed parameters
Client.self.setCollisionPlane(objectupdate.CollisionPlane);
Client.self.setRelativePosition(objectupdate.Position);
Client.self.setVelocity(objectupdate.Velocity);
Client.self.setAcceleration(objectupdate.Acceleration);
Client.self.setRelativeRotation(objectupdate.Rotation);
Client.self.setAngularVelocity(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 TextureEntry(block.TextureEntry, 0,
block.TextureEntry.length);
onObjectDataBlockUpdate.raiseEvent(new ObjectDataBlockUpdateEventArgs(simulator, avatar, data, block, objectupdate, nameValues));
long 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)
{
JLogger.warn("Unexpected Data field for an avatar update, length " + block.Data.length);
}
avatar.ParentID = block.ParentID;
avatar.RegionHandle = update.RegionData.RegionHandle;
JLogger.debug("Set an avatar: " + avatar.getName()
+ "\n with name values:\n" + NameValue.NameValuesToString(avatar.NameValues));
SetAvatarSittingOn(simulator, avatar, block.ParentID, oldSeatID);
// Textures
avatar.Textures = objectupdate.Textures;
//endregion Create an Avatar from the decoded data
onAvatarUpdate.raiseEvent(new AvatarUpdateEventArgs(simulator, avatar, update.RegionData.TimeDilation, isNewAvatar));
break;
//endregion Avatar
case ParticleSystem:
DecodeParticleUpdate(block);
// TODO: Create a callback for particle updates
break;
default:
JLogger.debug("Got an ObjectUpdate block with an unrecognized PCode " + pcode.toString());
break;
}
}
}