From 41fee8a893227b4ab947fcd7f68e04eab43dd0bb Mon Sep 17 00:00:00 2001 From: "enus.linden" Date: Fri, 28 Aug 2009 16:48:12 +0000 Subject: [PATCH] removing components from base that will llive in client --- pyogp/lib/base/agent.py | 1128 --------------------------- pyogp/lib/base/agentdomain.py | 174 ----- pyogp/lib/base/agentmanager.py | 177 ----- pyogp/lib/base/appearance.py | 350 --------- pyogp/lib/base/assets.py | 264 ------- pyogp/lib/base/event_system.py | 220 ------ pyogp/lib/base/groups.py | 634 --------------- pyogp/lib/base/inventory.py | 1138 --------------------------- pyogp/lib/base/login.py | 448 ----------- pyogp/lib/base/objects.py | 1343 -------------------------------- pyogp/lib/base/parcel.py | 1157 --------------------------- pyogp/lib/base/permissions.py | 65 -- pyogp/lib/base/region.py | 627 --------------- pyogp/lib/base/visualparams.py | 539 ------------- 14 files changed, 8264 deletions(-) delete mode 100644 pyogp/lib/base/agent.py delete mode 100644 pyogp/lib/base/agentdomain.py delete mode 100644 pyogp/lib/base/agentmanager.py delete mode 100644 pyogp/lib/base/appearance.py delete mode 100644 pyogp/lib/base/assets.py delete mode 100644 pyogp/lib/base/event_system.py delete mode 100644 pyogp/lib/base/groups.py delete mode 100644 pyogp/lib/base/inventory.py delete mode 100644 pyogp/lib/base/login.py delete mode 100644 pyogp/lib/base/objects.py delete mode 100644 pyogp/lib/base/parcel.py delete mode 100644 pyogp/lib/base/permissions.py delete mode 100644 pyogp/lib/base/region.py delete mode 100644 pyogp/lib/base/visualparams.py diff --git a/pyogp/lib/base/agent.py b/pyogp/lib/base/agent.py deleted file mode 100644 index 9853d9f..0000000 --- a/pyogp/lib/base/agent.py +++ /dev/null @@ -1,1128 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, WARNING, INFO, DEBUG -import re -import sys -import signal -import sets -import struct - -#related -from eventlet import api - -# pyogp -from pyogp.lib.base.login import Login, LegacyLoginParams, OGPLoginParams -from pyogp.lib.base.datatypes import Quaternion, Vector3, UUID -from pyogp.lib.base.exc import LoginError -from pyogp.lib.base.region import Region -from pyogp.lib.base.inventory import AIS, UDP_Inventory -from pyogp.lib.base.groups import GroupManager, Group -from pyogp.lib.base.event_system import AppEventsHandler, AppEvent -from pyogp.lib.base.appearance import AppearanceManager -from pyogp.lib.base.assets import AssetManager - -# pyogp messaging -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 ImprovedIMDialogue, MoneyTransactionType, TransactionFlags, AgentState, AgentUpdateFlags, AgentControlFlags - -# initialize logging -logger = getLogger('pyogp.lib.base.agent') -log = logger.log - -class Agent(object): - """ The Agent class is a container for agent specific data. - - Example, of login via the agent class: - Initialize the login class - - >>> client = Agent() - >>> client.login('https://login.agni.lindenlab.com/cgi-bin/login.cgi', 'firstname', 'lastname', 'secret', start_location = 'last') - - Sample implementations: examples/sample_agent_login.py - Tests: tests/login.txt, tests/test_agent.py - - """ - - def __init__(self, settings = None, firstname = '', lastname = '', password = '', agent_id = None, events_handler = None, handle_signals=True): - """ initialize this agent """ - - # 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() - - # allow the eventhandler to be passed in - # so that applications running multiple avatars - # may use the same eventhandler - - # otherwise, let's just use our own - if events_handler != None: - self.events_handler = events_handler - else: - self.events_handler = AppEventsHandler() - - # signal handler to capture erm signals - if handle_signals: - self.signal_handler = signal.signal(signal.SIGINT, self.sigint_handler) - - # storage containers for agent attributes - # we overwrite with what the grid tells us, rather than what - # is passed in and stored in Login() - self.firstname = firstname - self.lastname = lastname - self.password = password - self.agent_id = None - self.session_id = None - self.secure_session_id = None - self.name = self.Name() - self.active_group_powers = None - self.active_group_name = None - self.active_group_title = None - self.active_group_id = None - self.health = None - self._login_params = None - self.circuit_code = None - - # other storage containers - self.inventory_host = None - self.agent_access = None - self.udp_blacklist = None - self.home = None - self.inventory = None - self.start_location = None - self.group_manager = GroupManager(self, self.settings) - self.asset_manager = AssetManager(self, self.settings) - - # additional attributes - self.login_response = None - self.connected = False - self.grid_type = None - self.running = True - self.helpers = Helpers() - - # data we store as it comes in from the grid - self.Position = Vector3() # this will get updated later, but seed it with 000 - self.LookAt = Vector3() - self.ActiveGroupID = UUID() - - # populated via ObjectUpdates - self.FootCollisionPlane = Quaternion() - self.Velocity = Vector3() - self.Acceleration = Vector3() - self.Rotation = Vector3() - self.AngularVelocity = Vector3() - - # movement - self.state = AgentState.Null # typing, editing - self.control_flags = 0 - self.agent_update_flags = AgentUpdateFlags.Null - - - # should we include these here? - self.agentdomain = None # the agent domain the agent is connected to if an OGP context - self.child_regions = [] # all neighboring regions - self._pending_child_regions = [] # neighbor regions an agent may connect to - self.region = None # the host simulation for the agent - - # init AppearanceManager() - self.appearance = AppearanceManager(self, self.settings) - - # Cache of region name->handle; per-agent to prevent information leaks - self.region_name_map = {} - - # Cache of agent_id->(first_name, last_name); per agent to prevent info leaks - self.agent_id_map = {} - - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Initializing agent: %s' % (self)) - - def Name(self): - """ returns a concatenated firstname + ' ' + lastname""" - - return self.firstname + ' ' + self.lastname - - def login(self, loginuri, firstname=None, lastname=None, password=None, login_params = None, start_location=None, handler=None, connect_region = True): - """ login to a login endpoint using the Login() class """ - - if (re.search('auth.cgi$', loginuri)): - - self.grid_type = 'OGP' - - elif (re.search('login.cgi$', loginuri)): - - self.grid_type = 'Legacy' - - else: - log(WARNING, 'Unable to identify the loginuri schema. Stopping') - sys.exit(-1) - - if firstname != None: - self.firstname = firstname - if lastname != None: - self.lastname = lastname - if password != None: - self.password = password - - # handle either login params passed in, or, account info - if login_params == None: - - if (self.firstname == '') or (self.lastname == '') or (self.password == ''): - - raise LoginError('Unable to login an unknown agent.') - - else: - - self._login_params = self._get_login_params(self.firstname, self.lastname, self.password) - - else: - - self._login_params = login_params - - # login and parse the response - login = Login(settings = self.settings) - - self.login_response = login.login(loginuri, self._login_params, start_location, handler = handler) - self._parse_login_response() - - # ToDo: what to do with self.login_response['look_at']? - - if self.settings.MULTIPLE_SIM_CONNECTIONS: - api.spawn(self._monitor_for_new_regions) - - if connect_region: - self._enable_current_region() - - - def logout(self): - """ logs an agent out of the current region. calls Region()._kill_coroutines() for all child regions, and Region().logout() for the host region """ - - if not self.connected: - log(INFO, 'Agent is not logged into the grid. Stopping.') - sys.exit() - - self.running = False - - if self.region == None: - return - else: - - # kill udp and or event queue for child regions - [region.kill_coroutines() for region in self.child_regions] - - if self.region.logout(): - self.connected = False - - # zero out the password in case we dump it somewhere - self.password = '' - - def _get_login_params(self, firstname, lastname, password): - """ get the proper login parameters of the legacy or ogp enabled grid """ - - if self.grid_type == 'OGP': - - login_params = OGPLoginParams(firstname, lastname, password) - - elif self.grid_type == 'Legacy': - - login_params = LegacyLoginParams(firstname, lastname, password) - - return login_params - - def _parse_login_response(self): - """ evaluates the login response and propagates data to the Agent() attributes. enables InventoryManager() if settings dictate """ - - if self.grid_type == 'Legacy': - - self.firstname = re.sub(r'\"', '', self.login_response['first_name']) - self.lastname = self.login_response['last_name'] - self.agent_id = UUID(self.login_response['agent_id']) - self.session_id = UUID(self.login_response['session_id']) - self.secure_session_id = UUID(self.login_response['secure_session_id']) - - self.connected = bool(self.login_response['login']) - self.inventory_host = self.login_response['inventory_host'] - self.agent_access = self.login_response['agent_access'] - self.udp_blacklist = self.login_response['udp_blacklist'] - self.start_location = self.login_response['start_location'] - - if self.login_response.has_key('home'): - self.home = Home(self.login_response['home']) - - elif self.grid_type == 'OGP': - - pass - - def _enable_current_region(self, region_x = None, region_y = None, seed_capability = None, udp_blacklist = None, sim_ip = None, sim_port = None, circuit_code = None): - """ enables and connects udp and event queue for an agent's current region """ - - if self.login_response.has_key('circuit_code'): - self.circuit_code = self.login_response['circuit_code'] - - region_x = region_x or self.login_response['region_x'] - region_y = region_y or self.login_response['region_y'] - seed_capability = seed_capability or self.login_response['seed_capability'] - udp_blacklist = udp_blacklist or self.login_response['udp_blacklist'] - sim_ip = sim_ip or self.login_response['sim_ip'] - sim_port = sim_port or self.login_response['sim_port'] - circuit_code = circuit_code or self.login_response['circuit_code'] - - # enable the current region, setting connect = True - self.region = Region(region_x, region_y, seed_capability, udp_blacklist, sim_ip, sim_port, circuit_code, self, settings = self.settings, events_handler = self.events_handler) - - self.region.is_host_region = True - - # start the simulator udp and event queue connections - if self.settings.LOG_COROUTINE_SPAWNS: - log(INFO, "Spawning a coroutine for connecting to the agent's host region.") - - api.spawn(self.region.connect) - - self.enable_callbacks() - - - - def _enable_child_region(self, region_params): - """ enables a child region. eligible simulators are sent in EnableSimulator over the event queue, and routed through the packet handler """ - - # if this is the sim we are already connected to, skip it - if self.region.sim_ip == region_params['IP'] and self.region.sim_port == region_params['Port']: - #self.region.sendCompleteAgentMovement() - log(DEBUG, "Not enabling a region we are already connected to: %s" % (str(region_params['IP']) + ":" + str(region_params['Port']))) - return - - child_region = Region(circuit_code = self.circuit_code, - sim_ip = region_params['IP'], - sim_port = region_params['Port'], - handle = region_params['Handle'], - agent = self, - settings = self.settings, - events_handler = self.events_handler) - - self.child_regions.append(child_region) - - log(INFO, "Enabling a child region with ip:port of %s" % (str(region_params['IP']) + ":" + str(region_params['Port']))) - - if self.settings.LOG_COROUTINE_SPAWNS: - log(INFO, "Spawning a coroutine for connecting to a neighboring region.") - - api.spawn(child_region.connect_child) - - def _monitor_for_new_regions(self): - """ enable connections to neighboring regions found in the pending queue """ - - while self.running: - - if len(self._pending_child_regions) > 0: - - for region_params in self._pending_child_regions: - - self._enable_child_region(region_params) - self._pending_child_regions.remove(region_params) - - api.sleep(0) - - def _start_EQ_on_neighboring_region(self, message): - """ enables the event queue on an agent's neighboring region """ - - region = [region for region in self.child_regions if message.blocks['Message_Data'][0].get_variable('sim-ip-and-port').data == str(region.sim_ip) + ":" + str(region.sim_port)] - - if region != []: - - region[0]._set_seed_capability(message.blocks['Message_Data'][0].get_variable('seed-capability').data) - - region[0]._get_region_capabilities() - - log(DEBUG, 'Spawning neighboring region event queue connection') - region[0]._startEventQueue() - - def enable_callbacks(self): - """ enable the Agents() callback handlers for packet received events """ - # TODO oopify - - if self.settings.ENABLE_INVENTORY_MANAGEMENT: - while self.region.capabilities == {}: - - api.sleep(0) - - inventory_caps = ['FetchInventory', 'WebFetchInventoryDescendents', 'FetchLib', 'FetchLibDescendents'] - - if sets.Set(self.region.capabilities.keys()).intersection(inventory_caps): - - caps = dict([(capname, self.region.capabilities[capname]) for capname in inventory_caps]) - - log(INFO, "Using the capability based inventory management mechanism") - - self.inventory = AIS(self, caps) - - else: - - log(INFO, "Using the UDP based inventory management mechanism") - - self.inventory = UDP_Inventory(self) - - self.inventory._parse_folders_from_login_response() - self.inventory.enable_callbacks() - - if self.settings.ENABLE_APPEARANCE_MANAGEMENT: - self.appearance.enable_callbacks() - if self.settings.ENABLE_GROUP_CHAT: - self.group_manager.enable_callbacks() - if self.settings.MULTIPLE_SIM_CONNECTIONS: - - onEnableSimulator_received = self.region.message_handler.register('EnableSimulator') - onEnableSimulator_received.subscribe(self.onEnableSimulator) - - onEstablishAgentCommunication_received = self.region.message_handler.register('EstablishAgentCommunication') - onEstablishAgentCommunication_received.subscribe(self.onEstablishAgentCommunication) - - if self.settings.HANDLE_PACKETS: - - onAlertMessage_received = self.region.message_handler.register('AlertMessage') - onAlertMessage_received.subscribe(self.onAlertMessage) - - onAgentDataUpdate_received = self.region.message_handler.register('AgentDataUpdate') - onAgentDataUpdate_received.subscribe(self.onAgentDataUpdate) - - onAgentMovementComplete_received = self.region.message_handler.register('AgentMovementComplete') - onAgentMovementComplete_received.subscribe(self.onAgentMovementComplete) - - onHealthMessage_received = self.region.message_handler.register('HealthMessage') - onHealthMessage_received.subscribe(self.onHealthMessage) - - onImprovedInstantMessage_received = self.region.message_handler.register('ImprovedInstantMessage') - onImprovedInstantMessage_received.subscribe(self.onImprovedInstantMessage) - - self.region.message_handler.register('TeleportStart').subscribe(self.simple_callback('Info')) - self.region.message_handler.register('TeleportProgress').subscribe(self.simple_callback('Info')) - self.region.message_handler.register('TeleportFailed').subscribe(self.simple_callback('Info')) - self.region.message_handler.register('TeleportFinish').subscribe(self.onTeleportFinish) - - self.region.message_handler.register('OfflineNotification').subscribe(self.simple_callback('AgentBlock')) - self.region.message_handler.register('OnlineNotification').subscribe(self.simple_callback('AgentBlock')) - - self.region.message_handler.register('MoneyBalanceReply').subscribe(self.simple_callback('MoneyData')) - self.region.message_handler.register('RoutedMoneyBalanceReply').subscribe(self.simple_callback('MoneyData')) - - if self.settings.ENABLE_COMMUNICATIONS_TRACKING: - onChatFromSimulator_received = self.region.message_handler.register('ChatFromSimulator') - onChatFromSimulator_received.subscribe(self.onChatFromSimulator) - - - def simple_callback(self, blockname): - """Generic callback creator for single-block packets.""" - - def repack(packet, blockname): - """Repack a single block packet into an AppEvent""" - payload = {} - block = packet.blocks[blockname][0] - for var in block.var_list: - payload[var] = block.get_variable(var).data - - return AppEvent(packet.name, payload=payload) - - return lambda p: self.events_handler.handle(repack(p, blockname)) - - - def send_AgentDataUpdateRequest(self): - """ queues a packet requesting an agent data update """ - - packet = Message('AgentDataUpdateRequest', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id)) - - self.region.enqueue_message(packet) - - # ~~~~~~~~~~~~~~ - # Communications - # ~~~~~~~~~~~~~~ - - # Chat - - def say(self, - _Message, - Type = 1, - Channel = 0): - """ queues a packet to send open chat via ChatFromViewer - - Channel: 0 is open chat - Type: 0 = Whisper - 1 = Say - 2 = Shout - """ - - packet = Message('ChatFromViewer', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id), - Block('ChatData', - Message = _Message, - Type = Type, - Channel = Channel)) - - self.region.enqueue_message(packet) - - # Instant Message (im, group chat) - - def instant_message(self, - ToAgentID = None, - _Message = None, - _ID = None): - """ sends an instant message to another avatar, wrapping Agent().send_ImprovedInstantMessage() with some handy defaults """ - - if ToAgentID != None and _Message != None: - - if _ID == None: - _ID = self.agent_id - - _AgentID = self.agent_id - _SessionID = self.session_id - _FromGroup = False - _ToAgentID = UUID(str(ToAgentID)) - _ParentEstateID = 0 - _RegionID = UUID() - _Position = self.Position - _Offline = 0 - _Dialog = ImprovedIMDialogue.FromAgent - _ID = _ID - _Timestamp = 0 - _FromAgentName = self.firstname + ' ' + self.lastname - _Message = _Message - _BinaryBucket = '' - - self.send_ImprovedInstantMessage(_AgentID, - _SessionID, - _FromGroup, - _ToAgentID, - _ParentEstateID, - _RegionID, - _Position, - _Offline, - _Dialog, - _ID, - _Timestamp, - _FromAgentName, - _Message, - _BinaryBucket) - - else: - - log(INFO, "Please specify an agentid and message to send in agent.instant_message") - - def send_ImprovedInstantMessage(self, - AgentID = None, - SessionID = None, - FromGroup = None, - ToAgentID = None, - ParentEstateID = None, - RegionID = None, - Position = None, - Offline = None, - Dialog = None, - _ID = None, - Timestamp = None, - FromAgentName = None, - _Message = None, - BinaryBucket = None): - """ sends an instant message packet to ToAgentID. this is a multi-purpose message for inventory offer handling, im, group chat, and more """ - - packet = Message('ImprovedInstantMessage', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - Block('MessageBlock', - FromGroup = FromGroup, - ToAgentID = ToAgentID, - ParentEstateID = ParentEstateID, - RegionID = RegionID, - Position = Position, - Offline = Offline, - Dialog = Dialog, - ID = UUID(str(_ID)), - Timestamp = Timestamp, - FromAgentName = FromAgentName, - Message = _Message, - BinaryBucket = BinaryBucket)) - - self.region.enqueue_message(packet, True) - - def send_RetrieveInstantMessages(self): - """ asks simulator for instant messages stored while agent was offline """ - - packet = Message('RetrieveInstantMessages', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id)) - - self.region.enqueue_message(packet()) - - - def sigint_handler(self, signal_sent, frame): - """ catches terminal signals (Ctrl-C) to kill running client instances """ - - log(INFO, "Caught signal... %d. Stopping" % signal_sent) - self.logout() - - def __repr__(self): - """ returns a representation of the agent """ - - if self.firstname == None: - return 'A new agent instance' - else: - return self.Name() - - def onAgentDataUpdate(self, packet): - """ callback handler for received AgentDataUpdate messages which populates various Agent() attributes """ - - if self.agent_id == None: - self.agent_id = packet.blocks['AgentData'][0].get_variable('AgentID').data - - if self.firstname == None: - self.firstname = packet.blocks['AgentData'][0].get_variable('FirstName').data - - if self.lastname == None: - self.firstname = packet.blocks['AgentData'][0].get_variable('LastName').data - - self.active_group_title = packet.blocks['AgentData'][0].get_variable('GroupTitle').data - - self.active_group_id = packet.blocks['AgentData'][0].get_variable('ActiveGroupID').data - - self.active_group_powers = packet.blocks['AgentData'][0].get_variable('GroupPowers').data - - self.active_group_name = packet.blocks['AgentData'][0].get_variable('GroupName').data - - def onAgentMovementComplete(self, packet): - """ callback handler for received AgentMovementComplete messages which populates various Agent() and Region() attributes """ - - self.Position = packet.blocks['Data'][0].get_variable('Position').data - if self.Position == None: - log(WARNING, "agent.position is None agent.py") - self.LookAt = packet.blocks['Data'][0].get_variable('LookAt').data - - self.region.RegionHandle = packet.blocks['Data'][0].get_variable('RegionHandle').data - - #agent.Timestamp = packet.blocks['Data'][0].get_variable('Timestamp') - - self.region.ChannelVersion = packet.blocks['SimData'][0].get_variable('ChannelVersion').data - - # Raise a plain-vanilla AppEvent - self.simple_callback('Data')(packet) - - def sendDynamicsUpdate(self): - """Called when an ObjectUpdate is received for the agent; raises - an app event.""" - payload = {} - payload['Position'] = self.Position - payload['FootCollisionPlane'] = self.FootCollisionPlane - payload['Velocity'] = self.Velocity - payload['Acceleration'] = self.Acceleration - payload['Rotation'] = self.Rotation - payload['AngularVelocity'] = self.AngularVelocity - self.events_handler.handle(AppEvent("AgentDynamicsUpdate", payload=payload)) - - def onHealthMessage(self, packet): - """ callback handler for received HealthMessage messages which populates Agent().health """ - - self.health = packet.blocks['HealthData'][0].get_variable('Health').data - - def onAgentGroupDataUpdate(self, packet): - """ callback handler for received AgentGroupDataUpdate messages which updates stored group instances in the group_manager """ - - # AgentData block - # AgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data - - # GroupData block - for GroupData_block in packet.blocks['GroupData']: - - AcceptNotices = GroupData_block.get_variable('AcceptNotices').data - GroupPowers = GroupData_block.get_variable('GroupPowers').data - GroupID = GroupData_block.get_variable('GroupID').data - GroupName = GroupData_block.get_variable('GroupName').data - ListInProfile = GroupData_block.get_variable('ListInProfile').data - Contribution = GroupData_block.get_variable('Contribution').data - GroupInsigniaID = GroupData_block.get_variable('GroupInsigniaID').data - - # make sense of group powers - GroupPowers = [ord(x) for x in GroupPowers] - GroupPowers = ''.join([str(x) for x in GroupPowers]) - - group = Group(AcceptNotices, GroupPowers, GroupID, GroupName, ListInProfile, Contribution, GroupInsigniaID ) - - self.group_manager.store_group(group) - - def onChatFromSimulator(self, packet): - """ callback handler for received ChatFromSimulator messages which parses and fires a ChatReceived event. """ - - FromName = packet.blocks['ChatData'][0].get_variable('FromName').data - SourceID = packet.blocks['ChatData'][0].get_variable('SourceID').data - OwnerID = packet.blocks['ChatData'][0].get_variable('OwnerID').data - SourceType = packet.blocks['ChatData'][0].get_variable('SourceType').data - ChatType = packet.blocks['ChatData'][0].get_variable('ChatType').data - Audible = packet.blocks['ChatData'][0].get_variable('Audible').data - Position = packet.blocks['ChatData'][0].get_variable('Position').data - _Message = packet.blocks['ChatData'][0].get_variable('Message').data - - message = AppEvent('ChatReceived', - FromName = FromName, - SourceID = SourceID, - OwnerID = OwnerID, - SourceType = SourceType, - ChatType = ChatType, - Audible = Audible, - Position = Position, - Message = _Message) - - log(INFO, "Received chat from %s: %s" % (FromName, _Message)) - - self.events_handler.handle(message) - - def onImprovedInstantMessage(self, packet): - """ callback handler for received ImprovedInstantMessage messages. much is passed in this message, and handling the data is only partially implemented """ - - Dialog = packet.blocks['MessageBlock'][0].get_variable('Dialog').data - FromAgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data - - if Dialog == ImprovedIMDialogue.InventoryOffered: - - self.inventory.handle_inventory_offer(packet) - - elif Dialog == ImprovedIMDialogue.InventoryAccepted: - - if str(FromAgentID) != str(self.agent_id): - - FromAgentName = packet.blocks['MessageBlock'][0].get_variable('FromAgentName').data - InventoryName = packet.blocks['MessageBlock'][0].get_variable('Message').data - - log(INFO, "Agent %s accepted the inventory offer." % (FromAgentName)) - - elif Dialog == ImprovedIMDialogue.InventoryDeclined: - - if str(FromAgentID) != str(self.agent_id): - - FromAgentName = packet.blocks['MessageBlock'][0].get_variable('FromAgentName').data - InventoryName = packet.blocks['MessageBlock'][0].get_variable('Message').data - - log(INFO, "Agent %s declined the inventory offer." % (FromAgentName)) - - elif Dialog == ImprovedIMDialogue.FromAgent: - - RegionID = packet.blocks['MessageBlock'][0].get_variable('RegionID').data - Position = packet.blocks['MessageBlock'][0].get_variable('Position').data - ID = packet.blocks['MessageBlock'][0].get_variable('ID').data - FromAgentName = packet.blocks['MessageBlock'][0].get_variable('FromAgentName').data - _Message = packet.blocks['MessageBlock'][0].get_variable('Message').data - - message = AppEvent('InstantMessageReceived', FromAgentID = FromAgentID, RegionID = RegionID, Position = Position, ID = ID, FromAgentName = FromAgentName, Message = _Message) - - log(INFO, "Received instant message from %s: %s" % (FromAgentName, _Message)) - - self.events_handler.handle(message) - - else: - - self.helpers.log_packet(packet, self) - - def onAlertMessage(self, packet): - """ callback handler for received AlertMessage messages. logs and raises an event """ - - AlertMessage = packet.blocks['AlertData'][0].get_variable('Message').data - - message = AppEvent('AlertMessage', AlertMessage = AlertMessage) - - log(WARNING, "AlertMessage from simulator: %s" % (AlertMessage)) - - self.events_handler.handle(message) - - def onEnableSimulator(self, packet): - """ callback handler for received EnableSimulator messages. stores the region data for later connections """ - - IP = [ord(x) for x in packet.blocks['SimulatorInfo'][0].get_variable('IP').data] - IP = '.'.join([str(x) for x in IP]) - - Port = packet.blocks['SimulatorInfo'][0].get_variable('Port').data - - # not sure what this is, but pass it up - Handle = [ord(x) for x in packet.blocks['SimulatorInfo'][0].get_variable('Handle').data] - - region_params = {'IP': IP, 'Port': Port, 'Handle': Handle} - - log(INFO, 'Received EnableSimulator for %s' % (str(IP) + ":" + str(Port))) - - # are we already prepping to connect to the sim? - if region_params not in self._pending_child_regions: - - # are we already connected to the sim? - known_region = False - - # don't append to the list if we already know about this region - for region in self.child_regions: - if region.sim_ip == region_params['IP'] and region.sim_port == region_params['Port']: - known_region = True - - #agent._enable_child_region(IP, Port, Handle) - if not known_region: - self._pending_child_regions.append(region_params) - - def onEstablishAgentCommunication(self, message): - """ callback handler for received EstablishAgentCommunication messages. try to enable the event queue for a neighboring region based on the data received """ - - log(INFO, 'Received EstablishAgentCommunication for %s' % (message.blocks['Message_Data'][0].get_variable('sim-ip-and-port').data)) - - is_running = False - - # don't enable the event queue when we already have it running - for region in self.child_regions: - if (str(region.sim_ip) + ":" + str(region.sim_port) == message.blocks['Message_Data'][0].get_variable('sim-ip-and-port').data) and region.event_queue != None: - if region.event_queue._running: - is_running = True - - # start the event queue - if not is_running: - self._start_EQ_on_neighboring_region(message) - - - def teleport(self, - region_name=None, - region_handle=None, - region_id=None, - position=Vector3(X=128, Y=128, Z=128), - look_at=Vector3(X=128, Y=128, Z=128)): - """Initiate a teleport to the specified location. When passing a region name - it may be necessary to request the destination region handle from the current sim - before the teleport can start.""" - - log(INFO, 'teleport name=%s handle=%s id=%s', str(region_name), str(region_handle), str(region_id)) - - # Handle intra-region teleports even by name - if not region_id and region_name and region_name.lower() == self.region.SimName.lower(): - region_id = self.region.RegionID - - if not region_id and not region_handle and region_name and \ - region_name.lower() in self.region_name_map: - region_handle = self.region_name_map[region_name.lower()] - - if region_id: - - log(INFO, 'sending TP request packet') - - packet = Message('TeleportRequest', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id), - Block('Info', - RegionID = region_id, - Position = position, - LookAt = look_at)) - - self.region.enqueue_message(packet) - - elif region_handle: - - log(INFO, 'sending TP location request packet') - - packet = Message('TeleportLocationRequest', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id), - Block('Info', - RegionHandle = region_handle, - Position = position, - LookAt = look_at)) - - self.region.enqueue_message(packet) - - else: - log(INFO, "Target region's handle not known, sending map name request") - # do a region_name to region_id lookup and then request the teleport - self.send_MapNameRequest( - region_name, - lambda handle: self.teleport(region_handle=handle, position=position, look_at=look_at)) - - - def send_MapNameRequest(self, region_name, callback): - """ sends a MapNameRequest message to the host simulator """ - - handler = self.region.message_handler.register('MapBlockReply') - - def onMapBlockReplyPacket(packet): - """ handles the MapBlockReply message from a simulator """ - - log(INFO, 'MapBlockReplyPacket received') - - for block in packet.blocks['Data']: - - if block.get_variable('Name').data.lower() == region_name.lower(): - - handler.unsubscribe(onMapBlockReplyPacket) - - x = block.get_variable('X').data - y = block.get_variable('Y').data - region_handle = Region.xy_to_handle(x, y) - - if region_handle: - self.region_name_map[region_name.lower()] = region_handle - callback(region_handle) - else: - # *TODO: May get a region_handle of 0 if region - # is offline/unknown. Should callback handle it? - log(WARNING, 'Got null region_handle for %s', region_name) - return - - # Leave it registered, as the event may come later - - # Register a handler for the response - handler.subscribe(onMapBlockReplyPacket) - - # ...and make the request - log(INFO, 'sending MapNameRequestPacket') - - packet = Message('MapNameRequest', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id, - Flags = 0, - EstateID = 0, - Godlike = False), - Block('NameData', - Name = region_name.lower())) - - self.region.enqueue_message(packet) - - def onTeleportFinish(self, packet): - """Handle the end of a successful teleport""" - - log(INFO, "Teleport finished, taking care of details...") - - # Raise a plain-vanilla AppEvent for the Info block - self.simple_callback('Info')(packet) - - # packed binary U64 to integral x, y - region_handle = packet.blocks['Info'][0].get_variable('RegionHandle').data - region_x, region_y = Region.handle_to_xy(region_handle) - - # packed binary to dotted-octet - sim_ip = packet.blocks['Info'][0].get_variable('SimIP').data - sim_ip = '.'.join(map(str, struct.unpack('BBBB', sim_ip))) - - # *TODO: Make this more graceful - log(INFO, "Disconnecting from old region") - [region.kill_coroutines() for region in self.child_regions] - self.region.kill_coroutines() - - self.region = None - self.child_regions = [] - self._pending_child_regions = [] - - log(INFO, "Enabling new region") - self._enable_current_region( - region_x = region_x, - region_y = region_y, - seed_capability = packet.blocks['Info'][0].get_variable('SeedCapability').data, - sim_ip = sim_ip, - sim_port = packet.blocks['Info'][0].get_variable('SimPort').data - ) - - def request_agent_names(self, agent_ids, callback): - """Request agent names. When all names are known, callback - will be called with a list of tuples (agent_id, first_name, - last_name). If all names are known, callback will be called - immediately.""" - - def _fire_callback(_): - cbdata = [(agent_id, - self.agent_id_map[agent_id][0], - self.agent_id_map[agent_id][1]) - for agent_id in agent_ids] - callback(cbdata) - - names_to_request = [ agent_id - for agent_id in agent_ids - if agent_id not in self.agent_id_map ] - if names_to_request: - self.send_UUIDNameRequest(names_to_request, _fire_callback) - else: - _fire_callback([]) - - - def send_UUIDNameRequest(self, agent_ids, callback): - """ sends a UUIDNameRequest message to the host simulator """ - - handler = self.region.message_handler.register('UUIDNameReply') - - def onUUIDNameReply(packet): - """ handles the UUIDNameReply message from a simulator """ - log(INFO, 'UUIDNameReplyPacket received') - - cbdata = [] - for block in packet.blocks['UUIDNameBlock']: - agent_id = str(block.get_variable('ID').data) - first_name = block.get_variable('FirstName').data - last_name = block.get_variable('LastName').data - self.agent_id_map[agent_id] = (first_name, last_name) - cbdata.append((agent_id, first_name, last_name)) - - # Fire the callback only when all names are received - missing = [ agent_id - for agent_id in agent_ids - if agent_id not in self.agent_id_map ] - if not missing: - handler.unsubscribe(onUUIDNameReply) - callback(cbdata) - else: - log(INFO, 'Still waiting on %d names in send_UUIDNameRequest', len(missing)) - - handler.subscribe(onUUIDNameReply) - - log(INFO, 'sending UUIDNameRequest') - - packet = Message('UUIDNameRequest', - [Block('UUIDNameBlock', ID = UUID(agent_id)) for agent_id in agent_ids]) - - self.region.enqueue_message(packet) - - # *TODO: Should use this instead, but somehow it fails ?!? - #self.region.sendUUIDNameRequest(agent_ids=agent_ids) - - def request_balance(self, callback): - """Request the current agent balance.""" - handler = self.region.message_handler.register('MoneyBalanceReply') - - def onMoneyBalanceReply(packet): - """ handles the MoneyBalanceReply message from a simulator """ - log(INFO, 'MoneyBalanceReply received') - handler.unsubscribe(onMoneyBalanceReply) # One-shot handler - balance = packet.blocks['MoneyData'][0].get_variable('MoneyBalance').data - callback(balance) - - handler.subscribe(onMoneyBalanceReply) - - log(INFO, 'sending MoneyBalanceRequest') - - packet = Message('MoneyBalanceRequest', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id), - Block('MoneyData', - TransactionID = UUID())) - - self.region.enqueue_message(packet) - - def give_money(self, target_id, amount, - description='', - transaction_type=MoneyTransactionType.Gift, - flags=TransactionFlags.Null): - """Give money to another agent""" - - log(INFO, 'sending MoneyTransferRequest') - - packet = Message('MoneyTransferRequest', - Block('AgentData', - AgentID = self.agent_id, - SessionID = self.session_id), - Block('MoneyData', - SourceID = self.agent_id, - DestID = UUID(target_id), - Flags = flags, - Amount = amount, - AggregatePermNextOwner = 0, - AggregatePermInventory = 0, - TransactionType = transaction_type, - Description = description)) - - self.region.enqueue_message(packet) - - def sit_on_ground(self): - """Sit on the ground at the agent's current location""" - - self.control_flags |= AgentControlFlags.SitOnGround - self._send_update() - - def stand(self): - """Stand up from sitting""" - - # Start standing... - self.control_flags &= ~AgentControlFlags.SitOnGround - self.control_flags |= AgentControlFlags.StandUp - self._send_update() - - # And finish standing - self.control_flags &= ~AgentControlFlags.StandUp - self._send_update() - - - def fly(self, flying=True): - """Start or stop flying""" - - if flying: - self.control_flags |= AgentControlFlags.Fly - else: - self.control_flags &= ~AgentControlFlags.Fly - - self._send_update() - - def _send_update(self): - """ force a send of an AgentUpdate message to the host simulator """ - - log(INFO, 'sending AgentUpdate') - - self.region.sendAgentUpdate(self.agent_id, self.session_id, - State=self.state, - ControlFlags=self.control_flags, - Flags=self.agent_update_flags, - - CameraAtAxis = self.settings.DEFAULT_CAMERA_AT_AXIS, - CameraLeftAxis = self.settings.DEFAULT_CAMERA_LEFT_AXIS, - CameraUpAxis = self.settings.DEFAULT_CAMERA_UP_AXIS, - Far = self.settings.DEFAULT_CAMERA_DRAW_DISTANCE - ) - - -class Home(object): - """ contains the parameters describing an agent's home location as returned in login_response['home'] """ - - def __init__(self, params): - """ initialize the Home object by parsing the data passed in """ - - # eval(params) would be nice, but fails to parse the string the way one thinks it might - items = params.split(', \'') - - # this creates: - # self.region_handle - # self.look_at - # self.position - for i in items: - i = re.sub(r'[\"\{}\'"]', '', i) - i = i.split(':') - setattr(self, i[0], eval(re.sub('r', '', i[1]))) - - self.global_x = self.region_handle[0] - self.global_y = self.region_handle[1] - - # convert the position and lookat to a Vector3 instance - self.look_at = Vector3(X=self.look_at[0], Y=self.look_at[1], Z=self.look_at[2]) - self.position = Vector3(X=self.position[0], Y=self.position[1], Z=self.position[2]) - - diff --git a/pyogp/lib/base/agentdomain.py b/pyogp/lib/base/agentdomain.py deleted file mode 100644 index 3f89c8a..0000000 --- a/pyogp/lib/base/agentdomain.py +++ /dev/null @@ -1,174 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# std lib -import urllib2 -from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG - -# related -from indra.base import llsd - -# pyogp -from pyogp.lib.base.network.stdlib_client import StdLibClient, HTTPError -from pyogp.lib.base.caps import SeedCapability -import pyogp.lib.base.exc -from pyogp.lib.base.settings import Settings - -# initialize logging -logger = getLogger('pyogp.lib.base.agentdomain') -log = logger.log - -class AgentDomain(object): - """an agent domain endpoint""" - - def __init__(self, uri, restclient = None): - """ initialize the agent domain endpoint """ - - if restclient == None: - self.restclient = StdLibClient() - else: - self.restclient = restclient - - - self.settings = Settings() - self.login_uri = uri - self.credentials = None - - self.connectedStatus = False - - self.capabilities = {} - self.agentdomain_caps_list = ['rez_avatar/place'] - self._isEventQueueRunning = False - - self.seed_cap = None - log(DEBUG, 'initializing agent domain: %s' %self) - - def login(self, credentials): - """ login to the agent domain """ - - response = self.post_to_loginuri(credentials) - - self.eval_login_response(response) - - def post_to_loginuri(self, credentials): - """ post to login_uri and return response """ - - self.credentials = credentials - log(INFO, 'Logging in to %s as %s %s' % (self.login_uri, self.credentials.firstname, self.credentials.lastname)) - - payload = credentials.serialize() - content_type = credentials.content_type - headers = {'Content-Type': content_type} - - # now create the request. We assume for now that self.uri is the login uri - # TODO: make this pluggable so we can use other transports like eventlet in the future - # TODO: add logging and error handling - - try: - response = self.restclient.POST(self.login_uri, payload, headers=headers) - except HTTPError, error: - if error.code==404: - raise ResourceNotFound(self.login_uri) - else: - raise ResourceError(self.login_uri, error.code, error.msg, error.fp.read(), method="POST") - - return response - - def eval_login_response(self, response): - """ parse the login uri response """ - - seed_cap_url_data = self.parse_login_response(response) - try: - seed_cap_url = seed_cap_url_data['agent_seed_capability'] - self.seed_cap = SeedCapability('seed_cap', seed_cap_url, self.restclient) - self.connectedStatus = True - log(INFO, 'logged in to %s' % (self.login_uri)) - except KeyError: - raise UserNotAuthorized(self.credentials) - - def parse_login_response(self, response): - """ parse the login uri response and returns deserialized data """ - - data = llsd.parse(response.body) - - log(DEBUG, 'deserialized login response body = %s' % (data)) - - return data - - def place_avatar(self, region_uri, position=[117,73,21]): - """ handles the rez_avatar/place cap on the agent domain, populates some initial region attributes """ - - # wow, this needs some thought... place avatar should really move to the region domain... - - if not self.capabilities.has_key('rez_avatar/place'): - self.capabilities['rez_avatar/place'] = self.seed_cap.get(['rez_avatar/place'])['rez_avatar/place'] - - payload = {'public_region_seed_capability' : region_uri, 'position':position} - result = self.capabilities['rez_avatar/place'].POST(payload) - - if result['region_seed_capability'] is None: - raise UserRezFailed(region) - else: - log(INFO, 'Region_uri %s returned a seed_cap of %s' % (region_uri, result['region_seed_capability'])) - - log(DEBUG, 'Full rez_avatar/place response is: %s' % (result)) - - return result - - def get_agentdomain_capabilities(self): - """ queries the region seed cap for capabilities """ - - if (self.seed_cap == None): - raise RegionSeedCapNotAvailable("querying for agents's agent domain capabilities") - # well then get it - # return something? - else: - - log(INFO, 'Getting caps from agent domain seed cap %s' % (self.seed_cap)) - - # use self.region_caps.keys() to pass a list to be parsed into LLSD - self.capabilities = self.seed_cap.get(self.agentdomain_caps_list) - - def _processEventQueue(self): - - self._isEventQueueRunning = True - - - if self.capabilities['event_queue'] == None: - raise RegionCapNotAvailable('event_queue') - # change the exception here (add a new one) - else: - while self._isEventQueueRunning: - - # need to be able to pull data from a queue somewhere - data = {} - api.sleep(self.settings.agentdomain_event_queue_interval) - - #if self.last_id != -1: - #data = {'ack':self.last_id, 'done':False} - - result = self.capabilities['event_queue'].POST(data) - - self.last_id = result['id'] - - #log(DEBUG, 'region event queue cap called, returned id: %s' % (self.last_id)) - - log(DEBUG, 'AgentDomain EventQueueGet result: %s' % (result)) - - - diff --git a/pyogp/lib/base/agentmanager.py b/pyogp/lib/base/agentmanager.py deleted file mode 100644 index b0882b2..0000000 --- a/pyogp/lib/base/agentmanager.py +++ /dev/null @@ -1,177 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libraries -from logging import getLogger, ERROR, WARNING, INFO, DEBUG -import signal -import sys - -# related -from eventlet import api - -# pyogp -from pyogp.lib.base.datatypes import UUID -from pyogp.lib.base.exc import LoginError -from pyogp.lib.base.utilities.helpers import Wait - -# initialize logging -logger = getLogger('pyogp.lib.base.agentmanager') -log = logger.log - -class AgentManager(object): - """ a simple class to track multiple agents - - This class can perhaps begin to manage sessions in time. - """ - - def __init__(self, settings = None): - """ initialize the agent 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() - - # store the agents in a dictionary keyed by a uuid - # this could be agent_id, if we know it ahead of time - # or, create a new one - self.agents = {} - - # signal handler to capture erm signals - self.signal_handler = signal.signal(signal.SIGINT, self.sigint_handler) - - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Initializing agent manager for %s agents' % (len(self.agents))) - - def initialize(self, agents): - """ accept a list of Agent() instances, and store them in the agents attribute """ - - if type(agents) != list: - log(WARNING, "The AgentManager requires a list of Agent instances to initialize. Stopping.") - return False - - for agent in agents: - - self.store_agent(agent) - - def store_agent(self, agent): - """ adds an agent to the store """ - - if str(type(agent)) != '': - log(WARNING, "The AgentManager stores only Agent instances to initialize. Stopping.") - return False - - # check if the agent is on the store - if self._is_stored(agent) == False: - - if agent.agent_id == None: - key = UUID().random() - else: - key = agent.agent_id - - self.agents[key] = agent - - log(INFO, "Stored agent %s with a key of %s" % (agent.name, key)) - - else: - - if agent.firstname != None and agent.lastname != None: - uniq = ' named ' + agent.Name() - - log(WARNING, "The AgentManager is already storing an agent%s. Stopping." % (uniq)) - - def _is_stored(self, agent): - """ returns True if an agent is already stored, False if not """ - - if agent.Name() in self._stored_names(): - return True - elif agent.agent_id in self._stored_agent_ids(): - return True - else: - return False - - def _stored_names(self): - """ returns the stored agent's names in a list """ - - names = [] - - for key in self.agents: - names.append(self.agents[key].Name()) - - return names - - def _stored_agent_ids(self): - """ returns the stored agent's names in a list """ - - ids = [] - - for key in self.agents: - if self.agents[key].agent_id != None: - ids.append(self.agents[key].agent_id) - - return ids - - def login(self, key, loginuri, start_location): - """ spawns a new agent via an eventlet coroutine """ - - if self.settings.LOG_COROUTINE_SPAWNS: - log(INFO, "Spawning a coroutine for agent login for %s." % (self.agents[key].Name())) - - try: - api.spawn(self.agents[key].login, loginuri = loginuri, start_location = start_location) - except LoginError, error: - log(ERROR, "Skipping agent with failed login: %s due to %s." % (self.agents[key].Name(), error)) - - def has_agents_running(self): - """ returns true if there is a client who's running value = True """ - - for key in self.agents: - if self.agents[key].running: - return True - - return False - - def get_active_agents(self): - """ returns a list of agents that are connected to a grid """ - - active = [] - - for key in self.agents: - active.append(self.agents[key]) - - return active - - def sigint_handler(self, sigint, frame): - """ handles signals from the command line (and others) and logs out all agents """ - - log(INFO, "Caught signal... %d. Stopping" % sigint) - - for agent in self.get_active_agents(): - agent.logout() - - Wait(10) - - if self.has_agents_running(): - log(WARNING, "These agents have not yet shut down. Killing the process hard.\n\t\t%s" % (self.get_active_agents())) - sys.exit(1) - - - diff --git a/pyogp/lib/base/appearance.py b/pyogp/lib/base/appearance.py deleted file mode 100644 index fa5fb24..0000000 --- a/pyogp/lib/base/appearance.py +++ /dev/null @@ -1,350 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG -import uuid - -#related -from eventlet import api - -# pyogp -from pyogp.lib.base.datamanager import DataManager -# pyogp messaging -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.objects import Object -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 - -# initialize logging -logger = getLogger('pyogp.lib.base.appearance') -log = logger.log - -class AppearanceManager(DataManager): - """The AppearanceManager class handles appearance of an Agent() instance - - Sample implementations: - Tests: - """ - - def __init__(self, agent, settings = None): - """ - initialize the appearance manager - TODO Fix the Z by generating actual height - """ - super(AppearanceManager, self).__init__(agent, settings) - self.AgentSetSerialNum = 1 - self.AgentCachedSerialNum = 1 - self.wearables = [] #indexed by WearableType - for i in range(TextureIndex.TEX_COUNT): - self.wearables.append(Wearable(i)) - self.helpers = Helpers() - self.bakedTextures = {} #indexed by TextureIndex - for i in range(BakedIndex.BAKED_COUNT): - 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 - """ - onAgentWearablesUpdate_received = self.agent.region.message_handler.register('AgentWearablesUpdate') - onAgentWearablesUpdate_received.subscribe(self.onAgentWearablesUpdate) - onAgentCachedTextureResponse_received = self.agent.region.message_handler.register('AgentCachedTextureResponse') - onAgentCachedTextureResponse_received.subscribe(self.onAgentCachedTextureResponse) - #onAvatarAppearance_received = self.agent.region.message_handler.register('AvatarAppearance') - #onAvatarAppearance_received.subscribe(self.onAvatarAppearance) - self.request_agent_wearables() - ''' - 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 - - self.send_AgentWearablesRequest(self.agent.agent_id, - self.agent.session_id) - - def wearableArrived(self, assetID, isSuccess): - """ - callback for wearables request - """ - - - 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 - the AgentWearables update packet. - This message should only be received once. - - Error Checking: make sure agent and session id are correct - make sure this method is only called once. - - """ - - #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)) - - def onAvatarTextureUpdate(self, packet): - raise NotImplemented("onAvatarTextureUpdate") - - def onAvatarAppearance(self, packet): - """ - Informs viewer how other avatars look - """ - raise NotImplemented("onAvatarAppearance") - - def onAgentCachedTextureResponse(self, packet): - """ - Update the bakedTextures with their TextureIDs and HostNames and call - send_AgentSetAppearance - """ - #log(INFO, "AgentCachedTextureRespose received: %s" % packet) - for bakedTexture in packet.blocks['WearableData']: - 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.agent.agent_id, - self.agent.session_id, - self.Size, - self.bakedTextures, - self.TextureEntry, - self.visualParams) - - def verifyAgentData(self, packet): - """ - verifies that packet refers to this agent - """ - pAgentID = packet.blocks['AgentData'][0].get_variable("AgentID").data - 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 - """ - def __init__(self, WearableType = None, ItemID = UUID(), AssetID = UUID()): - self.WearableType = WearableType - 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): - """ - Represents 1 of the 6 baked textures of an avatar - """ - def __init__(self, bakedIndex, TextureID = UUID()): - self.bakedIndex = bakedIndex - self.TextureID = TextureID - self.HostName = None - - if bakedIndex == BakedIndex.BAKED_HEAD: - self.secret_hash = UUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6") - elif bakedIndex == BakedIndex.BAKED_UPPER: - self.secret_hash = UUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f") - elif bakedIndex == BakedIndex.BAKED_LOWER: - self.secret_hash = UUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f") - elif bakedIndex == BakedIndex.BAKED_EYES: - self.secret_hash = UUID("b2cf28af-b840-1071-3c6a-78085d8128b5") - elif bakedIndex == BakedIndex.BAKED_SKIRT: - self.secret_hash = UUID("ea800387-ea1a-14e0-56cb-24f2022f969a") - elif bakedIndex == BakedIndex.BAKED_HAIR: - self.secret_hash = UUID("0af1ef7c-ad24-11dd-8790-001f5bf833e8") - else: - 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): - """ - Represents 1 of the 21 baked and not baked textures of an avatar. - """ - def __init__(self, TextureIndex, TextureID = None): - self.TextureIndex = TextureIndex - self.TextureID = TextureID - - - - - diff --git a/pyogp/lib/base/assets.py b/pyogp/lib/base/assets.py deleted file mode 100644 index 97b975c..0000000 --- a/pyogp/lib/base/assets.py +++ /dev/null @@ -1,264 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG -import uuid - -#related -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.message import Message, Block -from pyogp.lib.base.utilities.helpers import Helpers -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 -logger = getLogger('pyogp.lib.base.assets') -log = logger.log - -class AssetManager(DataManager): - """ - The AssetManager class handles the assets of an Agent() instance - - Sample implementations: - Tests: test_assets.py - """ - - def __init__(self, agent, settings = None): - super(AssetManager, self).__init__(agent, settings) - #indexed by assetID - self.assets = {} - - def enable_callbacks(self): - pass - - - 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 - if request failed. On successful request the asset is store in - self.assets - """ - transferID = UUID() #associate the assetID with the transferID - transferID.random() - transferInfoHandler = self.agent.region.message_handler.register('TransferInfo') - transferPacketHandler = self.agent.region.message_handler.register('TransferPacket') - - 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) - 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)) - if callback != None: - callback(assetID, False) - transferPacketHandler.unsubscribe(onTransferPacket) - transferInfoHandler.unsubscribe(onTransferInfo) - - transferInfoHandler.subscribe(onTransferInfo) - transferPacketHandler.subscribe(onTransferPacket) - - if isPriority: - priority = 1.0 - else: - priortity = 0.0 - - 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, - TransferSourceType.Asset, - 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): - """ - 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, data): - self.assetID = assetID - 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()) - paramVal = i.next() - #TODO Verify this is correct behavior this fix may be a hack - if paramVal == '.': - self.params[paramID] = 0.0 - else: - self.params[paramID] = float(paramVal) - 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 - - diff --git a/pyogp/lib/base/event_system.py b/pyogp/lib/base/event_system.py deleted file mode 100644 index 0881d5b..0000000 --- a/pyogp/lib/base/event_system.py +++ /dev/null @@ -1,220 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG -import time - -# related -from eventlet import api - -# pyogp -from pyogp.lib.base.utilities.events import Event -from pyogp.lib.base.settings import Settings -from pyogp.lib.base.exc import DataParsingError - -# initialize logging -logger = getLogger('event_system') -log = logger.log - -class AppEventsHandler(object): - """ general class handling individual events """ - - def __init__(self, settings = None): - """ initialize the AppEventsHandler """ - - # 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.handlers = {} - - def register(self, event_name, timeout = 0): - """ create a watcher for a specific event in this event system. the timeout is optional, and defaults to no timeout """ - - if self.settings.LOG_VERBOSE: log(DEBUG, 'Creating a monitor for %s' % (event_name)) - - return self.handlers.setdefault(event_name, AppEventNotifier(event_name, self.settings, timeout)) - - def is_event_handled(self, event_name): - """ if the event is being monitored, return True, otherwise, return False """ - - try: - - handler = self.handlers[event_name] - return True - - except KeyError: - - return False - - def handle(self, event): - """ essentially a case statement to pass event data to notifiers """ - - try: - - handler = self.handlers[event.name] - - # Handle the packet if we have subscribers - if len(handler) > 0: - if self.settings.LOG_VERBOSE: log(DEBUG, 'Handling event: %s' % (event.name)) - - handler(event) - - except KeyError: - #log(INFO, "Received an unhandled packet: %s" % (packet.name)) - pass - -class AppEventNotifier(object): - """ access points for subscribing to application wide events. timeout = 0 for no timeout """ - - def __init__(self, event_name, settings, timeout = 0): - """ initialize an event notifier by name, with an optional timeout """ - - self.event = Event() - self.event_name = event_name - self.settings = settings - - if type(timeout) == int: - self.timeout = timeout - else: - raise DataParsingError("Timeout must be an integer creating an event watcher for %s" % (event_name)) - - def subscribe(self, *args, **kwdargs): - """ register a callback handler for a specific event, starting the timer if != 0, otherwise it will watch until forced to unsubscribe by the caller """ - - self.args = args - self.kwdargs = kwdargs - - self.event.subscribe(*args, **kwdargs) - - if self.timeout != 0: - self._start_timer() - - def received(self, event): - """ notifies subscribers about an event firing and passes along the data """ - - self.event(event) - - def unsubscribe(self, *args, **kwdargs): - """ stop watching this event """ - - self.event.unsubscribe(*args, **kwdargs) - - if self.settings.LOG_VERBOSE: log(DEBUG, "Removed the monitor for %s by %s" % (args, kwdargs)) - - def _start_timer(self): - """ begins the timer when a timeout value is specified. returns None when the timer expires, then unsubscribes """ - - now = time.time() - start = now - - # spawn an empty coroutine for the duration of the timeout - while now - start < self.timeout: - - api.sleep() - now = time.time() - - # once the timeout has expired... - if self.settings.LOG_VERBOSE: log(DEBUG, "Timing out the monitor for %s by %s" % (self.args, self.kwdargs)) - - # return None to the callback handler - self.received(None) - - # unsubscribe the watcher due to the timeout - self.unsubscribe(*self.args, **self.kwdargs) - - def __len__(self): - - return len(self.event) - - __call__ = received - -########################## -# Application Level Events -########################## - -class AppEvent(object): - """ container for an event payload. - - name = name of the event, to which applications will subscribe. - payload = dict of the contents of the event (key:value) - **kwdargs = key:value pairs - - either payload or **kwdargs should be used, not both - """ - - def __init__(self, name, payload = None, llsd = None, **kwargs): - """ initialize the AppEvent contents """ - - self.name = name - self.payload = {} - - if payload != None and len(kwargs) > 0 and llsd != None: - - raise DataParsingError("AppEvent cannot parse both an explicit payload and a kwdargs representation of a payload") - return - - if payload != None: - - if type(payload) == dict: - - self.payload = payload - - else: - - raise DataParsingError("AppEvent payload must be a dict. A %s was passed in." % (type(payload))) - return - - elif len(kwargs) > 0: - - for key in kwargs.keys(): - - self.payload[key] = kwargs[key] - - elif llsd != None: - - self.from_llsd(llsd) - - else: - - raise DataParsingError("AppEvent needs a payload or kwdargs, none were provided for %s." % (self.name)) - return - - def to_llsd(self): - """ transform the event payload into llsd """ - - raise NotImplemented("AppEvent().to_llsd() has not yet been written") - - def from_llsd(self): - """ transform llsd into an event payload """ - - raise NotImplemented("AppEvent().from_llsd() has not yet been written") - -class AppEventEnum(object): - """ enumeration of application level events and their keys""" - - InstantMessageReceived = ['FromAgentID', 'RegionID', 'Position', 'ID', 'FromAgentName', 'Message'] - ChatReceived = ['FromName', 'SourceID', 'OwnerID', 'SourceType', 'ChatType', 'Audible', 'Position', 'Message'] - - - diff --git a/pyogp/lib/base/groups.py b/pyogp/lib/base/groups.py deleted file mode 100644 index 281cbaf..0000000 --- a/pyogp/lib/base/groups.py +++ /dev/null @@ -1,634 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, WARNING, INFO, DEBUG -import re - -# related -from eventlet import api - -# for MockChatInterface -import sys -import select -import tty -import termios - -# pyogp -from pyogp.lib.base.datatypes import UUID, Vector3 -from pyogp.lib.base.exc import DataParsingError -from pyogp.lib.base.utilities.helpers import Wait -from pyogp.lib.base.datamanager import DataManager - -# pyogp messaging -from pyogp.lib.base.message.message import Message, Block - -# pyogp utilities -from pyogp.lib.base.utilities.enums import ImprovedIMDialogue - -# initialize logging -logger = getLogger('pyogp.lib.base.groups') -log = logger.log - -class GroupManager(DataManager): - """ a storage bin for groups - - also, a functional area for group creation operations - """ - - def __init__(self, agent, settings = None): - """ initialize the group manager """ - super(GroupManager, self).__init__(agent, settings) - # the group store consists of a list - # of Group() instances - self.group_store = [] - - if self.settings.LOG_VERBOSE: - log(DEBUG, "Initialized the Group Manager") - - def enable_callbacks(self): - """enables the callback handlers for this GroupManager""" - if self.settings.HANDLE_PACKETS: - onAgentGroupDataUpdate_received = self.agent.region.message_handler.register("AgentGroupDataUpdate") - onAgentGroupDataUpdate_received.subscribe(self.onAgentGroupDataUpdate) - - onChatterBoxInvitation_received = self.agent.region.message_handler.register('ChatterBoxInvitation') - onChatterBoxInvitation_received.subscribe(self.onChatterBoxInvitation_Message) - - onChatterBoxSessionEventReply_received = self.agent.region.message_handler.register('ChatterBoxSessionEventReply') - onChatterBoxSessionEventReply_received.subscribe(self.onChatterBoxSessionEventReply) - - onChatterBoxSessionAgentListUpdates_received = self.agent.region.message_handler.register('ChatterBoxSessionAgentListUpdates') - onChatterBoxSessionAgentListUpdates_received.subscribe(self.onChatterBoxSessionAgentListUpdates) - - onChatterBoxSessionStartReply_received = self.agent.region.message_handler.register('ChatterBoxSessionStartReply') - onChatterBoxSessionStartReply_received.subscribe(self.onChatterBoxSessionStartReply) - - - def handle_group_chat(self, message): - """ process a ChatterBoxInvitation_Message instance""" - - group = [group for group in self.group_store if str(message.blocks['Message_Data'][0].get_variable('instantmessage').data['message_params']['id']) == str(group.GroupID)] - - if group != []: - group[0].handle_inbound_chat(message) - else: - log(WARNING, "Received group chat message from unknown group. Group: %s. Agent: %s. Message: %s" % (message.blocks['Message_Data'][0].get_variable('session_name').data, message.blocks['Message_Data'][0].get_variable('from_name').data, message.blocks['Message_Data'][0].get_variable('message').data)) - - def store_group(self, _group): - """ append to or replace a group in self.group_store """ - - # replace an existing list member, else, append - - try: - - index = [self.group_store.index(_group_) for _group_ in self.group_store if _group_.ID == _group.GroupID] - - self.group_store[index[0]] = _group - - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Replacing a stored group: \'%s\'' % (_group.GroupID)) - - except: - - self.group_store.append(_group) - - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Stored a new group: \'%s\'' % (_group.GroupID)) - - def update_group(self, group_data): - """ accepts a dictionary of group data and creates/updates a group """ - - group = [group for group in self.group_store if str(group_data['GroupID']) == str(group.GroupID)] - - if group != []: - group[0].update_properties(group_data) - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Updating a stored group: \'%s\'' % (group[0].GroupID)) - else: - group = Group(GroupID = group_data['GroupID'], - GroupPowers = group_data['GroupPowers'], - AcceptNotices = group_data['AcceptNotices'], - GroupInsigniaID = group_data['GroupInsigniaID'], - Contribution = group_data['Contribution'], - GroupName = group_data['GroupName']) - - self.store_group(group) - - def update_group_by_name(self, group_data, name): - """ accepts a dictionary of group data and creates/updates a group """ - - pattern = re.compile(name) - - group = [group for group in self.group_store if pattern.match(group.GroupName)] - - if group != []: - group[0].update_properties(group_data) - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Updating a stored group: \'%s\'' % (group[0].GroupName)) - else: - log(INFO, "Received an update for an unknown group for name: %s" % (name)) - - def update_group_by_session_id(self, group_data): - """ accepts a dictionary of group data and creates/updates a group """ - - group = [group for group in self.group_store if str(group.session_id) == str(group_data['session_id'])] - - if group != []: - group[0].update_properties(group_data) - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Updating a stored group: \'%s\'' % (group[0].GroupName)) - else: - log(INFO, "Received an update for an unknown group with a session id of: %s" % (str(group_data['session_id']))) - - def create_group(self, - AgentID = None, - SessionID = None, - Name = None, - Charter = '', - ShowInList = True, - InsigniaID = UUID(), - MembershipFee = 0, - OpenEnrollment = False, - AllowPublish = False, - MaturePublish = False): - """ sends a message to the agent's current region requesting to create a group - - enables a callback (which should be unsubscribed from once we get a response) - """ - - if Name != None: - - log(INFO, "Sending a request to create group with a name of \'%s\'" % (Name)) - - if AgentID == None: - AgentID = self.agent.agent_id - if SessionID == None: - SessionID = self.agent.session_id - - packet = Message('CreateGroupRequest', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - Block('GroupData', - Name = Name, - Charter = Charter, - ShowInList = ShowInList, - InsigniaID = InsigniaID, - MembershipFee = MembershipFee, - OpenEnrollment = OpenEnrollment, - AllowPublish = AllowPublish, - MaturePublish = MaturePublish)) - - self.agent.region.enqueue_message(packet, True) - - if self.settings.HANDLE_PACKETS: - # enable the callback to watch for the CreateGroupReply packet - self.onCreateGroupReply_received = self.agent.region.message_handler.register('CreateGroupReply') - self.onCreateGroupReply_received.subscribe(self.onCreateGroupReply) - else: - - raise DataParsingError('Failed to create a group, please specify a name') - - def get_group(self, GroupID = None): - """ searches the store and returns group if stored, None otherwise """ - - group = [group for group in self.group_store if str(group.GroupID) == str(GroupID)] - - if group == []: - return None - else: - return group[0] - - def join_group(self, group_id): - """ sends a JoinGroupRequest packet for the specified uuid """ - - # set up the callback - self.onJoinGroupReply_received = self.agent.message_handler.register('JoinGroupReply') - self.onJoinGroupReply_received.subscribe(self.onJoinGroupReply) - - self.send_JoinGroupRequest(self.agent.agent_id, self.agent.session_id, group_id) - - def send_JoinGroupRequest(self, agent_id, session_id, group_id): - """ sends a JoinGroupRequest message to the hsot simulator """ - - packet = Message('JoinGroupRequest', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - Block('GroupData', - GroupID = group_id)) - - self.agent.region.enqueue_message(packet, True) - - def activate_group(self, group_id): - """ set a particular group as active """ - - self.send_ActivateGroup(self.agent.agent_id, self.agent.session_id, group_id) - - def send_ActivateGroup(self, agent_id, session_id, group_id): - """ sends an ActivateGroup message to the host simulator """ - - packet = Message('ActivateGroup', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id, - GroupID = group_id)) - - self.agent.region.enqueue_message(packet) - - # ~~~~~~~~~~~~~~~~~~ - # Callback functions - # ~~~~~~~~~~~~~~~~~~ - - def onCreateGroupReply(self, packet): - """ when we get a CreateGroupReply packet, log Success, and if True, request the group details. remove the callback in any case """ - - # remove the monitor - self.onCreateGroupReply_received.unsubscribe(self.onCreateGroupReply) - - AgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data - GroupID = packet.blocks['ReplyData'][0].get_variable('GroupID').data - Success = packet.blocks['ReplyData'][0].get_variable('Success').data - _Message = packet.blocks['ReplyData'][0].get_variable('Message').data - - if Success: - log(INFO, "Created group %s. Message data is: %s" % (GroupID, _Message)) - log(WARNING, "We now need to request the group data...") - else: - log(WARNING, "Failed to create group due to: %s" % (_Message)) - - def onJoinGroupReply(self, packet): - """ the simulator tells us if joining a group was a success. """ - - self.onJoinGroupReply_received.unsubscribe(self.onJoinGroupReply) - - AgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data - GroupID = packet.blocks['GroupData'][0].get_variable('GroupID').data - Success = packet.blocks['GroupData'][0].get_variable('Success').data - - if Success: - log(INFO, "Joined group %s" % (GroupID)) - else: - log(WARNING, "Failed to join group %s" % (GroupID)) - - def onAgentGroupDataUpdate(self, packet): - """ deal with the data that comes in over the event queue """ - - group_data = {} - - AgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data - - # GroupData block - for GroupData_block in packet.blocks['GroupData']: - - group_data['GroupID'] = GroupData_block.get_variable('GroupID').data - group_data['GroupPowers'] = GroupData_block.get_variable('GroupPowers').data - group_data['AcceptNotices'] = GroupData_block.get_variable('AcceptNotices').data - group_data['GroupInsigniaID'] = GroupData_block.get_variable('GroupInsigniaID').data - group_data['Contribution'] = GroupData_block.get_variable('Contribution').data - group_data['GroupName'] = GroupData_block.get_variable('GroupName').data - - # make sense of group powers - group_data['GroupPowers'] = [ord(x) for x in group_data['GroupPowers']] - group_data['GroupPowers'] = ''.join([str(x) for x in group_data['GroupPowers']]) - - self.update_group(group_data) - - def onChatterBoxInvitation_Message(self, message): - """ handle a group chat message from the event queue """ - - self.handle_group_chat(message) - - def onChatterBoxSessionEventReply(self, message): - """ handle a response from the simulator re: a message we sent to a group chat """ - - self.agent.helpers.log_event_queue_data(message, self) - - def onChatterBoxSessionAgentListUpdates(self, message): - """ parse teh response to a request to join a group chat and propagate data out """ - - data = {} - data['session_id'] = message.blocks['Message_Data'][0].get_variable('session_id').data - data['agent_updates'] = message.blocks['Message_Data'][0].get_variable('agent_updates').data - - self.update_group_by_session_id(data) - - def onChatterBoxSessionStartReply(self, message): - - data = {} - data['temp_session_id'] = message.blocks['Message_Data'][0].get_variable('temp_session_id').data - data['success'] = message.blocks['Message_Data'][0].get_variable('success').data - data['session_id'] = message.blocks['Message_Data'][0].get_variable('session_id').data - data['session_info'] = message.blocks['Message_Data'][0].get_variable('session_info').data - - self.update_group_by_name(data, data['session_info']['session_name']) - - -class Group(object): - """ representation of a group """ - - def __init__(self, AcceptNotices = None, GroupPowers = None, GroupID = None, GroupName = None, ListInProfile = None, Contribution = None, GroupInsigniaID = None, agent = None): - - self.AcceptNotices = AcceptNotices - self.GroupPowers = GroupPowers - self.GroupID = UUID(str(GroupID)) - self.GroupName = GroupName - self.ListInProfile = ListInProfile - self.Contribution = Contribution - self.GroupInsigniaID = UUID(str(GroupInsigniaID)) - - self.agent = agent - - # store a history of ChatterBoxInvitation messages, and outgoing packets - self.chat_history = [] - self.session_id = None - - def activate_group(self): - """ set this group as active """ - - self.send_ActivateGroup(self.agent.agent_id, self.agent.session_id, self.GroupID) - - def send_ActivateGroup(self, agent_id, session_id, group_id): - """ send an ActivateGroup message to the host simulator """ - - packet = Message('ActivateGroup', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id, - GroupID = group_id)) - - self.agent.region.enqueue_message(packet) - - def update_properties(self, properties): - """ takes a dictionary of attribute:value and makes it so """ - - for attribute in properties: - - setattr(self, attribute, properties[attribute]) - - def request_join_group_chat(self): - """ sends an ImprovedInstantMessage packet with the atributes necessary to join a group chat """ - - log(INFO, "Requesting to join group chat session for \'%s\'" % (self.GroupName)) - - _AgentID = self.agent.agent_id - _SessionID = self.agent.session_id - _FromGroup = False - _ToAgentID = self.GroupID - _ParentEstateID = 0 - _RegionID = UUID() - _Position = Vector3() - _Offline = 0 - _Dialog = ImprovedIMDialogue.SessionGroupStart - _ID = self.GroupID - _Timestamp = 0 - _FromAgentName = self.agent.Name() - _Message = 'Message''' - _BinaryBucket = '' - - self.agent.send_ImprovedInstantMessage(_AgentID, - _SessionID, - _FromGroup, - _ToAgentID, - _ParentEstateID, - _RegionID, - _Position, - _Offline, - _Dialog, - _ID, - _Timestamp, - _FromAgentName, - _Message, - _BinaryBucket) - - def chat(self, Message = None): - """ sends an instant message to another avatar - - wraps send_ImprovedInstantMessage with some handy defaults """ - - if self.session_id == None: - self.request_join_group_chat() - - Wait(5) - - if self.session_id == None: - log(WARNING, "Failed to start chat session with group %s. Please try again later." % (self.GroupName)) - return - - if Message != None: - - _ID = self.GroupID - _AgentID = self.agent.agent_id - _SessionID = self.agent.session_id - _FromGroup = False - _ToAgentID = self.GroupID - _ParentEstateID = 0 - _RegionID = UUID() - _Position = Vector3() # don't send position, send uuid zero - _Offline = 0 - _Dialog = ImprovedIMDialogue.SessionSend - _ID = self.GroupID - _Timestamp = 0 - _FromAgentName = self.agent.Name() + "\x00" #struct.pack(">" + str(len(self.agent.Name)) + "c", *(self.agent.Name())) - _Message = Message + "\x00" #struct.pack(">" + str(len(Message)) + "c", *(Message)) - _BinaryBucket = "\x00" # self.GroupName #struct.pack(">" + str(len(self.GroupName)) + "c", *(self.GroupName)) - - self.agent.send_ImprovedInstantMessage(_AgentID, - _SessionID, - _FromGroup, - _ToAgentID, - _ParentEstateID, - _RegionID, - _Position, - _Offline, - _Dialog, - _ID, - _Timestamp, - _FromAgentName, - _Message, - _BinaryBucket) - - def handle_inbound_chat(self, message): - """ parses an incoming chat message from a group """ - - session_id = message.blocks['Message_Data'][0].get_variable('session_id').data - session_name = message.blocks['Message_Data'][0].get_variable('session_name').data - from_name = message.blocks['Message_Data'][0].get_variable('from_name').data - _message = message.blocks['Message_Data'][0].get_variable('instantmessage').data['message_params']['message'] - - self.chat_history.append(message) - - # Todo: raise an app level event - log(INFO, "Group chat received. Group: %s From: %s Message: %s" % (session_name, from_name, _message)) - -class MockChatInterface(object): - """ a super simple chat interface for testing group chat in a console """ - - def __init__(self, agent, chat_handler): - - self.agent = agent - self.chat_handler = chat_handler - self.old_settings = termios.tcgetattr(sys.stdin) - - self.message = '' - - def data_watcher(self): - - return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []) - - def start(self): - - self.message = '' - - try: - tty.setcbreak(sys.stdin.fileno()) - - while self.agent.running: - - if self.data_watcher(): - c = sys.stdin.read(1) - #print c - if c == '\x1b': - self.message = self.get_and_send_input() - #if c == '\x1b': # x1b is ESC - #self.chat_handler(c) - #break - #else: - #self.message += c - api.sleep() - except: - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings) - #finally: - # termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings) - - if self.agent.running: - self.start() - - def get_and_send_input(self): - - print "This is blocking in this implementation. Go fast, else get disconnected..." - - self.message = raw_input("Enter Message to group") - self.chat_handler(self.message) - -''' -class ChatterBoxInvitation_Message(object): - """ a group chat message sent over the event queue """ - - def __init__(self, session_name = None, from_name = None, session_id = None, _type = None, region_id = None, offline = None, timestamp = None, ttl = None, to_id = None, source = None, from_group = None, position = None, parent_estate_id = None, message = None, binary_bucket = None, _id = None, god_level = None, limited_to_estate = None, check_estate = None, agent_id = None, from_id = None, ChatterBoxInvitation_Data = None): - - if ChatterBoxInvitation_Data != None: - - self.session_name = ChatterBoxInvitation_Data['session_name'] - self.from_name = ChatterBoxInvitation_Data['from_name'] - self.session_id = UUID(string = str(ChatterBoxInvitation_Data['session_id'])) - #self.from_name = ChatterBoxInvitation_Data['session_name'] - self._type = ChatterBoxInvitation_Data['instantmessage']['message_params']['type'] - self.region_id = UUID(string = str(ChatterBoxInvitation_Data['instantmessage']['message_params']['region_id'])) - self.offline = ChatterBoxInvitation_Data['instantmessage']['message_params']['offline'] - self.timestamp = ChatterBoxInvitation_Data['instantmessage']['message_params']['timestamp'] - self.ttl = ChatterBoxInvitation_Data['instantmessage']['message_params']['ttl'] - self.to_id = UUID(string = str(ChatterBoxInvitation_Data['instantmessage']['message_params']['to_id'])) - self.source = ChatterBoxInvitation_Data['instantmessage']['message_params']['source'] - self.from_group = ChatterBoxInvitation_Data['instantmessage']['message_params']['from_group'] - self.position = ChatterBoxInvitation_Data['instantmessage']['message_params']['position'] - self.parent_estate_id = ChatterBoxInvitation_Data['instantmessage']['message_params']['parent_estate_id'] - self.message = ChatterBoxInvitation_Data['instantmessage']['message_params']['message'] - self.binary_bucket = ChatterBoxInvitation_Data['instantmessage']['message_params']['data']['binary_bucket'] - self._id = UUID(string = str(ChatterBoxInvitation_Data['instantmessage']['message_params']['id'])) - #self.from_id = ChatterBoxInvitation_Data['instantmessage']['message_params']['from_id'] - self.god_level = ChatterBoxInvitation_Data['instantmessage']['agent_params']['god_level'] - self.limited_to_estate = ChatterBoxInvitation_Data['instantmessage']['agent_params']['limited_to_estate'] - self.check_estate = ChatterBoxInvitation_Data['instantmessage']['agent_params']['check_estate'] - self.agent_id = UUID(string = str(ChatterBoxInvitation_Data['instantmessage']['agent_params']['agent_id'])) - self.from_id = UUID(string = str(ChatterBoxInvitation_Data['from_id'])) - #self.message = ChatterBoxInvitation_Data['message'] - - self.name = 'ChatterBoxInvitation' - - else: - - self.session_name = session_name - self.from_name = from_name - self.session_id = session_id - #self.from_name = from_name - self._type = _type - self.region_id = region_id - self.offline = offline - self.timestamp = timestamp - self.ttl = ttl - self.to_id = to_id - self.source = source - self.from_group = from_group - self.position = position - self.parent_estate_id = parent_estate_id - self.message = message - self.binary_bucket = binary_bucket - self._id = _id - #self.from_id = from_id - self.god_level = god_level - self.limited_to_estate = limited_to_estate - self.check_estate = check_estate - self.agent_id = agent_id - self.from_id = from_id - #self.message = message - - self.name = 'ChatterBoxInvitation' - -class ChatterBoxSessionEventReply_Message(object): - - def __init__(self, message_data): - - self.success = message_data['success'] - self.event = message_data['event'] - self.session_id = UUID(string = str(message_data['session_id'])) - self.error = message_data['error'] - - self.name = 'ChatterBoxSessionEventReply' - -class ChatterBoxSessionAgentListUpdates_Message(object): - """ incomplete implementation""" - - def __init__(self, message_data): - - self.agent_updates = message_data['agent_updates'] - self.session_id = UUID(string = str(message_data['session_id'])) - - self.name = 'ChatterBoxSessionAgentListUpdates' - - #{'body': {'agent_updates': {'a517168d-1af5-4854-ba6d-672c8a59e439': {'info': {'can_voice_chat': True, 'is_moderator': False}}}, 'session_id': '4dd70b7f-8b3a-eef9-fc2f-909151d521f6', 'updates': {}}, 'message': 'ChatterBoxSessionAgentListUpdates'}] - - -class ChatterBoxSessionStartReply_Message(object): - """ incomplete implementation""" - - def __init__(self, message_data): - - self.temp_session_id = UUID(string = str(message_data['temp_session_id'])) - self.success = message_data['success'] - self.session_id = UUID(string = str(message_data['session_id'])) - self.session_info = message_data['session_info'] - - self.name = "ChatterBoxSessionStartReply" - - #{'body': {'temp_session_id': 4dd70b7f-8b3a-eef9-fc2f-909151d521f6, 'success': True, 'session_id': 4dd70b7f-8b3a-eef9-fc2f-909151d521f6, 'session_info': {'voice_enabled': True, 'session_name': "Enus' Construction Crew", 'type': 0, 'moderated_mode': {'voice': False}}}, 'message': 'ChatterBoxSessionStartReply'}], -''' - - - diff --git a/pyogp/lib/base/inventory.py b/pyogp/lib/base/inventory.py deleted file mode 100644 index de4e8d5..0000000 --- a/pyogp/lib/base/inventory.py +++ /dev/null @@ -1,1138 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, ERROR, WARNING, INFO, DEBUG -import re - -# related -import uuid -import struct - -# pyogp -from pyogp.lib.base.datatypes import UUID -from pyogp.lib.base.exc import ResourceError, ResourceNotFound, DataParsingError -from pyogp.lib.base.datamanager import DataManager - -# pyogp.message -from pyogp.lib.base.message.message import Message, Block - -# pyogp utilities -from pyogp.lib.base.utilities.enums import ImprovedIMDialogue - -# initialize logging -logger = getLogger('pyogp.lib.base.inventory') -log = logger.log - -# ToDo: handle library inventory properly. right now, it's treated as regular inv. store it in self.library_folders - -class InventoryManager(DataManager): - """ is an inventory container - - Initialize the event queue client class - >>> inventory = InventoryManager() - - Sample implementations: agent.py - Tests: tests/test_inventory.py - """ - - def __init__(self, agent = None, settings = None): - """ set up the inventory manager """ - super(InventoryManager, self).__init__(agent, settings) - # 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 - - if self.settings.LOG_VERBOSE: - log(INFO, "Initializing inventory storage") - - def enable_callbacks(self): - """ enable monitors for certain inventory related packet events """ - - onInventoryDescendents_received = self.agent.region.message_handler.register('InventoryDescendents') - onInventoryDescendents_received.subscribe(self.onInventoryDescendents) - - onFetchInventoryReply_received = self.agent.region.message_handler.register('FetchInventoryReply') - onFetchInventoryReply_received.subscribe(self.onFetchInventoryReply) - - onBulkUpdateInventory_received = self.agent.region.message_handler.register('BulkUpdateInventory') - onBulkUpdateInventory_received.subscribe(self.onBulkUpdateInventory) - - def onInventoryDescendents(self, packet): - - raise NotImplemented("onInventoryDescendents") - - def onFetchInventoryReply(self, packet): - - raise NotImplemented("onFetchInventoryReply") - - def onBulkUpdateInventory(self, packet): - """ handle the inventory data being delivered in the BullkUpdateInventory packet """ - - for FolderData_block in packet.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) - - self._store_inventory_folder(folder) - - for ItemData_block in packet.blocks['ItemData']: - - # what is CallbackID??? not doing anything with it. - - _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) - - self._store_inventory_item(inventory_item) - - 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._store_inventory_folder(folder, 'inventory') for folder in self.agent.login_response['inventory-skeleton']] - - if self.agent.login_response.has_key('inventory-skel-lib'): - [self._store_inventory_folder(folder, 'library') for folder in self.agent.login_response['inventory-skel-lib']] - - def _store_inventory_item(self, inventory_item, destination = 'inventory'): - """ inventory items comes from packets, or from caps """ - - if destination == 'inventory': - - container = self.folders - - elif destination == 'library': - - container = self.library_folders - - else: - - log(WARNING, 'Not storing folder data, as it\'s not bound for a valid destination.') - return - - # 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 = [container.index(folder) for folder in container if str(folder.FolderID) == str(inventory_item.FolderID)] - - # did not find a folder for this item - # this is probably a case we are not handling correctly - if len(index) == 0: - if self.settings.LOG_VERBOSE: - log(DEBUG, 'Did not find parent folder %s for Inventory Item %s: %s' % (inventory_item.ItemID, inventory_item.FolderID, inventory_item.Name)) - return - - try: - - inventory_index = [container[index].index(item) for item in container[index].inventory] - - container[index[0]].inventory[inventory_index[0]] = inventory_item - - if self.settings.LOG_VERBOSE: - 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 ' % (inventory_item.ItemID)) - - 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... - """ - - 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 == []: - search_folders = self.folders - else: - search_folders = folder_list - - 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: - - if isinstance(item, InventoryItem): - if str(item.ItemID) == str(_id): - match_list.append(item) - elif isinstance(item, InventoryFolder): - if str(item.FolderID) == str(_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 _store_inventory_folder(self, folder_data, destination = 'inventory'): - """ inventory folder data comes from either packets or login, or from caps. destination is either 'inventory' or 'library' """ - - if destination == 'inventory': - - container = self.folders - - elif destination == 'library': - - container = self.library_folders - - else: - - log(WARNING, 'Not storing folder data, as it\'s not bound for a valid destination.') - return - - # if it's a dict, we are parsing login response data or a caps response - # transform to an InventoryFolder() instance and store it if we havne't already stored it, otherwise, update the params - if type(folder_data) == dict: - - if folder_data.has_key('category_id'): - - # this is from caps - - folder = InventoryFolder(folder_data['name'], folder_data['category_id'], folder_data['parent_id'], folder_data['version'], folder_data['type_default'], folder_data['agent_id']) - - else: - - # this is from login inv skeleton - - folder = InventoryFolder(folder_data['name'], folder_data['folder_id'], folder_data['parent_id'], folder_data['version'], folder_data['type_default']) - - elif isinstance(folder_data, InventoryFolder): - - folder = folder_data - - # see if we have already stored this folder - index = [container.index(target_folder) for target_folder in container if str(folder.FolderID) == str(target_folder.FolderID)] - - # skip storing folders we already know about - if len(index) == 1: - - # we may be receiving an update to the folder attributes, if so, save off the contents, swap in the new representation, then append the contents - - if self.settings.LOG_VERBOSE: log(DEBUG, 'Replacing a stored inventory folder: %s for agent \'%s\'' % (folder.FolderID, self.agent.agent_id)) - - contents = container[index[0]].inventory - - container[index[0]] = folder - - container[index[0]].inventory = contents - - else: - - if self.settings.LOG_VERBOSE: log(DEBUG, "Storing inventory folder %s" % (folder.Name)) - - 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 - - container.append(folder) - - def display_folder_contents(self, folder_id = 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 str(member.ParentID) == str(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 str(folder.ParentID) == str(self.inventory.inventory_root.FolderID)] - - def _request_folder_contents(self, folder_id = None, source = 'inventory'): - """ send a request to the server for folder contents, wraps sendFetchInventoryDescendentsPacket """ - - if source == 'inventory': - - self.sendFetchInventoryDescendentsRequest(folder_id = folder_id) - - elif source == 'library': - - self.sendFetchLibDescendentsRequest(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: - - self.send_FetchInventory(self.agent.agent_id, self.agent.session_id, self.agent.agent_id, id_list) - - def send_FetchInventory(self, agent_id, session_id, owner_id, inventory_ids): - """ - send a FetchInventory message to the host simulator - each of the ids in inventory_ids will be sent in it's own InventoryData block - """ - - packet = Message('FetchInventory', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id), - *[Block('InventoryData', - OwnerID = owner_id, - ItemID = item_id) for item_id in inventory_ids]) - - # enqueue the message - self.region.enqueue_message(packet) - - def give_inventory(self, ItemID = None, agent_id = None): - """ offers another agent the specified inventory item - - searches the local inventory for the specified ItemID - given a match, sends an ImprovedInventoryMessage packet to the specified AgentID - """ - - if not (ItemID or agent_id): - log(WARNING, "ItemID and agent_id are required in InventoryManager().give_inventory()") - return - - item_id = self.search_inventory(item_id = ItemID) - - if item_id == []: - log(WARNING, "ItemID %s not found in inventory of %s" % (ItemID, self.agent.Name())) - return - elif len(item_id) > 1: - log(WARNING, "Multiple matches in inventory for ItemID %s, using the first one in InventoryManager().give_inventory()" % (ItemID)) - - inv_item = item_id[0] - - inv_item.give(self.agent, agent_id) - - def handle_inventory_offer(self, packet): - """ parses and handles an incoming inventory offer """ - - FromAgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data - FromAgentName = packet.blocks['MessageBlock'][0].get_variable('FromAgentName').data - InventoryName = packet.blocks['MessageBlock'][0].get_variable('Message').data - ID = packet.blocks['MessageBlock'][0].get_variable('ID').data - _Message = packet.blocks['MessageBlock'][0].get_variable('Message').data - ToAgentID = packet.blocks['MessageBlock'][0].get_variable('ToAgentID').data - - BinaryBucket = packet.blocks['MessageBlock'][0].get_variable('BinaryBucket').data - - # parse the binary bucket - AssetType = struct.unpack(">b", BinaryBucket[0:1])[0] - ItemID = UUID(BinaryBucket[1:17]) - - if str(FromAgentID) == str(self.agent.agent_id): - - log(INFO, "%s offered inventory to %s" % (self.agent.Name(), ToAgentID)) - - else: - - log(INFO, "%s received an inventory offer from %s for %s." % (self.agent.Name(), FromAgentName, InventoryName)) - - if self.settings.LOG_VERBOSE: - log(DEBUG, "Inventory offer data. \n\tFromAgentID: %s \n\tFromAgentName: %s \n\tInventoryName: %s \n\tID: %s \n\tAssetType: %s \n\tItemID: %s \n\tMessage: %s" % (FromAgentID, FromAgentName, InventoryName, ID, AssetType, ItemID, Message)) - - if self.agent.settings.ACCEPT_INVENTORY_OFFERS: - - accept = True - - else: - - accept = False - - self.confirm_inventory_offer(FromAgentID, FromAgentName, InventoryName, ID, AssetType, ItemID, accept) - - def confirm_inventory_offer(self, FromAgentID, FromAgentName, InventoryName, ID, AssetType, ItemID, accept): - """ sends an ImprovedInstantMessage packet accepting or declining an inventory offer """ - - if accept: - accept_key = ImprovedIMDialogue.InventoryAccepted - log(INFO, "Sending an inventory accepted message to %s for item %s(%s)" % (FromAgentName, InventoryName, ID)) - else: - accept_key = ImprovedIMDialogue.InventoryDeclined - log(INFO, "Sending an inventory declined message to %s for item %s(%s)" % (FromAgentName, InventoryName, ID)) - - 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 """ - - raise NotImplemented("sendFetchInventoryDescendentsRequest") - - def sendFetchInventoryRequest(self, folder_id = None): - """ send a request to the grid for inventory items in folder """ - - raise NotImplemented("sendFetchInventoryRequest") - - def sendFetchLibRequest(self, item_id = None): - """ send a request to the grid for library items """ - - raise NotImplemented("sendFetchLibRequest") - - def sendFetchLibDescendentsRequest(self, item_id = None): - """ send a request to the grid for library folder contents """ - - raise NotImplemented("sendFetchDescendentsRequest") - -class AIS(InventoryManager): - """ AIS specific inventory manager """ - - def __init__(self, agent, capabilities, settings = None): - - super(AIS, self).__init__(agent, settings) - - self.capabilities = capabilities - - def sendFetchInventoryRequest(self, item_ids = []): - """ send a request to the grid for inventory item attributes """ - - if len(item_ids) == 0: - log(WARNING, "sendFetchInventoryRequest requires > 0 item_ids, 0 passed in") - return - elif type(item_ids) != list: - log(WARNING, "sendFetchInventoryRequest requires a list of item_ids, %s passed in" % (type(item_ids))) - return - - cap = self.capabilities['FetchInventory'] - - post_body = {'items': [{'item_id': str(item_id)} for item_id in item_ids]} - - custom_headers = {'Accept' : 'application/llsd+xml'} - - try: - result = cap.POST(post_body, custom_headers) - except ResourceError, error: - log(ERROR, error) - return - except ResourceNotFound, error: - log(ERROR, "404 calling: %s" % (error)) - return - - for item in response['folders']: - - self._store_caps_items(item, folder_id) - - def sendFetchLibRequest(self, folder_id, item_ids = []): - """ send a request to the grid for library item attributes """ - - if len(item_ids) == 0: - log(WARNING, "sendFetchLibRequest requires > 0 item_ids, 0 passed in") - return - elif type(item_ids) != list: - log(WARNING, "sendFetchLibRequest a list of item_ids, %s passed in" % (type(item_ids))) - return - - cap = self.capabilities['FetchLib'] - - post_body = {'items': [{'item_id': str(item_id)} for item_id in item_ids]} - - custom_headers = {'Accept' : 'application/llsd+xml'} - - try: - result = cap.POST(post_body, custom_headers) - except ResourceError, error: - log(ERROR, error) - return - except ResourceNotFound, error: - log(ERROR, "404 calling: %s" % (error)) - return - - for item in response['folders']: - - self._store_caps_items(item, folder_id, 'library') - - def sendFetchInventoryDescendentsRequest(self, folder_id): - """ send a request to the server for inventory folder contents """ - - cap = self.capabilities['WebFetchInventoryDescendents'] - - post_body = {'folders': [{'folder_id': str(folder_id), 'owner_id': str(self.agent.agent_id)}]} - - 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 - - ''' - Response shape - - {'folders':[ {'category': CATEGORY_SHAPE, - 'categories': [CATEGORY_SHAPE,], - 'items': [ITEM_SHAPE,] }]} - ''' - - for member in response['folders']: - - if member.has_key('category'): - self._store_inventory_folder(member['category']) - - if member.has_key('categories'): - [self._store_inventory_folder(folder_info) for folder_info in member['categories']] - - # pass a tuple of (item_info, category_id) - if member.has_key('items'): - self._store_caps_items(member['items'], member['category']['category_id']) - - def _store_caps_items(self, items, folder_id, destination = 'inventory'): - """ transform an AIS caps response's inventory items and folder_id to InventoryItems and store it """ - - for item in items: - inventory_item = InventoryItem(item['item_id'], folder_id, item['permissions']['creator_id'], item['permissions']['owner_id'], item['permissions']['group_id'], item['permissions']['base_mask'], item['permissions']['owner_mask'], item['permissions']['group_mask'], item['permissions']['everyone_mask'], item['permissions']['next_owner_mask'], None, item['asset_id'], item['type'], item['inv_type'], item['flags'], item['sale_info']['sale_type'], item['sale_info']['sale_price'], item['name'], item['desc'], item['created_at'], None, item['permissions']['last_owner_id']) - - self._store_inventory_item(inventory_item, destination) - - def sendFetchLibDescendentsRequest(self, folder_id): - """ send a request to the server for folder contents """ - - cap = self.capabilities['FetchLibDescendents'] - - post_body = {'folders': [{'folder_id': str(folder_id), 'owner_id': self.settings.ALEXANDRIA_LINDEN}]} - - 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 - - ''' - Response shape - - {'folders':[ {'category': CATEGORY_SHAPE, - 'categories': [CATEGORY_SHAPE,], - 'items': [ITEM_SHAPE,] }]} - ''' - - for member in response['folders']: - - if member.has_key('category'): - self._store_inventory_folder(member['category'], 'library') - - if member.has_key('categories'): - [self._store_inventory_folder(folder_info, 'library') for folder_info in member['categories']] - - if member.has_key('items'): - self._store_caps_items(member['items'], member['category']['category_id'], 'library') - - -class UDP_Inventory(InventoryManager): - - def __init__(self, agent, settings = None): - - super(UDP_Inventory, self).__init__(agent, settings) - - def sendFetchInventoryRequest(self, folder_id = None): - """ send a request to the grid for folder attributes """ - - raise NotImplemented("sendFetchInventoryRequest") - - def sendFetchLibRequest(self, item_id = None): - """ send a request to the grid for library items """ - - raise NotImplemented("sendFetchInventoryRequest") - - def sendFetchInventoryDescendentsRequest(self, folder_id = None): - """ send a request to the grid for folder contents """ - - packet = Message('FetchInventoryDescendents', - Block('AgentData', - AgentID = self.agent.agent_id, # MVT_LLUUID - SessionID = self.agent.session_id), # MVT_LLUUID - Block('InventoryData', - FolderID = UUID(str(folder_id)), # MVT_LLUUID - OwnerID = self.agent.agent_id, # MVT_LLUUID - SortOrder = 0, # MVT_S32, 0 = name, 1 = time - FetchFolders = True, # MVT_BOOL - FetchItems = True)) # MVT_BOOL - - self.agent.region.enqueue_message(packet) - - def sendFetchLibDescendentsRequest(self, folder_id = None): - """ send a request to the grid for library items """ - - raise NotImplemented("sendFetchLibDescendentsRequest") - - def onFetchInventoryReply(self, packet): - - _agent_id = packet.blocks['AgentData'][0].get_variable('AgentID') - - for InventoryData_block in packet.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) - - self._store_inventory_item(inventory_item) - - def onInventoryDescendents(self, packet): - - if packet.blocks['AgentData'][0].get_variable('Descendents') > 0: - - _agent_id = packet.blocks['AgentData'][0].get_variable('AgentID') - _folder_id = packet.blocks['AgentData'][0].get_variable('FolderID') - _owner_id = packet.blocks['AgentData'][0].get_variable('OwnerID') - _version = packet.blocks['AgentData'][0].get_variable('Version') - _descendents = packet.blocks['AgentData'][0].get_variable('Descendents') - # _descendents is not dealt with in any way here - - - if str(packet.blocks['ItemData'][0].get_variable('ItemID').data) != str(UUID()): - - for ItemData_block in packet.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) - - self._store_inventory_item(inventory_item) - - if str(packet.blocks['FolderData'][0].get_variable('FolderID').data) != str(UUID()): - - for FolderData_block in packet.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, _agent_id) - - self._store_inventory_folder(folder) - -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, AgentID = UUID(), Descendents = 0): - """ initialize the inventory folder """ - - self.type = 'InventoryFolder' - - self.Name = Name - self.FolderID = UUID(str(FolderID)) - self.ParentID = UUID(str(ParentID)) - self.Version = Version - self.Type = Type - self.Descendents = Descendents - self.AgentID = UUID(str(AgentID)) - - self.inventory = [] - - def purge_descendents(self, agent): - """ removes inventory from this folder, by unlinking the inventory from this parent folder """ - - self.send_PurgeInventoryDescendents(agent.agent_id, agent.session_id, self.FolderID) - - def send_PurgeInventoryDescendents(self, agent, agent_id, session_id, folder_id): - """ send a PurgeInventoryDescendents message to the host simulator """ - - packet = Message('PurgeInventoryDescendents', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id), - Block('InventoryData', - FolderID = folder_id)) - - agent.region.enqueue_message(packet) - - def move(self, agent, parent_id, restamp = False): - """ reparents this inventory folder """ - - self.send_MoveInventoryFolder(agent, agent.agent_id, agent.session_id, restamp, self.FolderID, UUID(str(parent_id))) - - def send_MoveInventoryFolder(self, agent, agent_id, session_id, restamp, folder_id, parent_id): - """ sends a MoveInventoryFolder message to the host simulator """ - - # ToDo: the InventoryData block may the variable, if moved to a more - # generic location (not nested under Inventory folder), - # make it such that multiple blocks can be sent - # use *[Block('InventoryData',FolderID = folder_id) for folder_id in folder_ids] - - packet = Message('MoveInventoryFolder', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id, - Stamp = restamp), - Block('InventoryData', - FolderID = folder_id, - ParentID = parent_id)) - - agent.region.enqueue_message(packet) - - def remove(self, agent): - """ removes this inventory folder """ - - self.send_RemoveInventoryFolder(agent, agent.agent_id, agent.session_id, False, self.FolderID) - - def send_RemoveInventoryFolder(self, agent, agent_id, session_id, restamp, folder_id): - """ sends a RemoveInventoryFolder message to the host simulator """ - - # ToDo: the InventoryData block may the variable, if moved to a more - # generic location (not nested under Inventory folder), - # make it such that multiple blocks can be sent - # use *[Block('InventoryData',FolderID = folder_id) for folder_id in folder_ids] - - packet = Message('RemoveInventoryFolder', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id, - Stamp = restamp), - Block('InventoryData', - FolderID = folder_id)) - - agent.region.enqueue_message(packet) - -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, - LastOwnerID = UUID()): - """ initialize the inventory item """ - - self.type = 'InventoryItem' - - self.ItemID = UUID(str(ItemID)) # LLUUID - self.FolderID = UUID(str(FolderID)) # LLUUID - self.CreatorID = UUID(str(CreatorID)) # LLUUID - self.OwnerID = UUID(str(OwnerID)) # LLUUID - self.GroupID = 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(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 - self.LastOwnerID = UUID(str(LastOwnerID)) - - def rez_object(self, agent, relative_position = (1, 0, 0)): - - # self.agent.Position holds where we are. we need to add this Vector3 to the incoming tuple (vector to a vector) - location_to_rez_x = agent.Position.X + relative_position[0] - location_to_rez_y = agent.Position.Y + relative_position[1] - location_to_rez_z = agent.Position.Z + 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(self, agent, name = None, value = None): - """ allow arbitraty update to any data in the inventory item - - accepts a dictionary of key:value pairs which will update the stored inventory items - and then send an UpdateInventoryItem packet - """ - - 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)) - - def give(self, agent, to_agent_id): - """ sends the target agent an inventory offer of this """ - - # prep variables and send the message - _AgentID = agent.agent_id - _SessionID = agent.session_id - _FromGroup = False - _ToAgentID = to_agent_id - _ParentEstateID = 0 - _RegionID = agent.region.RegionID - _Position = agent.Position - _Offline = 0 - _Dialog = ImprovedIMDialogue.InventoryOffered - _ID = uuid.uuid4() - _Timestamp = 0 - _FromAgentName = agent.Name() - _Message = self.Name - _BinaryBucket = struct.pack(">b", self.Type) + self.ItemID.get_bytes() # binary of asset type and id - - log(INFO, "Sending inventory offer of %s from %s to %s" % (self.Name, agent.agent_id, to_agent_id)) - - agent.send_ImprovedInstantMessage(_AgentID, _SessionID, _FromGroup, _ToAgentID, _ParentEstateID, _RegionID, _Position, _Offline, _Dialog, _ID, _Timestamp, _FromAgentName, _Message, _BinaryBucket) - -# ~~~~~~~~~~~~~~~ -# Packet Wrappers -# ~~~~~~~~~~~~~~~ - -def sendUpdateInventoryItem(agent, inventory_items = [], ): - """ sends an UpdateInventoryItem packet to a region - - this function expects an InventoryItem instance already with updated data - """ - - packet = Message('UpdateInventoryItem', - Block('AgentData', - AgentID = agent.agent_id, - SessionID = agent.session_id, - TransactionID = UUID()), - *[Block('InventoryData', - ItemID = item.ItemID, - FolderID = item.FolderID, - CallbackID = UUID(), - CreatorID = item.CreatorID, - OwnerID = item.OwnerID, - GroupID = item.GroupID, - BaseMask = item.BaseMask, - OwnerMask = item.OwnerMask, - GroupMask = item.GroupMask, - EveryoneMask = item.EveryoneMask, - NextOwnerMask = item.NextOwnerMask, - GroupOwned = item.GroupOwned, - TransactionID = UUID(), - Type = item.Type, - InvType = item.InvType, - Flags = item.Flags, - SaleType = item.SaleType, - SalePrice = item.SalePrice, - Name = item.Name, - Description = item.Description, - CreationDate = item.CreationDate, - CRC = item.CRC) for item in inventory_items]) - - agent.region.enqueue_message(packet) - -def sendRezObject(agent, inventory_item, RayStart, RayEnd, FromTaskID = UUID(), BypassRaycast = 1, RayTargetID = UUID(), RayEndIsIntersection = False, RezSelected = False, RemoveItem = False, ItemFlags = 0, GroupMask = 0, EveryoneMask = 0, NextOwnerMask = 0): - """ sends a RezObject packet to a region """ - - packet = Message('RezObject', - Block('AgentData', - 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), - 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)) - - agent.region.enqueue_message(packet) - - - - - diff --git a/pyogp/lib/base/login.py b/pyogp/lib/base/login.py deleted file mode 100644 index 60d0b5e..0000000 --- a/pyogp/lib/base/login.py +++ /dev/null @@ -1,448 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG -import xmlrpclib -import re -import sys - -# related -from indra.base import llsd - -# pyogp -from pyogp.lib.base.exc import LoginError, ParseStartLocError, HTTPError, ResourceError, ResourceNotFound -from pyogp.lib.base.tests.mock_xmlrpc import MockXMLRPC -from pyogp.lib.base.tests.base import MockXMLRPCLogin -from pyogp.lib.base.network.stdlib_client import StdLibClient - -# initialize globals -logger = getLogger('login') -log = logger.log - -class Login(object): - """ logs into a login endpoint - - There are 2 cases here: 'legacy' login and 'OGP' login. - Legacy = standard Second Life/OpenSim login - OGP = Open Grid Protocol enabled grid, where one logs into an agent domain - - Example (legacy oriented): - - The login type is determined by parsing the login uri - 'legacy' = login.cgi - 'ogp' = auth.cgi - - Initialize the login class - >>> login = Login() - - Setup some login parameters. - >>> login_params = LegacyLoginParams('firstname', 'lastname', 'password') - >>> login_params = login_params.serialize() - - login stores & returns the response from the loginuri (in our example a mock response) - >>> login.login('http://localhost:12345/login.cgi', login_params, 'region') - {'login': 'true', 'seed_capability': 'http://127.0.0.1:12345/seed_cap'} - - >>> login.response['login'] - true - - Sample implementations: examples/sample_login.py - Tests: tests/login.txt, tests/test_legacy_login.py, tests/test_ogp_login.py - """ - - def __init__(self, settings = None, handler = None): - """ initialize the login object """ - - # 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() - - # this can be either 'legacy' or 'ogp' - self.type = None - - # storage containers for login attributes - self.loginuri = None - self.login_params = {} - self.input_params = {} - self.start_location = None - - # this will hold the reponse from the loginuri - self.response = None - - # holds the transform response if there is one - self.transform_response = None - - # if we are testing, we can initialize a mock handler - self.handler = None - - if self.settings.LOG_VERBOSE: log(INFO, 'Initializing login') - - def login(self, loginuri = None, login_params = None, start_location = None, handler = None): - """ high level login initiator, returns the login response as a dict""" - - if (re.search('auth.cgi$', loginuri)): - - self.type = 'ogp' - - # prep the login parameters object - self._init_ogp_login_params(loginuri, login_params, start_location) - - if handler == None and self.handler == None: - - self.handler = StdLibClient() - else: - - self.handler = handler - - self._post_to_ogp_loginuri() - - elif (re.search('login.cgi$', loginuri)): - - self.type = 'legacy' - - # prep the login parameters object - self._init_legacy_login_params(loginuri, login_params, start_location) - - # handler can be a mock endpoint for testing, otherwise it's xmlrpc - if handler == None and self.handler == None: - - # If we are running normally, we need xmlrpc for legacy login - self._init_legacy_login_handler(self.loginuri) - - else: - - self.handler = handler - - self._post_to_legacy_loginuri() - - else: - log(WARNING, 'Unknown loginuri type: %s' % (self.loginuri)) - raise LoginError('Unknown loginuri type: %s' % (self.loginuri)) - - return self.response - - def _init_legacy_login_handler(self, loginuri): - """ sets up the xmlrpc handler """ - - # MockXMLRPC is the test handler, retain it - if type(self.handler) == type(MockXMLRPC(MockXMLRPCLogin(), self.loginuri)): - pass - else: - self.handler = xmlrpclib.ServerProxy(self.loginuri) - - def _init_legacy_login_params(self, loginuri = None, login_params = None, start_location = None): - """ prepares the login attributes for submission """ - - if (loginuri != None): - self.loginuri = loginuri - - if (login_params != None): - - # now store the account data - self.login_params = login_params.serialize() - - # store the input params - self.input_params['firstname'] = self.login_params['first'] - self.input_params['lastname'] = self.login_params['last'] - self.input_params['password'] = self.login_params['passwd'] - - if (start_location != None): - self.start_location = self._parse_legacy_start_location(start_location) - else: - self.start_location = self.settings.DEFAULT_START_LOCATION - - self.login_params['start'] = self.start_location - - # add some additonal details to the login_params based on what is specified in settings - # do not overwrite specified settings - self._get_extended_legacy_params() - - def _init_ogp_login_params(self, loginuri = None, login_params = None, start_location = None): - """ prepares the login attributes for submission """ - - if (loginuri != None): - self.loginuri = loginuri - - if (login_params != None): - - # we use content_type for http calls here - self.content_type = login_params.content_type - - # now store the account data - self.login_params = login_params.serialize() - - # store the input params - self.input_params['firstname'] = login_params.firstname - self.input_params['lastname'] = login_params.lastname - self.input_params['password'] = login_params.password - - if (start_location != None): - self.start_location = start_location - else: - self.start_location = self.settings.DEFAULT_START_LOCATION - - def _post_to_legacy_loginuri(self, loginuri = None, login_params = None, login_method = 'login_to_simulator'): - """ post to a login uri and return the results """ - - if loginuri != None: - self.loginuri = loginuri - - if login_params != None: - self.login_params = login_params.serialize() - - self._init_legacy_login_handler(loginuri) - - log(INFO, 'Logging \'%s %s\' into %s with method: %s' % (self.login_params['first'], self.login_params['last'], self.loginuri, login_method)) - if self.settings.LOG_VERBOSE: log(DEBUG, '\'%s %s\' has the following login parameters: %s' % (self.login_params['first'], self.login_params['last'], self.login_params)) - - # This handles the standard 'login_to_simulator' case - # plus, all the transforms that may need to be followed - login_handler = self.handler.__getattr__(login_method) - - try: - self.response = login_handler(self.login_params) - except Exception, error: - raise LoginError('Failed to login agent due to: %s' % (error)) - - if self.response['login'] in ('true', 'false'): - - self._parse_response() - - else: - # handle transformation - self._handle_transform(self.response) - - def _handle_transform(self, transform): - """ follows a transform """ - - if self.settings.LOG_VERBOSE: log(DEBUG, 'Login response for \'%s %s\' is: %s' % (self.input_params['firstname'], self.input_params['lastname'], transform)) - - # store the response in the transform response attribute - self.transform_response = transform - - log(INFO, 'Following a login redirect to %s with method: %s. Message: %s' % (transform['next_url'], transform['next_method'], transform['message'])) - - self._post_to_legacy_loginuri(loginuri = transform['next_url'], login_method = transform['next_method']) - - def _get_extended_legacy_params(self): - """ get the extra bits needed for login """ - - # do not overwrite existing settings - default_login_params = self.settings.get_default_xmlrpc_login_parameters() - - if not self.login_params.has_key('channel'): self.login_params['channel'] = default_login_params['channel'] - if not self.login_params.has_key('version'): self.login_params['version'] = default_login_params['version'] - if not self.login_params.has_key('mac'): self.login_params['mac'] = default_login_params['mac'] - if not self.login_params.has_key('agree_to_tos'): self.login_params['agree_to_tos'] = default_login_params['agree_to_tos'] - if not self.login_params.has_key('read_critical'): self.login_params['read_critical'] = default_login_params['read_critical'] - if not self.login_params.has_key('id0'): self.login_params['id0'] = default_login_params['id0'] - if not self.login_params.has_key('options'): self.login_params['options'] = default_login_params['options'] - - log(DEBUG, 'Initializing login parameters for \'%s %s\'' % (self.login_params['first'], self.login_params['last'])) - - def _parse_response(self): - """ evaluates the data contained in the login response - This is just a lot of logging for the most part... - """ - - if self.type == 'legacy': - - if self.settings.LOG_VERBOSE: log(DEBUG, 'Login response for \'%s %s\' is: %s' % (self.input_params['firstname'], self.input_params['lastname'], self.response)) - - if self.response['login'] == 'true': - - log(INFO, 'Logged in \'%s %s\'' % (self.input_params['firstname'], self.input_params['lastname'])) - - if self.response.has_key('message'): log(INFO, 'Login message: %s' % (self.response['message'])) - - elif self.response == None: - - log(WARNING, 'Failed to login \'%s %s\' due to %s' % (self.input_params['firstname'], self.input_params['lastname'], 'empty response from loginuri')) - - raise LoginError('Failed login due to empty response from loginuri') - - elif self.response['login'] == 'false': - - log(WARNING, 'Failed login for \'%s %s\', Reason: %s' % (self.input_params['firstname'], self.input_params['lastname'], self.response['message'])) - - raise LoginError('Failed login due to: %s' % (self.response['message'])) - - else: - - raise LoginError('Unknown error during login') - - elif self.type == 'ogp': - - if self.settings.LOG_VERBOSE: log(DEBUG, 'Login response for \'%s %s\' is: %s' % (self.input_params['firstname'], self.input_params['lastname'], self.response.body)) - - if self.response == None: - log(WARNING, 'Failed to login \'%s %s\' due to %s' % (self.input_params['firstname'], self.input_params['lastname'], 'empty response from loginuri')) - raise LoginError('Failed login due to empty response from loginuri') - elif self.response._status == '200 OK': - - self.response = llsd.parse(self.response.body) - - if self.response['authenticated']: - log(INFO, 'Logged in \'%s %s\'' % (self.input_params['firstname'], self.input_params['lastname'])) - elif not self.response['authenticated']: - log(WARNING, 'Failed login for \'%s %s\', Reason: %s' % (self.input_params['firstname'], self.input_params['lastname'], self.response['message'])) - raise LoginError('Failed login due to: %s' % (self.response['message'])) - else: - - raise LoginError('Unknown error during login') - - else: - - return - - def _parse_legacy_start_location(self, start_location): - """ make sure a user specified start location is in the correct form """ - - # for now, we accept a region name, or a tuple containing region name, x, y, z - # x, y, and z must be integers - - if type(start_location) == tuple: - try: - return 'uri:%s&%i&%i&%i' % (start_location[0], start_location[1], start_location[2], start_location[3]) - except IndexError, error: - log(WARNING, 'Invalid start_location specified (%s), using default of \'%s\'' % (start_location, self.settings.DEFAULT_START_LOCATION)) - return self.settings.DEFAULT_START_LOCATION - except TypeError, error: - log(WARNING, 'Invalid start_location specified (%s), using default of \'%s\'' % (start_location, self.settings.DEFAULT_START_LOCATION)) - return self.settings.DEFAULT_START_LOCATION - elif type(start_location) == str and re.match("uri:", start_location[0:4].lower()): - location = start_location.split(":")[1] - mg = location.split('&',3) - # try forcing to ints - if len(mg) == 4: - return 'uri:%s&%i&%i&%i' % (mg[0],int(mg[1]),int(mg[2]),int(mg[3])) - elif len(mg) == 3: - return 'uri:%s&%i&%i&30' % (mg[0],int(mg[1]),int(mg[2])) - elif len(mg) == 2: - return 'uri:%s&%i&128&30' % (mg[0],int(mg[1])) - elif len(mg) == 1: - return 'uri:%s&128&128&30' % mg[0] - else: - return self.settings.DEFAULT_START_LOCATION - - elif type(start_location) == str: - mg = start_location.split('/',3) - # try forcing to ints - if len(mg) == 4: - return 'uri:%s&%i&%i&%i' % (mg[0],int(mg[1]),int(mg[2]),int(mg[3])) - elif len(mg) == 3: - return 'uri:%s&%i&%i&30' % (mg[0],int(mg[1]),int(mg[2])) - elif len(mg) == 2: - return 'uri:%s&%i&128&30' % (mg[0],int(mg[1])) - elif len(mg) == 1: - return 'uri:%s&128&128&30' % mg[0] - else: - return self.settings.DEFAULT_START_LOCATION - else: - # if we cannot make sense of what is passed in, use the settings default - return self.settings.DEFAULT_START_LOCATION - - def _post_to_ogp_loginuri(self, loginuri = None, login_params = None, start_location = None): - """ logs in to an agent domain's loginuri """ - - if loginuri != None: - self.loginuri = loginuri - - # we expect an OGPLoginParams object here - if login_params != None: - self.login_params = login_params.serialize() - # store the input params - for k in self.login_params: - self.input_params[k] = login_params[k] - - headers = {'Content-Type': self.content_type} - - log(INFO, 'Logging in to %s as %s %s' % (self.loginuri, self.input_params['firstname'], self.input_params['lastname'])) - if self.settings.LOG_VERBOSE: log(DEBUG, '\'%s %s\' has the following login parameters: %s with headers of: %s' % (self.input_params['firstname'], self.input_params['lastname'], self.login_params, headers)) - - try: - self.response = self.handler.POST(self.loginuri, self.login_params, headers=headers) - self._parse_response() - except HTTPError, error: - if error.code==404: - raise ResourceNotFound(self.login_uri) - else: - raise ResourceError(self.loginuri, error.code, error.msg, error.fp.read(), method="POST") - -class LegacyLoginParams(object): - """ a legacy plain password credential """ - - def __init__(self, firstname, lastname, password): - """ initialize this credential """ - - self.firstname = firstname - self.lastname = lastname - self.password = password - - def __repr__(self): - """ return a string representation """ - - return 'Legacy login instance for \'%s %s\'' %(self.firstname, self.lastname) - - def serialize(self): - """ return a dictionary of login params """ - - login_params = { - 'first': self.firstname, - 'last': self.lastname, - 'passwd': self.password - } - - return login_params - -class OGPLoginParams(object): - """ an OGP plain password credential """ - - def __init__(self, firstname, lastname, password): - """ initialize this credential """ - - self.firstname = firstname - self.lastname = lastname - self.password = password - self.content_type = 'application/llsd+xml' - - def __repr__(self): - """ return a string representation """ - - return 'OGP login parameters for \'%s %s\'' % (self.firstname, self.lastname) - - def serialize(self): - """ return a dictionary of login params """ - - login_params = { - 'firstname': self.firstname, - 'lastname': self.lastname, - 'password': self.password - } - - llsd_params = llsd.format_xml(login_params) - - return llsd_params - - - diff --git a/pyogp/lib/base/objects.py b/pyogp/lib/base/objects.py deleted file mode 100644 index d07afa6..0000000 --- a/pyogp/lib/base/objects.py +++ /dev/null @@ -1,1343 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, ERROR, WARNING, INFO, DEBUG -import uuid -import re -import struct -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 -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, \ - Permissions, AssetType - -# initialize logging -logger = getLogger('pyogp.lib.base.objects') -log = logger.log - -class ObjectManager(DataManager): - """ is an Object Manager - - Initialize the event queue client class - >>> objects = ObjectManager() - - Sample implementations: region.py - Tests: tests/test_objects.py - """ - - def __init__(self, agent = None, region = None, settings = None, message_handler = None, events_handler = None): - """ set up the object manager """ - super(ObjectManager, self).__init__(agent, settings) - self.region = region - - # the object store consists of a list - # of Object() instances - self.object_store = [] - - # the avatar store consists of a list - # of Avatar() instances - self.avatar_store = [] - - # other useful things - self.helpers = Helpers() - - self.message_handler = message_handler - - if self.settings.LOG_VERBOSE: log(INFO, "Initializing object storage") - - def enable_callbacks(self): - """enables the callback handlers for this ObjectManager""" - if self.settings.HANDLE_PACKETS: - # supply a MessageHandler if not given one - if self.message_handler == None: - self.message_handler = MessageHandler() - - onObjectUpdate_received = self.message_handler.register('ObjectUpdate') - onObjectUpdate_received.subscribe(self.onObjectUpdate) - - onObjectUpdateCached_received = self.message_handler.register('ObjectUpdateCached') - onObjectUpdateCached_received.subscribe(self.onObjectUpdateCached) - - onObjectUpdateCompressed_received = self.message_handler.register('ObjectUpdateCompressed') - onObjectUpdateCompressed_received.subscribe(self.onObjectUpdateCompressed) - - onObjectProperties_received = self.message_handler.register('ObjectProperties') - onObjectProperties_received.subscribe(self.onObjectProperties) - - onKillObject_received = self.message_handler.register('KillObject') - onKillObject_received.subscribe(self.onKillObject) - - # uncomment these to view packets sent back to simulator - # onObjectName_sent = self.message_handler.register('ObjectName') - # onObjectName_sent.subscribe(self.helpers.log_packet, self) - - # onDeRezObject_sent = self.message_handler.register('DeRezObject') - # onDeRezObject_sent.subscribe(self.helpers.log_packet, self) - - - def process_multiple_object_updates(self, objects): - """ process a list of object updates """ - - [self.process_object_update(_object) for _object in objects] - - def process_object_update(self, _object): - """ append to or replace an object in self.objects """ - - # this is an avatar - if _object.PCode == PCodeEnum.Avatar: - - self.store_avatar(_object) - - # this is a Primitive - elif _object.PCode == PCodeEnum.Primitive: - - self.store_object(_object) - - else: - - if self.settings.LOG_VERBOSE: - log(DEBUG, "Not processing object update of type %s" % (PCodeEnum(_object.PCode))) - - def store_object(self, _object): - """ store a representation of an object that has been transformed from data off the wire """ - - # replace an existing list member, else, append - - index = [self.object_store.index(_object_) for _object_ in self.object_store if _object_.LocalID == _object.LocalID] - - if index != []: - - self.object_store[index[0]] = _object - - #if self.settings.LOG_VERBOSE: log(DEBUG, 'Updating a stored object: %s in region \'%s\'' % (_object.FullID, self.region.SimName)) - - else: - - self.object_store.append(_object) - - #if self.settings.LOG_VERBOSE: log(DEBUG, 'Stored a new object: %s in region \'%s\'' % (_object.LocalID, self.region.SimName)) - - def store_avatar(self, _objectdata): - """ store a representation of an avatar (Object() instance) that has been transformed from data off the wire """ - - #ToDo: should there be a separate Avatar() class? - - # if the object data pertains to us, update our data! - if str(_objectdata.FullID) == str(self.agent.agent_id): - - if _objectdata.Position: - self.agent.Position = _objectdata.Position - if _objectdata.FootCollisionPlane: - self.agent.FootCollisionPlane = _objectdata.FootCollisionPlane - if _objectdata.Velocity: - self.agent.Velocity = _objectdata.Velocity - if _objectdata.Acceleration: - self.agent.Acceleration = _objectdata.Acceleration - if _objectdata.Rotation: - self.agent.Rotation = _objectdata.Rotation - if _objectdata.AngularVelocity: - self.agent.AngularVelocity = _objectdata.AngularVelocity - - if self.settings.ENABLE_APPEARANCE_MANAGEMENT: - self.agent.appearance.TextureEntry = _objectdata.TextureEntry - - self.agent.sendDynamicsUpdate() - - index = [self.avatar_store.index(_avatar_) for _avatar_ in self.avatar_store if _avatar_.LocalID == _objectdata.LocalID] - - if index != []: - - self.avatar_store[index[0]] = _objectdata - - #if self.settings.LOG_VERBOSE: log(DEBUG, 'Replacing a stored avatar: %s in region \'%s\'' % (_objectdata.LocalID, self.region.SimName)) - - else: - - self.avatar_store.append(_objectdata) - - #if self.settings.LOG_VERBOSE: log(DEBUG, 'Stored a new avatar: %s in region \'%s\'' % (_objectdata.LocalID, self.region.SimName)) - - def get_object_from_store(self, LocalID = None, FullID = None): - """ searches the store and returns object if stored, None otherwise """ - - _object = [] - - if LocalID != None: - _object = [_object for _object in self.object_store if _object.LocalID == LocalID] - elif FullID != None: - _object = [_object for _object in self.object_store if str(_object.FullID) == str(FullID)] - - if _object == []: - return None - else: - return _object[0] - - def get_avatar_from_store(self, LocalID = None, FullID = None): - """ searches the store and returns object if stored, None otherwise """ - - if LocalID != None: - _avatar = [_avatar for _avatar in self.avatar_store if _avatar.LocalID == LocalID] - elif FullID != None: - _avatar = [_avatar for _avatar in self.avatar_store if _avatar.FullID == FullID] - - if _avatar == []: - return None - else: - return _avatar[0] - - def my_objects(self): - """ returns a list of known objects where the calling client is the owner """ - - matches = [_object for _object in self.object_store if str(_object.OwnerID) == str(self.agent.agent_id)] - - return matches - - def find_objects_by_name(self, Name): - """ searches the store for known objects by name - - returns a list of all such known objects - """ - - pattern = re.compile(Name) - - matches = [_object for _object in self.object_store if pattern.match(_object.Name)] - - return matches - - def find_objects_within_radius(self, radius): - """ returns objects nearby. returns a list of objects """ - - if type(radius) != float: - radius = float(radius) - - objects_nearby = [] - - for item in self.object_store: - - if item.Position == None: - continue - - if math.sqrt(math.pow((item.Position.X - self.agent.Position.X), 2) + math.pow((item.Position.Y - self.agent.Position.Y), 2) + math.pow((item.Position.Z - self.agent.Position.Z), 2)) <= radius: - objects_nearby.append(item) - - return objects_nearby - - def remove_object_from_store(self, ID = None): - """ handles removing a stored object representation """ - - victim = self.get_object_from_store(LocalID = ID) - - if victim == None: - victim = self.get_avatar_from_store(LocalID = ID) - - # if we do not know about this object, pass - if victim == None or victim == []: - return - - # this is an avatar - if victim.PCode == PCodeEnum.Avatar: - - self.kill_stored_avatar(ID) - - # this is a Primitive - elif victim.PCode == PCodeEnum.Primitive: - - self.kill_stored_object(ID) - - else: - - if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: - log(DEBUG, "Not processing kill of unstored object type %s" % (PCodeEnum(victim.PCode))) - - def kill_stored_avatar(self, ID): - """ removes a stored avatar (Object() instance) from our list """ - - index = [self.avatar_store.index(_avatar_) for _avatar_ in self.avatar_store if _avatar_.LocalID == ID] - - if index != []: - del self.avatar_store[index[0]] - if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: - log(DEBUG, "Kill on object data for avatar tracked as local id %s" % (ID)) - - def kill_stored_object(self, ID): - - index = [self.object_store.index(_object_) for _object_ in self.object_store if _object_.LocalID == ID] - - if index != []: - del self.object_store[index[0]] - if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: - log(DEBUG, "Kill on object data for object tracked as local id %s" % (ID)) - - def update_multiple_objects_properties(self, object_list): - """ update the attributes of objects """ - - #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Processing multiple object properties updates: %s" % (len(object_list))) - - for object_properties in object_list: - - self.update_object_properties(object_properties) - - def update_object_properties(self, object_properties): - """ update the attributes of an object - - If the object is known, we update the properties. - If not, we create a new object - """ - - #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Processing object properties update for FullID: %s" % (object_properties['FullID'])) - - if object_properties.has_key('PCode'): - # this is an avatar - if object_properties['PCode'] == PCodeEnum.Avatar: - - #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Creating a new avatar and storing their attributes. LocalID = %s" % (object_properties['LocalID'])) - - _object = Object() - _object._update_properties(object_properties) - - self.store_avatar(_object) - - else: - - self.update_prim_properties(object_properties) - - else: - - self.update_prim_properties(object_properties) - - def update_prim_properties(self, prim_properties): - """ handles an object update and updates or adds to internal representation """ - - _object = self.get_object_from_store(FullID = prim_properties['FullID']) - - if _object == None: - #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Creating a new object and storing it's attributes. LocalID = %s" % (object_properties['LocalID'])) - _object = Object() - _object._update_properties(prim_properties) - self.store_object(_object) - 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 - - accepts a list of (ID, CacheMissType) - """ - - packet = Message('RequestMultipleObjects', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - *[Block('ObjectData', - CacheMissType = ID_CacheMissType[1], - ID = ID_CacheMissType[0]) for ID_CacheMissType in ID_CacheMissType_list]) - - # enqueue the message, send as reliable - self.region.enqueue_message(packet, True) - - def create_default_box(self, GroupID = UUID(), relative_position = (1, 0, 0)): - """ creates the default box, defaulting as 1m to the east, with an option GroupID to set the prim to""" - - # 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 = self.agent.Position.X + relative_position[0] - location_to_rez_y = self.agent.Position.Y + relative_position[1] - location_to_rez_z = self.agent.Position.Z + relative_position[2] - - location_to_rez = (location_to_rez_x, location_to_rez_y, location_to_rez_z) - - # 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) - - 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 - object actually created. - - AddFlags (see also ObjectUpdate) - 0x01 - use physics - 0x02 - create selected - - GroupID defaults to (No group active) - ''' - - packet = Message('ObjectAdd', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID, - GroupID = GroupID), - Block('ObjectData', - PCode = PCode, - Material = Material, - AddFlags = AddFlags, - PathCurve = PathCurve, - ProfileCurve = ProfileCurve, - PathBegin = PathBegin, - PathEnd = PathEnd, - PathScaleX = PathScaleX, - PathScaleY = PathScaleY, - PathShearX = PathShearX, - PathShearY = PathShearY, - PathTwist = PathTwist, - PathTwistBegin = PathTwistBegin, - PathRadiusOffset = PathRadiusOffset, - PathTaperX = PathTaperX, - PathTaperY = PathTaperY, - PathRevolutions = PathRevolutions, - PathSkew = PathSkew, - ProfileBegin = ProfileBegin, - ProfileEnd = ProfileEnd, - ProfileHollow = ProfileHollow, - BypassRaycast = BypassRaycast, - RayStart = RayStart, - RayEnd = RayEnd, - RayTargetID = RayTargetID, - RayEndIsIntersection = RayEndIsIntersection, - Scale = Scale, - Rotation = Rotation, - State = State)) - - self.region.enqueue_message(packet, True) - - def onObjectUpdate(self, packet): - """ populates an Object instance and adds it to the ObjectManager() store """ - - REGION_SIZE = 256.0 - MIN_HEIGHT = -REGION_SIZE - MAX_HEIGHT = 4096.0 - - object_list = [] - - # ToDo: handle these 2 variables properly - _RegionHandle = packet.blocks['RegionData'][0].get_variable('RegionHandle').data - _TimeDilation = packet.blocks['RegionData'][0].get_variable('TimeDilation').data - - for ObjectData_block in packet.blocks['ObjectData']: - - object_properties = {} - - object_properties['LocalID'] = ObjectData_block.get_variable('ID').data - object_properties['State'] = ObjectData_block.get_variable('State').data - object_properties['FullID'] = ObjectData_block.get_variable('FullID').data - object_properties['CRC'] = ObjectData_block.get_variable('CRC').data - object_properties['PCode'] = ObjectData_block.get_variable('PCode').data - object_properties['Material'] = ObjectData_block.get_variable('Material').data - object_properties['ClickAction'] = ObjectData_block.get_variable('ClickAction').data - object_properties['Scale'] = ObjectData_block.get_variable('Scale').data - object_properties['ObjectData'] = ObjectData_block.get_variable('ObjectData').data - object_properties['ParentID'] = ObjectData_block.get_variable('ParentID').data - object_properties['UpdateFlags'] = ObjectData_block.get_variable('UpdateFlags').data - object_properties['PathCurve'] = ObjectData_block.get_variable('PathCurve').data - object_properties['ProfileCurve'] = ObjectData_block.get_variable('ProfileCurve').data - object_properties['PathBegin'] = ObjectData_block.get_variable('PathBegin').data - object_properties['PathEnd'] = ObjectData_block.get_variable('PathEnd').data - object_properties['PathScaleX'] = ObjectData_block.get_variable('PathScaleX').data - object_properties['PathScaleY'] = ObjectData_block.get_variable('PathScaleY').data - object_properties['PathShearX'] = ObjectData_block.get_variable('PathShearX').data - object_properties['PathShearY'] = ObjectData_block.get_variable('PathShearY').data - object_properties['PathTwist'] = ObjectData_block.get_variable('PathTwist').data - object_properties['PathTwistBegin'] = ObjectData_block.get_variable('PathTwistBegin').data - object_properties['PathRadiusOffset'] = ObjectData_block.get_variable('PathRadiusOffset').data - object_properties['PathTaperX'] = ObjectData_block.get_variable('PathTaperX').data - object_properties['PathTaperY'] = ObjectData_block.get_variable('PathTaperY').data - object_properties['PathRevolutions'] = ObjectData_block.get_variable('PathRevolutions').data - object_properties['PathSkew'] = ObjectData_block.get_variable('PathSkew').data - object_properties['ProfileBegin'] = ObjectData_block.get_variable('ProfileBegin').data - object_properties['ProfileEnd'] = ObjectData_block.get_variable('ProfileEnd').data - object_properties['ProfileHollow'] = ObjectData_block.get_variable('ProfileHollow').data - object_properties['TextureEntry'] = ObjectData_block.get_variable('TextureEntry').data - object_properties['TextureAnim'] = ObjectData_block.get_variable('TextureAnim').data - object_properties['NameValue'] = ObjectData_block.get_variable('NameValue').data - object_properties['Data'] = ObjectData_block.get_variable('Data').data - object_properties['Text'] = ObjectData_block.get_variable('Text').data - object_properties['TextColor'] = ObjectData_block.get_variable('TextColor').data - object_properties['MediaURL'] = ObjectData_block.get_variable('MediaURL').data - object_properties['PSBlock'] = ObjectData_block.get_variable('PSBlock').data - object_properties['ExtraParams'] = ObjectData_block.get_variable('ExtraParams').data - object_properties['Sound'] = ObjectData_block.get_variable('Sound').data - object_properties['OwnerID'] = ObjectData_block.get_variable('OwnerID').data - object_properties['Gain'] = ObjectData_block.get_variable('Gain').data - object_properties['Flags'] = ObjectData_block.get_variable('Flags').data - object_properties['Radius'] = ObjectData_block.get_variable('Radius').data - object_properties['JointType'] = ObjectData_block.get_variable('JointType').data - object_properties['JointPivot'] = ObjectData_block.get_variable('JointPivot').data - object_properties['JointAxisOrAnchor'] = ObjectData_block.get_variable('JointAxisOrAnchor').data - - # deal with the data stored in _ObjectData - # see http://wiki.secondlife.com/wiki/ObjectUpdate#ObjectData_Format for details - - object_properties['FootCollisionPlane'] = None - object_properties['Position'] = None - object_properties['Velocity'] = None - object_properties['Acceleration'] = None - object_properties['Rotation'] = None - object_properties['AngularVelocity'] = None - - objdata = object_properties['ObjectData'] - if len(objdata) == 76 or len(objdata) == 60: - - pos = 0 - - if len(objdata) == 76: - # Foot collision plane. LLVector4. - # Angular velocity is ignored and set to 0. Falls through to 60 bytes parser. - object_properties['FootCollisionPlane'] = Quaternion(objdata, pos) - pos += 16 - - # 32 bit precision update. - - object_properties['Position'] = Vector3(objdata, pos+0) - object_properties['Velocity'] = Vector3(objdata, pos+12) - object_properties['Acceleration'] = Vector3(objdata, pos+24) - object_properties['Rotation'] = Quaternion(objdata, pos+36, 3) # unpack from vector3 - object_properties['AngularVelocity'] = Vector3(objdata, pos+48) - - # *TODO: This is... weird - #if len(objdata) == 76: - # object_properties['AngularVelocity'] = Vector3() - - - elif len(objdata) == 48 or len(objdata) == 32: - - pos = 0 - if len(objdata) == 48: - # Foot collision plane. LLVector4 - object_properties['FootCollisionPlane'] = Quaternion(objdata, pos) - pos += 16 - - # 32 bit precision update. - - # Position. U16Vec3. - # Velocity. U16Vec3. - # Acceleration. U16Vec3. - # Rotation. U16Rot(4xU16). - # Angular velocity. LLVector3. - - object_properties['Position'] = Vector3( - X=Helpers.packed_u16_to_float(objdata, pos+ 0, -0.5*REGION_SIZE, 1.5*REGION_SIZE), - Y=Helpers.packed_u16_to_float(objdata, pos+ 2, -0.5*REGION_SIZE, 1.5*REGION_SIZE), - Z=Helpers.packed_u16_to_float(objdata, pos+ 4, MIN_HEIGHT, MAX_HEIGHT)) - object_properties['Velocity'] = Vector3( - X=Helpers.packed_u16_to_float(objdata, pos+ 6, -REGION_SIZE, REGION_SIZE), - Y=Helpers.packed_u16_to_float(objdata, pos+ 8, -REGION_SIZE, REGION_SIZE), - Z=Helpers.packed_u16_to_float(objdata, pos+10, -REGION_SIZE, REGION_SIZE)) - object_properties['Acceleration'] = Vector3( - X=Helpers.packed_u16_to_float(objdata, pos+12, -REGION_SIZE, REGION_SIZE), - Y=Helpers.packed_u16_to_float(objdata, pos+14, -REGION_SIZE, REGION_SIZE), - Z=Helpers.packed_u16_to_float(objdata, pos+16, -REGION_SIZE, REGION_SIZE)) - object_properties['Rotation'] = Quaternion( - X=Helpers.packed_u16_to_float(objdata, pos+18, -1.0, 1.0), - Y=Helpers.packed_u16_to_float(objdata, pos+20, -1.0, 1.0), - Z=Helpers.packed_u16_to_float(objdata, pos+22, -1.0, 1.0), - W=Helpers.packed_u16_to_float(objdata, pos+24, -1.0, 1.0)) - object_properties['AngularVelocity'] = Vector3( - X=Helpers.packed_u16_to_float(objdata, pos+26, -REGION_SIZE, REGION_SIZE), - Y=Helpers.packed_u16_to_float(objdata, pos+28, -REGION_SIZE, REGION_SIZE), - Z=Helpers.packed_u16_to_float(objdata, pos+30, -REGION_SIZE, REGION_SIZE)) - - elif len(objdata) == 16: - - # 8 bit precision update. - - # Position. U8Vec3. - # Velocity. U8Vec3. - # Acceleration. U8Vec3. - # Rotation. U8Rot(4xU8). - # Angular velocity. U8Vec3 - - object_properties['Position'] = Vector3( - X=Helpers.packed_u8_to_float(objdata, 0, -0.5*REGION_SIZE, 1.5*REGION_SIZE), - Y=Helpers.packed_u8_to_float(objdata, 1, -0.5*REGION_SIZE, 1.5*REGION_SIZE), - Z=Helpers.packed_u8_to_float(objdata, 2, MIN_HEIGHT, MAX_HEIGHT)) - object_properties['Velocity'] = Vector3( - X=Helpers.packed_u8_to_float(objdata, 3, -REGION_SIZE, REGION_SIZE), - Y=Helpers.packed_u8_to_float(objdata, 4, -REGION_SIZE, REGION_SIZE), - Z=Helpers.packed_u8_to_float(objdata, 5, -REGION_SIZE, REGION_SIZE)) - object_properties['Acceleration'] = Vector3( - X=Helpers.packed_u8_to_float(objdata, 6, -REGION_SIZE, REGION_SIZE), - Y=Helpers.packed_u8_to_float(objdata, 7, -REGION_SIZE, REGION_SIZE), - Z=Helpers.packed_u8_to_float(objdata, 8, -REGION_SIZE, REGION_SIZE)) - object_properties['Rotation'] = Quaternion( - X=Helpers.packed_u8_to_float(objdata, 9, -1.0, 1.0), - Y=Helpers.packed_u8_to_float(objdata, 10, -1.0, 1.0), - Z=Helpers.packed_u8_to_float(objdata, 11, -1.0, 1.0), - W=Helpers.packed_u8_to_float(objdata, 12, -1.0, 1.0)) - object_properties['AngularVelocity'] = Vector3( - 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) - - def onObjectUpdateCached(self, packet): - """ borrowing from libomv, we'll request object data for all data coming in via ObjectUpdateCached""" - - # ToDo: handle these 2 variables properly - _RegionHandle = packet.blocks['RegionData'][0].get_variable('RegionHandle').data - _TimeDilation = packet.blocks['RegionData'][0].get_variable('TimeDilation').data - - _request_list = [] - - for ObjectData_block in packet.blocks['ObjectData']: - - LocalID = ObjectData_block.get_variable('ID').data - _CRC = ObjectData_block.get_variable('CRC').data - _UpdateFlags = ObjectData_block.get_variable('UpdateFlags').data - - # Objects.request_object_update() expects a tuple of (_ID, CacheMissType) - - # see if we have the object stored already - _object = self.get_object_from_store(LocalID = LocalID) - - if _object == None or _object == []: - CacheMissType = 1 - else: - CacheMissType = 0 - - _request_list.append((LocalID, CacheMissType)) - - # ask the simulator for updates - self.request_object_update(self.agent.agent_id, self.agent.session_id, ID_CacheMissType_list = _request_list) - - def onObjectUpdateCompressed(self, packet): - """ handles an ObjectUpdateCompressed message from a simulator """ - - object_list = [] - - # ToDo: handle these 2 variables properly - _RegionHandle = packet.blocks['RegionData'][0].get_variable('RegionHandle').data - _TimeDilation = packet.blocks['RegionData'][0].get_variable('TimeDilation').data - - for ObjectData_block in packet.blocks['ObjectData']: - - object_properties = {} - - object_properties['UpdateFlags'] = ObjectData_block.get_variable('UpdateFlags').data - object_properties['Data'] = ObjectData_block.get_variable('Data').data - _Data = object_properties['Data'] - - pos = 0 # position in the binary string - object_properties['FullID'] = UUID(bytes = _Data, offset = 0) # LLUUID - pos += 16 - object_properties['LocalID'] = struct.unpack("B", _Data[pos:pos+1])[0] - pos += 1 - - if object_properties['PCode'] != 9: # if it is not a prim, stop. - log(WARNING, 'Fix Me!! Skipping parsing of ObjectUpdateCompressed packet when it is not a prim.') - # we ought to parse it and make sense of the data... - continue - - object_properties['State'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['CRC'] = struct.unpack("B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['ClickAction'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['Scale'] = Vector3(_Data, pos) - pos += 12 - object_properties['Position'] = Vector3(_Data, pos) - pos += 12 - object_properties['Rotation'] = Vector3(_Data, pos) - pos += 12 - object_properties['Flags'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['OwnerID'] = UUID(bytes = _Data, offset = pos) - pos += 16 - - # Placeholder vars, to be populated via flags if present - object_properties['AngularVelocity'] = Vector3() - object_properties['ParentID'] = UUID() - object_properties['Text'] = '' - object_properties['TextColor'] = None - object_properties['MediaURL'] = '' - object_properties['Sound'] = UUID() - object_properties['Gain'] = 0 - object_properties['Flags'] = 0 - object_properties['Radius'] = 0 - object_properties['NameValue'] = '' - object_properties['ExtraParams'] = None - - if object_properties['Flags'] != 0: - - log(WARNING, "FixMe! Quiting parsing an ObjectUpdateCompressed packet with flags due to incomplete implemention. Storing a partial representation of an object with uuid of %s" % (object_properties['FullID'])) - - # the commented code is not working right, we need to figure out why! - # ExtraParams in particular seemed troublesome - - ''' - print 'Flags: ', Flags - - if (Flags & CompressedUpdateFlags.contains_AngularVelocity) != 0: - _AngularVelocity = Vector3(_Data, pos) - pos += 12 - print 'AngularVelocity: ', _AngularVelocity - else: - _AngularVelocity = None - - if (Flags & CompressedUpdateFlags.contains_Parent) != 0: - _ParentID = UUID(_Data, pos) - pos += 16 - print 'ParentID: ', _ParentID - else: - _ParentID = None - - if (Flags & CompressedUpdateFlags.Tree) != 0: - # skip it, only iterate the position - pos += 1 - print 'Tree' - - if (Flags & CompressedUpdateFlags.ScratchPad) != 0: - # skip it, only iterate the position - size = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - pos += size - print 'Scratchpad size' - - if (Flags & CompressedUpdateFlags.contains_Text) != 0: - # skip it, only iterate the position - _Text = '' - while struct.unpack(">B", _Data[pos:pos+1])[0] != 0: - pos += 1 - pos += 1 - _TextColor = struct.unpack("B", _Data[pos:pos+1])[0] != 0: - pos += 1 - pos += 1 - print '_MediaURL: ', _MediaURL - - if (Flags & CompressedUpdateFlags.contains_Particles) != 0: - # skip it, only iterate the position - ParticleData = _Data[pos:pos+86] - pos += 86 - print 'Particles' - - # parse ExtraParams - # ToDo: finish this up, for now we are just incrementing the position and not dealing with the data - - _Flexible = None - _Light = None - _Sculpt = None - - num_extra_params = struct.unpack(">b", _Data[pos:pos+1])[0] - print 'Number of extra params: ', num_extra_params - pos += 1 - - for i in range(num_extra_params): - - # ExtraParam type - extraparam_type = struct.unpack("f", _Data[pos:pos+4])[0] - pos += 4 - - #_Flags = stuct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - - #_Radius = struct.unpack(">f", _Data[pos:pos+4])[0] - pos += 4 - - if (Flags & CompressedUpdateFlags.contains_NameValues) != 0: - # skip it, only iterate the position - _NameValue = '' - - while _Data[pos:pos+1] != 0: - #_NameValue += struct.unpack(">c", _Data[pos:pos+1])[0] - pos += 1 - pos += 1 - ''' - - object_properties['PathCurve'] = None - object_properties['PathBegin'] = None - object_properties['PathEnd'] = None - object_properties['PathScaleX'] = None - object_properties['PathScaleY'] = None - object_properties['PathShearX'] = None - object_properties['PathShearY'] = None - object_properties['PathTwist'] = None - object_properties['PathTwistBegin'] = None - object_properties['PathRadiusOffset'] = None - object_properties['PathTaperX'] = None - object_properties['PathTaperY'] = None - object_properties['PathRevolutions'] = None - object_properties['PathSkew'] = None - object_properties['ProfileCurve'] = None - object_properties['ProfileBegin'] = None - object_properties['ProfileEnd'] = None - object_properties['ProfileHollow'] = None - object_properties['TextureEntry'] = None - object_properties['TextureAnim'] = None - object_properties['TextureAnim'] = None - - else: - - object_properties['PathCurve'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathBegin'] = struct.unpack("B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathScaleY'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathShearX'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathShearY'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathTwist'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathTwistBegin'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathRadiusOffset'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathTaperX'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathTaperY'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathRevolutions'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['PathSkew'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['ProfileCurve'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['ProfileBegin'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['ProfileEnd'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - object_properties['ProfileHollow'] = struct.unpack(">B", _Data[pos:pos+1])[0] - pos += 1 - - # Texture handling - size = struct.unpack(">> object = Object() - - Sample implementations: objects.py - Tests: tests/test_objects.py - """ - - def __init__(self, LocalID = None, State = None, FullID = None, CRC = None, PCode = None, Material = None, ClickAction = None, Scale = None, ObjectData = None, ParentID = None, UpdateFlags = None, PathCurve = None, ProfileCurve = None, PathBegin = None, PathEnd = None, PathScaleX = None, PathScaleY = None, PathShearX = None, PathShearY = None, PathTwist = None, PathTwistBegin = None, PathRadiusOffset = None, PathTaperX = None, PathTaperY = None, PathRevolutions = None, PathSkew = None, ProfileBegin = None, ProfileEnd = None, ProfileHollow = None, TextureEntry = None, TextureAnim = None, NameValue = None, Data = None, Text = None, TextColor = None, MediaURL = None, PSBlock = None, ExtraParams = None, Sound = None, OwnerID = None, Gain = None, Flags = None, Radius = None, JointType = None, JointPivot = None, JointAxisOrAnchor = None, FootCollisionPlane = None, Position = None, Velocity = None, Acceleration = None, Rotation = None, AngularVelocity = None): - """ set up the object attributes """ - - self.LocalID = LocalID # U32 - self.State = State # U8 - self.FullID = FullID # LLUUID - self.CRC = CRC # U32 // TEMPORARY HACK FOR JAMES - self.PCode = PCode # U8 - self.Material = Material # U8 - self.ClickAction = ClickAction # U8 - self.Scale = Scale # LLVector3 - self.ObjectData = ObjectData # Variable 1 - self.ParentID = ParentID # U32 - self.UpdateFlags = UpdateFlags # U32 // U32, see object_flags.h - self.PathCurve = PathCurve # U8 - self.ProfileCurve = ProfileCurve # U8 - self.PathBegin = PathBegin # U16 // 0 to 1, quanta = 0.01 - self.PathEnd = PathEnd # U16 // 0 to 1, quanta = 0.01 - self.PathScaleX = PathScaleX # U8 // 0 to 1, quanta = 0.01 - self.PathScaleY = PathScaleY # U8 // 0 to 1, quanta = 0.01 - self.PathShearX = PathShearX # U8 // -.5 to .5, quanta = 0.01 - self.PathShearY = PathShearY # U8 // -.5 to .5, quanta = 0.01 - self.PathTwist = PathTwist # S8 // -1 to 1, quanta = 0.01 - self.PathTwistBegin = PathTwistBegin # S8 // -1 to 1, quanta = 0.01 - self.PathRadiusOffset = PathRadiusOffset # S8 // -1 to 1, quanta = 0.01 - self.PathTaperX = PathTaperX # S8 // -1 to 1, quanta = 0.01 - self.PathTaperY = PathTaperY # S8 // -1 to 1, quanta = 0.01 - self.PathRevolutions = PathRevolutions # U8 // 0 to 3, quanta = 0.015 - self.PathSkew = PathSkew # S8 // -1 to 1, quanta = 0.01 - self.ProfileBegin = ProfileBegin # U16 // 0 to 1, quanta = 0.01 - self.ProfileEnd = ProfileEnd # U16 // 0 to 1, quanta = 0.01 - self.ProfileHollow = ProfileHollow # U16 // 0 to 1, quanta = 0.01 - self.TextureEntry = TextureEntry # Variable 2 - self.TextureAnim = TextureAnim # Variable 1 - self.NameValue = NameValue # Variable 2 - self.Data = Data # Variable 2 - self.Text = Text # Variable 1 // llSetText() hovering text - self.TextColor = TextColor # Fixed 4 // actually, a LLColor4U - self.MediaURL = MediaURL # Variable 1 // URL for web page, movie, etc. - self.PSBlock = PSBlock # Variable 1 - self.ExtraParams = ExtraParams # Variable 1 - self.Sound = Sound # LLUUID - self.OwnerID = OwnerID # LLUUID // HACK object's owner id, only set if non-null sound, for muting - self.Gain = Gain # F32 - self.Flags = Flags # U8 - self.Radius = Radius # F32 // cutoff radius - self.JointType = JointType # U8 - self.JointPivot = JointPivot # LLVector3 - self.JointAxisOrAnchor = JointAxisOrAnchor # LLVector3 - - # from ObjectUpdateCompressed - self.FootCollisionPlane = FootCollisionPlane - self.Position = Position - self.Velocity = Velocity - self.Acceleration = Acceleration - self.Rotation = Rotation - self.AngularVelocity = AngularVelocity - - # from ObjectProperties - self.CreatorID = None - self.GroupID = None - self.CreationDate = None - self.BaseMask = None - self.OwnerMask = None - self.GroupMask = None - self.EveryoneMask = None - self.NextOwnerMask = None - self.OwnershipCost = None - # TaxRate - self.SaleType = None - self.SalePrice = None - self.AggregatePerms = None - self.AggregatePermTextures = None - self.AggregatePermTexturesOwner = None - self.Category = None - self.InventorySerial = None - self.ItemID = None - self.FolderID = None - self.FromTaskID = None - self.LastOwnerID = None - self.Name = None - self.Description = None - self.TouchName = None - self.SitName = None - self.TextureID = None - - def update_object_permissions(self, agent, Field, Set, Mask, Override = False): - """ update permissions for a list of objects - - This will update a specific bit to a specific value. - """ - - self.send_ObjectPermissions(agent, agent.agent_id, agent.session_id, Field, Set, Mask, Override) - - def send_ObjectPermissions(self, agent, AgentID, SessionID, Field, Set, Mask, Override): - """ send an ObjectPermissions message to the host simulator""" - - # Todo: ObjectData is variable - # in the future when making this message not be Object() specific - # make it such that one can pass in a dictionary containing - # a list of attributes to build the *[Block()] - - packet = Message('ObjectPermissions', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - Block('HeaderData', - Override = Override), - Block('ObjectData', - ObjectLocalID = self.LocalID, - Field = Field, - Set = Set, - Mask = Mask)) - - agent.region.enqueue_message(packet) - - def set_object_full_permissions(self, agent): - """ - Set Next Owner Permissions Copy, Modify, Transfer - This is also called 'full permissions'. - """ - - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Modify) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer) - - def set_object_copy_mod_permissions(self, agent): - """ - Set Next Owner Permissions to Copy/Mod - This is a common permission set for attachements. - """ - - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Modify) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Transfer) - - def set_object_mod_transfer_permissions(self, agent): - """ - Set Next Owner Permissions to Mod/Transfer - This is a common permission set for clothing. - """ - - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Copy) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Modify) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer) - - def set_object_transfer_only_permissions(self, agent): - """ - Set Next Owner Permissions to Transfer Only - This is the most restrictive set of permissions allowed. - """ - - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Copy) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Modify) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer) - - def set_object_copy_transfer_permissions(self, agent): - """ - Set Next Owner Permissions to Copy/Transfer - """ - - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Modify) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer) - - def set_object_copy_only_permissions(self, agent): - """ - Set Next Owner Permissions to Copy Only - """ - - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Modify) - self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Transfer) - - def set_object_name(self, agent, Name): - """ update the name of an object - - """ - - self.send_ObjectName(agent, agent.agent_id, agent.session_id, {1:[self.LocalID, Name]}) - - def send_ObjectName(self, agent, AgentID, SessionID, LocalID_Name_map): - """ send on ObjectName message to the host simulator - - expects LocalID_Name_map = {1:[LocalID, Name]} - """ - - packet = Message('ObjectName', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - *[Block('ObjectData', - LocalID = LocalID_Name_map[item][0], - Name = LocalID_Name_map[item][1]) for item in LocalID_Name_map]) - - agent.region.enqueue_message(packet) - - def set_object_description(self, agent, Description): - """ update the description of an objects - - """ - - self.send_ObjectDescription(agent, agent.agent_id, agent.session_id, {1:[self.LocalID, Description]}) - - def send_ObjectDescription(self, agent, AgentID, SessionID, LocalID_Description_map): - """ send on ObjectDescription message to the host simulator - - expects LocalID_Description_map = {1:[LocalID, Description]} - """ - - packet = Message('ObjectDescription', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - *[Block('ObjectData', - LocalID = LocalID_Description_map[item][0], - Description = LocalID_Description_map[item][1]) for item in LocalID_Description_map]) - - agent.region.enqueue_message(packet) - - def derez(self, agent, destination, destinationID, transactionID, GroupID, PacketCount = 1, PacketNumber = 0): - """ derez an object, specifying the destination """ - - self.send_DerezObject(agent, agent.agent_id, agent.session_id, GroupID, destination, destinationID, transactionID, PacketCount, PacketNumber, self.LocalID) - - def send_DerezObject(self, agent, AgentID, SessionID, GroupID, Destination, DestinationID, TransactionID, PacketCount, PacketNumber, ObjectLocalID): - """ send a DerezObject message to the host simulator """ - - packet = Message('DerezObject', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - Block('AgentBlock', - GroupID = GroupID, - Destination = Destination, - DestinationID = DestinationID, - TransactionID = TransactionID, - PacketCount = PacketCount, - PacketNumber = PacketNumber), - Block('ObjectData', - ObjectLocalID = ObjectLocalID)) - - agent.region.enqueue_message(packet) - - def take(self, agent): - """ take object into inventory """ - - parent_folder = self.FolderID - - # Check if we have inventory turned on - if not(parent_folder and agent.settings.ENABLE_INVENTORY_MANAGEMENT): - log(WARNING,"Inventory not available, please enable settings.ENABLE_INVENTORY_MANAGEMENT") - return - - if not(parent_folder): - # locate Object folder - objects_folder = [ folder for folder in agent.inventory.folders if folder.Name == 'Objects' ] - if objects_folder: - parent_folder = objects_folder[0].FolderID - else: - log(ERROR,"Unable to locate top-level Objects folder to take item into inventory.") - return - - self.derez(agent, 4, parent_folder, uuid.uuid4(), agent.ActiveGroupID) - - def select(self, agent): - """ select an object - - """ - - self.send_ObjectSelect(agent, agent.agent_id, agent.session_id, [self.LocalID]) - - def send_ObjectSelect(self, agent, AgentID, SessionID, ObjectLocalIDs): - """ send an ObjectSelect message to the agent's host simulator - - expects a list of ObjectLocalIDs """ - - packet = Message('ObjectSelect', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - *[Block('ObjectData', - ObjectLocalID = ObjectLocalID) for ObjectLocalID in ObjectLocalIDs]) - - agent.region.enqueue_message(packet) - - def deselect(self, agent): - """ deselect an object - - """ - - 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 - - expects a list of ObjectLocalIDs """ - - packet = Message('ObjectDeselect', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - *[Block('ObjectData', - ObjectLocalID = ObjectLocalID) for ObjectLocalID in ObjectLocalIDs]) - - agent.region.enqueue_message(packet) - - - def _update_properties(self, properties): - """ takes a dictionary of attribute:value and makes it so """ - - for attribute in properties.keys(): - - setattr(self, attribute, properties[attribute]) - - - - diff --git a/pyogp/lib/base/parcel.py b/pyogp/lib/base/parcel.py deleted file mode 100644 index 6a227ae..0000000 --- a/pyogp/lib/base/parcel.py +++ /dev/null @@ -1,1157 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python modules -from logging import getLogger, WARNING, INFO, DEBUG - -# related -from eventlet import api - -# pyogp -from pyogp.lib.base.datatypes import UUID -from pyogp.lib.base.exc import NotImplemented -from pyogp.lib.base.datamanager import DataManager - -# pyogp messaging -from pyogp.lib.base.message.message import Message, Block -from pyogp.lib.base.message.message_handler import MessageHandler - -# utilities -from pyogp.lib.base.utilities.helpers import Helpers - -# initialize logging -logger = getLogger('pyogp.lib.base.parcel') -log = logger.log - -# ToDo: unhandled related messages -# ForceObjectSelect -# ParcelBuyPass -# ParcelAccessListUpdate -# ParcelDwellRequest -# ParcelDwellReply -# ParcelGodMarkAsContent -# ViewerStartAuction -# ParcelObjectOwnersRequest -# ParcelObjectOwnersReply - -# non client oriented related messages -# #RequestParcelTransfer (sim->dataserver) -# #UpdateParcel (sim->dataserver) -# #RemoveParcel (sim->dataserver) -# #MergeParcel (sim->dataserver) -# #LogParcelChanges (sim->dataserver) -# #CheckParcelSales (sim->dataserver) -# #ParcelSales (dataserver->sim) -# #StartAuction (sim->dataserver) -# #ConfirmAuctionStart (dataserver->sim) -# #CompleteAuction (sim->dataserver) -# #CancelAuction (sim->dataserver) -# #CheckParcelAuctions (sim->dataserver) -# #ParcelAuctions (dataserver->sim) - -class ParcelManager(DataManager): - """ a parcel manager, generally used as an attribute of a region """ - - def __init__(self, agent = None, region = None, settings = None, message_handler = None, events_handler = None): - """ initialize the parcel manager """ - super(ParcelManager, self).__init__(agent, settings) - - self.region = region - - self.message_handler = message_handler - - # otherwise, let's just use our own - # unused atm - #if events_handler != None: - # self.events_handler = events_handler - #else: - # self.events_handler = AppEventsHandler() - - self.helpers = Helpers() - - # initialize the parcel storage container - self.parcels = [] - # initialize the parcel overlay storage container - self.parcel_overlay = {} - - # initialize map (x, y) with 0; filled in as parcel properties are received - self.parcel_map = [[0 for _ in range(64)] for _ in range(64)] - self.parcel_map_full = False - - if self.settings.LOG_VERBOSE: log(DEBUG, "Initializing the parcel manager in region %s." % (self.region.SimName)) - - def enable_callbacks(self): - """enable the callback handlers for this ParcelManager""" - if self.message_handler == None: - self.message_handler = MessageHandler() - - self.onParcelOverlay_received = self.message_handler.register('ParcelOverlay') - self.onParcelOverlay_received.subscribe(self.onParcelOverlay) - - self.onParcelProperties_received = self.message_handler.register('ParcelProperties') - self.onParcelProperties_received.subscribe(self.onParcelProperties) - - self.onParcelPropertiesUpdate_received = self.message_handler.register('ParcelPropertiesUpdate') - self.onParcelPropertiesUpdate_received.subscribe(self.onParcelPropertiesUpdate) - - self.onParcelInfoReply_received = self.message_handler.register('ParcelInfoReply') - self.onParcelInfoReply_received.subscribe(self.onParcelInfoReply) - - def onParcelOverlay(self, packet): - """ parse and handle an incoming ParcelOverlay packet - - Currently, we store this data in the ParcelManager.packet_overlay dictionary - as parcel_overlay[sequence_id] = data (unparsed binary) - """ - - # unpack the data - sequence_id = packet.blocks['ParcelData'][0].get_variable('SequenceID').data - data = packet.blocks['ParcelData'][0].get_variable('Data').data - - # store the data - # ToDo: make sense of the binary blob in data - self.parcel_overlay[sequence_id] = data - - def onParcelProperties(self, packet): - """ parse and handle an incoming ParcelProperties packet. Parse and serialize the info into a Parcel() representation, then store it (or replace the stored version) """ - - parcel_info = {} - - parcel_info['RequestResult'] = packet.blocks['ParcelData'][0].get_variable('RequestResult').data - parcel_info['SequenceID'] = packet.blocks['ParcelData'][0].get_variable('SequenceID').data - parcel_info['SnapSelection'] = packet.blocks['ParcelData'][0].get_variable('SnapSelection').data - parcel_info['SelfCount'] = packet.blocks['ParcelData'][0].get_variable('SelfCount').data - parcel_info['OtherCount'] = packet.blocks['ParcelData'][0].get_variable('OtherCount').data - parcel_info['PublicCount'] = packet.blocks['ParcelData'][0].get_variable('PublicCount').data - parcel_info['LocalID'] = packet.blocks['ParcelData'][0].get_variable('LocalID').data - parcel_info['OwnerID'] = packet.blocks['ParcelData'][0].get_variable('OwnerID').data - parcel_info['IsGroupOwned'] = packet.blocks['ParcelData'][0].get_variable('IsGroupOwned').data - parcel_info['AuctionID'] = packet.blocks['ParcelData'][0].get_variable('AuctionID').data - parcel_info['ClaimDate'] = packet.blocks['ParcelData'][0].get_variable('ClaimDate').data - parcel_info['ClaimPrice'] = packet.blocks['ParcelData'][0].get_variable('ClaimPrice').data - parcel_info['RentPrice'] = packet.blocks['ParcelData'][0].get_variable('RentPrice').data - parcel_info['AABBMin'] = packet.blocks['ParcelData'][0].get_variable('AABBMin').data - parcel_info['AABBMax'] = packet.blocks['ParcelData'][0].get_variable('AABBMax').data - parcel_info['Bitmap'] = packet.blocks['ParcelData'][0].get_variable('Bitmap').data - parcel_info['Area'] = packet.blocks['ParcelData'][0].get_variable('Area').data - parcel_info['Status'] = packet.blocks['ParcelData'][0].get_variable('Status').data - parcel_info['SimWideMaxPrims'] = packet.blocks['ParcelData'][0].get_variable('SimWideMaxPrims').data - parcel_info['SimWideTotalPrims'] = packet.blocks['ParcelData'][0].get_variable('SimWideTotalPrims').data - parcel_info['MaxPrims'] = packet.blocks['ParcelData'][0].get_variable('MaxPrims').data - parcel_info['TotalPrims'] = packet.blocks['ParcelData'][0].get_variable('TotalPrims').data - parcel_info['OwnerPrims'] = packet.blocks['ParcelData'][0].get_variable('OwnerPrims').data - parcel_info['GroupPrims'] = packet.blocks['ParcelData'][0].get_variable('GroupPrims').data - parcel_info['OtherPrims'] = packet.blocks['ParcelData'][0].get_variable('OtherPrims').data - parcel_info['SelectedPrims'] = packet.blocks['ParcelData'][0].get_variable('SelectedPrims').data - parcel_info['ParcelPrimBonus'] = packet.blocks['ParcelData'][0].get_variable('ParcelPrimBonus').data - parcel_info['OtherCleanTime'] = packet.blocks['ParcelData'][0].get_variable('OtherCleanTime').data - parcel_info['ParcelFlags'] = packet.blocks['ParcelData'][0].get_variable('ParcelFlags').data - parcel_info['SalePrice'] = packet.blocks['ParcelData'][0].get_variable('SalePrice').data - parcel_info['Name'] = packet.blocks['ParcelData'][0].get_variable('Name').data - parcel_info['Desc'] = packet.blocks['ParcelData'][0].get_variable('Desc').data - parcel_info['MusicURL'] = packet.blocks['ParcelData'][0].get_variable('MusicURL').data - parcel_info['MediaURL'] = packet.blocks['ParcelData'][0].get_variable('MediaURL').data - parcel_info['MediaID'] = packet.blocks['ParcelData'][0].get_variable('MediaID').data - parcel_info['MediaAutoScale'] = packet.blocks['ParcelData'][0].get_variable('MediaAutoScale').data - parcel_info['GroupID'] = packet.blocks['ParcelData'][0].get_variable('GroupID').data - parcel_info['PassPrice'] = packet.blocks['ParcelData'][0].get_variable('PassPrice').data - parcel_info['PassHours'] = packet.blocks['ParcelData'][0].get_variable('PassHours').data - parcel_info['Category'] = packet.blocks['ParcelData'][0].get_variable('Category').data - parcel_info['AuthBuyerID'] = packet.blocks['ParcelData'][0].get_variable('AuthBuyerID').data - parcel_info['SnapshotID'] = packet.blocks['ParcelData'][0].get_variable('SnapshotID').data - parcel_info['UserLocation'] = packet.blocks['ParcelData'][0].get_variable('UserLocation').data - parcel_info['UserLookAt'] = packet.blocks['ParcelData'][0].get_variable('UserLookAt').data - parcel_info['LandingType'] = packet.blocks['ParcelData'][0].get_variable('LandingType').data - parcel_info['RegionPushOverride'] = packet.blocks['ParcelData'][0].get_variable('RegionPushOverride').data - parcel_info['RegionDenyAnonymous'] = packet.blocks['ParcelData'][0].get_variable('RegionDenyAnonymous').data - parcel_info['RegionDenyIdentified'] = packet.blocks['ParcelData'][0].get_variable('RegionDenyIdentified').data - parcel_info['RegionDenyTransacted'] = packet.blocks['ParcelData'][0].get_variable('RegionDenyTransacted').data - parcel_info['RegionDenyAgeUnverified'] = packet.blocks['AgeVerificationBlock'][0].get_variable('RegionDenyAgeUnverified').data - - self._store_parcel_properties(parcel_info) - - def onParcelPropertiesUpdate(self, packet): - """ parse and handle an incoming ParcelPropertiesUpdate packet. parse the data into a dictionary and pass the blob to the Parcel() instance for self handling """ - - parcel_update = {} - - parcel_update['LocalID'] = packet.blocks['ParcelData'][0].get_variable('LocalID').data - parcel_update['Flags'] = packet.blocks['ParcelData'][0].get_variable('Flags').data - parcel_update['ParcelFlags'] = packet.blocks['ParcelData'][0].get_variable('ParcelFlags').data - parcel_update['SalePrice'] = packet.blocks['ParcelData'][0].get_variable('SalePrice').data - parcel_update['Name'] = packet.blocks['ParcelData'][0].get_variable('Name').data - parcel_update['Desc'] = packet.blocks['ParcelData'][0].get_variable('Desc').data - parcel_update['MusicURL'] = packet.blocks['ParcelData'][0].get_variable('MusicURL').data - parcel_update['MediaURL'] = packet.blocks['ParcelData'][0].get_variable('MediaURL').data - parcel_update['MediaID'] = packet.blocks['ParcelData'][0].get_variable('MediaID').data - parcel_update['MediaAutoScale'] = packet.blocks['ParcelData'][0].get_variable('MediaAutoScale').data - parcel_update['GroupID'] = packet.blocks['ParcelData'][0].get_variable('GroupID').data - parcel_update['PassPrice'] = packet.blocks['ParcelData'][0].get_variable('PassPrice').data - parcel_update['PassHours'] = packet.blocks['ParcelData'][0].get_variable('PassHours').data - parcel_update['Category'] = packet.blocks['ParcelData'][0].get_variable('Category').data - parcel_update['AuthBuyerID'] = packet.blocks['ParcelData'][0].get_variable('AuthBuyerID').data - parcel_update['SnapshotID'] = packet.blocks['ParcelData'][0].get_variable('SnapshotID').data - parcel_update['UserLocation'] = packet.blocks['ParcelData'][0].get_variable('UserLocation').data - parcel_update['UserLookAt'] = packet.blocks['ParcelData'][0].get_variable('UserLookAt').data - parcel_update['LandingType'] = packet.blocks['ParcelData'][0].get_variable('LandingType').data - - self._update_parcel_properties(parcel_update) - - def _store_parcel_properties(self, parcel_info): - """ store a representation of a parcel """ - - # update the attributes of an existing parcel list member, else, append - - index = [self.parcels.index(parcel) for parcel in self.parcels if parcel.LocalID == parcel_info['LocalID']] - - if index != []: - - self._update_parcel_properties(parcel_info) - - if self.settings.LOG_VERBOSE: log(DEBUG, 'Updating a stored parcel: %s in region \'%s\'' % (parcel_info['LocalID'], self.region.SimName)) - - else: - - new_parcel = Parcel(self.region, self.agent, RequestResult = parcel_info['RequestResult'], SequenceID = parcel_info['SequenceID'], SnapSelection = parcel_info['SnapSelection'], SelfCount = parcel_info['SelfCount'], OtherCount = parcel_info['OtherCount'], PublicCount = parcel_info['PublicCount'], LocalID = parcel_info['LocalID'], OwnerID = parcel_info['OwnerID'], IsGroupOwned = parcel_info['IsGroupOwned'], AuctionID = parcel_info['AuctionID'], ClaimDate = parcel_info['ClaimDate'], ClaimPrice = parcel_info['ClaimPrice'], RentPrice = parcel_info['RentPrice'], AABBMin = parcel_info['AABBMin'], AABBMax = parcel_info['AABBMax'], Bitmap = parcel_info['Bitmap'], Area = parcel_info['Area'], Status = parcel_info['Status'], SimWideMaxPrims = parcel_info['SimWideMaxPrims'], SimWideTotalPrims = parcel_info['SimWideTotalPrims'], MaxPrims = parcel_info['MaxPrims'], TotalPrims = parcel_info['TotalPrims'], OwnerPrims = parcel_info['OwnerPrims'], GroupPrims = parcel_info['GroupPrims'], OtherPrims = parcel_info['OtherPrims'], SelectedPrims = parcel_info['SelectedPrims'], ParcelPrimBonus = parcel_info['ParcelPrimBonus'], OtherCleanTime = parcel_info['OtherCleanTime'], ParcelFlags = parcel_info['ParcelFlags'], SalePrice = parcel_info['SalePrice'], Name = parcel_info['Name'], Desc = parcel_info['Desc'], MusicURL = parcel_info['MusicURL'], MediaURL = parcel_info['MediaURL'], MediaID = parcel_info['MediaID'], MediaAutoScale = parcel_info['MediaAutoScale'], GroupID = parcel_info['GroupID'], PassPrice = parcel_info['PassPrice'], PassHours = parcel_info['PassHours'], Category = parcel_info['Category'], AuthBuyerID = parcel_info['AuthBuyerID'], SnapshotID = parcel_info['SnapshotID'], UserLocation = parcel_info['UserLocation'], UserLookAt = parcel_info['UserLookAt'], LandingType = parcel_info['LandingType'], RegionPushOverride = parcel_info['RegionPushOverride'], RegionDenyAnonymous = parcel_info['RegionDenyAnonymous'], RegionDenyIdentified = parcel_info['RegionDenyIdentified'], RegionDenyTransacted = parcel_info['RegionDenyTransacted'], RegionDenyAgeUnverified = parcel_info['RegionDenyAgeUnverified'], settings = self.settings) - - self.parcels.append(new_parcel) - self._update_parcel_map(new_parcel) - - if self.settings.LOG_VERBOSE: log(DEBUG, 'Stored a new parcel: %s in region \'%s\'' % (new_parcel.LocalID, self.region.SimName)) - - def _update_parcel_properties(self, parcel_properties): - """ update a stored parcel's properties. finds the stored parcel and passes it a dictionary to process """ - - parcels_found = [] - - if parcel_properties.has_key('LocalID'): - - LocalID = parcel_properties['LocalID'] - - parcels_found = [parcel for parcel in self.parcels if str(parcel.LocalID) == str(LocalID)] - - if len(parcels_found) == 0: - - log(INFO, "Received ParcelPropertiesUpdate for parcel we do not know about yet. Storing a partial representation.") - - new_parcel = Parcel(self.region, self.agent, LocalID = parcel_properties['LocalID'], Flags = parcel_properties['Flags'], ParcelFlags = parcel_properties['ParcelFlags'], SalePrice = parcel_properties['SalePrice'], Name = parcel_properties['Name'], Desc = parcel_properties['Desc'], MusicURL = parcel_properties['MusicURL'], MediaURL = parcel_properties['MediaURL'], MediaID = parcel_properties['MediaID'], MediaAutoScale = parcel_properties['MediaAutoScale'], GroupID = parcel_properties['GroupID'], PassPrice = parcel_properties['PassPrice'], PassHours = parcel_properties['PassHours'], Category = parcel_properties['Category'], AuthBuyerID = parcel_properties['AuthBuyerID'], SnapshotID = parcel_properties['SnapshotID'], UserLocation = parcel_properties['UserLocation'], UserLookAt = parcel_properties['UserLookAt'], LandingType = parcel_properties['LandingType'], settings = self.settings) - - self._store_parcel(new_parcel) - - elif len(parcels_found) == 1: - - parcel = parcels_found[0] - - parcel._update_properties(parcel_properties) - - elif parcel_properties.has_key('ParcelID'): - - ParcelID = parcel_properties['ParcelID'] - - parcels_found = [parcel for parcel in self.parcels if str(parcel.ParcelID) == str(ParcelID)] - - if len(parcels_found) == 0: - - log(INFO, "Received ParcelPropertiesUpdate for parcel we do not know about yet. Storing a partial representation.") - - new_parcel = Parcel(self.region, self.agent, LocalID = parcel_properties['LocalID'], Flags = parcel_properties['Flags'], ParcelFlags = parcel_properties['ParcelFlags'], SalePrice = parcel_properties['SalePrice'], Name = parcel_properties['Name'], Desc = parcel_properties['Desc'], MusicURL = parcel_properties['MusicURL'], MediaURL = parcel_properties['MediaURL'], MediaID = parcel_properties['MediaID'], MediaAutoScale = parcel_properties['MediaAutoScale'], GroupID = parcel_properties['GroupID'], PassPrice = parcel_properties['PassPrice'], PassHours = parcel_properties['PassHours'], Category = parcel_properties['Category'], AuthBuyerID = parcel_properties['AuthBuyerID'], SnapshotID = parcel_properties['SnapshotID'], UserLocation = parcel_properties['UserLocation'], UserLookAt = parcel_properties['UserLookAt'], LandingType = parcel_properties['LandingType'], settings = self.settings) - - self._store_parcel(new_parcel) - - elif len(parcels_found) == 1: - - parcel = parcels_found[0] - - parcel._update_properties(parcel_properties) - - def _update_parcel_map(self, parcel): - """Use the parcel's bitmap to update the manager's (x,y) to LocalID mapping""" - - full = True - - for x in range(64): - for y in range(64): - - index = x + (64 * y) - byte = index >> 3 - mask = 1 << (index % 8) - - # *TODO: Bitmap should be stored as a byte array, not a string - if ord(parcel.Bitmap[byte]) & mask: - self.parcel_map[x][y] = parcel.LocalID - - full = full and (self.parcel_map[x][y] != 0) - - self.parcel_map_full = full - - - def get_parcel_by_id(self, local_id): - """Returns a parcel if info has been received, None otherwise.""" - for parcel in self.parcels: - if parcel.LocalID == local_id: - return parcel - return None - - def get_parcel_id_by_location(self, local_x, local_y): - """Returns a parcel's local id if info has been received, 0 otherwise.""" - return self.parcel_map[ int(local_x)/4 ][ int(local_y)/4 ] - - def get_parcel_by_location(self, local_x, local_y): - """Returns a parcel if info has been received, None otherwise.""" - return self.get_parcel_by_id( self.get_parcel_id_by_location(local_x, local_y) ) - - def get_current_parcel(self): - """Returns the agent's current parcel if info has been received, None otherwise.""" - return self.get_parcel_by_location( self.agent.Position.X, self.agent.Position.Y ) - - - def request_estate_covenant(self, ): - """ request the estate covenant (for the current estate)""" - - self.onEstateCovenantReply_received = self.message_handler.register('EstateCovenantReply') - self.onEstateCovenantReply_received.subscribe(self.onEstateCovenantReply) - - self.sendEstateCovenantRequest(self.agent.agent_id, self.agent.session_id) - - def sendEstateCovenantRequest(self, agent_id, session_id): - """ send an EstateCovenantRequest message to the host simulator """ - - packet = Message('EstateCovenantRequest', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id)) - - self.region.enqueue_message(packet) - - def onEstateCovenantReply(self, packet): - """ parse and handle an EstateCovenantReply packet """ - - try: - - self.onEstateCovenantReply_received.unsubscribe(self.onEstateCovenantReply) - - except AttributeError: - - pass - - CovenantID = packet.blocks['Data'][0].get_variable('CovenantID').data - CovenantTimestamp = packet.blocks['Data'][0].get_variable('CovenantTimestamp').data - EstateName = packet.blocks['Data'][0].get_variable('EstateName').data - EstateOwnerID = packet.blocks['Data'][0].get_variable('EstateOwnerID').data - - log(INFO, "Received EstateCovenantReply for estate name %s with a CovenantID of %s." % (EstateName, CovenantID)) - - # storing this data as a dict in the parcel manager until we have something better to do with it - self.estatecovenantreply = {'CovenantID': CovenantID, 'CovenantTimestamp': CovenantTimestamp, 'EstateName': EstateName, 'EstateOwnerID': EstateOwnerID} - - def sendParcelPropertiesRequest(self, agent_id, session_id, SequenceID, West, South, East, North, SnapSelection): - """ sends a ParcelPropertiesRequest message to the host simulator """ - - packet = Message('ParcelPropertiesRequest', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id), - Block('ParcelData', - SequenceID = SequenceID, - West = West, - South = South, - East = East, - North = North, - SnapSelection = SnapSelection)) - - self.region.enqueue_message(packet) - - def sendParcelPropertiesRequestByID(self, agent_id, session_id, SequenceID, LocalID): - """ sends a ParcelPropertiesRequestByID packet """ - - packet = Message('ParcelPropertiesRequestByID', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id), - Block('ParcelData', - SequenceID = SequenceID, - LocalID = LocalID)) - - self.region.enqueue_message(packet) - - def request_parcel_info(self, parcel_id): - """ request information for a parcel by id """ - - if type(parcel_id) == str: - - try: - - parcel_id = UUID(parcel_id) - - except ValueError: - - log(WARNING, 'Parcel_id passed to request_parcel_info must but a valid UUID or string representation of a uuid. %s was passed in' % (parcel_id)) - - return - - elif not isinstance(parcel_id, UUID): - - log(WARNING, 'Parcel_id passed to request_parcel_info must but a valid UUID or string representation of a uuid. %s was passed in' % (parcel_id)) - - return - - self.sendParcelInfoRequest(self.agent.agent_id, self.agent.session_id, parcel_id) - - def sendParcelInfoRequest(self, agent_id, session_id, parcel_id): - """ send a ParcelInfoRequest packet for the specified parcel_id """ - - packet = Message('ParcelInfoRequest', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id), - Block('Data', - ParcelID = parcel_id)) - - self.region.enqueue_message(packet) - - def onParcelInfoReply(self, packet): - """ parse and handle a ParcelInfoReply packet """ - - parcel_info = {} - - parcel_info['ParcelID'] = packet.blocks['Data'][0].get_variable('ParcelID').data - parcel_info['OwnerID'] = packet.blocks['Data'][0].get_variable('OwnerID').data - parcel_info['Name'] = packet.blocks['Data'][0].get_variable('Name').data - parcel_info['Desc'] = packet.blocks['Data'][0].get_variable('Desc').data - parcel_info['ActualArea'] = packet.blocks['Data'][0].get_variable('ActualArea').data - parcel_info['BillableArea'] = packet.blocks['Data'][0].get_variable('BillableArea').data - parcel_info['Flags'] = packet.blocks['Data'][0].get_variable('Flags').data - parcel_info['GlobalX'] = packet.blocks['Data'][0].get_variable('GlobalX').data - parcel_info['GlobalY'] = packet.blocks['Data'][0].get_variable('GlobalY').data - parcel_info['GlobalZ'] = packet.blocks['Data'][0].get_variable('GlobalZ').data - parcel_info['SimName'] = packet.blocks['Data'][0].get_variable('SimName').data - parcel_info['SnapshotID'] = packet.blocks['Data'][0].get_variable('SnapshotID').data - parcel_info['Dwell'] = packet.blocks['Data'][0].get_variable('Dwell').data - parcel_info['SalePrice'] = packet.blocks['Data'][0].get_variable('SalePrice').data - parcel_info['AuctionID'] = packet.blocks['Data'][0].get_variable('AuctionID').data - - self._update_parcel_properties(parcel_info) - - def request_current_parcel_properties(self, refresh = False): - """ request the properties of the parcel the agent currently inhabits """ - - x = self.agent.Position.X - y = self.agent.Position.Y - - if refresh or self.get_parcel_id_by_location(x, y) == 0: - self.sendParcelPropertiesRequest(self.agent.agent_id, self.agent.session_id, -50000, x, y, x, y, False) - - def request_all_parcel_properties(self, delay = 0.5, refresh = False): - """ request the properties of all of the parcels on the current region. The delay parameter is a sleep between the send of each packet request; if refresh, current data will be discarded before requesting. If refresh is not True, data will not be re-requested for region locations already queried. """ - - # spawn a coroutine so this is non blocking - api.spawn(self.__request_all_parcel_properties, delay, refresh) - - def __request_all_parcel_properties(self, delay = 1, refresh = False): - """ request the properties of all of the parcels on the current region """ - - if refresh: - self.parcel_map = [[0 for _ in range(64)] for _ in range(64)] - self.parcel_map_full = False - - # minimum parcel size is 4x4m (16sq) - # ugh this is a wretched way to request parcel info, but it is what it is - for y in range(64): - for x in range(64): - - if self.parcel_map[x][y] == 0: - # Target: center of 4m by 4m parcel - tx = x * 4 + 2 - ty = y * 4 + 2 - self.sendParcelPropertiesRequest(self.agent.agent_id, self.agent.session_id, -50000, tx, ty, tx, ty, False) - - api.sleep(delay) - - def return_parcel_objects(self, ): - """ return the specified objects for the specified parcel """ - - pass - - ''' - // ParcelReturnObjects - // viewer -> sim - // reliable - { - ParcelReturnObjects Low 199 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { ReturnType U32 } - } - { - TaskIDs Variable - { TaskID LLUUID } - } - { - OwnerIDs Variable - { OwnerID LLUUID } - } - } - ''' - - def disable_objects(self, ): - """ set objects nonphysical and disable scripts for the specified parcel """ - - pass - - ''' - // Disable makes objects nonphysical and turns off their scripts. - // ParcelDisableObjects - // viewer -> sim - // reliable - { - ParcelDisableObjects Low 201 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { ReturnType U32 } - } - { - TaskIDs Variable - { TaskID LLUUID } - } - { - OwnerIDs Variable - { OwnerID LLUUID } - } - } - ''' - - def sendParcelDisableObjects(self, ): - """ send a ParcelDisableObjects packet """ - - pass - - ''' - // Disable makes objects nonphysical and turns off their scripts. - // ParcelDisableObjects - // viewer -> sim - // reliable - { - ParcelDisableObjects Low 201 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { ReturnType U32 } - } - { - TaskIDs Variable - { TaskID LLUUID } - } - { - OwnerIDs Variable - { OwnerID LLUUID } - } - } - ''' - - def join_parcels(self, ): - """ joins the specified parcels """ - - pass - - ''' - // ParcelJoin - Take all parcels which are owned by agent and inside - // rectangle, and make them 1 parcel if they all are leased. - // viewer -> sim - // reliable - { - ParcelJoin Low 210 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { West F32 } - { South F32 } - { East F32 } - { North F32 } - } - } - - ''' - - def sendParcelJoin(self, ): - """ send a ParcelJoin packet """ - - pass - - ''' - // ParcelJoin - Take all parcels which are owned by agent and inside - // rectangle, and make them 1 parcel if they all are leased. - // viewer -> sim - // reliable - { - ParcelJoin Low 210 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { West F32 } - { South F32 } - { East F32 } - { North F32 } - } - } - ''' - - def divide_parcel(self, ): - """ divide the selection into a new parcel """ - - pass - - ''' - // ParcelDivide - // If the selection is a subsection of exactly one parcel, - // chop out that section and make a new parcel of it. - // viewer -> sim - // reliable - { - ParcelDivide Low 211 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { West F32 } - { South F32 } - { East F32 } - { North F32 } - } - } - ''' - - def sendParcelDivide(self, ): - """ send a ParcelDivide packet """ - - pass - - ''' - // ParcelDivide - // If the selection is a subsection of exactly one parcel, - // chop out that section and make a new parcel of it. - // viewer -> sim - // reliable - { - ParcelDivide Low 211 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { West F32 } - { South F32 } - { East F32 } - { North F32 } - } - } - ''' - - def request_parcel_access_list(self, LocalID, Flags): - """ request an access list for the specified parcel, while enabling a callback handler for the response """ - - self.onParcelAccessListReply_received = self.message_handler.register('ParcelAccessListReply') - self.onParcelAccessListReply_received.subscribe(self.onParcelAccessListReply, LocalID = LocalID) - - self.sendParcelAccessListRequest(self.agent.agent_id, self.agent.session_id, LocalID, Flags) - - def sendParcelAccessListRequest(self, agent_id, session_id, LocalID, Flags, SequenceID = -5150): - """ send a ParcelAccessListRequest packet to the host simulator """ - - packet = Message('ParcelAccessListRequest', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id), - Block('Data', - SequenceID = SequenceID, - Flags = Flags, - LocalID = LocalID)) - - self.region.enqueue_message(packet) - - - def onParcelAccessListReply(self, packet): - """ parse and handle a ParcelAccessListReply packet """ - - #self.onParcelAccessListReply_received.unsubscribe(self.onParcelAccessListReply, LocalID = LocalID) - - raise NotImplemented("sendFetchInventoryDescendentsRequest") - - ''' - // sim -> viewer - // ParcelAccessListReply - { - ParcelAccessListReply Low 216 Trusted Zerocoded - { - Data Single - { AgentID LLUUID } - { SequenceID S32 } - { Flags U32 } - { LocalID S32 } - } - { - List Variable - { ID LLUUID } - { Time S32 } // time_t - { Flags U32 } - } - } - ''' - - def request_parcel_dwell(self, LocalID): - """ request dwell for the specified parcel, while enabling a callback handler for the response """ - - self.onParcelDwellReply_received = self.message_handler.register('ParcelDwellReply') - self.onParcelDwellReply_received.subscribe(self.onParcelDwellReply, LocalID = LocalID) - - self.sendParcelDwellRequest(self.agent.agent_id, self.agent.session_id, LocalID) - - def sendParcelDwellRequest(self, agent_id, session_id, LocalID): - """ send a ParcelDwellRequest packet """ - - packet = Message('ParcelDwellRequest', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id), - Block('Data', - LocalID = LocalID, - ParcelID = UUID())) - - self.region.enqueue_message(packet, True) - - def onParcelDwellReply(self, packet, LocalID = None): - """ parse and handle a ParcelDwellReply packet""" - - AgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data - - # log receipt of a packet that was intended to be sent to another agent - # ToDo: should we raise an event in this case? yes.... later - if str(AgentID) != str(self.agent.agent_id): - - log(WARNING, "%s received a packet for the wrong agent_id. Expected: %s Received: %s" % (self.agent.Name(), self.agent.agent_id, AgentID)) - - # get the body of the message - parcel_info = {} - - parcel_info['LocalID'] = packet.blocks['Data'][0].get_variable('LocalID').data - parcel_info['ParcelID'] = packet.blocks['Data'][0].get_variable('ParcelID').data - parcel_info['Dwell'] = packet.blocks['Data'][0].get_variable('Dwell').data - - if LocalID == parcel_info['LocalID']: - - self.onParcelDwellReply_received.unsubscribe(self.onParcelDwellReply, LocalID = LocalID) - - self._update_parcel_properties(parcel_info) - -class Parcel(object): - """ a representation of a parcel """ - - def __init__(self, region, agent, RequestResult = None, SequenceID = None, SnapSelection = None, SelfCount = None, OtherCount = None, PublicCount = None, LocalID = None, OwnerID = None, IsGroupOwned = None, AuctionID = None, ClaimDate = None, ClaimPrice = None, RentPrice = None, AABBMin = None, AABBMax = None, Bitmap = None, Area = None, Status = None, SimWideMaxPrims = None, SimWideTotalPrims = None, MaxPrims = None, TotalPrims = None, OwnerPrims = None, GroupPrims = None, OtherPrims = None, SelectedPrims = None, ParcelPrimBonus = None, OtherCleanTime = None, ParcelFlags = None, SalePrice = None, Name = None, Desc = None, MusicURL = None, MediaURL = None, MediaID = None, MediaAutoScale = None, GroupID = None, PassPrice = None, PassHours = None, Category = None, AuthBuyerID = None, SnapshotID = None, UserLocation = None, UserLookAt = None, LandingType = None, RegionPushOverride = None, RegionDenyAnonymous = None, RegionDenyIdentified = None, RegionDenyTransacted = None, RegionDenyAgeUnverified = None, ParcelID = None, ActualArea = None, BillableArea = None, Flags = None, GlobalX = None, GlobalY = None, GlobalZ = None, SimName = None, Dwell = None, settings = None): - """ initialize a representation of a parcel. the data is currently being populated directly from the ParcelProperties message """ - - # 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.region = region - self.agent = agent - - # mapping values from the ParcelProperties message - # not all properties will ultimately live here, but for now - # mapping all of them will do - - # from ParcelInfoReply - self.ParcelID = ParcelID - self.OwnerID = OwnerID - self.ActualArea = ActualArea - self.BillableArea = BillableArea - self.Flags = Flags - self.GlobalX = GlobalX - self.GlobalY = GlobalY - self.GlobalZ = GlobalZ - self.SimName = SimName - self.Dwell = Dwell - self.AuctionID = AuctionID - - # from ParcelProperties - self.RequestResult = RequestResult - self.SequenceID = SequenceID - self.SnapSelection = SnapSelection - self.SelfCount = SelfCount - self.OtherCount = OtherCount - self.PublicCount = PublicCount - self.LocalID = LocalID - self.OwnerID = OwnerID - self.IsGroupOwned = IsGroupOwned - self.AuctionID = AuctionID - self.ClaimDate = ClaimDate - self.ClaimPrice = ClaimPrice - self.RentPrice = RentPrice - self.AABBMin = AABBMin - self.AABBMax = AABBMax - self.Bitmap = Bitmap - self.Area = Area - self.Status = Status - self.SimWideMaxPrims = SimWideMaxPrims - self.SimWideTotalPrims = SimWideTotalPrims - self.MaxPrims = MaxPrims - self.TotalPrims = TotalPrims - self.OwnerPrims = OwnerPrims - self.GroupPrims = GroupPrims - self.OtherPrims = OtherPrims - self.SelectedPrims = SelectedPrims - self.ParcelPrimBonus = ParcelPrimBonus - self.OtherCleanTime = OtherCleanTime - self.ParcelFlags = ParcelFlags - self.SalePrice = SalePrice - self.Name = Name - self.Desc = Desc - self.MusicURL = MusicURL - self.MediaURL = MediaURL - self.MediaID = MediaID - self.MediaAutoScale = MediaAutoScale - self.GroupID = GroupID - self.PassPrice = PassPrice - self.PassHours = PassHours - self.Category = Category - self.AuthBuyerID = AuthBuyerID - self.SnapshotID = SnapshotID - self.UserLocation = UserLocation - self.UserLookAt = UserLookAt - self.LandingType = LandingType - self.RegionPushOverride = RegionPushOverride - self.RegionDenyAnonymous = RegionDenyAnonymous - self.RegionDenyIdentified = RegionDenyIdentified - self.RegionDenyTransacted = RegionDenyTransacted - self.RegionDenyAgeUnverified = RegionDenyAgeUnverified - - def _update_properties(self, parcel_properties): - """ update a parcel's properties via a dictionary """ - - for attribute in parcel_properties: - - # if self.settings.LOG_VERBOSE: log(DEBUG, "Updating parcel data for %s. %s = %s" % (self, attribute, parcel_properties[attribute])) - - setattr(self, attribute, parcel_properties[attribute]) - - def return_objects(self, ): - """ return the specified objects for this parcel """ - - pass - - ''' - // ParcelReturnObjects - // viewer -> sim - // reliable - { - ParcelReturnObjects Low 199 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { ReturnType U32 } - } - { - TaskIDs Variable - { TaskID LLUUID } - } - { - OwnerIDs Variable - { OwnerID LLUUID } - } - } - ''' - - def set_other_clean_time(self, ): - """ sends a SetOtherCleanTime packet for this parcel """ - - pass - - ''' - // ParcelSetOtherCleanTime - // viewer -> sim - // reliable - { - ParcelSetOtherCleanTime Low 200 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { OtherCleanTime S32 } - } - } - ''' - - def disable_objects(self, ): - """ set objects nonphysical and disable scripts for this parcel """ - - pass - - ''' - // Disable makes objects nonphysical and turns off their scripts. - // ParcelDisableObjects - // viewer -> sim - // reliable - { - ParcelDisableObjects Low 201 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { ReturnType U32 } - } - { - TaskIDs Variable - { TaskID LLUUID } - } - { - OwnerIDs Variable - { OwnerID LLUUID } - } - } - ''' - - def select_objects(self, ): - """ selects the specified objects for this parcel """ - - pass - - ''' - // ParcelSelectObjects - // viewer -> sim - // reliable - { - ParcelSelectObjects Low 202 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { ReturnType U32 } - } - { - ReturnIDs Variable - { ReturnID LLUUID } - } - } - ''' - - def deed_to_group(self, ): - """ deed this parcel to a group """ - - pass - - ''' - // ParcelDeedToGroup - deed a patch of land to a group - // viewer -> sim - // reliable - { - ParcelDeedToGroup Low 207 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - Data Single - { GroupID LLUUID } - { LocalID S32 } // parcel id - } - } - ''' - - def reclaim(self, ): - """ reclaim this parcel""" - - pass - - ''' - // reserved for when island owners force re-claim parcel - { - ParcelReclaim Low 208 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - Data Single - { LocalID S32 } // parcel id - } - } - ''' - - def claim(self, ): - """ change the owner of a parcel """ - - pass - - ''' - // ParcelClaim - change the owner of a patch of land - // viewer -> sim - // reliable - { - ParcelClaim Low 209 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - Data Single - { GroupID LLUUID } - { IsGroupOwned BOOL } - { Final BOOL } // true if buyer is in tier - } - { - ParcelData Variable - { West F32 } - { South F32 } - { East F32 } - { North F32 } - } - } - ''' - - def release(self, ): - """ release this parcel to the public """ - - pass - - ''' - // ParcelRelease - // Release a parcel to public - // viewer -> sim - // reliable - { - ParcelRelease Low 212 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - Data Single - { LocalID S32 } // parcel ID - } - } - ''' - - def buy(self, ): - """ buy this parcel """ - - pass - - ''' - // ParcelBuy - change the owner of a patch of land. - // viewer -> sim - // reliable - { - ParcelBuy Low 213 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - Data Single - { GroupID LLUUID } - { IsGroupOwned BOOL } - { RemoveContribution BOOL } - { LocalID S32 } - { Final BOOL } // true if buyer is in tier - } - { - ParcelData Single - { Price S32 } - { Area S32 } - } - } - ''' - - def godforce_owner(self, ): - """ god force own this parcel """ - - pass - - ''' - // ParcelGodForceOwner Unencoded - { - ParcelGodForceOwner Low 214 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - Data Single - { OwnerID LLUUID } - { LocalID S32 } // parcel ID - } - } - ''' - - diff --git a/pyogp/lib/base/permissions.py b/pyogp/lib/base/permissions.py deleted file mode 100644 index 0d4f6cc..0000000 --- a/pyogp/lib/base/permissions.py +++ /dev/null @@ -1,65 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# standard python libs -from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG - -# pyogp - -# pyogp messaging - -# initialize logging -logger = getLogger('pyogp.lib.base.permissions') -log = logger.log - -class PermissionsMask(object): - """ permissions flags mappings """ - - # the types of permissions - Transfer = 1 << 13 # 0x00002000 - Modify = 1 << 14 # 0x00004000 - Copy = 1 << 15 # 0x00008000 - Move = 1 << 19 # 0x00080000 - _None = 0 # 0x00000000 - All = 0x7FFFFFFF - # Reserved - #Unrestricted = Modify | Copy | Transfer - -class PermissionsTarget(object): - """ who the permissions apply to """ - - Base = 0x01 - Owner = 0x02 - Group = 0x04 - Everyone = 0x08 - NextOwner = 0x10 - -class Permissions(object): - """ class representing the permissions of an object or inventory item """ - - def __init__(self, BaseMask = None, OwnerMask = None, GroupMask = None, EveryoneMask = None, NextOwnerMask = None): - """ store the values of the various targets permissions """ - - self.BaseMask = BaseMask - self.OwnerMask = OwnerMask - self.GroupMask = GroupMask - self.EveryoneMask = EveryoneMask - self.NextOwnerMask = NextOwnerMask - - - diff --git a/pyogp/lib/base/region.py b/pyogp/lib/base/region.py deleted file mode 100644 index f863dcc..0000000 --- a/pyogp/lib/base/region.py +++ /dev/null @@ -1,627 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -# std lib -from logging import getLogger, ERROR, INFO, DEBUG -import time - -# related -from indra.base import llsd -from eventlet import api - -# pyogp -from pyogp.lib.base.caps import Capability -from pyogp.lib.base.network.stdlib_client import StdLibClient, HTTPError -from pyogp.lib.base.exc import ResourceNotFound, ResourceError, RegionSeedCapNotAvailable, RegionMessageError -from pyogp.lib.base.settings import Settings -from pyogp.lib.base.utilities.helpers import Helpers -from pyogp.lib.base.event_queue import EventQueueClient -from pyogp.lib.base.objects import ObjectManager -from pyogp.lib.base.datatypes import UUID -from pyogp.lib.base.event_system import AppEventsHandler -from pyogp.lib.base.parcel import ParcelManager - -# messaging -from pyogp.lib.base.message.udpdispatcher import UDPDispatcher -from pyogp.lib.base.message.circuit import Host -from pyogp.lib.base.message.message import Message, Block -from pyogp.lib.base.message.message_handler import MessageHandler - -# utilities -from pyogp.lib.base.utilities.helpers import Wait - -# initialize logging -logger = getLogger('pyogp.lib.base.region') -log = logger.log - -class Region(object): - """ a region container - - The Region class is a container for region specific data. - It is also a nice place for convenience code. - - Example, of initializing a region class: - - Initialize the login class - - >>> region = Region(256, 256, 'https://somesim.cap/uuid', 'EnableSimulator,TeleportFinish,CrossedRegion', '127.0.0.1', 13000, 650000000, {'agent_id':'00000000-0000-0000-0000-000000000000', 'session_id':'00000000-0000-0000-0000-000000000000', 'secure_session_id:'00000000-0000-0000-0000-000000000000'}) - - Start the udp and event queue connections to the region - - >>> region.connect() - - Sample implementations: examples/sample_region_connect.py - Tests: tests/region.txt, tests/test_region.py - - """ - - def __init__(self, global_x = 0, global_y = 0, seed_capability_url = None, udp_blacklist = None, sim_ip = None, sim_port = None, circuit_code = None, agent = None, settings = None, message_handler = None, handle = None, events_handler = None): - """ initialize a region """ - - # allow the settings to be passed in - # otherwise, grab the defaults - if settings != None: - self.settings = settings - else: - self.settings = Settings() - - # allow the packet_handler to be passed in - # otherwise, grab the defaults - if message_handler != None: - self.message_handler = message_handler - #elif self.settings.HANDLE_PACKETS: - else: - self.message_handler = MessageHandler() - - # allow the eventhandler to be passed in - # so that applications running multiple avatars - # may use the same eventhandler - - # otherwise, let's just use our own - if events_handler != None: - self.events_handler = events_handler - else: - self.events_handler = AppEventsHandler() - - # initialize the init params - self.global_x = int(global_x) - self.global_y = int(global_y) - self.grid_x = self.global_x/256 - self.grid_y = self.global_y/256 - self.seed_capability_url = seed_capability_url - self.udp_blacklist = udp_blacklist - self.sim_ip = sim_ip - self.sim_port = sim_port - self.circuit_code = circuit_code - self.agent = agent # an agent object - self.handle = handle - self.is_host_region = False - - # UDP connection information - if (self.sim_ip != None) and (self.sim_port != None): - self.messenger = UDPDispatcher(settings = self.settings, message_handler = self.message_handler, region = self) - self.host = Host((self.sim_ip, self.sim_port)) - else: - self.host = None - - # other attributes - self.RegionHandle = None # from AgentMovementComplete - self.SimName = None - self.seed_capability = None - self.capabilities = {} - self.event_queue = None - self.connected = False - self.helpers = Helpers() - - self._isUDPRunning = False - self._isEventQueueRunning = False - - # data storage containers - self.packet_queue = [] - - self.objects = ObjectManager(agent = self.agent, region = self, settings = self.settings, message_handler = self.message_handler, events_handler = self.events_handler) - - self.parcel_manager = ParcelManager(agent = self.agent, region = self, settings = self.settings, message_handler = self.message_handler, events_handler = self.events_handler) - - - # required packet handlers - onPacketAck_received = self.message_handler.register('PacketAck') - onPacketAck_received.subscribe(self.helpers.null_packet_handler, self) - - # data we need - self.region_caps_list = ['ChatSessionRequest', - 'CopyInventoryFromNotecard', - 'DispatchRegionInfo', - 'EstateChangeInfo', - 'EventQueueGet', - 'FetchInventory', - 'WebFetchInventoryDescendents', - 'FetchLib', - 'FetchLibDescendents', - 'GroupProposalBallot', - 'HomeLocation', - 'MapLayer', - 'MapLayerGod', - 'NewFileAgentInventory', - 'ParcelPropertiesUpdate', - 'ParcelVoiceInfoRequest', - 'ProvisionVoiceAccountRequest', - 'RemoteParcelRequest', - 'RequestTextureDownload', - 'SearchStatRequest', - 'SearchStatTracking', - 'SendPostcard', - 'SendUserReport', - 'SendUserReportWithScreenshot', - 'ServerReleaseNotes', - 'StartGroupProposal', - 'UpdateAgentLanguage', - 'UpdateGestureAgentInventory', - 'UpdateNotecardAgentInventory', - 'UpdateScriptAgent', - 'UpdateGestureTaskInventory', - 'UpdateNotecardTaskInventory', - 'UpdateScriptTask', - 'ViewerStartAuction', - 'UntrustedSimulatorMessage', - 'ViewerStats' - ] - if self.settings.LOG_VERBOSE: - log(DEBUG, 'initializing region domain: %s' %self) - - def enable_callbacks(self): - '''enables the callback handles for this Region''' - - if self.settings.ENABLE_OBJECT_TRACKING: - self.objects.enable_callbacks() - if self.settings.ENABLE_PARCEL_TRACKING: - self.parcel_manager.enable_callbacks() - - if self.settings.HANDLE_PACKETS: - pass - - def enable_child_simulator(self, IP, Port, Handle): - - log(INFO, "Would enable a simulator at %s:%s with a handle of %s" % (IP, Port, Handle)) - - def send_message_next(self, packet, reliable = False): - """ inserts this packet at the fron of the queue """ - - #if str(type(packet)) != '': - #packet = packet() - - self.packet_queue.insert(0, (packet, reliable)) - - def enqueue_message(self, packet, reliable = False): - """ queues packets for the messaging system to send """ - - #if str(type(packet)) != '': - #packet = packet() - - self.packet_queue.append((packet, reliable)) - - def send_message(self, packet, reliable = False): - """ send a packet to the host """ - - #if str(type(packet)) != '': - #packet = packet() - - if self.host == None or self.messenger == None: - raise RegionMessageError(self) - else: - if reliable == False: - self.messenger.send_message(packet, self.host) - else: - self.messenger.send_reliable(packet, self.host, 0) - - def send_reliable(self, packet): - """ send a reliable packet to the host """ - - #if str(type(packet)) != '': - #packet = packet() - - if self.host == None or self.messenger == None: - raise RegionMessageError(self) - else: - self.messenger.send_reliable(packet, self.host, 0) - - def _set_seed_capability(self, url = None): - """ sets the seed_cap attribute as a RegionSeedCapability instance """ - - if url != None: - self.seed_capability_url = url - self.seed_cap = RegionSeedCapability('seed_cap', self.seed_capability_url, settings = self.settings) - - if self.settings.LOG_VERBOSE: - log(DEBUG, 'setting region domain seed cap: %s' % (self.seed_capability_url)) - - def _get_region_public_seed(self, custom_headers={'Accept' : 'application/llsd+xml'}): - """ call this capability, return the parsed result """ - - if self.settings.ENABLE_CAPS_LOGGING: - log(DEBUG, 'Getting region public_seed %s' %(self.region_uri)) - - try: - restclient = StdLibClient() - response = restclient.GET(self.region_uri, custom_headers) - except HTTPError, e: - if e.code == 404: - raise ResourceNotFound(self.region_uri) - else: - raise ResourceError(self.region_uri, e.code, e.msg, e.fp.read(), method="GET") - - data = llsd.parse(response.body) - - if self.settings.ENABLE_CAPS_LOGGING: - log(DEBUG, 'Get of cap %s response is: %s' % (self.region_uri, data)) - - return data - - def _get_region_capabilities(self): - """ queries the region seed cap for capabilities """ - - if (self.seed_cap == None): - raise RegionSeedCapNotAvailable("querying for agent capabilities") - else: - - if self.settings.ENABLE_CAPS_LOGGING: - log(INFO, 'Getting caps from region seed cap %s' % (self.seed_cap)) - - # use self.region_caps.keys() to pass a list to be parsed into LLSD - self.capabilities = self.seed_cap.get(self.region_caps_list, self.settings) - - def connect(self): - """ connect to the udp circuit code and event queue""" - self.enable_callbacks() - # if this is the agent's host region, spawn the event queue - # spawn an eventlet api instance that runs the event queue connection - if self.seed_capability_url != None: - - # set up the seed capability - self._set_seed_capability() - - # grab the agent's capabilities from the sim - self._get_region_capabilities() - - log(DEBUG, 'Spawning region event queue connection') - self._startEventQueue() - - - # send the first few packets necessary to establish presence - self._init_agent_in_region() - - self.last_ping = 0 - - # spawn an eventlet api instance that runs the UDP connection - log(DEBUG, 'Spawning region UDP connection') - if self.settings.LOG_COROUTINE_SPAWNS: - log(INFO, "Spawning a coroutine for udp connection to the agent's host region %s" % (str(self.sim_ip) + ":" + str(self.sim_port))) - api.spawn(self._processUDP) - - log(DEBUG, "Spawned region data connections") - - def connect_child(self): - """ connect to the a child region udp circuit code """ - - # send the UseCircuitCode packet - self.sendUseCircuitCode(self.circuit_code, self.agent.session_id, self.agent.agent_id) - - self.last_ping = 0 - - # spawn an eventlet api instance that runs the UDP connection - log(DEBUG, 'Spawning region UDP connection for child region %s' % (str(self.sim_ip) + ":" + str(self.sim_port))) - - if self.settings.LOG_COROUTINE_SPAWNS: - log(INFO, "Spawning a coroutine for udp connection to the agent's child region %s" % (str(self.sim_ip) + ":" + str(self.sim_port))) - - api.spawn(self._processUDP) - - def logout(self): - """ send a logout packet """ - - log(INFO, "Disconnecting from region %s" % (self.SimName)) - - try: - - self.send_LogoutRequest(self.agent.agent_id, self.agent.session_id) - - # ToDo: We should parse a response packet prior to really disconnecting - Wait(1) - - self._isUDPRunning = False - self._stopEventQueue() - - return True - except Exception, error: - log(ERROR, "Error logging out from region.") - return False - - def send_LogoutRequest(self, agent_id, session_id): - """ send a LogoutRequest message to the host simulator """ - - packet = Message('LogoutRequest', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id)) - - self.send_message(packet) - - def kill_coroutines(self): - """ trigger to end processes spawned by the child regions """ - - self._isUDPRunning = False - self._stopEventQueue() - - def _init_agent_in_region(self): - """ send a few packets to set things up """ - - # send the UseCircuitCode packet - self.sendUseCircuitCode(self.circuit_code, self.agent.session_id, self.agent.agent_id) - - # wait a sec, then send the rest - time.sleep(1) - - # send the CompleteAgentMovement packet - self.sendCompleteAgentMovement(self.agent.agent_id, self.agent.session_id, self.circuit_code) - - # send a UUIDNameRequest packet - #self.sendUUIDNameRequest() - - # send an AgentUpdate packet to complete the loop - self.sendAgentUpdate(self.agent.agent_id, self.agent.session_id) - - def sendUseCircuitCode(self, circuit_code, session_id, agent_id): - """ initializing on a simulator requires announcing the circuit code an agent will use """ - - packet = Message('UseCircuitCode', - Block('CircuitCode', - Code = circuit_code, - SessionID = session_id, - ID = agent_id)) - - self.send_reliable(packet) - - def sendCompleteAgentMovement(self, agent_id, session_id, circuit_code): - """ initializing on a simulator requires sending CompleteAgentMovement, also required on teleport """ - - packet = Message('CompleteAgentMovement', - Block('AgentData', - AgentID = agent_id, - SessionID = session_id, - CircuitCode = circuit_code)) - - self.send_reliable(packet) - - def sendUUIDNameRequest(self, agent_ids = []): - """ sends a packet requesting the name corresponding to a UUID """ - - packet = Message('UUIDNameRequest', - *[Block('UUIDNameBlock', - ID = agent_id) for agent_id in agent_ids]) - - self.send_message(packet) - - def sendAgentUpdate(self, - AgentID, - SessionID, - BodyRotation = (0.0,0.0,0.0,1.0), - HeadRotation = (0.0,0.0,0.0,1.0), - State = 0x00, - CameraCenter = (0.0,0.0,0.0), - CameraAtAxis = (0.0,0.0,0.0), - CameraLeftAxis = (0.0,0.0,0.0), - CameraUpAxis = (0.0,0.0,0.0), - Far = 0, - ControlFlags = 0x00, - Flags = 0x00): - """ sends an AgentUpdate packet to *this* simulator""" - - packet = Message('AgentUpdate', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID, - BodyRotation = BodyRotation, - HeadRotation = HeadRotation, - State = State, - CameraCenter = CameraCenter, - CameraAtAxis = CameraAtAxis, - CameraLeftAxis = CameraLeftAxis, - CameraUpAxis = CameraUpAxis, - Far = Far, - ControlFlags = ControlFlags, - Flags = Flags)) - - self.send_message(packet) - - def sendRegionHandshakeReply(self, AgentID, SessionID, Flags = 00): - """ sends a RegionHandshake packet """ - - packet = Message('RegionHandshakeReply', - Block('AgentData', - AgentID = AgentID, - SessionID = SessionID), - Block('RegionInfo', - Flags = Flags)) - - self.send_reliable(packet) - - def sendCompletePingCheck(self, PingID): - """ sends a CompletePingCheck packet """ - - packet = Message('CompletePingCheck', - Block('PingID', - PingID = PingID)) - - self.send_message(packet) - - # we need to increment the last ping id - self.last_ping += 1 - - def _processUDP(self): - """ check for packets and handle certain cases """ - - self._isUDPRunning = True - - # the RegionHandshake packet requires a response - onRegionHandshake_received = self.message_handler.register('RegionHandshake') - onRegionHandshake_received.subscribe(self.onRegionHandshake) - - # the StartPingCheck packet requires a response - onStartPingCheck_received = self.message_handler.register('StartPingCheck') - onStartPingCheck_received.subscribe(self.onStartPingCheck) - - while self._isUDPRunning: - - # free up resources for other stuff to happen - api.sleep(0) - - # check for new messages - msg_buf, msg_size = self.messenger.udp_client.receive_packet(self.messenger.socket) - self.messenger.receive_check(self.messenger.udp_client.get_sender(), - msg_buf, msg_size) - - if self.messenger.has_unacked(): - - self.messenger.process_acks() - - # if this region is the host region, send agent updates - if self.is_host_region: - # pull the camera back a bit, 20m - # we are currently facing east, so pull back on the x axis - CameraCenter = (self.agent.Position.X - 20.0, self.agent.Position.Y, self.agent.Position.Z) - - self.sendAgentUpdate(self.agent.agent_id, self.agent.session_id, CameraCenter = CameraCenter, CameraAtAxis = self.settings.DEFAULT_CAMERA_AT_AXIS, CameraLeftAxis = self.settings.DEFAULT_CAMERA_AT_AXIS, CameraUpAxis = self.settings.DEFAULT_CAMERA_UP_AXIS, Far = self.settings.DEFAULT_CAMERA_DRAW_DISTANCE) - - # send pending messages in the queue - for (packet, reliable) in self.packet_queue: - self.send_message(packet, reliable) - self.packet_queue.remove((packet, reliable)) - - log(DEBUG, "Stopped the UDP connection for %s" % (self.SimName)) - - def _startEventQueue(self): - """ polls the event queue capability and parses the results """ - - self.event_queue = EventQueueClient(self.capabilities['EventQueueGet'], settings = self.settings, message_handler = self.message_handler, region = self) - - api.spawn(self.event_queue.start) - - self._isEventQueueRunning = True - - def _stopEventQueue(self): - """ shuts down the running event queue """ - - if self._isEventQueueRunning == True and self.event_queue._running == True: - self.event_queue.stop() - - def onRegionHandshake(self, packet): - """ handles the response to receiving a RegionHandshake packet """ - - # send the reply - self.sendRegionHandshakeReply(self.agent.agent_id, self.agent.session_id) - - # propagate the incoming data - self.SimName = packet.blocks['RegionInfo'][0].get_variable('SimName').data - self.SimAccess = packet.blocks['RegionInfo'][0].get_variable('SimAccess').data - self.SimOwner = packet.blocks['RegionInfo'][0].get_variable('SimOwner').data - self.IsEstateManager = packet.blocks['RegionInfo'][0].get_variable('IsEstateManager').data - self.WaterHeight = packet.blocks['RegionInfo'][0].get_variable('WaterHeight').data - self.BillableFactor = packet.blocks['RegionInfo'][0].get_variable('BillableFactor').data - self.TerrainBase0 = packet.blocks['RegionInfo'][0].get_variable('TerrainBase0').data - self.TerrainBase1 = packet.blocks['RegionInfo'][0].get_variable('TerrainBase1').data - self.TerrainBase2 = packet.blocks['RegionInfo'][0].get_variable('TerrainBase2').data - self.TerrainStartHeight00 = packet.blocks['RegionInfo'][0].get_variable('TerrainStartHeight00').data - self.TerrainStartHeight01 = packet.blocks['RegionInfo'][0].get_variable('TerrainStartHeight01').data - self.TerrainStartHeight10 = packet.blocks['RegionInfo'][0].get_variable('TerrainStartHeight10').data - self.TerrainStartHeight11 = packet.blocks['RegionInfo'][0].get_variable('TerrainStartHeight11').data - self.TerrainHeightRange00 = packet.blocks['RegionInfo'][0].get_variable('TerrainHeightRange00').data - self.TerrainHeightRange01 = packet.blocks['RegionInfo'][0].get_variable('TerrainHeightRange01').data - self.TerrainHeightRange10 = packet.blocks['RegionInfo'][0].get_variable('TerrainHeightRange10').data - self.TerrainHeightRange11 = packet.blocks['RegionInfo'][0].get_variable('TerrainHeightRange11').data - self.CPUClassID = packet.blocks['RegionInfo3'][0].get_variable('CPUClassID').data - self.CPURatio = packet.blocks['RegionInfo3'][0].get_variable('CPURatio').data - self.ColoName = packet.blocks['RegionInfo3'][0].get_variable('ColoName').data - self.ProductSKU = packet.blocks['RegionInfo3'][0].get_variable('ProductSKU').data - self.ProductName = packet.blocks['RegionInfo3'][0].get_variable('ProductName').data - self.RegionID = packet.blocks['RegionInfo2'][0].get_variable('RegionID').data - - # we are connected - self.connected = True - - log(INFO, "Connected agent \'%s %s\' to region %s" % (self.agent.firstname, self.agent.lastname, self.SimName)) - - def onStartPingCheck(self, packet): - """ sends the CompletePingCheck packet """ - - self.sendCompletePingCheck(self.last_ping) - - @staticmethod - def xy_to_handle(x, y): - """Convert an x, y region location into a 64-bit handle""" - return (int(x)*256 << 32) + int(y)*256 - - @staticmethod - def handle_to_xy(handle): - """Convert a handle into an x,y region location. Handle can be an int or binary string.""" - import struct - if isinstance(handle, str): - handle = struct.unpack('Q', handle)[0] - - x = int((handle >> 32)/256) - y = int((handle & 0xffffffff)/256) - return x, y - - -class RegionSeedCapability(Capability): - """ a seed capability which is able to retrieve other capabilities """ - - def get(self, names=[], settings = None): - """if this is a seed cap we can retrieve other caps here""" - - # 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() - - #log(INFO, 'requesting from the region domain the following caps: %s' % (names)) - - payload = names - parsed_result = self.POST(payload) #['caps'] - if self.settings.ENABLE_CAPS_LOGGING: - log(INFO, 'Request for caps returned: %s' % (parsed_result.keys())) - - caps = {} - for name in names: - # TODO: some caps might be seed caps, how do we know? - if parsed_result.has_key(name): - caps[name] = Capability(name, parsed_result[name], settings = self.settings) - else: - if self.settings.ENABLE_CAPS_LOGGING: - log(DEBUG, 'Requested capability \'%s\' is not available' % (name)) - #log(INFO, 'got cap: %s' % (name)) - - return caps - - def __repr__(self): - return "" % (self.public_url) - - - diff --git a/pyogp/lib/base/visualparams.py b/pyogp/lib/base/visualparams.py deleted file mode 100644 index 6e8a0d1..0000000 --- a/pyogp/lib/base/visualparams.py +++ /dev/null @@ -1,539 +0,0 @@ - -""" -Contributors can be viewed at: -http://svn.secondlife.com/svn/linden/projects/2008/pyogp/lib/base/trunk/CONTRIBUTORS.txt - -$LicenseInfo:firstyear=2008&license=apachev2$ - -Copyright 2009, Linden Research, Inc. - -Licensed under the Apache License, Version 2.0. -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/lib/base/LICENSE.txt - -$/LicenseInfo$ -""" - -class Param(object): - """ - a visual parameter of an agent - """ - def __init__(self, id, group, name, wearable, value_default, value_min, - value_max, value, label, label_min, label_max): - - self.id = id - self.group = group - self.name = name - self.wearable = wearable - self.value_default = float(value_default) - self.value_min = float(value_min) - self.value_max = float(value_max) - self.value = float(value) - self.label = label - self.label_min = label_min - self.label_max = label_max - - def floatToByte(self): - """ - Converts value from a float to a byte - """ - result = self.value - self.value_min - result /= self.value_max - self.value_min - return int(255*result) - - def byteToFloat(self, val): - """ - Converts val from a byte to a float and stores its to value - """ - result = val * (1.0/255) - result *= self.value_max - self.value_min - result += self.value_min - self.value = round(result, 3) - -class VisualParams(object): - """ - The visual params of an agent - """ - params = {} - def __init__(self): - self.params[1] = Param(1, 0, u'Big_Brow', u'shape', 0.0, -.3, 2, 0.0, u'Brow Size', u'Small', u'Large') - self.params[2] = Param(2, 0, u'Nose_Big_Out', u'shape', 0.0, -0.8, 2.5, 0.0, u'Nose Size', u'Small', u'Large') - self.params[4] = Param(4, 0, u'Broad_Nostrils', u'shape', 0.0, -.5, 1, 0.0, u'Nostril Width', u'Narrow', u'Broad') - self.params[5] = Param(5, 0, u'Cleft_Chin', u'shape', 0.0, -.1, 1, 0.0, u'Chin Cleft', u'Round', u'Cleft') - self.params[6] = Param(6, 0, u'Bulbous_Nose_Tip', u'shape', 0.0, -.3, 1, 0.0, u'Nose Tip Shape', u'Pointy', u'Bulbous') - self.params[7] = Param(7, 0, u'Weak_Chin', u'shape', 0.0, -.5, .5, 0.0, u'Chin Angle', u'Chin Out', u'Chin In') - self.params[8] = Param(8, 0, u'Double_Chin', u'shape', 0.0, -.5, 1.5, 0.0, u'Chin-Neck', u'Tight Chin', u'Double Chin') - self.params[10] = Param(10, 0, u'Sunken_Cheeks', u'shape', 0.0, -1.5, 3, 0.0, u'Lower Cheeks', u'Well-Fed', u'Sunken') - self.params[11] = Param(11, 0, u'Noble_Nose_Bridge', u'shape', 0.0, -.5, 1.5, 0.0, u'Upper Bridge', u'Low', u'High') - self.params[12] = Param(12, 0, u'Jowls', u'shape', 0.0, -.5, 2.5, 0.0, '', u'Less', u'More') - self.params[13] = Param(13, 0, u'Cleft_Chin_Upper', u'shape', 0.0, 0, 1.5, 0.0, u'Upper Chin Cleft', u'Round', u'Cleft') - self.params[14] = Param(14, 0, u'High_Cheek_Bones', u'shape', 0.0, -.5, 1, 0.0, u'Cheek Bones', u'Low', u'High') - self.params[15] = Param(15, 0, u'Ears_Out', u'shape', 0.0, -.5, 1.5, 0.0, u'Ear Angle', u'In', u'Out') - self.params[16] = Param(16, 0, u'Pointy_Eyebrows', u'hair', 0.0, -.5, 3, 0.0, u'Eyebrow Points', u'Smooth', u'Pointy') - self.params[17] = Param(17, 0, u'Square_Jaw', u'shape', 0.0, -.5, 1, 0.0, u'Jaw Shape', u'Pointy', u'Square') - self.params[18] = Param(18, 0, u'Puffy_Upper_Cheeks', u'shape', 0.0, -1.5, 2.5, 0.0, u'Upper Cheeks', u'Thin', u'Puffy') - self.params[19] = Param(19, 0, u'Upturned_Nose_Tip', u'shape', 0.0, -1.5, 1, 0.0, u'Nose Tip Angle', u'Downturned', u'Upturned') - self.params[20] = Param(20, 0, u'Bulbous_Nose', u'shape', 0.0, -.5, 1.5, 0.0, u'Nose Thickness', u'Thin Nose', u'Bulbous Nose') - self.params[21] = Param(21, 0, u'Upper_Eyelid_Fold', u'shape', 0.0, -0.2, 1.3, 0.0, u'Upper Eyelid Fold', u'Uncreased', u'Creased') - self.params[22] = Param(22, 0, u'Attached_Earlobes', u'shape', 0.0, 0, 1, 0.0, u'Attached Earlobes', u'Unattached', u'Attached') - self.params[23] = Param(23, 0, u'Baggy_Eyes', u'shape', 0.0, -.5, 1.5, 0.0, u'Eye Bags', u'Smooth', u'Baggy') - self.params[24] = Param(24, 0, u'Wide_Eyes', u'shape', 0.0, -1.5, 2, 0.0, u'Eye Opening', u'Narrow', u'Wide') - self.params[25] = Param(25, 0, u'Wide_Lip_Cleft', u'shape', 0.0, -.8, 1.5, 0.0, u'Lip Cleft', u'Narrow', u'Wide') - self.params[26] = Param(26, 1, u'Lips_Thin', u'shape', 0.0, 0, .7, 0.0, '', '', '') - self.params[27] = Param(27, 0, u'Wide_Nose_Bridge', u'shape', 0.0, -1.3, 1.2, 0.0, u'Bridge Width', u'Narrow', u'Wide') - self.params[28] = Param(28, 1, u'Lips_Fat', u'shape', 0.0, 0, 2, 0.0, '', '', '') - self.params[29] = Param(29, 1, u'Wide_Upper_Lip', u'shape', 0.0, -.7, 1.3, 0.0, '', '', '') - self.params[30] = Param(30, 1, u'Wide_Lower_Lip', u'shape', 0.0, -.7, 1.3, 0.0, '', '', '') - self.params[31] = Param(31, 0, u'Arced_Eyebrows', u'hair', .5, 0, 2, .5, u'Eyebrow Arc', u'Flat', u'Arced') - self.params[32] = Param(32, 1, u'Male_Skeleton', u'shape', 0.0, 0, 1, 0.0, '', u'Female', u'Male') - self.params[33] = Param(33, 0, u'Height', u'shape', 0.0, -2.3, 2, 0.0, u'Height', u'Short', u'Tall') - self.params[34] = Param(34, 0, u'Thickness', u'shape', 0.0, -0.7, 1.5, 0.0, u'Body Thickness', u'Body Thin', u'Body Thick') - self.params[35] = Param(35, 0, u'Big_Ears', u'shape', 0.0, -1, 2, 0.0, u'Ear Size', u'Small', u'Large') - self.params[36] = Param(36, 0, u'Shoulders', u'shape', -0.5, -1.8, 1.4, -0.5, u'Shoulders', u'Narrow', u'Broad') - self.params[37] = Param(37, 0, u'Hip Width', u'shape', 0.0, -3.2, 2.8, 0.0, u'Hip Width', u'Narrow', u'Wide') - self.params[38] = Param(38, 0, u'Torso Length', u'shape', 0.0, -1, 1, 0.0, '', u'Short Torso', u'Long Torso') - self.params[40] = Param(40, 1, u'Male_Head', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[41] = Param(41, 1, u'Old', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[51] = Param(51, 1, u'Furrowed_Eyebrows', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[53] = Param(53, 1, u'Surprised_Eyebrows', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[54] = Param(54, 1, u'Worried_Eyebrows', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[55] = Param(55, 1, u'Frown_Mouth', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[57] = Param(57, 1, u'Smile_Mouth', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[58] = Param(58, 1, u'Blink_Left', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[59] = Param(59, 1, u'Blink_Right', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[70] = Param(70, 1, u'Lipsync_Aah', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[71] = Param(71, 1, u'Lipsync_Ooh', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[80] = Param(80, 0, u'male', u'shape', 0.0, 0, 1, 0.0, '', '', '') - self.params[93] = Param(93, 0, u'Glove Length', u'gloves', .8, .01, 1, .8, '', u'Short', u'Long') - self.params[98] = Param(98, 0, u'Eye Lightness', u'eyes', 0.0, 0, 1, 0.0, '', u'Darker', u'Lighter') - self.params[99] = Param(99, 0, u'Eye Color', u'eyes', 0, 0, 1, 0, '', u'Natural', u'Unnatural') - self.params[100] = Param(100, 1, u'Male_Torso', '', 0.0, 0, 1, 0.0, '', u'Male_Torso', '') - self.params[101] = Param(101, 1, u'Hands_Relaxed', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[102] = Param(102, 1, u'Hands_Point', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[103] = Param(103, 1, u'Hands_Fist', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[104] = Param(104, 1, u'Big_Belly_Torso', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[105] = Param(105, 0, u'Breast Size', u'shape', .5, 0, 1, .5, '', u'Small', u'Large') - self.params[106] = Param(106, 1, u'Muscular_Torso', u'shape', 0.0, 0, 1.4, 0.0, u'Torso Muscles', u'Regular', u'Muscular') - self.params[108] = Param(108, 0, u'Rainbow Color', u'skin', 0.0, 0, 1, 0.0, '', u'None', u'Wild') - self.params[110] = Param(110, 0, u'Red Skin', u'skin', 0.0, 0, 0.1, 0.0, u'Ruddiness', u'Pale', u'Ruddy') - self.params[111] = Param(111, 0, u'Pigment', u'skin', .5, 0, 1, .5, '', u'Light', u'Dark') - self.params[112] = Param(112, 0, u'Rainbow Color', u'hair', 0.0, 0, 1, 0.0, '', u'None', u'Wild') - self.params[113] = Param(113, 0, u'Red Hair', u'hair', 0.0, 0, 1, 0.0, '', u'No Red', u'Very Red') - self.params[114] = Param(114, 0, u'Blonde Hair', u'hair', .5, 0, 1, .5, '', u'Black', u'Blonde') - self.params[115] = Param(115, 0, u'White Hair', u'hair', 0.0, 0, 1, 0.0, '', u'No White', u'All White') - self.params[116] = Param(116, 0, u'Rosy Complexion', u'skin', 0.0, 0, 1, 0.0, '', u'Less Rosy', u'More Rosy') - self.params[117] = Param(117, 0, u'Lip Pinkness', u'skin', 0.0, 0, 1, 0.0, '', u'Darker', u'Pinker') - self.params[118] = Param(118, 1, u'Wrinkles', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[119] = Param(119, 0, u'Eyebrow Size', u'hair', 0.5, 0, 1, 0.5, '', u'Thin Eyebrows', u'Bushy Eyebrows') - self.params[125] = Param(125, 1, u'Shading', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[126] = Param(126, 1, u'Shading', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[130] = Param(130, 0, u'Front Fringe', u'hair', .45, 0, 1, .45, '', u'Short', u'Long') - self.params[131] = Param(131, 0, u'Side Fringe', u'hair', .5, 0, 1, .5, '', u'Short', u'Long') - self.params[132] = Param(132, 0, u'Back Fringe', u'hair', .39, 0, 1, .39, '', u'Short', u'Long') - self.params[133] = Param(133, 0, u'Hair Front', u'hair', .25, 0, 1, .25, '', u'Short', u'Long') - self.params[134] = Param(134, 0, u'Hair Sides', u'hair', .5, 0, 1, .5, '', u'Short', u'Long') - self.params[135] = Param(135, 0, u'Hair Back', u'hair', .55, 0, 1, .55, '', u'Short', u'Long') - self.params[136] = Param(136, 0, u'Hair Sweep', u'hair', .5, 0, 1, .5, '', u'Sweep Forward', u'Sweep Back') - self.params[137] = Param(137, 0, u'Hair Tilt', u'hair', .5, 0, 1, .5, '', u'Left', u'Right') - self.params[140] = Param(140, 0, u'Hair_Part_Middle', u'hair', 0.0, 0, 2, 0.0, u'Middle Part', u'No Part', u'Part') - self.params[141] = Param(141, 0, u'Hair_Part_Right', u'hair', 0.0, 0, 2, 0.0, u'Right Part', u'No Part', u'Part') - self.params[142] = Param(142, 0, u'Hair_Part_Left', u'hair', 0.0, 0, 2, 0.0, u'Left Part', u'No Part', u'Part') - self.params[143] = Param(143, 0, u'Hair_Sides_Full', u'hair', 0.125, -4, 1.5, 0.125, u'Full Hair Sides', u'Mowhawk', u'Full Sides') - self.params[144] = Param(144, 1, u'Bangs_Front_Up', u'hair', 0.0, 0, 1, 0.0, u'Front Bangs Up', u'Bangs', u'Bangs Up') - self.params[145] = Param(145, 1, u'Bangs_Front_Down', u'hair', 0.0, 0, 5, 0.0, u'Front Bangs Down', u'Bangs', u'Bangs Down') - self.params[146] = Param(146, 1, u'Bangs_Sides_Up', u'hair', 0.0, 0, 1, 0.0, u'Side Bangs Up', u'Side Bangs', u'Side Bangs Up') - self.params[147] = Param(147, 1, u'Bangs_Sides_Down', u'hair', 0.0, 0, 2, 0.0, u'Side Bangs Down', u'Side Bangs', u'Side Bangs Down') - self.params[148] = Param(148, 1, u'Bangs_Back_Up', u'hair', 0.0, 0, 1, 0.0, u'Back Bangs Up', u'Back Bangs', u'Back Bangs Up') - self.params[149] = Param(149, 1, u'Bangs_Back_Down', u'hair', 0.0, 0, 2, 0.0, u'Back Bangs Down', u'Back Bangs', u'Back Bangs Down') - self.params[150] = Param(150, 0, u'Body Definition', u'skin', 0, 0, 1, 0, '', u'Less', u'More') - self.params[151] = Param(151, 1, u'Big_Butt_Legs', u'shape', 0.0, 0, 1, 0.0, u'Butt Size', u'Regular', u'Large') - self.params[152] = Param(152, 1, u'Muscular_Legs', u'shape', 0.0, 0, 1.5, 0.0, u'Leg Muscles', u'Regular Muscles', u'More Muscles') - self.params[153] = Param(153, 1, u'Male_Legs', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[155] = Param(155, 0, u'Lip Width', u'shape', 0, -0.9, 1.3, 0, u'Lip Width', u'Narrow Lips', u'Wide Lips') - self.params[156] = Param(156, 1, u'Big_Belly_Legs', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[157] = Param(157, 0, u'Belly Size', u'shape', 0, 0, 1, 0, '', u'Small', u'Big') - self.params[158] = Param(158, 1, u'Shading', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[159] = Param(159, 1, u'Shading', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[160] = Param(160, 1, u'Shading', u'pants', 0.0, 0, 1, 0.0, '', '', '') - self.params[161] = Param(161, 1, u'Shading', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[162] = Param(162, 0, u'Facial Definition', u'skin', 0, 0, 1, 0, '', u'Less', u'More') - self.params[163] = Param(163, 0, u'wrinkles', u'skin', 0, 0, 1, 0, '', u'Less', u'More') - self.params[165] = Param(165, 0, u'Freckles', u'skin', 0.0, 0, 1, 0.0, '', u'Less', u'More') - self.params[166] = Param(166, 0, u'Sideburns', u'hair', 0.0, 0, 1, 0.0, '', u'Short Sideburns', u'Mutton Chops') - self.params[167] = Param(167, 0, u'Moustache', u'hair', 0.0, 0, 1, 0.0, '', u'Chaplin', u'Handlebars') - self.params[168] = Param(168, 0, u'Soulpatch', u'hair', 0.0, 0, 1, 0.0, '', u'Less soul', u'More soul') - self.params[169] = Param(169, 0, u'Chin Curtains', u'hair', 0.0, 0, 1, 0.0, '', u'Less Curtains', u'More Curtains') - self.params[171] = Param(171, 1, u'Hair_Front_Down', u'hair', 0.0, 0, 1, 0.0, u'Front Hair Down', u'Front Hair', u'Front Hair Down') - self.params[172] = Param(172, 1, u'Hair_Front_Up', u'hair', 0.0, 0, 1, 0.0, u'Front Hair Up', u'Front Hair', u'Front Hair Up') - self.params[173] = Param(173, 1, u'Hair_Sides_Down', u'hair', 0.0, 0, 1, 0.0, u'Sides Hair Down', u'Sides Hair', u'Sides Hair Down') - self.params[174] = Param(174, 1, u'Hair_Sides_Up', u'hair', 0.0, 0, 1, 0.0, u'Sides Hair Up', u'Sides Hair', u'Sides Hair Up') - self.params[175] = Param(175, 1, u'Hair_Back_Down', u'hair', 0.0, 0, 3, 0.0, u'Back Hair Down', u'Back Hair', u'Back Hair Down') - self.params[176] = Param(176, 1, u'Hair_Back_Up', u'hair', 0.0, 0, 1, 0.0, u'Back Hair Up', u'Back Hair', u'Back Hair Up') - self.params[177] = Param(177, 0, u'Hair_Rumpled', u'hair', 0.0, 0, 1, 0.0, u'Rumpled Hair', u'Smooth Hair', u'Rumpled Hair') - self.params[178] = Param(178, 1, u'Hair_Swept_Back', u'hair', 0.0, 0, 1, 0.0, u'Swept Back Hair', u'NotHair', u'Swept Back') - self.params[179] = Param(179, 1, u'Hair_Swept_Forward', u'hair', 0.0, 0, 1, 0.0, u'Swept Forward Hair', u'Hair', u'Swept Forward') - self.params[180] = Param(180, 1, u'Hair_Volume', u'hair', 0.0, 0, 1.3, 0.0, u'Hair Volume', u'Less', u'More') - self.params[181] = Param(181, 0, u'Hair_Big_Front', u'hair', 0.14, -1, 1, 0.14, u'Big Hair Front', u'Less', u'More') - self.params[182] = Param(182, 0, u'Hair_Big_Top', u'hair', .7, -1, 1, .7, u'Big Hair Top', u'Less', u'More') - self.params[183] = Param(183, 0, u'Hair_Big_Back', u'hair', 0.05, -1, 1, 0.05, u'Big Hair Back', u'Less', u'More') - self.params[184] = Param(184, 0, u'Hair_Spiked', u'hair', 0.0, 0, 1, 0.0, u'Spiked Hair', u'No Spikes', u'Big Spikes') - self.params[185] = Param(185, 0, u'Deep_Chin', u'shape', 0.0, -1, 1, 0.0, u'Chin Depth', u'Shallow', u'Deep') - self.params[186] = Param(186, 1, u'Egg_Head', u'shape', 0.0, -1.3, 1, 0.0, u'Egg Head', u'Chin Heavy', u'Forehead Heavy') - self.params[187] = Param(187, 1, u'Squash_Stretch_Head', u'shape', 0.0, -.5, 1, 0.0, u'Squash/Stretch Head', u'Squash Head', u'Stretch Head') - self.params[188] = Param(188, 1, u'Square_Head', u'shape', 0.0, 0, .7, 0.0, '', u'Less Square', u'More Square') - self.params[189] = Param(189, 1, u'Round_Head', u'shape', 0.0, 0, 1, 0.0, '', u'Less Round', u'More Round') - self.params[190] = Param(190, 1, u'Hair_Tilt_Right', u'hair', 0.0, 0, 1, 0.0, u'Hair Tilted Right', u'Hair', u'Tilt Right') - self.params[191] = Param(191, 1, u'Hair_Tilt_Left', u'hair', 0.0, 0, 1, 0.0, u'Hair Tilted Left', u'Hair', u'Tilt Left') - self.params[192] = Param(192, 0, u'Bangs_Part_Middle', u'hair', 0.0, 0, 1, 0.0, u'Part Bangs', u'No Part', u'Part Bangs') - self.params[193] = Param(193, 0, u'Head Shape', u'shape', .5, 0, 1, .5, u'Head Shape', u'More Square', u'More Round') - self.params[194] = Param(194, 1, u'Eye_Spread', '', 0.0, -2, 2, 0.0, '', u'Eyes Together', u'Eyes Spread') - self.params[195] = Param(195, 1, u'EyeBone_Spread', u'shape', 0.0, -1, 1, 0.0, '', u'Eyes Together', u'Eyes Spread') - self.params[196] = Param(196, 0, u'Eye Spacing', u'shape', 0, -2, 1, 0, u'Eye Spacing', u'Close Set Eyes', u'Far Set Eyes') - self.params[197] = Param(197, 1, u'Shoe_Heels', u'shoes', 0.0, 0, 1, 0.0, '', u'No Heels', u'High Heels') - self.params[198] = Param(198, 0, u'Heel Height', u'shoes', 0, 0, 1, 0, '', u'Low Heels', u'High Heels') - self.params[300] = Param(300, 1, u'Express_Closed_Mouth', '', 1, 0, 1, 1, '', '', '') - self.params[301] = Param(301, 1, u'Express_Tongue_Out', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[302] = Param(302, 1, u'Express_Surprise_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[303] = Param(303, 1, u'Express_Wink_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[304] = Param(304, 1, u'Express_Embarrassed_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[305] = Param(305, 1, u'Express_Shrug_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[306] = Param(306, 1, u'Express_Kiss', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[307] = Param(307, 1, u'Express_Bored_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[308] = Param(308, 1, u'Express_Repulsed_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[309] = Param(309, 1, u'Express_Disdain', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[310] = Param(310, 1, u'Express_Afraid_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[311] = Param(311, 1, u'Express_Worry_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[312] = Param(312, 1, u'Express_Cry_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[313] = Param(313, 1, u'Express_Sad_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[314] = Param(314, 1, u'Express_Anger_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[315] = Param(315, 1, u'Express_Frown', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[316] = Param(316, 1, u'Express_Laugh_Emote', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[317] = Param(317, 1, u'Express_Toothsmile', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[318] = Param(318, 1, u'Express_Smile', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[400] = Param(400, 1, u'Displace_Hair_Facial', u'hair', 0.0, 0, 2, 0.0, u'Hair Thickess', u'Cropped Hair', u'Bushy Hair') - self.params[500] = Param(500, 1, u'Shoe_Heel_Height', u'shoes', 0.0, 0, 1, 0.0, u'Heel Height', u'Low Heels', u'High Heels') - self.params[501] = Param(501, 1, u'Shoe_Platform_Height', u'shoes', 0.0, 0, 1, 0.0, u'Platform Height', u'Low Platforms', u'High Platforms') - self.params[502] = Param(502, 1, u'Shoe_Platform', u'shoes', 0.0, 0, 1, 0.0, '', u'No Heels', u'High Heels') - self.params[503] = Param(503, 0, u'Platform Height', u'shoes', 0, 0, 1, 0, '', u'Low Platforms', u'High Platforms') - self.params[505] = Param(505, 0, u'Lip Thickness', u'shape', .5, 0, 1, .5, '', u'Thin Lips', u'Fat Lips') - self.params[506] = Param(506, 0, u'Mouth_Height', u'shape', 0.0, -2, 2, 0.0, u'Mouth Position', u'High', u'Low') - self.params[507] = Param(507, 0, u'Breast_Gravity', u'shape', 0, -1.5, 2, 0, u'Breast Buoyancy', u'Less Gravity', u'More Gravity') - self.params[508] = Param(508, 0, u'Shoe_Platform_Width', u'shoes', 0.0, -1, 2, 0.0, u'Platform Width', u'Narrow', u'Wide') - self.params[509] = Param(509, 1, u'Shoe_Heel_Point', u'shoes', 0.0, 0, 1, 0.0, u'Heel Shape', u'Default Heels', u'Pointy Heels') - self.params[510] = Param(510, 1, u'Shoe_Heel_Thick', u'shoes', 0.0, 0, 1, 0.0, u'Heel Shape', u'default Heels', u'Thick Heels') - self.params[511] = Param(511, 1, u'Shoe_Toe_Point', u'shoes', 0.0, 0, 1, 0.0, u'Toe Shape', u'Default Toe', u'Pointy Toe') - self.params[512] = Param(512, 1, u'Shoe_Toe_Square', u'shoes', 0.0, 0, 1, 0.0, u'Toe Shape', u'Default Toe', u'Square Toe') - self.params[513] = Param(513, 0, u'Heel Shape', u'shoes', .5, 0, 1, .5, '', u'Pointy Heels', u'Thick Heels') - self.params[514] = Param(514, 0, u'Toe Shape', u'shoes', .5, 0, 1, .5, '', u'Pointy', u'Square') - self.params[515] = Param(515, 0, u'Foot_Size', u'shape', 0.0, -1, 3, 0.0, u'Foot Size', u'Small', u'Big') - self.params[516] = Param(516, 1, u'Displace_Loose_Lowerbody', u'pants', 0, 0, 1, 0, u'Pants Fit', '', '') - self.params[517] = Param(517, 0, u'Wide_Nose', u'shape', 0.0, -.5, 1, 0.0, u'Nose Width', u'Narrow', u'Wide') - self.params[518] = Param(518, 0, u'Eyelashes_Long', u'shape', 0.0, -.3, 1.5, 0.0, u'Eyelash Length', u'Short', u'Long') - self.params[600] = Param(600, 1, u'Sleeve Length Cloth', u'shirt', .7, 0, 0.85, .7, '', '', '') - self.params[601] = Param(601, 1, u'Shirt Bottom Cloth', u'shirt', .8, 0, 1, .8, '', '', '') - self.params[602] = Param(602, 1, u'Collar Front Height Cloth', u'shirt', .8, 0, 1, .8, '', '', '') - self.params[603] = Param(603, 0, u'Sleeve Length', u'undershirt', .4, .01, 1, .4, '', u'Short', u'Long') - self.params[604] = Param(604, 0, u'Bottom', u'undershirt', .85, 0, 1, .85, '', u'Short', u'Long') - self.params[605] = Param(605, 0, u'Collar Front', u'undershirt', .84, 0, 1, .84, '', u'Low', u'High') - self.params[606] = Param(606, 0, u'Sleeve Length', u'jacket', .8, 0, 1, .8, '', u'Short', u'Long') - self.params[607] = Param(607, 0, u'Collar Front', u'jacket', .8, 0, 1, .8, '', u'Low', u'High') - self.params[608] = Param(608, 0, u'bottom length lower', u'jacket', .8, 0, 1, .8, u'Jacket Length', u'Short', u'Long') - self.params[609] = Param(609, 0, u'open jacket', u'jacket', .2, 0, 1, .2, u'Open Front', u'Open', u'Closed') - self.params[614] = Param(614, 1, u'Waist Height Cloth', u'pants', .8, 0, 1, .8, '', '', '') - self.params[615] = Param(615, 1, u'Pants Length Cloth', u'pants', .8, 0, 1, .8, '', '', '') - self.params[616] = Param(616, 0, u'Shoe Height', u'shoes', 0.1, 0, 1, 0.1, '', u'Short', u'Tall') - self.params[617] = Param(617, 0, u'Socks Length', u'socks', 0.35, 0, 1, 0.35, '', u'Short', u'Long') - self.params[619] = Param(619, 0, u'Pants Length', u'underpants', .3, 0, 1, .3, '', u'Short', u'Long') - self.params[620] = Param(620, 1, u'bottom length upper', u'jacket', .8, 0, 1, .8, '', u'hi cut', u'low cut') - self.params[621] = Param(621, 1, u'bottom length lower', u'jacket', .8, 0, 1, .8, '', u'hi cut', u'low cut') - self.params[622] = Param(622, 1, u'open upper', u'jacket', .8, 0, 1, .8, '', u'closed', u'open') - self.params[623] = Param(623, 1, u'open lower', u'jacket', .8, 0, 1, .8, '', u'open', u'closed') - self.params[624] = Param(624, 0, u'Pants Waist', u'underpants', .8, 0, 1, .8, '', u'Low', u'High') - self.params[625] = Param(625, 0, u'Leg_Pantflair', u'pants', 0.0, 0, 1.5, 0.0, u'Cuff Flare', u'Tight Cuffs', u'Flared Cuffs') - self.params[626] = Param(626, 1, u'Big_Chest', u'shape', 0.0, 0, 1, 0.0, u'Chest Size', u'Small', u'Large') - self.params[627] = Param(627, 1, u'Small_Chest', u'shape', 0.0, 0, 1, 0.0, u'Chest Size', u'Large', u'Small') - self.params[628] = Param(628, 1, u'Displace_Loose_Upperbody', u'shirt', 0, 0, 1, 0, u'Shirt Fit', '', '') - self.params[629] = Param(629, 0, u'Forehead Angle', u'shape', .5, 0, 1, .5, '', u'More Vertical', u'More Sloped') - self.params[630] = Param(630, 1, u'Forehead_Round', u'shape', 0.0, 0, 1, 0.0, u'Round Forehead', u'Less', u'More') - self.params[631] = Param(631, 1, u'Forehead_Slant', u'shape', 0.0, 0, 1, 0.0, u'Slanted Forehead', u'Less', u'More') - self.params[632] = Param(632, 1, u'Express_Open_Mouth', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[633] = Param(633, 1, u'Fat_Head', u'shape', 0.0, 0, 1, 0.0, u'Fat Head', u'Skinny', u'Fat') - self.params[634] = Param(634, 1, u'Fat_Torso', u'shape', 0.0, 0, 1, 0.0, u'Fat Torso', u'skinny', u'fat') - self.params[635] = Param(635, 1, u'Fat_Legs', u'shape', 0.0, 0, 1, 0.0, u'Fat Torso', u'skinny', u'fat') - self.params[637] = Param(637, 0, u'Body Fat', u'shape', 0, 0, 1, 0, '', u'Less Body Fat', u'More Body Fat') - self.params[638] = Param(638, 0, u'Low_Crotch', u'pants', 0.0, 0, 1.3, 0.0, u'Pants Crotch', u'High and Tight', u'Low and Loose') - self.params[640] = Param(640, 1, u'Hair_Egg_Head', u'hair', 0.0, -1.3, 1, 0.0, '', '', '') - self.params[641] = Param(641, 1, u'Hair_Squash_Stretch_Head', u'hair', 0.0, -.5, 1, 0.0, '', '', '') - self.params[642] = Param(642, 1, u'Hair_Square_Head', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[643] = Param(643, 1, u'Hair_Round_Head', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[644] = Param(644, 1, u'Hair_Forehead_Round', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[645] = Param(645, 1, u'Hair_Forehead_Slant', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[646] = Param(646, 0, u'Egg_Head', u'shape', 0, -1.3, 1, 0, u'Egg Head', u'Chin Heavy', u'Forehead Heavy') - self.params[647] = Param(647, 0, u'Squash_Stretch_Head', u'shape', 0, -0.5, 1, 0, u'Head Stretch', u'Squash Head', u'Stretch Head') - self.params[648] = Param(648, 1, u'Scrawny_Torso', u'shape', 0.0, 0, 1.3, 0.0, u'Torso Muscles', u'Regular', u'Scrawny') - self.params[649] = Param(649, 0, u'Torso Muscles', u'shape', .5, 0, 1, .5, u'Torso Muscles', u'Less Muscular', u'More Muscular') - self.params[650] = Param(650, 0, u'Eyelid_Corner_Up', u'shape', 0.0, -1.3, 1.2, 0.0, u'Outer Eye Corner', u'Corner Down', u'Corner Up') - self.params[651] = Param(651, 1, u'Scrawny_Legs', u'shape', 0.0, 0, 1.5, 0.0, u'Scrawny Leg', u'Regular Muscles', u'Less Muscles') - self.params[652] = Param(652, 0, u'Leg Muscles', u'shape', .5, 0, 1, .5, '', u'Less Muscular', u'More Muscular') - self.params[653] = Param(653, 0, u'Tall_Lips', u'shape', 0.0, -1, 2, 0.0, u'Lip Fullness', u'Less Full', u'More Full') - self.params[654] = Param(654, 0, u'Shoe_Toe_Thick', u'shoes', 0.0, 0, 2, 0.0, u'Toe Thickness', u'Flat Toe', u'Thick Toe') - self.params[655] = Param(655, 1, u'Head Size', u'shape', 0.0, -.25, .10, 0.0, u'Head Size', u'Small Head', u'Big Head') - self.params[656] = Param(656, 0, u'Crooked_Nose', u'shape', 0.0, -2, 2, 0.0, u'Crooked Nose', u'Nose Left', u'Nose Right') - self.params[657] = Param(657, 1, u'Smile_Mouth', u'shape', 0.0, 0, 1.4, 0.0, u'Mouth Corner', u'Corner Normal', u'Corner Up') - self.params[658] = Param(658, 1, u'Frown_Mouth', u'shape', 0.0, 0, 1.2, 0.0, u'Mouth Corner', u'Corner Normal', u'Corner Down') - self.params[659] = Param(659, 0, u'Mouth Corner', u'shape', .5, 0, 1, .5, '', u'Corner Down', u'Corner Up') - self.params[660] = Param(660, 1, u'Shear_Head', u'shape', 0, -2, 2, 0, u'Shear Face', u'Shear Left', u'Shear Right') - self.params[661] = Param(661, 1, u'EyeBone_Head_Shear', u'shape', 0.0, -2, 2, 0.0, '', u'Eyes Shear Left Up', u'Eyes Shear Right Up') - self.params[662] = Param(662, 0, u'Face Shear', u'shape', .5, 0, 1, .5, '', u'Shear Right Up', u'Shear Left Up') - self.params[663] = Param(663, 0, u'Shift_Mouth', u'shape', 0, -2, 2, 0, u'Shift Mouth', u'Shift Left', u'Shift Right') - self.params[664] = Param(664, 0, u'Pop_Eye', u'shape', 0, -2, 2, 0, u'Eye Pop', u'Pop Right Eye', u'Pop Left Eye') - self.params[665] = Param(665, 0, u'Jaw_Jut', u'shape', 0, -2, 2, 0, u'Jaw Jut', u'Overbite', u'Underbite') - self.params[666] = Param(666, 1, u'Hands_Relaxed_L', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[667] = Param(667, 1, u'Hands_Point_L', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[668] = Param(668, 1, u'Hands_Fist_L', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[669] = Param(669, 1, u'Hands_Relaxed_R', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[670] = Param(670, 1, u'Hands_Point_R', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[671] = Param(671, 1, u'Hands_Fist_R', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[672] = Param(672, 1, u'Hands_Typing', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[674] = Param(674, 0, u'Hair_Shear_Back', u'hair', -0.3, -1, 2, -0.3, u'Shear Back', u'Full Back', u'Sheared Back') - self.params[675] = Param(675, 0, u'Hand Size', u'shape', 0.0, -.3, .3, 0.0, '', u'Small Hands', u'Large Hands') - self.params[676] = Param(676, 0, u'Love_Handles', u'shape', 0, -1, 2, 0, u'Love Handles', u'Less Love', u'More Love') - self.params[677] = Param(677, 1, u'Scrawny_Torso_Male', u'shape', 0.0, 0, 1.3, 0.0, u'Torso Scrawny', u'Regular', u'Scrawny') - self.params[678] = Param(678, 0, u'Torso Muscles', u'shape', .5, 0, 1, .5, '', u'Less Muscular', u'More Muscular') - self.params[679] = Param(679, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .10, 0.0, u'Eyeball Size', u'small eye', u'big eye') - self.params[680] = Param(680, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .10, 0.0, u'Eyeball Size', u'small eye', u'big eye') - self.params[681] = Param(681, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .10, 0.0, u'Eyeball Size', u'small eye', u'big eye') - self.params[682] = Param(682, 0, u'Head Size', u'shape', .5, 0, 1, .5, u'Head Size', u'Small Head', u'Big Head') - self.params[683] = Param(683, 0, u'Neck Thickness', u'shape', -.15, -.4, .2, -.15, '', u'Skinny Neck', u'Thick Neck') - self.params[684] = Param(684, 0, u'Breast_Female_Cleavage', u'shape', 0, -.3, 1.3, 0, u'Breast Cleavage', u'Separate', u'Join') - self.params[685] = Param(685, 0, u'Chest_Male_No_Pecs', u'shape', 0, -.5, 1.1, 0, u'Pectorals', u'Big Pectorals', u'Sunken Chest') - self.params[686] = Param(686, 1, u'Head_Eyes_Big', u'shape', 0, -2, 2, 0, u'Eye Size', u'Beady Eyes', u'Anime Eyes') - self.params[687] = Param(687, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .25, 0.0, u'Big Eyeball', u'small eye', u'big eye') - self.params[688] = Param(688, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .25, 0.0, u'Big Eyeball', u'small eye', u'big eye') - self.params[689] = Param(689, 1, u'EyeBone_Big_Eyes', u'shape', 0.0, -1, 1, 0.0, '', u'Eyes Back', u'Eyes Forward') - self.params[690] = Param(690, 0, u'Eye Size', u'shape', .5, 0, 1, .5, u'Eye Size', u'Beady Eyes', u'Anime Eyes') - self.params[691] = Param(691, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .25, 0.0, u'Big Eyeball', u'small eye', u'big eye') - self.params[692] = Param(692, 0, u'Leg Length', u'shape', 0.0, -1, 1, 0.0, '', u'Short Legs', u'Long Legs') - self.params[693] = Param(693, 0, u'Arm Length', u'shape', .6, -1, 1, .6, '', u'Short Arms', u'Long arms') - self.params[694] = Param(694, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .10, 0.0, u'Eyeball Size', u'small eye', u'big eye') - self.params[695] = Param(695, 1, u'Eyeball_Size', u'shape', 0.0, -.25, .25, 0.0, u'Big Eyeball', u'small eye', u'big eye') - self.params[700] = Param(700, 0, u'Lipstick Color', u'skin', .25, 0, 1, .25, '', u'Pink', u'Black') - self.params[701] = Param(701, 0, u'Lipstick', u'skin', 0.0, 0, .9, 0.0, '', u'No Lipstick', u'More Lipstick') - self.params[702] = Param(702, 0, u'Lipgloss', u'skin', 0.0, 0, 1, 0.0, '', u'No Lipgloss', u'Glossy') - self.params[703] = Param(703, 0, u'Eyeliner', u'skin', 0.0, 0, 1, 0.0, '', u'No Eyeliner', u'Full Eyeliner') - self.params[704] = Param(704, 0, u'Blush', u'skin', 0, 0, .9, 0, '', u'No Blush', u'More Blush') - self.params[705] = Param(705, 0, u'Blush Color', u'skin', .5, 0, 1, .5, '', u'Pink', u'Orange') - self.params[706] = Param(706, 0, u'Out Shdw Opacity', u'skin', .6, .2, 1, .6, '', u'Clear', u'Opaque') - self.params[707] = Param(707, 0, u'Outer Shadow', u'skin', 0.0, 0, .7, 0.0, '', u'No Eyeshadow', u'More Eyeshadow') - self.params[708] = Param(708, 0, u'Out Shdw Color', u'skin', 0.0, 0, 1, 0.0, '', u'Light', u'Dark') - self.params[709] = Param(709, 0, u'Inner Shadow', u'skin', 0, 0, 1, 0, '', u'No Eyeshadow', u'More Eyeshadow') - self.params[710] = Param(710, 0, u'Nail Polish', u'skin', 0.0, 0, 1, 0.0, '', u'No Polish', u'Painted Nails') - self.params[711] = Param(711, 0, u'Blush Opacity', u'skin', .5, 0, 1, .5, '', u'Clear', u'Opaque') - self.params[712] = Param(712, 0, u'In Shdw Color', u'skin', 0.0, 0, 1, 0.0, '', u'Light', u'Dark') - self.params[713] = Param(713, 0, u'In Shdw Opacity', u'skin', .7, .2, 1, .7, '', u'Clear', u'Opaque') - self.params[714] = Param(714, 0, u'Eyeliner Color', u'skin', 0.0, 0, 1, 0.0, '', u'Dark Green', u'Black') - self.params[715] = Param(715, 0, u'Nail Polish Color', u'skin', 0.0, 0, 1, 0.0, '', u'Pink', u'Black') - self.params[750] = Param(750, 0, u'Eyebrow Density', u'hair', 0.7, 0, 1, 0.7, '', u'Sparse', u'Dense') - self.params[751] = Param(751, 1, u"5 O'Clock Shadow", u'hair', 0.7, 0, 1, 0.7, '', u'Dense hair', u'Shadow hair') - self.params[752] = Param(752, 0, u'Hair Thickness', u'hair', .5, 0, 1, .5, '', u"5 O'Clock Shadow", u'Bushy Hair') - self.params[753] = Param(753, 0, u'Saddlebags', u'shape', 0, -0.5, 3, 0, u'Saddle Bags', u'Less Saddle', u'More Saddle') - self.params[754] = Param(754, 0, u'Hair_Taper_Back', u'hair', 0, -1, 2, 0, u'Taper Back', u'Wide Back', u'Narrow Back') - self.params[755] = Param(755, 0, u'Hair_Taper_Front', u'hair', 0.05, -1.5, 1.5, 0.05, u'Taper Front', u'Wide Front', u'Narrow Front') - self.params[756] = Param(756, 0, u'Neck Length', u'shape', 0, -1, 1, 0, '', u'Short Neck', u'Long Neck') - self.params[757] = Param(757, 0, u'Lower_Eyebrows', u'hair', -1, -4, 2, -1, u'Eyebrow Height', u'Higher', u'Lower') - self.params[758] = Param(758, 0, u'Lower_Bridge_Nose', u'shape', 0.0, -1.5, 1.5, 0.0, u'Lower Bridge', u'Low', u'High') - self.params[759] = Param(759, 0, u'Low_Septum_Nose', u'shape', 0.5, -1, 1.5, 0.5, u'Nostril Division', u'High', u'Low') - self.params[760] = Param(760, 0, u'Jaw_Angle', u'shape', 0, -1.2, 2, 0, u'Jaw Angle', u'Low Jaw', u'High Jaw') - self.params[761] = Param(761, 1, u'Hair_Volume_Small', u'hair', 0.0, 0, 1.3, 0.0, u'Hair Volume', u'Less', u'More') - self.params[762] = Param(762, 0, u'Hair_Shear_Front', u'hair', 0.0, 0, 3, 0.0, u'Shear Front', u'Full Front', u'Sheared Front') - self.params[763] = Param(763, 0, u'Hair Volume', u'hair', .55, 0, 1, .55, '', u'Less Volume', u'More Volume') - self.params[764] = Param(764, 0, u'Lip_Cleft_Deep', u'shape', 0.0, -.5, 1.2, 0.0, u'Lip Cleft Depth', u'Shallow', u'Deep') - self.params[765] = Param(765, 0, u'Puffy_Lower_Lids', u'shape', 0.0, -.3, 2.5, 0.0, u'Puffy Eyelids', u'Flat', u'Puffy') - self.params[766] = Param(766, 1, u'Hands_Salute_R', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[767] = Param(767, 1, u'Bug_Eyed_Head', u'shape', 0, -2, 2, 0, u'Eye Depth', u'Sunken Eyes', u'Bug Eyes') - self.params[768] = Param(768, 1, u'EyeBone_Bug', u'shape', 0.0, -2, 2, 0.0, '', u'Eyes Sunken', u'Eyes Bugged') - self.params[769] = Param(769, 0, u'Eye Depth', u'shape', .5, 0, 1, .5, '', u'Sunken Eyes', u'Bugged Eyes') - self.params[770] = Param(770, 1, u'Elongate_Head', u'shape', 0, -1, 1, 0, u'Shear Face', u'Flat Head', u'Long Head') - self.params[771] = Param(771, 1, u'Elongate_Head_Hair', u'hair', 0.0, -1, 1, 0.0, '', '', '') - self.params[772] = Param(772, 1, u'EyeBone_Head_Elongate', u'shape', 0.0, -1, 1, 0.0, '', u'Eyes Short Head', u'Eyes Long Head') - self.params[773] = Param(773, 0, u'Head Length', u'shape', .5, 0, 1, .5, '', u'Flat Head', u'Long Head') - self.params[774] = Param(774, 1, u'Shear_Head_Hair', u'hair', 0.0, -2, 2, 0.0, '', '', '') - self.params[775] = Param(775, 0, u'Body Freckles', u'skin', 0, 0, 1, 0, '', u'Less Freckles', u'More Freckles') - self.params[776] = Param(776, 1, u'freckles upper', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[777] = Param(777, 1, u'freckles lower', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[778] = Param(778, 1, u'Collar Back Height Cloth', u'shirt', .8, 0, 1, .8, '', '', '') - self.params[779] = Param(779, 0, u'Collar Back', u'undershirt', .84, 0, 1, .84, '', u'Low', u'High') - self.params[780] = Param(780, 0, u'Collar Back', u'jacket', .8, 0, 1, .8, '', u'Low', u'High') - self.params[781] = Param(781, 0, u'Collar Back', u'shirt', .78, 0, 1, .78, '', u'Low', u'High') - self.params[782] = Param(782, 1, u'Hair_Pigtails_Short', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[783] = Param(783, 1, u'Hair_Pigtails_Med', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[784] = Param(784, 1, u'Hair_Pigtails_Long', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[785] = Param(785, 0, u'Pigtails', u'hair', 0, 0, 1, 0, '', u'Short Pigtails', u'Long Pigtails') - self.params[786] = Param(786, 1, u'Hair_Ponytail_Short', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[787] = Param(787, 1, u'Hair_Ponytail_Med', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[788] = Param(788, 1, u'Hair_Ponytail_Long', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[789] = Param(789, 0, u'Ponytail', u'hair', 0, 0, 1, 0, '', u'Short Ponytail', u'Long Ponytail') - self.params[790] = Param(790, 1, u'Hair_Pigtails_Medlong', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[791] = Param(791, 1, u'Hands_Peace_R', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[792] = Param(792, 1, u'Hands_Spread_R', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[793] = Param(793, 1, u'Leg_Longcuffs', u'pants', 0, 0, 3, 0, u'Longcuffs', '', '') - self.params[794] = Param(794, 1, u'Small_Butt', u'shape', 0.0, 0, 1, 0.0, u'Butt Size', u'Regular', u'Small') - self.params[795] = Param(795, 0, u'Butt Size', u'shape', .25, 0, 1, .25, u'Butt Size', u'Flat Butt', u'Big Butt') - self.params[796] = Param(796, 0, u'Pointy_Ears', u'shape', 0.0, -.4, 3, 0.0, u'Ear Tips', u'Flat', u'Pointy') - self.params[797] = Param(797, 1, u'Fat_Upper_Lip', u'shape', 0.0, 0, 1.5, 0.0, u'Fat Upper Lip', u'Normal Upper', u'Fat Upper') - self.params[798] = Param(798, 1, u'Fat_Lower_Lip', u'shape', 0.0, 0, 1.5, 0.0, u'Fat Lower Lip', u'Normal Lower', u'Fat Lower') - self.params[799] = Param(799, 0, u'Lip Ratio', u'shape', .5, 0, 1, .5, u'Lip Ratio', u'More Upper Lip', u'More Lower Lip') - self.params[800] = Param(800, 0, u'Sleeve Length', u'shirt', .89, 0, 1, .89, '', u'Short', u'Long') - self.params[801] = Param(801, 0, u'Shirt Bottom', u'shirt', 1, 0, 1, 1, '', u'Short', u'Long') - self.params[802] = Param(802, 0, u'Collar Front', u'shirt', .78, 0, 1, .78, '', u'Low', u'High') - self.params[803] = Param(803, 0, u'shirt_red', u'shirt', 1, 0, 1, 1, '', '', '') - self.params[804] = Param(804, 0, u'shirt_green', u'shirt', 1, 0, 1, 1, '', '', '') - self.params[805] = Param(805, 0, u'shirt_blue', u'shirt', 1, 0, 1, 1, '', '', '') - self.params[806] = Param(806, 0, u'pants_red', u'pants', 1, 0, 1, 1, '', '', '') - self.params[807] = Param(807, 0, u'pants_green', u'pants', 1, 0, 1, 1, '', '', '') - self.params[808] = Param(808, 0, u'pants_blue', u'pants', 1, 0, 1, 1, '', '', '') - self.params[809] = Param(809, 1, u'lower_jacket_red', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[810] = Param(810, 1, u'lower_jacket_green', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[811] = Param(811, 1, u'lower_jacket_blue', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[812] = Param(812, 0, u'shoes_red', u'shoes', 1, 0, 1, 1, '', '', '') - self.params[813] = Param(813, 0, u'shoes_green', u'shoes', 1, 0, 1, 1, '', '', '') - self.params[814] = Param(814, 0, u'Waist Height', u'pants', 1, 0, 1, 1, '', u'Low', u'High') - self.params[815] = Param(815, 0, u'Pants Length', u'pants', .8, 0, 1, .8, '', u'Short', u'Long') - self.params[816] = Param(816, 0, u'Loose Lower Clothing', u'pants', 0.0, 0, 1, 0.0, u'Pants Fit', u'Tight Pants', u'Loose Pants') - self.params[817] = Param(817, 0, u'shoes_blue', u'shoes', 1, 0, 1, 1, '', '', '') - self.params[818] = Param(818, 0, u'socks_red', u'socks', 1, 0, 1, 1, '', '', '') - self.params[819] = Param(819, 0, u'socks_green', u'socks', 1, 0, 1, 1, '', '', '') - self.params[820] = Param(820, 0, u'socks_blue', u'socks', 1, 0, 1, 1, '', '', '') - self.params[821] = Param(821, 0, u'undershirt_red', u'undershirt', 1, 0, 1, 1, '', '', '') - self.params[822] = Param(822, 0, u'undershirt_green', u'undershirt', 1, 0, 1, 1, '', '', '') - self.params[823] = Param(823, 0, u'undershirt_blue', u'undershirt', 1, 0, 1, 1, '', '', '') - self.params[824] = Param(824, 0, u'underpants_red', u'underpants', 1, 0, 1, 1, '', '', '') - self.params[825] = Param(825, 0, u'underpants_green', u'underpants', 1, 0, 1, 1, '', '', '') - self.params[826] = Param(826, 0, u'underpants_blue', u'underpants', 1, 0, 1, 1, '', '', '') - self.params[827] = Param(827, 0, u'gloves_red', u'gloves', 1, 0, 1, 1, '', '', '') - self.params[828] = Param(828, 0, u'Loose Upper Clothing', u'shirt', 0.0, 0, 1, 0.0, u'Shirt Fit', u'Tight Shirt', u'Loose Shirt') - self.params[829] = Param(829, 0, u'gloves_green', u'gloves', 1, 0, 1, 1, '', '', '') - self.params[830] = Param(830, 0, u'gloves_blue', u'gloves', 1, 0, 1, 1, '', '', '') - self.params[831] = Param(831, 1, u'upper_jacket_red', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[832] = Param(832, 1, u'upper_jacket_green', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[833] = Param(833, 1, u'upper_jacket_blue', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[834] = Param(834, 0, u'jacket_red', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[835] = Param(835, 0, u'jacket_green', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[836] = Param(836, 0, u'jacket_blue', u'jacket', 1, 0, 1, 1, '', '', '') - self.params[840] = Param(840, 0, u'Shirtsleeve_flair', u'shirt', 0.0, 0, 1.5, 0.0, u'Sleeve Looseness', u'Tight Sleeves', u'Loose Sleeves') - self.params[841] = Param(841, 0, u'Bowed_Legs', u'shape', 0, -1, 1, 0, u'Knee Angle', u'Knock Kneed', u'Bow Legged') - self.params[842] = Param(842, 0, u'Hip Length', u'shape', 0.0, -1, 1, 0.0, '', u'Short hips', u'Long Hips') - self.params[843] = Param(843, 1, u'No_Chest', u'shape', 0.0, 0, 1, 0.0, u'Chest Size', u'Some', u'None') - self.params[844] = Param(844, 0, u'Glove Fingers', u'gloves', 1, .01, 1, 1, '', u'Fingerless', u'Fingers') - self.params[845] = Param(845, 1, u'skirt_poofy', u'skirt', 0.0, 0, 1.5, 0.0, u'poofy skirt', u'less poofy', u'more poofy') - self.params[846] = Param(846, 1, u'skirt_loose', u'skirt', 0.0, 0, 1, 0.0, u'loose skirt', u'form fitting', u'loose') - self.params[847] = Param(847, 1, u'skirt_bowlegs', u'skirt', 0, -1, 1, 0, u'legs skirt', '', '') - self.params[848] = Param(848, 0, u'skirt_bustle', u'skirt', .2, 0, 2, .2, u'bustle skirt', u'no bustle', u'more bustle') - self.params[849] = Param(849, 1, u'skirt_belly', '', 0.0, 0, 1, 0.0, u'big belly skirt', '', '') - self.params[850] = Param(850, 1, u'skirt_saddlebags', '', 0.0, -.5, 3, 0.0, '', '', '') - self.params[851] = Param(851, 1, u'skirt_chubby', '', 0, 0, 1, 0, '', u'less', u'more') - self.params[852] = Param(852, 1, u'skirt_bigbutt', '', 0.0, 0, 1, 0.0, u'bigbutt skirt', u'less', u'more') - self.params[853] = Param(853, 1, u'Bowed_Legs', u'shape', 0.0, -1, 1, 0.0, u'Knee Angle', '', '') - self.params[854] = Param(854, 1, u'Saddlebags', '', 0.0, -.5, 3, 0.0, '', '', '') - self.params[855] = Param(855, 1, u'Love_Handles', '', 0, -1, 2, 0, '', '', '') - self.params[856] = Param(856, 1, u'skirt_lovehandles', '', 0, -1, 2, 0, '', u'less', u'more') - self.params[857] = Param(857, 1, u'skirt_male', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[858] = Param(858, 0, u'Skirt Length', u'skirt', .4, .01, 1, .4, '', u'Short', u'Long') - self.params[859] = Param(859, 0, u'Slit Front', u'skirt', 1, 0, 1, 1, '', u'Open Front', u'Closed Front') - self.params[860] = Param(860, 0, u'Slit Back', u'skirt', 1, 0, 1, 1, '', u'Open Back', u'Closed Back') - self.params[861] = Param(861, 0, u'Slit Left', u'skirt', 1, 0, 1, 1, '', u'Open Left', u'Closed Left') - self.params[862] = Param(862, 0, u'Slit Right', u'skirt', 1, 0, 1, 1, '', u'Open Right', u'Closed Right') - self.params[863] = Param(863, 0, u'skirt_looseness', u'skirt', .333, 0, 1, .333, u'Skirt Fit', u'Tight Skirt', u'Poofy Skirt') - self.params[866] = Param(866, 1, u'skirt_tight', u'skirt', 0.0, 0, 1, 0.0, u'tight skirt', u'form fitting', u'loose') - self.params[867] = Param(867, 1, u'skirt_smallbutt', u'skirt', 0.0, 0, 1, 0.0, u'tight skirt', u'form fitting', u'loose') - self.params[868] = Param(868, 0, u'Shirt Wrinkles', u'shirt', 0, 0, 1, 0, '', '', '') - self.params[869] = Param(869, 0, u'Pants Wrinkles', u'pants', 0, 0, 1, 0, '', '', '') - self.params[870] = Param(870, 1, u'Pointy_Eyebrows', u'hair', 0.0, -.5, 1, 0.0, u'Eyebrow Points', u'Smooth', u'Pointy') - self.params[871] = Param(871, 1, u'Lower_Eyebrows', u'hair', 0.0, -2, 2, 0.0, u'Eyebrow Height', u'Higher', u'Lower') - self.params[872] = Param(872, 1, u'Arced_Eyebrows', u'hair', 0.0, 0, 1, 0.0, u'Eyebrow Arc', u'Flat', u'Arced') - self.params[873] = Param(873, 1, u'Bump base', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[874] = Param(874, 1, u'Bump upperdef', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[875] = Param(875, 1, u'jacket upper Wrinkles', u'jacket', 0, 0, 1, 0, '', '', '') - self.params[876] = Param(876, 1, u'jacket upper Wrinkles', u'jacket', 0, 0, 1, 0, '', '', '') - self.params[877] = Param(877, 0, u'Jacket Wrinkles', u'jacket', 0, 0, 1, 0, u'Jacket Wrinkles', u'No Wrinkles', u'Wrinkles') - self.params[878] = Param(878, 1, u'Bump upperdef', u'skin', 0.0, 0, 1, 0.0, '', '', '') - self.params[879] = Param(879, 0, u'Male_Package', u'shape', 0, -.5, 2, 0, u'Package', u'Coin Purse', u'Duffle Bag') - self.params[880] = Param(880, 0, u'Eyelid_Inner_Corner_Up', u'shape', 0.0, -1.3, 1.2, 0.0, u'Inner Eye Corner', u'Corner Down', u'Corner Up') - self.params[899] = Param(899, 1, u'Upper Clothes Shading', u'shirt', 0, 0, 1, 0, '', '', '') - self.params[900] = Param(900, 1, u'Sleeve Length Shadow', u'shirt', 0.02, 0.02, .87, 0.02, '', '', '') - self.params[901] = Param(901, 1, u'Shirt Shadow Bottom', u'shirt', 0.0, 0.02, 1, 0.0, '', '', '') - self.params[902] = Param(902, 1, u'Collar Front Shadow Height', u'shirt', 0.0, 0.02, 1, 0.0, '', '', '') - self.params[903] = Param(903, 1, u'Collar Back Shadow Height', u'shirt', 0.0, 0.02, 1, 0.0, '', '', '') - self.params[913] = Param(913, 1, u'Lower Clothes Shading', u'pants', 0, 0, 1, 0, '', '', '') - self.params[914] = Param(914, 1, u'Waist Height Shadow', u'pants', 0.0, 0.02, 1, 0.0, '', '', '') - self.params[915] = Param(915, 1, u'Pants Length Shadow', u'pants', 0.0, 0.02, 1, 0.0, '', '', '') - self.params[921] = Param(921, 0, u'skirt_red', u'skirt', 1, 0, 1, 1, '', '', '') - self.params[922] = Param(922, 0, u'skirt_green', u'skirt', 1, 0, 1, 1, '', '', '') - self.params[923] = Param(923, 0, u'skirt_blue', u'skirt', 1, 0, 1, 1, '', '', '') - self.params[1000] = Param(1000, 1, u'Eyebrow Size Bump', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[1001] = Param(1001, 1, u'Eyebrow Size', u'hair', 0.5, 0, 1, 0.5, '', '', '') - self.params[1002] = Param(1002, 1, u'Eyebrow Density Bump', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[1003] = Param(1003, 1, u'Eyebrow Density', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1004] = Param(1004, 1, u'Sideburns bump', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[1005] = Param(1005, 1, u'Sideburns', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1006] = Param(1006, 1, u'Moustache bump', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[1007] = Param(1007, 1, u'Moustache', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1008] = Param(1008, 1, u'Soulpatch bump', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[1009] = Param(1009, 1, u'Soulpatch', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1010] = Param(1010, 1, u'Chin Curtains bump', u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[1011] = Param(1011, 1, u'Chin Curtains', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1012] = Param(1012, 1, u"5 O'Clock Shadow bump", u'hair', 0.0, 0, 1, 0.0, '', '', '') - self.params[1013] = Param(1013, 1, u'Sleeve Length Cloth', u'shirt', 0.0, 0, 0.85, 0.0, '', '', '') - self.params[1014] = Param(1014, 1, u'Shirt Bottom Cloth', u'shirt', 0.0, 0, 1, 0.0, '', '', '') - self.params[1015] = Param(1015, 1, u'Collar Front Height Cloth', u'shirt', 0.0, 0, 1, 0.0, '', '', '') - self.params[1016] = Param(1016, 1, u'Collar Back Height Cloth', u'shirt', 0.0, 0, 1, 0.0, '', '', '') - self.params[1017] = Param(1017, 1, u'Waist Height Cloth', u'pants', 0.0, 0, 1, 0.0, '', '', '') - self.params[1018] = Param(1018, 1, u'Pants Length Cloth', u'pants', 0.0, 0, 1, 0.0, '', '', '') - self.params[1019] = Param(1019, 1, u'Jacket Sleeve Length bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1020] = Param(1020, 1, u'jacket Sleeve Length', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1021] = Param(1021, 1, u'Jacket Collar Front bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1022] = Param(1022, 1, u'jacket Collar Front', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1023] = Param(1023, 1, u'Jacket Collar Back bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1024] = Param(1024, 1, u'jacket Collar Back', '', 0.0, 0, 1, 0.0, '', '', '') - self.params[1025] = Param(1025, 1, u'jacket bottom length upper bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1026] = Param(1026, 1, u'jacket open upper bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1027] = Param(1027, 1, u'jacket bottom length lower bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1028] = Param(1028, 1, u'jacket open lower bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1029] = Param(1029, 1, u'Sleeve Length Cloth', u'shirt', 0.0, 0, 0.85, 0.0, '', '', '') - self.params[1030] = Param(1030, 1, u'Shirt Bottom Cloth', u'shirt', 0.0, 0, 1, 0.0, '', '', '') - self.params[1031] = Param(1031, 1, u'Collar Front Height Cloth', u'shirt', 0.0, 0, 1, 0.0, '', '', '') - self.params[1032] = Param(1032, 1, u'Collar Back Height Cloth', u'shirt', 0.0, 0, 1, 0.0, '', '', '') - self.params[1033] = Param(1033, 1, u'jacket bottom length lower bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1034] = Param(1034, 1, u'jacket open lower bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1035] = Param(1035, 1, u'Waist Height Cloth', u'pants', 0.0, 0, 1, 0.0, '', '', '') - self.params[1036] = Param(1036, 1, u'Pants Length Cloth', u'pants', 0.0, 0, 1, 0.0, '', '', '') - self.params[1037] = Param(1037, 1, u'jacket bottom length upper bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1038] = Param(1038, 1, u'jacket open upper bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1039] = Param(1039, 1, u'Jacket Sleeve Length bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1040] = Param(1040, 1, u'Jacket Collar Front bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1041] = Param(1041, 1, u'Jacket Collar Back bump', u'jacket', 0.0, 0, 1, 0.0, '', '', '') - self.params[1042] = Param(1042, 1, u'Sleeve Length', u'undershirt', .4, .01, 1, .4, '', '', '') - self.params[1043] = Param(1043, 1, u'Sleeve Length bump', u'undershirt', .4, .01, 1, .4, '', '', '') - self.params[1044] = Param(1044, 1, u'Bottom', u'undershirt', .8, 0, 1, .8, '', '', '') - self.params[1045] = Param(1045, 1, u'Bottom bump', u'undershirt', .8, 0, 1, .8, '', '', '') - self.params[1046] = Param(1046, 1, u'Collar Front', u'undershirt', .8, 0, 1, .8, '', '', '') - self.params[1047] = Param(1047, 1, u'Collar Front bump', u'undershirt', .8, 0, 1, .8, '', '', '') - self.params[1048] = Param(1048, 1, u'Collar Back', u'undershirt', .8, 0, 1, .8, '', u'Low', u'High') - self.params[1049] = Param(1049, 1, u'Collar Back bump', u'undershirt', .8, 0, 1, .8, '', '', '') - self.params[1050] = Param(1050, 1, u'Socks Length bump', u'socks', 0.35, 0, 1, 0.35, '', '', '') - self.params[1051] = Param(1051, 1, u'Socks Length bump', u'socks', 0.35, 0, 1, 0.35, '', '', '') - self.params[1052] = Param(1052, 1, u'Shoe Height', u'shoes', 0.1, 0, 1, 0.1, '', '', '') - self.params[1053] = Param(1053, 1, u'Shoe Height bump', u'shoes', 0.1, 0, 1, 0.1, '', '', '') - self.params[1054] = Param(1054, 1, u'Pants Length', u'underpants', .3, 0, 1, .3, '', '', '') - self.params[1055] = Param(1055, 1, u'Pants Length', u'underpants', .3, 0, 1, .3, '', '', '') - self.params[1056] = Param(1056, 1, u'Pants Waist', u'underpants', .8, 0, 1, .8, '', '', '') - self.params[1057] = Param(1057, 1, u'Pants Waist', u'underpants', .8, 0, 1, .8, '', '', '') - self.params[1058] = Param(1058, 1, u'Glove Length', u'gloves', .8, .01, 1, .8, '', '', '') - self.params[1059] = Param(1059, 1, u'Glove Length bump', u'gloves', .8, .01, 1, .8, '', '', '') - self.params[1060] = Param(1060, 1, u'Glove Fingers', u'gloves', 1, .01, 1, 1, '', '', '') - self.params[1061] = Param(1061, 1, u'Glove Fingers bump', u'gloves', 1, .01, 1, 1, '', '', '') - - -