private void SendTypingPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; // We can't combine chat blocks together, so send a packet for each typing event // that is pulled off the event queue for (int i = 0; i < eventDatas.Length; i++) { TypingData data = (TypingData)eventDatas[i].Event.State; ChatAudibleLevel audible = GetAudibleLevel(data.Source.ScenePosition, presence.ScenePosition, NORMAL_DIST); ChatFromSimulatorPacket packet = new ChatFromSimulatorPacket(); packet.ChatData.Audible = (byte)audible; packet.ChatData.ChatType = (byte)(data.StartTyping ? ChatType.StartTyping : ChatType.StopTyping); packet.ChatData.FromName = Utils.StringToBytes(data.Source.Name); packet.ChatData.Message = Utils.EmptyBytes; packet.ChatData.OwnerID = data.Source.OwnerID; packet.ChatData.Position = data.Source.ScenePosition; packet.ChatData.SourceID = data.Source.ID; packet.ChatData.SourceType = (byte)ChatSourceType.Agent; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, false); } }
private void SendTerrainPacket(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { const int PATCHES_PER_PACKET = 3; if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; List<int> patches = new List<int>(PATCHES_PER_PACKET); for (int i = 0; i < eventDatas.Length; i++) { int[] state = (int[])eventDatas[i].Event.State; int x = state[0]; int y = state[1]; patches.Add(y * 16 + x); if (patches.Count == PATCHES_PER_PACKET || i == eventDatas.Length - 1) { LayerDataPacket packet = TerrainCompressor.CreateLandPacket(m_terrain.GetHeightmap(), patches.ToArray()); m_udp.SendPacket(agent, packet, ThrottleCategory.Land, false); patches = new List<int>(PATCHES_PER_PACKET); } } }
private void SendViewerEffectPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; ViewerEffectPacket packet = new ViewerEffectPacket(); packet.Header.Reliable = false; packet.AgentData.AgentID = presence.ID; packet.Effect = new ViewerEffectPacket.EffectBlock[eventDatas.Length]; for (int i = 0; i < eventDatas.Length; i++) packet.Effect[i] = (ViewerEffectPacket.EffectBlock)eventDatas[i].Event.State; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true); }
private void SendChatPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; // We can't combine chat blocks together, so send a packet for each chat event // that is pulled off the event queue for (int i = 0; i < eventDatas.Length; i++) { ChatArgs data = (ChatArgs)eventDatas[i].Event.State; ChatAudibleLevel audible; ChatType type; string sourceName; ChatSourceType sourceType; string message; ChatFromSimulatorPacket packet = new ChatFromSimulatorPacket(); if (data.Source == null) { // System message audible = ChatAudibleLevel.Fully; type = ChatType.Normal; sourceName = m_scene.Name; message = data.Message; sourceType = ChatSourceType.System; packet.ChatData.FromName = Utils.StringToBytes(m_scene.Name); packet.ChatData.OwnerID = UUID.Zero; packet.ChatData.Position = Vector3.Zero; packet.ChatData.SourceID = m_scene.ID; } else { // Message from an agent or object sourceName = data.Source.Name; switch (data.Type) { case EntityChatType.Debug: type = ChatType.Debug; audible = ChatAudibleLevel.Fully; break; case EntityChatType.Owner: type = ChatType.OwnerSay; audible = ChatAudibleLevel.Fully; break; case EntityChatType.Broadcast: type = ChatType.Normal; audible = ChatAudibleLevel.Fully; break; default: type = GetChatType(data.AudibleDistance); audible = GetAudibleLevel(data.Source.ScenePosition, presence.ScenePosition, data.AudibleDistance); break; } if (audible == ChatAudibleLevel.Fully) message = data.Message; else message = String.Empty; if (data.Source is IScenePresence) sourceType = ChatSourceType.Agent; else sourceType = ChatSourceType.Object; packet.ChatData.FromName = Utils.StringToBytes(data.Source.Name); packet.ChatData.OwnerID = data.Source.OwnerID; packet.ChatData.Position = data.Source.ScenePosition; packet.ChatData.SourceID = data.Source.ID; } packet.ChatData.Audible = (byte)audible; packet.ChatData.ChatType = (byte)type; packet.ChatData.Message = Utils.StringToBytes(message); packet.ChatData.SourceType = (byte)sourceType; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, false); } }
private void SendAvatarAppearancePackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; for (int i = 0; i < eventDatas.Length; i++) { IScenePresence curPresence = (IScenePresence)eventDatas[i].Event.State; if (curPresence == presence) { m_log.Warn("Attempted to send an AvatarAppearance packet to the controlling agent"); continue; } AvatarAppearancePacket appearance = new AvatarAppearancePacket(); appearance.Sender.ID = curPresence.ID; appearance.Sender.IsTrial = false; Primitive.TextureEntry textureEntry = null; byte[] visualParams = null; if (curPresence is LLAgent) { LLAgent curAgent = (LLAgent)curPresence; // If this agent has not set VisualParams yet, skip it if (curAgent.VisualParams == null) continue; textureEntry = curAgent.TextureEntry; visualParams = curAgent.VisualParams; } if (textureEntry == null) { // Use a default texture entry for this avatar textureEntry = DEFAULT_TEXTURE_ENTRY; } if (visualParams == null) { // Use default visual params for this avatar visualParams = DEFAULT_VISUAL_PARAMS; } appearance.ObjectData.TextureEntry = textureEntry.GetBytes(); appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[visualParams.Length]; for (int j = 0; j < visualParams.Length; j++) { appearance.VisualParam[j] = new AvatarAppearancePacket.VisualParamBlock(); appearance.VisualParam[j].ParamValue = visualParams[j]; } m_udp.SendPacket(agent, appearance, ThrottleCategory.Task, false); } }
private void SendAvatarAnimationPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; for (int i = 0; i < eventDatas.Length; i++) { LLAgent animAgent = (LLAgent)eventDatas[i].Event.State; AvatarAnimationPacket packet = new AvatarAnimationPacket(); packet.Sender.ID = animAgent.ID; Animation[] animations = animAgent.Animations.GetAnimations(); packet.AnimationList = new AvatarAnimationPacket.AnimationListBlock[animations.Length]; for (int j = 0; j < animations.Length; j++) { Animation animation = animations[j]; packet.AnimationList[j] = new AvatarAnimationPacket.AnimationListBlock { AnimID = animation.ID, AnimSequenceID = animation.SequenceNum }; } packet.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[1]; packet.AnimationSourceList[0] = new AvatarAnimationPacket.AnimationSourceListBlock { ObjectID = animAgent.ID }; packet.PhysicalAvatarEventList = new AvatarAnimationPacket.PhysicalAvatarEventListBlock[0]; m_udp.SendPacket(agent, packet, ThrottleCategory.Task, false); } }
private void SendKillPacket(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; KillObjectPacket kill = new KillObjectPacket(); kill.ObjectData = new KillObjectPacket.ObjectDataBlock[eventDatas.Length]; for (int i = 0; i < eventDatas.Length; i++) { kill.ObjectData[i] = new KillObjectPacket.ObjectDataBlock(); kill.ObjectData[i].ID = (uint)eventDatas[i].Event.State; } m_udp.SendPacket(agent, kill, ThrottleCategory.Task, true); }
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 QueuedInterestListEvent ObjectUpdateCombiner(QueuedInterestListEvent currentData, QueuedInterestListEvent newData) { // If this event and the previous event are both updates, combine the UpdateFlags together if (currentData.Event.Type == OBJECT_UPDATE && newData.Event.Type == OBJECT_UPDATE) { EntityAddOrUpdateArgs currentArgs = (EntityAddOrUpdateArgs)currentData.Event.State; EntityAddOrUpdateArgs newArgs = (EntityAddOrUpdateArgs)newData.Event.State; System.Diagnostics.Debug.Assert(currentArgs.Entity.ID == newArgs.Entity.ID, "Attempting to combine two different entities"); // Combine the update flags on these two updates newArgs.UpdateFlags |= currentArgs.UpdateFlags; newArgs.ExtraFlags |= currentArgs.ExtraFlags; } // Otherwise, the new event overrides the previous event return newData; }
private void SendPreloadSoundsPacket(QueuedInterestListEvent[] eventDatas, IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) return; LLAgent agent = (LLAgent)presence; PreloadSoundPacket preload = new PreloadSoundPacket(); preload.DataBlock = new PreloadSoundPacket.DataBlockBlock[eventDatas.Length]; for (int i = 0; i < eventDatas.Length; i++) { object[] state = (object[])eventDatas[i].Event.State; ISceneEntity source = (ISceneEntity)state[0]; UUID soundID = (UUID)state[1]; preload.DataBlock[i] = new PreloadSoundPacket.DataBlockBlock { ObjectID = source.ID, OwnerID = source.OwnerID, SoundID = soundID }; } m_udp.SendPacket(agent, preload, ThrottleCategory.Task, true); }
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 }