2010-03-05 23:18:47 +00:00
/ *
* Copyright ( c ) Contributors , http : //opensimulator.org/
* See CONTRIBUTORS . TXT for a full list of copyright holders .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ` ` AS IS ' ' AND ANY
* EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
2010-06-09 19:51:24 +00:00
using System ;
2010-03-06 00:07:47 +00:00
using System.Collections.Generic ;
2010-03-05 23:18:47 +00:00
using System.Reflection ;
using log4net ;
2010-07-30 18:26:54 +00:00
using Mono.Addins ;
2010-03-05 23:18:47 +00:00
using Nini.Config ;
using OpenMetaverse ;
2010-04-16 21:54:25 +00:00
using OpenMetaverse.Packets ;
2010-03-05 23:18:47 +00:00
using OpenSim.Framework ;
using OpenSim.Region.Framework ;
using OpenSim.Region.Framework.Interfaces ;
using OpenSim.Region.Framework.Scenes ;
2010-08-26 21:09:52 +00:00
using OpenSim.Region.Framework.Scenes.Serialization ;
2010-03-05 23:18:47 +00:00
namespace OpenSim.Region.CoreModules.Avatar.Attachments
2010-03-10 04:15:36 +00:00
{
2010-07-30 18:26:54 +00:00
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")]
public class AttachmentsModule : IAttachmentsModule , INonSharedRegionModule
2010-03-05 23:18:47 +00:00
{
private static readonly ILog m_log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2011-08-30 21:25:38 +00:00
private Scene m_scene ;
2011-08-24 20:35:44 +00:00
private IDialogModule m_dialogModule ;
2010-07-30 18:26:54 +00:00
2010-09-12 17:43:49 +00:00
public string Name { get { return "Attachments Module" ; } }
public Type ReplaceableInterface { get { return null ; } }
2010-03-05 23:18:47 +00:00
2010-07-30 18:26:54 +00:00
public void Initialise ( IConfigSource source ) { }
public void AddRegion ( Scene scene )
2010-03-05 23:18:47 +00:00
{
m_scene = scene ;
2011-08-24 20:35:44 +00:00
m_dialogModule = m_scene . RequestModuleInterface < IDialogModule > ( ) ;
2010-07-30 18:26:54 +00:00
m_scene . RegisterModuleInterface < IAttachmentsModule > ( this ) ;
2010-07-30 20:41:44 +00:00
m_scene . EventManager . OnNewClient + = SubscribeToClientEvents ;
// TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI
2010-03-05 23:18:47 +00:00
}
2010-07-30 18:26:54 +00:00
public void RemoveRegion ( Scene scene )
2010-03-05 23:18:47 +00:00
{
2010-07-30 18:26:54 +00:00
m_scene . UnregisterModuleInterface < IAttachmentsModule > ( this ) ;
2010-07-30 20:41:44 +00:00
m_scene . EventManager . OnNewClient - = SubscribeToClientEvents ;
2010-03-05 23:18:47 +00:00
}
2010-07-30 18:26:54 +00:00
public void RegionLoaded ( Scene scene ) { }
public void Close ( )
2010-03-05 23:18:47 +00:00
{
2010-07-30 18:26:54 +00:00
RemoveRegion ( m_scene ) ;
2010-03-05 23:18:47 +00:00
}
2010-07-30 20:41:44 +00:00
public void SubscribeToClientEvents ( IClientAPI client )
{
client . OnRezSingleAttachmentFromInv + = RezSingleAttachmentFromInventory ;
client . OnRezMultipleAttachmentsFromInv + = RezMultipleAttachmentsFromInventory ;
client . OnObjectAttach + = AttachObject ;
client . OnObjectDetach + = DetachObject ;
2011-08-23 21:05:22 +00:00
client . OnDetachAttachmentIntoInv + = DetachSingleAttachmentToInv ;
2011-08-30 21:25:38 +00:00
client . OnObjectDrop + = DetachSingleAttachmentToGround ;
2010-07-30 20:41:44 +00:00
}
public void UnsubscribeFromClientEvents ( IClientAPI client )
{
client . OnRezSingleAttachmentFromInv - = RezSingleAttachmentFromInventory ;
client . OnRezMultipleAttachmentsFromInv - = RezMultipleAttachmentsFromInventory ;
client . OnObjectAttach - = AttachObject ;
client . OnObjectDetach - = DetachObject ;
2011-08-23 21:05:22 +00:00
client . OnDetachAttachmentIntoInv - = DetachSingleAttachmentToInv ;
2011-08-30 21:25:38 +00:00
client . OnObjectDrop - = DetachSingleAttachmentToGround ;
2010-07-30 20:41:44 +00:00
}
2011-08-30 22:05:43 +00:00
/// <summary>
/// RezAttachments. This should only be called upon login on the first region.
/// Attachment rezzings on crossings and TPs are done in a different way.
/// </summary>
public void RezAttachments ( IScenePresence sp )
{
if ( null = = sp . Appearance )
{
m_log . WarnFormat ( "[ATTACHMENTS MODULE]: Appearance has not been initialized for agent {0}" , sp . UUID ) ;
return ;
}
List < AvatarAttachment > attachments = sp . Appearance . GetAttachments ( ) ;
foreach ( AvatarAttachment attach in attachments )
{
2011-09-03 00:11:16 +00:00
uint p = ( uint ) attach . AttachPoint ;
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Doing initial rez of attachment with itemID {0}, assetID {1}, point {2} for {3} in {4}",
// attach.ItemID, attach.AssetID, p, sp.Name, m_scene.RegionInfo.RegionName);
2011-08-30 22:05:43 +00:00
// For some reason assetIDs are being written as Zero's in the DB -- need to track tat down
// But they're not used anyway, the item is being looked up for now, so let's proceed.
//if (UUID.Zero == assetID)
//{
// m_log.DebugFormat("[ATTACHMENT]: Cannot rez attachment in point {0} with itemID {1}", p, itemID);
// continue;
//}
try
{
2011-09-03 00:11:16 +00:00
// If we're an NPC then skip all the item checks and manipulations since we don't have an
// inventory right now.
if ( sp . PresenceType = = PresenceType . Npc )
RezSingleAttachmentFromInventoryInternal ( sp , UUID . Zero , attach . AssetID , p ) ;
else
RezSingleAttachmentFromInventory ( sp . ControllingClient , attach . ItemID , p ) ;
2011-08-30 22:05:43 +00:00
}
catch ( Exception e )
{
m_log . ErrorFormat ( "[ATTACHMENTS MODULE]: Unable to rez attachment: {0}{1}" , e . Message , e . StackTrace ) ;
}
}
}
2011-08-30 22:32:30 +00:00
public void SaveChangedAttachments ( IScenePresence sp )
{
2011-09-13 19:25:32 +00:00
// m_log.DebugFormat("[ATTACHMENTS MODULE]: Saving changed attachments for {0}", sp.Name);
2011-08-31 15:29:51 +00:00
foreach ( SceneObjectGroup grp in sp . GetAttachments ( ) )
2011-08-30 22:32:30 +00:00
{
2011-09-13 21:13:58 +00:00
// if (grp.HasGroupChanged) // Resizer scripts?
// {
2011-08-30 22:32:30 +00:00
grp . IsAttachment = false ;
grp . AbsolutePosition = grp . RootPart . AttachedPos ;
UpdateKnownItem ( sp . ControllingClient , grp , grp . GetFromItemID ( ) , grp . OwnerID ) ;
grp . IsAttachment = true ;
2011-09-13 21:13:58 +00:00
// }
2011-08-30 22:32:30 +00:00
}
}
2011-08-31 16:53:58 +00:00
public void DeleteAttachmentsFromScene ( IScenePresence sp , bool silent )
{
2011-09-13 21:13:58 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Deleting attachments from scene {0} for {1}, silent = {2}",
// m_scene.RegionInfo.RegionName, sp.Name, silent);
2011-08-31 16:53:58 +00:00
foreach ( SceneObjectGroup sop in sp . GetAttachments ( ) )
{
sop . Scene . DeleteSceneObject ( sop , silent ) ;
}
sp . ClearAttachments ( ) ;
}
2010-07-30 20:41:44 +00:00
2010-07-30 18:26:54 +00:00
/// <summary>
/// Called by client
/// </summary>
/// <param name="remoteClient"></param>
/// <param name="objectLocalID"></param>
/// <param name="AttachmentPt"></param>
/// <param name="silent"></param>
2010-07-14 17:21:01 +00:00
public void AttachObject ( IClientAPI remoteClient , uint objectLocalID , uint AttachmentPt , bool silent )
2010-03-12 22:39:15 +00:00
{
2011-08-24 19:49:23 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
// objectLocalID, remoteClient.Name, AttachmentPt, silent);
2010-03-12 22:39:15 +00:00
2010-06-09 19:51:24 +00:00
try
2010-03-12 22:39:15 +00:00
{
2011-08-22 22:59:48 +00:00
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
if ( sp = = null )
{
m_log . ErrorFormat (
"[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}" , remoteClient . Name , remoteClient . AgentId ) ;
return ;
}
2010-06-09 19:51:24 +00:00
// If we can't take it, we can't attach it!
SceneObjectPart part = m_scene . GetSceneObjectPart ( objectLocalID ) ;
if ( part = = null )
return ;
if ( ! m_scene . Permissions . CanTakeObject ( part . UUID , remoteClient . AgentId ) )
2010-11-23 04:26:07 +00:00
{
remoteClient . SendAgentAlertMessage (
"You don't have sufficient permissions to attach this object" , false ) ;
2010-06-09 19:51:24 +00:00
return ;
2010-11-23 04:26:07 +00:00
}
2010-06-09 19:51:24 +00:00
2010-10-15 05:24:57 +00:00
// TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
// be removed when that functionality is implemented in opensim
AttachmentPt & = 0x7f ;
2010-06-09 19:51:24 +00:00
// Calls attach with a Zero position
2011-08-22 22:59:48 +00:00
if ( AttachObject ( sp , part . ParentGroup , AttachmentPt , false ) )
2010-03-12 22:39:15 +00:00
{
2010-06-09 19:51:24 +00:00
m_scene . EventManager . TriggerOnAttach ( objectLocalID , part . ParentGroup . GetFromItemID ( ) , remoteClient . AgentId ) ;
// Save avatar attachment information
2011-09-13 21:13:58 +00:00
m_log . Debug (
2010-10-29 20:37:13 +00:00
"[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient . AgentId
+ ", AttachmentPoint: " + AttachmentPt ) ;
2010-06-09 19:51:24 +00:00
2010-03-12 22:39:15 +00:00
}
}
2010-06-09 19:51:24 +00:00
catch ( Exception e )
{
2011-08-22 22:59:48 +00:00
m_log . ErrorFormat ( "[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}" , e . Message , e . StackTrace ) ;
2010-06-09 19:51:24 +00:00
}
2010-03-12 22:39:15 +00:00
}
2011-08-22 22:59:48 +00:00
2010-07-14 18:51:12 +00:00
public bool AttachObject ( IClientAPI remoteClient , SceneObjectGroup group , uint AttachmentPt , bool silent )
2011-08-22 22:59:48 +00:00
{
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
if ( sp = = null )
{
m_log . ErrorFormat (
"[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}" , remoteClient . Name , remoteClient . AgentId ) ;
return false ;
}
return AttachObject ( sp , group , AttachmentPt , silent ) ;
}
2011-09-03 00:11:16 +00:00
private bool AttachObject ( IScenePresence sp , SceneObjectGroup group , uint attachmentPt , bool silent )
2010-03-05 23:18:47 +00:00
{
2011-09-12 20:57:22 +00:00
lock ( sp . AttachmentsSyncLock )
2010-11-23 04:26:07 +00:00
{
2011-09-13 19:25:32 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})",
// group.Name, group.LocalId, sp.Name, attachmentPt, silent);
2011-09-12 20:57:22 +00:00
if ( sp . GetAttachments ( attachmentPt ) . Contains ( group ) )
2011-09-06 01:40:19 +00:00
{
2011-09-12 20:57:22 +00:00
// m_log.WarnFormat(
// "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached",
// group.Name, group.LocalId, sp.Name, AttachmentPt);
2011-09-03 00:11:16 +00:00
2011-09-12 20:57:22 +00:00
return false ;
2011-09-06 01:40:19 +00:00
}
2011-09-03 00:11:16 +00:00
2011-09-12 20:57:22 +00:00
Vector3 attachPos = group . AbsolutePosition ;
// TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
// be removed when that functionality is implemented in opensim
attachmentPt & = 0x7f ;
// 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.
if ( attachmentPt ! = 0 & & attachmentPt ! = group . AttachmentPoint )
{
attachPos = Vector3 . Zero ;
}
// AttachmentPt 0 means the client chose to 'wear' the attachment.
if ( attachmentPt = = 0 )
{
// Check object for stored attachment point
attachmentPt = group . AttachmentPoint ;
}
// if we still didn't find a suitable attachment point.......
if ( attachmentPt = = 0 )
{
// Stick it on left hand with Zero Offset from the attachment point.
attachmentPt = ( uint ) AttachmentPoint . LeftHand ;
attachPos = Vector3 . Zero ;
}
group . AttachmentPoint = attachmentPt ;
group . AbsolutePosition = attachPos ;
// We also don't want to do any of the inventory operations for an NPC.
if ( sp . PresenceType ! = PresenceType . Npc )
{
// Remove any previous attachments
List < SceneObjectGroup > attachments = sp . GetAttachments ( attachmentPt ) ;
// At the moment we can only deal with a single attachment
if ( attachments . Count ! = 0 )
{
UUID oldAttachmentItemID = attachments [ 0 ] . GetFromItemID ( ) ;
if ( oldAttachmentItemID ! = UUID . Zero )
DetachSingleAttachmentToInv ( oldAttachmentItemID , sp ) ;
else
m_log . WarnFormat (
"[ATTACHMENTS MODULE]: When detaching existing attachment {0} {1} at point {2} to make way for {3} {4} for {5}, couldn't find the associated item ID to adjust inventory attachment record!" ,
attachments [ 0 ] . Name , attachments [ 0 ] . LocalId , attachmentPt , group . Name , group . LocalId , sp . Name ) ;
}
// Add the new attachment to inventory if we don't already have it.
UUID newAttachmentItemID = group . GetFromItemID ( ) ;
if ( newAttachmentItemID = = UUID . Zero )
newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv ( sp . ControllingClient , group ) . ID ;
ShowAttachInUserInventory ( sp , attachmentPt , newAttachmentItemID , group ) ;
}
AttachToAgent ( sp , group , attachmentPt , attachPos , silent ) ;
2011-09-03 00:11:16 +00:00
}
2010-11-23 04:26:07 +00:00
2010-03-05 23:18:47 +00:00
return true ;
2010-03-06 00:07:47 +00:00
}
2010-03-12 22:48:49 +00:00
2010-04-16 21:54:25 +00:00
public void RezMultipleAttachmentsFromInventory (
IClientAPI remoteClient ,
RezMultipleAttachmentsFromInvPacket . HeaderDataBlock header ,
RezMultipleAttachmentsFromInvPacket . ObjectDataBlock [ ] objects )
{
2011-09-12 20:57:22 +00:00
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
if ( sp = = null )
2010-04-16 21:54:25 +00:00
{
2011-09-12 20:57:22 +00:00
m_log . ErrorFormat (
"[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()" ,
remoteClient . Name , remoteClient . AgentId ) ;
return ;
}
lock ( sp . AttachmentsSyncLock )
{
2011-09-13 21:13:58 +00:00
// m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing multiple attachments from inventory for {0}", sp.Name);
2011-09-12 20:57:22 +00:00
foreach ( RezMultipleAttachmentsFromInvPacket . ObjectDataBlock obj in objects )
{
RezSingleAttachmentFromInventory ( sp , obj . ItemID , obj . AttachmentPt ) ;
}
2010-04-16 21:54:25 +00:00
}
}
2011-09-12 20:57:22 +00:00
public ISceneEntity RezSingleAttachmentFromInventory ( IClientAPI remoteClient , UUID itemID , uint AttachmentPt )
2010-03-12 23:20:38 +00:00
{
2011-08-17 22:41:20 +00:00
// m_log.DebugFormat(
2011-09-13 21:13:58 +00:00
// "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
2011-08-17 22:41:20 +00:00
// (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
2011-08-22 22:59:48 +00:00
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
if ( sp = = null )
{
m_log . ErrorFormat (
"[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezSingleAttachmentFromInventory()" ,
remoteClient . Name , remoteClient . AgentId ) ;
2011-08-30 21:25:38 +00:00
return null ;
2011-08-22 22:59:48 +00:00
}
2011-09-12 20:57:22 +00:00
return RezSingleAttachmentFromInventory ( sp , itemID , AttachmentPt ) ;
}
public ISceneEntity RezSingleAttachmentFromInventory ( ScenePresence sp , UUID itemID , uint AttachmentPt )
{
2011-09-13 19:25:32 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: RezSingleAttachmentFromInventory to point {0} from item {1} for {2}",
// (AttachmentPoint)AttachmentPt, itemID, sp.Name);
2010-10-14 16:08:59 +00:00
// TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
// be removed when that functionality is implemented in opensim
AttachmentPt & = 0x7f ;
2011-09-13 21:13:58 +00:00
// Viewer 2/3 sometimes asks to re-wear items that are already worn (and show up in it's inventory as such).
// This often happens during login - not sure the exact reason.
// For now, we will ignore the request. Unfortunately, this means that we need to dig through all the
// ScenePresence attachments. We can't use the data in AvatarAppearance because that's present at login
// before anything has actually been attached.
bool alreadyOn = false ;
List < SceneObjectGroup > existingAttachments = sp . GetAttachments ( ) ;
foreach ( SceneObjectGroup so in existingAttachments )
{
if ( so . GetFromItemID ( ) = = itemID )
{
alreadyOn = true ;
break ;
}
}
// if (sp.Appearance.GetAttachmentForItem(itemID) != null)
if ( alreadyOn )
{
// m_log.WarnFormat(
// "[ATTACHMENTS MODULE]: Ignoring request by {0} to wear item {1} at {2} since it is already worn",
// sp.Name, itemID, AttachmentPt);
return null ;
}
2011-09-03 00:11:16 +00:00
SceneObjectGroup att = RezSingleAttachmentFromInventoryInternal ( sp , itemID , UUID . Zero , AttachmentPt ) ;
2010-03-12 23:20:38 +00:00
2011-09-05 23:36:38 +00:00
if ( att = = null )
DetachSingleAttachmentToInv ( itemID , sp . ControllingClient ) ;
2010-03-12 23:20:38 +00:00
2011-08-30 21:25:38 +00:00
return att ;
2010-09-12 17:43:49 +00:00
}
2010-03-12 23:20:38 +00:00
2011-08-22 22:59:48 +00:00
private SceneObjectGroup RezSingleAttachmentFromInventoryInternal (
2011-09-03 00:11:16 +00:00
IScenePresence sp , UUID itemID , UUID assetID , uint attachmentPt )
2010-03-12 22:48:49 +00:00
{
IInventoryAccessModule invAccess = m_scene . RequestModuleInterface < IInventoryAccessModule > ( ) ;
if ( invAccess ! = null )
{
2011-09-12 20:57:22 +00:00
lock ( sp . AttachmentsSyncLock )
2010-03-12 22:48:49 +00:00
{
2011-09-12 20:57:22 +00:00
SceneObjectGroup objatt ;
if ( itemID ! = UUID . Zero )
objatt = invAccess . RezObject ( sp . ControllingClient ,
itemID , Vector3 . Zero , Vector3 . Zero , UUID . Zero , ( byte ) 1 , true ,
false , false , sp . UUID , true ) ;
else
objatt = invAccess . RezObject ( sp . ControllingClient ,
null , assetID , Vector3 . Zero , Vector3 . Zero , UUID . Zero , ( byte ) 1 , true ,
false , false , sp . UUID , true ) ;
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}",
// objatt.Name, remoteClient.Name, AttachmentPt);
if ( objatt ! = null )
2010-09-06 19:59:52 +00:00
{
2011-09-12 20:57:22 +00:00
// HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
objatt . HasGroupChanged = false ;
bool tainted = false ;
if ( attachmentPt ! = 0 & & attachmentPt ! = objatt . AttachmentPoint )
tainted = true ;
// This will throw if the attachment fails
try
{
AttachObject ( sp , objatt , attachmentPt , false ) ;
}
catch ( Exception e )
{
m_log . ErrorFormat (
"[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}" ,
objatt . Name , objatt . UUID , sp . Name , e . Message , e . StackTrace ) ;
// Make sure the object doesn't stick around and bail
sp . RemoveAttachment ( objatt ) ;
m_scene . DeleteSceneObject ( objatt , false ) ;
return null ;
}
if ( tainted )
objatt . HasGroupChanged = true ;
// Fire after attach, so we don't get messy perms dialogs
// 4 == AttachedRez
objatt . CreateScriptInstances ( 0 , true , m_scene . DefaultScriptEngine , 4 ) ;
objatt . ResumeScripts ( ) ;
// Do this last so that event listeners have access to all the effects of the attachment
m_scene . EventManager . TriggerOnAttach ( objatt . LocalId , itemID , sp . UUID ) ;
return objatt ;
2010-09-06 19:59:52 +00:00
}
2011-09-12 20:57:22 +00:00
else
2010-09-06 19:59:52 +00:00
{
2011-09-12 20:57:22 +00:00
m_log . WarnFormat (
"[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}" ,
itemID , sp . Name , attachmentPt ) ;
2010-09-06 19:59:52 +00:00
}
2010-03-12 22:48:49 +00:00
}
}
return null ;
2010-09-12 17:43:49 +00:00
}
2010-03-05 23:35:49 +00:00
/// <summary>
/// Update the user inventory to reflect an attachment
/// </summary>
2011-08-22 22:59:48 +00:00
/// <param name="sp"></param>
2010-03-05 23:35:49 +00:00
/// <param name="AttachmentPt"></param>
/// <param name="itemID"></param>
/// <param name="att"></param>
2011-08-22 22:59:48 +00:00
private void ShowAttachInUserInventory (
2011-09-03 00:11:16 +00:00
IScenePresence sp , uint AttachmentPt , UUID itemID , SceneObjectGroup att )
2010-03-05 23:35:49 +00:00
{
// m_log.DebugFormat(
2011-09-13 21:13:58 +00:00
// "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
// att.Name, sp.Name, AttachmentPt, itemID);
2010-03-05 23:35:49 +00:00
if ( UUID . Zero = = itemID )
{
2010-03-06 00:07:47 +00:00
m_log . Error ( "[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID." ) ;
2010-03-05 23:35:49 +00:00
return ;
}
if ( 0 = = AttachmentPt )
{
2010-03-06 00:07:47 +00:00
m_log . Error ( "[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point." ) ;
2010-03-05 23:35:49 +00:00
return ;
}
2011-08-22 22:59:48 +00:00
InventoryItemBase item = new InventoryItemBase ( itemID , sp . UUID ) ;
item = m_scene . InventoryService . GetItem ( item ) ;
bool changed = sp . Appearance . SetAttachment ( ( int ) AttachmentPt , itemID , item . AssetID ) ;
if ( changed & & m_scene . AvatarFactory ! = null )
m_scene . AvatarFactory . QueueAppearanceSave ( sp . UUID ) ;
2010-03-10 04:15:36 +00:00
}
2010-03-06 00:07:47 +00:00
2010-04-16 23:00:45 +00:00
public void DetachObject ( uint objectLocalID , IClientAPI remoteClient )
{
2011-09-13 19:25:32 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: DetachObject() for object {0} on {1}", objectLocalID, remoteClient.Name);
2010-04-16 23:00:45 +00:00
SceneObjectGroup group = m_scene . GetGroupByPrim ( objectLocalID ) ;
if ( group ! = null )
{
2011-08-23 21:05:22 +00:00
DetachSingleAttachmentToInv ( group . GetFromItemID ( ) , remoteClient ) ;
2010-04-16 23:00:45 +00:00
}
}
2011-08-23 21:05:22 +00:00
public void DetachSingleAttachmentToInv ( UUID itemID , IClientAPI remoteClient )
2010-03-06 00:07:47 +00:00
{
ScenePresence presence ;
2010-03-19 12:58:34 +00:00
if ( m_scene . TryGetScenePresence ( remoteClient . AgentId , out presence ) )
2010-03-06 00:07:47 +00:00
{
2011-09-12 20:57:22 +00:00
lock ( presence . AttachmentsSyncLock )
{
// Save avatar attachment information
m_log . Debug ( "[ATTACHMENTS MODULE]: Detaching from UserID: " + remoteClient . AgentId + ", ItemID: " + itemID ) ;
2010-03-06 00:07:47 +00:00
2011-09-12 20:57:22 +00:00
bool changed = presence . Appearance . DetachAttachment ( itemID ) ;
if ( changed & & m_scene . AvatarFactory ! = null )
m_scene . AvatarFactory . QueueAppearanceSave ( remoteClient . AgentId ) ;
DetachSingleAttachmentToInv ( itemID , presence ) ;
}
2011-08-22 22:59:48 +00:00
}
2010-03-10 04:15:36 +00:00
}
2010-03-06 00:07:47 +00:00
2011-08-30 21:25:38 +00:00
public void DetachSingleAttachmentToGround ( uint soLocalId , IClientAPI remoteClient )
2010-04-16 22:15:13 +00:00
{
2011-08-30 21:06:24 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: DetachSingleAttachmentToGround() for {0}, object {1}",
2011-09-13 21:13:58 +00:00
// remoteClient.Name, soLocalId);
2011-08-30 21:06:24 +00:00
2011-08-30 21:25:38 +00:00
SceneObjectGroup so = m_scene . GetGroupByPrim ( soLocalId ) ;
2011-08-26 21:37:53 +00:00
if ( so = = null )
2010-04-16 22:15:13 +00:00
return ;
2011-08-26 22:06:41 +00:00
if ( so . AttachedAvatar ! = remoteClient . AgentId )
2010-09-30 14:57:52 +00:00
return ;
2011-08-26 21:37:53 +00:00
UUID inventoryID = so . GetFromItemID ( ) ;
2010-04-16 22:15:13 +00:00
2011-08-30 21:06:24 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: In DetachSingleAttachmentToGround(), object is {0} {1}, associated item is {2}",
// so.Name, so.LocalId, inventoryID);
2010-04-16 22:15:13 +00:00
ScenePresence presence ;
if ( m_scene . TryGetScenePresence ( remoteClient . AgentId , out presence ) )
{
2011-09-12 20:57:22 +00:00
lock ( presence . AttachmentsSyncLock )
{
if ( ! m_scene . Permissions . CanRezObject (
so . PrimCount , remoteClient . AgentId , presence . AbsolutePosition ) )
return ;
2010-04-16 22:15:13 +00:00
2011-09-12 20:57:22 +00:00
bool changed = presence . Appearance . DetachAttachment ( inventoryID ) ;
if ( changed & & m_scene . AvatarFactory ! = null )
m_scene . AvatarFactory . QueueAppearanceSave ( remoteClient . AgentId ) ;
2010-10-29 20:37:13 +00:00
2011-09-12 20:57:22 +00:00
presence . RemoveAttachment ( so ) ;
DetachSceneObjectToGround ( so , presence ) ;
2010-04-16 22:15:13 +00:00
2011-09-12 20:57:22 +00:00
List < UUID > uuids = new List < UUID > ( ) ;
uuids . Add ( inventoryID ) ;
m_scene . InventoryService . DeleteItems ( remoteClient . AgentId , uuids ) ;
remoteClient . SendRemoveInventoryItem ( inventoryID ) ;
}
2010-04-16 22:15:13 +00:00
2011-09-12 20:57:22 +00:00
m_scene . EventManager . TriggerOnAttach ( so . LocalId , so . UUID , UUID . Zero ) ;
}
2010-04-16 22:15:13 +00:00
}
2011-08-26 21:49:11 +00:00
/// <summary>
2011-08-30 21:06:24 +00:00
/// Detach the given scene object to the ground.
2011-08-26 21:49:11 +00:00
/// </summary>
/// <remarks>
/// The caller has to take care of all the other work in updating avatar appearance, inventory, etc.
/// </remarks>
/// <param name="so">The scene object to detach.</param>
/// <param name="sp">The scene presence from which the scene object is being detached.</param>
private void DetachSceneObjectToGround ( SceneObjectGroup so , ScenePresence sp )
{
SceneObjectPart rootPart = so . RootPart ;
rootPart . FromItemID = UUID . Zero ;
so . AbsolutePosition = sp . AbsolutePosition ;
2011-08-26 22:06:41 +00:00
so . AttachedAvatar = UUID . Zero ;
2011-08-26 21:49:11 +00:00
rootPart . SetParentLocalId ( 0 ) ;
so . ClearPartAttachmentData ( ) ;
rootPart . ApplyPhysics ( rootPart . GetEffectiveObjectFlags ( ) , rootPart . VolumeDetectActive , m_scene . m_physicalPrim ) ;
so . HasGroupChanged = true ;
rootPart . Rezzed = DateTime . Now ;
rootPart . RemFlag ( PrimFlags . TemporaryOnRez ) ;
so . AttachToBackup ( ) ;
m_scene . EventManager . TriggerParcelPrimCountTainted ( ) ;
rootPart . ScheduleFullUpdate ( ) ;
rootPart . ClearUndoState ( ) ;
}
2010-04-16 22:15:13 +00:00
2010-03-06 00:07:47 +00:00
// What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards.
// To LocalId or UUID, *THAT* is the question. How now Brown UUID??
2011-09-03 00:11:16 +00:00
private void DetachSingleAttachmentToInv ( UUID itemID , IScenePresence sp )
2010-03-06 00:07:47 +00:00
{
2011-09-13 19:25:32 +00:00
// m_log.DebugFormat("[ATTACHMENTS MODULE]: Detaching item {0} to inventory for {1}", itemID, sp.Name);
2010-03-06 00:07:47 +00:00
if ( itemID = = UUID . Zero ) // If this happened, someone made a mistake....
return ;
// We can NOT use the dictionries here, as we are looking
// for an entity by the fromAssetID, which is NOT the prim UUID
2010-09-10 19:04:12 +00:00
EntityBase [ ] detachEntities = m_scene . GetEntities ( ) ;
2010-03-06 00:07:47 +00:00
SceneObjectGroup group ;
2011-09-12 20:57:22 +00:00
lock ( sp . AttachmentsSyncLock )
2010-03-06 00:07:47 +00:00
{
2011-09-12 20:57:22 +00:00
foreach ( EntityBase entity in detachEntities )
2010-03-06 00:07:47 +00:00
{
2011-09-12 20:57:22 +00:00
if ( entity is SceneObjectGroup )
2010-03-06 00:07:47 +00:00
{
2011-09-12 20:57:22 +00:00
group = ( SceneObjectGroup ) entity ;
if ( group . GetFromItemID ( ) = = itemID )
{
m_scene . EventManager . TriggerOnAttach ( group . LocalId , itemID , UUID . Zero ) ;
sp . RemoveAttachment ( group ) ;
// Prepare sog for storage
group . AttachedAvatar = UUID . Zero ;
group . ForEachPart (
delegate ( SceneObjectPart part )
{
// If there are any scripts,
// then always trigger a new object and state persistence in UpdateKnownItem()
if ( part . Inventory . ContainsScripts ( ) )
group . HasGroupChanged = true ;
}
) ;
group . RootPart . SetParentLocalId ( 0 ) ;
group . IsAttachment = false ;
group . AbsolutePosition = group . RootPart . AttachedPos ;
UpdateKnownItem ( sp . ControllingClient , group , group . GetFromItemID ( ) , group . OwnerID ) ;
m_scene . DeleteSceneObject ( group , false ) ;
return ;
}
2010-03-06 00:07:47 +00:00
}
}
}
2010-03-10 04:15:36 +00:00
}
2010-08-26 20:50:19 +00:00
2010-12-14 00:11:41 +00:00
public void UpdateAttachmentPosition ( SceneObjectGroup sog , Vector3 pos )
{
// First we save the
// attachment point information, then we update the relative
// positioning. Then we have to mark the object as NOT an
// attachment. This is necessary in order to correctly save
// and retrieve GroupPosition information for the attachment.
// Finally, we restore the object's attachment status.
2011-08-26 23:33:24 +00:00
uint attachmentPoint = sog . AttachmentPoint ;
2010-12-14 00:11:41 +00:00
sog . UpdateGroupPosition ( pos ) ;
2011-08-26 23:15:21 +00:00
sog . IsAttachment = false ;
2010-12-14 00:11:41 +00:00
sog . AbsolutePosition = sog . RootPart . AttachedPos ;
2011-08-26 23:33:24 +00:00
sog . AttachmentPoint = attachmentPoint ;
2010-12-14 00:11:41 +00:00
sog . HasGroupChanged = true ;
}
2010-08-26 21:09:52 +00:00
/// <summary>
/// Update the attachment asset for the new sog details if they have changed.
/// </summary>
2011-08-17 23:53:05 +00:00
/// <remarks>
2010-08-26 21:09:52 +00:00
/// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects,
/// these details are not stored on the region.
2011-08-17 23:53:05 +00:00
/// </remarks>
2010-08-26 21:09:52 +00:00
/// <param name="remoteClient"></param>
/// <param name="grp"></param>
/// <param name="itemID"></param>
/// <param name="agentID"></param>
2010-11-16 21:01:56 +00:00
public void UpdateKnownItem ( IClientAPI remoteClient , SceneObjectGroup grp , UUID itemID , UUID agentID )
2010-08-26 21:09:52 +00:00
{
if ( grp ! = null )
{
2011-09-13 21:13:58 +00:00
if ( grp . HasGroupChanged | | grp . ContainsScripts ( ) )
2010-08-26 21:09:52 +00:00
{
2011-08-26 20:46:12 +00:00
m_log . DebugFormat (
2011-09-13 21:13:58 +00:00
"[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}" ,
2011-08-26 23:33:24 +00:00
grp . UUID , grp . AttachmentPoint ) ;
2011-08-26 20:46:12 +00:00
2011-09-13 21:13:58 +00:00
string sceneObjectXml = SceneObjectSerializer . ToOriginalXmlFormat ( grp ) ;
2010-08-26 21:09:52 +00:00
2011-09-13 21:13:58 +00:00
InventoryItemBase item = new InventoryItemBase ( itemID , remoteClient . AgentId ) ;
item = m_scene . InventoryService . GetItem ( item ) ;
2010-08-26 21:09:52 +00:00
2011-09-13 21:13:58 +00:00
if ( item ! = null )
{
AssetBase asset = m_scene . CreateAsset (
grp . GetPartName ( grp . LocalId ) ,
grp . GetPartDescription ( grp . LocalId ) ,
( sbyte ) AssetType . Object ,
Utils . StringToBytes ( sceneObjectXml ) ,
remoteClient . AgentId ) ;
m_scene . AssetService . Store ( asset ) ;
item . AssetID = asset . FullID ;
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
2010-08-26 21:09:52 +00:00
{
2011-09-13 21:13:58 +00:00
m_log . DebugFormat (
"[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}" ,
grp . UUID , grp . AttachmentPoint ) ;
2010-08-26 21:09:52 +00:00
}
}
2010-09-01 00:11:52 +00:00
}
/// <summary>
/// Attach this scene object to the given avatar.
/// </summary>
2011-09-03 00:11:16 +00:00
/// <remarks>
2010-09-01 00:11:52 +00:00
/// This isn't publicly available since attachments should always perform the corresponding inventory
/// operation (to show the attach in user inventory and update the asset with positional information).
2011-09-03 00:11:16 +00:00
/// </remarks>
2010-09-01 00:11:52 +00:00
/// <param name="sp"></param>
/// <param name="so"></param>
/// <param name="attachmentpoint"></param>
2011-04-29 03:19:54 +00:00
/// <param name="attachOffset"></param>
2010-09-01 00:11:52 +00:00
/// <param name="silent"></param>
2011-09-03 00:11:16 +00:00
private void AttachToAgent (
IScenePresence avatar , SceneObjectGroup so , uint attachmentpoint , Vector3 attachOffset , bool silent )
2010-09-01 00:11:52 +00:00
{
2011-09-13 21:13:58 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}",
2011-08-17 22:41:20 +00:00
// so.Name, avatar.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos);
2010-09-01 00:11:52 +00:00
so . DetachFromBackup ( ) ;
// Remove from database and parcel prim count
m_scene . DeleteFromStorage ( so . UUID ) ;
m_scene . EventManager . TriggerParcelPrimCountTainted ( ) ;
2011-08-26 22:06:41 +00:00
so . AttachedAvatar = avatar . UUID ;
2010-09-01 00:11:52 +00:00
if ( so . RootPart . PhysActor ! = null )
{
m_scene . PhysicsScene . RemovePrim ( so . RootPart . PhysActor ) ;
so . RootPart . PhysActor = null ;
}
2011-04-29 03:19:54 +00:00
so . AbsolutePosition = attachOffset ;
so . RootPart . AttachedPos = attachOffset ;
2011-08-26 23:15:21 +00:00
so . IsAttachment = true ;
2010-09-01 00:11:52 +00:00
so . RootPart . SetParentLocalId ( avatar . LocalId ) ;
2011-08-26 23:33:24 +00:00
so . AttachmentPoint = attachmentpoint ;
2010-09-01 00:11:52 +00:00
avatar . AddAttachment ( so ) ;
if ( ! silent )
{
// Killing it here will cause the client to deselect it
// It then reappears on the avatar, deselected
// through the full update below
/ /
if ( so . IsSelected )
{
m_scene . SendKillObject ( so . RootPart . LocalId ) ;
}
so . IsSelected = false ; // fudge....
so . ScheduleGroupForFullUpdate ( ) ;
}
// In case it is later dropped again, don't let
// it get cleaned up
so . RootPart . RemFlag ( PrimFlags . TemporaryOnRez ) ;
}
2011-08-24 20:35:44 +00:00
/// <summary>
2011-09-06 01:29:22 +00:00
/// Add a scene object as a new attachment in the user inventory.
2011-08-24 20:35:44 +00:00
/// </summary>
/// <param name="remoteClient"></param>
/// <param name="grp"></param>
2011-08-24 20:40:36 +00:00
/// <returns>The user inventory item created that holds the attachment.</returns>
2011-09-06 01:29:22 +00:00
private InventoryItemBase AddSceneObjectAsNewAttachmentInInv ( IClientAPI remoteClient , SceneObjectGroup grp )
2011-08-24 20:35:44 +00:00
{
2011-09-13 21:13:58 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}",
// grp.Name, grp.LocalId, remoteClient.Name);
2011-08-24 20:35:44 +00:00
Vector3 inventoryStoredPosition = new Vector3
( ( ( grp . AbsolutePosition . X > ( int ) Constants . RegionSize )
? Constants . RegionSize - 6
: grp . AbsolutePosition . X )
,
( grp . AbsolutePosition . Y > ( int ) Constants . RegionSize )
? Constants . RegionSize - 6
: grp . AbsolutePosition . Y ,
grp . AbsolutePosition . Z ) ;
Vector3 originalPosition = grp . AbsolutePosition ;
grp . AbsolutePosition = inventoryStoredPosition ;
// If we're being called from a script, then trying to serialize that same script's state will not complete
// in any reasonable time period. Therefore, we'll avoid it. The worst that can happen is that if
// the client/server crashes rather than logging out normally, the attachment's scripts will resume
// without state on relog. Arguably, this is what we want anyway.
string sceneObjectXml = SceneObjectSerializer . ToOriginalXmlFormat ( grp , false ) ;
grp . AbsolutePosition = originalPosition ;
AssetBase asset = m_scene . CreateAsset (
grp . GetPartName ( grp . LocalId ) ,
grp . GetPartDescription ( grp . LocalId ) ,
( sbyte ) AssetType . Object ,
Utils . StringToBytes ( sceneObjectXml ) ,
remoteClient . AgentId ) ;
m_scene . AssetService . Store ( asset ) ;
InventoryItemBase item = new InventoryItemBase ( ) ;
item . CreatorId = grp . RootPart . CreatorID . ToString ( ) ;
item . CreatorData = grp . RootPart . CreatorData ;
item . Owner = remoteClient . AgentId ;
item . ID = UUID . Random ( ) ;
item . AssetID = asset . FullID ;
item . Description = asset . Description ;
item . Name = asset . Name ;
item . AssetType = asset . Type ;
item . InvType = ( int ) InventoryType . Object ;
InventoryFolderBase folder = m_scene . InventoryService . GetFolderForType ( remoteClient . AgentId , AssetType . Object ) ;
if ( folder ! = null )
item . Folder = folder . ID ;
else // oopsies
item . Folder = UUID . Zero ;
if ( ( remoteClient . AgentId ! = grp . RootPart . OwnerID ) & & m_scene . Permissions . PropagatePermissions ( ) )
{
item . BasePermissions = grp . RootPart . NextOwnerMask ;
item . CurrentPermissions = grp . RootPart . NextOwnerMask ;
item . NextPermissions = grp . RootPart . NextOwnerMask ;
item . EveryOnePermissions = grp . RootPart . EveryoneMask & grp . RootPart . NextOwnerMask ;
item . GroupPermissions = grp . RootPart . GroupMask & grp . RootPart . NextOwnerMask ;
}
else
{
item . BasePermissions = grp . RootPart . BaseMask ;
item . CurrentPermissions = grp . RootPart . OwnerMask ;
item . NextPermissions = grp . RootPart . NextOwnerMask ;
item . EveryOnePermissions = grp . RootPart . EveryoneMask ;
item . GroupPermissions = grp . RootPart . GroupMask ;
}
item . CreationDate = Util . UnixTimeSinceEpoch ( ) ;
// sets itemID so client can show item as 'attached' in inventory
grp . SetFromItemID ( item . ID ) ;
if ( m_scene . AddInventoryItem ( item ) )
{
remoteClient . SendInventoryItemCreateUpdate ( item , 0 ) ;
}
else
{
if ( m_dialogModule ! = null )
m_dialogModule . SendAlertToUser ( remoteClient , "Operation failed" ) ;
}
2011-08-24 20:40:36 +00:00
return item ;
2011-08-24 20:35:44 +00:00
}
2010-03-05 23:18:47 +00:00
}
2011-09-12 20:57:22 +00:00
}