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 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 _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 setUp(self): self.region = Region(global_x=256, global_y=256, seed_capability_url='fake_url', udp_blacklist=[], sim_ip=1, sim_port=1, circuit_code=1, agent=None, settings=None, message_handler=None)
def setUp(self): self.settings = Settings() self.settings.ENABLE_DEFERRED_PACKET_PARSING = False self.deserializer = UDPMessageDeserializer(settings=self.settings) self.region = Region() self.region.SimName = 'TestMe' self.object_store = ObjectManager(region=self.region, settings=self.settings, message_handler=MessageHandler()) self.object_store.enable_callbacks() self.data = []
def onMapBlockReply(packet): """ handles the MapBlockReply message from a simulator """ for data in packet['Data']: if data['Name'].lower() == region_name: # Stop listening once we have the data we asked for handler.unsubscribe(onMapBlockReply) region_handle = Region.gridxy_to_handle( data['X'], data['Y']) callback(region_handle) return
def onMapItemReply(packet): item_type = packet['RequestData'][0]['ItemType'] if item_type == MapItem.AgentLocations: for data in packet['Data']: count = data['Extra'] x = data['X'] y = data['Y'] if count and Region.globalxy_to_handle(x, y) == region_handle: cluster_list.append( (count, x % Region.WIDTH, y % Region.WIDTH)) # This end condition is unreliable, and determined by inspecting # the simulator code. The viewer just receives the packets and # displays data as it appears, but has no completion indicator. if len(packet['Data']) < BLOCK_COUNT_PER_PACKET: handler.unsubscribe(onMapItemReply) callback(cluster_list)
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)