293 lines
9.9 KiB
Python
293 lines
9.9 KiB
Python
"""
|
|
@file agent.py
|
|
@date 2008-09-16
|
|
Contributors can be viewed at:
|
|
http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt
|
|
|
|
$LicenseInfo:firstyear=2008&license=apachev2$
|
|
|
|
Copyright 2008, Linden Research, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License").
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
or in
|
|
http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt
|
|
|
|
$/LicenseInfo$
|
|
"""
|
|
|
|
# standard python libs
|
|
from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG
|
|
import re
|
|
import sys
|
|
import signal
|
|
|
|
#related
|
|
from eventlet import api
|
|
|
|
# pyogp
|
|
from pyogp.lib.base.login import Login, LegacyLoginParams, OGPLoginParams
|
|
from pyogp.lib.base.exc import LoginError
|
|
from pyogp.lib.base.region import Region
|
|
from pyogp.lib.base.inventory import Inventory
|
|
|
|
# pyogp messaging
|
|
from pyogp.lib.base.message.packet_handler import PacketHandler
|
|
|
|
# initialize logging
|
|
logger = getLogger('pyogp.lib.base.agent')
|
|
log = logger.log
|
|
|
|
class Agent(object):
|
|
""" an agent container
|
|
|
|
The Agent class is a container for agent specific data.
|
|
It is also a nice place for convenience code.
|
|
|
|
Example, of login via the agent class:
|
|
|
|
Initialize the login class
|
|
>>> client = Agent()
|
|
|
|
>>> client.login('https://login.agni.lindenlab.com/cgi-bin/login.cgi', 'firstname', 'lastname', 'secret', start_location = 'last')
|
|
|
|
Sample implementations: examples/sample_agent_login.py
|
|
Tests: tests/login.txt, tests/test_agent.py
|
|
|
|
"""
|
|
|
|
def __init__(self, settings = None):
|
|
""" initialize this agent """
|
|
|
|
# allow the settings to be passed in
|
|
# otherwise, grab the defaults
|
|
if settings != None:
|
|
self.settings = settings
|
|
else:
|
|
from pyogp.lib.base.settings import Settings
|
|
self.settings = Settings()
|
|
|
|
# signal handler to capture erm signals
|
|
self.signal_handler = signal.signal(signal.SIGINT, self.sigint_handler)
|
|
|
|
# storage containers for agent attributes
|
|
# we store what the grid tells us, rather than what
|
|
# is passed in and stored in Login()
|
|
self.firstname = None
|
|
self.lastname = None
|
|
self.agent_id = None
|
|
self.session_id = None
|
|
self.secure_session_id = None
|
|
|
|
# other storage containers
|
|
self.inventory_host = None
|
|
self.agent_access = None
|
|
self.udp_blacklist = None
|
|
self.home = None
|
|
|
|
# additional attributes
|
|
self.login_response = None
|
|
self.connected = False
|
|
self.grid_type = None
|
|
self.running = True
|
|
|
|
# should we include these here?
|
|
self.agentdomain = None # the agent domain the agent is connected to if an OGP context
|
|
self.regions = [] # all known regions
|
|
self.region = None # the host simulation for the agent
|
|
|
|
# set up callbacks (is this a decent place to do this? it's perhaps premature)
|
|
if self.settings.HANDLE_PACKETS:
|
|
self.packet_handler = PacketHandler()
|
|
|
|
onAgentDataUpdate_received = self.packet_handler._register('AgentDataUpdate')
|
|
onAgentDataUpdate_received.subscribe(onAgentDataUpdate, self)
|
|
|
|
onAgentMovementComplete_received = self.packet_handler._register('AgentMovementComplete')
|
|
onAgentMovementComplete_received.subscribe(onAgentMovementComplete, self)
|
|
|
|
onHealthMessage_received = self.packet_handler._register('HealthMessage')
|
|
onHealthMessage_received.subscribe(onHealthMessage, self)
|
|
|
|
if self.settings.LOG_VERBOSE: log(DEBUG, 'Initializing agent: %s' % (self))
|
|
|
|
def login(self, loginuri, firstname=None, lastname=None, password=None, login_params = None, start_location=None, handler=None, connect_region = False):
|
|
""" login to a login endpoint. this should move to a login class in time """
|
|
|
|
if (re.search('auth.cgi$', loginuri)):
|
|
|
|
self.grid_type = 'OGP'
|
|
|
|
elif (re.search('login.cgi$', loginuri)):
|
|
|
|
self.grid_type = 'Legacy'
|
|
|
|
else:
|
|
log(WARNING, 'Unable to identify the loginuri schema. Stopping')
|
|
sys.exit(-1)
|
|
|
|
# handle either login params passed in, or, account info
|
|
if login_params == None:
|
|
|
|
if (firstname == None) or (lastname == None) or (password == None):
|
|
|
|
raise LoginError('Unable to login an unknown agent.')
|
|
|
|
else:
|
|
|
|
self._login_params = self._get_login_params(loginuri, firstname, lastname, password)
|
|
|
|
else:
|
|
|
|
self._login_params = login_params
|
|
|
|
# login and parse the response
|
|
login = Login(settings = self.settings)
|
|
|
|
try:
|
|
|
|
self.login_response = login.login(loginuri, self._login_params, start_location, handler = handler)
|
|
self._parse_login_response()
|
|
|
|
except LoginError, error:
|
|
|
|
log(WARNING, 'Failed to login user. Stopping')
|
|
sys.exit(-1)
|
|
|
|
# ToDo: what to do with self.login_response['look_at']?
|
|
|
|
if connect_region:
|
|
self._enable_current_region()
|
|
|
|
def logout(self):
|
|
""" logs an agent out of the current region """
|
|
|
|
if self.region.logout():
|
|
self.connected = False
|
|
|
|
def _get_login_params(self, loginuri, firstname, lastname, password):
|
|
""" get the proper login parameters """
|
|
|
|
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 """
|
|
|
|
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 = self.login_response['agent_id']
|
|
self.session_id = self.login_response['session_id']
|
|
self.secure_session_id = self.login_response['secure_session_id']
|
|
|
|
self.connected = bool(self.login_response['login'])
|
|
self.inventory_host = self.login_response['inventory_host']
|
|
self.agent_access = self.login_response['agent_access']
|
|
self.udp_blacklist = self.login_response['udp_blacklist']
|
|
|
|
if self.login_response.has_key('home'): self.home = Home(self.login_response['home'])
|
|
|
|
if self.settings.ENABLE_INVENTORY_MANAGEMENT:
|
|
|
|
self.inventory = Inventory(self)
|
|
self.inventory._parse_folders_from_login_response()
|
|
|
|
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 an agents current region """
|
|
|
|
# enable the current rebion, setting connect = True
|
|
self.region = Region(self.login_response['region_x'], self.login_response['region_y'], self.login_response['seed_capability'], self.login_response['udp_blacklist'], self.login_response['sim_ip'], self.login_response['sim_port'], self.login_response['circuit_code'], self, packet_handler = self.packet_handler)
|
|
|
|
# start the simulator udp and event queue connections
|
|
api.spawn(self.region.connect)
|
|
#self.region.connect()
|
|
while self.running:
|
|
api.sleep(0)
|
|
|
|
def sigint_handler(self, signal, frame):
|
|
log(INFO, "Caught signal... %d. Stopping" % signal)
|
|
self.running = False
|
|
self.logout()
|
|
#sys.exit(0)
|
|
|
|
def __repr__(self):
|
|
""" returns a representation of the agent """
|
|
|
|
if self.firstname == None:
|
|
return 'A new agent instance'
|
|
else:
|
|
return '%s %s' % (self.firstname, self.lastname)
|
|
|
|
def onAgentDataUpdate(packet, agent):
|
|
|
|
if agent.agent_id == None:
|
|
agent.agent_id = packet.message_data.blocks['AgentData'][0].get_variable('AgentID')
|
|
|
|
if agent.firstname == None:
|
|
agent.firstname = packet.message_data.blocks['AgentData'][0].get_variable('FirstName')
|
|
|
|
if agent.lastname == None:
|
|
agent.firstname = packet.message_data.blocks['AgentData'][0].get_variable('LastName')
|
|
|
|
agent.GroupTitle = packet.message_data.blocks['AgentData'][0].get_variable('GroupTitle')
|
|
|
|
agent.ActiveGroupID = packet.message_data.blocks['AgentData'][0].get_variable('ActiveGroupID')
|
|
|
|
agent.GroupPowers = packet.message_data.blocks['AgentData'][0].get_variable('GroupPowers')
|
|
|
|
agent.GroupName = packet.message_data.blocks['AgentData'][0].get_variable('GroupName')
|
|
|
|
def onAgentMovementComplete(packet, agent):
|
|
|
|
agent.Position = packet.message_data.blocks['Data'][0].get_variable('Position')
|
|
|
|
agent.LookAt = packet.message_data.blocks['Data'][0].get_variable('LookAt')
|
|
|
|
#agent.RegionHandle = packet.message_data.blocks['Data'][0].get_variable('RegionHandle')
|
|
|
|
#agent.Timestamp = packet.message_data.blocks['Data'][0].get_variable('Timestamp')
|
|
|
|
agent.region.ChannelVersion = packet.message_data.blocks['SimData'][0].get_variable('ChannelVersion')
|
|
|
|
def onHealthMessage(packet, agent):
|
|
|
|
agent.health = packet.message_data.blocks['HealthData'][0].get_variable('Health')
|
|
|
|
class Home(object):
|
|
""" contains the parameters descibing an agent's home location """
|
|
|
|
def __init__(self, params):
|
|
|
|
# eval(params) would be nice, but fails to parse the string the way one thinks it might
|
|
items = params.split(', \'')
|
|
|
|
# this creates:
|
|
# self.region_handle
|
|
# self.look_at
|
|
# self.position
|
|
for i in items:
|
|
i = re.sub(r'[\"\{}\'"]', '', i)
|
|
i = i.split(':')
|
|
setattr(self, i[0], eval(re.sub('r', '', i[1])))
|
|
|
|
self.global_x = self.region_handle[0]
|
|
self.global_y = self.region_handle[1]
|
|
|
|
self.local_x = self.position[0]
|
|
self.local_y = self.position[1]
|
|
self.local_z = self.position[2] |