/// <summary> /// Update the attachment asset for the new sog details if they have changed. /// </summary> /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects, /// these details are not stored on the region. /// <param name="remoteClient"></param> /// <param name="grp"></param> /// <param name="itemID"></param> /// <param name="agentID"></param> protected UUID UpdateKnownItem(IClientAPI remoteClient, ISceneEntity grp, UUID itemID, UUID agentID) { if (grp != null) { if (!grp.HasGroupChanged) { //MainConsole.Instance.WarnFormat("[ATTACHMENTS MODULE]: Save request for {0} which is unchanged", grp.UUID); return(UUID.Zero); } //let things like state saves and another async things be performed before we serialize the object grp.BackupPreparation(); MainConsole.Instance.InfoFormat( "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}", grp.UUID, grp.GetAttachmentPoint()); string sceneObjectXml = SceneEntitySerializer.SceneObjectSerializer.ToOriginalXmlFormat(grp); AssetBase asset = new AssetBase(UUID.Random(), grp.Name, AssetType.Object, remoteClient.AgentId) { Description = grp.RootChild.Description, Data = Utils.StringToBytes(sceneObjectXml) }; asset.ID = m_scene.AssetService.Store(asset); m_scene.InventoryService.UpdateAssetIDForItem(itemID, asset.ID); return(asset.ID); } return(UUID.Zero); }
/// <summary> /// Update the attachment asset for the new sog details if they have changed. /// </summary> /// /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects, /// these details are not stored on the region. /// /// <param name="remoteClient"></param> /// <param name="grp"></param> /// <param name="itemID"></param> /// <param name="agentID"></param> protected void UpdateKnownItem(IClientAPI remoteClient, ISceneEntity grp, UUID itemID, UUID agentID) { if (grp != null) { if (!grp.HasGroupChanged) { //MainConsole.Instance.WarnFormat("[ATTACHMENTS MODULE]: Save request for {0} which is unchanged", grp.UUID); return; } //let things like state saves and another async things be performed before we serialize the object grp.BackupPreparation(); MainConsole.Instance.InfoFormat( "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}", grp.UUID, grp.GetAttachmentPoint()); string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat((SceneObjectGroup)grp); InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId); item = m_scene.InventoryService.GetItem(item); if (item != null) { AssetBase asset = new AssetBase(UUID.Random(), grp.Name, AssetType.Object, remoteClient.AgentId) { Description = grp.RootChild.Description, Data = Utils.StringToBytes(sceneObjectXml) }; asset.ID = m_scene.AssetService.Store(asset); if (item.Folder == UUID.Zero) { InventoryFolderBase folder = m_scene.InventoryService.GetFolderForType(remoteClient.AgentId, InventoryType.Unknown, AssetType.Object); if (folder == null) { return;//Probably a non user (bot) } item.Folder = folder.ID; } item.AssetID = asset.ID; item.Description = asset.Description; item.Name = asset.Name; item.AssetType = asset.Type; item.InvType = (int)InventoryType.Object; m_scene.InventoryService.UpdateItem(item); // this gets called when the agent logs off! remoteClient.SendInventoryItemCreateUpdate(item, 0); } else { MainConsole.Instance.Warn("[AttachmentModule]: Could not find inventory item for attachment to update!"); } } }
private void DetachSingleAttachmentGroupToInventoryInternal(UUID itemID, IClientAPI remoteClient, bool fireEvent, ISceneEntity group) { if (fireEvent) { m_scene.EventManager.TriggerOnAttach(group.LocalId, itemID, UUID.Zero); group.DetachToInventoryPrep(); } IScenePresence presence = m_scene.GetScenePresence(remoteClient.AgentId); if (presence != null) { AvatarAttachments attModule = presence.RequestModuleInterface <AvatarAttachments>(); if (attModule != null) { attModule.RemoveAttachment(group); } if (attModule != null) { presence.SetAttachments(attModule.Get()); } } MainConsole.Instance.Debug("[ATTACHMENTS MODULE]: Saving attachpoint: " + ((uint)group.GetAttachmentPoint()).ToString()); //Update the saved attach points if (group.RootChild.AttachedPos != group.RootChild.SavedAttachedPos || group.RootChild.SavedAttachmentPoint != group.RootChild.AttachmentPoint) { group.RootChild.SavedAttachedPos = group.RootChild.AttachedPos; group.RootChild.SavedAttachmentPoint = group.RootChild.AttachmentPoint; //Make sure we get updated group.HasGroupChanged = true; } // If an item contains scripts, it's always changed. // This ensures script state is saved on detach foreach (ISceneChildEntity p in group.ChildrenEntities()) { if (p.Inventory.ContainsScripts()) { group.HasGroupChanged = true; break; } } if (group.HasGroupChanged) { UpdateKnownItem(remoteClient, group, group.RootChild.FromUserInventoryItemID, group.OwnerID); } }
/// <summary> /// Update the attachment asset for the new sog details if they have changed. /// </summary> /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects, /// these details are not stored on the region. /// <param name="remoteClient"></param> /// <param name="grp"></param> /// <param name="itemID"></param> /// <param name="agentID"></param> protected UUID UpdateKnownItem(IClientAPI remoteClient, ISceneEntity grp, UUID itemID, UUID agentID) { if (grp != null) { if (!grp.HasGroupChanged) { //MainConsole.Instance.WarnFormat("[Attachments]: Save request for {0} which is unchanged", grp.UUID); return(UUID.Zero); } // Saving attachments for NPCs messes them up for the real owner! var botMgr = m_scene.RequestModuleInterface <IBotManager> (); if (botMgr != null) { if (botMgr.IsNpcAgent(agentID)) { return(UUID.Zero); } } //let things like state saves and another async things be performed before we serialize the object grp.BackupPreparation(); MainConsole.Instance.InfoFormat( "[Attachments]: Updating asset for attachment {0}, attachpoint {1}", grp.UUID, grp.GetAttachmentPoint()); string sceneObjectXml = SceneEntitySerializer.SceneObjectSerializer.ToOriginalXmlFormat(grp); AssetBase asset = new AssetBase(UUID.Random(), grp.Name, AssetType.Object, remoteClient.AgentId) { Description = grp.RootChild.Description, Data = Utils.StringToBytes(sceneObjectXml) }; asset.ID = m_scene.AssetService.Store(asset); m_scene.InventoryService.UpdateAssetIDForItem(itemID, asset.ID); // this gets called when the agent logs off! //remoteClient.SendInventoryItemCreateUpdate(item, 0); return(asset.ID); } return(UUID.Zero); }
/// <summary> /// Update the attachment asset for the new sog details if they have changed. /// </summary> /// /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects, /// these details are not stored on the region. /// /// <param name="remoteClient"></param> /// <param name="grp"></param> /// <param name="itemID"></param> /// <param name="agentID"></param> protected void UpdateKnownItem (IClientAPI remoteClient, ISceneEntity grp, UUID itemID, UUID agentID) { if (grp != null) { if (!grp.HasGroupChanged) { //m_log.WarnFormat("[ATTACHMENTS MODULE]: Save request for {0} which is unchanged", grp.UUID); return; } //let things like state saves and another async things be performed before we serialize the object grp.BackupPreparation(); m_log.InfoFormat( "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}", grp.UUID, grp.GetAttachmentPoint()); string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat((SceneObjectGroup)grp); InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId); item = m_scene.InventoryService.GetItem(item); if (item != null) { AssetBase asset = new AssetBase(UUID.Random(), grp.Name, AssetType.Object, remoteClient.AgentId); asset.Description = grp.RootChild.Description; asset.Data = Utils.StringToBytes(sceneObjectXml); asset.ID = m_scene.AssetService.Store(asset); if (item.Folder == UUID.Zero) { InventoryFolderBase folder = m_scene.InventoryService.GetFolderForType (remoteClient.AgentId, InventoryType.Unknown, AssetType.Object); if (folder == null) return;//Probably a non user (bot) item.Folder = folder.ID; } item.AssetID = asset.ID; item.Description = asset.Description; item.Name = asset.Name; item.AssetType = asset.Type; item.InvType = (int)InventoryType.Object; m_scene.InventoryService.UpdateItem(item); // this gets called when the agent logs off! if (remoteClient != null) remoteClient.SendInventoryItemCreateUpdate(item, 0); } else { m_log.Warn("[AttachmentModule]: Could not find inventory item for attachment to update!"); } } }
private void DetachSingleAttachmentGroupToInventoryInternal (UUID itemID, IClientAPI remoteClient, bool fireEvent, ISceneEntity group) { if (fireEvent) { m_scene.EventManager.TriggerOnAttach (group.LocalId, itemID, UUID.Zero); group.DetachToInventoryPrep (); } IScenePresence presence = m_scene.GetScenePresence (remoteClient.AgentId); if (presence != null) { AvatarAttachments attModule = presence.RequestModuleInterface<AvatarAttachments> (); if (attModule != null) attModule.RemoveAttachment (group); presence.RemoveAttachment (group); } m_log.Debug ("[ATTACHMENTS MODULE]: Saving attachpoint: " + ((uint)group.GetAttachmentPoint ()).ToString ()); //Update the saved attach points if (group.RootChild.AttachedPos != group.RootChild.SavedAttachedPos || group.RootChild.SavedAttachmentPoint != group.RootChild.AttachmentPoint) { group.RootChild.SavedAttachedPos = group.RootChild.AttachedPos; group.RootChild.SavedAttachmentPoint = group.RootChild.AttachmentPoint; //Make sure we get updated group.HasGroupChanged = true; } // If an item contains scripts, it's always changed. // This ensures script state is saved on detach foreach (ISceneChildEntity p in group.ChildrenEntities ()) { if (p.Inventory.ContainsScripts ()) { group.HasGroupChanged = true; break; } } UpdateKnownItem (remoteClient, group, group.RootChild.FromUserInventoryItemID, group.OwnerID); }
/// <summary> /// Attach the object to the avatar /// </summary> /// <param name="remoteClient">The client that is having the attachment done</param> /// <param name="localID">The localID (SceneObjectPart) that is being attached (for the attach script event)</param> /// <param name="group">The group (SceneObjectGroup) that is being attached</param> /// <param name="AttachmentPt">The point to where the attachment will go</param> /// <param name="item">If this is not null, it saves a query in this method to the InventoryService /// This is the Item that the object is in (if it is in one yet)</param> protected void FindAttachmentPoint (IClientAPI remoteClient, uint localID, ISceneEntity group, int AttachmentPt, InventoryItemBase item) { //Make sure that we arn't over the limit of attachments ISceneEntity[] attachments = GetAttachmentsForAvatar (remoteClient.AgentId); if (attachments.Length + 1 > m_maxNumberOfAttachments) { //Too many remoteClient.SendAgentAlertMessage( "You are wearing too many attachments. Take one off to attach this object", false); return; } Vector3 attachPos = group.GetAttachmentPos(); bool hasMultipleAttachmentsSet = (AttachmentPt & 0x7f) != 0 || AttachmentPt == 0; if(!m_allowMultipleAttachments) hasMultipleAttachmentsSet = false; AttachmentPt &= 0x7f; //Disable it! Its evil! //Did the attachment change position or attachment point? bool changedPositionPoint = false; // If the attachment point isn't the same as the one previously used // set it's offset position = 0 so that it appears on the attachment point // and not in a weird location somewhere unknown. //Simplier terms: the attachment point changed, set it to the default 0,0,0 location if (AttachmentPt != 0 && AttachmentPt != (int)(group.GetAttachmentPoint() & 0x7f)) { attachPos = Vector3.Zero; changedPositionPoint = true; } else { // AttachmentPt 0 means the client chose to 'wear' the attachment. if (AttachmentPt == 0) { // Check object for stored attachment point AttachmentPt = (int)group.GetSavedAttachmentPoint() & 0x7f; attachPos = group.GetAttachmentPos(); } //Check state afterwards... use the newer GetSavedAttachmentPoint and Pos above first if (AttachmentPt == 0) { // Check object for older stored attachment point AttachmentPt = group.RootChild.Shape.State & 0x7f; //attachPos = group.AbsolutePosition; } // if we still didn't find a suitable attachment point, force it to the default //This happens on the first time an avatar 'wears' an object if (AttachmentPt == 0) { // Stick it on right hand with Zero Offset from the attachment point. AttachmentPt = (int)AttachmentPoint.RightHand; //Default location attachPos = Vector3.Zero; changedPositionPoint = true; } } group.HasGroupChanged = changedPositionPoint; //Update where we are put group.SetAttachmentPoint((byte)AttachmentPt); //Fix the position with the one we found group.AbsolutePosition = attachPos; // Remove any previous attachments IScenePresence presence = m_scene.GetScenePresence (remoteClient.AgentId); if (presence == null) return; UUID itemID = UUID.Zero; //Check for multiple attachment bits and whether we should remove the old if(!hasMultipleAttachmentsSet) { foreach (ISceneEntity grp in attachments) { if (grp.GetAttachmentPoint() == (byte)AttachmentPt) { itemID = grp.RootChild.FromUserInventoryItemID; break; } } if (itemID != UUID.Zero) DetachSingleAttachmentToInventory(itemID, remoteClient); } itemID = group.RootChild.FromUserInventoryItemID; group.RootChild.AttachedAvatar = presence.UUID; List<ISceneChildEntity> parts = group.ChildrenEntities(); for (int i = 0; i < parts.Count; i++) parts[i].AttachedAvatar = presence.UUID; if (group.RootChild.PhysActor != null) { m_scene.PhysicsScene.RemovePrim (group.RootChild.PhysActor); group.RootChild.PhysActor = null; } group.RootChild.AttachedPos = attachPos; group.RootChild.IsAttachment = true; group.AbsolutePosition = attachPos; group.RootChild.SetParentLocalId (presence.LocalId); group.SetAttachmentPoint(Convert.ToByte(AttachmentPt)); AvatarAttachments attPlugin = presence.RequestModuleInterface<AvatarAttachments>(); if (attPlugin != null) { attPlugin.AddAttachment (group); presence.AddAttachment (group); } // Killing it here will cause the client to deselect it // It then reappears on the avatar, deselected // through the full update below // if (group.IsSelected) { foreach (ISceneChildEntity part in group.ChildrenEntities()) { part.CreateSelected = true; } } //Kill the previous entity so that it will be selected SendKillEntity(group.RootChild); //NOTE: This MUST be here, otherwise we limit full updates during attachments when they are selected and it will block the first update. // So until that is changed, this MUST stay. The client will instantly reselect it, so this value doesn't stay borked for long. group.IsSelected = false; if (itemID == UUID.Zero) { //Delete the object inworld to inventory List<ISceneEntity> groups = new List<ISceneEntity> (1) { group }; IInventoryAccessModule inventoryAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>(); if (inventoryAccess != null) inventoryAccess.DeleteToInventory(DeRezAction.AcquireToUserInventory, UUID.Zero, groups, remoteClient.AgentId, out itemID); } else { //it came from an item, we need to start the scripts // Fire after attach, so we don't get messy perms dialogs // 4 == AttachedRez group.CreateScriptInstances(0, true, StateSource.AttachedRez, UUID.Zero); group.ResumeScripts(); } if (UUID.Zero == itemID) { m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID."); remoteClient.SendAgentAlertMessage( "Unable to save attachment. Error inventory item ID.", false); return; } // XXYY!! if (item == null) { item = new InventoryItemBase(itemID, remoteClient.AgentId); item = m_scene.InventoryService.GetItem(item); } //Update the ItemID with the new item group.SetFromItemID (itemID); //If we updated the attachment, we need to save the change IAvatarAppearanceModule appearance = presence.RequestModuleInterface<IAvatarAppearanceModule> (); if (appearance.Appearance.SetAttachment ((int)AttachmentPt, itemID, item.AssetID)) AvatarFactory.QueueAppearanceSave(remoteClient.AgentId); // In case it is later dropped again, don't let // it get cleaned up group.RootChild.RemFlag(PrimFlags.TemporaryOnRez); group.HasGroupChanged = false; //Now recreate it so that it is selected group.ScheduleGroupUpdate(PrimUpdateFlags.ForcedFullUpdate); m_scene.EventManager.TriggerOnAttach(localID, group.RootChild.FromUserInventoryItemID, remoteClient.AgentId); }
/// <summary> /// Update the attachment asset for the new sog details if they have changed. /// </summary> /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects, /// these details are not stored on the region. /// <param name="remoteClient"></param> /// <param name="grp"></param> /// <param name="itemID"></param> /// <param name="agentID"></param> protected UUID UpdateKnownItem(IClientAPI remoteClient, ISceneEntity grp, UUID itemID, UUID agentID) { if (grp != null) { if (!grp.HasGroupChanged) { //MainConsole.Instance.WarnFormat("[ATTACHMENTS MODULE]: Save request for {0} which is unchanged", grp.UUID); return UUID.Zero; } //let things like state saves and another async things be performed before we serialize the object grp.BackupPreparation(); MainConsole.Instance.InfoFormat( "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}", grp.UUID, grp.GetAttachmentPoint()); string sceneObjectXml = SceneEntitySerializer.SceneObjectSerializer.ToOriginalXmlFormat(grp); AssetBase asset = new AssetBase(UUID.Random(), grp.Name, AssetType.Object, remoteClient.AgentId) { Description = grp.RootChild.Description, Data = Utils.StringToBytes(sceneObjectXml) }; asset.ID = m_scene.AssetService.Store(asset); m_scene.InventoryService.UpdateAssetIDForItem(itemID, asset.ID); // this gets called when the agent logs off! //remoteClient.SendInventoryItemCreateUpdate(item, 0); return asset.ID; } return UUID.Zero; }
public virtual void TriggerSound( UUID soundId, UUID ownerID, UUID objectID, UUID parentID, double gain, Vector3 position, UInt64 handle, float radius) { bool LocalOnly = false; ILandObject ILO = null; IParcelManagementModule parcelManagement = m_scene.RequestModuleInterface <IParcelManagementModule>(); if (parcelManagement != null) { ILO = parcelManagement.GetLandObject(position.X, position.Y); if (ILO != null) //Check only if null, otherwise this breaks megaregions { LocalOnly = (ILO.LandData.Flags & (uint)ParcelFlags.SoundLocal) == (uint)ParcelFlags.SoundLocal; } } ISceneChildEntity part = m_scene.GetSceneObjectPart(objectID); if (part == null) { IScenePresence sp; if (!m_scene.TryGetScenePresence(objectID, out sp)) { return; } } else { ISceneEntity grp = part.ParentEntity; if (grp.IsAttachment && grp.GetAttachmentPoint() > 30) { objectID = ownerID; parentID = ownerID; } } m_scene.ForEachScenePresence(delegate(IScenePresence sp) { if (Cones.Count != 0) { foreach (ConeOfSilence CS in Cones.Values) { if (Util.GetDistanceTo(sp.AbsolutePosition, CS.Position) > CS.Radius) { // Presence is outside of the Cone of silence if (Util.GetDistanceTo(CS.Position, position) < CS.Radius) { //Sound was triggered inside the cone, but avatar is outside continue; } } else { // Avatar is inside the cone of silence if (Util.GetDistanceTo(CS.Position, position) > CS.Radius) { //Sound was triggered outside of the cone, but avatar is inside of the cone. continue; } } } } if (sp.IsChildAgent) { return; } float dis = (float)Util.GetDistanceTo(sp.AbsolutePosition, position); if (dis > 100.0f) // Max audio distance { return; } //Check to see if the person is local and the av is in the same parcel if (LocalOnly && sp.CurrentParcelUUID != ILO.LandData.GlobalID) { return; } // Scale by distance float thisSPGain; if (radius == 0) { thisSPGain = (float)(gain * ((100.0f - dis) / 100.0f)); } else { thisSPGain = (float)(gain * ((radius - dis) / radius)); } sp.ControllingClient.SendTriggeredSound( soundId, ownerID, objectID, parentID, handle, position, thisSPGain); }); }
/// <summary> /// Attach the object to the avatar /// </summary> /// <param name="remoteClient">The client that is having the attachment done</param> /// <param name="localID">The localID (SceneObjectPart) that is being attached (for the attach script event)</param> /// <param name="group">The group (SceneObjectGroup) that is being attached</param> /// <param name="AttachmentPt">The point to where the attachment will go</param> /// <param name="assetID" /> /// <param name="forceUpdatePrim">Force updating of the prim the next time the user attempts to deattach it</param> /// <param name="isTempAttach">Is a temporary attachment</param> protected void FindAttachmentPoint(IClientAPI remoteClient, uint localID, ISceneEntity group, int AttachmentPt, UUID assetID, bool forceUpdatePrim, bool isTempAttach) { //Make sure that we arn't over the limit of attachments ISceneEntity[] attachments = GetAttachmentsForAvatar(remoteClient.AgentId); if (attachments.Length + 1 > m_maxNumberOfAttachments) { //Too many remoteClient.SendAgentAlertMessage( "You are wearing too many attachments. Take one off to attach this object", false); return; } Vector3 attachPos = group.GetAttachmentPos(); bool hasMultipleAttachmentsSet = (AttachmentPt & 0x7f) != 0 || AttachmentPt == 0; if (!m_allowMultipleAttachments) { hasMultipleAttachmentsSet = false; } AttachmentPt &= 0x7f; //Disable it! Its evil! //Did the attachment change position or attachment point? bool changedPositionPoint = false; // If the attachment point isn't the same as the one previously used // set it's offset position = 0 so that it appears on the attachment point // and not in a weird location somewhere unknown. //Simplier terms: the attachment point changed, set it to the default 0,0,0 location if (AttachmentPt != 0 && AttachmentPt != (group.GetAttachmentPoint() & 0x7f)) { attachPos = Vector3.Zero; changedPositionPoint = true; } else { // AttachmentPt 0 means the client chose to 'wear' the attachment. if (AttachmentPt == 0) { // Check object for stored attachment point AttachmentPt = group.GetSavedAttachmentPoint() & 0x7f; attachPos = group.GetAttachmentPos(); } //Check state afterwards... use the newer GetSavedAttachmentPoint and Pos above first if (AttachmentPt == 0) { // Check object for older stored attachment point AttachmentPt = group.RootChild.Shape.State & 0x7f; } // if we still didn't find a suitable attachment point, force it to the default //This happens on the first time an avatar 'wears' an object if (AttachmentPt == 0) { // Stick it on right hand with Zero Offset from the attachment point. AttachmentPt = (int)AttachmentPoint.RightHand; //Default location attachPos = Vector3.Zero; changedPositionPoint = true; } } MainConsole.Instance.DebugFormat( "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2} localID {3}", group.Name, remoteClient.Name, AttachmentPt, group.LocalId); //Update where we are put group.SetAttachmentPoint((byte)AttachmentPt); //Fix the position with the one we found group.AbsolutePosition = attachPos; // Remove any previous attachments IScenePresence presence = m_scene.GetScenePresence(remoteClient.AgentId); if (presence == null) { return; } UUID itemID = UUID.Zero; //Check for multiple attachment bits and whether we should remove the old if (!hasMultipleAttachmentsSet) { foreach (ISceneEntity grp in attachments) { if (grp.GetAttachmentPoint() == (byte)AttachmentPt) { itemID = grp.RootChild.FromUserInventoryItemID; break; } } if (itemID != UUID.Zero) { DetachSingleAttachmentToInventory(itemID, remoteClient); } } itemID = group.RootChild.FromUserInventoryItemID; group.RootChild.AttachedAvatar = presence.UUID; List <ISceneChildEntity> parts = group.ChildrenEntities(); foreach (ISceneChildEntity t in parts) { t.AttachedAvatar = presence.UUID; } if (group.RootChild.PhysActor != null) { m_scene.PhysicsScene.DeletePrim(group.RootChild.PhysActor); group.RootChild.PhysActor = null; } group.RootChild.AttachedPos = attachPos; group.RootChild.IsAttachment = true; group.AbsolutePosition = attachPos; group.RootChild.SetParentLocalId(presence.LocalId); group.SetAttachmentPoint(Convert.ToByte(AttachmentPt)); // Killing it here will cause the client to deselect it // It then reappears on the avatar, deselected // through the full update below if (group.IsSelected) { foreach (ISceneChildEntity part in group.ChildrenEntities()) { part.CreateSelected = true; } } //NOTE: This MUST be here, otherwise we limit full updates during attachments when they are selected and it will block the first update. // So until that is changed, this MUST stay. The client will instantly reselect it, so this value doesn't stay borked for long. group.IsSelected = false; if (!isTempAttach) { if (itemID == UUID.Zero) { //Delete the object inworld to inventory List <ISceneEntity> groups = new List <ISceneEntity>(1) { group }; IInventoryAccessModule inventoryAccess = m_scene.RequestModuleInterface <IInventoryAccessModule>(); if (inventoryAccess != null) { inventoryAccess.DeleteToInventory(DeRezAction.AcquireToUserInventory, UUID.Zero, groups, remoteClient.AgentId, out itemID); } } else { //it came from an item, we need to start the scripts // Fire after attach, so we don't get messy perms dialogs. group.CreateScriptInstances(0, true, StateSource.AttachedRez, UUID.Zero, false); } if (UUID.Zero == itemID) { MainConsole.Instance.Error( "[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID."); remoteClient.SendAgentAlertMessage( "Unable to save attachment. Error inventory item ID.", false); return; } // XXYY!! if (assetID == UUID.Zero) { assetID = m_scene.InventoryService.GetItemAssetID(remoteClient.AgentId, itemID); //Update the ItemID with the new item group.SetFromItemID(itemID, assetID); } } else { // Fire after attach, so we don't get messy perms dialogs. group.CreateScriptInstances(0, true, StateSource.AttachedRez, UUID.Zero, false); group.RootChild.FromUserInventoryItemID = UUID.Zero; } AvatarAttachments attPlugin = presence.RequestModuleInterface <AvatarAttachments>(); if (attPlugin != null) { attPlugin.AddAttachment(group); presence.SetAttachments(attPlugin.Get()); if (!isTempAttach) { IAvatarAppearanceModule appearance = presence.RequestModuleInterface <IAvatarAppearanceModule>(); appearance.Appearance.SetAttachments(attPlugin.Get()); AvatarFactory.QueueAppearanceSave(remoteClient.AgentId); } } // In case it is later dropped again, don't let // it get cleaned up group.RootChild.RemFlag(PrimFlags.TemporaryOnRez); group.HasGroupChanged = changedPositionPoint || forceUpdatePrim; //Now recreate it so that it is selected group.ScheduleGroupUpdate(PrimUpdateFlags.ForcedFullUpdate); m_scene.EventManager.TriggerOnAttach(localID, group.RootChild.FromUserInventoryItemID, remoteClient.AgentId); }