diff --git a/pyogp/lib/base/appearance.py b/pyogp/lib/base/appearance.py index 4dfbec2..debbd99 100644 --- a/pyogp/lib/base/appearance.py +++ b/pyogp/lib/base/appearance.py @@ -14,7 +14,7 @@ from pyogp.lib.base.message.message import Message, Block from pyogp.lib.base.utilities.helpers import Helpers from pyogp.lib.base.exc import NotImplemented from pyogp.lib.base.objects import Object -from pyogp.lib.base.params import VisualParams +from pyogp.lib.base.visualparams import VisualParams from pyogp.lib.base.datatypes import UUID, Vector3 from pyogp.lib.base.utilities.enums import BakedIndex, TextureIndex, \ WearableMap, AssetType, WearablesIndex diff --git a/pyogp/lib/base/assets.py b/pyogp/lib/base/assets.py index 9872a33..bf904de 100644 --- a/pyogp/lib/base/assets.py +++ b/pyogp/lib/base/assets.py @@ -14,9 +14,10 @@ from pyogp.lib.base.utilities.enums import TransferChannelType, TransferSourceTy from pyogp.lib.base.message.message_handler import MessageHandler from pyogp.lib.base.message.message import Message, Block from pyogp.lib.base.utilities.helpers import Helpers -from pyogp.lib.base.exc import NotImplemented +from pyogp.lib.base.exc import NotImplemented, ResourceError, ResourceNotFound from pyogp.lib.base.objects import Object from pyogp.lib.base.datatypes import Vector3, UUID +from pyogp.lib.base.caps import Capability # initialize logging @@ -35,12 +36,12 @@ class AssetManager(DataManager): super(AssetManager, self).__init__(agent, settings) #indexed by assetID self.assets = {} - + def enable_callbacks(self): pass - def request_asset(self, assetID, assetType, isPriority, callback): + def request_asset(self, assetID, assetType, isPriority, callback=None, itemID=None): """ Sends a TransferRequest to the sim for asset assetID with type assetType, will call back with the assetID and True with asset received or False @@ -55,26 +56,31 @@ class AssetManager(DataManager): def onTransferPacket(packet): """ TransferPacket of a successful TransferRequest + TODO wait for all all packets to arrive and assemble the data """ # fill in data for Asset in the requests queue and pop it off and story in assets dict if str(transferID) == str(packet.blocks['TransferData'][0].get_variable('TransferID').data): self.assets[str(assetID)] = AssetWearable(assetID, assetType, packet.blocks['TransferData'][0].get_variable('Data').data) - callback(assetID, True) + if callback != None: + callback(assetID, True) transferPacketHandler.unsubscribe(onTransferPacket) def onTransferInfo(packet): """ Status of TransferRequest Account for size and multiple packets + TODO set packet count """ + if str(transferID) == str(packet.blocks['TransferInfo'][0].get_variable('TransferID')): status = packet.blocks['TransferInfo'][0].get_variable("Status").data if status != TransferStatus.OK: log(WARNING, "Request for asset %s failed with status %s" \ % (assetID, status)) - callback(assetID, False) + if callback != None: + callback(assetID, False) transferPacketHandler.unsubscribe(onTransferPacket) transferInfoHandler.unsubscribe(onTransferInfo) @@ -85,8 +91,17 @@ class AssetManager(DataManager): priority = 1.0 else: priortity = 0.0 - params = assetID.get_bytes() \ - + Helpers().int_to_bytes(assetType) + + params = '' + if itemID != None: + params += self.agent.agent_id.get_bytes() + \ + self.agent.session_id.get_bytes() + \ + self.agent.agent_id.get_bytes() + \ + UUID().get_bytes() + \ + itemID.get_bytes() + + params += assetID.get_bytes() + \ + Helpers().int_to_bytes(assetType) self.send_TransferRequest(transferID, TransferChannelType.Asset, @@ -94,11 +109,71 @@ class AssetManager(DataManager): priority, params) + + """ + def upload_asset(self, transaction_id, type_, tempfile, store_local, + asset_data=None): + + assetUploadCompleteHandler = self.agent.region.message_handler.register('AssetUploadComplete') + def onAssetUploadComplete(packet): + log(INFO, "AssetUploadComplete: %s" % packet) + + assetUploadCompleteHandler.subscribe(onAssetUploadComplete) + + self.send_AssetUploadRequest(transaction_id, type_, tempfile, + store_local) + """ + def upload_script_via_caps(self, item_id, script): + + def upload_script_via_caps_responder(response): + + if response['state'] == 'upload': + cap = Capability('UpdateScriptAgentResponse', response['uploader']) + response = cap.POST_FILE(script) + upload_script_via_caps_responder(response) + elif response['state'] == 'complete': + log(DEBUG, "Upload of script Successful") + else: + log(WARNING, "Upload failed") + + cap = self.agent.region.capabilities['UpdateScriptAgent'] + post_body = {'item_id' : str(item_id), 'target': 'lsl2'} + custom_headers = {'Accept' : 'application/llsd+xml'} + + try: + response = cap.POST(post_body, custom_headers) + except ResourceError, error: + log(ERROR, error) + return + except ResourceNotFound, error: + log(ERROR, "404 calling: %s" % (error)) + return + upload_script_via_caps_responder(response) + + + + + def upload_via_udp(self): + pass def get_asset(self, assetID): - return self.assets[str(assetID)] + def send_AssetUploadRequest(self, TransactionID, Type, Tempfile, \ + StoreLocal, AssetData=None): + """ + Sends an AssetUploadRequest packet to request that an asset be + uploaded to the to the sim + """ + packet = Message('AssetUploadRequest', + Block('AssetBlock', + TransactionID = TransactionID, + Type = Type, + Tempfile = Tempfile, + StoreLocal = StoreLocal, + AssetData = AssetData)) + self.agent.region.enqueue_message(packet) + def send_TransferRequest(self, TransferID, ChannelType, SourceType, Priority, Params): @@ -118,8 +193,6 @@ class AssetManager(DataManager): self.agent.region.enqueue_message(packet) - - class Asset(object): def __init__(self, assetID, assetType, data): self.assetID = assetID diff --git a/pyogp/lib/base/caps.py b/pyogp/lib/base/caps.py index de3194a..b83549d 100644 --- a/pyogp/lib/base/caps.py +++ b/pyogp/lib/base/caps.py @@ -2,7 +2,7 @@ import urllib2 from types import * from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG - +import re # related # pyogp @@ -107,15 +107,42 @@ class Capability(object): else: raise ResourceError(self.public_url, e.code, e.msg, e.fp.read(), method="POST") + return self._response_handler(response) + + def POST_FILE(self, file_name, custom_headers={}): + """ Opens file at file_name and posts contents to this cap. """ + headers = {"Content-type" : "application/octet-stream"} + fd = open(file_name) + payload = fd.read() + fd.close + + try: + response = self.restclient.POST(self.public_url, + payload, headers=headers) + except HTTPError, e: + if e.code==404: + raise ResourceNotFound(self.public_url) + else: + raise ResourceError(self.public_url, e.code, e.msg, + e.fp.read(), method="POST") + + return self._response_handler(response) + + def _response_handler(self, response): # now deserialize the data again, we ask for a utility with the content type # as the name content_type_charset = response.headers['Content-Type'] content_type = content_type_charset.split(";")[0] # remove the charset part + pattern = re.compile('<\?xml\sversion="1.0"\s\?>.*?') # ToDo: write a generic serializer/deserializer - if (content_type == 'application/llsd+xml') or (content_type == 'application/xml'): + if (content_type == 'application/llsd+xml') or \ + (content_type == 'application/xml') or \ + (content_type == 'text/html' and \ + pattern.match(response.body) != None): deserializer = LLSDDeserializer() else: + print response raise DeserializerNotFound(content_type) data = deserializer.deserialize(response.body) @@ -125,6 +152,7 @@ class Capability(object): return data + def __repr__(self): return "" %(self.name, self.public_url) diff --git a/pyogp/lib/base/inventory.py b/pyogp/lib/base/inventory.py index 752ccc1..706b509 100644 --- a/pyogp/lib/base/inventory.py +++ b/pyogp/lib/base/inventory.py @@ -182,16 +182,17 @@ class InventoryManager(DataManager): container[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)) + log(DEBUG, 'Replacing a stored inventory item: %s' % (inventory_item.ItemID)) except: container[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)) + log(DEBUG, 'Storing a new inventory item: %s ' % (inventory_item.ItemID)) - def search_inventory(self, folder_list = [], item_id = None, name = None, match_list = []): + 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 @@ -200,60 +201,52 @@ class InventoryManager(DataManager): This does not request inventory from the grid. It could, were we to go about enabling this... """ + + def match(): + _match = lambda arg : False + if item_id != None: + def _match(arg): + if isinstance(arg, InventoryItem): + return str(arg.ItemID) == str(item_id) + else: + return str(arg.FolderID) == str(item_id) + elif name != None: + pattern = re.compile(name) + def _match(arg): + if pattern.match(arg.Name): + return True + else: + return False + return _match + + is_match = match() # search our inventory storage if we aren't told what to look in - if folder_list == []: + if folder_list == []: search_folders = self.folders else: search_folders = folder_list - if item_id != None: - - item_id = UUID(str(item_id)) - - for item in search_folders: - - if isinstance(item, InventoryItem): - if str(item.ItemID) == str(item_id): - match_list.append(item) - - elif isinstance(item, InventoryFolder): - 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 isinstance(item, InventoryItem): - if pattern.match(item.Name): - match_list.append(item) - - elif isinstance(item, InventoryFolder): - matches = self.search_inventory_folder(item.FolderID, name = name.strip()) - match_list.extend(matches) - - return match_list - - else: - - # return an empty list - return [] - + for item in search_folders: + if is_match(item): + match_list.append(item) + if isinstance(item, InventoryFolder): + matches = self.search_inventory_folder(item.FolderID, + _id=item_id, + name=name) + match_list.extend(matches) + return match_list + 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 str(folder.FolderID) == str(folder_id)][0] - + for item in search_folder.inventory: if _id != None: @@ -261,9 +254,8 @@ class InventoryManager(DataManager): if isinstance(item, InventoryItem): if str(item.ItemID) == str(_id): match_list.append(item) - elif isinstance(item, InventoryFolder): - if item.FolderID == _id: + if str(item.FolderID) == str(_id): match_list.append(item) elif name != None: @@ -469,6 +461,81 @@ class InventoryManager(DataManager): self.agent.send_ImprovedInstantMessage(self.agent.agent_id, self.agent.session_id, 0, FromAgentID, 0, UUID(), self.agent.Position, 0, accept_key, ID, 0, self.agent.Name(), '', '') + def create_new_item(self, folder, name, desc, asset_type, inv_type, + wearable_type, next_owner_permission, callback=None): + """ + Creates a new item in folder. + """ + transaction_id = UUID() + transaction_id.random() + + updateCreateInventoryHandler = self.agent.region.message_handler.register('UpdateCreateInventoryItem') + + def onUpdateCreateInventoryItem(packet): + if str(transaction_id) != \ + str(packet.blocks['AgentData'][0]\ + .get_variable('TransactionID').data): + inv_data = packet.blocks['InventoryData'][0] + item = InventoryItem(inv_data.get_variable('ItemID').data, + inv_data.get_variable('FolderID').data, + inv_data.get_variable('CreatorID').data, + inv_data.get_variable('OwnerID').data, + inv_data.get_variable('GroupID').data, + inv_data.get_variable('BaseMask').data, + inv_data.get_variable('OwnerMask').data, + inv_data.get_variable('GroupMask').data, + inv_data.get_variable('EveryoneMask').data, + inv_data.get_variable('NextOwnerMask').data, + inv_data.get_variable('GroupOwned').data, + inv_data.get_variable('AssetID').data, + inv_data.get_variable('Type').data, + inv_data.get_variable('InvType').data, + inv_data.get_variable('Flags').data, + inv_data.get_variable('SaleType').data, + inv_data.get_variable('SalePrice').data, + inv_data.get_variable('Name').data, + inv_data.get_variable('Description').data, + inv_data.get_variable('CreationDate').data, + inv_data.get_variable('CRC').data) + + self._store_inventory_item(item) + updateCreateInventoryHandler.unsubscribe(onUpdateCreateInventoryItem) + if callback != None: + callback(item) + + updateCreateInventoryHandler.subscribe(onUpdateCreateInventoryItem) + + self.send_CreateInventoryItem(self.agent.agent_id, + self.agent.session_id, + 0, + folder.FolderID, + transaction_id, + next_owner_permission, + asset_type, + inv_type, + wearable_type, + name, + desc) + + def send_CreateInventoryItem(self, agent_id, session_id, callback_id, + folder_id, transaction_id, next_owner_mask, + type_, inv_type, wearable_type, name, desc): + """ sends a CreateInventoryItem message """ + args = [Block('AgentData', + AgentID = agent_id, + SessionID = session_id)] + args += [Block('InventoryBlock', + CallbackID = callback_id, + FolderID = folder_id, + TransactionID = transaction_id, + NextOwnerMask = next_owner_mask, + Type = type_, + InvType = inv_type, + WearableType = wearable_type, + Name = name, + Description = desc)] + self.agent.region.enqueue_message(Message("CreateInventoryItem", *args)) + def sendFetchInventoryDescendentsRequest(self, folder_id = None): """ send a request to the grid for folder contents """ @@ -863,7 +930,29 @@ class InventoryItem(object): 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, LastOwnerID = UUID()): + 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, + LastOwnerID = UUID()): """ initialize the inventory item """ self.type = 'InventoryItem' @@ -984,47 +1073,49 @@ def sendRezObject(agent, inventory_item, RayStart, RayEnd, FromTaskID = UUID(), packet = Message('RezObject', Block('AgentData', - AgentID = agent.agent_id, - SessionID = agent.session_id, - GroupID = agent.ActiveGroupID), + AgentID = agent.agent_id, + SessionID = agent.session_id, + GroupID = agent.ActiveGroupID), Block('RezData', - FromTaskID = UUID(str(FromTaskID)), - BypassRaycast = BypassRaycast, - RayStart = RayStart, - RayEnd = RayEnd, - RayTargetID = UUID(str(RayTargetID)), - RayEndIsIntersection = RayEndIsIntersection, - RezSelected = RezSelected, - RemoveItem = RemoveItem, - ItemFlags = ItemFlags, - GroupMask = GroupMask, - EveryoneMask = EveryoneMask, - NextOwnerMask = NextOwnerMask), + FromTaskID = UUID(str(FromTaskID)), + BypassRaycast = BypassRaycast, + RayStart = RayStart, + RayEnd = RayEnd, + RayTargetID = UUID(str(RayTargetID)), + RayEndIsIntersection = RayEndIsIntersection, + RezSelected = RezSelected, + RemoveItem = RemoveItem, + ItemFlags = ItemFlags, + GroupMask = GroupMask, + EveryoneMask = EveryoneMask, + NextOwnerMask = NextOwnerMask), Block('InventoryData', - ItemID = inventory_item.ItemID, - FolderID = inventory_item.FolderID, - CreatorID = inventory_item.CreatorID, - OwnerID = inventory_item.OwnerID, - GroupID = inventory_item.GroupID, - BaseMask = inventory_item.BaseMask, - OwnerMask = inventory_item.OwnerMask, - GroupMask = inventory_item.GroupMask, - EveryoneMask = inventory_item.EveryoneMask, - GroupOwned = inventory_item.GroupOwned, - TransactionID = UUID(), - Type = inventory_item.Type, - InvType = inventory_item.InvType, - Flags = inventory_item.Flags, - SaleType = inventory_item.SaleType, - SalePrice = inventory_item.SalePrice, - Name = inventory_item.Name, - Description = inventory_item.Description, - CreationDate = inventory_item.CreationDate, - CRC = inventory_item.CRC, - NextOwnerMask = inventory_item.NextOwnerMask)) + ItemID = inventory_item.ItemID, + FolderID = inventory_item.FolderID, + CreatorID = inventory_item.CreatorID, + OwnerID = inventory_item.OwnerID, + GroupID = inventory_item.GroupID, + BaseMask = inventory_item.BaseMask, + OwnerMask = inventory_item.OwnerMask, + GroupMask = inventory_item.GroupMask, + EveryoneMask = inventory_item.EveryoneMask, + GroupOwned = inventory_item.GroupOwned, + TransactionID = UUID(), + Type = inventory_item.Type, + InvType = inventory_item.InvType, + Flags = inventory_item.Flags, + SaleType = inventory_item.SaleType, + SalePrice = inventory_item.SalePrice, + Name = inventory_item.Name, + Description = inventory_item.Description, + CreationDate = inventory_item.CreationDate, + CRC = inventory_item.CRC, + NextOwnerMask = inventory_item.NextOwnerMask)) agent.region.enqueue_message(packet) + + """ Contributors can be viewed at: http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt diff --git a/pyogp/lib/base/objects.py b/pyogp/lib/base/objects.py index 7bcdee7..a81a3cb 100644 --- a/pyogp/lib/base/objects.py +++ b/pyogp/lib/base/objects.py @@ -7,11 +7,13 @@ import math # related + # pyogp from pyogp.lib.base import * from pyogp.lib.base.datamanager import DataManager from pyogp.lib.base.permissions import PermissionsTarget, PermissionsMask from pyogp.lib.base.datatypes import UUID, Vector3, Quaternion +from pyogp.lib.base.event_system import AppEvent # pyogp message from pyogp.lib.base.message.message_handler import MessageHandler @@ -20,7 +22,8 @@ from pyogp.lib.base.message.message import Message, Block # pyogp utilities from pyogp.lib.base.utilities.helpers import Helpers -from pyogp.lib.base.utilities.enums import PCodeEnum, CompressedUpdateFlags +from pyogp.lib.base.utilities.enums import PCodeEnum, CompressedUpdateFlags, \ + Permissions, AssetType # initialize logging logger = getLogger('pyogp.lib.base.objects') @@ -329,6 +332,11 @@ class ObjectManager(DataManager): else: #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Updating an object's attributes. LocalID = %s" % (object_properties['LocalID'])) _object._update_properties(prim_properties) + if _object.UpdateFlags & 2 != 0 and self.agent != None: + + self.agent.events_handler.handle(AppEvent("ObjectSelected", + payload = {'object':_object})) + def request_object_update(self, AgentID, SessionID, ID_CacheMissType_list = None): """ requests object updates from the simulator @@ -360,9 +368,28 @@ class ObjectManager(DataManager): # not sure what RayTargetID is, send as uuid of zeros RayTargetID = UUID() - self.object_add(self.agent.agent_id, self.agent.session_id, GroupID = GroupID, PCode = PCodeEnum.Primitive, Material = 3, AddFlags = 2, PathCurve = 16, ProfileCurve = 1, PathBegin = 0, PathEnd = 0, PathScaleX = 100, PathScaleY = 100, PathShearX = 0, PathShearY = 0, PathTwist = 0, PathTwistBegin = 0, PathRadiusOffset = 0, PathTaperX = 0, PathTaperY = 0, PathRevolutions = 0, PathSkew = 0, ProfileBegin = 0, ProfileEnd = 0, ProfileHollow = 0, BypassRaycast = 1, RayStart = location_to_rez, RayEnd = location_to_rez, RayTargetID = RayTargetID, RayEndIsIntersection = 0, Scale = (0.5, 0.5, 0.5), Rotation = (0, 0, 0, 1), State = 0) + self.object_add(self.agent.agent_id, self.agent.session_id, + GroupID = GroupID, PCode = PCodeEnum.Primitive, + Material = 3, AddFlags = 2, PathCurve = 16, + ProfileCurve = 1, PathBegin = 0, PathEnd = 0, + PathScaleX = 100, PathScaleY = 100, PathShearX = 0, + PathShearY = 0, PathTwist = 0, PathTwistBegin = 0, + PathRadiusOffset = 0, PathTaperX = 0, PathTaperY = 0, + PathRevolutions = 0, PathSkew = 0, ProfileBegin = 0, + ProfileEnd = 0, ProfileHollow = 0, BypassRaycast = 1, + RayStart = location_to_rez, RayEnd = location_to_rez, + RayTargetID = RayTargetID, RayEndIsIntersection = 0, + Scale = (0.5, 0.5, 0.5), Rotation = (0, 0, 0, 1), + State = 0) - def object_add(self, AgentID, SessionID, PCode, Material, AddFlags, PathCurve, ProfileCurve, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathTwist, PathTwistBegin, PathRadiusOffset, PathTaperX, PathTaperY, PathRevolutions, PathSkew, ProfileBegin, ProfileEnd, ProfileHollow, BypassRaycast, RayStart, RayEnd, RayTargetID, RayEndIsIntersection, Scale, Rotation, State, GroupID = UUID()): + def object_add(self, AgentID, SessionID, PCode, Material, AddFlags, + PathCurve, ProfileCurve, PathBegin, PathEnd, PathScaleX, + PathScaleY, PathShearX, PathShearY, PathTwist, + PathTwistBegin, PathRadiusOffset, PathTaperX, + PathTaperY, PathRevolutions, PathSkew, ProfileBegin, + ProfileEnd, ProfileHollow, BypassRaycast, RayStart, + RayEnd, RayTargetID, RayEndIsIntersection, Scale, + Rotation, State, GroupID = UUID()): ''' ObjectAdd - create new object in the world Simulator will assign ID and send message back to signal @@ -580,7 +607,7 @@ class ObjectManager(DataManager): X=Helpers.packed_u8_to_float(objdata, 13, -REGION_SIZE, REGION_SIZE), Y=Helpers.packed_u8_to_float(objdata, 14, -REGION_SIZE, REGION_SIZE), Z=Helpers.packed_u8_to_float(objdata, 15, -REGION_SIZE, REGION_SIZE)) - + object_list.append(object_properties) self.update_multiple_objects_properties(object_list) @@ -913,6 +940,61 @@ class ObjectManager(DataManager): self.update_multiple_objects_properties(object_list) + def send_RezScript(self, agent, prim, item_id=UUID(), + Enabled=True, + GroupID=UUID(), + BaseMask=Permissions.All, + OwnerMask=Permissions.All, + GroupMask=Permissions.None_, + EveryoneMask=Permissions.None_, + NextOwnerMask=Permissions.Transfer&Permissions.Move, + GroupOwned=False, + Type=AssetType.LSLText, + InvType=AssetType.LSLText, + Flags=0, + SaleType=0, + SalePrice=0, + Name="New Script", + Description="Created by PyOGP", + CreationDate=0, + CRC=0): + """ sends a RezScript message to the sim, not providing a item_id will + rez the default script otherwise the script with ItemID item_id will be rezzed + to prim""" + packet = Message('RezScript', + Block('AgentData', + AgentID = agent.agent_id, + SessionID = agent.session_id, + GroupID = agent.ActiveGroupID), + Block('UpdateBlock', + ObjectLocalID = prim.LocalID, + Enabled = Enabled), + Block('InventoryBlock', + ItemID = item_id, + FolderID = prim.FullID, + CreatorID = agent.agent_id, + OwnerID = agent.agent_id, + GroupID = GroupID, + BaseMask = BaseMask, + OwnerMask = OwnerMask, + GroupMask = GroupMask, + EveryoneMask = EveryoneMask, + NextOwnerMask = NextOwnerMask, + GroupOwned = GroupOwned, + TransactionID = UUID(), + Type = Type, + InvType = InvType, + Flags = Flags, + SaleType = SaleType, + SalePrice = SalePrice, + Name = Name, + Description = Description, + CreationDate = CreationDate, + CRC = CRC)) + agent.region.enqueue_message(packet) + + + class Object(object): """ represents an Object @@ -1214,7 +1296,7 @@ class Object(object): """ - self.send_ObjectSelect(agent, agent.agent_id, agent.session_id, [self.LocalID]) + self.send_ObjectDeselect(agent, agent.agent_id, agent.session_id, [self.LocalID]) def send_ObjectDeselect(self, agent, AgentID, SessionID, ObjectLocalIDs): """ send an ObjectDeselect message to the agent's host simulator @@ -1230,13 +1312,15 @@ class Object(object): agent.region.enqueue_message(packet) + def _update_properties(self, properties): """ takes a dictionary of attribute:value and makes it so """ - for attribute in properties: + for attribute in properties.keys(): setattr(self, attribute, properties[attribute]) + """ Contributors can be viewed at: http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt diff --git a/pyogp/lib/base/tests/test_appearance.py b/pyogp/lib/base/tests/test_appearance.py index cdacbc1..971b015 100644 --- a/pyogp/lib/base/tests/test_appearance.py +++ b/pyogp/lib/base/tests/test_appearance.py @@ -22,8 +22,6 @@ class TestAppearance(unittest.TestCase): self.settings = Settings() self.agent = Agent() self.appearance = AppearanceManager(self.agent, settings = self.settings) - - self.agent.agent_id = UUID("01234567-89ab-cdef-0123-456789abcdef") self.agent.session_id = UUID("fedcba98-7654-3210-fedc-ba9876543210") self.agent.region = DummyRegion() diff --git a/pyogp/lib/base/tests/test_inventory.py b/pyogp/lib/base/tests/test_inventory.py index 14137e1..fa5e3f9 100644 --- a/pyogp/lib/base/tests/test_inventory.py +++ b/pyogp/lib/base/tests/test_inventory.py @@ -5,7 +5,14 @@ import unittest import uuid # pyogp -from pyogp.lib.base.inventory import InventoryManager, InventoryFolder +from pyogp.lib.base.inventory import InventoryManager, InventoryFolder, \ + InventoryItem +from pyogp.lib.base.datatypes import UUID +from pyogp.lib.base.agent import Agent +from pyogp.lib.base.region import Region +from pyogp.lib.base.utilities.enums import AssetType, InventoryType, \ + WearablesIndex, Permissions +from pyogp.lib.base.message.message import Message, Block # pyogp tests import pyogp.lib.base.tests.config @@ -16,37 +23,186 @@ class TestInventory(unittest.TestCase): self.inventory = InventoryManager() - self.folder_data = [{'parent_id': '00000000-0000-0000-0000-000000000000', 'version': 154, 'name': 'My Inventory', 'type_default': 8, 'folder_id': '0201a00f-afde-477d-967b-e731d186b9d6'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 9, 'name': 'Trash', 'type_default': 14, 'folder_id': '1640c442-85a9-c917-361a-f86bf72dab1a'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Animations', 'type_default': 20, 'folder_id': '2e406320-ab10-6e2e-8f6f-de2572ffd426'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 6, 'name': 'Daft Tux', 'type_default': -1, 'folder_id': '3d62430b-9a61-7921-f376-46fb81b61594'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 2, 'name': 'Bot attachments1', 'type_default': -1, 'folder_id': '54e00f7f-1110-26d4-de54-6bd0809a429b'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 4, 'name': 'Daft Tux', 'type_default': -1, 'folder_id': '57e90615-2b0f-f029-3c09-3bbfa89f09f1'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 394, 'name': 'Totoro', 'type_default': -1, 'folder_id': '6115057c-8db5-be07-2701-397b819e84a5'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 3, 'name': 'Calling Cards', 'type_default': 2, 'folder_id': '629d9d71-ffc3-dbe4-3b96-0dc75c7be1bc'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 5, 'name': 'PinkieBot', 'type_default': -1, 'folder_id': '8a0cef67-18d8-fb6d-06ef-f1520595cf34'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 96, 'name': 'Objects', 'type_default': 6, 'folder_id': '8ed27f52-b020-509b-585d-ad2f3bfbce05'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 4, 'name': 'Notecards', 'type_default': 7, 'folder_id': '909ee313-b342-88b5-dca4-45d9df75b4b8'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 7, 'name': 'PinkieBot', 'type_default': -1, 'folder_id': '948c687f-ae1e-bf86-3faa-6cd1b2f132ff'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Textures', 'type_default': 0, 'folder_id': '9aacf954-8a44-f4ca-5913-34d035eeae41'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Sounds', 'type_default': 1, 'folder_id': 'bf2a5d50-2766-5031-1ffb-20acd137b58f'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Lost And Found', 'type_default': 16, 'folder_id': 'd6d82d34-47ff-afec-f71f-b1ea20df9ccb'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 675, 'name': 'Totoro Box for Bots', 'type_default': -1, 'folder_id': 'e2bc553b-d0c4-c588-f664-04d8474f8376'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Landmarks', 'type_default': 3, 'folder_id': 'e7c67062-6b47-feb6-85d7-86013cdb068c'}, {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Scripts', 'type_default': 10, 'folder_id': 'f7e94373-f376-fd46-59a5-57d2562bf514'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 4, 'name': 'PinkieBot', 'type_default': -1, 'folder_id': '155551ef-c49a-98b8-d554-ab9f7a1b1ae8'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 2, 'name': 'Bot attachments1', 'type_default': -1, 'folder_id': '15edc217-14dc-b454-7c82-535e2fa423b2'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 63, 'name': 'Trash', 'type_default': 14, 'folder_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Photo Album', 'type_default': 15, 'folder_id': '580fb866-1c5f-4f58-8927-0d9e295799f4'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 2, 'name': 'Calling Cards', 'type_default': 2, 'folder_id': '6f4c244f-8f14-42f9-a900-4b73f9a2a48e'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 7, 'name': 'Scripts', 'type_default': 10, 'folder_id': '7adc4a9a-0370-4833-a25c-872be4ae6e22'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Landmarks', 'type_default': 3, 'folder_id': '7b3f2f88-666e-4b68-80b1-4dab3359ba3b'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 5, 'name': 'Textures', 'type_default': 0, 'folder_id': '82af0bfc-42e5-4134-980d-a7587e563499'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 8, 'name': 'Notecards', 'type_default': 7, 'folder_id': '8fe0babc-c915-4481-9299-2ec868541496'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 9, 'name': 'Body Parts', 'type_default': 13, 'folder_id': 'aba5a5d2-9e13-401c-943d-95f79904c487'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 3, 'name': 'Gestures', 'type_default': 21, 'folder_id': 'bd1d2af5-df49-4b98-9fc6-301b49042a89'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 49, 'name': 'Objects', 'type_default': 6, 'folder_id': 'c55798a9-463a-4065-9604-4572924387a8'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 7, 'name': 'Clothing', 'type_default': 5, 'folder_id': 'c833cc69-ee0d-4d50-b014-be8101cbb7c1'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 21, 'name': 'Object', 'type_default': -1, 'folder_id': 'cc22dc06-71fa-0059-f2af-64723c987068'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 37, 'name': 'Lost And Found', 'type_default': 16, 'folder_id': 'd01c9207-347f-48cc-a307-06f7a967e974'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Animations', 'type_default': 20, 'folder_id': 'dcb179f7-a58a-4b97-8bfa-bc395988945a'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 45, 'name': 'Totoro', 'type_default': -1, 'folder_id': 'effd593a-10b8-8fb1-52a3-86b930b5b6cb'}, {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Sounds', 'type_default': 1, 'folder_id': 'fca217d3-62b9-498f-b084-a137582850da'}, {'parent_id': 'bd1d2af5-df49-4b98-9fc6-301b49042a89', 'version': 19, 'name': 'Female Gestures', 'type_default': -1, 'folder_id': '01a2f40b-a034-5911-d6a3-7804547c105a'}, {'parent_id': 'bd1d2af5-df49-4b98-9fc6-301b49042a89', 'version': 28, 'name': 'Common Gestures', 'type_default': -1, 'folder_id': '38bc2d0e-f626-6fd6-3ea7-cb8f9b753af3'}] - - def tearDown(self): - - pass - - def test_display_folder_contents_by_id(self): + self.folder_data = [{'parent_id': '00000000-0000-0000-0000-000000000000', 'version': 154, 'name': 'My Inventory', 'type_default': 8, 'folder_id': '0201a00f-afde-477d-967b-e731d186b9d6'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 9, 'name': 'Trash', 'type_default': 14, 'folder_id': '1640c442-85a9-c917-361a-f86bf72dab1a'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Animations', 'type_default': 20, 'folder_id': '2e406320-ab10-6e2e-8f6f-de2572ffd426'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 6, 'name': 'Daft Tux', 'type_default': -1, 'folder_id': '3d62430b-9a61-7921-f376-46fb81b61594'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 2, 'name': 'Bot attachments1', 'type_default': -1, 'folder_id': '54e00f7f-1110-26d4-de54-6bd0809a429b'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 4, 'name': 'Daft Tux', 'type_default': -1, 'folder_id': '57e90615-2b0f-f029-3c09-3bbfa89f09f1'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 394, 'name': 'Totoro', 'type_default': -1, 'folder_id': '6115057c-8db5-be07-2701-397b819e84a5'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 3, 'name': 'Calling Cards', 'type_default': 2, 'folder_id': '629d9d71-ffc3-dbe4-3b96-0dc75c7be1bc'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 5, 'name': 'PinkieBot', 'type_default': -1, 'folder_id': '8a0cef67-18d8-fb6d-06ef-f1520595cf34'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 96, 'name': 'Objects', 'type_default': 6, 'folder_id': '8ed27f52-b020-509b-585d-ad2f3bfbce05'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 4, 'name': 'Notecards', 'type_default': 7, 'folder_id': '909ee313-b342-88b5-dca4-45d9df75b4b8'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 7, 'name': 'PinkieBot', 'type_default': -1, 'folder_id': '948c687f-ae1e-bf86-3faa-6cd1b2f132ff'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Textures', 'type_default': 0, 'folder_id': '9aacf954-8a44-f4ca-5913-34d035eeae41'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Sounds', 'type_default': 1, 'folder_id': 'bf2a5d50-2766-5031-1ffb-20acd137b58f'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Lost And Found', 'type_default': 16, 'folder_id': 'd6d82d34-47ff-afec-f71f-b1ea20df9ccb'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 675, 'name': 'Totoro Box for Bots', 'type_default': -1, 'folder_id': 'e2bc553b-d0c4-c588-f664-04d8474f8376'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Landmarks', 'type_default': 3, 'folder_id': 'e7c67062-6b47-feb6-85d7-86013cdb068c'}, + {'parent_id': '0201a00f-afde-477d-967b-e731d186b9d6', 'version': 1, 'name': 'Scripts', 'type_default': 10, 'folder_id': 'f7e94373-f376-fd46-59a5-57d2562bf514'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 4, 'name': 'PinkieBot', 'type_default': -1, 'folder_id': '155551ef-c49a-98b8-d554-ab9f7a1b1ae8'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 2, 'name': 'Bot attachments1', 'type_default': -1, 'folder_id': '15edc217-14dc-b454-7c82-535e2fa423b2'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 63, 'name': 'Trash', 'type_default': 14, 'folder_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Photo Album', 'type_default': 15, 'folder_id': '580fb866-1c5f-4f58-8927-0d9e295799f4'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 2, 'name': 'Calling Cards', 'type_default': 2, 'folder_id': '6f4c244f-8f14-42f9-a900-4b73f9a2a48e'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 7, 'name': 'Scripts', 'type_default': 10, 'folder_id': '7adc4a9a-0370-4833-a25c-872be4ae6e22'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Landmarks', 'type_default': 3, 'folder_id': '7b3f2f88-666e-4b68-80b1-4dab3359ba3b'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 5, 'name': 'Textures', 'type_default': 0, 'folder_id': '82af0bfc-42e5-4134-980d-a7587e563499'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 8, 'name': 'Notecards', 'type_default': 7, 'folder_id': '8fe0babc-c915-4481-9299-2ec868541496'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 9, 'name': 'Body Parts', 'type_default': 13, 'folder_id': 'aba5a5d2-9e13-401c-943d-95f79904c487'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 3, 'name': 'Gestures', 'type_default': 21, 'folder_id': 'bd1d2af5-df49-4b98-9fc6-301b49042a89'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 49, 'name': 'Objects', 'type_default': 6, 'folder_id': 'c55798a9-463a-4065-9604-4572924387a8'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 7, 'name': 'Clothing', 'type_default': 5, 'folder_id': 'c833cc69-ee0d-4d50-b014-be8101cbb7c1'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 21, 'name': 'Object', 'type_default': -1, 'folder_id': 'cc22dc06-71fa-0059-f2af-64723c987068'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 37, 'name': 'Lost And Found', 'type_default': 16, 'folder_id': 'd01c9207-347f-48cc-a307-06f7a967e974'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Animations', 'type_default': 20, 'folder_id': 'dcb179f7-a58a-4b97-8bfa-bc395988945a'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 45, 'name': 'Totoro', 'type_default': -1, 'folder_id': 'effd593a-10b8-8fb1-52a3-86b930b5b6cb'}, + {'parent_id': '52aaafe8-7c5e-8fe0-b47e-097198648c9b', 'version': 1, 'name': 'Sounds', 'type_default': 1, 'folder_id': 'fca217d3-62b9-498f-b084-a137582850da'}, + {'parent_id': 'bd1d2af5-df49-4b98-9fc6-301b49042a89', 'version': 19, 'name': 'Female Gestures', 'type_default': -1, 'folder_id': '01a2f40b-a034-5911-d6a3-7804547c105a'}, + {'parent_id': 'bd1d2af5-df49-4b98-9fc6-301b49042a89', 'version': 28, 'name': 'Common Gestures', 'type_default': -1, 'folder_id': '38bc2d0e-f626-6fd6-3ea7-cb8f9b753af3'}] for folder in self.folder_data: - self.inventory._store_inventory_folder(folder) + uuid_ = UUID('12345678-1234-1234-1234-123456789abc') + inventory_item = InventoryItem(uuid_, + '0201a00f-afde-477d-967b-e731d186b9d6', + uuid_, + uuid_, + UUID(), + 0, + 0, + 0, + 0, + 0, + False, + uuid_, + 10, + 10, + 0, + 0, + 0, + "a_item", + "a_item_desc", + 0, + 0) + self.inventory._store_inventory_item(inventory_item) + + def tearDown(self): + self.inventory = None + self.folder_data = None + + def test_display_folder_contents_by_id(self): + self.assertEquals(len(self.inventory.display_folder_contents(folder_id = uuid.UUID('52aaafe8-7c5e-8fe0-b47e-097198648c9b'))), 18) def test_display_folder_contents_by_nonexistant_id(self): - for folder in self.folder_data: - - self.inventory._store_inventory_folder(folder) - self.assertEquals(len(self.inventory.display_folder_contents(folder_id = uuid.UUID('00000001-0000-0000-0000-000000000000'))), 0) - ''' - def test_display_folder_contents_by_name(self): + def test_search_inventory_for_folders(self): + + matches = self.inventory.search_inventory(self.inventory.folders, + item_id='0201a00f-afde-477d-967b-e731d186b9d6') + self.assertEqual(len(matches), 1) + folder = matches.pop() + self.assertEqual(folder.Name, 'My Inventory') + + matches = self.inventory.search_inventory(self.inventory.folders, + name='Trash') + self.assertTrue(len(matches), 1) + folder = matches.pop() + self.assertEqual(str(folder.FolderID), '52aaafe8-7c5e-8fe0-b47e-097198648c9b') + + def test_search_inventory_for_items(self): + + matches = self.inventory.search_inventory(self.inventory.folders, + item_id='12345678-1234-1234-1234-123456789abc') + self.assertTrue(len(matches), 1) + item = matches.pop() + self.assertEqual(item.Name, 'a_item') + + matches = self.inventory.search_inventory(self.inventory.folders, + name='a_item') + self.assertTrue(len(matches), 1) + item = matches.pop() + self.assertEqual(str(item.ItemID), '12345678-1234-1234-1234-123456789abc') + + def test_create_new_item(self): + + self.inventory.agent = Agent() + agent_id = UUID() + agent_id.random() + self.inventory.agent.agent_id = agent_id + + self.inventory.agent.region = DummyRegion() + + matches = self.inventory.search_inventory(self.inventory.folders, + name='My Inventory') + self.assertEqual(len(matches), 1) + folder = matches.pop() + self.inventory.create_new_item(folder, "Name", "Desc", AssetType.LSLText, + InventoryType.LSL, WearablesIndex.WT_SHAPE, + 0) + packet = self.inventory.agent.region.dummy_packet_holder.pop() + self.assertEqual(packet.name, "CreateInventoryItem") + + self.assertEqual(packet.get_block('InventoryBlock')[0].get_variable('FolderID').data, folder.FolderID) + self.assertEqual(packet.get_block('InventoryBlock')[0].get_variable('Name').data, 'Name') + self.assertEqual(packet.get_block('InventoryBlock')[0].get_variable('Description').data, 'Desc') + + fake_uuid = UUID() + fake_uuid.random() + packet = Message('UpdateCreateInventoryItem', + Block('AgentData', + AgentID = UUID(), + SimApproved = True, + TransactionID = UUID()), + Block('InventoryData', + ItemID=fake_uuid, + FolderID=folder.FolderID, + CallbackID=0, + CreatorID=agent_id, + OwnerID=agent_id, + GroupID=UUID(), + BaseMask=Permissions.All, + OwnerMask=Permissions.All, + GroupMask=Permissions.None_, + EveryoneMask=Permissions.None_, + NextOwnerMask=Permissions.None_, + GroupOwned=False, + AssetID=UUID(), + Type=AssetType.LSLText, + InvType=InventoryType.LSL, + Flags=0, + SaleType=0, + SalePrice=0, + Name="Name", + Description="Desc", + CreationDate=0, + CRC=0)) + self.inventory.agent.region.message_handler.handle(packet) + matches = self.inventory.search_inventory(self.inventory.folders, + name="Name") + self.assertEqual(len(matches), 1) + item = matches.pop() + self.assertEqual(item.Name, "Name") + ''' + def test_display_folder_contents_by_name(self): for folder in self.folder_data: self.inventory._store_inventory_folder(folder) self.assertEquals(len(self.inventory._display_folder_contents(name = 'Trash')), 18) - ''' + ''' + + +class DummyRegion(Region): + dummy_packet_holder = [] + def enqueue_message(self, packet, reliable = False): + self.dummy_packet_holder.append(packet) def test_suite(): from unittest import TestSuite, makeSuite diff --git a/pyogp/lib/base/tests/test_objects.py b/pyogp/lib/base/tests/test_objects.py index 5b7bad6..eee8182 100644 --- a/pyogp/lib/base/tests/test_objects.py +++ b/pyogp/lib/base/tests/test_objects.py @@ -8,9 +8,12 @@ from binascii import unhexlify from pyogp.lib.base.objects import * from pyogp.lib.base.settings import Settings from pyogp.lib.base.region import Region +from pyogp.lib.base.agent import Agent +from pyogp.lib.base.datatypes import UUID, Vector3 # pyogp messaging from pyogp.lib.base.message.udpdeserializer import UDPMessageDeserializer +from pyogp.lib.base.message.message import Message, Block # pyogp tests import pyogp.lib.base.tests.config @@ -29,6 +32,7 @@ class TestObjects(unittest.TestCase): self.object_store = ObjectManager(region = self.region, settings = self.settings) self.object_store.enable_callbacks() + self.data = [] def tearDown(self): pass @@ -67,7 +71,59 @@ class TestObjects(unittest.TestCase): self.assertEquals(known_objects, [(str(uuid.UUID('e2ba7ac7-db28-24e3-484d-f418b045e62d')), 159536), (str(uuid.UUID('6cbafc4a-9758-9481-cd74-c7ccc89e7440')), 171059), (str(uuid.UUID('d2b300c3-71f1-6887-2750-6d48da05e2f5')), 171036), (str(uuid.UUID('dbb7d110-3f65-0859-d494-3dc40ffb2b61')), 171037), (str(uuid.UUID('1f389eb9-8639-28ff-c37b-a3e4f39a7fed')), 171038)]) + def test_onObjectUpdate_selected(self): + self.object_store.agent = Agent() + fake_uuid = UUID() + fake_uuid.random() + packet = Message('ObjectUpdate', + Block('RegionData', + RegionHandle=0, + TimeDilation=0), + Block('ObjectData', + ID=1, + State=1, + FullID=fake_uuid, + CRC=0, + PCode=0, + Material=0, + ClickAction=0, + Scale=Vector3(X=0.0, Y=0.0, Z=0.0), + ObjectData='', + ParentID=fake_uuid, + UpdateFlags=0, + ProfileCurve=0, + PathBegin=0.0, + PathEnd=0.0, + PathScaleX=0.0, + PathScaleY=0.0, + PathShearX=0.0, + PathShearY=0.0, + PathTwist=-1, + PathTwistBegin=-1, + PathRadiusOffset=-1, + PathTaperX=-1, + PathTaperY=-1, + PathRevolutions=0, + PathSkew=-1, + ProfileBegin=0, + ProfileEnd=0, + ProfileHollow=0, + TextureEntry='', + TextureAnim='', + NameValue='Test', + Data='', + Text='', + TextColor=0x0, + MedialURL='')) + + def callback(payload): + self.data.append("foo") + object_handler = self.object_store.agent.events_handler.register("ObjectSelected") + object_handler.subscribe(callback) + self.object_store.region.message_handler.handle(packet) + self.assertTrue(self.data.pop, "foo") + def test_suite(): from unittest import TestSuite, makeSuite suite = TestSuite() diff --git a/pyogp/lib/base/utilities/enums.py b/pyogp/lib/base/utilities/enums.py index 8e78028..b825cff 100644 --- a/pyogp/lib/base/utilities/enums.py +++ b/pyogp/lib/base/utilities/enums.py @@ -439,7 +439,17 @@ class TransferStatus(object): Error = -1 UnknownSource = -2 InsufficientPermissions = -3 - + + + +class Permissions(object): + Transfer = 1 << 13 + Modify = 1 << 14 + Copy = 1 << 15 + Move = 1 << 19 + None_ = 0 + All = 0x7FFFFFFF + Unrestricted = Transfer | Modify | Copy """ Contributors can be viewed at: http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt diff --git a/pyogp/lib/base/params.py b/pyogp/lib/base/visualparams.py similarity index 100% rename from pyogp/lib/base/params.py rename to pyogp/lib/base/visualparams.py