| 1 |
# standard python libs |
|---|
| 2 |
from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG |
|---|
| 3 |
import re |
|---|
| 4 |
import sys |
|---|
| 5 |
import signal |
|---|
| 6 |
import uuid |
|---|
| 7 |
import sets |
|---|
| 8 |
|
|---|
| 9 |
#related |
|---|
| 10 |
from eventlet import api |
|---|
| 11 |
|
|---|
| 12 |
# pyogp |
|---|
| 13 |
from pyogp.lib.base.login import Login, LegacyLoginParams, OGPLoginParams |
|---|
| 14 |
from pyogp.lib.base.datatypes import * |
|---|
| 15 |
from pyogp.lib.base.exc import LoginError |
|---|
| 16 |
from pyogp.lib.base.region import Region |
|---|
| 17 |
from pyogp.lib.base.inventory import * |
|---|
| 18 |
from pyogp.lib.base.groups import * |
|---|
| 19 |
from pyogp.lib.base.event_system import * |
|---|
| 20 |
from pyogp.lib.base.appearance import * |
|---|
| 21 |
|
|---|
| 22 |
# pyogp messaging |
|---|
| 23 |
from pyogp.lib.base.message.message_handler import MessageHandler |
|---|
| 24 |
|
|---|
| 25 |
from pyogp.lib.base.message.packets import * |
|---|
| 26 |
|
|---|
| 27 |
# pyogp utilities |
|---|
| 28 |
from pyogp.lib.base.utilities.helpers import Helpers |
|---|
| 29 |
from pyogp.lib.base.utilities.enums import ImprovedIMDialogue, MoneyTransactionType, TransactionFlags |
|---|
| 30 |
|
|---|
| 31 |
# initialize logging |
|---|
| 32 |
logger = getLogger('pyogp.lib.base.agent') |
|---|
| 33 |
log = logger.log |
|---|
| 34 |
|
|---|
| 35 |
class Agent(object): |
|---|
| 36 |
""" The Agent class is a container for agent specific data. |
|---|
| 37 |
|
|---|
| 38 |
Example, of login via the agent class: |
|---|
| 39 |
Initialize the login class |
|---|
| 40 |
|
|---|
| 41 |
>>> client = Agent() |
|---|
| 42 |
>>> client.login('https://login.agni.lindenlab.com/cgi-bin/login.cgi', 'firstname', 'lastname', 'secret', start_location = 'last') |
|---|
| 43 |
|
|---|
| 44 |
Sample implementations: examples/sample_agent_login.py |
|---|
| 45 |
Tests: tests/login.txt, tests/test_agent.py |
|---|
| 46 |
|
|---|
| 47 |
""" |
|---|
| 48 |
|
|---|
| 49 |
def __init__(self, settings = None, firstname = '', lastname = '', password = '', agent_id = None, events_handler = None, handle_signals=True): |
|---|
| 50 |
""" initialize this agent """ |
|---|
| 51 |
|
|---|
| 52 |
# allow the settings to be passed in |
|---|
| 53 |
# otherwise, grab the defaults |
|---|
| 54 |
if settings != None: |
|---|
| 55 |
self.settings = settings |
|---|
| 56 |
else: |
|---|
| 57 |
from pyogp.lib.base.settings import Settings |
|---|
| 58 |
self.settings = Settings() |
|---|
| 59 |
|
|---|
| 60 |
# allow the eventhandler to be passed in |
|---|
| 61 |
# so that applications running multiple avatars |
|---|
| 62 |
# may use the same eventhandler |
|---|
| 63 |
|
|---|
| 64 |
# otherwise, let's just use our own |
|---|
| 65 |
if events_handler != None: |
|---|
| 66 |
self.events_handler = events_handler |
|---|
| 67 |
else: |
|---|
| 68 |
self.events_handler = AppEventsHandler() |
|---|
| 69 |
|
|---|
| 70 |
# signal handler to capture erm signals |
|---|
| 71 |
if handle_signals: |
|---|
| 72 |
self.signal_handler = signal.signal(signal.SIGINT, self.sigint_handler) |
|---|
| 73 |
|
|---|
| 74 |
# storage containers for agent attributes |
|---|
| 75 |
# we overwrite with what the grid tells us, rather than what |
|---|
| 76 |
# is passed in and stored in Login() |
|---|
| 77 |
self.firstname = firstname |
|---|
| 78 |
self.lastname = lastname |
|---|
| 79 |
self.password = password |
|---|
| 80 |
self.agent_id = None |
|---|
| 81 |
self.session_id = None |
|---|
| 82 |
self.secure_session_id = None |
|---|
| 83 |
self.name = self.Name() |
|---|
| 84 |
|
|---|
| 85 |
# other storage containers |
|---|
| 86 |
self.inventory_host = None |
|---|
| 87 |
self.agent_access = None |
|---|
| 88 |
self.udp_blacklist = None |
|---|
| 89 |
self.home = None |
|---|
| 90 |
self.inventory = None |
|---|
| 91 |
self.start_location = None |
|---|
| 92 |
self.group_manager = GroupManager(self, self.settings) |
|---|
| 93 |
|
|---|
| 94 |
|
|---|
| 95 |
# additional attributes |
|---|
| 96 |
self.login_response = None |
|---|
| 97 |
self.connected = False |
|---|
| 98 |
self.grid_type = None |
|---|
| 99 |
self.running = True |
|---|
| 100 |
self.helpers = Helpers() |
|---|
| 101 |
|
|---|
| 102 |
# data we store as it comes in from the grid |
|---|
| 103 |
self.Position = Vector3() # this will get updated later, but seed it with 000 |
|---|
| 104 |
self.ActiveGroupID = UUID() |
|---|
| 105 |
|
|---|
| 106 |
# should we include these here? |
|---|
| 107 |
self.agentdomain = None # the agent domain the agent is connected to if an OGP context |
|---|
| 108 |
self.child_regions = [] # all neighboring regions |
|---|
| 109 |
self._pending_child_regions = [] # neighbor regions an agent may connect to |
|---|
| 110 |
self.region = None # the host simulation for the agent |
|---|
| 111 |
|
|---|
| 112 |
# init AppearanceManager() |
|---|
| 113 |
self.appearance = AppearanceManager(self.settings, self) |
|---|
| 114 |
|
|---|
| 115 |
# Cache of region name->handle; per-agent to prevent information leaks |
|---|
| 116 |
self.region_name_map = {} |
|---|
| 117 |
|
|---|
| 118 |
# Cache of agent_id->(first_name, last_name); per agent to prevent info leaks |
|---|
| 119 |
self.agent_id_map = {} |
|---|
| 120 |
|
|---|
| 121 |
if self.settings.LOG_VERBOSE: log(DEBUG, 'Initializing agent: %s' % (self)) |
|---|
| 122 |
|
|---|
| 123 |
def Name(self): |
|---|
| 124 |
""" returns a concatenated firstname + ' ' + lastname""" |
|---|
| 125 |
|
|---|
| 126 |
return self.firstname + ' ' + self.lastname |
|---|
| 127 |
|
|---|
| 128 |
def login(self, loginuri, firstname=None, lastname=None, password=None, login_params = None, start_location=None, handler=None, connect_region = True): |
|---|
| 129 |
""" login to a login endpoint using the Login() class """ |
|---|
| 130 |
|
|---|
| 131 |
if (re.search('auth.cgi$', loginuri)): |
|---|
| 132 |
|
|---|
| 133 |
self.grid_type = 'OGP' |
|---|
| 134 |
|
|---|
| 135 |
elif (re.search('login.cgi$', loginuri)): |
|---|
| 136 |
|
|---|
| 137 |
self.grid_type = 'Legacy' |
|---|
| 138 |
|
|---|
| 139 |
else: |
|---|
| 140 |
log(WARNING, 'Unable to identify the loginuri schema. Stopping') |
|---|
| 141 |
sys.exit(-1) |
|---|
| 142 |
|
|---|
| 143 |
if firstname != None: |
|---|
| 144 |
self.firstname = firstname |
|---|
| 145 |
if lastname != None: |
|---|
| 146 |
self.lastname = lastname |
|---|
| 147 |
if password != None: |
|---|
| 148 |
self.password = password |
|---|
| 149 |
|
|---|
| 150 |
# handle either login params passed in, or, account info |
|---|
| 151 |
if login_params == None: |
|---|
| 152 |
|
|---|
| 153 |
if (self.firstname == '') or (self.lastname == '') or (self.password == ''): |
|---|
| 154 |
|
|---|
| 155 |
raise LoginError('Unable to login an unknown agent.') |
|---|
| 156 |
|
|---|
| 157 |
else: |
|---|
| 158 |
|
|---|
| 159 |
self._login_params = self._get_login_params(loginuri, self.firstname, self.lastname, self.password) |
|---|
| 160 |
|
|---|
| 161 |
else: |
|---|
| 162 |
|
|---|
| 163 |
self._login_params = login_params |
|---|
| 164 |
|
|---|
| 165 |
# login and parse the response |
|---|
| 166 |
login = Login(settings = self.settings) |
|---|
| 167 |
|
|---|
| 168 |
self.login_response = login.login(loginuri, self._login_params, start_location, handler = handler) |
|---|
| 169 |
self._parse_login_response() |
|---|
| 170 |
|
|---|
| 171 |
# ToDo: what to do with self.login_response['look_at']? |
|---|
| 172 |
|
|---|
| 173 |
if self.settings.MULTIPLE_SIM_CONNECTIONS: |
|---|
| 174 |
api.spawn(self._monitor_for_new_regions) |
|---|
| 175 |
|
|---|
| 176 |
if connect_region: |
|---|
| 177 |
self._enable_current_region() |
|---|
| 178 |
|
|---|
| 179 |
|
|---|
| 180 |
def logout(self): |
|---|
| 181 |
""" logs an agent out of the current region. calls Region()._kill_coroutines() for all child regions, and Region().logout() for the host region """ |
|---|
| 182 |
|
|---|
| 183 |
if not self.connected: |
|---|
| 184 |
log(INFO, 'Agent is not logged into the grid. Stopping.') |
|---|
| 185 |
sys.exit() |
|---|
| 186 |
|
|---|
| 187 |
self.running = False |
|---|
| 188 |
|
|---|
| 189 |
if self.region == None: |
|---|
| 190 |
return |
|---|
| 191 |
else: |
|---|
| 192 |
|
|---|
| 193 |
# kill udp and or event queue for child regions |
|---|
| 194 |
[region._kill_coroutines() for region in self.child_regions] |
|---|
| 195 |
|
|---|
| 196 |
if self.region.logout(): |
|---|
| 197 |
self.connected = False |
|---|
| 198 |
|
|---|
| 199 |
# zero out the password in case we dump it somewhere |
|---|
| 200 |
self.password = '' |
|---|
| 201 |
|
|---|
| 202 |
def _get_login_params(self, loginuri, firstname, lastname, password): |
|---|
| 203 |
""" get the proper login parameters of the legacy or ogp enabled grid """ |
|---|
| 204 |
|
|---|
| 205 |
if self.grid_type == 'OGP': |
|---|
| 206 |
|
|---|
| 207 |
login_params = OGPLoginParams(firstname, lastname, password) |
|---|
| 208 |
|
|---|
| 209 |
elif self.grid_type == 'Legacy': |
|---|
| 210 |
|
|---|
| 211 |
login_params = LegacyLoginParams(firstname, lastname, password) |
|---|
| 212 |
|
|---|
| 213 |
return login_params |
|---|
| 214 |
|
|---|
| 215 |
def _parse_login_response(self): |
|---|
| 216 |
""" evaluates the login response and propagates data to the Agent() attributes. enables InventoryManager() if settings dictate """ |
|---|
| 217 |
|
|---|
| 218 |
if self.grid_type == 'Legacy': |
|---|
| 219 |
|
|---|
| 220 |
self.firstname = re.sub(r'\"', '', self.login_response['first_name']) |
|---|
| 221 |
self.lastname = self.login_response['last_name'] |
|---|
| 222 |
self.agent_id = UUID(self.login_response['agent_id']) |
|---|
| 223 |
self.session_id = UUID(self.login_response['session_id']) |
|---|
| 224 |
self.secure_session_id = UUID(self.login_response['secure_session_id']) |
|---|
| 225 |
|
|---|
| 226 |
self.connected = bool(self.login_response['login']) |
|---|
| 227 |
self.inventory_host = self.login_response['inventory_host'] |
|---|
| 228 |
self.agent_access = self.login_response['agent_access'] |
|---|
| 229 |
self.udp_blacklist = self.login_response['udp_blacklist'] |
|---|
| 230 |
self.start_location = self.login_response['start_location'] |
|---|
| 231 |
|
|---|
| 232 |
if self.login_response.has_key('home'): self.home = Home(self.login_response['home']) |
|---|
| 233 |
|
|---|
| 234 |
elif self.grid_type == 'OGP': |
|---|
| 235 |
|
|---|
| 236 |
pass |
|---|
| 237 |
|
|---|
| 238 |
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): |
|---|
| 239 |
""" enables and connects udp and event queue for an agent's current region """ |
|---|
| 240 |
|
|---|
| 241 |
if self.login_response.has_key('circuit_code'): |
|---|
| 242 |
self.circuit_code = self.login_response['circuit_code'] |
|---|
| 243 |
|
|---|
| 244 |
region_x = region_x or self.login_response['region_x'] |
|---|
| 245 |
region_y = region_y or self.login_response['region_y'] |
|---|
| 246 |
seed_capability = seed_capability or self.login_response['seed_capability'] |
|---|
| 247 |
udp_blacklist = udp_blacklist or self.login_response['udp_blacklist'] |
|---|
| 248 |
sim_ip = sim_ip or self.login_response['sim_ip'] |
|---|
| 249 |
sim_port = sim_port or self.login_response['sim_port'] |
|---|
| 250 |
circuit_code = circuit_code or self.login_response['circuit_code'] |
|---|
| 251 |
|
|---|
| 252 |
# enable the current region, setting connect = True |
|---|
| 253 |
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) |
|---|
| 254 |
|
|---|
| 255 |
self.region.is_host_region = True |
|---|
| 256 |
|
|---|
| 257 |
# start the simulator udp and event queue connections |
|---|
| 258 |
if self.settings.LOG_COROUTINE_SPAWNS: log(INFO, "Spawning a coroutine for connecting to the agent's host region.") |
|---|
| 259 |
|
|---|
| 260 |
api.spawn(self.region.connect) |
|---|
| 261 |
|
|---|
| 262 |
self.enable_callbacks() |
|---|
| 263 |
|
|---|
| 264 |
|
|---|
| 265 |
|
|---|
| 266 |
def _enable_child_region(self, region_params): |
|---|
| 267 |
""" enables a child region. eligible simulators are sent in EnableSimulator over the event queue, and routed through the packet handler """ |
|---|
| 268 |
|
|---|
| 269 |
# if this is the sim we are already connected to, skip it |
|---|
| 270 |
if self.region.sim_ip == region_params['IP'] and self.region.sim_port == region_params['Port']: |
|---|
| 271 |
#self.region.sendCompleteAgentMovement() |
|---|
| 272 |
log(DEBUG, "Not enabling a region we are already connected to: %s" % (str(region_params['IP']) + ":" + str(region_params['Port']))) |
|---|
| 273 |
return |
|---|
| 274 |
|
|---|
| 275 |
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) |
|---|
| 276 |
|
|---|
| 277 |
self.child_regions.append(child_region) |
|---|
| 278 |
|
|---|
| 279 |
log(INFO, "Enabling a child region with ip:port of %s" % (str(region_params['IP']) + ":" + str(region_params['Port']))) |
|---|
| 280 |
|
|---|
| 281 |
if self.settings.LOG_COROUTINE_SPAWNS: log(INFO, "Spawning a coroutine for connecting to a neighboring region.") |
|---|
| 282 |
|
|---|
| 283 |
api.spawn(child_region.connect_child) |
|---|
| 284 |
|
|---|
| 285 |
def _monitor_for_new_regions(self): |
|---|
| 286 |
""" enable connections to neighboring regions found in the pending queue """ |
|---|
| 287 |
|
|---|
| 288 |
while self.running: |
|---|
| 289 |
|
|---|
| 290 |
if len(self._pending_child_regions) > 0: |
|---|
| 291 |
|
|---|
| 292 |
for region_params in self._pending_child_regions: |
|---|
| 293 |
|
|---|
| 294 |
self._enable_child_region(region_params) |
|---|
| 295 |
self._pending_child_regions.remove(region_params) |
|---|
| 296 |
|
|---|
| 297 |
api.sleep(0) |
|---|
| 298 |
|
|---|
| 299 |
def _start_EQ_on_neighboring_region(self, message): |
|---|
| 300 |
""" enables the event queue on an agent's neighboring region """ |
|---|
| 301 |
|
|---|
| 302 |
region = [region for region in self.child_regions if message.blocks['Message_Data'][0].get_variable('sim-ip-and-port').data == str(region.sim_ip) + ":" + str(region.sim_port)] |
|---|
| 303 |
|
|---|
| 304 |
if region != []: |
|---|
| 305 |
|
|---|
| 306 |
region[0]._set_seed_capability(message.blocks['Message_Data'][0].get_variable('seed-capability').data) |
|---|
| 307 |
|
|---|
| 308 |
region[0]._get_region_capabilities() |
|---|
| 309 |
|
|---|
| 310 |
log(DEBUG, 'Spawning neighboring region event queue connection') |
|---|
| 311 |
region[0]._startEventQueue() |
|---|
| 312 |
|
|---|
| 313 |
def enable_callbacks(self): |
|---|
| 314 |
""" enable the Agents() callback handlers for packet received events """ |
|---|
| 315 |
# TODO oopify |
|---|
| 316 |
|
|---|
| 317 |
if self.settings.ENABLE_INVENTORY_MANAGEMENT: |
|---|
| 318 |
while self.region.capabilities == {}: |
|---|
| 319 |
|
|---|
| 320 |
api.sleep(0) |
|---|
| 321 |
|
|---|
| 322 |
inventory_caps = ['FetchInventory', 'WebFetchInventoryDescendents', 'FetchLib', 'FetchLibDescendents'] |
|---|
| 323 |
|
|---|
| 324 |
if sets.Set(self.region.capabilities.keys()).intersection(inventory_caps): |
|---|
| 325 |
|
|---|
| 326 |
caps = dict([(capname, self.region.capabilities[capname]) for capname in inventory_caps]) |
|---|
| 327 |
|
|---|
| 328 |
log(INFO, "Using the capability based inventory management mechanism") |
|---|
| 329 |
|
|---|
| 330 |
self.inventory = AIS(self, caps) |
|---|
| 331 |
|
|---|
| 332 |
else: |
|---|
| 333 |
|
|---|
| 334 |
log(INFO, "Using the UDP based inventory management mechanism") |
|---|
| 335 |
|
|---|
| 336 |
self.inventory = UDP_Inventory(self) |
|---|
| 337 |
|
|---|
| 338 |
self.inventory._parse_folders_from_login_response() |
|---|
| 339 |
self.inventory.enable_callbacks() |
|---|
| 340 |
|
|---|
| 341 |
if self.settings.ENABLE_APPEARANCE_MANAGEMENT: |
|---|
| 342 |
self.appearance.enable_callbacks() |
|---|
| 343 |
if self.settings.ENABLE_GROUP_CHAT: |
|---|
| 344 |
self.group_manager.enable_callbacks() |
|---|
| 345 |
if self.settings.MULTIPLE_SIM_CONNECTIONS: |
|---|
| 346 |
|
|---|
| 347 |
onEnableSimulator_received = self.region.message_handler.register('EnableSimulator') |
|---|
| 348 |
onEnableSimulator_received.subscribe(self.onEnableSimulator) |
|---|
| 349 |
|
|---|
| 350 |
onEstablishAgentCommunication_received = self.region.message_handler.register('EstablishAgentCommunication') |
|---|
| 351 |
onEstablishAgentCommunication_received.subscribe(self.onEstablishAgentCommunication) |
|---|
| 352 |
|
|---|
| 353 |
if self.settings.HANDLE_PACKETS: |
|---|
| 354 |
|
|---|
| 355 |
onAlertMessage_received = self.region.message_handler.register('AlertMessage') |
|---|
| 356 |
onAlertMessage_received.subscribe(self.onAlertMessage) |
|---|
| 357 |
|
|---|
| 358 |
onAgentDataUpdate_received = self.region.message_handler.register('AgentDataUpdate') |
|---|
| 359 |
onAgentDataUpdate_received.subscribe(self.onAgentDataUpdate) |
|---|
| 360 |
|
|---|
| 361 |
onAgentMovementComplete_received = self.region.message_handler.register('AgentMovementComplete') |
|---|
| 362 |
onAgentMovementComplete_received.subscribe(self.onAgentMovementComplete) |
|---|
| 363 |
|
|---|
| 364 |
onHealthMessage_received = self.region.message_handler.register('HealthMessage') |
|---|
| 365 |
onHealthMessage_received.subscribe(self.onHealthMessage) |
|---|
| 366 |
|
|---|
| 367 |
onImprovedInstantMessage_received = self.region.message_handler.register('ImprovedInstantMessage') |
|---|
| 368 |
onImprovedInstantMessage_received.subscribe(self.onImprovedInstantMessage) |
|---|
| 369 |
|
|---|
| 370 |
self.region.message_handler.register('TeleportStart').subscribe(self.simple_callback('Info')) |
|---|
| 371 |
self.region.message_handler.register('TeleportProgress').subscribe(self.simple_callback('Info')) |
|---|
| 372 |
self.region.message_handler.register('TeleportFailed').subscribe(self.simple_callback('Info')) |
|---|
| 373 |
self.region.message_handler.register('TeleportFinish').subscribe(self.onTeleportFinish) |
|---|
| 374 |
|
|---|
| 375 |
self.region.message_handler.register('OfflineNotification').subscribe(self.simple_callback('AgentBlock')) |
|---|
| 376 |
self.region.message_handler.register('OnlineNotification').subscribe(self.simple_callback('AgentBlock')) |
|---|
| 377 |
|
|---|
| 378 |
self.region.message_handler.register('MoneyBalanceReply').subscribe(self.simple_callback('MoneyData')) |
|---|
| 379 |
self.region.message_handler.register('RoutedMoneyBalanceReply').subscribe(self.simple_callback('MoneyData')) |
|---|
| 380 |
|
|---|
| 381 |
if self.settings.ENABLE_COMMUNICATIONS_TRACKING: |
|---|
| 382 |
onChatFromSimulator_received = self.region.message_handler.register('ChatFromSimulator') |
|---|
| 383 |
onChatFromSimulator_received.subscribe(self.onChatFromSimulator) |
|---|
| 384 |
|
|---|
| 385 |
|
|---|
| 386 |
def simple_callback(self, blockname): |
|---|
| 387 |
"""Generic callback creator for single-block packets.""" |
|---|
| 388 |
|
|---|
| 389 |
def repack(packet, blockname): |
|---|
| 390 |
"""Repack a single block packet into an AppEvent""" |
|---|
| 391 |
payload = {} |
|---|
| 392 |
block = packet.blocks[blockname][0] |
|---|
| 393 |
for var in block.var_list: |
|---|
| 394 |
payload[var] = block.get_variable(var).data |
|---|
| 395 |
|
|---|
| 396 |
return AppEvent(packet.name, payload=payload) |
|---|
| 397 |
|
|---|
| 398 |
return lambda p: self.events_handler._handle(repack(p, blockname)) |
|---|
| 399 |
|
|---|
| 400 |
|
|---|
| 401 |
def send_AgentDataUpdateRequest(self): |
|---|
| 402 |
""" queues a packet requesting an agent data update """ |
|---|
| 403 |
|
|---|
| 404 |
packet = AgentDataUpdateRequestPacket() |
|---|
| 405 |
|
|---|
| 406 |
packet.AgentData['AgentID'] = self.agent_id |
|---|
| 407 |
packet.AgentData['SessionID'] = self.session_id |
|---|
| 408 |
|
|---|
| 409 |
self.region.enqueue_message(packet()) |
|---|
| 410 |
|
|---|
| 411 |
# ~~~~~~~~~~~~~~ |
|---|
| 412 |
# Communications |
|---|
| 413 |
# ~~~~~~~~~~~~~~ |
|---|
| 414 |
|
|---|
| 415 |
# Chat |
|---|
| 416 |
|
|---|
| 417 |
def say(self, Message, Type = 1, Channel = 0): |
|---|
| 418 |
""" queues a packet to send open chat via ChatFromViewer |
|---|
| 419 |
|
|---|
| 420 |
Channel: 0 is open chat |
|---|
| 421 |
Type: 0 = Whisper |
|---|
| 422 |
1 = Say |
|---|
| 423 |
2 = Shout |
|---|
| 424 |
""" |
|---|
| 425 |
|
|---|
| 426 |
packet = ChatFromViewerPacket() |
|---|
| 427 |
|
|---|
| 428 |
packet.AgentData['AgentID'] = self.agent_id |
|---|
| 429 |
packet.AgentData['SessionID'] = self.session_id |
|---|
| 430 |
|
|---|
| 431 |
packet.ChatData['Message'] = Message |
|---|
| 432 |
packet.ChatData['Type'] = Type |
|---|
| 433 |
packet.ChatData['Channel'] = Channel |
|---|
| 434 |
|
|---|
| 435 |
self.region.enqueue_message(packet()) |
|---|
| 436 |
|
|---|
| 437 |
# Instant Message (im, group chat) |
|---|
| 438 |
|
|---|
| 439 |
def instant_message(self, ToAgentID = None, Message = None, _ID = None): |
|---|
| 440 |
""" sends an instant message to another avatar, wrapping Agent().send_ImprovedInstantMessage() with some handy defaults """ |
|---|
| 441 |
|
|---|
| 442 |
if ToAgentID != None and Message != None: |
|---|
| 443 |
|
|---|
| 444 |
if _ID == None: _ID = self.agent_id |
|---|
| 445 |
|
|---|
| 446 |
_AgentID = self.agent_id |
|---|
| 447 |
_SessionID = self.session_id |
|---|
| 448 |
_FromGroup = False |
|---|
| 449 |
_ToAgentID = UUID(str(ToAgentID)) |
|---|
| 450 |
_ParentEstateID = 0 |
|---|
| 451 |
_RegionID = UUID() |
|---|
| 452 |
_Position = self.Position |
|---|
| 453 |
_Offline = 0 |
|---|
| 454 |
_Dialog = ImprovedIMDialogue.FromAgent |
|---|
| 455 |
_ID = _ID |
|---|
| 456 |
_Timestamp = 0 |
|---|
| 457 |
_FromAgentName = self.firstname + ' ' + self.lastname |
|---|
| 458 |
_Message = Message |
|---|
| 459 |
_BinaryBucket = '' |
|---|
| 460 |
|
|---|
| 461 |
self.send_ImprovedInstantMessage(_AgentID, _SessionID, _FromGroup, _ToAgentID, _ParentEstateID, _RegionID, _Position, _Offline, _Dialog, _ID, _Timestamp, _FromAgentName, _Message, _BinaryBucket) |
|---|
| 462 |
|
|---|
| 463 |
else: |
|---|
| 464 |
|
|---|
| 465 |
log(INFO, "Please specify an agentid and message to send in agent.instant_message") |
|---|
| 466 |
|
|---|
| 467 |
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, AgentDataBlock = {}, MessageBlockBlock = {}): |
|---|
| 468 |
""" sends an instant message packet to ToAgentID. this is a multi-purpose message for inventory offer handling, im, group chat, and more """ |
|---|
| 469 |
|
|---|
| 470 |
packet = ImprovedInstantMessagePacket() |
|---|
| 471 |
|
|---|
| 472 |
if AgentDataBlock == {}: |
|---|
| 473 |
packet.AgentData['AgentID'] = AgentID |
|---|
| 474 |
packet.AgentData['SessionID'] = SessionID |
|---|
| 475 |
else: |
|---|
| 476 |
packet.AgentData = AgentDataBlock |
|---|
| 477 |
|
|---|
| 478 |
if FromAgentName == None: |
|---|
| 479 |
FromAgentName = self.Name() |
|---|
| 480 |
|
|---|
| 481 |
# ha! when scripting out packets.py, never considered a block named *block |
|---|
| 482 |
if MessageBlockBlock == {}: |
|---|
| 483 |
|
|---|
| 484 |
packet.MessageBlock['FromGroup'] = FromGroup # Bool |
|---|
| 485 |
packet.MessageBlock['ToAgentID'] = UUID(str(ToAgentID)) # LLUUID |
|---|
| 486 |
packet.MessageBlock['ParentEstateID'] = ParentEstateID # U32 |
|---|
| 487 |
packet.MessageBlock['RegionID'] = UUID(str(RegionID)) # LLUUID |
|---|
| 488 |
packet.MessageBlock['Position'] = Position() # LLVector3 |
|---|
| 489 |
packet.MessageBlock['Offline'] = Offline # U8 |
|---|
| 490 |
packet.MessageBlock['Dialog'] = Dialog # U8 IM Type |
|---|
| 491 |
packet.MessageBlock['ID'] = UUID(str(_ID)) # LLUUID |
|---|
| 492 |
packet.MessageBlock['Timestamp'] = Timestamp # U32 |
|---|
| 493 |
packet.MessageBlock['FromAgentName'] = FromAgentName # Variable 1 |
|---|
| 494 |
packet.MessageBlock['Message'] = Message # Variable 2 |
|---|
| 495 |
packet.MessageBlock['BinaryBucket'] = BinaryBucket # Variable 2 |
|---|
| 496 |
|
|---|
| 497 |
self.region.enqueue_message(packet(), True) |
|---|
| 498 |
|
|---|
| 499 |
def send_RetrieveInstantMessages(self): |
|---|
| 500 |
""" asks simulator for instant messages stored while agent was offline """ |
|---|
| 501 |
|
|---|
| 502 |
packet = RetrieveInstantMessagesPackets() |
|---|
| 503 |
|
|---|
| 504 |
packet.AgentDataBlock['AgentID'] = self.agent_id |
|---|
| 505 |
packet.AgentDataBlock['SessionID'] = self.session_id |
|---|
| 506 |
|
|---|
| 507 |
self.region.enqueue_message(packet()) |
|---|
| 508 |
|
|---|
| 509 |
|
|---|
| 510 |
def sigint_handler(self, signal, frame): |
|---|
| 511 |
""" catches terminal signals (Ctrl-C) to kill running client instances """ |
|---|
| 512 |
|
|---|
| 513 |
log(INFO, "Caught signal... %d. Stopping" % signal) |
|---|
| 514 |
#self.running = False |
|---|
| 515 |
self.logout() |
|---|
| 516 |
#sys.exit(0) |
|---|
| 517 |
|
|---|
| 518 |
def __repr__(self): |
|---|
| 519 |
""" returns a representation of the agent """ |
|---|
| 520 |
|
|---|
| 521 |
if self.firstname == None: |
|---|
| 522 |
return 'A new agent instance' |
|---|
| 523 |
else: |
|---|
| 524 |
return self.Name() |
|---|
| 525 |
|
|---|
| 526 |
def onAgentDataUpdate(self, packet): |
|---|
| 527 |
""" callback handler for received AgentDataUpdate messages which populates various Agent() attributes """ |
|---|
| 528 |
|
|---|
| 529 |
if self.agent_id == None: |
|---|
| 530 |
self.agent_id = packet.blocks['AgentData'][0].get_variable('AgentID').data |
|---|
| 531 |
|
|---|
| 532 |
if self.firstname == None: |
|---|
| 533 |
self.firstname = packet.blocks['AgentData'][0].get_variable('FirstName').data |
|---|
| 534 |
|
|---|
| 535 |
if self.lastname == None: |
|---|
| 536 |
self.firstname = packet.blocks['AgentData'][0].get_variable('LastName').data |
|---|
| 537 |
|
|---|
| 538 |
self.GroupTitle = packet.blocks['AgentData'][0].get_variable('GroupTitle').data |
|---|
| 539 |
|
|---|
| 540 |
self.ActiveGroupID = packet.blocks['AgentData'][0].get_variable('ActiveGroupID').data |
|---|
| 541 |
|
|---|
| 542 |
self.GroupPowers = packet.blocks['AgentData'][0].get_variable('GroupPowers').data |
|---|
| 543 |
|
|---|
| 544 |
self.GroupName = packet.blocks['AgentData'][0].get_variable('GroupName').data |
|---|
| 545 |
|
|---|
| 546 |
def onAgentMovementComplete(self, packet): |
|---|
| 547 |
""" callback handler for received AgentMovementComplete messages which populates various Agent() and Region() attributes """ |
|---|
| 548 |
|
|---|
| 549 |
self.Position = packet.blocks['Data'][0].get_variable('Position').data |
|---|
| 550 |
if self.Position == None: |
|---|
| 551 |
log(WARNING, "agent.position is None agent.py") |
|---|
| 552 |
self.LookAt = packet.blocks['Data'][0].get_variable('LookAt').data |
|---|
| 553 |
|
|---|
| 554 |
self.region.RegionHandle = packet.blocks['Data'][0].get_variable('RegionHandle').data |
|---|
| 555 |
|
|---|
| 556 |
#agent.Timestamp = packet.blocks['Data'][0].get_variable('Timestamp') |
|---|
| 557 |
|
|---|
| 558 |
self.region.ChannelVersion = packet.blocks['SimData'][0].get_variable('ChannelVersion').data |
|---|
| 559 |
|
|---|
| 560 |
# Raise a plain-vanilla AppEvent |
|---|
| 561 |
self.simple_callback('Data')(packet) |
|---|
| 562 |
|
|---|
| 563 |
def onHealthMessage(self, packet): |
|---|
| 564 |
""" callback handler for received HealthMessage messages which populates Agent().health """ |
|---|
| 565 |
|
|---|
| 566 |
self.health = packet.blocks['HealthData'][0].get_variable('Health').data |
|---|
| 567 |
|
|---|
| 568 |
def onAgentGroupDataUpdate(self, packet): |
|---|
| 569 |
""" callback handler for received AgentGroupDataUpdate messages which updates stored group instances in the group_manager """ |
|---|
| 570 |
|
|---|
| 571 |
# AgentData block |
|---|
| 572 |
AgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data |
|---|
| 573 |
|
|---|
| 574 |
# GroupData block |
|---|
| 575 |
for GroupData_block in packet.blocks['GroupData']: |
|---|
| 576 |
|
|---|
| 577 |
AcceptNotices = GroupData_block.get_variable('AcceptNotices').data |
|---|
| 578 |
GroupPowers = GroupData_block.get_variable('GroupPowers').data |
|---|
| 579 |
GroupID = GroupData_block.get_variable('GroupID').data |
|---|
| 580 |
GroupName = GroupData_block.get_variable('GroupName').data |
|---|
| 581 |
ListInProfile = GroupData_block.get_variable('ListInProfile').data |
|---|
| 582 |
Contribution = GroupData_block.get_variable('Contribution').data |
|---|
| 583 |
GroupInsigniaID = GroupData_block.get_variable('GroupInsigniaID').data |
|---|
| 584 |
|
|---|
| 585 |
# make sense of group powers |
|---|
| 586 |
GroupPowers = [ord(x) for x in GroupPowers] |
|---|
| 587 |
GroupPowers = ''.join([str(x) for x in GroupPowers]) |
|---|
| 588 |
|
|---|
| 589 |
group = Group(AcceptNotices, GroupPowers, GroupID, GroupName, ListInProfile, Contribution,GroupInsigniaID ) |
|---|
| 590 |
|
|---|
| 591 |
self.group_manager.store_group(group) |
|---|
| 592 |
|
|---|
| 593 |
def onChatFromSimulator(self, packet): |
|---|
| 594 |
""" callback handler for received ChatFromSimulator messages which parses and fires a ChatReceived event. """ |
|---|
| 595 |
|
|---|
| 596 |
log(INFO, "Working on parsing chat messages....") |
|---|
| 597 |
|
|---|
| 598 |
FromName = packet.blocks['ChatData'][0].get_variable('FromName').data |
|---|
| 599 |
SourceID = packet.blocks['ChatData'][0].get_variable('SourceID').data |
|---|
| 600 |
OwnerID = packet.blocks['ChatData'][0].get_variable('OwnerID').data |
|---|
| 601 |
SourceType = packet.blocks['ChatData'][0].get_variable('SourceType').data |
|---|
| 602 |
ChatType = packet.blocks['ChatData'][0].get_variable('ChatType').data |
|---|
| 603 |
Audible = packet.blocks['ChatData'][0].get_variable('Audible').data |
|---|
| 604 |
Position = packet.blocks['ChatData'][0].get_variable('Position').data |
|---|
| 605 |
Message = packet.blocks['ChatData'][0].get_variable('Message').data |
|---|
| 606 |
|
|---|
| 607 |
message = AppEvent('ChatReceived', FromName = FromName, SourceID = SourceID, OwnerID = OwnerID, SourceType = SourceType, ChatType = ChatType, Audible = Audible, Position = Position, Message = Message) |
|---|
| 608 |
|
|---|
| 609 |
log(INFO, "Received chat from %s: %s" % (FromName, Message)) |
|---|
| 610 |
|
|---|
| 611 |
self.events_handler._handle(message) |
|---|
| 612 |
|
|---|
| 613 |
def onImprovedInstantMessage(self, packet): |
|---|
| 614 |
""" callback handler for received ImprovedInstantMessage messages. much is passed in this message, and handling the data is only partially implemented """ |
|---|
| 615 |
|
|---|
| 616 |
log(INFO, "Working on parsing ImprovedInstantMessage messages....") |
|---|
| 617 |
|
|---|
| 618 |
Dialog = packet.blocks['MessageBlock'][0].get_variable('Dialog').data |
|---|
| 619 |
FromAgentID = packet.blocks['AgentData'][0].get_variable('AgentID').data |
|---|
| 620 |
|
|---|
| 621 |
if Dialog == ImprovedIMDialogue.InventoryOffered: |
|---|
| 622 |
|
|---|
| 623 |
self.inventory.handle_inventory_offer(packet) |
|---|
| 624 |
|
|---|
| 625 |
elif Dialog == ImprovedIMDialogue.InventoryAccepted: |
|---|
| 626 |
|
|---|
| 627 |
if str(FromAgentID) != str(self.agent_id): |
|---|
| 628 |
|
|---|
| 629 |
FromAgentName = packet.blocks['MessageBlock'][0].get_variable('FromAgentName').data |
|---|
| 630 |
InventoryName = packet.blocks['MessageBlock'][0].get_variable('Message').data |
|---|
| 631 |
|
|---|
| 632 |
log(INFO, "Agent %s accepted the inventory offer." % (FromAgentName)) |
|---|
| 633 |
|
|---|
| 634 |
elif Dialog == ImprovedIMDialogue.InventoryDeclined: |
|---|
| 635 |
|
|---|
| 636 |
if str(FromAgentID) != str(self.agent_id): |
|---|
| 637 |
|
|---|
| 638 |
FromAgentName = packet.blocks['MessageBlock'][0].get_variable('FromAgentName').data |
|---|
| 639 |
InventoryName = packet.blocks['MessageBlock'][0].get_variable('Message').data |
|---|
| 640 |
|
|---|
| 641 |
log(INFO, "Agent %s declined the inventory offer." % (FromAgentName)) |
|---|
| 642 |
|
|---|
| 643 |
elif Dialog == ImprovedIMDialogue.FromAgent: |
|---|
| 644 |
|
|---|
| 645 |
RegionID = packet.blocks['MessageBlock'][0].get_variable('RegionID').data |
|---|
| 646 |
Position = packet.blocks['MessageBlock'][0].get_variable('Position').data |
|---|
| 647 |
ID = packet.blocks['MessageBlock'][0].get_variable('ID').data |
|---|
| 648 |
FromAgentName = packet.blocks['MessageBlock'][0].get_variable('FromAgentName').data |
|---|
| 649 |
Message = packet.blocks['MessageBlock'][0].get_variable('Message').data |
|---|
| 650 |
|
|---|
| 651 |
message = AppEvent('InstantMessageReceived', FromAgentID = FromAgentID, RegionID = RegionID, Position = Position, ID = ID, FromAgentName = FromAgentName, Message = Message) |
|---|
| 652 |
|
|---|
| 653 |
log(INFO, "Received instant message from %s: %s" % (FromAgentName, Message)) |
|---|
| 654 |
|
|---|
| 655 |
self.events_handler._handle(message) |
|---|
| 656 |
|
|---|
| 657 |
else: |
|---|
| 658 |
|
|---|
| 659 |
self.helpers.log_packet(packet, self) |
|---|
| 660 |
|
|---|
| 661 |
def onAlertMessage(self, packet): |
|---|
| 662 |
""" callback handler for received AlertMessage messages. logs and raises an event """ |
|---|
| 663 |
|
|---|
| 664 |
# ToDo: raise an event when this is received |
|---|
| 665 |
|
|---|
| 666 |
AlertMessage = packet.blocks['AlertData'][0].get_variable('Message').data |
|---|
| 667 |
|
|---|
| 668 |
log(WARNING, "AlertMessage from simulator: %s" % (AlertMessage)) |
|---|
| 669 |
|
|---|
| 670 |
def onEnableSimulator(self, packet): |
|---|
| 671 |
""" callback handler for received EnableSimulator messages. stores the region data for later connections """ |
|---|
| 672 |
|
|---|
| 673 |
IP = [ord(x) for x in packet.blocks['SimulatorInfo'][0].get_variable('IP').data] |
|---|
| 674 |
IP = '.'.join([str(x) for x in IP]) |
|---|
| 675 |
|
|---|
| 676 |
Port = packet.blocks['SimulatorInfo'][0].get_variable('Port').data |
|---|
| 677 |
|
|---|
| 678 |
# not sure what this is, but pass it up |
|---|
| 679 |
Handle = [ord(x) for x in packet.blocks['SimulatorInfo'][0].get_variable('Handle').data] |
|---|
| 680 |
|
|---|
| 681 |
region_params = {'IP': IP, 'Port': Port, 'Handle': Handle} |
|---|
| 682 |
|
|---|
| 683 |
log(INFO, 'Received EnableSimulator for %s' % (str(IP) + ":" + str(Port))) |
|---|
| 684 |
|
|---|
| 685 |
# are we already prepping to connect to the sim? |
|---|
| 686 |
if region_params not in self._pending_child_regions: |
|---|
| 687 |
|
|---|
| 688 |
# are we already connected to the sim? |
|---|
| 689 |
known_region = False |
|---|
| 690 |
|
|---|
| 691 |
# don't append to the list if we already know about this region |
|---|
| 692 |
for region in self.child_regions: |
|---|
| 693 |
if region.sim_ip == region_params['IP'] and region.sim_port == region_params['Port']: |
|---|
| 694 |
known_region = True |
|---|
| 695 |
|
|---|
| 696 |
#agent._enable_child_region(IP, Port, Handle) |
|---|
| 697 |
if not known_region: |
|---|
| 698 |
self._pending_child_regions.append(region_params) |
|---|
| 699 |
|
|---|
| 700 |
def onEstablishAgentCommunication(self, message): |
|---|
| 701 |
""" callback handler for received EstablishAgentCommunication messages. try to enable the event queue for a neighboring region based on the data received """ |
|---|
| 702 |
|
|---|
| 703 |
log(INFO, 'Received EstablishAgentCommunication for %s' % (message.blocks['Message_Data'][0].get_variable('sim-ip-and-port').data)) |
|---|
| 704 |
|
|---|
| 705 |
is_running = False |
|---|
| 706 |
|
|---|
| 707 |
# don't enable the event queue when we already have it running |
|---|
| 708 |
for region in self.child_regions: |
|---|
| 709 |
if (str(region.sim_ip) + ":" + str(region.sim_port) == message.blocks['Message_Data'][0].get_variable('sim-ip-and-port').data) and region.event_queue != None: |
|---|
| 710 |
if region.event_queue._running: |
|---|
| 711 |
is_running = True |
|---|
| 712 |
|
|---|
| 713 |
# start the event queue |
|---|
| 714 |
if not is_running: |
|---|
| 715 |
self._start_EQ_on_neighboring_region(message) |
|---|
| 716 |
|
|---|
| 717 |
|
|---|
| 718 |
def teleport(self, |
|---|
| 719 |
region_name=None, |
|---|
| 720 |
region_handle=None, |
|---|
| 721 |
region_id=None, |
|---|
| 722 |
position=Vector3(X=128, Y=128, Z=128), |
|---|
| 723 |
look_at=Vector3(X=128, Y=128, Z=128)): |
|---|
| 724 |
"""Initiate a teleport to the specified location. When passing a region name |
|---|
| 725 |
it may be necessary to request the destination region handle from the current sim |
|---|
| 726 |
before the teleport can start.""" |
|---|
| 727 |
|
|---|
| 728 |
log(INFO, 'teleport name=%s handle=%s id=%s', str(region_name), str(region_handle), str(region_id)) |
|---|
| 729 |
|
|---|
| 730 |
# Handle intra-region teleports even by name |
|---|
| 731 |
if not region_id and region_name and region_name.lower() == self.region.SimName.lower(): |
|---|
| 732 |
region_id = self.region.RegionID |
|---|
| 733 |
|
|---|
| 734 |
if not region_id and not region_handle and region_name.lower() in self.region_name_map: |
|---|
| 735 |
region_handle = self.region_name_map[region_name.lower()] |
|---|
| 736 |
|
|---|
| 737 |
if region_id: |
|---|
| 738 |
log(INFO, 'sending TP request packet') |
|---|
| 739 |
packet = TeleportRequestPacket() |
|---|
| 740 |
|
|---|
| 741 |
packet.AgentData['AgentID'] = self.agent_id |
|---|
| 742 |
packet.AgentData['SessionID'] = self.session_id |
|---|
| 743 |
|
|---|
| 744 |
packet.Info['RegionID'] = region_id |
|---|
| 745 |
packet.Info['Position'] = position |
|---|
| 746 |
packet.Info['LookAt'] = look_at |
|---|
| 747 |
|
|---|
| 748 |
self.region.enqueue_message(packet()) |
|---|
| 749 |
|
|---|
| 750 |
elif region_handle: |
|---|
| 751 |
log(INFO, 'sending TP location request packet') |
|---|
| 752 |
packet = TeleportLocationRequestPacket() |
|---|
| 753 |
|
|---|
| 754 |
packet.AgentData['AgentID'] = self.agent_id |
|---|
| 755 |
packet.AgentData['SessionID'] = self.session_id |
|---|
| 756 |
|
|---|
| 757 |
packet.Info['RegionHandle'] = region_handle |
|---|
| 758 |
packet.Info['Position'] = position |
|---|
| 759 |
packet.Info['LookAt'] = look_at |
|---|
| 760 |
|
|---|
| 761 |
self.region.enqueue_message(packet()) |
|---|
| 762 |
|
|---|
| 763 |
else: |
|---|
| 764 |
log(INFO, "Target region's handle not known, sending map name request") |
|---|
| 765 |
# do a region_name to region_id lookup and then request the teleport |
|---|
| 766 |
self.send_MapNameRequest( |
|---|
| 767 |
region_name, |
|---|
| 768 |
lambda handle: self.teleport(region_handle=handle, position=position, look_at=look_at)) |
|---|
| 769 |
|
|---|
| 770 |
|
|---|
| 771 |
def send_MapNameRequest(self, region_name, callback): |
|---|
| 772 |
|
|---|
| 773 |
handler = self.region.message_handler.register('MapBlockReply') |
|---|
| 774 |
|
|---|
| 775 |
def onMapBlockReplyPacket(packet): |
|---|
| 776 |
log(INFO, 'MapBlockReplyPacket received') |
|---|
| 777 |
for block in packet.blocks['Data']: |
|---|
| 778 |
if block.get_variable('Name').data.lower() == region_name.lower(): |
|---|
| 779 |
handler.unsubscribe(onMapBlockReplyPacket) |
|---|
| 780 |
|
|---|
| 781 |
x = block.get_variable('X').data |
|---|
| 782 |
y = block.get_variable('Y').data |
|---|
| 783 |
region_handle = Region.xy_to_handle(x,y) |
|---|
| 784 |
|
|---|
| 785 |
self.region_name_map[region_name.lower()] = region_handle |
|---|
| 786 |
|
|---|
| 787 |
callback(region_handle) |
|---|
| 788 |
return |
|---|
| 789 |
# Leave it registered, as the event may come later |
|---|
| 790 |
|
|---|
| 791 |
# Register a handler for the response |
|---|
| 792 |
handler.subscribe(onMapBlockReplyPacket) |
|---|
| 793 |
|
|---|
| 794 |
# ...and make the request |
|---|
| 795 |
log(INFO, 'sending MapNameRequestPacket') |
|---|
| 796 |
packet = MapNameRequestPacket() |
|---|
| 797 |
packet.AgentData['AgentID'] = self.agent_id |
|---|
| 798 |
packet.AgentData['SessionID'] = self.session_id |
|---|
| 799 |
packet.AgentData['Flags'] = 0 |
|---|
| 800 |
packet.AgentData['EstateID'] = 0 |
|---|
| 801 |
packet.AgentData['Godlike'] = False |
|---|
| 802 |
packet.NameData['Name'] = region_name.lower() |
|---|
| 803 |
self.region.enqueue_message(packet()) |
|---|
| 804 |
|
|---|
| 805 |
def onTeleportFinish(self, packet): |
|---|
| 806 |
"""Handle the end of a successful teleport""" |
|---|
| 807 |
|
|---|
| 808 |
log(INFO, "Teleport finished, taking care of details...") |
|---|
| 809 |
|
|---|
| 810 |
# Raise a plain-vanilla AppEvent for the Info block |
|---|
| 811 |
self.simple_callback('Info')(packet) |
|---|
| 812 |
|
|---|
| 813 |
# packed binary U64 to integral x, y |
|---|
| 814 |
region_handle = packet.blocks['Info'][0].get_variable('RegionHandle').data |
|---|
| 815 |
region_x, region_y = Region.handle_to_xy(region_handle) |
|---|
| 816 |
|
|---|
| 817 |
# packed binary to dotted-octet |
|---|
| 818 |
sim_ip = packet.blocks['Info'][0].get_variable('SimIP').data |
|---|
| 819 |
sim_ip = '.'.join(map(str,struct.unpack('BBBB', sim_ip))) |
|---|
| 820 |
|
|---|
| 821 |
# *TODO: Make this more graceful |
|---|
| 822 |
log(INFO, "Disconnecting from old region") |
|---|
| 823 |
[region._kill_coroutines() for region in self.child_regions] |
|---|
| 824 |
self.region._kill_coroutines() |
|---|
| 825 |
|
|---|
| 826 |
self.region = None |
|---|
| 827 |
self.child_regions = [] |
|---|
| 828 |
self._pending_child_regions = [] |
|---|
| 829 |
|
|---|
| 830 |
log(INFO, "Enabling new region") |
|---|
| 831 |
self._enable_current_region( |
|---|
| 832 |
region_x = region_x, |
|---|
| 833 |
region_y = region_y, |
|---|
| 834 |
seed_capability = packet.blocks['Info'][0].get_variable('SeedCapability').data, |
|---|
| 835 |
sim_ip = sim_ip, |
|---|
| 836 |
sim_port = packet.blocks['Info'][0].get_variable('SimPort').data |
|---|
| 837 |
) |
|---|
| 838 |
|
|---|
| 839 |
|
|---|
| 840 |
def request_agent_names(self, agent_ids, callback): |
|---|
| 841 |
"""Request agent names. When all names are known, callback |
|---|
| 842 |
will be called with a list of tuples (agent_id, first_name, |
|---|
| 843 |
last_name). If all names are known, callback will be called |
|---|
| 844 |
immediately.""" |
|---|
| 845 |
|
|---|
| 846 |
def _fire_callback(_): |
|---|
| 847 |
cbdata = [(agent_id, |
|---|
| 848 |
self.agent_id_map[agent_id][0], |
|---|
| 849 |
self.agent_id_map[agent_id][1]) |
|---|
| 850 |
for agent_id in agent_ids] |
|---|
| 851 |
callback(cbdata) |
|---|
| 852 |
|
|---|
| 853 |
names_to_request = [ agent_id |
|---|
| 854 |
for agent_id in agent_ids |
|---|
| 855 |
if agent_id not in self.agent_id_map ] |
|---|
| 856 |
if names_to_request: |
|---|
| 857 |
self.send_UUIDNameRequest(names_to_request, _fire_callback) |
|---|
| 858 |
else: |
|---|
| 859 |
_fire_callback([]) |
|---|
| 860 |
|
|---|
| 861 |
|
|---|
| 862 |
def send_UUIDNameRequest(self, agent_ids, callback): |
|---|
| 863 |
handler = self.region.message_handler.register('UUIDNameReply') |
|---|
| 864 |
|
|---|
| 865 |
def onUUIDNameReply(packet): |
|---|
| 866 |
log(INFO, 'UUIDNameReplyPacket received') |
|---|
| 867 |
|
|---|
| 868 |
cbdata = [] |
|---|
| 869 |
for block in packet.blocks['UUIDNameBlock']: |
|---|
| 870 |
agent_id = str(block.get_variable('ID').data) |
|---|
| 871 |
first_name = block.get_variable('FirstName').data |
|---|
| 872 |
last_name = block.get_variable('LastName').data |
|---|
| 873 |
self.agent_id_map[agent_id] = (first_name, last_name) |
|---|
| 874 |
cbdata.append((agent_id, first_name, last_name)) |
|---|
| 875 |
|
|---|
| 876 |
# Fire the callback only when all names are received |
|---|
| 877 |
missing = [ agent_id |
|---|
| 878 |
for agent_id in agent_ids |
|---|
| 879 |
if agent_id not in self.agent_id_map ] |
|---|
| 880 |
if len(missing) == 0: |
|---|
| 881 |
handler.unsubscribe(onUUIDNameReply) |
|---|
| 882 |
callback(cbdata) |
|---|
| 883 |
else: |
|---|
| 884 |
log(INFO, 'Still waiting on %d names', len(missing)) |
|---|
| 885 |
|
|---|
| 886 |
handler.subscribe(onUUIDNameReply) |
|---|
| 887 |
log(INFO, 'sending UUIDNameRequest') |
|---|
| 888 |
packet = UUIDNameRequestPacket() |
|---|
| 889 |
packet.UUIDNameBlockBlocks = [ {'ID':UUID(agent_id) } for agent_id in agent_ids ] |
|---|
| 890 |
self.region.enqueue_message(packet()) |
|---|
| 891 |
|
|---|
| 892 |
|
|---|
| 893 |
def request_balance(self, callback): |
|---|
| 894 |
"""Request the current agent balance.""" |
|---|
| 895 |
handler = self.region.message_handler.register('MoneyBalanceReply') |
|---|
| 896 |
|
|---|
| 897 |
def onMoneyBalanceReply(packet): |
|---|
| 898 |
log(INFO, 'MoneyBalanceReply received') |
|---|
| 899 |
handler.unsubscribe(onMoneyBalanceReply) # One-shot handler |
|---|
| 900 |
balance = packet.blocks['MoneyData'][0].get_variable('MoneyBalance').data |
|---|
| 901 |
callback(balance) |
|---|
| 902 |
|
|---|
| 903 |
handler.subscribe(onMoneyBalanceReply) |
|---|
| 904 |
log(INFO, 'sending MoneyBalanceRequest') |
|---|
| 905 |
packet = MoneyBalanceRequestPacket() |
|---|
| 906 |
packet.AgentData['AgentID'] = self.agent_id |
|---|
| 907 |
packet.AgentData['SessionID'] = self.session_id |
|---|
| 908 |
packet.MoneyData['TransactionID'] = UUID() |
|---|
| 909 |
self.region.enqueue_message(packet()) |
|---|
| 910 |
|
|---|
| 911 |
|
|---|
| 912 |
def give_money(self, target_id, amount, |
|---|
| 913 |
description='', |
|---|
| 914 |
transaction_type=MoneyTransactionType.Gift, |
|---|
| 915 |
flags=TransactionFlags.Null): |
|---|
| 916 |
"""Give money to another agent""" |
|---|
| 917 |
log(INFO, 'sending MoneyTransferRequest') |
|---|
| 918 |
packet = MoneyTransferRequestPacket() |
|---|
| 919 |
packet.AgentData['AgentID'] = self.agent_id |
|---|
| 920 |
packet.AgentData['SessionID'] = self.session_id |
|---|
| 921 |
packet.MoneyData['SourceID'] = self.agent_id |
|---|
| 922 |
packet.MoneyData['DestID'] = UUID(target_id) |
|---|
| 923 |
packet.MoneyData['Flags'] = flags |
|---|
| 924 |
packet.MoneyData['Amount'] = amount |
|---|
| 925 |
packet.MoneyData['AggregatePermNextOwner'] = 0 |
|---|
| 926 |
packet.MoneyData['AggregatePermInventory'] = 0 |
|---|
| 927 |
packet.MoneyData['TransactionType'] = transaction_type |
|---|
| 928 |
packet.MoneyData['Description'] = description |
|---|
| 929 |
self.region.enqueue_message(packet()) |
|---|
| 930 |
|
|---|
| 931 |
|
|---|
| 932 |
class Home(object): |
|---|
| 933 |
""" contains the parameters describing an agent's home location as returned in login_response['home'] """ |
|---|
| 934 |
|
|---|
| 935 |
def __init__(self, params): |
|---|
| 936 |
""" initialize the Home object by parsing the data passed in """ |
|---|
| 937 |
|
|---|
| 938 |
# eval(params) would be nice, but fails to parse the string the way one thinks it might |
|---|
| 939 |
items = params.split(', \'') |
|---|
| 940 |
|
|---|
| 941 |
# this creates: |
|---|
| 942 |
# self.region_handle |
|---|
| 943 |
# self.look_at |
|---|
| 944 |
# self.position |
|---|
| 945 |
for i in items: |
|---|
| 946 |
i = re.sub(r'[\"\{}\'"]', '', i) |
|---|
| 947 |
i = i.split(':') |
|---|
| 948 |
setattr(self, i[0], eval(re.sub('r', '', i[1]))) |
|---|
| 949 |
|
|---|
| 950 |
self.global_x = self.region_handle[0] |
|---|
| 951 |
self.global_y = self.region_handle[1] |
|---|
| 952 |
|
|---|
| 953 |
self.local_x = self.position[0] |
|---|
| 954 |
self.local_y = self.position[1] |
|---|
| 955 |
self.local_z = self.position[2] |
|---|
| 956 |
|
|---|
| 957 |
""" |
|---|
| 958 |
Contributors can be viewed at: |
|---|
| 959 |
http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt |
|---|
| 960 |
|
|---|
| 961 |
$LicenseInfo:firstyear=2008&license=apachev2$ |
|---|
| 962 |
|
|---|
| 963 |
Copyright 2009, Linden Research, Inc. |
|---|
| 964 |
|
|---|
| 965 |
Licensed under the Apache License, Version 2.0 (the "License"). |
|---|
| 966 |
You may obtain a copy of the License at: |
|---|
| 967 |
http://www.apache.org/licenses/LICENSE-2.0 |
|---|
| 968 |
or in |
|---|
| 969 |
http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt |
|---|
| 970 |
|
|---|
| 971 |
$/LicenseInfo$ |
|---|
| 972 |
""" |
|---|