From dfe5e9d4ebb705d0c20d6260bae5d11659ac904d Mon Sep 17 00:00:00 2001
From: Justin Clarke Casey
Date: Mon, 7 Apr 2008 01:46:00 +0000
Subject: * EXPERIMENTAL ROUGH DRAFT: First rough implementation of avatar to
 avatar item giving * Now you can drag an object from your inventory and give
 it to another avatar * !!! Use at your own risk !!!  Many things are
 unimplemented as of yet, including permissions (the person receiving your
 item can probably do absolutely everything with it) * Also, items for the
 receiving end up in their root folder rather than the objects folder

---
 OpenSim/Framework/IClientAPI.cs                    |  13 +-
 OpenSim/Region/ClientStack/ClientView.cs           |  84 ++++++++++-
 .../Environment/Modules/InstantMessageModule.cs    |   8 +-
 .../Region/Environment/Modules/InventoryModule.cs  | 158 ++++++++++++++++++++-
 .../Region/Environment/Scenes/Scene.Inventory.cs   |  87 +++++++++++-
 .../Region/Examples/SimpleModule/MyNpcCharacter.cs |  11 ++
 6 files changed, 349 insertions(+), 12 deletions(-)

(limited to 'OpenSim')

diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index 5687e01..17d9510 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -576,6 +576,9 @@ namespace OpenSim.Framework
 
         void SendInstantMessage(LLUUID fromAgent, LLUUID fromAgentSession, string message, LLUUID toAgent,
                                 LLUUID imSessionID, string fromName, byte dialog, uint timeStamp);
+        void SendInstantMessage(LLUUID fromAgent, LLUUID fromAgentSession, string message, LLUUID toAgent,
+                                LLUUID imSessionID, string fromName, byte dialog, uint timeStamp, 
+                                byte[] binaryBucket);        
 
         void SendLayerData(float[] map);
         void SendLayerData(int px, int py, float[] map);
@@ -620,7 +623,7 @@ namespace OpenSim.Framework
 
         void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, LLVector3 position,
                                  LLQuaternion rotation, LLVector3 velocity, LLVector3 rotationalvelocity);
-
+        
         void SendInventoryFolderDetails(LLUUID ownerID, LLUUID folderID, List<InventoryItemBase> items,
                                         List<InventoryFolderBase> folders, bool fetchFolders,
                                         bool fetchItems);
@@ -635,6 +638,14 @@ namespace OpenSim.Framework
 
         void SendRemoveInventoryItem(LLUUID itemID);
         void SendTaskInventory(LLUUID taskID, short serial, byte[] fileName);
+        
+        /// <summary>
+        /// Used by the server to inform the client of a new inventory item.  Used when transferring items
+        /// between avatars, possibly among other things.
+        /// </summary>
+        /// <param name="item"></param>
+        void SendBulkUpdateInventory(InventoryItemBase item);
+        
         void SendXferPacket(ulong xferID, uint packet, byte[] data);
         void SendAvatarPickerReply(AvatarPickerReplyPacket Pack);
 
diff --git a/OpenSim/Region/ClientStack/ClientView.cs b/OpenSim/Region/ClientStack/ClientView.cs
index a67c247..c98eb84 100644
--- a/OpenSim/Region/ClientStack/ClientView.cs
+++ b/OpenSim/Region/ClientStack/ClientView.cs
@@ -482,6 +482,11 @@ namespace OpenSim.Region.ClientStack
             return result;
         }
 
+        /// <summary>
+        /// Try to process a packet using registered packet handlers
+        /// </summary>
+        /// <param name="packet"></param>
+        /// <returns>True if a handler was found which successfully processed the packet.</returns>
         protected virtual bool ProcessPacketMethod(Packet packet)
         {
             bool result = false;
@@ -865,14 +870,30 @@ namespace OpenSim.Region.ClientStack
         }
 
         /// <summary>
-        /// 
+        /// Send an instant message to this client
         /// </summary>
         /// <param name="message"></param>
         /// <param name="target"></param>
         public void SendInstantMessage(LLUUID fromAgent, LLUUID fromAgentSession, string message, LLUUID toAgent,
                                        LLUUID imSessionID, string fromName, byte dialog, uint timeStamp)
         {
-            ImprovedInstantMessagePacket msg = (ImprovedInstantMessagePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedInstantMessage);
+            SendInstantMessage(
+                fromAgent, fromAgentSession, message, toAgent, 
+                imSessionID, fromName, dialog, timeStamp, new byte[0]);
+        }
+            
+        /// <summary>
+        /// Send an instant message to this client
+        /// </summary>
+        /// <param name="message"></param>
+        /// <param name="target"></param>
+        public void SendInstantMessage(LLUUID fromAgent, LLUUID fromAgentSession, string message, LLUUID toAgent,
+                                       LLUUID imSessionID, string fromName, byte dialog, uint timeStamp, 
+                                       byte[] binaryBucket)
+        {
+            ImprovedInstantMessagePacket msg 
+                = (ImprovedInstantMessagePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedInstantMessage);
+            
             msg.AgentData.AgentID = fromAgent;
             msg.AgentData.SessionID = fromAgentSession;
             msg.MessageBlock.FromAgentName = Helpers.StringToField(fromName);
@@ -886,7 +907,7 @@ namespace OpenSim.Region.ClientStack
             msg.MessageBlock.Timestamp = timeStamp;
             msg.MessageBlock.ToAgentID = toAgent;
             msg.MessageBlock.Message = Helpers.StringToField(message);
-            msg.MessageBlock.BinaryBucket = new byte[0];
+            msg.MessageBlock.BinaryBucket = binaryBucket;
 
             OutPacket(msg, ThrottleOutPacketType.Task);
         }
@@ -1374,12 +1395,67 @@ namespace OpenSim.Region.ClientStack
 
             OutPacket(inventoryReply, ThrottleOutPacketType.Asset);
         }
+        
+        /// <see>IClientAPI.SendBulkUpdateInventory(InventoryItemBase)</see>
+        public void SendBulkUpdateInventory(InventoryItemBase item) 
+        {            
+            uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All;
+            
+            BulkUpdateInventoryPacket bulkUpdate 
+                = (BulkUpdateInventoryPacket)PacketPool.Instance.GetPacket(PacketType.BulkUpdateInventory);
+            
+            bulkUpdate.AgentData.AgentID = AgentId;
+            bulkUpdate.AgentData.TransactionID = LLUUID.Random();  
+            
+            bulkUpdate.FolderData = new BulkUpdateInventoryPacket.FolderDataBlock[1];
+            bulkUpdate.FolderData[0] = new BulkUpdateInventoryPacket.FolderDataBlock();
+            bulkUpdate.FolderData[0].FolderID = LLUUID.Zero;
+            bulkUpdate.FolderData[0].ParentID = LLUUID.Zero;
+            bulkUpdate.FolderData[0].Type = -1;
+            bulkUpdate.FolderData[0].Name = new byte[0];
+            
+            bulkUpdate.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[1];
+            bulkUpdate.ItemData[0] = new BulkUpdateInventoryPacket.ItemDataBlock();            
+            bulkUpdate.ItemData[0].ItemID = item.inventoryID;
+            bulkUpdate.ItemData[0].AssetID = item.assetID;
+            bulkUpdate.ItemData[0].CreatorID = item.creatorsID;
+            bulkUpdate.ItemData[0].BaseMask = item.inventoryBasePermissions;
+            bulkUpdate.ItemData[0].CreationDate = 1000;
+            bulkUpdate.ItemData[0].Description = Helpers.StringToField(item.inventoryDescription);
+            bulkUpdate.ItemData[0].EveryoneMask = item.inventoryEveryOnePermissions;
+            bulkUpdate.ItemData[0].Flags = 0;
+            bulkUpdate.ItemData[0].FolderID = item.parentFolderID;
+            bulkUpdate.ItemData[0].GroupID = new LLUUID("00000000-0000-0000-0000-000000000000");
+            bulkUpdate.ItemData[0].GroupMask = 0;
+            bulkUpdate.ItemData[0].InvType = (sbyte)item.invType;
+            bulkUpdate.ItemData[0].Name = Helpers.StringToField(item.inventoryName);
+            bulkUpdate.ItemData[0].NextOwnerMask = item.inventoryNextPermissions;
+            bulkUpdate.ItemData[0].OwnerID = item.avatarID;
+            bulkUpdate.ItemData[0].OwnerMask = item.inventoryCurrentPermissions;
+            bulkUpdate.ItemData[0].SalePrice = 100;
+            bulkUpdate.ItemData[0].SaleType = 0;
+            bulkUpdate.ItemData[0].Type = (sbyte)item.assetType;
+            bulkUpdate.ItemData[0].CRC =
+                Helpers.InventoryCRC(1000, 0, bulkUpdate.ItemData[0].InvType,
+                                     bulkUpdate.ItemData[0].Type, bulkUpdate.ItemData[0].AssetID,
+                                     bulkUpdate.ItemData[0].GroupID, 100,
+                                     bulkUpdate.ItemData[0].OwnerID, bulkUpdate.ItemData[0].CreatorID,
+                                     bulkUpdate.ItemData[0].ItemID, bulkUpdate.ItemData[0].FolderID,
+                                     FULL_MASK_PERMISSIONS, 1, FULL_MASK_PERMISSIONS, FULL_MASK_PERMISSIONS,
+                                     FULL_MASK_PERMISSIONS);
+            
+            OutPacket(bulkUpdate, ThrottleOutPacketType.Asset);
+        }
 
         /// <see>IClientAPI.SendInventoryItemCreateUpdate(InventoryItemBase)</see>
         public void SendInventoryItemCreateUpdate(InventoryItemBase Item)
         {
             uint FULL_MASK_PERMISSIONS = (uint)PermissionMask.All;
-            UpdateCreateInventoryItemPacket InventoryReply = (UpdateCreateInventoryItemPacket)PacketPool.Instance.GetPacket(PacketType.UpdateCreateInventoryItem);
+            
+            UpdateCreateInventoryItemPacket InventoryReply 
+                = (UpdateCreateInventoryItemPacket)PacketPool.Instance.GetPacket(
+                    PacketType.UpdateCreateInventoryItem);
+                
             // TODO: don't create new blocks if recycling an old packet
             InventoryReply.AgentData.AgentID = AgentId;
             InventoryReply.AgentData.SimApproved = true;
diff --git a/OpenSim/Region/Environment/Modules/InstantMessageModule.cs b/OpenSim/Region/Environment/Modules/InstantMessageModule.cs
index 53d9fd9..6f7235e 100644
--- a/OpenSim/Region/Environment/Modules/InstantMessageModule.cs
+++ b/OpenSim/Region/Environment/Modules/InstantMessageModule.cs
@@ -70,13 +70,17 @@ namespace OpenSim.Region.Environment.Modules
                                       uint ParentEstateID, LLVector3 Position, LLUUID RegionID, 
                                       byte[] binaryBucket)
         {
-            bool FriendDialog = ((dialog == (byte)38) || (dialog == (byte)39) || (dialog == (byte)40));
+            bool dialogHandledElsewhere 
+                = ((dialog == (byte)38) || (dialog == (byte)39) || (dialog == (byte)40)
+                   || dialog == (byte)InstantMessageDialog.InventoryOffered
+                   || dialog == (byte)InstantMessageDialog.InventoryAccepted
+                   || dialog == (byte)InstantMessageDialog.InventoryDeclined);
 
             // IM dialogs need to be pre-processed and have their sessionID filled by the server
             // so the sim can match the transaction on the return packet.
             
             // Don't send a Friend Dialog IM with a LLUUID.Zero session.
-            if (!(FriendDialog && imSessionID == LLUUID.Zero))
+            if (!(dialogHandledElsewhere && imSessionID == LLUUID.Zero))
             {
                 foreach (Scene scene in m_scenes)
                 {
diff --git a/OpenSim/Region/Environment/Modules/InventoryModule.cs b/OpenSim/Region/Environment/Modules/InventoryModule.cs
index a0f3832..eadbb4f 100644
--- a/OpenSim/Region/Environment/Modules/InventoryModule.cs
+++ b/OpenSim/Region/Environment/Modules/InventoryModule.cs
@@ -25,7 +25,13 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+using System;
+using System.Collections.Generic;
+
+using libsecondlife;
 using Nini.Config;
+
+using OpenSim.Framework;
 using OpenSim.Region.Environment.Interfaces;
 using OpenSim.Region.Environment.Scenes;
 
@@ -33,11 +39,22 @@ namespace OpenSim.Region.Environment.Modules
 {
     public class InventoryModule : IRegionModule
     {
+        private static readonly log4net.ILog m_log 
+            = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
+        
         private Scene m_scene;
+        
+        /// <summary>
+        /// We need to keep track of the pending item offers between clients since the itemId offered only
+        /// occurs in the initial offer message, not the accept message.  So this dictionary links
+        /// IM Session Ids to ItemIds
+        /// </summary>
+        private IDictionary<LLUUID, LLUUID> m_pendingOffers = new Dictionary<LLUUID, LLUUID>();
 
         public void Initialise(Scene scene, IConfigSource config)
-        {
+        {            
             m_scene = scene;
+            scene.EventManager.OnNewClient += OnNewClient;            
         }
 
         public void PostInitialise()
@@ -57,5 +74,144 @@ namespace OpenSim.Region.Environment.Modules
         {
             get { return false; }
         }
+        
+        private void OnNewClient(IClientAPI client)
+        {
+            // Inventory giving is conducted via instant message
+            client.OnInstantMessage += OnInstantMessage;
+        }
+        
+        private void OnInstantMessage(IClientAPI client, LLUUID fromAgentID,
+                                      LLUUID fromAgentSession, LLUUID toAgentID,
+                                      LLUUID imSessionID, uint timestamp, string fromAgentName,
+                                      string message, byte dialog, bool fromGroup, byte offline, 
+                                      uint ParentEstateID, LLVector3 Position, LLUUID RegionID, 
+                                      byte[] binaryBucket)
+        {
+            if (dialog == (byte)InstantMessageDialog.InventoryOffered)
+            {
+                m_log.DebugFormat(
+                     "[AGENT INVENTORY]: Routing inventory offering message from {0}, {1} to {2}", 
+                     client.AgentId, client.Name, toAgentID);
+                
+                if (m_scene.Entities.ContainsKey(toAgentID) && m_scene.Entities[toAgentID] is ScenePresence)
+                {
+                    ScenePresence user = (ScenePresence)m_scene.Entities[toAgentID];
+                    
+                    if (!user.IsChildAgent)
+                    {
+                        //byte[] rawId = new byte[16];
+                        
+                        // First byte of the array is probably the item type
+                        // Next 16 bytes are the UUID
+                        //Array.Copy(binaryBucket, 1, rawId, 0, 16); 
+                                                                  
+                        //LLUUID itemId = new LLUUID(new Guid(rawId));
+                        LLUUID itemId = new LLUUID(binaryBucket, 1);
+                        
+                        m_log.DebugFormat(
+                             "[AGENT INVENTORY]: ItemId for giving is {0}", itemId);
+                        
+                        m_pendingOffers[imSessionID] = itemId;
+                        
+                        user.ControllingClient.SendInstantMessage(
+                            fromAgentID, fromAgentSession, message, toAgentID, imSessionID, fromAgentName, 
+                            dialog, timestamp, binaryBucket);
+                        
+                        return;
+                    }
+                    else
+                    {
+                        m_log.WarnFormat(
+                             "[AGENT INVENTORY]: Agent {0} targeted for inventory give by {1}, {2} of {3} was a child agent!", 
+                             toAgentID, client.AgentId, client.Name, message);
+                    }
+                }    
+                else
+                {
+                    m_log.WarnFormat(
+                         "[AGENT INVENTORY]: Could not find agent {0} for user {1}, {2} to give {3}",
+                         toAgentID, client.AgentId, client.Name, message);
+                }
+            }
+            else if (dialog == (byte)InstantMessageDialog.InventoryAccepted)
+            {
+                m_log.DebugFormat(
+                     "[AGENT INVENTORY]: Routing inventory accepted message from {0}, {1} to {2}", 
+                     client.AgentId, client.Name, toAgentID);
+                
+                if (m_scene.Entities.ContainsKey(toAgentID) && m_scene.Entities[toAgentID] is ScenePresence)
+                {
+                    ScenePresence user = (ScenePresence)m_scene.Entities[toAgentID];
+                    
+                    if (!user.IsChildAgent)
+                    {
+                        user.ControllingClient.SendInstantMessage(
+                            fromAgentID, fromAgentSession, message, toAgentID, imSessionID, fromAgentName, 
+                            dialog, timestamp, binaryBucket);
+                        
+                        if (m_pendingOffers.ContainsKey(imSessionID))
+                        {                                                
+                            m_log.DebugFormat(
+                                 "[AGENT INVENTORY]: Accepted item id {0}", m_pendingOffers[imSessionID]);
+                            
+                            // Since the message originates from the accepting client, the toAgentID is
+                            // the agent giving the item.
+                            m_scene.GiveInventoryItem(client, toAgentID, m_pendingOffers[imSessionID]);
+                            
+                            m_pendingOffers.Remove(imSessionID);
+                        }
+                        else
+                        {
+                            m_log.ErrorFormat(
+                                 "[AGENT INVENTORY]: Could not find an item associated with session id {0} to accept", 
+                                 imSessionID);
+                        }
+                                                
+                        return;
+                    }
+                    else
+                    {
+                        m_log.WarnFormat(
+                             "[AGENT INVENTORY]: Agent {0} targeted for inventory give by {1}, {2} of {3} was a child agent!", 
+                             toAgentID, client.AgentId, client.Name, message);
+                    }
+                }    
+                else
+                {
+                    m_log.WarnFormat(
+                         "[AGENT INVENTORY]: Could not find agent {0} for user {1}, {2} to give {3}",
+                         toAgentID, client.AgentId, client.Name, message);
+                }                
+            }
+            else if (dialog == (byte)InstantMessageDialog.InventoryDeclined)
+            {
+                if (m_scene.Entities.ContainsKey(toAgentID) && m_scene.Entities[toAgentID] is ScenePresence)
+                {
+                    ScenePresence user = (ScenePresence)m_scene.Entities[toAgentID];
+                    
+                    if (!user.IsChildAgent)
+                    {
+                        user.ControllingClient.SendInstantMessage(
+                            fromAgentID, fromAgentSession, message, toAgentID, imSessionID, fromAgentName, 
+                            dialog, timestamp, binaryBucket);
+                        
+                        if (m_pendingOffers.ContainsKey(imSessionID))
+                        {                                                
+                            m_log.DebugFormat(
+                                 "[AGENT INVENTORY]: Declined item id {0}", m_pendingOffers[imSessionID]);
+                            
+                            m_pendingOffers.Remove(imSessionID);
+                        }
+                        else
+                        {
+                            m_log.ErrorFormat(
+                                 "[AGENT INVENTORY]: Could not find an item associated with session id {0} to decline", 
+                                 imSessionID);
+                        } 
+                    }
+                }
+            }                
+        }
     }
 }
diff --git a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
index 2b8f344..f6f1367 100644
--- a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
+++ b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
@@ -286,10 +286,89 @@ namespace OpenSim.Region.Environment.Scenes
                     "[AGENT INVENTORY]: Agent ID " + remoteClient.AgentId + " not found for an inventory item update.");
             }
         }
+        
+        /// <summary>
+        /// Give an inventory item from one avatar to another
+        /// </summary>
+        /// <param name="recipientClient"></param>
+        /// <param name="sender"></param>
+        /// <param name="itemId"></param>
+        public void GiveInventoryItem(IClientAPI recipientClient, LLUUID senderId, LLUUID itemId)
+        {
+            // Retrieve the item from the sender
+            CachedUserInfo senderUserInfo = CommsManager.UserProfileCacheService.GetUserDetails(senderId);            
+            
+            if (senderUserInfo == null)
+            {
+                m_log.ErrorFormat(
+                     "[AGENT INVENTORY]: Failed to find sending user {0} for item {1}", senderId, itemId);
+                
+                return;
+            }
+
+            if (senderUserInfo.RootFolder != null)
+            {
+                InventoryItemBase item = senderUserInfo.RootFolder.HasItem(itemId);
+                if (item != null)
+                {             
+                    // TODO get recipient's root folder
+                    CachedUserInfo recipientUserInfo 
+                        = CommsManager.UserProfileCacheService.GetUserDetails(recipientClient.AgentId);                    
+                    
+                    if (recipientUserInfo != null)
+                    {
+                        // Insert a copy of the item into the recipient                    
+                        InventoryItemBase itemCopy = new InventoryItemBase();
+                        itemCopy.avatarID = recipientClient.AgentId;
+                        itemCopy.creatorsID = recipientClient.AgentId;
+                        itemCopy.inventoryID = LLUUID.Random();
+                        itemCopy.assetID = item.assetID;
+                        itemCopy.inventoryDescription = item.inventoryDescription;
+                        itemCopy.inventoryName = item.inventoryName;
+                        itemCopy.assetType = item.assetType;
+                        itemCopy.invType = item.invType;
+                        itemCopy.parentFolderID = recipientUserInfo.RootFolder.folderID;
+                        itemCopy.inventoryCurrentPermissions = 2147483647;
+                        itemCopy.inventoryNextPermissions = 2147483647;
+                        itemCopy.inventoryEveryOnePermissions = item.inventoryEveryOnePermissions;
+                        itemCopy.inventoryBasePermissions = item.inventoryBasePermissions;
+                        itemCopy.inventoryCurrentPermissions = item.inventoryCurrentPermissions;
+
+                        recipientUserInfo.AddItem(recipientClient.AgentId, itemCopy);
+                        
+                        // Let the recipient client know about this new item
+                        recipientClient.SendBulkUpdateInventory(itemCopy);                         
+                    }
+                    else
+                    {
+                        m_log.ErrorFormat(
+                            "[AGENT INVENTORY]: Could not find userinfo for recipient user {0}, {1} of item {2}, {3} from {4}", 
+                            recipientClient.Name, recipientClient.AgentId, item.inventoryName, 
+                            item.inventoryID, senderId);
+                    }
+                }
+                else
+                {
+                    m_log.ErrorFormat(
+                        "[AGENT INVENTORY]: Failed to find item {0} to give to {1}", itemId, senderId);
+                    
+                    return;
+                }
+            }
+            else
+            {
+                m_log.Error("[AGENT INVENTORY]: Failed to find item " + itemId.ToString() + ", no root folder");
+                return;
+            }                       
+        }
 
         public void CopyInventoryItem(IClientAPI remoteClient, uint callbackID, LLUUID oldAgentID, LLUUID oldItemID,
                                       LLUUID newFolderID, string newName)
         {
+            m_log.DebugFormat(
+                "[AGENT INVENTORY]: CopyInventoryItem received by {0} with oldAgentID {1}, oldItemID {2}, new FolderID {3}, newName {4}",
+                remoteClient.AgentId, oldAgentID, oldItemID, newFolderID, newName);
+            
             InventoryItemBase item = CommsManager.UserProfileCacheService.libraryRoot.HasItem(oldItemID);
             if (item == null)
             {
@@ -349,9 +428,8 @@ namespace OpenSim.Region.Environment.Scenes
         public void MoveInventoryItem(IClientAPI remoteClient, LLUUID folderID, LLUUID itemID, int length,
                                       string newName)
         {
-            m_log.Info(
-                "[AGENT INVENTORY]: " +
-                "Moving item for " + remoteClient.AgentId.ToString());
+            m_log.DebugFormat(
+                "[AGENT INVENTORY]: Moving item {0} to {1} for {2}", itemID, folderID, remoteClient.AgentId);
 
             CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId);
             if (userInfo == null)
@@ -429,7 +507,8 @@ namespace OpenSim.Region.Environment.Scenes
         }
 
         /// <summary>
-        /// Create a new inventory item.
+        /// Create a new inventory item.  Called when the client creates a new item directly within their
+        /// inventory (e.g. by selecting a context inventory menu option).
         /// </summary>
         /// <param name="remoteClient"></param>
         /// <param name="transactionID"></param>
diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
index 3f090df..9901a50 100644
--- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
+++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
@@ -270,6 +270,12 @@ namespace OpenSim.Region.Examples.SimpleModule
                                                LLUUID imSessionID, string fromName, byte dialog, uint timeStamp)
         {
         }
+        
+        public virtual void SendInstantMessage(LLUUID fromAgent, LLUUID fromAgentSession, string message, LLUUID toAgent,
+                                               LLUUID imSessionID, string fromName, byte dialog, uint timeStamp,
+                                               byte[] binaryBucket)
+        {
+        }        
 
         public virtual void SendLayerData(float[] map)
         {
@@ -388,6 +394,11 @@ namespace OpenSim.Region.Examples.SimpleModule
         public virtual void SendRemoveInventoryItem(LLUUID itemID)
         {
         }
+        
+        /// <see>IClientAPI.SendBulkUpdateInventory(InventoryItemBase)</see>
+        public virtual void SendBulkUpdateInventory(InventoryItemBase item) 
+        {        
+        }
 
         public virtual void SendTaskInventory(LLUUID taskID, short serial, byte[] fileName)
         {
-- 
cgit v1.1