From 55cf7be2ba2ffe5c1dacef77b02b7506475a9a7b Mon Sep 17 00:00:00 2001 From: "enus.linden" Date: Mon, 20 Jul 2009 23:34:00 +0000 Subject: [PATCH] removing dependencies to packets.py from assets and appearance --- pyogp/lib/base/appearance.py | 295 ++++++++++++++++++------------ pyogp/lib/base/assets.py | 166 +++++++++++++---- pyogp/lib/base/utilities/enums.py | 55 ++++++ 3 files changed, 363 insertions(+), 153 deletions(-) diff --git a/pyogp/lib/base/appearance.py b/pyogp/lib/base/appearance.py index e6ced7e..4dfbec2 100644 --- a/pyogp/lib/base/appearance.py +++ b/pyogp/lib/base/appearance.py @@ -9,13 +9,15 @@ from eventlet import api from pyogp.lib.base.datamanager import DataManager # pyogp messaging from pyogp.lib.base.message.message_handler import MessageHandler -from pyogp.lib.base.message.packets import * +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.datatypes import * -from pyogp.lib.base.utilities.enums import BakedIndex, TextureIndex, WearableMap +from pyogp.lib.base.datatypes import UUID, Vector3 +from pyogp.lib.base.utilities.enums import BakedIndex, TextureIndex, \ + WearableMap, AssetType, WearablesIndex # initialize logging logger = getLogger('pyogp.lib.base.appearance') @@ -40,13 +42,15 @@ class AppearanceManager(DataManager): for i in range(TextureIndex.TEX_COUNT): self.wearables.append(Wearable(i)) self.helpers = Helpers() - self.bakedTextures = [] #indexed by TextureIndex + self.bakedTextures = {} #indexed by TextureIndex for i in range(BakedIndex.BAKED_COUNT): - self.bakedTextures.append(BakedTexture(i)) - self.params = VisualParams().params + self.bakedTextures[i] = BakedTexture(i) + self.visualParams = VisualParams().params + self.visualParams[32].value = 1.0 self.TextureEntry = "" self.Size = Vector3(X = 0.45, Y = 0.60, Z = 1.14 ) # Z which is Height needs to be calculated using params + self.requests = [] def enable_callbacks(self): """ enables the calback handlers for this AppearanceManager @@ -62,25 +66,48 @@ class AppearanceManager(DataManager): onAgentDataUpdate_received = self.agent.region.message_handler.register('AgentDataUpdate') onAgentDataUpdate_received.subscribe(self.helpers.log_packet, self) ''' - def request_agent_wearables(self): """ Asks the simulator what the avatar is wearing + #TODO create a one--shot callback """ if self.agent.agent_id == None or self.agent.session_id == None or \ str(self.agent.agent_id) == str(UUID()) or \ str(self.agent.session_id) == str(UUID()): log(WARNING, "Agent has either no agent_id or session_id, message not sent") return - - packet = AgentWearablesRequestPacket() + + self.send_AgentWearablesRequest(self.agent.agent_id, + self.agent.session_id) + + def wearableArrived(self, assetID, isSuccess): + """ + callback for wearables request + """ - packet.AgentData['AgentID'] = self.agent.agent_id - packet.AgentData['SessionID'] = self.agent.session_id - - self.agent.region.enqueue_message(packet()) + self.requests.remove(str(assetID)) + if isSuccess: + asset = self.agent.asset_manager.get_asset(assetID) + #log(INFO, "wearable data\n, %s" % asset.data ) + for paramID in asset.params.keys(): + #log (INFO, 'Changing param %d from %f to %f' %(paramID, + # self.visualParams[paramID].value, + # asset.params[paramID])) + + self.visualParams[paramID].value = asset.params[paramID] + + if len(self.requests) == 0: + #log(INFO, "YAY!! Got all requested assets") + self.send_AgentCachedTexture(self.agent.agent_id, + self.agent.session_id, + self.bakedTextures, + self.wearables) + self.send_AgentIsNowWearing(self.agent.agent_id, + self.agent.session_id, + self.wearables) + def onAgentWearablesUpdate(self, packet): """ Automatically tells simulator avatar is wearing the wearables from @@ -90,68 +117,22 @@ class AppearanceManager(DataManager): Error Checking: make sure agent and session id are correct make sure this method is only called once. - #TODO download wearables using assetIDs, upload wearables if wearing none """ - self.verifyAgentData(packet) - #log(INFO, "Got AgentWearablesUpdate: %s" % packet) - for wearable in packet.blocks['WearableData']: - wearableType = wearable.get_variable('WearableType').data - itemID = wearable.get_variable('ItemID').data - assetID = wearable.get_variable('AssetID').data - self.wearables[wearableType].ItemID = itemID - self.wearables[wearableType].AssetID = assetID - self.send_AgentIsNowWearing() - self.send_AgentCachedTexture() - - - def send_AgentIsNowWearing(self): - """ - Tell the simulator that avatar is wearing initial items - """ - packet = AgentIsNowWearingPacket() - - packet.AgentData['AgentID'] = self.agent.agent_id - packet.AgentData['SessionID'] = self.agent.session_id - for wearable in self.wearables: - WearableData = {} - WearableData['ItemID'] = wearable.ItemID - WearableData['WearableType'] = wearable.WearableType - packet.WearableDataBlocks.append(WearableData) - self.agent.region.enqueue_message(packet(), True) - - def send_AgentSetAppearance(self): - """ - Informs simulator how avatar looks - """ - packet = AgentSetAppearancePacket() - #AgentData - packet.AgentData['AgentID'] = self.agent.agent_id - packet.AgentData['SessionID'] = self.agent.session_id - packet.AgentData['SerialNum'] = self.AgentSetSerialNum - packet.AgentData['Size'] = Vector3(X = 0.45, Y = 0.60, Z = 1.14) #Hard code? From Height in avatar_lad.xml - - #WearableData - for bakedTexture in self.bakedTextures: - WearableData = {} - WearableData['CacheID'] = bakedTexture.TextureID - WearableData['TextureIndex'] = bakedTexture.bakedIndex - packet.WearableDataBlocks.append(WearableData) + #self.verifyAgentData(packet) + log(INFO, "Got AgentWearablesUpdate: %s" % packet) + for wearableData in packet.blocks['WearableData']: + wearableType = wearableData.get_variable('WearableType').data + itemID = wearableData.get_variable('ItemID').data + assetID = wearableData.get_variable('AssetID').data + wearable = self.wearables[wearableType] + wearable.ItemID = itemID + wearable.AssetID = assetID + if str(assetID) != '00000000-0000-0000-0000-000000000000': + self.agent.asset_manager.request_asset(assetID, wearable.getAssetType(), + True, self.wearableArrived) + self.requests.append(str(assetID)) - #ObjectData - packet.ObjectData['TextureEntry'] = self.TextureEntry - - #VisualParam - paramkeys = self.params.keys() - paramkeys.sort() #since param id is not sent the parameters should be in sorted order - for paramkey in paramkeys: - if self.params[paramkey].group == 0: - paramBlock = {} - paramBlock['ParamValue'] = self.params[paramkey].floatToByte() - packet.VisualParamBlocks.append(paramBlock) - self.agent.region.enqueue_message(packet()) - self.AgentSetSerialNum += 1 - def onAvatarTextureUpdate(self, packet): raise NotImplemented("onAvatarTextureUpdate") @@ -160,39 +141,7 @@ class AppearanceManager(DataManager): Informs viewer how other avatars look """ raise NotImplemented("onAvatarAppearance") - - def send_AgentCachedTexture(self): - """ - Ask the simulator what baked textures it has cached. - TODO Create a one-shot callback? - """ - #AgentData - packet = AgentCachedTexturePacket() - packet.AgentData['AgentID'] = self.agent.agent_id - packet.AgentData['SessionID'] = self.agent.session_id - packet.AgentData['SerialNum'] = self.AgentCachedSerialNum - - #WearableData - for i in range(BakedIndex.BAKED_COUNT): - wearableData = {} - wearableData['ID'] = self.get_hash(i) - wearableData['TextureIndex'] = i - packet.WearableDataBlocks.append(wearableData) - self.agent.region.enqueue_message(packet(), True) - self.AgentCachedSerialNum += 1 - - def get_hash(self, bakedIndex): - """ - Creates a hash using the assetIDs for each wearable in a baked layer - """ - wearable_map = WearableMap().map - hash = UUID() - for wearable_index in wearable_map[bakedIndex]: - hash ^= self.wearables[wearable_index].AssetID - if str(hash) != '00000000-0000-0000-0000-000000000000': - hash ^= self.bakedTextures[bakedIndex].Hash - return hash - + def onAgentCachedTextureResponse(self, packet): """ Update the bakedTextures with their TextureIDs and HostNames and call @@ -203,7 +152,12 @@ class AppearanceManager(DataManager): bakedIndex = bakedTexture.get_variable('TextureIndex').data self.bakedTextures[bakedIndex].TextureID = bakedTexture.get_variable('TextureID').data self.bakedTextures[bakedIndex].HostName = bakedTexture.get_variable('HostName').data - self.send_AgentSetAppearance() + self.send_AgentSetAppearance(self.agent.agent_id, + self.agent.session_id, + self.Size, + self.bakedTextures, + self.TextureEntry, + self.visualParams) def verifyAgentData(self, packet): """ @@ -213,8 +167,92 @@ class AppearanceManager(DataManager): pSessionID = packet.blocks['AgentData'][0].get_variable("SessionID").data if str(pAgentID) != str(self.agent.agent_id): log(WARNING, "%s packet does not have an AgentID", packet.name) + if str(pSessionID) != str(self.agent.session_id): + log(WARNING, "%s packet does not have a SessionID" % packet.name) + + def send_AgentWearablesRequest(self, AgentID, SessionID): + """ + sends and AgentWearablesRequest message. + """ + packet = Message('AgentWearablesRequest', + Block('AgentData', + AgentID = AgentID, + SessionID = SessionID)) + self.agent.region.enqueue_message(packet) + + def send_AgentIsNowWearing(self, AgentID, SessionID, wearables): + """ + Tell the simulator that avatar is wearing initial items + """ + args = [Block('AgentData', + AgentID = AgentID, + SessionID = SessionID)] + args += [Block('WearableData', + ItemID = wearable.ItemID, + WearableType = wearable.WearableType) \ + for wearable in wearables] + + packet = Message('AgentIsNowWearing', *args) + + self.agent.region.enqueue_message(packet, True) + + def send_AgentCachedTexture(self, AgentID, SessionID, bakedTextures, + wearables): + """ + Ask the simulator what baked textures it has cached. + TODO Create a one-shot callback? + """ + args = [Block('AgentData', + AgentID = AgentID, + SessionID = SessionID, + SerialNum = self.AgentCachedSerialNum)] + + args += [Block('WearableData', + ID = bakedTextures[i].get_hash(wearables), + TextureIndex = i) \ + for i in range(BakedIndex.BAKED_COUNT)] + packet = Message('AgentCachedTexture', *args) + + + + self.AgentCachedSerialNum += 1 + self.agent.region.enqueue_message(packet, True) + + + def send_AgentSetAppearance(self, AgentID, SessionID, Size, bakedTextures, + TextureEntry, visualParams): + """ + Informs simulator how avatar looks + """ + args = [Block('AgentData', + AgentID = AgentID, + SessionID = SessionID, + SerialNum = self.AgentSetSerialNum, + Size = Size)] + + args += [Block('WearableData', + CacheID = bakedTextures[i].TextureID, + TextureIndex = bakedTextures[i].bakedIndex) \ + for i in range(BakedIndex.BAKED_COUNT)] + + args += [Block('ObjectData', + TextureEntry = TextureEntry)] + + paramkeys = visualParams.keys() + paramkeys.sort() + args += [Block('VisualParam', + ParamValue = visualParams[key].floatToByte()) \ + for key in paramkeys] + + packet = Message('AgentSetAppearance', *args) + + self.AgentSetSerialNum += 1 + self.agent.region.enqueue_message(packet) + + + class Wearable(object): """ Represents 1 of the 13 wearables an avatar can wear @@ -224,6 +262,25 @@ class Wearable(object): self.ItemID = ItemID self.AssetID = AssetID + def getAssetType(self): + if self.WearableType == WearablesIndex.WT_SHAPE or \ + self.WearableType == WearablesIndex.WT_SKIN or \ + self.WearableType == WearablesIndex.WT_HAIR or \ + self.WearableType == WearablesIndex.WT_EYES: + return AssetType.BodyPart + elif self.WearableType == WearablesIndex.WT_SHIRT or \ + self.WearableType == WearablesIndex.WT_PANTS or \ + self.WearableType == WearablesIndex.WT_SHOES or \ + self.WearableType == WearablesIndex.WT_SOCKS or \ + self.WearableType == WearablesIndex.WT_JACKET or \ + self.WearableType == WearablesIndex.WT_GLOVES or \ + self.WearableType == WearablesIndex.WT_UNDERSHIRT or \ + self.WearableType == WearablesIndex.WT_UNDERPANTS or \ + self.WearableType == WearablesIndex.WT_SKIRT: + return AssetType.Clothing + else: + return AssetType.NONE + class BakedTexture(object): """ @@ -235,19 +292,31 @@ class BakedTexture(object): self.HostName = None if bakedIndex == BakedIndex.BAKED_HEAD: - self.Hash = UUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6") + self.secret_hash = UUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6") elif bakedIndex == BakedIndex.BAKED_UPPER: - self.Hash = UUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f") + self.secret_hash = UUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f") elif bakedIndex == BakedIndex.BAKED_LOWER: - self.Hash = UUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f") + self.secret_hash = UUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f") elif bakedIndex == BakedIndex.BAKED_EYES: - self.Hash = UUID("b2cf28af-b840-1071-3c6a-78085d8128b5") + self.secret_hash = UUID("b2cf28af-b840-1071-3c6a-78085d8128b5") elif bakedIndex == BakedIndex.BAKED_SKIRT: - self.Hash = UUID("ea800387-ea1a-14e0-56cb-24f2022f969a") + self.secret_hash = UUID("ea800387-ea1a-14e0-56cb-24f2022f969a") elif bakedIndex == BakedIndex.BAKED_HAIR: - self.Hash = UUID("0af1ef7c-ad24-11dd-8790-001f5bf833e8") + self.secret_hash = UUID("0af1ef7c-ad24-11dd-8790-001f5bf833e8") else: - self.Hash = UUID() + self.secret_hash = UUID() + + def get_hash(self, wearables): + """ + Creates a hash using the assetIDs for each wearable in a baked layer + """ + wearable_map = WearableMap().map + hash = UUID() + for wearable_index in wearable_map[self.bakedIndex]: + hash ^= wearables[wearable_index].AssetID + if str(hash) != '00000000-0000-0000-0000-000000000000': + hash ^= self.secret_hash + return hash class AvatarTexture(object): """ diff --git a/pyogp/lib/base/assets.py b/pyogp/lib/base/assets.py index addbd74..b4016f6 100644 --- a/pyogp/lib/base/assets.py +++ b/pyogp/lib/base/assets.py @@ -7,13 +7,16 @@ from eventlet import api # pyogp from pyogp.lib.base.datamanager import DataManager +from pyogp.lib.base.utilities.enums import TransferChannelType, TransferSourceType, \ + TransferTargetType, TransferStatus + # pyogp messaging from pyogp.lib.base.message.message_handler import MessageHandler -from pyogp.lib.base.message.packets import * +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.datatypes import * +from pyogp.lib.base.datatypes import Vector3 # initialize logging @@ -25,59 +28,142 @@ class AssetManager(DataManager): The AssetManager class handles the assets of an Agent() instance Sample implementations: - Tests: + Tests: test_assets.py """ def __init__(self, agent, settings = None): super(AssetManager, self).__init__(agent, settings) - self.requests = [] # queue of requests? - self.assets = {} # indexed by assetID? + #indexed by assetID + self.assets = {} def enable_callbacks(self): - self.agent.region.message_handler.register('TransferInfo').subscribe(self.onTransferInfo) - self.agent.region.message_handler.register('TransferPacket').subscribe(self.onTransferPacket) + pass - def request_asset(self, assetID, assetType, isPriority): - self.requests.append(Asset(assetID, assetType, isPriority)) - packet = TransferRequestPacket() - #TransferInfo + + def request_asset(self, assetID, assetType, isPriority, callback): + """ + 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 + if request failed. On successful request the asset is store in + self.assets + """ transferID = UUID() #associate the assetID with the transferID transferID.random() - packet.TransferInfo['TransferID'] = transferID - packet.TransferInfo['ChannelType'] = 2 - packet.TransferInfo['SourceType'] = 2 + transferInfoHandler = self.agent.region.message_handler.register('TransferInfo') + transferPacketHandler = self.agent.region.message_handler.register('TransferPacket') + + def onTransferPacket(packet): + """ + TransferPacket of a successful TransferRequest + """ + # 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) + transferPacketHandler.unsubscribe(onTransferPacket) + + def onTransferInfo(packet): + """ + Status of TransferRequest + Account for size and multiple packets + """ + 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) + transferPacketHandler.unsubscribe(onTransferPacket) + transferInfoHandler.unsubscribe(onTransferInfo) + + transferInfoHandler.subscribe(onTransferInfo) + transferPacketHandler.subscribe(onTransferPacket) + if isPriority: - packet.TransferInfo['Priority'] = 1.0 + priority = 1.0 else: - packet.TransferInfo['Priority'] = 0.0 - packet.TransferInfo['Params'] = assetID.get_bytes() \ - + Helpers().int_to_bytes(5) #FIXME <-assetID & type endianness of type may not be correct - self.agent.region.enqueue_message(packet()) - return transferID + priortity = 0.0 + params = assetID.get_bytes() \ + + Helpers().int_to_bytes(assetType) + + self.send_TransferRequest(transferID, + TransferChannelType.Asset, + TransferSourceType.Asset, + priority, + params) - def onTransferInfo(self, packet): - log(INFO, "TransferInfo received %s" % packet) - #raise NotImplemented("onTranferInfo") - - def onTransferPacket(self, packet): - # fill in data for Asset in the requests queue and pop it off and story in assets dict - log(INFO, "TransferPacket received %s" % packet) - #raise NotImplemented("onTransferPacket") - - def get_asset(self, xxxID): - raise NotImplemented("get_asset") - - def pending_downloads(self): - if len(self.requests) > 0: - return True - return False + def get_asset(self, assetID): + + return self.assets[str(assetID)] + + + def send_TransferRequest(self, TransferID, ChannelType, SourceType, + Priority, Params): + """ + sends a TransferRequest packet to request an asset to download, the + assetID and assetType of the request are stored in the Params variable + see assets.request_asset for example. + """ + packet = Message('TransferRequest', + Block('TransferInfo', + TransferID = TransferID, + ChannelType = ChannelType, + SourceType = SourceType, + Priority = Priority, + Params = Params)) + + + self.agent.region.enqueue_message(packet) + + + class Asset(object): - def __init__(self, assetID, assetType, isPriority): + def __init__(self, assetID, assetType, data): self.assetID = assetID - self.assetType = assetID - self.isPriority = isPriority - self.data = None + self.assetType = assetType + self.data = data + +class AssetWearable(Asset): + + def __init__(self, assetID, assetType, data): + super(AssetWearable, self).__init__(assetID, assetType, data) + self.Version = -1 + self.Name = '' + self.Description = '' + self.Type = -1 + self.Permissions = '' + self.SaleInfo = '' + self.params = {} + self.textures = {} + self.parse_data() + + def parse_data(self): + tokens = self.data.split() + i = iter(tokens) + while True: + try: + token = i.next() + if token.lower() == 'version': + self.Version = int(i.next()) + if token.lower() == 'type': + self.Type = int(i.next()) + if token.lower() == 'parameters': + count = int(i.next()) + while count > 0: + paramID = int(i.next()) + self.params[paramID] = float(i.next()) + count -= 1 + if token.lower() == 'textures': + count = int(i.next()) + while count > 0: + textureID = int(i.next()) + self.textures[textureID] = UUID(i.next()) + count -= 1 + except StopIteration: + break """ Contributors can be viewed at: http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt diff --git a/pyogp/lib/base/utilities/enums.py b/pyogp/lib/base/utilities/enums.py index 34fc5d5..8e78028 100644 --- a/pyogp/lib/base/utilities/enums.py +++ b/pyogp/lib/base/utilities/enums.py @@ -384,6 +384,61 @@ class WearableMap(object): self.map[BakedIndex.BAKED_SKIRT] = [WearablesIndex.WT_SKIRT] self.map[BakedIndex.BAKED_HAIR] = [WearablesIndex.WT_HAIR] + +class AssetType(object): + Texture = 0 + Sound = 1 + CallingCard = 2 + Landmark = 3 + Script = 4 + Clothing = 5 + Object = 6 + NoteCard = 7 + Category = 8 + RootCategory = 9 + LSLText = 10 + LSLByteCode = 11 + TextureTGA = 12 + BodyPart = 13 + Trash = 14 + SnapshotCategory = 15 + LostAndFound = 16 + SoundWav = 17 + ImageTGA = 18 + ImageJPEG = 19 + Animation = 20 + Gesture = 21 + Simstate = 22 + Count = 23 + NONE = -1 + +class TransferChannelType(object): + Unknown = 0 + Misc = 1 + Asset = 2 + NumTypes = 3 + +class TransferSourceType(object): + Unknown = 0 + File = 1 + Asset = 2 + SimInvItem = 3 + SimEstate = 4 + NumTypes = 5 + +class TransferTargetType(object): + Unknown = 0 + File = 1 + VFile = 2 + +class TransferStatus(object): + OK = 0 + Done = 1 + Skip = 2 + Abort = 3 + Error = -1 + UnknownSource = -2 + InsufficientPermissions = -3 """ Contributors can be viewed at: