""" @file inventory.py @date 2009-03-03 Contributors can be viewed at: http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt $LicenseInfo:firstyear=2008&license=apachev2$ Copyright 2008, Linden Research, Inc. Licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 or in http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt $/LicenseInfo$ """ # standard python libs from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG import re # related import uuid # pyogp from pyogp.lib.base.settings import Settings from pyogp.lib.base.message.packets import * # initialize logging logger = getLogger('pyogp.lib.base.inventory') log = logger.log class Inventory(object): """ is an inventory container Initialize the event queue client class >>> inventory = Inventory() Sample implementations: agent.py Tests: tests/test_inventory.py """ def __init__(self, agent = None, settings = None): """ set up the inventory manager """ # allow the settings to be passed in # otherwise, grab the defaults if settings != None: self.settings = settings else: from pyogp.lib.base.settings import Settings self.settings = Settings() self.agent = agent # For now, store the inventory contents in a list # of folders with it's contents list containing it's inventory items # Ditto the library self.folders = [] self.library_folders = [] # the root inventory and library root folder are special cases self.inventory_root = None self.library_root = None # set up callbacks if self.settings.HANDLE_PACKETS: if self.agent != None: self.packet_handler = self.agent.packet_handler else: from pyogp.lib.base.message.packethandler import PacketHandler self.packet_handler = PacketHandler() onInventoryDescendents_received = self.packet_handler._register('InventoryDescendents') onInventoryDescendents_received.subscribe(onInventoryDescendents, self) onFetchInventoryReply_received = self.packet_handler._register('FetchInventoryReply') onFetchInventoryReply_received.subscribe(onFetchInventoryReply, self) if self.settings.LOG_VERBOSE: log(INFO, "Initializing inventory storage") def _parse_folders_from_login_response(self): """ the login response may contain inventory information, append data to our folders list """ if self.settings.LOG_VERBOSE: log(DEBUG, 'Parsing the login response for inventory folders') if self.agent.login_response.has_key('inventory-skeleton'): [self._add_inventory_folder(folder) for folder in self.agent.login_response['inventory-skeleton']] if self.agent.login_response.has_key('inventory-skel-lib'): [self._add_inventory_folder(folder) for folder in self.agent.login_response['inventory-skel-lib']] def _add_inventory_item(self, inventory_item): """ inventory items comes from packets """ # replace an existing list member, else, append # for now, we are assuming the parent folder exists. # if it doesn't we'll know via traceback index = [self.folders.index(folder) for folder in self.folders if folder.FolderID == inventory_item.FolderID] try: inventory_index = [self.folders[index].index(item) for item in self.folders[index].inventory] self.folders[index[0]].inventory[inventory_index[0]] = inventory_item if self.settings.LOG_VERBOSE: log(DEBUG, 'Replacing a stored inventory item: %s for agent \'%s\'' % (inventory_item.ItemID, self.agent.agent_id)) except: self.folders[index[0]].inventory.append(inventory_item) if self.settings.LOG_VERBOSE: log(DEBUG, 'Storing a new inventory item: %s in agent \'%s\'' % (inventory_item.ItemID, self.agent.agent_id)) def search_inventory(self, folder_list = [], item_id = None, name = None, match_list = []): """ search through all inventory folders for an id(uuid) or Name, and return a list of matching InventoryItems or InventoryFolders recursive search, based on folder_id. if no folder id is specified look through everything in self.folders This does not request inventory from the grid. It could, were we to go about enabling this... """ # search our inventory storage if we aren't told what to look in if folder_list == []: search_folders = self.folders else: search_folders = folder_list if item_id != None: item_id = uuid.UIUD(str(item_id)) for item in search_folders: if str(type(item)) == '': if uuid.UIUD(str(item.ItemID)) == item_id: match_list.append(item) elif str(type(item)) == '': matches = self.search_inventory_folder(item.FolderID, _id = item_id) match_list.extend(matches) return match_list elif name != None: for item in search_folders: pattern = re.compile(name) if str(type(item)) == '': if pattern.match(item.Name): match_list.append(item) elif str(type(item)) == '': matches = self.search_inventory_folder(item.FolderID, name = name.strip()) match_list.extend(matches) return match_list else: # return an empty list return [] def search_inventory_folder(self, folder_id, _id = None, name = None): """ search an inventory folder for _id or name return a list of matches """ match_list = [] search_folder = [folder for folder in self.folders if folder.FolderID == folder_id][0] for item in search_folder.inventory: if _id != None: if str(type(item)) == '': if item.ItemID == _id: match_list.append(item) elif str(type(item)) == '': if item.FolderID== _id: match_list.append(item) elif name != None: pattern = re.compile(name) if pattern.match(item.Name): match_list.append(item) return match_list def _add_inventory_folder(self, folder_data): """ inventory folder data comes from either packets or login """ # if it's a dict, we are parsing login response data if type(folder_data) == dict: if self.settings.LOG_VERBOSE: log(DEBUG, "Adding inventory folder %s" % (folder_data['name'])) folder = InventoryFolder(folder_data['name'], folder_data['folder_id'], folder_data['parent_id'], folder_data['version'], folder_data['type_default']) self.folders.append(folder) if folder_data['parent_id'] == '00000000-0000-0000-0000-000000000000' and folder_data['name'] == 'My Inventory': self.inventory_root = folder elif folder_data['parent_id'] == '00000000-0000-0000-0000-000000000000' and folder_data['name'] == 'Library': self.library_root = folder # otherwise, we are adding an InventoryFolder() instance else: if self.settings.LOG_VERBOSE: log(DEBUG, "Adding inventory folder %s" % (folder_data.Name)) self.folders.append(folder_data) def display_folder_contents(self, folder_id = None, name = None): """ returns a list of the local representation of a folder's contents """ if folder_id != None: return [member for member in self.folders if member.ParentID == folder_id] # the name case is not handled at this time ''' elif name != None: folder_ids = [member.folder_id for member in self.contents if member.name == name] return [member for member in self.contents if member.name == name] ''' def request_known_folder_contents(self, folder_id = None): """ send requests to the server for contents of all known folders """ [self.inventory._request_folder_contents(folder.FolderID) for folder in self.inventory.folders if folder.ParentID == self.inventory.inventory_root.FolderID] def _request_folder_contents(self, folder_id = None, name = None): """ send a request to the server for folder contents, wraps sendFetchInventoryDescendentsPacket """ self.sendFetchInventoryDescendentsPacket(folder_id = folder_id) def request_inventory_by_id(self, id_list = None): """ ask for inventory data by id via a list """ if id_list != None: packet = FetchInventoryPacket() # AgentData block packet.AgentData['AgentID'] = uuid.UUID(str(self.agent.agent_id)) # MVT_LLUUID packet.AgentData['SessionID'] = uuid.UUID(str(self.agent.session_id)) # MVT_LLUUID for inventory_id in id_list: InventoryData = {} InventoryData['OwnerID'] = uuid.UUID(str(self.agent.agent_id)) # MVT_LLUUID InventoryData['ItemID'] = uuid.UUID(str(inventory_id)) # MVT_LLUUID packet.InventoryDataBlocks.append(InventoryData) # enqueue the message self.region.enqueue_message(packet()) def sendFetchInventoryDescendentsPacket(self, folder_id = None, name = None): """ send a request to the server for folder contents """ # the name case is not handled at this time packet = FetchInventoryDescendentsPacket() # AgentData block packet.AgentData['AgentID'] = uuid.UUID(str(self.agent.agent_id)) # MVT_LLUUID packet.AgentData['SessionID'] = uuid.UUID(str(self.agent.session_id)) # MVT_LLUUID # InventoryData block packet.InventoryData['FolderID'] = uuid.UUID(str(folder_id)) # MVT_LLUUID packet.InventoryData['OwnerID'] = uuid.UUID(str(self.agent.agent_id)) # MVT_LLUUID packet.InventoryData['SortOrder'] = 0 # MVT_S32, 0 = name, 1 = time packet.InventoryData['FetchFolders'] = True # MVT_BOOL packet.InventoryData['FetchItems'] = True # MVT_BOOL self.agent.region.enqueue_message(packet()) class InventoryFolder(object): """ represents an Inventory folder Initialize the event queue client class >>> inventoryfolder = InventoryFolder() Sample implementations: inventory.py Tests: tests/test_inventory.py """ def __init__(self, Name = None, FolderID = None, ParentID = None, Version = None, Type = None): """ initialize the inventory folder """ self.type = 'InventoryFolder' self.Name = Name self.FolderID = uuid.UUID(str(FolderID)) self.ParentID = uuid.UUID(str(ParentID)) self.Version = Version self.Type = Type self.inventory = [] class InventoryItem(object): """ represents an Inventory item Initialize the InventoryItem class >>> inventoryitem = InventoryItem() Sample implementations: inventory.py Tests: tests/test_inventory.py """ def __init__(self, ItemID = None, FolderID = None, CreatorID = None, OwnerID = None, GroupID = None, BaseMask = None, OwnerMask = None, GroupMask = None, EveryoneMask = None, NextOwnerMask = 0, GroupOwned = None, AssetID = None, Type = None, InvType = None, Flags = None, SaleType = None, SalePrice = None, Name = None, Description = None, CreationDate = None, CRC = None): """ initialize the inventory item """ self.type = 'InventoryItem' self.ItemID = uuid.UUID(str(ItemID)) # LLUUID self.FolderID = uuid.UUID(str(FolderID)) # LLUUID self.CreatorID = uuid.UUID(str(CreatorID)) # LLUUID self.OwnerID = uuid.UUID(str(OwnerID)) # LLUUID self.GroupID = uuid.UUID(str(GroupID)) # LLUUID self.BaseMask = BaseMask # U32 self.OwnerMask = OwnerMask # U32 self.GroupMask = GroupMask # U32 self.EveryoneMask = EveryoneMask # U32 self.NextOwnerMask = NextOwnerMask self.GroupOwned = GroupOwned # Bool self.AssetID = uuid.UUID(str(AssetID)) # LLUUID self.Type = Type # S8 self.InvType = InvType # S8 self.Flags = Flags # U32 self.SaleType = SaleType # U8 self.SalePrice = SalePrice # S32 self.Name = Name # Variable 1 self.Description = Description # Variable 1 self.CreationDate = CreationDate # S32 self.CRC = CRC # U32 def rez_object(self, agent, relative_position = (1, 0, 0)): """ try and rez an inventory item """ # self.agent.Position holds where we are. we need to add this tuple to the incoming tuple (vector to a vector) location_to_rez_x = agent.Position[0] + relative_position[0] location_to_rez_y = agent.Position[1] + relative_position[1] location_to_rez_z = agent.Position[2] + relative_position[2] location_to_rez = (location_to_rez_x, location_to_rez_y, location_to_rez_z) sendRezObject(agent, self, location_to_rez, location_to_rez) def update(agent, name = None, value = None): """ allow arbitraty update to any data in the inventory item """ if self.__dict__.has_key(name): self.setattr(self, name, value) sendUpdateInventoryItem(agent, [self]) else: raise DataParsingError('Inventory item attribute update failes, no field named \'%s\'' % (name)) # ~~~~~~~~~~~~~~~ # Packet Wrappers # ~~~~~~~~~~~~~~~ def sendUpdateInventoryItem(agent, inventory_items = [], ): """ sends an UpdateInventoryItem packet to a region this function expects an already transformed InventoryItem instance """ packet - UpdateInventoryItemPacket() packet.AgentData["AgentID"] = uuid.UUID(str(agent.agent_id)) packet.AgentData["SessionID"] = uuid.UUID(str(agent.session_id)) packet.AgentData["TransactionID"] = uuid.UUID('00000000-0000-0000-0000-000000000000') for item in inventory_items: packet.InventoryData['ItemID'] = uuid.UUID(str(item.ItemID)) packet.InventoryData['FolderID'] = uuid.UUID(str(item.FolderID)) packet.InventoryData['CallbackID'] = uuid.UUID('00000000-0000-0000-0000-000000000000') packet.InventoryData['CreatorID'] = uuid.UUID(str(item.CreatorID)) packet.InventoryData['OwnerID'] = uuid.UUID(str(item.OwnerID)) packet.InventoryData['GroupID'] = uuid.UUID(str(item.GroupID)) packet.InventoryData['BaseMask'] = item.BaseMask packet.InventoryData['OwnerMask'] = item.OwnerMask packet.InventoryData['GroupMask'] = item.GroupMask packet.InventoryData['EveryoneMask'] = item.EveryoneMask packet.InventoryData['NextOwnerMask'] = item.NextOwnerMask packet.InventoryData['GroupOwned'] = item.GroupOwned packet.InventoryData['TransactionID'] = uuid.UUID('00000000-0000-0000-0000-000000000000') packet.InventoryData['Type'] = item.Type packet.InventoryData['InvType'] = item.InvType packet.InventoryData['Flags'] = item.Flags packet.InventoryData['SaleType'] = item.SaleType packet.InventoryData['SalePrice'] = item.SalePrice packet.InventoryData['Name'] = item.Name packet.InventoryData['Description'] = item.Description packet.InventoryData['CreationDate'] = item.CreationDate packet.InventoryData['CRC'] = item.CRC agent.region.enqueue_message(packet()) def sendRezObject(agent, inventory_item, RayStart, RayEnd, FromTaskID = uuid.UUID('00000000-0000-0000-0000-000000000000'), BypassRaycast = 1, RayTargetID = uuid.UUID('00000000-0000-0000-0000-000000000000'), RayEndIsIntersection = False, RezSelected = False, RemoveItem = False, ItemFlags = 0, GroupMask = 0, EveryoneMask = 0, NextOwnerMask = 0): """ sends a RezObject packet to a region """ packet = RezObjectPacket() packet.AgentData['AgentID'] = uuid.UUID(str(agent.agent_id)) packet.AgentData['SessionID'] = uuid.UUID(str(agent.session_id)) packet.AgentData['GroupID'] = uuid.UUID(str(agent.ActiveGroupID)) packet.RezData['FromTaskID'] = uuid.UUID(str(FromTaskID)) packet.RezData['BypassRaycast'] = BypassRaycast # ??? packet.RezData['RayStart'] = RayStart packet.RezData['RayEnd'] = RayEnd packet.RezData['RayTargetID'] = uuid.UUID(str(RayTargetID)) packet.RezData['RayEndIsIntersection'] = RayEndIsIntersection packet.RezData['RezSelected'] = RezSelected packet.RezData['RemoveItem'] = RemoveItem packet.RezData['ItemFlags'] = ItemFlags packet.RezData['GroupMask'] = GroupMask packet.RezData['EveryoneMask'] = EveryoneMask packet.RezData['NextOwnerMask'] = inventory_item.NextOwnerMask packet.InventoryData['ItemID'] = uuid.UUID(str(inventory_item.ItemID)) packet.InventoryData['FolderID'] = uuid.UUID(str(inventory_item.FolderID)) packet.InventoryData['CreatorID'] = uuid.UUID(str(inventory_item.CreatorID)) packet.InventoryData['OwnerID'] = uuid.UUID(str(inventory_item.OwnerID)) packet.InventoryData['GroupID'] = uuid.UUID(str(inventory_item.GroupID)) packet.InventoryData['BaseMask'] = inventory_item.BaseMask packet.InventoryData['OwnerMask'] = inventory_item.OwnerMask packet.InventoryData['GroupMask'] = inventory_item.GroupMask packet.InventoryData['EveryoneMask'] = inventory_item.EveryoneMask packet.InventoryData['GroupOwned'] = inventory_item.GroupOwned packet.InventoryData['TransactionID'] = uuid.UUID('00000000-0000-0000-0000-000000000000') packet.InventoryData['Type'] = inventory_item.Type packet.InventoryData['InvType'] = inventory_item.InvType packet.InventoryData['Flags'] = inventory_item.Flags packet.InventoryData['SaleType'] = inventory_item.SaleType packet.InventoryData['SalePrice'] = inventory_item.SalePrice packet.InventoryData['Name'] = inventory_item.Name packet.InventoryData['Description'] = inventory_item.Description packet.InventoryData['CreationDate'] = inventory_item.CreationDate packet.InventoryData['CRC'] = inventory_item.CRC packet.InventoryData['NextOwnerMask'] = inventory_item.NextOwnerMask agent.region.enqueue_message(packet()) #~~~~~~~~~~ # Callbacks #~~~~~~~~~~ def onInventoryDescendents(packet, inventory): if packet.message_data.blocks['AgentData'][0].get_variable('Descendents') > 0: _agent_id = packet.message_data.blocks['AgentData'][0].get_variable('AgentID') _agent_id = packet.message_data.blocks['AgentData'][0].get_variable('AgentID') _folder_id = packet.message_data.blocks['AgentData'][0].get_variable('FolderID') _owner_id = packet.message_data.blocks['AgentData'][0].get_variable('OwnerID') _version = packet.message_data.blocks['AgentData'][0].get_variable('Version') _descendents = packet.message_data.blocks['AgentData'][0].get_variable('Descendents') if packet.message_data.blocks['ItemData'][0].get_variable('ItemID').data != uuid.UUID('00000000-0000-0000-0000-000000000000'): for ItemData_block in packet.message_data.blocks['ItemData']: _ItemID = ItemData_block.get_variable('ItemID').data _FolderID = ItemData_block.get_variable('FolderID').data _CreatorID = ItemData_block.get_variable('CreatorID').data _OwnerID = ItemData_block.get_variable('OwnerID').data _GroupID = ItemData_block.get_variable('GroupID').data _BaseMask = ItemData_block.get_variable('BaseMask').data _OwnerMask = ItemData_block.get_variable('OwnerMask').data _GroupMask = ItemData_block.get_variable('GroupMask').data _EveryoneMask = ItemData_block.get_variable('EveryoneMask').data _NextOwnerMask = ItemData_block.get_variable('NextOwnerMask').data _GroupOwned = ItemData_block.get_variable('GroupOwned').data _AssetID = ItemData_block.get_variable('AssetID').data _Type = ItemData_block.get_variable('Type').data _InvType = ItemData_block.get_variable('InvType').data _Flags = ItemData_block.get_variable('Flags').data _SaleType = ItemData_block.get_variable('SaleType').data _SalePrice = ItemData_block.get_variable('SalePrice').data _Name = ItemData_block.get_variable('Name').data _Description = ItemData_block.get_variable('Description').data _CreationDate = ItemData_block.get_variable('CreationDate').data _CRC = ItemData_block.get_variable('CRC').data inventory_item = InventoryItem(_ItemID, _FolderID, _CreatorID, _OwnerID, _GroupID, _BaseMask, _OwnerMask, _GroupMask, _EveryoneMask, _NextOwnerMask, _GroupOwned, _AssetID, _Type, _InvType, _Flags, _SaleType, _SalePrice, _Name, _Description, _CreationDate, _CRC) inventory._add_inventory_item(inventory_item) if packet.message_data.blocks['FolderData'][0].get_variable('FolderID').data != uuid.UUID('00000000-0000-0000-0000-000000000000'): for FolderData_block in packet.message_data.blocks['FolderData']: _FolderID = FolderData_block.get_variable('FolderID').data _ParentID = FolderData_block.get_variable('ParentID').data _Type = FolderData_block.get_variable('Type').data _Name = FolderData_block.get_variable('Name').data folder = InventoryFolder( _Name, _FolderID, _ParentID, None, _Type) inventory._add_inventory_folder(folder) def onFetchInventoryReply(packet, inventory): _agent_id = packet.message_data.blocks['AgentData'][0].get_variable('AgentID') for InventoryData_block in packet.message_data.blocks['InventoryData']: _ItemID = InventoryData_block.get_variable('ItemID').data _FolderID = InventoryData_block.get_variable('FolderID').data _CreatorID = InventoryData_block.get_variable('CreatorID').data _OwnerID = InventoryData_block.get_variable('OwnerID').data _GroupID = InventoryData_block.get_variable('GroupID').data _BaseMask = InventoryData_block.get_variable('BaseMask').data _OwnerMask = InventoryData_block.get_variable('OwnerMask').data _GroupMask = InventoryData_block.get_variable('GroupMask').data _EveryoneMask = InventoryData_block.get_variable('EveryoneMask').data _NextOwnerMask = InventoryData_block.get_variable('NextOwnerMask').data _GroupOwned = InventoryData_block.get_variable('GroupOwned').data _AssetID = InventoryData_block.get_variable('AssetID').data _Type = InventoryData_block.get_variable('Type').data _InvType = InventoryData_block.get_variable('InvType').data _Flags = InventoryData_block.get_variable('Flags').data _SaleType = InventoryData_block.get_variable('SaleType').data _SalePrice = InventoryData_block.get_variable('SalePrice').data _Name = InventoryData_block.get_variable('Name').data _Description = InventoryData_block.get_variable('Description').data _CreationDate = InventoryData_block.get_variable('CreationDate').data _CRC = InventoryData_block.get_variable('CRC').data inventory_item = InventoryItem(_ItemID, _FolderID, _CreatorID, _OwnerID, _GroupID, _BaseMask, _OwnerMask, _GroupMask, _EveryoneMask, _NextOwnerMask, _GroupOwned, _AssetID, _Type, _InvType, _Flags, _SaleType, _SalePrice, _Name, _Description, _CreationDate, _CRC) inventory._add_inventory_item(inventory_item)