Files
Hippolyzer/pyogp/lib/base/regiondomain.py

396 lines
14 KiB
Python

"""
@file regiondomain.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$
"""
# std lib
from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG
import re
from urllib import quote
from urlparse import urlparse, urljoin
import time
import uuid
import os
# eventlet
import sys
lib_dir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..', 'src/lib'))
if lib_dir not in sys.path:
sys.path.insert(0, lib_dir)
#from eventlet import api, coros
try:
from eventlet import api, coros
except ImportError:
print "Error importing eventlet"
sys.exit()
# related
from indra.base import llsd
# pyogp
from pyogp.lib.base.caps import Capability, SeedCapability
from pyogp.lib.base.network.stdlib_client import StdLibClient, HTTPError
import pyogp.lib.base.exc
from pyogp.lib.base.settings import Settings
# messaging
from pyogp.lib.base.message.udpdispatcher import UDPDispatcher
from pyogp.lib.base.message.message import Message, Block
from pyogp.lib.base.message.circuit import Host
from pyogp.lib.base.message.types import MsgType
from pyogp.lib.base.message.packets import *
# initialize logging
logger = getLogger('pyogp.lib.base.regiondomain')
log = logger.log
class Region(object):
"""models a region endpoint"""
def __init__(self, uri=None, regionname=None):
"""initialize the region with the region uri"""
if (uri != None): self.region_uri = self.parse_region_uri(uri)
self.regionname = regionname
self.seed_cap_url = ''
self.seed_cap = None
self.settings = Settings()
# details is currently a catchall for things that need to be split out...
self.details = {}
self._isUDPRunning = False
self._isEventQueueRunning = False
self.capabilities = {}
self.region_caps_list = ['ChatSessionRequest',
'CopyInventoryFromNotecard',
'DispatchRegionInfo',
'EstateChangeInfo',
'EventQueueGet',
'FetchInventory',
'WebFetchInventoryDescendents',
'FetchLib',
'FetchLibDescendents',
'GroupProposalBallot',
'HomeLocation',
'MapLayer',
'MapLayerGod',
'NewFileAgentInventory',
'ParcelPropertiesUpdate',
'ParcelVoiceInfoRequest',
'ProvisionVoiceAccountRequest',
'RemoteParcelRequest',
'RequestTextureDownload',
'SearchStatRequest',
'SearchStatTracking',
'SendPostcard',
'SendUserReport',
'SendUserReportWithScreenshot',
'ServerReleaseNotes',
'StartGroupProposal',
'UpdateAgentLanguage',
'UpdateGestureAgentInventory',
'UpdateNotecardAgentInventory',
'UpdateScriptAgent',
'UpdateGestureTaskInventory',
'UpdateNotecardTaskInventory',
'UpdateScriptTask',
'ViewerStartAuction',
'UntrustedSimulatorMessage',
'ViewerStats'
]
#self.actor = None
log(DEBUG, 'initializing region domain: %s' %self)
def set_seed_cap_url(self, url):
self.seed_cap_url = url
self.seed_cap = RegionSeedCapability('seed_cap', self.seed_cap_url)
log(DEBUG, 'setting region domain seed cap: %s' % (self.seed_cap_url))
def parse_region_uri(self, uri):
""" parse a region uri and returns one formatted appropriately """
region_uri = urljoin(uri, quote(urlparse(uri)[2]))
# test if it is a lindenlab.com domain name
'''
if (re.search('lindenlab', urlparse(uri)[1])):
if (re.search('lindenlab\.com/public_seed$', uri)):
region_uri = uri
else:
region_uri = uri + '/public_seed'
else:
# this is a crappy test to see if it's already been urlencoded, it only checkes for spaces
if re.search('%20', urlparse(uri)[2]):
region_uri = uri
else:
region_uri = urljoin(uri, quote(urlparse(uri)[2]))
'''
return region_uri
def get_region_public_seed(self,custom_headers={'Accept' : 'application/llsd+xml'}):
"""call this capability, return the parsed result"""
log(DEBUG, 'Getting region public_seed %s' %(self.region_uri))
try:
restclient = StdLibClient()
response = restclient.GET(self.region_uri, custom_headers)
except HTTPError, e:
if e.code==404:
raise exc.ResourceNotFound(self.region_uri)
else:
raise exc.ResourceError(self.region_uri, e.code, e.msg, e.fp.read(), method="GET")
data = llsd.parse(response.body)
if self.settings.ENABLE_CAPS_LOGGING: log(DEBUG, 'Get of cap %s response is: %s' % (self.region_uri, data))
return data
def get_region_capabilities(self):
""" queries the region seed cap for capabilities """
if (self.seed_cap == None):
raise exc.RegionSeedCapNotAvailable("querying for agent capabilities")
return
else:
log(INFO, 'Getting caps from region seed cap %s' % (self.seed_cap))
# use self.region_caps.keys() to pass a list to be parsed into LLSD
self.capabilities = self.seed_cap.get(self.region_caps_list)
def connect(self):
""" connect to the udp circuit code and event queue"""
self.messenger = UDPDispatcher()
self.host = None
self.host = Host((self.details['sim_ip'],
self.details['sim_port']))
self.init_agent_in_region()
self.last_ping = 0
#self.start = time.time()
#self.now = self.start
self.packets = {}
log(DEBUG, 'Spawning region UDP connection')
api.spawn(self._processUDP)
log(DEBUG, 'Spawning region event queue connection')
self.get_region_capabilities()
api.spawn(self._processEventQueue)
def logout(self):
""" send a logout packet """
# this should move to a handled method
msg = Message('LogoutRequest',
Block('AgentData', AgentID=uuid.UUID(self.details['agent_id']),
SessionID=uuid.UUID(self.details['session_id'])
)
)
self.messenger.send_message(msg, self.host)
def init_agent_in_region(self):
""" send a few packets to set things up """
packet = UseCircuitCodePacket()
packet.CircuitCode['Code'] = self.details['circuit_code']
packet.CircuitCode['SessionID'] = uuid.UUID(self.details['session_id']) # MVT_LLUUID
packet.CircuitCode['ID'] = uuid.UUID(self.details['agent_id']) # MVT_LLUUID
msg = Message('UseCircuitCode',
Block('CircuitCode', Code=self.details['circuit_code'],
SessionID=uuid.UUID(self.details['session_id']),
ID=uuid.UUID(self.details['agent_id'])))
self.messenger.send_reliable(packet(), self.host, 0)
time.sleep(1)
#SENDS CompleteAgentMovement
msg = Message('CompleteAgentMovement',
Block('AgentData', AgentID=uuid.UUID(self.details['agent_id']),
SessionID=uuid.UUID(self.details['session_id']),
CircuitCode=self.details['circuit_code']))
self.messenger.send_reliable(msg, self.host, 0)
#SENDS UUIDNameRequest
msg = Message('UUIDNameRequest',
Block('UUIDNameBlock', ID=uuid.UUID(self.details['agent_id'])
)
)
self.messenger.send_message(msg, self.host)
msg = Message('AgentUpdate',
Block('AgentData', AgentID=uuid.UUID(self.details['agent_id']),
SessionID=uuid.UUID(self.details['session_id']),
BodyRotation=(0.0,0.0,0.0,0.0),
HeadRotation=(0.0,0.0,0.0,0.0),
State=0x00,
CameraCenter=(0.0,0.0,0.0),
CameraAtAxis=(0.0,0.0,0.0),
CameraLeftAxis=(0.0,0.0,0.0),
CameraUpAxis=(0.0,0.0,0.0),
Far=0,
ControlFlags=0x00,
Flags=0x00))
self.messenger.send_message(msg, self.host)
def _processUDP(self):
self._isUDPRunning = True
while self._isUDPRunning:
# free up resources for other stuff to happen
api.sleep(0)
msg_buf, msg_size = self.messenger.udp_client.receive_packet(self.messenger.socket)
packet = self.messenger.receive_check(self.messenger.udp_client.get_sender(),
msg_buf, msg_size)
if packet != None:
#print 'Received: ' + packet.name + ' from ' + self.messenger.udp_client.sender.ip + ":" + \
#str(self.messenger.udp_client.sender.port)
#MESSAGE HANDLERS
if packet.name == 'RegionHandshake':
# ToDo: move these guys elsewhere
log(WARNING, "MOVE ME NOW")
#msg = Message('RegionHandshakeReply',
#[Block('AgentData', AgentID=uuid.UUID(self.details['agent_id']),
#SessionID=uuid.UUID(self.details['session_id'])),
#Block('RegionInfo', RegionInfo=0x00)])
packet = RegionHandshakeReplyPacket()
packet.AgentData['SessionID'] = uuid.UUID(self.details['session_id']) # MVT_LLUUID
packet.AgentData['AgentID'] = uuid.UUID(self.details['agent_id'])
packet.RegionInfo['Flags'] = 0x00
for i in range(0,10):
self.messenger.send_reliable(packet(), self.host, 0)
elif packet.name == 'StartPingCheck':
log(WARNING, "MOVE ME NOW")
msg = Message('CompletePingCheck',
Block('PingID', PingID=self.last_ping))
self.messenger.send_message(msg, self.host)
self.last_ping += 1
# ToDo: REMOVE ME: self.packets will jsut grow and grow, this is here for testing purposes
#if packet.name not in self.packets:
#self.packets[packet.name] = 1
#else:
#self.packets[packet.name] += 1
else:
#print 'No message'
pass
#self.now = time.time()
if self.messenger.has_unacked():
#print 'Acking'
self.messenger.process_acks()
msg = Message('AgentUpdate',
Block('AgentData', AgentID=uuid.UUID(self.details['agent_id']),
SessionID=uuid.UUID(self.details['session_id']),
BodyRotation=(0.0,0.0,0.0,0.0),
HeadRotation=(0.0,0.0,0.0,0.0),
State=0x00,
CameraCenter=(0.0,0.0,0.0),
CameraAtAxis=(0.0,0.0,0.0),
CameraLeftAxis=(0.0,0.0,0.0),
CameraUpAxis=(0.0,0.0,0.0),
Far=0,
ControlFlags=0x00,
Flags=0x00))
self.messenger.send_message(msg, self.host)
def _processEventQueue(self):
self._isEventQueueRunning = True
self.last_id = -1
if self.capabilities['EventQueueGet'] == None:
raise exc.RegionCapNotAvailable('EventQueueGet')
# well then get it...
else:
while self._isEventQueueRunning:
# need to be able to pull data from a queue somewhere
data = {}
api.sleep(self.settings.region_event_queue_interval)
if self.last_id != -1:
data = {'ack':self.last_id, 'done':True}
# ToDo: this is blocking, we need to break this
result = self.capabilities['EventQueueGet'].POST(data)
if result != None: self.last_id = result['id']
#log(DEBUG, 'region event queue cap called, returned id: %s' % (self.last_id))
if self.settings.ENABLE_CAPS_LOGGING: log(DEBUG, 'Region EventQueueGet result: %s' % (result))
class RegionSeedCapability(Capability):
""" a seed capability which is able to retrieve other capabilities """
def get(self, names=[]):
"""if this is a seed cap we can retrieve other caps here"""
#log(INFO, 'requesting from the region domain the following caps: %s' % (names))
payload = names
parsed_result = self.POST(payload) #['caps']
log(INFO, 'Request for caps returned: %s' % (parsed_result.keys()))
caps = {}
for name in names:
# TODO: some caps might be seed caps, how do we know?
if parsed_result.has_key(name):
caps[name]=Capability(name, parsed_result[name])
else:
log(DEBUG, 'Requested capability \'%s\' is not available' % (name))
#log(INFO, 'got cap: %s' % (name))
return caps
def __repr__(self):
return "<RegionSeedCapability for %s>" % (self.public_url)