Пример #1
0
        /// <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);
        }
Пример #2
0
        /// <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!");
                }
            }
        }
Пример #3
0
        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);
        }
Пример #8
0
        /// <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;
        }
Пример #9
0
        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);
            });
        }
Пример #10
0
        /// <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);
        }