private void CompressedUpdateHandler(Packet packet, Simulator simulator) { ObjectUpdateCompressedPacket update = (ObjectUpdateCompressedPacket)packet; PrimObject prim; foreach (ObjectUpdateCompressedPacket.ObjectDataBlock block in update.ObjectData) { int i = 0; prim = new PrimObject(Client); try { prim.ID = new LLUUID(block.Data, 0); i += 16; prim.LocalID = (uint)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); byte pcode = block.Data[i++]; if (pcode == (byte)PCode.Prim) { #region PrimRegion prim.State = (uint)block.Data[i++]; i += 4; // CRC prim.Material = (uint)block.Data[i++]; i++; // TODO: ClickAction prim.Scale = new LLVector3(block.Data, i); i += 12; prim.Position = new LLVector3(block.Data, i); i += 12; prim.Rotation = new LLQuaternion(block.Data, i, true); i += 12; uint flags = (uint)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); if ((flags & 0x02) != 0) { byte TreeData = block.Data[i++]; // TODO: Unknown byte i++; if (OnNewPrim != null) { OnNewPrim(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); } continue; } if ((flags & 0x20) != 0) { prim.ParentID = (uint)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); } else { prim.ParentID = 0; } if ((flags & 0x80) != 0) { // TODO: Use this. What is it? LLVector3 Omega = new LLVector3(block.Data, i); i += 12; } if ((flags & 0x04) != 0) { string text = ""; while (block.Data[i] != 0) { text += (char)block.Data[i]; i++; } prim.Text = text; i++; // Text color i += 4; } else { prim.Text = ""; } if ((flags & 0x08) != 0) { prim.ParticleSys = new ParticleSystem(block.Data, i); i += 86; } i += prim.SetExtraParamsFromBytes(block.Data, i); //Sound data if ((flags & 0x10) != 0) { //TODO: use this data LLUUID SoundUUID = new LLUUID(block.Data, i); i += 16; LLUUID OwnerUUID = new LLUUID(block.Data, i); i += 16; if (!BitConverter.IsLittleEndian) { Array.Reverse(block.Data, i, 4); Array.Reverse(block.Data, i + 5, 4); } float SoundGain = BitConverter.ToSingle(block.Data, i); i += 4; byte SoundFlags = block.Data[i++]; float SoundRadius = BitConverter.ToSingle(block.Data, i); i += 4; } //Indicates that this is an attachment? if ((flags & 0x100) != 0) { //A string //Example: "AttachItemID STRING RW SV fa9a5ab8-1bad-b449-9873-cf5b803e664e" while (block.Data[i] != 0) { i++; } i++; } prim.PathCurve = (uint)block.Data[i++]; prim.PathBegin = PrimObject.PathBeginFloat(block.Data[i++]); prim.PathEnd = PrimObject.PathEndFloat(block.Data[i++]); prim.PathTaperX = PrimObject.PathScaleFloat(block.Data[i++]); prim.PathTaperY = PrimObject.PathScaleFloat(block.Data[i++]); prim.PathShearX = PrimObject.PathShearFloat(block.Data[i++]); prim.PathShearY = PrimObject.PathShearFloat(block.Data[i++]); prim.PathTwist = (int)block.Data[i++]; prim.PathTwistBegin = (int)block.Data[i++]; prim.PathRadiusOffset = PrimObject.PathRadiusOffsetFloat((sbyte)block.Data[i++]); //prim.PathTaperX = PrimObject.PathTaperFloat(block.Data[i++]); //prim.PathTaperY = PrimObject.PathTaperFloat(block.Data[i++]); i += 2; prim.PathRevolutions = PrimObject.PathRevolutionsFloat(block.Data[i++]); prim.PathSkew = PrimObject.PathSkewFloat(block.Data[i++]); prim.ProfileCurve = (uint)block.Data[i++]; prim.ProfileBegin = PrimObject.ProfileBeginFloat(block.Data[i++]); prim.ProfileEnd = PrimObject.ProfileEndFloat(block.Data[i++]); prim.ProfileHollow = (uint)block.Data[i++]; int textureEntryLength = (int)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); prim.Textures = new TextureEntry(block.Data, i, textureEntryLength); i += textureEntryLength; if (i < block.Data.Length) { int textureAnimLength = (int)(block.Data[i++] + (block.Data[i++] << 8) + (block.Data[i++] << 16) + (block.Data[i++] << 24)); prim.TextureAnim = new TextureAnimation(block.Data, i); } if (OnNewPrim != null) { OnNewPrim(simulator, prim, update.RegionData.RegionHandle, update.RegionData.TimeDilation); } #endregion PrimRegion } else if (pcode == (byte)PCode.Avatar) { Client.Log("######### Got an ObjectUpdateCompressed for an avatar, implement this! #########", Helpers.LogLevel.Warning); } else if (pcode == (byte)PCode.Grass || pcode == (byte)PCode.Tree) { // TODO: Add new_tree and any other tree-like prims ; } else { // TODO: ... continue; } } catch (System.IndexOutOfRangeException e) { Client.Log("Had a problem decoding an ObjectUpdateCompressed packet: " + e.ToString(), Helpers.LogLevel.Warning); Client.Log(block.ToString(), Helpers.LogLevel.Warning); } } }
void SendObjectPacket(SimulationObject obj, bool canUseCompressed, bool canUseImproved, PrimFlags creatorFlags, UpdateFlags updateFlags) { if (!canUseImproved && !canUseCompressed) { #region ObjectUpdate Logger.DebugLog("Sending ObjectUpdate"); if (sceneAgents.ContainsKey(obj.Prim.OwnerID)) { // Send an update out to the creator ObjectUpdatePacket updateToOwner = new ObjectUpdatePacket(); updateToOwner.RegionData.RegionHandle = regionHandle; updateToOwner.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); updateToOwner.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; updateToOwner.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner, obj.CRC); udp.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State); } // Send an update out to everyone else ObjectUpdatePacket updateToOthers = new ObjectUpdatePacket(); updateToOthers.RegionData.RegionHandle = regionHandle; updateToOthers.RegionData.TimeDilation = UInt16.MaxValue; updateToOthers.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; updateToOthers.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags, obj.CRC); ForEachAgent( delegate(Agent recipient) { if (recipient.ID != obj.Prim.OwnerID) udp.SendPacket(recipient.ID, updateToOthers, PacketCategory.State); } ); #endregion ObjectUpdate } else if (!canUseImproved) { #region ObjectUpdateCompressed #region Size calculation and field serialization CompressedFlags flags = 0; int size = 84; byte[] textBytes = null; byte[] mediaURLBytes = null; byte[] particleBytes = null; byte[] extraParamBytes = null; byte[] nameValueBytes = null; byte[] textureBytes = null; byte[] textureAnimBytes = null; if ((updateFlags & UpdateFlags.AngularVelocity) != 0) { flags |= CompressedFlags.HasAngularVelocity; size += 12; } if ((updateFlags & UpdateFlags.ParentID) != 0) { flags |= CompressedFlags.HasParent; size += 4; } if ((updateFlags & UpdateFlags.ScratchPad) != 0) { switch (obj.Prim.PrimData.PCode) { case PCode.Grass: case PCode.Tree: case PCode.NewTree: flags |= CompressedFlags.Tree; size += 2; // Size byte plus one byte break; default: flags |= CompressedFlags.ScratchPad; size += 1 + obj.Prim.ScratchPad.Length; // Size byte plus bytes break; } } if ((updateFlags & UpdateFlags.Text) != 0) { flags |= CompressedFlags.HasText; textBytes = Utils.StringToBytes(obj.Prim.Text); size += textBytes.Length; // Null-terminated, no size byte size += 4; // Text color } if ((updateFlags & UpdateFlags.MediaURL) != 0) { flags |= CompressedFlags.MediaURL; mediaURLBytes = Utils.StringToBytes(obj.Prim.MediaURL); size += mediaURLBytes.Length; // Null-terminated, no size byte } if ((updateFlags & UpdateFlags.Particles) != 0) { flags |= CompressedFlags.HasParticles; particleBytes = obj.Prim.ParticleSys.GetBytes(); size += particleBytes.Length; // Should be exactly 86 bytes } // Extra Params extraParamBytes = obj.Prim.GetExtraParamsBytes(); size += extraParamBytes.Length; if ((updateFlags & UpdateFlags.Sound) != 0) { flags |= CompressedFlags.HasSound; size += 25; // SoundID, SoundGain, SoundFlags, SoundRadius } if ((updateFlags & UpdateFlags.NameValue) != 0) { flags |= CompressedFlags.HasNameValues; nameValueBytes = Utils.StringToBytes(NameValue.NameValuesToString(obj.Prim.NameValues)); size += nameValueBytes.Length; // Null-terminated, no size byte } size += 23; // PrimData size += 4; // Texture Length textureBytes = obj.Prim.Textures.GetBytes(); size += textureBytes.Length; // Texture Entry if ((updateFlags & UpdateFlags.TextureAnim) != 0) { flags |= CompressedFlags.TextureAnimation; size += 4; // TextureAnim Length textureAnimBytes = obj.Prim.TextureAnim.GetBytes(); size += textureAnimBytes.Length; // TextureAnim } #endregion Size calculation and field serialization #region Packet serialization int pos = 0; byte[] data = new byte[size]; // UUID obj.Prim.ID.ToBytes(data, 0); pos += 16; // LocalID Utils.UIntToBytes(obj.Prim.LocalID, data, pos); pos += 4; // PCode data[pos++] = (byte)obj.Prim.PrimData.PCode; // State data[pos++] = obj.Prim.PrimData.State; // CRC Utils.UIntToBytes(obj.CRC, data, pos); pos += 4; // Material data[pos++] = (byte)obj.Prim.PrimData.Material; // ClickAction data[pos++] = (byte)obj.Prim.ClickAction; // Scale obj.Prim.Scale.ToBytes(data, pos); pos += 12; // Position obj.Prim.Position.ToBytes(data, pos); pos += 12; // Rotation obj.Prim.Rotation.ToBytes(data, pos); pos += 12; // Compressed Flags Utils.UIntToBytes((uint)flags, data, pos); pos += 4; // OwnerID obj.Prim.OwnerID.ToBytes(data, pos); pos += 16; if ((flags & CompressedFlags.HasAngularVelocity) != 0) { obj.Prim.AngularVelocity.ToBytes(data, pos); pos += 12; } if ((flags & CompressedFlags.HasParent) != 0) { Utils.UIntToBytes(obj.Prim.ParentID, data, pos); pos += 4; } if ((flags & CompressedFlags.ScratchPad) != 0) { data[pos++] = (byte)obj.Prim.ScratchPad.Length; Buffer.BlockCopy(obj.Prim.ScratchPad, 0, data, pos, obj.Prim.ScratchPad.Length); pos += obj.Prim.ScratchPad.Length; } else if ((flags & CompressedFlags.Tree) != 0) { data[pos++] = 1; data[pos++] = (byte)obj.Prim.TreeSpecies; } if ((flags & CompressedFlags.HasText) != 0) { Buffer.BlockCopy(textBytes, 0, data, pos, textBytes.Length); pos += textBytes.Length; obj.Prim.TextColor.ToBytes(data, pos, false); pos += 4; } if ((flags & CompressedFlags.MediaURL) != 0) { Buffer.BlockCopy(mediaURLBytes, 0, data, pos, mediaURLBytes.Length); pos += mediaURLBytes.Length; } if ((flags & CompressedFlags.HasParticles) != 0) { Buffer.BlockCopy(particleBytes, 0, data, pos, particleBytes.Length); pos += particleBytes.Length; } // Extra Params Buffer.BlockCopy(extraParamBytes, 0, data, pos, extraParamBytes.Length); pos += extraParamBytes.Length; if ((flags & CompressedFlags.HasSound) != 0) { obj.Prim.Sound.ToBytes(data, pos); pos += 16; Utils.FloatToBytes(obj.Prim.SoundGain, data, pos); pos += 4; data[pos++] = (byte)obj.Prim.SoundFlags; Utils.FloatToBytes(obj.Prim.SoundRadius, data, pos); pos += 4; } if ((flags & CompressedFlags.HasNameValues) != 0) { Buffer.BlockCopy(nameValueBytes, 0, data, pos, nameValueBytes.Length); pos += nameValueBytes.Length; } // Path PrimData data[pos++] = (byte)obj.Prim.PrimData.PathCurve; Utils.UInt16ToBytes(Primitive.PackBeginCut(obj.Prim.PrimData.PathBegin), data, pos); pos += 2; Utils.UInt16ToBytes(Primitive.PackEndCut(obj.Prim.PrimData.PathEnd), data, pos); pos += 2; data[pos++] = Primitive.PackPathScale(obj.Prim.PrimData.PathScaleX); data[pos++] = Primitive.PackPathScale(obj.Prim.PrimData.PathScaleY); data[pos++] = (byte)Primitive.PackPathShear(obj.Prim.PrimData.PathShearX); data[pos++] = (byte)Primitive.PackPathShear(obj.Prim.PrimData.PathShearY); data[pos++] = (byte)Primitive.PackPathTwist(obj.Prim.PrimData.PathTwist); data[pos++] = (byte)Primitive.PackPathTwist(obj.Prim.PrimData.PathTwistBegin); data[pos++] = (byte)Primitive.PackPathTwist(obj.Prim.PrimData.PathRadiusOffset); data[pos++] = (byte)Primitive.PackPathTaper(obj.Prim.PrimData.PathTaperX); data[pos++] = (byte)Primitive.PackPathTaper(obj.Prim.PrimData.PathTaperY); data[pos++] = Primitive.PackPathRevolutions(obj.Prim.PrimData.PathRevolutions); data[pos++] = (byte)Primitive.PackPathTwist(obj.Prim.PrimData.PathSkew); // Profile PrimData data[pos++] = obj.Prim.PrimData.profileCurve; Utils.UInt16ToBytes(Primitive.PackBeginCut(obj.Prim.PrimData.ProfileBegin), data, pos); pos += 2; Utils.UInt16ToBytes(Primitive.PackEndCut(obj.Prim.PrimData.ProfileEnd), data, pos); pos += 2; Utils.UInt16ToBytes(Primitive.PackProfileHollow(obj.Prim.PrimData.ProfileHollow), data, pos); pos += 2; // Texture Length Utils.UIntToBytes((uint)textureBytes.Length, data, pos); pos += 4; // Texture Entry Buffer.BlockCopy(textureBytes, 0, data, pos, textureBytes.Length); pos += textureBytes.Length; if ((flags & CompressedFlags.TextureAnimation) != 0) { Utils.UIntToBytes((uint)textureAnimBytes.Length, data, pos); pos += 4; Buffer.BlockCopy(textureAnimBytes, 0, data, pos, textureAnimBytes.Length); pos += textureAnimBytes.Length; } #endregion Packet serialization #region Packet sending //Logger.DebugLog("Sending ObjectUpdateCompressed with " + flags.ToString()); if (sceneAgents.ContainsKey(obj.Prim.OwnerID)) { // Send an update out to the creator ObjectUpdateCompressedPacket updateToOwner = new ObjectUpdateCompressedPacket(); updateToOwner.RegionData.RegionHandle = regionHandle; updateToOwner.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); updateToOwner.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[1]; updateToOwner.ObjectData[0] = new ObjectUpdateCompressedPacket.ObjectDataBlock(); updateToOwner.ObjectData[0].UpdateFlags = (uint)(obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner); updateToOwner.ObjectData[0].Data = data; udp.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State); } // Send an update out to everyone else ObjectUpdateCompressedPacket updateToOthers = new ObjectUpdateCompressedPacket(); updateToOthers.RegionData.RegionHandle = regionHandle; updateToOthers.RegionData.TimeDilation = UInt16.MaxValue; updateToOthers.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[1]; updateToOthers.ObjectData[0] = new ObjectUpdateCompressedPacket.ObjectDataBlock(); updateToOthers.ObjectData[0].UpdateFlags = (uint)obj.Prim.Flags; updateToOthers.ObjectData[0].Data = data; ForEachAgent( delegate(Agent recipient) { if (recipient.ID != obj.Prim.OwnerID) udp.SendPacket(recipient.ID, updateToOthers, PacketCategory.State); } ); #endregion Packet sending #endregion ObjectUpdateCompressed } else { #region ImprovedTerseObjectUpdate //Logger.DebugLog("Sending ImprovedTerseObjectUpdate"); int pos = 0; byte[] data = new byte[(obj.Prim is Avatar ? 60 : 44)]; // LocalID Utils.UIntToBytes(obj.Prim.LocalID, data, pos); pos += 4; // Avatar/CollisionPlane data[pos++] = obj.Prim.PrimData.State; if (obj.Prim is Avatar) { data[pos++] = 1; obj.Prim.CollisionPlane.ToBytes(data, pos); pos += 16; } else { ++pos; } // Position obj.Prim.Position.ToBytes(data, pos); pos += 12; // Velocity Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Velocity.X, -128.0f, 128.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Velocity.Y, -128.0f, 128.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Velocity.Z, -128.0f, 128.0f), data, pos); pos += 2; // Acceleration Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Acceleration.X, -64.0f, 64.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Acceleration.Y, -64.0f, 64.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Acceleration.Z, -64.0f, 64.0f), data, pos); pos += 2; // Rotation Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.X, -1.0f, 1.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.Y, -1.0f, 1.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.Z, -1.0f, 1.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.W, -1.0f, 1.0f), data, pos); pos += 2; // Angular Velocity Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.AngularVelocity.X, -64.0f, 64.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.AngularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.AngularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2; ImprovedTerseObjectUpdatePacket update = new ImprovedTerseObjectUpdatePacket(); update.RegionData.RegionHandle = RegionHandle; update.RegionData.TimeDilation = (ushort)(timeDilation * (float)UInt16.MaxValue); update.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; update.ObjectData[0] = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); update.ObjectData[0].Data = data; if ((updateFlags & UpdateFlags.Textures) != 0) { byte[] textureBytes = obj.Prim.Textures.GetBytes(); byte[] textureEntry = new byte[textureBytes.Length + 4]; // Texture Length Utils.IntToBytes(textureBytes.Length, textureEntry, 0); // Texture Buffer.BlockCopy(textureBytes, 0, textureEntry, 4, textureBytes.Length); update.ObjectData[0].TextureEntry = textureEntry; } else { update.ObjectData[0].TextureEntry = Utils.EmptyBytes; } udp.BroadcastPacket(update, PacketCategory.State); #endregion ImprovedTerseObjectUpdate } }
private void SendEntityPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>> compressedUpdateBlocks = new Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>>(); Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); for (int i = 0; i < eventDatas.Length; i++) { EntityAddOrUpdateArgs e = (EntityAddOrUpdateArgs)eventDatas[i].Event.State; ISceneEntity entity = e.Entity; #region UpdateFlags to packet type conversion UpdateFlags updateFlags = e.UpdateFlags; LLUpdateFlags llUpdateFlags = (LLUpdateFlags)e.ExtraFlags; bool canUseImproved = true; if (updateFlags.HasFlag(UpdateFlags.FullUpdate) || updateFlags.HasFlag(UpdateFlags.Parent) || updateFlags.HasFlag(UpdateFlags.Scale) || updateFlags.HasFlag(UpdateFlags.Shape) || llUpdateFlags.HasFlag(LLUpdateFlags.PrimFlags) || llUpdateFlags.HasFlag(LLUpdateFlags.Text) || llUpdateFlags.HasFlag(LLUpdateFlags.NameValue) || llUpdateFlags.HasFlag(LLUpdateFlags.ExtraData) || llUpdateFlags.HasFlag(LLUpdateFlags.TextureAnim) || llUpdateFlags.HasFlag(LLUpdateFlags.Sound) || llUpdateFlags.HasFlag(LLUpdateFlags.Particles) || llUpdateFlags.HasFlag(LLUpdateFlags.Material) || llUpdateFlags.HasFlag(LLUpdateFlags.ClickAction) || llUpdateFlags.HasFlag(LLUpdateFlags.MediaURL) || llUpdateFlags.HasFlag(LLUpdateFlags.Joint)) { canUseImproved = false; } #endregion UpdateFlags to packet type conversion #region Block Construction if (!canUseImproved) { if (entity is IScenePresence) objectUpdateBlocks.Value.Add(CreateAvatarObjectUpdateBlock((IScenePresence)entity, presence)); else objectUpdateBlocks.Value.Add(CreateObjectUpdateBlock(entity, presence)); } else { terseUpdateBlocks.Value.Add(CreateTerseUpdateBlock(entity, llUpdateFlags.HasFlag(LLUpdateFlags.Textures))); } #endregion Block Construction // Unset CreateSelected after it has been sent once if (entity is LLPrimitive) { LLPrimitive prim = (LLPrimitive)entity; prim.Prim.Flags &= ~PrimFlags.CreateSelected; } } #region Packet Sending ushort timeDilation = (m_physics != null) ? Utils.FloatToUInt16(m_physics.TimeDilation, 0.0f, 1.0f) : UInt16.MaxValue; if (objectUpdateBlocks.IsValueCreated) { List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value; ObjectUpdatePacket packet = new ObjectUpdatePacket(); packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition); packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count]; for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); } if (compressedUpdateBlocks.IsValueCreated) { List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value; ObjectUpdateCompressedPacket packet = new ObjectUpdateCompressedPacket(); packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition); packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count]; for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); } if (terseUpdateBlocks.IsValueCreated) { List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition); packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); } #endregion Packet Sending }
private void SendEntityPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>> compressedUpdateBlocks = new Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>>(); Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); Lazy<List<ObjectUpdateCachedPacket.ObjectDataBlock>> cachedUpdateBlocks = new Lazy<List<ObjectUpdateCachedPacket.ObjectDataBlock>>(); for (int i = 0; i < eventDatas.Length; i++) { EntityAddOrUpdateArgs e = (EntityAddOrUpdateArgs)eventDatas[i].Event.State; ISceneEntity entity = e.Entity; #region Determine packet type UpdateFlags updateFlags = e.UpdateFlags; LLUpdateFlags llUpdateFlags = (LLUpdateFlags)e.ExtraFlags; LLPrimitive prim = entity as LLPrimitive; bool canUseCached = false; bool canUseTerse = true; DateTime lastSeen; if (CACHE_CHECK_ENABLED && prim != null && updateFlags.HasFlag(UpdateFlags.FullUpdate) && !llUpdateFlags.HasFlag(LLUpdateFlags.NoCachedUpdate) && m_recentAvatars.TryGetValue(presence.ID, out lastSeen) && lastSeen > prim.LastUpdated) { // This avatar was marked as leaving the same later than the last update // timestamp of this prim. Send a cache check canUseCached = true; } else if (updateFlags.HasFlag(UpdateFlags.FullUpdate) || updateFlags.HasFlag(UpdateFlags.Parent) || updateFlags.HasFlag(UpdateFlags.Scale) || updateFlags.HasFlag(UpdateFlags.Shape) || llUpdateFlags.HasFlag(LLUpdateFlags.PrimFlags) || llUpdateFlags.HasFlag(LLUpdateFlags.Text) || llUpdateFlags.HasFlag(LLUpdateFlags.NameValue) || llUpdateFlags.HasFlag(LLUpdateFlags.ExtraData) || llUpdateFlags.HasFlag(LLUpdateFlags.TextureAnim) || llUpdateFlags.HasFlag(LLUpdateFlags.Sound) || llUpdateFlags.HasFlag(LLUpdateFlags.Particles) || llUpdateFlags.HasFlag(LLUpdateFlags.Material) || llUpdateFlags.HasFlag(LLUpdateFlags.ClickAction) || llUpdateFlags.HasFlag(LLUpdateFlags.MediaURL) || llUpdateFlags.HasFlag(LLUpdateFlags.Joint)) { canUseTerse = false; } #endregion Determine packet type #region Block Construction if (canUseCached && prim != null) { cachedUpdateBlocks.Value.Add(new ObjectUpdateCachedPacket.ObjectDataBlock { CRC = prim.GetCrc(), ID = prim.LocalID }); } else if (!canUseTerse) { if (entity is IScenePresence) { IScenePresence thisPresence = (IScenePresence)entity; ObjectUpdatePacket.ObjectDataBlock block = CreateAvatarObjectUpdateBlock(thisPresence); block.UpdateFlags = (uint)GetUpdateFlags(thisPresence, presence); objectUpdateBlocks.Value.Add(block); } else if (prim != null) { ObjectUpdateCompressedPacket.ObjectDataBlock block = CreateCompressedObjectUpdateBlock(prim, prim.GetCrc()); block.UpdateFlags = (uint)GetUpdateFlags(prim, presence, m_permissions); compressedUpdateBlocks.Value.Add(block); // ObjectUpdateCompressed doesn't carry velocity or acceleration fields, so // we need to send a separate terse packet if this prim has a non-zero // velocity or acceleration if (prim.Velocity != Vector3.Zero || prim.Acceleration != Vector3.Zero) terseUpdateBlocks.Value.Add(CreateTerseUpdateBlock(entity, false)); //ObjectUpdatePacket.ObjectDataBlock block = CreateObjectUpdateBlock(prim); //block.UpdateFlags = (uint)GetUpdateFlags(prim, presence, m_permissions); //block.CRC = prim.GetCrc(); //objectUpdateBlocks.Value.Add(block); } else { // TODO: Create a generic representation for non-LLPrimitive entities? continue; } } else { terseUpdateBlocks.Value.Add(CreateTerseUpdateBlock(entity, llUpdateFlags.HasFlag(LLUpdateFlags.Textures))); } #endregion Block Construction // Unset CreateSelected after it has been sent once if (prim != null) prim.Prim.Flags &= ~PrimFlags.CreateSelected; } #region Packet Sending ushort timeDilation = (m_physics != null) ? Utils.FloatToUInt16(m_physics.TimeDilation, 0.0f, 1.0f) : UInt16.MaxValue; if (objectUpdateBlocks.IsValueCreated) { List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value; ObjectUpdatePacket packet = new ObjectUpdatePacket(); packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition); packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count]; for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); } if (compressedUpdateBlocks.IsValueCreated) { List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value; ObjectUpdateCompressedPacket packet = new ObjectUpdateCompressedPacket(); packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition); packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count]; for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); } if (terseUpdateBlocks.IsValueCreated) { List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition); packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); } if (cachedUpdateBlocks.IsValueCreated) { List<ObjectUpdateCachedPacket.ObjectDataBlock> blocks = cachedUpdateBlocks.Value; ObjectUpdateCachedPacket packet = new ObjectUpdateCachedPacket(); packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition); packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ObjectUpdateCachedPacket.ObjectDataBlock[blocks.Count]; for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); } #endregion Packet Sending }