Files
Hippolyzer/pyogp/lib/base/parcel.py
2009-05-15 16:16:32 +00:00

1166 lines
46 KiB
Python

# standard python modules
from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG
# related
from eventlet import api
# pyogp
from pyogp.lib.base.settings import Settings
from pyogp.lib.base.datatypes import UUID
from pyogp.lib.base.exc import *
# pyogp messaging
from pyogp.lib.base.message.packets import *
# utilities
from pyogp.lib.base.utilities.helpers import Helpers
# pyogp messaging
# pyogp utilities
# initialize logging
logger = getLogger('pyogp.lib.base.parcel')
log = logger.log
# ToDo: unhandled related messages
# ForceObjectSelect
# ParcelBuyPass
# ParcelAccessListUpdate
# ParcelDwellRequest
# ParcelDwellReply
# ParcelGodMarkAsContent
# ViewerStartAuction
# ParcelObjectOwnersRequest
# ParcelObjectOwnersReply
# non client oriented related messages
# #RequestParcelTransfer (sim->dataserver)
# #UpdateParcel (sim->dataserver)
# #RemoveParcel (sim->dataserver)
# #MergeParcel (sim->dataserver)
# #LogParcelChanges (sim->dataserver)
# #CheckParcelSales (sim->dataserver)
# #ParcelSales (dataserver->sim)
# #StartAuction (sim->dataserver)
# #ConfirmAuctionStart (dataserver->sim)
# #CompleteAuction (sim->dataserver)
# #CancelAuction (sim->dataserver)
# #CheckParcelAuctions (sim->dataserver)
# #ParcelAuctions (dataserver->sim)
class ParcelManager(object):
""" a parcel manager, generally used as an attribute of a region """
def __init__(self, region = None, agent = None, packet_handler = None, events_handler = None, settings = None):
""" initialize the parcel manager """
# 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()
# store the incoming parameters
self.region = region
self.agent = agent
self.packet_handler = packet_handler
# otherwise, let's just use our own
if events_handler != None:
self.events_handler = events_handler
else:
self.events_handler = EventsHandler()
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
# set up callbacks for parcel related packets
self.onParcelOverlay_received = self.packet_handler._register('ParcelOverlay')
self.onParcelOverlay_received.subscribe(self.onParcelOverlay)
self.onParcelProperties_received = self.agent.event_queue_handler._register('ParcelProperties')
self.onParcelProperties_received.subscribe(self.onParcelProperties)
self.onParcelPropertiesUpdate_received = self.packet_handler._register('ParcelPropertiesUpdate')
self.onParcelPropertiesUpdate_received.subscribe(self.onParcelPropertiesUpdate)
self.onParcelInfoReply_received = self.packet_handler._register('ParcelInfoReply')
self.onParcelInfoReply_received.subscribe(self.onParcelInfoReply)
if self.settings.LOG_VERBOSE: log(DEBUG, "Initializing the parcel manager for %s in region %s." % (self.agent.Name(), self.region.SimName))
def onParcelOverlay(self, packet):
""" parse and handle an incoming ParcelOverlay packet
Currently, we store this data in the ParcelManager.packet_overlay dictionary
as parcel_overlay[sequence_id] = data (unparsed binary)
"""
# unpack the data
sequence_id = packet.message_data.blocks['ParcelData'][0].get_variable('SequenceID').data
data = packet.message_data.blocks['ParcelData'][0].get_variable('Data').data
# store the data
# ToDo: make sense of the binary blob in data
self.parcel_overlay[sequence_id] = data
def onParcelProperties(self, packet):
""" parse and handle an incoming ParcelProperties packet. Parse and serialize the info into a Parcel() representation, then store it (or replace the stored version) """
parcel_info = {}
parcel_info['RequestResult'] = packet.message_data.blocks['ParcelData'][0].get_variable('RequestResult').data
parcel_info['SequenceID'] = packet.message_data.blocks['ParcelData'][0].get_variable('SequenceID').data
parcel_info['SnapSelection'] = packet.message_data.blocks['ParcelData'][0].get_variable('SnapSelection').data
parcel_info['SelfCount'] = packet.message_data.blocks['ParcelData'][0].get_variable('SelfCount').data
parcel_info['OtherCount'] = packet.message_data.blocks['ParcelData'][0].get_variable('OtherCount').data
parcel_info['PublicCount'] = packet.message_data.blocks['ParcelData'][0].get_variable('PublicCount').data
parcel_info['LocalID'] = packet.message_data.blocks['ParcelData'][0].get_variable('LocalID').data
parcel_info['OwnerID'] = packet.message_data.blocks['ParcelData'][0].get_variable('OwnerID').data
parcel_info['IsGroupOwned'] = packet.message_data.blocks['ParcelData'][0].get_variable('IsGroupOwned').data
parcel_info['AuctionID'] = packet.message_data.blocks['ParcelData'][0].get_variable('AuctionID').data
parcel_info['ClaimDate'] = packet.message_data.blocks['ParcelData'][0].get_variable('ClaimDate').data
parcel_info['ClaimPrice'] = packet.message_data.blocks['ParcelData'][0].get_variable('ClaimPrice').data
parcel_info['RentPrice'] = packet.message_data.blocks['ParcelData'][0].get_variable('RentPrice').data
parcel_info['AABBMin'] = packet.message_data.blocks['ParcelData'][0].get_variable('AABBMin').data
parcel_info['AABBMax'] = packet.message_data.blocks['ParcelData'][0].get_variable('AABBMax').data
parcel_info['Bitmap'] = packet.message_data.blocks['ParcelData'][0].get_variable('Bitmap').data
parcel_info['Area'] = packet.message_data.blocks['ParcelData'][0].get_variable('Area').data
parcel_info['Status'] = packet.message_data.blocks['ParcelData'][0].get_variable('Status').data
parcel_info['SimWideMaxPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('SimWideMaxPrims').data
parcel_info['SimWideTotalPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('SimWideTotalPrims').data
parcel_info['MaxPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('MaxPrims').data
parcel_info['TotalPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('TotalPrims').data
parcel_info['OwnerPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('OwnerPrims').data
parcel_info['GroupPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('GroupPrims').data
parcel_info['OtherPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('OtherPrims').data
parcel_info['SelectedPrims'] = packet.message_data.blocks['ParcelData'][0].get_variable('SelectedPrims').data
parcel_info['ParcelPrimBonus'] = packet.message_data.blocks['ParcelData'][0].get_variable('ParcelPrimBonus').data
parcel_info['OtherCleanTime'] = packet.message_data.blocks['ParcelData'][0].get_variable('OtherCleanTime').data
parcel_info['ParcelFlags'] = packet.message_data.blocks['ParcelData'][0].get_variable('ParcelFlags').data
parcel_info['SalePrice'] = packet.message_data.blocks['ParcelData'][0].get_variable('SalePrice').data
parcel_info['Name'] = packet.message_data.blocks['ParcelData'][0].get_variable('Name').data
parcel_info['Desc'] = packet.message_data.blocks['ParcelData'][0].get_variable('Desc').data
parcel_info['MusicURL'] = packet.message_data.blocks['ParcelData'][0].get_variable('MusicURL').data
parcel_info['MediaURL'] = packet.message_data.blocks['ParcelData'][0].get_variable('MediaURL').data
parcel_info['MediaID'] = packet.message_data.blocks['ParcelData'][0].get_variable('MediaID').data
parcel_info['MediaAutoScale'] = packet.message_data.blocks['ParcelData'][0].get_variable('MediaAutoScale').data
parcel_info['GroupID'] = packet.message_data.blocks['ParcelData'][0].get_variable('GroupID').data
parcel_info['PassPrice'] = packet.message_data.blocks['ParcelData'][0].get_variable('PassPrice').data
parcel_info['PassHours'] = packet.message_data.blocks['ParcelData'][0].get_variable('PassHours').data
parcel_info['Category'] = packet.message_data.blocks['ParcelData'][0].get_variable('Category').data
parcel_info['AuthBuyerID'] = packet.message_data.blocks['ParcelData'][0].get_variable('AuthBuyerID').data
parcel_info['SnapshotID'] = packet.message_data.blocks['ParcelData'][0].get_variable('SnapshotID').data
parcel_info['UserLocation'] = packet.message_data.blocks['ParcelData'][0].get_variable('UserLocation').data
parcel_info['UserLookAt'] = packet.message_data.blocks['ParcelData'][0].get_variable('UserLookAt').data
parcel_info['LandingType'] = packet.message_data.blocks['ParcelData'][0].get_variable('LandingType').data
parcel_info['RegionPushOverride'] = packet.message_data.blocks['ParcelData'][0].get_variable('RegionPushOverride').data
parcel_info['RegionDenyAnonymous'] = packet.message_data.blocks['ParcelData'][0].get_variable('RegionDenyAnonymous').data
parcel_info['RegionDenyIdentified'] = packet.message_data.blocks['ParcelData'][0].get_variable('RegionDenyIdentified').data
parcel_info['RegionDenyTransacted'] = packet.message_data.blocks['ParcelData'][0].get_variable('RegionDenyTransacted').data
parcel_info['RegionDenyAgeUnverified'] = packet.message_data.blocks['AgeVerificationBlock'][0].get_variable('RegionDenyAgeUnverified').data
self._store_parcel_properties(parcel_info)
def onParcelPropertiesUpdate(self, packet):
""" parse and handle an incoming ParcelPropertiesUpdate packet. parse the data into a dictionary and pass the blob to the Parcel() instance for self handling """
parcel_update = {}
parcel_update['LocalID'] = packet.message_data.blocks['ParcelData'][0].get_variable('LocalID').data
parcel_update['Flags'] = packet.message_data.blocks['ParcelData'][0].get_variable('Flags').data
parcel_update['ParcelFlags'] = packet.message_data.blocks['ParcelData'][0].get_variable('ParcelFlags').data
parcel_update['SalePrice'] = packet.message_data.blocks['ParcelData'][0].get_variable('SalePrice').data
parcel_update['Name'] = packet.message_data.blocks['ParcelData'][0].get_variable('Name').data
parcel_update['Desc'] = packet.message_data.blocks['ParcelData'][0].get_variable('Desc').data
parcel_update['MusicURL'] = packet.message_data.blocks['ParcelData'][0].get_variable('MusicURL').data
parcel_update['MediaURL'] = packet.message_data.blocks['ParcelData'][0].get_variable('MediaURL').data
parcel_update['MediaID'] = packet.message_data.blocks['ParcelData'][0].get_variable('MediaID').data
parcel_update['MediaAutoScale'] = packet.message_data.blocks['ParcelData'][0].get_variable('MediaAutoScale').data
parcel_update['GroupID'] = packet.message_data.blocks['ParcelData'][0].get_variable('GroupID').data
parcel_update['PassPrice'] = packet.message_data.blocks['ParcelData'][0].get_variable('PassPrice').data
parcel_update['PassHours'] = packet.message_data.blocks['ParcelData'][0].get_variable('PassHours').data
parcel_update['Category'] = packet.message_data.blocks['ParcelData'][0].get_variable('Category').data
parcel_update['AuthBuyerID'] = packet.message_data.blocks['ParcelData'][0].get_variable('AuthBuyerID').data
parcel_update['SnapshotID'] = packet.message_data.blocks['ParcelData'][0].get_variable('SnapshotID').data
parcel_update['UserLocation'] = packet.message_data.blocks['ParcelData'][0].get_variable('UserLocation').data
parcel_update['UserLookAt'] = packet.message_data.blocks['ParcelData'][0].get_variable('UserLookAt').data
parcel_update['LandingType'] = packet.message_data.blocks['ParcelData'][0].get_variable('LandingType').data
self._update_parcel_properties(parcel_update)
def _store_parcel_properties(self, parcel_info):
""" store a representation of a parcel """
# update the attributes of an existing parcel list member, else, append
index = [self.parcels.index(parcel) for parcel in self.parcels if parcel.LocalID == parcel_info['LocalID']]
if index != []:
self._update_parcel_properties(parcel_info)
if self.settings.LOG_VERBOSE: log(DEBUG, 'Updating a stored parcel: %s in region \'%s\'' % (parcel_info['LocalID'], self.region.SimName))
else:
new_parcel = Parcel(self.region, self.agent, RequestResult = parcel_info['RequestResult'], SequenceID = parcel_info['SequenceID'], SnapSelection = parcel_info['SnapSelection'], SelfCount = parcel_info['SelfCount'], OtherCount = parcel_info['OtherCount'], PublicCount = parcel_info['PublicCount'], LocalID = parcel_info['LocalID'], OwnerID = parcel_info['OwnerID'], IsGroupOwned = parcel_info['IsGroupOwned'], AuctionID = parcel_info['AuctionID'], ClaimDate = parcel_info['ClaimDate'], ClaimPrice = parcel_info['ClaimPrice'], RentPrice = parcel_info['RentPrice'], AABBMin = parcel_info['AABBMin'], AABBMax = parcel_info['AABBMax'], Bitmap = parcel_info['Bitmap'], Area = parcel_info['Area'], Status = parcel_info['Status'], SimWideMaxPrims = parcel_info['SimWideMaxPrims'], SimWideTotalPrims = parcel_info['SimWideTotalPrims'], MaxPrims = parcel_info['MaxPrims'], TotalPrims = parcel_info['TotalPrims'], OwnerPrims = parcel_info['OwnerPrims'], GroupPrims = parcel_info['GroupPrims'], OtherPrims = parcel_info['OtherPrims'], SelectedPrims = parcel_info['SelectedPrims'], ParcelPrimBonus = parcel_info['ParcelPrimBonus'], OtherCleanTime = parcel_info['OtherCleanTime'], ParcelFlags = parcel_info['ParcelFlags'], SalePrice = parcel_info['SalePrice'], Name = parcel_info['Name'], Desc = parcel_info['Desc'], MusicURL = parcel_info['MusicURL'], MediaURL = parcel_info['MediaURL'], MediaID = parcel_info['MediaID'], MediaAutoScale = parcel_info['MediaAutoScale'], GroupID = parcel_info['GroupID'], PassPrice = parcel_info['PassPrice'], PassHours = parcel_info['PassHours'], Category = parcel_info['Category'], AuthBuyerID = parcel_info['AuthBuyerID'], SnapshotID = parcel_info['SnapshotID'], UserLocation = parcel_info['UserLocation'], UserLookAt = parcel_info['UserLookAt'], LandingType = parcel_info['LandingType'], RegionPushOverride = parcel_info['RegionPushOverride'], RegionDenyAnonymous = parcel_info['RegionDenyAnonymous'], RegionDenyIdentified = parcel_info['RegionDenyIdentified'], RegionDenyTransacted = parcel_info['RegionDenyTransacted'], RegionDenyAgeUnverified = parcel_info['RegionDenyAgeUnverified'], settings = self.settings)
self.parcels.append(new_parcel)
self._update_parcel_map(new_parcel)
if self.settings.LOG_VERBOSE: log(DEBUG, 'Stored a new parcel: %s in region \'%s\'' % (new_parcel.LocalID, self.region.SimName))
def _update_parcel_properties(self, parcel_properties):
""" update a stored parcel's properties. finds the stored parcel and passes it a dictionary to process """
parcels_found = []
if parcel_properties.has_key('LocalID'):
LocalID = parcel_properties['LocalID']
parcels_found = [parcel for parcel in self.parcels if str(parcel.LocalID) == str(LocalID)]
if len(parcels_found) == 0:
log(INFO, "Received ParcelPropertiesUpdate for parcel we do not know about yet. Storing a partial representation.")
new_parcel = Parcel(self.region, self.agent, LocalID = parcel_properties['LocalID'], Flags = parcel_properties['Flags'], ParcelFlags = parcel_properties['ParcelFlags'], SalePrice = parcel_properties['SalePrice'], Name = parcel_properties['Name'], Desc = parcel_properties['Desc'], MusicURL = parcel_properties['MusicURL'], MediaURL = parcel_properties['MediaURL'], MediaID = parcel_properties['MediaID'], MediaAutoScale = parcel_properties['MediaAutoScale'], GroupID = parcel_properties['GroupID'], PassPrice = parcel_properties['PassPrice'], PassHours = parcel_properties['PassHours'], Category = parcel_properties['Category'], AuthBuyerID = parcel_properties['AuthBuyerID'], SnapshotID = parcel_properties['SnapshotID'], UserLocation = parcel_properties['UserLocation'], UserLookAt = parcel_properties['UserLookAt'], LandingType = parcel_properties['LandingType'], settings = self.settings)
self._store_parcel(new_parcel)
elif len(parcels_found) == 1:
parcel = parcels_found[0]
parcel._update_properties(parcel_properties)
elif parcel_properties.has_key('ParcelID'):
ParcelID = parcel_properties['ParcelID']
parcels_found = [parcel for parcel in self.parcels if str(parcel.ParcelID) == str(ParcelID)]
if len(parcels_found) == 0:
log(INFO, "Received ParcelPropertiesUpdate for parcel we do not know about yet. Storing a partial representation.")
new_parcel = Parcel(self.region, self.agent, LocalID = parcel_properties['LocalID'], Flags = parcel_properties['Flags'], ParcelFlags = parcel_properties['ParcelFlags'], SalePrice = parcel_properties['SalePrice'], Name = parcel_properties['Name'], Desc = parcel_properties['Desc'], MusicURL = parcel_properties['MusicURL'], MediaURL = parcel_properties['MediaURL'], MediaID = parcel_properties['MediaID'], MediaAutoScale = parcel_properties['MediaAutoScale'], GroupID = parcel_properties['GroupID'], PassPrice = parcel_properties['PassPrice'], PassHours = parcel_properties['PassHours'], Category = parcel_properties['Category'], AuthBuyerID = parcel_properties['AuthBuyerID'], SnapshotID = parcel_properties['SnapshotID'], UserLocation = parcel_properties['UserLocation'], UserLookAt = parcel_properties['UserLookAt'], LandingType = parcel_properties['LandingType'], settings = self.settings)
self._store_parcel(new_parcel)
elif len(parcels_found) == 1:
parcel = parcels_found[0]
parcel._update_properties(parcel_properties)
def _update_parcel_map(self, parcel):
"""Use the parcel's bitmap to update the manager's (x,y) to LocalID mapping"""
full = True
for x in range(64):
for y in range(64):
index = x + (64 * y)
byte = index >> 3
mask = 1 << (index % 8)
# *TODO: Bitmap should be stored as a byte array, not a string
if ord(parcel.Bitmap[byte]) & mask:
self.parcel_map[x][y] = parcel.LocalID
full = full and (self.parcel_map[x][y] != 0)
self.parcel_map_full = full
def get_parcel_by_id(self, local_id):
"""Returns a parcel if info has been received, None otherwise."""
for parcel in self.parcels:
if parcel.LocalID == local_id:
return parcel
return None
def get_parcel_id_by_location(self, local_x, local_y):
"""Returns a parcel's local id if info has been received, 0 otherwise."""
return self.parcel_map[ int(local_x)/4 ][ int(local_y)/4 ]
def get_parcel_by_location(self, local_x, local_y):
"""Returns a parcel if info has been received, None otherwise."""
return self.get_parcel_by_id( self.get_parcel_id_by_location(local_x, local_y) )
def get_current_parcel(self):
"""Returns the agent's current parcel if info has been received, None otherwise."""
return self.get_parcel_by_location( self.agent.Position.X, self.agent.Position.Y )
def request_estate_covenant(self, ):
""" request the estate covenant (for the current estate)"""
self.onEstateCovenantReply_received = self.packet_handler._register('EstateCovenantReply')
self.onEstateCovenantReply_received.subscribe(self.onEstateCovenantReply)
self.sendEstateCovenantRequest()
def sendEstateCovenantRequest(self):
""" send an EstateCovenantRequest packet """
packet = EstateCovenantRequestPacket()
packet.AgentData['AgentID'] = self.agent.agent_id
packet.AgentData['SessionID'] = self.agent.session_id
self.region.enqueue_message(packet)
def onEstateCovenantReply(self, packet):
""" parse and handle an EstateCovenantReply packet """
try:
self.onEstateCovenantReply_received.unsubscribe(self.onEstateCovenantReply)
except AttributeError:
pass
CovenantID = packet.message_data.blocks['Data'][0].get_variable('CovenantID').data
CovenantTimestamp = packet.message_data.blocks['Data'][0].get_variable('CovenantTimestamp').data
EstateName = packet.message_data.blocks['Data'][0].get_variable('EstateName').data
EstateOwnerID = packet.message_data.blocks['Data'][0].get_variable('EstateOwnerID').data
log(INFO, "Received EstateCovenantReply for estate name %s with a CovenantID of %s." % (EstateName, CovenantID))
# storing this data as a dict in the parcel manager until we have something better to do with it
self.estatecovenantreply = {'CovenantID': CovenantID, 'CovenantTimestamp': CovenantTimestamp, 'EstateName': EstateName, 'EstateOwnerID': EstateOwnerID}
def sendParcelPropertiesRequest(self, SequenceID, West, South, East, North, SnapSelection):
""" sends a ParcelPropertiesRequest packet """
packet = ParcelPropertiesRequestPacket()
packet.AgentData['AgentID'] = self.agent.agent_id
packet.AgentData['SessionID'] = self.agent.session_id
packet.ParcelData['SequenceID'] = SequenceID
packet.ParcelData['West'] = West
packet.ParcelData['South'] = South
packet.ParcelData['East'] = East
packet.ParcelData['North'] = North
packet.ParcelData['SnapSelection'] = SnapSelection
self.region.enqueue_message(packet)
def sendParcelPropertiesRequestByID(self, SequenceID, LocalID):
""" sends a ParcelPropertiesRequestByID packet """
packet = ParcelPropertiesRequestByID()
packet.AgentData['AgentID'] = self.agent.agent_id
packet.AgentData['SessionID'] = self.agent.session_id
packet.ParcelData['SequenceID'] = SequenceID
packet.ParcelData['LocalID'] = LocalID
self.region.enqueue_message(packet)
def request_parcel_info(self, parcel_id):
""" request information for a parcel by id """
if type(parcel_id) == str:
try:
parcel_id = UUID(parcel_id)
except ValueError:
log(WARNING, 'Parcel_id passed to request_parcel_info must but a valid UUID or string representation of a uuid. %s was passed in' % (parcel_id))
return
elif not isinstance(parcel_id, UUID):
log(WARNING, 'Parcel_id passed to request_parcel_info must but a valid UUID or string representation of a uuid. %s was passed in' % (parcel_id))
return
self.sendParcelInfoRequest(parcel_id)
def sendParcelInfoRequest(self, parcel_id):
""" send a ParcelInfoRequest packet for the specified parcel_id """
packet = ParcelInfoRequestPacket()
# build the agent data block
packet.AgentData['AgentID'] = self.agent.agent_id
packet.AgentData['SessionID'] = self.agent.session_id
packet.Data['ParcelID'] = parcel_id
self.region.enqueue_message(packet)
def onParcelInfoReply(self, packet):
""" parse and handle a ParcelInfoReply packet """
parcel_info = {}
parcel_info['ParcelID'] = packet.message_data.blocks['Data'][0].get_variable('ParcelID').data
parcel_info['OwnerID'] = packet.message_data.blocks['Data'][0].get_variable('OwnerID').data
parcel_info['Name'] = packet.message_data.blocks['Data'][0].get_variable('Name').data
parcel_info['Desc'] = packet.message_data.blocks['Data'][0].get_variable('Desc').data
parcel_info['ActualArea'] = packet.message_data.blocks['Data'][0].get_variable('ActualArea').data
parcel_info['BillableArea'] = packet.message_data.blocks['Data'][0].get_variable('BillableArea').data
parcel_info['Flags'] = packet.message_data.blocks['Data'][0].get_variable('Flags').data
parcel_info['GlobalX'] = packet.message_data.blocks['Data'][0].get_variable('GlobalX').data
parcel_info['GlobalY'] = packet.message_data.blocks['Data'][0].get_variable('GlobalY').data
parcel_info['GlobalZ'] = packet.message_data.blocks['Data'][0].get_variable('GlobalZ').data
parcel_info['SimName'] = packet.message_data.blocks['Data'][0].get_variable('SimName').data
parcel_info['SnapshotID'] = packet.message_data.blocks['Data'][0].get_variable('SnapshotID').data
parcel_info['Dwell'] = packet.message_data.blocks['Data'][0].get_variable('Dwell').data
parcel_info['SalePrice'] = packet.message_data.blocks['Data'][0].get_variable('SalePrice').data
parcel_info['AuctionID'] = packet.message_data.blocks['Data'][0].get_variable('AuctionID').data
self._update_parcel_properties(parcel_info)
def request_current_parcel_properties(self, refresh = False):
""" request the properties of the parcel the agent currently inhabits """
x = self.agent.Position.X
y = self.agent.Position.Y
if refresh or self.get_parcel_id_by_location(x, y) == 0:
self.sendParcelPropertiesRequest(-50000, x, y, x, y, False)
def request_all_parcel_properties(self, delay = 0.5, refresh = False):
""" request the properties of all of the parcels on the current region. The delay parameter is a sleep between the send of each packet request; if refresh, current data will be discarded before requesting. If refresh is not True, data will not be re-requested for region locations already queried. """
# spawn a coroutine so this is non blocking
api.spawn(self.__request_all_parcel_properties, delay, refresh)
def __request_all_parcel_properties(self, delay = 1, refresh = False):
""" request the properties of all of the parcels on the current region """
if refresh:
self.parcel_map = [[0 for _ in range(64)] for _ in range(64)]
self.parcel_map_full = False
# minimum parcel size is 4x4m (16sq)
# ugh this is a wretched way to request parcel info, but it is what it is
for y in range(64):
for x in range(64):
if self.parcel_map[x][y] == 0:
# Target: center of 4m by 4m parcel
tx = x * 4 + 2
ty = y * 4 + 2
self.sendParcelPropertiesRequest(-50000, tx, ty, tx, ty, False)
api.sleep(delay)
def return_parcel_objects(self, ):
""" return the specified objects for the specified parcel """
pass
'''
// ParcelReturnObjects
// viewer -> sim
// reliable
{
ParcelReturnObjects Low 199 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ LocalID S32 }
{ ReturnType U32 }
}
{
TaskIDs Variable
{ TaskID LLUUID }
}
{
OwnerIDs Variable
{ OwnerID LLUUID }
}
}
'''
def disable_objects(self, ):
""" set objects nonphysical and disable scripts for the specified parcel """
pass
'''
// Disable makes objects nonphysical and turns off their scripts.
// ParcelDisableObjects
// viewer -> sim
// reliable
{
ParcelDisableObjects Low 201 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ LocalID S32 }
{ ReturnType U32 }
}
{
TaskIDs Variable
{ TaskID LLUUID }
}
{
OwnerIDs Variable
{ OwnerID LLUUID }
}
}
'''
def sendParcelDisableObjects(self, ):
""" send a ParcelDisableObjects packet """
pass
'''
// Disable makes objects nonphysical and turns off their scripts.
// ParcelDisableObjects
// viewer -> sim
// reliable
{
ParcelDisableObjects Low 201 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ LocalID S32 }
{ ReturnType U32 }
}
{
TaskIDs Variable
{ TaskID LLUUID }
}
{
OwnerIDs Variable
{ OwnerID LLUUID }
}
}
'''
def join_parcels(self, ):
""" joins the specified parcels """
pass
'''
// ParcelJoin - Take all parcels which are owned by agent and inside
// rectangle, and make them 1 parcel if they all are leased.
// viewer -> sim
// reliable
{
ParcelJoin Low 210 NotTrusted Unencoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ West F32 }
{ South F32 }
{ East F32 }
{ North F32 }
}
}
'''
def sendParcelJoin(self, ):
""" send a ParcelJoin packet """
pass
'''
// ParcelJoin - Take all parcels which are owned by agent and inside
// rectangle, and make them 1 parcel if they all are leased.
// viewer -> sim
// reliable
{
ParcelJoin Low 210 NotTrusted Unencoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ West F32 }
{ South F32 }
{ East F32 }
{ North F32 }
}
}
'''
def divide_parcel(self, ):
""" divide the selection into a new parcel """
pass
'''
// ParcelDivide
// If the selection is a subsection of exactly one parcel,
// chop out that section and make a new parcel of it.
// viewer -> sim
// reliable
{
ParcelDivide Low 211 NotTrusted Unencoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ West F32 }
{ South F32 }
{ East F32 }
{ North F32 }
}
}
'''
def sendParcelDivide(self, ):
""" send a ParcelDivide packet """
pass
'''
// ParcelDivide
// If the selection is a subsection of exactly one parcel,
// chop out that section and make a new parcel of it.
// viewer -> sim
// reliable
{
ParcelDivide Low 211 NotTrusted Unencoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ West F32 }
{ South F32 }
{ East F32 }
{ North F32 }
}
}
'''
def request_parcel_access_list(self, LocalID, Flags):
""" request an access list for the specified parcel, while enabling a callback handler for the response """
self.onParcelAccessListReply_received = self.packet_handler._register('ParcelAccessListReply')
self.onParcelAccessListReply_received.subscribe(self.onParcelAccessListReply, LocalID = LocalID)
self.sendParcelAccessListRequest(LocalID, Flags)
def sendParcelAccessListRequest(self, LocalID, Flags, SequenceID = -5150):
""" send a ParcelAccessListRequest packet """
packet = ParcelAccessListRequestPacket()
# build the agent data block
packet.AgentData['AgentID'] = self.agent.agent_id
packet.AgentData['SessionID'] = self.agent.session_id
# build the Data block. the region will populate the ParcelID
packet.Data['SequenceID'] = SequenceID
packet.Data['Flags'] = Flags
packet.Data['LocalID'] = LocalID
self.region.enqueue_message(packet)
def onParcelAccessListReply(self, packet):
""" parse and handle a ParcelAccessListReply packet """
#self.onParcelAccessListReply_received.unsubscribe(self.onParcelAccessListReply, LocalID = LocalID)
raise NotImplemented("sendFetchInventoryDescendentsRequest")
'''
// sim -> viewer
// ParcelAccessListReply
{
ParcelAccessListReply Low 216 Trusted Zerocoded
{
Data Single
{ AgentID LLUUID }
{ SequenceID S32 }
{ Flags U32 }
{ LocalID S32 }
}
{
List Variable
{ ID LLUUID }
{ Time S32 } // time_t
{ Flags U32 }
}
}
'''
def request_parcel_dwell(self, LocalID):
""" request dwell for the specified parcel, while enabling a callback handler for the response """
self.onParcelDwellReply_received = self.packet_handler._register('ParcelDwellReply')
self.onParcelDwellReply_received.subscribe(self.onParcelDwellReply, LocalID = LocalID)
self.sendParcelDwellRequest(LocalID)
def sendParcelDwellRequest(self, LocalID):
""" send a ParcelDwellRequest packet """
packet = ParcelDwellRequestPacket()
# build the agent data block
packet.AgentData['AgentID'] = self.agent.agent_id
packet.AgentData['SessionID'] = self.agent.session_id
# build the Data block. the region will populate the ParcelID
packet.Data['LocalID'] = LocalID
packet.Data['ParcelID'] = UUID()
self.region.enqueue_message(packet, True)
def onParcelDwellReply(self, packet, LocalID = None):
""" parse and handle a ParcelDwellReply packet"""
AgentID = packet.message_data.blocks['AgentData'][0].get_variable('AgentID').data
# log receipt of a packet that was intended to be sent to another agent
# ToDo: should we raise an event in this case? yes.... later
if str(AgentID) != str(self.agent.agent_id):
log(WARNING, "%s received a packet for the wrong agent_id. Expected: %s Received: %s" % (self.agent.Name(), self.agent.agent_id, AgentID))
# get the body of the message
parcel_info = {}
parcel_info['LocalID'] = packet.message_data.blocks['Data'][0].get_variable('LocalID').data
parcel_info['ParcelID'] = packet.message_data.blocks['Data'][0].get_variable('ParcelID').data
parcel_info['Dwell'] = packet.message_data.blocks['Data'][0].get_variable('Dwell').data
if LocalID == parcel_info['LocalID']:
self.onParcelDwellReply_received.unsubscribe(self.onParcelDwellReply, LocalID = LocalID)
self._update_parcel_properties(parcel_info)
class Parcel(object):
""" a representation of a parcel """
def __init__(self, region, agent, RequestResult = None, SequenceID = None, SnapSelection = None, SelfCount = None, OtherCount = None, PublicCount = None, LocalID = None, OwnerID = None, IsGroupOwned = None, AuctionID = None, ClaimDate = None, ClaimPrice = None, RentPrice = None, AABBMin = None, AABBMax = None, Bitmap = None, Area = None, Status = None, SimWideMaxPrims = None, SimWideTotalPrims = None, MaxPrims = None, TotalPrims = None, OwnerPrims = None, GroupPrims = None, OtherPrims = None, SelectedPrims = None, ParcelPrimBonus = None, OtherCleanTime = None, ParcelFlags = None, SalePrice = None, Name = None, Desc = None, MusicURL = None, MediaURL = None, MediaID = None, MediaAutoScale = None, GroupID = None, PassPrice = None, PassHours = None, Category = None, AuthBuyerID = None, SnapshotID = None, UserLocation = None, UserLookAt = None, LandingType = None, RegionPushOverride = None, RegionDenyAnonymous = None, RegionDenyIdentified = None, RegionDenyTransacted = None, RegionDenyAgeUnverified = None, ParcelID = None, ActualArea = None, BillableArea = None, Flags = None, GlobalX = None, GlobalY = None, GlobalZ = None, SimName = None, Dwell = None, settings = None):
""" initialize a representation of a parcel. the data is currently being populated directly from the ParcelProperties message """
# 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()
self.region = region
self.agent = agent
# mapping values from the ParcelProperties message
# not all properties will ultimately live here, but for now
# mapping all of them will do
# from ParcelInfoReply
self.ParcelID = ParcelID
self.OwnerID = OwnerID
self.ActualArea = ActualArea
self.BillableArea = BillableArea
self.Flags = Flags
self.GlobalX = GlobalX
self.GlobalY = GlobalY
self.GlobalZ = GlobalZ
self.SimName = SimName
self.Dwell = Dwell
self.AuctionID = AuctionID
# from ParcelProperties
self.RequestResult = RequestResult
self.SequenceID = SequenceID
self.SnapSelection = SnapSelection
self.SelfCount = SelfCount
self.OtherCount = OtherCount
self.PublicCount = PublicCount
self.LocalID = LocalID
self.OwnerID = OwnerID
self.IsGroupOwned = IsGroupOwned
self.AuctionID = AuctionID
self.ClaimDate = ClaimDate
self.ClaimPrice = ClaimPrice
self.RentPrice = RentPrice
self.AABBMin = AABBMin
self.AABBMax = AABBMax
self.Bitmap = Bitmap
self.Area = Area
self.Status = Status
self.SimWideMaxPrims = SimWideMaxPrims
self.SimWideTotalPrims = SimWideTotalPrims
self.MaxPrims = MaxPrims
self.TotalPrims = TotalPrims
self.OwnerPrims = OwnerPrims
self.GroupPrims = GroupPrims
self.OtherPrims = OtherPrims
self.SelectedPrims = SelectedPrims
self.ParcelPrimBonus = ParcelPrimBonus
self.OtherCleanTime = OtherCleanTime
self.ParcelFlags = ParcelFlags
self.SalePrice = SalePrice
self.Name = Name
self.Desc = Desc
self.MusicURL = MusicURL
self.MediaURL = MediaURL
self.MediaID = MediaID
self.MediaAutoScale = MediaAutoScale
self.GroupID = GroupID
self.PassPrice = PassPrice
self.PassHours = PassHours
self.Category = Category
self.AuthBuyerID = AuthBuyerID
self.SnapshotID = SnapshotID
self.UserLocation = UserLocation
self.UserLookAt = UserLookAt
self.LandingType = LandingType
self.RegionPushOverride = RegionPushOverride
self.RegionDenyAnonymous = RegionDenyAnonymous
self.RegionDenyIdentified = RegionDenyIdentified
self.RegionDenyTransacted = RegionDenyTransacted
self.RegionDenyAgeUnverified = RegionDenyAgeUnverified
def _update_properties(self, parcel_properties):
""" update a parcel's properties via a dictionary """
for attribute in parcel_properties:
# if self.settings.LOG_VERBOSE: log(DEBUG, "Updating parcel data for %s. %s = %s" % (self, attribute, parcel_properties[attribute]))
setattr(self, attribute, parcel_properties[attribute])
def return_objects(self, ):
""" return the specified objects for this parcel """
pass
'''
// ParcelReturnObjects
// viewer -> sim
// reliable
{
ParcelReturnObjects Low 199 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ LocalID S32 }
{ ReturnType U32 }
}
{
TaskIDs Variable
{ TaskID LLUUID }
}
{
OwnerIDs Variable
{ OwnerID LLUUID }
}
}
'''
def set_other_clean_time(self, ):
""" sends a SetOtherCleanTime packet for this parcel """
pass
'''
// ParcelSetOtherCleanTime
// viewer -> sim
// reliable
{
ParcelSetOtherCleanTime Low 200 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ LocalID S32 }
{ OtherCleanTime S32 }
}
}
'''
def disable_objects(self, ):
""" set objects nonphysical and disable scripts for this parcel """
pass
'''
// Disable makes objects nonphysical and turns off their scripts.
// ParcelDisableObjects
// viewer -> sim
// reliable
{
ParcelDisableObjects Low 201 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ LocalID S32 }
{ ReturnType U32 }
}
{
TaskIDs Variable
{ TaskID LLUUID }
}
{
OwnerIDs Variable
{ OwnerID LLUUID }
}
}
'''
def select_objects(self, ):
""" selects the specified objects for this parcel """
pass
'''
// ParcelSelectObjects
// viewer -> sim
// reliable
{
ParcelSelectObjects Low 202 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
ParcelData Single
{ LocalID S32 }
{ ReturnType U32 }
}
{
ReturnIDs Variable
{ ReturnID LLUUID }
}
}
'''
def deed_to_group(self, ):
""" deed this parcel to a group """
pass
'''
// ParcelDeedToGroup - deed a patch of land to a group
// viewer -> sim
// reliable
{
ParcelDeedToGroup Low 207 NotTrusted Unencoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
Data Single
{ GroupID LLUUID }
{ LocalID S32 } // parcel id
}
}
'''
def reclaim(self, ):
""" reclaim this parcel"""
pass
'''
// reserved for when island owners force re-claim parcel
{
ParcelReclaim Low 208 NotTrusted Unencoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
Data Single
{ LocalID S32 } // parcel id
}
}
'''
def claim(self, ):
""" change the owner of a parcel """
pass
'''
// ParcelClaim - change the owner of a patch of land
// viewer -> sim
// reliable
{
ParcelClaim Low 209 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
Data Single
{ GroupID LLUUID }
{ IsGroupOwned BOOL }
{ Final BOOL } // true if buyer is in tier
}
{
ParcelData Variable
{ West F32 }
{ South F32 }
{ East F32 }
{ North F32 }
}
}
'''
def release(self, ):
""" release this parcel to the public """
pass
'''
// ParcelRelease
// Release a parcel to public
// viewer -> sim
// reliable
{
ParcelRelease Low 212 NotTrusted Unencoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
Data Single
{ LocalID S32 } // parcel ID
}
}
'''
def buy(self, ):
""" buy this parcel """
pass
'''
// ParcelBuy - change the owner of a patch of land.
// viewer -> sim
// reliable
{
ParcelBuy Low 213 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
Data Single
{ GroupID LLUUID }
{ IsGroupOwned BOOL }
{ RemoveContribution BOOL }
{ LocalID S32 }
{ Final BOOL } // true if buyer is in tier
}
{
ParcelData Single
{ Price S32 }
{ Area S32 }
}
}
'''
def godforce_owner(self, ):
""" god force own this parcel """
pass
'''
// ParcelGodForceOwner Unencoded
{
ParcelGodForceOwner Low 214 NotTrusted Zerocoded
{
AgentData Single
{ AgentID LLUUID }
{ SessionID LLUUID }
}
{
Data Single
{ OwnerID LLUUID }
{ LocalID S32 } // parcel ID
}
}
'''
"""
Contributors can be viewed at:
http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt
$LicenseInfo:firstyear=2008&license=apachev2$
Copyright 2009, 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$
"""