def unpack_v3(data, offset, min, max): vector3 = Vector3(X=Helpers.packed_u16_to_float(data, offset, min, max), Y=Helpers.packed_u16_to_float(data, offset + 2, min, max), Z=Helpers.packed_u16_to_float(data, offset + 4, min, max)) return vector3
def unpack_v3(data, offset, min, max): vector3 = Vector3(X=Helpers.packed_u16_to_float(data, offset, min, max), Y=Helpers.packed_u16_to_float(data, offset+2, min, max), Z=Helpers.packed_u16_to_float(data, offset+4, min, max)) return vector3
def unpack_q(data, offset): min = -1.0 max = 1.0 q = Quaternion(X=Helpers.packed_u16_to_float(data, offset, min, max), Y=Helpers.packed_u16_to_float(data, offset + 2, min, max), Z=Helpers.packed_u16_to_float(data, offset + 4, min, max), W=Helpers.packed_u16_to_float(data, offset + 6, min, max)) return q
def __init__(self, udp_client = None, settings = None, message_handler = None, message_template = None, message_xml = None): #holds the details of the message, or how the messages should be sent, #built, and read self.packets_in = 0 self.packets_out = 0 self.circuit_manager = CircuitManager() self.data_unpacker = DataUnpacker() #the ID of the packet we most recently received self.receive_packet_id = -1 if udp_client == None: self.udp_client = NetUDPClient() else: self.udp_client = udp_client self.udp_client.start_udp_connection() # allow the settings to be passed in # otherwise, grab the defaults if settings != None: self.settings = settings else: self.settings = Settings() # allow the passing in of message_template.xml as a file handle if not message_template: self.message_template = None else: if isinstance(message_template, file): self.message_template = message_template else: log.warning("%s parameter is expected to be a filehandle, it is a %s. \ Using the embedded message_template.msg" % (message_template, type(message_template))) self.message_template = None if not message_xml: self.message_xml = MessageDotXML() else: self.message_xml = message_xml self.helpers = Helpers() # 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: from pyogp.lib.base.message.message_handler import MessageHandler self.message_handler = MessageHandler() # set up our parsers self.udp_deserializer = UDPMessageDeserializer(self.message_handler, self.settings, message_template = self.message_template) self.udp_serializer = UDPMessageSerializer(message_template = self.message_template)
def unpack_q(data, offset): min = -1.0 max = 1.0 q = Quaternion(X=Helpers.packed_u16_to_float(data, offset, min, max), Y=Helpers.packed_u16_to_float(data, offset+2, min, max), Z=Helpers.packed_u16_to_float(data, offset+4, min, max), W=Helpers.packed_u16_to_float(data, offset+6, min, max)) return q
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: logger.debug("Initializing the parcel manager in region %s." % (self.region.SimName))
def __init__(self, agent, settings=None): """ initialize the appearance manager """ 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[i] = 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.requests = []
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))
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 request_asset(self, assetID, assetType, isPriority, callback=None, itemID=None): """ Sends a TransferRequest to the sim for asset assetID with type assetType, will call callback with the assetID and True with the asset received or False if request failed. On successful request the asset is store in self.assets TODO add a timeout """ 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 """ # fill in data for Asset in the requests queue and pop it off and story in assets dict if str(transferID) == str(packet['TransferData'][0]['TransferID']): # insert packet self.assets[str(assetID)].store_packet( packet['TransferData'][0]['Packet'], packet['TransferData'][0]['Data'], packet['TransferData'][0]['Status']) if self.assets[str(assetID)].is_downloaded(): self.assets[str(assetID)].assemble_data() if callback != None: callback(assetID, True) transferPacketHandler.unsubscribe(onTransferPacket) else: pass # wait for more packets def onTransferInfo(packet): """ Status of TransferRequest Account for size and multiple packets """ if not self.assets.has_key(str(assetID)): self.assets[str(assetID)] = Asset(assetID, assetType) if str(transferID) == str(packet['TransferInfo'][0]['TransferID']): status = packet['TransferInfo'][0]["Status"] if status != TransferStatus.OK: logger.warning("Request for asset %s failed with status %s" \ % (assetID, status)) if callback != None: callback(assetID, False) transferPacketHandler.unsubscribe(onTransferPacket) else: self.assets[str( assetID)].size = packet['TransferInfo'][0]['Size'] transferInfoHandler.unsubscribe(onTransferInfo) transferInfoHandler.subscribe(onTransferInfo) transferPacketHandler.subscribe(onTransferPacket) if isPriority: priority = 1.0 else: priority = 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 __pack_quat(self, endian, quat): if isinstance(quat, Quaternion): quat = quat() # convert to tuple vec = Helpers.pack_quaternion_to_vector3(quat) return self.__pack_tuple(endian, vec, 'f')
class UDPDispatcher(object): #implements(IUDPDispatcher) def __init__(self, udp_client = None, settings = None, message_handler = None, message_template = None, message_xml = None): #holds the details of the message, or how the messages should be sent, #built, and read self.packets_in = 0 self.packets_out = 0 self.circuit_manager = CircuitManager() self.data_unpacker = DataUnpacker() #the ID of the packet we most recently received self.receive_packet_id = -1 if udp_client == None: self.udp_client = NetUDPClient() else: self.udp_client = udp_client self.udp_client.start_udp_connection() # allow the settings to be passed in # otherwise, grab the defaults if settings != None: self.settings = settings else: self.settings = Settings() # allow the passing in of message_template.xml as a file handle if not message_template: self.message_template = None else: if isinstance(message_template, file): self.message_template = message_template else: log.warning("%s parameter is expected to be a filehandle, it is a %s. \ Using the embedded message_template.msg" % (message_template, type(message_template))) self.message_template = None if not message_xml: self.message_xml = MessageDotXML() else: self.message_xml = message_xml self.helpers = Helpers() # 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: from pyogp.lib.base.message.message_handler import MessageHandler self.message_handler = MessageHandler() # set up our parsers self.udp_deserializer = UDPMessageDeserializer(self.message_handler, self.settings, message_template = self.message_template) self.udp_serializer = UDPMessageSerializer(message_template = self.message_template) def find_circuit(self, host): circuit = self.circuit_manager.get_circuit(host) if circuit == None: #there is a case where we want to return None, #when the last packet was protected circuit = self.circuit_manager.add_circuit(host, self.receive_packet_id) return circuit def receive_check(self, host, msg_buf, msg_size): #determine if we have any messages that can be received through UDP #also, check and decode the message we have received recv_packet = None #msg_buf, msg_size = self.udp_client.receive_packet(self.socket) #we have a message if msg_size > 0: #determine sender #host = self.udp_client.get_sender() circuit = self.find_circuit(host) if circuit == None: raise exc.CircuitNotFound(host, 'preparing to check for packets') self.packets_in += 1 recv_packet = self.udp_deserializer.deserialize(msg_buf) #couldn't deserialize if recv_packet == None: # if its sent as reliable, we should ack it even if we aren't going to parse it # since we can skip parsing the packet in self.udp_deserializer # this indicate reliable send_flags = ord(msg_buf[0]) packet_id = self.data_unpacker.unpack_data(msg_buf, MsgType.MVT_U32, 1, endian_type=EndianType.BIG) # queue the ack up circuit.collect_ack(packet_id) return None #Case - trusted packets can only come in over trusted circuits if circuit.is_trusted and \ recv_packet.trusted == False: return None circuit.handle_packet(recv_packet) if self.settings.ENABLE_UDP_LOGGING: if self.settings.ENABLE_BYTES_TO_HEX_LOGGING: hex_string = '<=>' + self.helpers.bytes_to_hex(msg_buf) else: hex_string = '' if self.settings.ENABLE_HOST_LOGGING: host_string = ' (%s)' % (host) else: host_string = '' if not self.settings.PROXY_LOGGING: logger.debug('Received packet%s : %s (%s)%s' % (host_string, recv_packet.name, recv_packet.packet_id, hex_string)) if self.settings.HANDLE_PACKETS: self.message_handler.handle(recv_packet) return recv_packet def send_reliable(self, message, host, retries): """ Wants to be acked """ #sets up the message so send_message will add the RELIABLE flag to #the message return self.__send_message(message, host, reliable=True, retries=retries) def send_retry(self, message, host): """ This is a retry because we didn't get acked """ #sets up the message so send_message will add the RETRY flag to it return self.__send_message(host, message, retrying=True) def send_message(self, message, host): return self.__send_message(message, host) def __send_message(self, message, host, reliable=False, retries=0, retrying=False): """ Sends the message that is currently built to the desired host """ #make sure host is OK (ip and address aren't null) if host.is_ok() == False: return if isinstance(message,Message): packet = message else: packet = message() # enable monitoring of outgoing packets if self.settings.HANDLE_OUTGOING_PACKETS: self.message_handler.handle(packet) #use circuit manager to get the circuit to send on circuit = self.find_circuit(host) if reliable == True: circuit.prepare_packet(packet, PackFlags.LL_RELIABLE_FLAG, retries) if circuit.unack_packet_count <= 0: self.circuit_manager.unacked_circuits[host] = circuit elif retrying == True: circuit.prepare_packet(packet, PackFlags.LL_RESENT_FLAG) else: circuit.prepare_packet(packet) try: send_buffer = self.udp_serializer.serialize(packet) if self.settings.ENABLE_UDP_LOGGING: if packet.name in self.settings.UDP_SPAMMERS and self.settings.DISABLE_SPAMMERS: pass else: if self.settings.ENABLE_BYTES_TO_HEX_LOGGING: hex_string = '<=>' + self.helpers.bytes_to_hex(send_buffer) else: hex_string = '' if self.settings.ENABLE_HOST_LOGGING: host_string = ' (%s)' % (host) else: host_string = '' logger.debug('Sent packet %s : %s (%s)%s' % (host_string, packet.name, packet.packet_id, hex_string)) #TODO: remove this when testing a network self.udp_client.send_packet(send_buffer, host) self.packets_out += 1 return send_buffer except AssertionError: pass except Exception, error: logger.warning("Error trying to serialize the following packet: %s" % (packet)) traceback.print_exc() return