class Agent(object): """ The Agent class is a container for agent specific data. 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.client.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.local_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) self.map_service = MapService(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 agent_id->(first_name, last_name); per agent to prevent info leaks self.agent_id_map = {} if self.settings.LOG_VERBOSE: logger.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: logger.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: eventlet.spawn(self._monitor_for_new_regions) if connect_region: self._enable_current_region() eventlet.spawn(self.agent_updater) 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: logger.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'] if self.login_response.has_key('udp_blacklist'): 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.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: logger.info("Spawning a coroutine for connecting to the agent's host region.") 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() logger.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) logger.info("Enabling a child region with ip:port of %s" % (str(region_params['IP']) + ":" + str(region_params['Port']))) if self.settings.LOG_COROUTINE_SPAWNS: logger.info("Spawning a coroutine for connecting to a neighboring region.") eventlet.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) eventlet.sleep(10) 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['Message_Data'][0]['sim-ip-and-port'] == str(region.sim_ip) + ":" + str(region.sim_port)] if region != []: region[0]._set_seed_capability(message['Message_Data'][0]['seed-capability']) region[0]._get_region_capabilities() logger.debug('Spawning neighboring region event queue connection') region[0]._startEventQueue() def enable_callbacks(self): """ enable the Agents() callback handlers for packet received events """ if self.settings.ENABLE_INVENTORY_MANAGEMENT: while self.region.capabilities == {}: eventlet.sleep(5) 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]) logger.info("Using the capability based inventory management mechanism") self.inventory = AIS(self, caps) else: logger.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) # enable callbacks for the agent class to set up handling for related messages 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) onAvatarAnimation_received = self.region.message_handler.register('AvatarAnimation') onAvatarAnimation_received.subscribe(self.onAvatarAnimation) 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[blockname][0] for var in block.var_list: payload[var] = block[var] 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: logger.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 """ logger.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['AgentData'][0]['AgentID'] if self.firstname == None: self.firstname = packet['AgentData'][0]['FirstName'] if self.lastname == None: self.firstname = packet['AgentData'][0]['LastName'] self.active_group_title = packet['AgentData'][0]['GroupTitle'] self.active_group_id = packet['AgentData'][0]['ActiveGroupID'] self.active_group_powers = packet['AgentData'][0]['GroupPowers'] self.active_group_name = packet['AgentData'][0]['GroupName'] def onAgentMovementComplete(self, packet): """ callback handler for received AgentMovementComplete messages which populates various Agent() and Region() attributes """ self.Position = packet['Data'][0]['Position'] if self.Position == None: logger.warning("agent.position is None agent.py") self.LookAt = packet['Data'][0]['LookAt'] self.region.RegionHandle = packet['Data'][0]['RegionHandle'] #agent.Timestamp = packet['Data'][0]['Timestamp'] self.region.ChannelVersion = packet['SimData'][0]['ChannelVersion'] # 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['HealthData'][0]['Health'] def onChatFromSimulator(self, packet): """ callback handler for received ChatFromSimulator messages which parses and fires a ChatReceived event. """ FromName = packet['ChatData'][0]['FromName'] SourceID = packet['ChatData'][0]['SourceID'] OwnerID = packet['ChatData'][0]['OwnerID'] SourceType = packet['ChatData'][0]['SourceType'] ChatType = packet['ChatData'][0]['ChatType'] Audible = packet['ChatData'][0]['Audible'] Position = packet['ChatData'][0]['Position'] _Message = packet['ChatData'][0]['Message'] message = AppEvent('ChatReceived', FromName = FromName, SourceID = SourceID, OwnerID = OwnerID, SourceType = SourceType, ChatType = ChatType, Audible = Audible, Position = Position, Message = _Message) logger.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['MessageBlock'][0]['Dialog'] FromAgentID = packet['AgentData'][0]['AgentID'] if Dialog == ImprovedIMDialogue.InventoryOffered: self.inventory.handle_inventory_offer(packet) elif Dialog == ImprovedIMDialogue.InventoryAccepted: if str(FromAgentID) != str(self.agent_id): FromAgentName = packet['MessageBlock'][0]['FromAgentName'] InventoryName = packet['MessageBlock'][0]['Message'] logger.info("Agent %s accepted the inventory offer." % (FromAgentName)) elif Dialog == ImprovedIMDialogue.InventoryDeclined: if str(FromAgentID) != str(self.agent_id): FromAgentName = packet['MessageBlock'][0]['FromAgentName'] InventoryName = packet['MessageBlock'][0]['Message'] logger.info("Agent %s declined the inventory offer." % (FromAgentName)) elif Dialog == ImprovedIMDialogue.FromAgent: RegionID = packet['MessageBlock'][0]['RegionID'] Position = packet['MessageBlock'][0]['Position'] ID = packet['MessageBlock'][0]['ID'] FromAgentName = packet['MessageBlock'][0]['FromAgentName'] _Message = packet['MessageBlock'][0]['Message'] message = AppEvent('InstantMessageReceived', FromAgentID = FromAgentID, RegionID = RegionID, Position = Position, ID = ID, FromAgentName = FromAgentName, Message = _Message) logger.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['AlertData'][0]['Message'] message = AppEvent('AlertMessage', AlertMessage = AlertMessage) logger.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['SimulatorInfo'][0]['IP']] IP = '.'.join([str(x) for x in IP]) Port = packet['SimulatorInfo'][0]['Port'] # not sure what this is, but pass it up Handle = [ord(x) for x in packet['SimulatorInfo'][0]['Handle']] region_params = {'IP': IP, 'Port': Port, 'Handle': Handle} logger.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 """ logger.info('Received EstablishAgentCommunication for %s' % (message['Message_Data'][0]['sim-ip-and-port'])) 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['Message_Data'][0]['sim-ip-and-port']) and region.message_manager.event_queue != None: if region.message_manager.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, landmark_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.""" logger.info('teleport name=%s handle=%s id=%s', str(region_name), str(region_handle), str(region_id)) # Landmarks are easy, get those out of the way if landmark_id: logger.info('sending landmark TP request packet') packet = Message('TeleportLandmarkRequest', Block('Info', AgentID = self.agent_id, SessionID = self.session_id, LandmarkID = UUID(landmark_id))) self.region.enqueue_message(packet) return # 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 region_id: logger.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: logger.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: logger.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.map_service.request_handle( region_name, lambda handle: self.teleport(region_handle=handle, position=position, look_at=look_at)) def onTeleportFinish(self, packet): """Handle the end of a successful teleport""" logger.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['Info'][0]['RegionHandle'] region_x, region_y = Region.handle_to_globalxy(region_handle) # packed binary to dotted-octet sim_ip = packet['Info'][0]['SimIP'] sim_ip = '.'.join(map(str, struct.unpack('BBBB', sim_ip))) # *TODO: Make this more graceful logger.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 = [] logger.info("Enabling new region") self._enable_current_region( region_x = region_x, region_y = region_y, seed_capability = packet['Info'][0]['SeedCapability'], sim_ip = sim_ip, sim_port = packet['Info'][0]['SimPort'] ) 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 """ logger.info('UUIDNameReplyPacket received') cbdata = [] for block in packet['UUIDNameBlock']: agent_id = str(block['ID']) first_name = block['FirstName'] last_name = block['LastName'] 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: logger.info('Still waiting on %d names in send_UUIDNameRequest', len(missing)) handler.subscribe(onUUIDNameReply) logger.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 """ logger.info('MoneyBalanceReply received') handler.unsubscribe(onMoneyBalanceReply) # One-shot handler balance = packet['MoneyData'][0]['MoneyBalance'] callback(balance) handler.subscribe(onMoneyBalanceReply) logger.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""" logger.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 agent_updater(self): """ Sends AgentUpdate message every so often, for movement purposes. Needs a better name """ while self.connected: self._send_update() eventlet.sleep(1.0/self.settings.AGENT_UPDATES_PER_SECOND) def sit_on_ground(self): """Sit on the ground at the agent's current location""" self.control_flags |= AgentControlFlags.SitOnGround 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 def walk(self, walking=True): """Walk forward""" if walking: self.control_flags |= AgentControlFlags.AtPos else: self.control_flags &= ~AgentControlFlags.AtPos def fly(self, flying=True): """Start or stop flying""" if flying: self.control_flags |= AgentControlFlags.Fly else: self.control_flags &= ~AgentControlFlags.Fly def stop(self): self.control_flags = AgentControlFlags.Stop def up(self, going_up=True): """Start or stop going up""" if going_up: self.control_flags |= AgentControlFlags.UpPos else: self.control_flags &= ~AgentControlFlags.UpPos def _send_update(self): """ force a send of an AgentUpdate message to the host simulator """ #logger.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 ) def onAvatarAnimation(self, packet): """Ensure auto-triggered animations are stopped.""" # See newview/llagent.cpp if packet['Sender'][0]['ID'] == self.agent_id: for anim in packet['AnimationList']: if anim['AnimID'] in (AgentAnimations.STANDUP, AgentAnimations.PRE_JUMP, AgentAnimations.LAND, AgentAnimations.MEDIUM_LAND): self.control_flags |= AgentControlFlags.FinishAnim self._send_update() self.control_flags &= ~AgentControlFlags.FinishAnim def touch(self, objectID): """ Touches an inworld rezz'ed object """ self.grab(objectID) self.degrab(objectID) def grab(self, objectID, grabOffset = Vector3(), uvCoord = Vector3(), stCoord = Vector3(), faceIndex=0, position=Vector3(), normal=Vector3(), binormal=Vector3()): packet = Message('ObjectGrab', Block('AgentData', AgentID = self.agent_id, SessionID = self.session_id), Block('ObjectData', LocalID = objectID, GrabOffset = grabOffset), [Block('SurfaceInfo', UVCoord = uvCoord, STCoord = stCoord, FaceIndex = faceIndex, Position = position, Normal = normal, Binormal = binormal)]) self.region.enqueue_message(packet) def degrab(self, objectID, uvCoord = Vector3(), stCoord = Vector3(), faceIndex=0, position=Vector3(), normal=Vector3(), binormal=Vector3()): packet = Message('ObjectDeGrab', Block('AgentData', AgentID = self.agent_id, SessionID = self.session_id), Block('ObjectData', LocalID = objectID), [Block('SurfaceInfo', UVCoord = uvCoord, STCoord = stCoord, FaceIndex = faceIndex, Position = position, Normal = normal, Binormal = binormal)]) self.region.enqueue_message(packet) def grabUpdate(self, objectID, grabPosition = Vector3(), grabOffset = Vector3(), uvCoord = Vector3(), stCoord = Vector3(), faceIndex=0, position=Vector3(), normal=Vector3(), binormal=Vector3()): packet = Message('ObjectGrabUpdate', Block('AgentData', AgentID = self.agent_id, SessionID = self.session_id), Block('ObjectData', LocalID = objectID, GrabOffsetInitial = grabOffset, GrabPostion = grabPosition), [Block('SurfaceInfo', UVCoord = uvCoord, STCoord = stCoord, FaceIndex = faceIndex, Position = position, Normal = normal, Binormal = binormal)]) self.region.enqueue_message(packet)
def enable_callbacks(self): """ enable the Agents() callback handlers for packet received events """ if self.settings.ENABLE_INVENTORY_MANAGEMENT: while self.region.capabilities == {}: eventlet.sleep(5) 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]) logger.info("Using the capability based inventory management mechanism") self.inventory = AIS(self, caps) else: logger.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) # enable callbacks for the agent class to set up handling for related messages 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) onAvatarAnimation_received = self.region.message_handler.register('AvatarAnimation') onAvatarAnimation_received.subscribe(self.onAvatarAnimation)
def onAgentConnected(self, agent): self.inventory = UDP_Inventory(agent) self.filename = None self.xfer_list = defaultdict(str)
class InventoryHandler(Handler): def onAgentConnected(self, agent): self.inventory = UDP_Inventory(agent) self.filename = None self.xfer_list = defaultdict(str) def onRegionConnect(self, region): res = region.message_handler.register("InventoryDescendents") res.subscribe(self.onInventoryDescendents) res = region.message_handler.register("UpdateCreateInventoryItem") res.subscribe(self.onUpdateCreateInventoryItem) res = region.message_handler.register("ReplyTaskInventory") res.subscribe(self.onReplyTaskInventory) res = region.message_handler.register("SendXferPacket") res.subscribe(self.onSendXferPacket) self.inventory.enable_callbacks() def onRegionConnected(self, region): client = self.manager.client # send inventory skeleton if hasattr(client, 'login_response' ) and 'inventory-skeleton' in client.login_response: self.out_queue.put([ "InventorySkeleton", client.login_response['inventory-skeleton'] ]) self.inventory._parse_folders_from_login_response() def sendXferConfirmPacket(self, agent, xferid, packet): packet = Message('ConfirmXferPacket', Block('XferID', ID=xferid, Packet=packet)) agent.region.enqueue_message(packet) def sendXferRequest(self, agent, filename): xferid = random.getrandbits(64) packet = Message( 'RequestXfer', Block('XferID', ID=xferid, Filename=filename, FilePath=0, DeleteOnCompletion=False, UseBigPackets=False, VFileID=UUID(str("00000000-0000-0000-0000-000000000000")), VFileType=0)) agent.region.enqueue_message(packet) def processFetchInventoryDescendents(self, *args): self.logger.debug('inventory processFetchInventoryDescendents') self.inventory.sendFetchInventoryDescendentsRequest(*args) def processRequestTaskInventory(self, *args): self.logger.debug('inventory processRequestTaskInventory') sendRequestTaskInventory(self.manager.client, *args) def processRezObject(self, item_id, raystart, rayend): self.logger.debug('inventory processRezObject') items = [ _item for _item in self.inventory.items if str(_item.ItemID) == item_id ] if len(items): item = items[0] else: return self.logger.debug('sendRezObject') sendRezObject(self.manager.client, item, raystart, rayend) def serializableInventory(self): folders = [{ 'Name': member.Name, 'ParentID': str(member.ParentID), 'FolderID': str(member.FolderID), 'Descendents': int(member.Descendents) } for member in self.inventory.folders] items = [{ 'Name': member.Name, 'FolderID': str(member.FolderID), 'AssetID': str(member.AssetID), 'ItemID': str(member.ItemID), 'InvType': member.InvType } for member in self.inventory.items] return folders, items def onReplyTaskInventory(self, packet): logger = self.logger logger.debug('onReplyTaskInventory') print("ReplyTaskInventory", packet['InventoryData'][0]['TaskID']) filename = packet['InventoryData'][0]['Filename'] self.filename = filename self.sendXferRequest(self.manager.client, filename) def onInventoryDescendents(self, packet): logger = self.logger logger.debug('onInventoryDescendents') folder_id = packet['AgentData'][0]['FolderID'] folders, items = self.serializableInventory() self.out_queue.put( ['InventoryDescendents', str(folder_id), folders, items]) def onSendXferPacket(self, packet): logger = self.logger logger.debug('onSendXferPacket') xferid = packet['XferID'][0]['ID'] xferpacket = packet['XferID'][0]['Packet'] data = packet['DataPacket'][0]['Data'] if int(xferpacket) == 0 or int(xferpacket) == 0x80000000: data = data[4:] self.xfer_list[xferid] += data if xferpacket & 0x80000000: #last packet p = InventoryStringParser(self.xfer_list[xferid], 'inv_') self.out_queue.put(['ObjectInventory', p.result]) del self.xfer_list[xferid] self.sendXferConfirmPacket(self.manager.client, xferid, xferpacket) def onUpdateCreateInventoryItem(self, packet): logger = self.logger logger.debug('onUpdateCreateInventoryItem') inv_data = packet['InventoryData'][0] item = InventoryItem( inv_data['ItemID'], inv_data['FolderID'], inv_data['CreatorID'], inv_data['OwnerID'], inv_data['GroupID'], inv_data['BaseMask'], inv_data['OwnerMask'], inv_data['GroupMask'], inv_data['EveryoneMask'], inv_data['NextOwnerMask'], inv_data['GroupOwned'], inv_data['AssetID'], inv_data['Type'], inv_data['InvType'], inv_data['Flags'], inv_data['SaleType'], inv_data['SalePrice'], inv_data['Name'], inv_data['Description'], inv_data['CreationDate'], inv_data['CRC']) self.addInventoryItem(item) def addInventoryItem(self, item): self.inventory._store_inventory_item(item) for folder in self.inventory.folders: if str(folder.FolderID) == str(item.FolderID): folder.Descendents += 1 break folders, items = self.serializableInventory() self.out_queue.put( ['InventoryDescendents', str(item.FolderID), folders, items]) def removeInventoryItem(self, item_id): items = self.inventory.items folders = self.inventory.folders folder_id = None for _item in items: if str(_item.ItemID) == str(item_id): folder_id = str(_item.FolderID) items.remove(_item) break if folder_id: for folder in folders: if str(folder.FolderID) == str(folder_id): folder.Descendents -= 1 break def findItem(self, item_id): items = self.inventory.items for _item in items: if str(_item.ItemID) == str(item_id): return _item def processUpdateInventoryItem(self, item_id, trID, asset_type, inv_type, name, desc): agent = self.manager.client item = self.findItem(item_id) print("UPDATING INVENTORY ITEM", item, item_id) if item: self.sendUpdateInventoryItem(agent, trID, [item]) def processRezScript(self, obj_uuid_str, item_id): agent = self.manager.client obj = agent.region.objects.get_object_from_store(FullID=obj_uuid_str) item = self.findItem(item_id) if obj and item: agent.region.objects.send_RezScript(agent, obj, UUID(item_id), Name=item.Name) def processUpdateTaskInventoryItem(self, obj_id, item_id, trID, item_data): agent = self.manager.client obj = agent.region.objects.get_object_from_store(FullID=obj_id) if obj: self.sendUpdateTaskInventoryItem(agent, obj, trID, item_id, item_data) def processRemoveTaskInventoryItem(self, obj_id, item_id): agent = self.manager.client obj = agent.region.objects.get_object_from_store(FullID=obj_id) if obj: packet = Message( 'RemoveTaskInventory', Block('AgentData', AgentID=agent.agent_id, SessionID=agent.session_id), Block('InventoryData', LocalID=obj.LocalID, ItemID=UUID(item_id))) agent.region.enqueue_message(packet) def sendUpdateTaskInventoryItem(self, agent, obj, transaction_id, item_id, item): """ sends an UpdateInventoryItem packet to a region this function expects an InventoryItem instance already with updated data """ print(transaction_id, item_id, item) packet = Message( 'UpdateTaskInventory', Block('AgentData', AgentID=agent.agent_id, SessionID=agent.session_id), Block('UpdateData', LocalID=obj.LocalID, Key=0), # only 0 seems implemented in opensim Block('InventoryData', ItemID=UUID(item_id), FolderID=UUID(item['parent_id']), CreatorID=UUID(item['permissions']['creator_id']), OwnerID=UUID(item['permissions']['owner_id']), GroupID=UUID(item['permissions']['group_id']), BaseMask=int(item['permissions']['base_mask']), OwnerMask=int(item['permissions']['base_mask']), GroupMask=int(item['permissions']['group_mask']), EveryoneMask=int(item['permissions']['everyone_mask']), NextOwnerMask=int(item['permissions']['next_owner_mask']), GroupOwned=item['permissions']['group_id'] != '00000000-0000-0000-0000-000000000000', TransactionID=UUID(transaction_id), Type=10, InvType=10, Flags=int(item['flags']), SaleType=0, SalePrice=int(item['sale_info']['sale_price']), Name=item['name'].split('|')[0], Description=item['desc'], CreationDate=int(item['creation_date']), CRC=0)) agent.region.enqueue_message(packet) def sendUpdateInventoryItem(self, agent, transaction_id, 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=random.randint(0, pow(2, 32) - 1), 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(transaction_id), 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 processCreateInventoryItem(self, trID, asset_type, inv_type, name, desc): wearable_type = 0 agent = self.manager.client next_owner_permission = 0 print("processCreateInventoryItem", trID, asset_type) self.inventory.send_CreateInventoryItem(agent.agent_id, agent.session_id, 0, UUID(), UUID(trID), next_owner_permission, asset_type, inv_type, wearable_type, name, desc) def processRemoveInventoryItem(self, item_id): logger = self.logger logger.debug('processRemoveIntenvoryItem') self.removeInventoryItem(item_id) client = self.manager.client self.inventory.send_RemoveInventoryItem(client.agent_id, client.session_id, UUID(str(item_id)))
class InventoryHandler(Handler): def onAgentConnected(self, agent): self.inventory = UDP_Inventory(agent) self.filename = None self.xfer_list = defaultdict(str) def onRegionConnect(self, region): res = region.message_handler.register("InventoryDescendents") res.subscribe(self.onInventoryDescendents) res = region.message_handler.register("UpdateCreateInventoryItem") res.subscribe(self.onUpdateCreateInventoryItem) res = region.message_handler.register("ReplyTaskInventory") res.subscribe(self.onReplyTaskInventory) res = region.message_handler.register("SendXferPacket") res.subscribe(self.onSendXferPacket) self.inventory.enable_callbacks() def onRegionConnected(self, region): client = self.manager.client # send inventory skeleton if hasattr(client, 'login_response') and 'inventory-skeleton' in client.login_response: self.out_queue.put(["InventorySkeleton", client.login_response['inventory-skeleton']]) self.inventory._parse_folders_from_login_response() def sendXferConfirmPacket(self, agent, xferid, packet): packet = Message('ConfirmXferPacket', Block('XferID', ID = xferid, Packet = packet)) agent.region.enqueue_message(packet) def sendXferRequest(self, agent, filename): xferid = random.getrandbits(64) packet = Message('RequestXfer', Block('XferID', ID = xferid, Filename = filename, FilePath = 0, DeleteOnCompletion = False, UseBigPackets = False, VFileID = UUID(str("00000000-0000-0000-0000-000000000000")), VFileType = 0)) agent.region.enqueue_message(packet) def processFetchInventoryDescendents(self, *args): self.logger.debug('inventory processFetchInventoryDescendents') self.inventory.sendFetchInventoryDescendentsRequest(*args) def processRequestTaskInventory(self, *args): self.logger.debug('inventory processRequestTaskInventory') sendRequestTaskInventory(self.manager.client, *args) def processRezObject(self, item_id, raystart, rayend): self.logger.debug('inventory processRezObject') items = [_item for _item in self.inventory.items if str(_item.ItemID) == item_id] if len(items): item = items[0] else: return self.logger.debug('sendRezObject') sendRezObject(self.manager.client, item, raystart, rayend) def serializableInventory(self): folders = [{'Name' : member.Name, 'ParentID' : str(member.ParentID), 'FolderID' : str(member.FolderID), 'Descendents' : int(member.Descendents)} for member in self.inventory.folders] items = [{'Name' : member.Name, 'FolderID' : str(member.FolderID), 'AssetID' : str(member.AssetID), 'ItemID' : str(member.ItemID), 'InvType' : member.InvType} for member in self.inventory.items] return folders, items def onReplyTaskInventory(self, packet): logger = self.logger logger.debug('onReplyTaskInventory') print("ReplyTaskInventory", packet['InventoryData'][0]['TaskID']) filename = packet['InventoryData'][0]['Filename'] self.filename = filename self.sendXferRequest(self.manager.client, filename) def onInventoryDescendents(self, packet): logger = self.logger logger.debug('onInventoryDescendents') folder_id = packet['AgentData'][0]['FolderID'] folders, items = self.serializableInventory() self.out_queue.put(['InventoryDescendents', str(folder_id), folders, items]) def onSendXferPacket(self, packet): logger = self.logger logger.debug('onSendXferPacket') xferid = packet['XferID'][0]['ID'] xferpacket = packet['XferID'][0]['Packet'] data = packet['DataPacket'][0]['Data'] if int(xferpacket) == 0 or int(xferpacket) == 0x80000000: data = data[4:] self.xfer_list[xferid] += data if xferpacket & 0x80000000: #last packet p = InventoryStringParser(self.xfer_list[xferid], 'inv_') self.out_queue.put(['ObjectInventory', p.result]) del self.xfer_list[xferid] self.sendXferConfirmPacket(self.manager.client, xferid, xferpacket) def onUpdateCreateInventoryItem(self, packet): logger = self.logger logger.debug('onUpdateCreateInventoryItem') inv_data = packet['InventoryData'][0] item = InventoryItem(inv_data['ItemID'], inv_data['FolderID'], inv_data['CreatorID'], inv_data['OwnerID'], inv_data['GroupID'], inv_data['BaseMask'], inv_data['OwnerMask'], inv_data['GroupMask'], inv_data['EveryoneMask'], inv_data['NextOwnerMask'], inv_data['GroupOwned'], inv_data['AssetID'], inv_data['Type'], inv_data['InvType'], inv_data['Flags'], inv_data['SaleType'], inv_data['SalePrice'], inv_data['Name'], inv_data['Description'], inv_data['CreationDate'], inv_data['CRC']) self.addInventoryItem(item) def addInventoryItem(self, item): self.inventory._store_inventory_item(item) for folder in self.inventory.folders: if str(folder.FolderID) == str(item.FolderID): folder.Descendents += 1 break folders, items = self.serializableInventory() self.out_queue.put(['InventoryDescendents', str(item.FolderID), folders, items]) def removeInventoryItem(self, item_id): items = self.inventory.items folders = self.inventory.folders folder_id = None for _item in items: if str(_item.ItemID) == str(item_id): folder_id = str(_item.FolderID) items.remove(_item) break if folder_id: for folder in folders: if str(folder.FolderID) == str(folder_id): folder.Descendents -= 1 break def findItem(self, item_id): items = self.inventory.items for _item in items: if str(_item.ItemID) == str(item_id): return _item def processUpdateInventoryItem(self, item_id, trID, asset_type, inv_type, name, desc): agent = self.manager.client item = self.findItem(item_id) print("UPDATING INVENTORY ITEM", item, item_id) if item: self.sendUpdateInventoryItem(agent, trID, [item]) def processRezScript(self, obj_uuid_str, item_id): agent = self.manager.client obj = agent.region.objects.get_object_from_store(FullID = obj_uuid_str) item = self.findItem(item_id) if obj and item: agent.region.objects.send_RezScript(agent, obj, UUID(item_id), Name=item.Name) def processUpdateTaskInventoryItem(self, obj_id, item_id, trID, item_data): agent = self.manager.client obj = agent.region.objects.get_object_from_store(FullID = obj_id) if obj: self.sendUpdateTaskInventoryItem(agent, obj, trID, item_id, item_data) def processRemoveTaskInventoryItem(self, obj_id, item_id): agent = self.manager.client obj = agent.region.objects.get_object_from_store(FullID = obj_id) if obj: packet = Message('RemoveTaskInventory', Block('AgentData', AgentID = agent.agent_id, SessionID = agent.session_id), Block('InventoryData', LocalID = obj.LocalID, ItemID = UUID(item_id))) agent.region.enqueue_message(packet) def sendUpdateTaskInventoryItem(self, agent, obj, transaction_id, item_id, item): """ sends an UpdateInventoryItem packet to a region this function expects an InventoryItem instance already with updated data """ print(transaction_id, item_id, item) packet = Message('UpdateTaskInventory', Block('AgentData', AgentID = agent.agent_id, SessionID = agent.session_id), Block('UpdateData', LocalID = obj.LocalID, Key = 0), # only 0 seems implemented in opensim Block('InventoryData', ItemID = UUID(item_id), FolderID = UUID(item['parent_id']), CreatorID = UUID(item['permissions']['creator_id']), OwnerID = UUID(item['permissions']['owner_id']), GroupID = UUID(item['permissions']['group_id']), BaseMask = int(item['permissions']['base_mask']), OwnerMask = int(item['permissions']['base_mask']), GroupMask = int(item['permissions']['group_mask']), EveryoneMask = int(item['permissions']['everyone_mask']), NextOwnerMask = int(item['permissions']['next_owner_mask']), GroupOwned = item['permissions']['group_id'] != '00000000-0000-0000-0000-000000000000', TransactionID = UUID(transaction_id), Type = 10, InvType = 10, Flags = int(item['flags']), SaleType = 0, SalePrice = int(item['sale_info']['sale_price']), Name = item['name'].split('|')[0], Description = item['desc'], CreationDate = int(item['creation_date']), CRC = 0)) agent.region.enqueue_message(packet) def sendUpdateInventoryItem(self, agent, transaction_id, 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 = random.randint (0, pow (2, 32)-1), 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(transaction_id), 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 processCreateInventoryItem(self, trID, asset_type, inv_type, name, desc): wearable_type = 0 agent = self.manager.client next_owner_permission = 0 print("processCreateInventoryItem", trID, asset_type) self.inventory.send_CreateInventoryItem(agent.agent_id, agent.session_id, 0, UUID(), UUID(trID), next_owner_permission, asset_type, inv_type, wearable_type, name, desc) def processRemoveInventoryItem(self, item_id): logger = self.logger logger.debug('processRemoveIntenvoryItem') self.removeInventoryItem(item_id) client = self.manager.client self.inventory.send_RemoveInventoryItem(client.agent_id, client.session_id, UUID(str(item_id)))