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
{
2011-10-04 21:40:39 +00:00
#region 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 ;
2011-09-30 00:19:22 +00:00
/// <summary>
/// Are attachments enabled?
/// </summary>
public bool Enabled { get ; private set ; }
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
2011-09-30 00:19:22 +00:00
public void Initialise ( IConfigSource source )
{
IConfig config = source . Configs [ "Attachments" ] ;
if ( config ! = null )
Enabled = config . GetBoolean ( "Enabled" , true ) ;
else
Enabled = true ;
}
2010-07-30 18:26:54 +00:00
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 ) ;
2011-09-30 00:19:22 +00:00
if ( Enabled )
m_scene . EventManager . OnNewClient + = SubscribeToClientEvents ;
2010-07-30 20:41:44 +00:00
// 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 ) ;
2011-09-30 00:19:22 +00:00
if ( Enabled )
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
}
2011-10-04 21:40:39 +00:00
# endregion
#region IAttachmentsModule
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 )
{
2011-09-30 00:19:22 +00:00
if ( ! Enabled )
return ;
2011-08-30 22:05:43 +00:00
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
2011-10-03 23:44:32 +00:00
RezSingleAttachmentFromInventory ( sp , 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-09-30 00:19:22 +00:00
if ( ! Enabled )
return ;
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 ;
2011-10-04 21:40:39 +00:00
UpdateKnownItem ( sp , grp ) ;
2011-08-30 22:32:30 +00:00
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-09-30 00:19:22 +00:00
if ( ! Enabled )
return ;
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
2011-10-03 23:44:32 +00:00
public 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 )
2011-10-04 21:40:39 +00:00
DetachSingleAttachmentToInvInternal ( sp , oldAttachmentItemID ) ;
2011-09-12 20:57:22 +00:00
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 )
2011-10-04 21:40:39 +00:00
newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv ( sp , group ) . ID ;
2011-09-12 20:57:22 +00:00
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 ;
2011-09-12 20:57:22 +00:00
}
2011-10-03 23:44:32 +00:00
public ISceneEntity RezSingleAttachmentFromInventory ( IScenePresence sp , UUID itemID , uint AttachmentPt )
2011-09-12 20:57:22 +00:00
{
2011-09-30 00:19:22 +00:00
if ( ! Enabled )
return null ;
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 )
2011-10-04 21:40:39 +00:00
DetachSingleAttachmentToInv ( sp , itemID ) ;
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-10-04 21:40:39 +00:00
public void RezMultipleAttachmentsFromInventory ( IScenePresence sp , List < KeyValuePair < UUID , uint > > rezlist )
2010-03-06 00:07:47 +00:00
{
2011-09-30 00:19:22 +00:00
if ( ! Enabled )
return ;
2011-10-04 21:40:39 +00:00
// m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing multiple attachments from inventory for {0}", sp.Name);
lock ( sp . AttachmentsSyncLock )
2010-03-06 00:07:47 +00:00
{
2011-10-04 21:40:39 +00:00
foreach ( KeyValuePair < UUID , uint > rez in rezlist )
2011-09-12 20:57:22 +00:00
{
2011-10-04 21:40:39 +00:00
RezSingleAttachmentFromInventory ( sp , rez . Key , rez . Value ) ;
2011-09-12 20:57:22 +00:00
}
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-10-03 23:44:32 +00:00
public void DetachSingleAttachmentToGround ( IScenePresence sp , uint soLocalId )
2010-04-16 22:15:13 +00:00
{
2011-09-30 00:19:22 +00:00
if ( ! Enabled )
return ;
2011-08-30 21:06:24 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: DetachSingleAttachmentToGround() for {0}, object {1}",
2011-10-03 23:44:32 +00:00
// sp.UUID, 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-10-03 23:44:32 +00:00
if ( so . AttachedAvatar ! = sp . UUID )
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);
2011-10-03 23:44:32 +00:00
lock ( sp . AttachmentsSyncLock )
2010-04-16 22:15:13 +00:00
{
2011-10-03 23:44:32 +00:00
if ( ! m_scene . Permissions . CanRezObject (
so . PrimCount , sp . UUID , sp . AbsolutePosition ) )
2011-10-04 21:40:39 +00:00
return ;
2011-10-03 23:44:32 +00:00
bool changed = sp . Appearance . DetachAttachment ( inventoryID ) ;
if ( changed & & m_scene . AvatarFactory ! = null )
2011-10-04 21:40:39 +00:00
m_scene . AvatarFactory . QueueAppearanceSave ( sp . UUID ) ;
sp . RemoveAttachment ( so ) ;
SceneObjectPart rootPart = so . RootPart ;
rootPart . FromItemID = UUID . Zero ;
so . AbsolutePosition = sp . AbsolutePosition ;
so . AttachedAvatar = UUID . Zero ;
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 ( ) ;
2011-10-03 23:44:32 +00:00
rootPart . ClearUndoState ( ) ;
List < UUID > uuids = new List < UUID > ( ) ;
uuids . Add ( inventoryID ) ;
m_scene . InventoryService . DeleteItems ( sp . UUID , uuids ) ;
sp . ControllingClient . SendRemoveInventoryItem ( inventoryID ) ;
2011-09-12 20:57:22 +00:00
}
2011-08-26 21:49:11 +00:00
2011-10-03 23:44:32 +00:00
m_scene . EventManager . TriggerOnAttach ( so . LocalId , so . UUID , UUID . Zero ) ;
2011-08-26 21:49:11 +00:00
}
2010-03-06 00:07:47 +00:00
2011-10-04 21:40:39 +00:00
public void DetachSingleAttachmentToInv ( IScenePresence sp , UUID itemID )
{
2011-09-12 20:57:22 +00:00
lock ( sp . AttachmentsSyncLock )
2010-03-06 00:07:47 +00:00
{
2011-10-04 21:40:39 +00:00
// Save avatar attachment information
m_log . Debug ( "[ATTACHMENTS MODULE]: Detaching from UserID: " + sp . UUID + ", ItemID: " + itemID ) ;
2011-09-12 20:57:22 +00:00
2011-10-04 21:40:39 +00:00
bool changed = sp . Appearance . DetachAttachment ( itemID ) ;
if ( changed & & m_scene . AvatarFactory ! = null )
m_scene . AvatarFactory . QueueAppearanceSave ( sp . UUID ) ;
2011-09-12 20:57:22 +00:00
2011-10-04 21:40:39 +00:00
DetachSingleAttachmentToInvInternal ( sp , itemID ) ;
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 )
{
2011-09-30 00:19:22 +00:00
if ( ! Enabled )
return ;
2010-12-14 00:11:41 +00:00
// 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 ;
}
2011-10-04 21:40:39 +00:00
# endregion
#region AttachmentModule private methods
// This is public but is not part of the IAttachmentsModule interface.
// RegionCombiner module needs to poke at it to deliver client events.
// This breaks the encapsulation of the module and should get fixed somehow.
public void SubscribeToClientEvents ( IClientAPI client )
{
client . OnRezSingleAttachmentFromInv + = Client_OnRezSingleAttachmentFromInv ;
client . OnRezMultipleAttachmentsFromInv + = Client_OnRezMultipleAttachmentsFromInv ;
client . OnObjectAttach + = Client_OnObjectAttach ;
client . OnObjectDetach + = Client_OnObjectDetach ;
client . OnDetachAttachmentIntoInv + = Client_OnDetachAttachmentIntoInv ;
client . OnObjectDrop + = Client_OnObjectDrop ;
}
// This is public but is not part of the IAttachmentsModule interface.
// RegionCombiner module needs to poke at it to deliver client events.
// This breaks the encapsulation of the module and should get fixed somehow.
public void UnsubscribeFromClientEvents ( IClientAPI client )
{
client . OnRezSingleAttachmentFromInv - = Client_OnRezSingleAttachmentFromInv ;
client . OnRezMultipleAttachmentsFromInv - = Client_OnRezMultipleAttachmentsFromInv ;
client . OnObjectAttach - = Client_OnObjectAttach ;
client . OnObjectDetach - = Client_OnObjectDetach ;
client . OnDetachAttachmentIntoInv - = Client_OnDetachAttachmentIntoInv ;
client . OnObjectDrop - = Client_OnObjectDrop ;
}
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>
2011-10-04 21:40:39 +00:00
/// <param name="sp"></param>
2010-08-26 21:09:52 +00:00
/// <param name="grp"></param>
2011-10-04 21:40:39 +00:00
private void UpdateKnownItem ( IScenePresence sp , SceneObjectGroup grp )
2010-08-26 21:09:52 +00:00
{
2011-09-13 21:24:33 +00:00
if ( grp . HasGroupChanged | | grp . ContainsScripts ( ) )
2010-08-26 21:09:52 +00:00
{
2011-09-13 21:24:33 +00:00
m_log . DebugFormat (
"[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}" ,
grp . UUID , grp . AttachmentPoint ) ;
2011-08-26 20:46:12 +00:00
2011-09-13 21:24:33 +00:00
string sceneObjectXml = SceneObjectSerializer . ToOriginalXmlFormat ( grp ) ;
2010-08-26 21:09:52 +00:00
2011-10-04 21:40:39 +00:00
InventoryItemBase item = new InventoryItemBase ( grp . GetFromItemID ( ) , sp . UUID ) ;
2011-09-13 21:24:33 +00:00
item = m_scene . InventoryService . GetItem ( item ) ;
2010-08-26 21:09:52 +00:00
2011-09-13 21:24:33 +00:00
if ( item ! = null )
2010-08-26 21:09:52 +00:00
{
2011-09-13 21:24:33 +00:00
AssetBase asset = m_scene . CreateAsset (
grp . GetPartName ( grp . LocalId ) ,
grp . GetPartDescription ( grp . LocalId ) ,
( sbyte ) AssetType . Object ,
Utils . StringToBytes ( sceneObjectXml ) ,
2011-10-04 21:40:39 +00:00
sp . UUID ) ;
2011-09-13 21:24:33 +00:00
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!
2011-10-04 21:40:39 +00:00
if ( sp . ControllingClient ! = null )
sp . ControllingClient . SendInventoryItemCreateUpdate ( item , 0 ) ;
2010-08-26 21:09:52 +00:00
}
}
2011-09-13 21:24:33 +00:00
else
{
m_log . DebugFormat (
"[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}" ,
grp . UUID , grp . AttachmentPoint ) ;
}
2011-10-04 21:40:39 +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 (
2011-10-04 21:40:39 +00:00
IScenePresence sp , SceneObjectGroup so , uint attachmentpoint , Vector3 attachOffset , bool silent )
2010-09-01 00:11:52 +00:00
{
2011-10-04 21:40:39 +00:00
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}",
// 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-10-04 21:40:39 +00:00
so . AttachedAvatar = sp . 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 ;
2011-10-04 21:40:39 +00:00
so . RootPart . SetParentLocalId ( sp . LocalId ) ;
2011-08-26 23:33:24 +00:00
so . AttachmentPoint = attachmentpoint ;
2010-09-01 00:11:52 +00:00
2011-10-04 21:40:39 +00:00
sp . AddAttachment ( so ) ;
2010-09-01 00:11:52 +00:00
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 ( ) ;
}
2011-10-04 21:40:39 +00:00
2010-09-01 00:11:52 +00:00
// 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-10-04 21:40:39 +00:00
private InventoryItemBase AddSceneObjectAsNewAttachmentInInv ( IScenePresence sp , SceneObjectGroup grp )
2011-08-24 20:35:44 +00:00
{
2011-10-04 21:40:39 +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 ) ,
2011-10-04 21:40:39 +00:00
sp . UUID ) ;
2011-08-24 20:35:44 +00:00
m_scene . AssetService . Store ( asset ) ;
InventoryItemBase item = new InventoryItemBase ( ) ;
item . CreatorId = grp . RootPart . CreatorID . ToString ( ) ;
item . CreatorData = grp . RootPart . CreatorData ;
2011-10-04 21:40:39 +00:00
item . Owner = sp . UUID ;
2011-08-24 20:35:44 +00:00
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 ;
2011-10-04 21:40:39 +00:00
InventoryFolderBase folder = m_scene . InventoryService . GetFolderForType ( sp . UUID , AssetType . Object ) ;
2011-08-24 20:35:44 +00:00
if ( folder ! = null )
item . Folder = folder . ID ;
else // oopsies
item . Folder = UUID . Zero ;
2011-10-04 21:40:39 +00:00
if ( ( sp . UUID ! = grp . RootPart . OwnerID ) & & m_scene . Permissions . PropagatePermissions ( ) )
2011-08-24 20:35:44 +00:00
{
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 ) )
{
2011-10-04 21:40:39 +00:00
sp . ControllingClient . SendInventoryItemCreateUpdate ( item , 0 ) ;
2011-08-24 20:35:44 +00:00
}
else
{
if ( m_dialogModule ! = null )
2011-10-04 21:40:39 +00:00
m_dialogModule . SendAlertToUser ( sp . ControllingClient , "Operation failed" ) ;
2011-08-24 20:35:44 +00:00
}
2011-08-24 20:40:36 +00:00
return item ;
2011-08-24 20:35:44 +00:00
}
2011-10-04 21:40:39 +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??
private void DetachSingleAttachmentToInvInternal ( IScenePresence sp , UUID itemID )
{
// m_log.DebugFormat("[ATTACHMENTS MODULE]: Detaching item {0} to inventory for {1}", itemID, sp.Name);
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
EntityBase [ ] detachEntities = m_scene . GetEntities ( ) ;
SceneObjectGroup group ;
lock ( sp . AttachmentsSyncLock )
{
foreach ( EntityBase entity in detachEntities )
{
if ( entity is SceneObjectGroup )
{
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 . RootPart . SetParentLocalId ( 0 ) ;
group . IsAttachment = false ;
group . AbsolutePosition = group . RootPart . AttachedPos ;
UpdateKnownItem ( sp , group ) ;
m_scene . DeleteSceneObject ( group , false ) ;
return ;
}
}
}
}
}
private SceneObjectGroup RezSingleAttachmentFromInventoryInternal (
IScenePresence sp , UUID itemID , UUID assetID , uint attachmentPt )
{
IInventoryAccessModule invAccess = m_scene . RequestModuleInterface < IInventoryAccessModule > ( ) ;
if ( invAccess ! = null )
{
lock ( sp . AttachmentsSyncLock )
{
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 )
{
// 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 ;
}
else
{
m_log . WarnFormat (
"[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}" ,
itemID , sp . Name , attachmentPt ) ;
}
}
}
return null ;
}
/// <summary>
/// Update the user inventory to reflect an attachment
/// </summary>
/// <param name="sp"></param>
/// <param name="AttachmentPt"></param>
/// <param name="itemID"></param>
/// <param name="att"></param>
private void ShowAttachInUserInventory ( IScenePresence sp , uint AttachmentPt , UUID itemID , SceneObjectGroup att )
{
// m_log.DebugFormat(
// "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
// att.Name, sp.Name, AttachmentPt, itemID);
if ( UUID . Zero = = itemID )
{
m_log . Error ( "[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID." ) ;
return ;
}
if ( 0 = = AttachmentPt )
{
m_log . Error ( "[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point." ) ;
return ;
}
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 ) ;
}
# endregion
#region Client Event Handlers
private ISceneEntity Client_OnRezSingleAttachmentFromInv ( IClientAPI remoteClient , UUID itemID , uint AttachmentPt )
{
if ( ! Enabled )
return null ;
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
// (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
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 ) ;
return null ;
}
return RezSingleAttachmentFromInventory ( sp , itemID , AttachmentPt ) ;
}
private void Client_OnRezMultipleAttachmentsFromInv ( IClientAPI remoteClient , List < KeyValuePair < UUID , uint > > rezlist )
{
if ( ! Enabled )
return ;
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
if ( sp ! = null )
RezMultipleAttachmentsFromInventory ( sp , rezlist ) ;
else
m_log . ErrorFormat (
"[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()" ,
remoteClient . Name , remoteClient . AgentId ) ;
}
private void Client_OnObjectAttach ( IClientAPI remoteClient , uint objectLocalID , uint AttachmentPt , bool silent )
{
// m_log.DebugFormat(
// "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
// objectLocalID, remoteClient.Name, AttachmentPt, silent);
if ( ! Enabled )
return ;
try
{
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 ;
}
// 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 ) )
{
remoteClient . SendAgentAlertMessage (
"You don't have sufficient permissions to attach this object" , false ) ;
return ;
}
// 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 ;
// Calls attach with a Zero position
if ( AttachObject ( sp , part . ParentGroup , AttachmentPt , false ) )
{
m_scene . EventManager . TriggerOnAttach ( objectLocalID , part . ParentGroup . GetFromItemID ( ) , remoteClient . AgentId ) ;
// Save avatar attachment information
m_log . Debug (
"[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient . AgentId
+ ", AttachmentPoint: " + AttachmentPt ) ;
}
}
catch ( Exception e )
{
m_log . ErrorFormat ( "[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}" , e . Message , e . StackTrace ) ;
}
}
private void Client_OnObjectDetach ( uint objectLocalID , IClientAPI remoteClient )
{
if ( ! Enabled )
return ;
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
SceneObjectGroup group = m_scene . GetGroupByPrim ( objectLocalID ) ;
if ( sp ! = null & & group ! = null )
DetachSingleAttachmentToInv ( sp , group . GetFromItemID ( ) ) ;
}
private void Client_OnDetachAttachmentIntoInv ( UUID itemID , IClientAPI remoteClient )
{
if ( ! Enabled )
return ;
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
if ( sp ! = null )
DetachSingleAttachmentToInv ( sp , itemID ) ;
}
private void Client_OnObjectDrop ( uint soLocalId , IClientAPI remoteClient )
{
if ( ! Enabled )
return ;
ScenePresence sp = m_scene . GetScenePresence ( remoteClient . AgentId ) ;
if ( sp ! = null )
DetachSingleAttachmentToGround ( sp , soLocalId ) ;
}
# endregion
2010-03-05 23:18:47 +00:00
}
2011-10-03 18:55:54 +00:00
}