diff --git a/pyogp/lib/base/agent.py b/pyogp/lib/base/agent.py index 27fb62f..09b9a5d 100644 --- a/pyogp/lib/base/agent.py +++ b/pyogp/lib/base/agent.py @@ -232,7 +232,7 @@ class Agent(object): self.circuit_code = self.login_response['circuit_code'] # enable the current region, setting connect = True - self.region = Region(self.login_response['region_x'], self.login_response['region_y'], self.login_response['seed_capability'], self.login_response['udp_blacklist'], self.login_response['sim_ip'], self.login_response['sim_port'], self.login_response['circuit_code'], self, settings = self.settings, event_queue_handler = self.event_queue_handler) + self.region = Region(self.login_response['region_x'], self.login_response['region_y'], self.login_response['seed_capability'], self.login_response['udp_blacklist'], self.login_response['sim_ip'], self.login_response['sim_port'], self.login_response['circuit_code'], self, settings = self.settings, event_queue_handler = self.event_queue_handler, events_handler = self.events_handler) self.region.is_host_region = True @@ -277,7 +277,7 @@ class Agent(object): log(DEBUG, "Not enabling a region we are already connected to: %s" % (str(region_params['IP']) + ":" + str(region_params['Port']))) return - child_region = Region(circuit_code = self.circuit_code, sim_ip = region_params['IP'], sim_port = region_params['Port'], handle = region_params['Handle'], agent = self, settings = self.settings, event_queue_handler = self.event_queue_handler) + 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, event_queue_handler = self.event_queue_handler, events_handler = self.events_handler) self.child_regions.append(child_region) diff --git a/pyogp/lib/base/examples/sample_object_create_permissions.py b/pyogp/lib/base/examples/sample_object_create_permissions.py index e2fdcbc..df6fba0 100644 --- a/pyogp/lib/base/examples/sample_object_create_permissions.py +++ b/pyogp/lib/base/examples/sample_object_create_permissions.py @@ -54,7 +54,7 @@ def login(): # let's disable inventory handling for this example settings = Settings() - settings.ENABLE_INVENTORY_MANAGEMENT = True + settings.ENABLE_INVENTORY_MANAGEMENT = False settings.ENABLE_EQ_LOGGING = False settings.ENABLE_CAPS_LOGGING = False diff --git a/pyogp/lib/base/examples/sample_parcel_management.py b/pyogp/lib/base/examples/sample_parcel_management.py new file mode 100644 index 0000000..93cfd20 --- /dev/null +++ b/pyogp/lib/base/examples/sample_parcel_management.py @@ -0,0 +1,121 @@ +# standard +import re +import getpass, sys, logging +from optparse import OptionParser + +# related +from eventlet import api + +# pyogp +from pyogp.lib.base.agent import Agent +from pyogp.lib.base.settings import Settings +from pyogp.lib.base.utilities.helpers import Wait + + +def login(): + """ login an to a login endpoint """ + + parser = OptionParser() + + logger = logging.getLogger("pyogp.lib.base.example") + + parser.add_option("-l", "--loginuri", dest="loginuri", default="https://login.aditi.lindenlab.com/cgi-bin/login.cgi", + help="specified the target loginuri") + parser.add_option("-r", "--region", dest="region", default=None, + help="specifies the region (regionname/x/y/z) to connect to") + parser.add_option("-q", "--quiet", dest="verbose", default=True, action="store_false", + help="enable verbose mode") + + + (options, args) = parser.parse_args() + + if options.verbose: + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) # seems to be a no op, set it for the logger + formatter = logging.Formatter('%(asctime)-30s%(name)-30s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + # setting the level for the handler above seems to be a no-op + # it needs to be set for the logger, here the root logger + # otherwise it is NOTSET(=0) which means to log nothing. + logging.getLogger('').setLevel(logging.DEBUG) + else: + print "Attention: This script will print nothing if you use -q. So it might be boring to use it like that ;-)" + + # example from a pure agent perspective + + #grab a password! + password = getpass.getpass() + + # prep instance settings + settings = Settings() + + settings.ENABLE_INVENTORY_MANAGEMENT = False + settings.ENABLE_COMMUNICATIONS_TRACKING = False + settings.ENABLE_OBJECT_TRACKING = False + settings.ENABLE_UDP_LOGGING =True + settings.ENABLE_EQ_LOGGING = True + settings.ENABLE_CAPS_LOGGING = True + settings.MULTIPLE_SIM_CONNECTIONS = False + settings.ENABLE_PARCEL_TRACKING = True + + #First, initialize the agent + client = Agent(settings) + + # Now let's log it in + api.spawn(client.login, options.loginuri, args[0], args[1], password, start_location = options.region, connect_region = True) + + # wait for the agent to connect to it's region + while client.connected == False: + api.sleep(0) + + while client.region.connected == False: + api.sleep(0) + + # sample specific stuff + client.region.parcel_manager.request_current_parcel_properties() + + client.region.parcel_manager.request_all_parcel_properties() + + Wait(5) + + # run until killed + while client.running: + api.sleep(0) + + print '' + print 'Parcel data for %s parcels' % (len(client.region.parcel_manager.parcels)) + for parcel in client.region.parcel_manager.parcels: + print '' + for attr in parcel.__dict__: + print ' %s: %s' % (attr, parcel.__dict__[attr]) + + # inspect some data + print '' + print 'Parcel Overlay data:' + print client.region.parcel_manager.parcel_overlay + +def main(): + return login() + +if __name__=="__main__": + main() + +""" +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$ +""" + diff --git a/pyogp/lib/base/objects.py b/pyogp/lib/base/objects.py index 50615a3..f8a4b61 100644 --- a/pyogp/lib/base/objects.py +++ b/pyogp/lib/base/objects.py @@ -35,7 +35,7 @@ class Objects(object): Tests: tests/test_objects.py """ - def __init__(self, agent = None, region = None, settings = None, packet_handler = None): + def __init__(self, agent = None, region = None, settings = None, packet_handler = None, events_handler = None): """ set up the inventory manager """ # allow the settings to be passed in @@ -265,7 +265,6 @@ class Objects(object): #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Processing multiple object properties updates: %s" % (len(object_list))) - for object_properties in object_list: self.update_object_properties(object_properties) diff --git a/pyogp/lib/base/parcel.py b/pyogp/lib/base/parcel.py index 28cdf22..ba21372 100644 --- a/pyogp/lib/base/parcel.py +++ b/pyogp/lib/base/parcel.py @@ -1,9 +1,18 @@ # 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 +# pyogp messaging +from pyogp.lib.base.message.packets import * + +# utilities +from pyogp.lib.base.utilities.helpers import Helpers + # pyogp messaging # pyogp utilities @@ -12,7 +21,7 @@ from pyogp.lib.base.settings import Settings logger = getLogger('pyogp.lib.base.parcel') log = logger.log -# unhandled related messages +# ToDo: unhandled related messages # ForceObjectSelect # ParcelBuyPass # ParcelAccessListUpdate @@ -20,6 +29,8 @@ log = logger.log # ParcelDwellReply # ParcelGodMarkAsContent # ViewerStartAuction +# ParcelObjectOwnersRequest +# ParcelObjectOwnersReply # non client oriented related messages # #RequestParcelTransfer (sim->dataserver) @@ -39,7 +50,7 @@ log = logger.log class ParcelManager(object): """ a parcel manager, generally used as an attribute of a region """ - def __init__(self, region, agent, packet_handler, event_system, settings = None): + 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 @@ -54,163 +65,198 @@ class ParcelManager(object): self.region = region self.agent = agent self.packet_handler = packet_handler - self.event_system = event_system + + # 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 = {} # 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.packet_handler._register('ParcelProperties') + 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 """ + """ parse and handle an incoming ParcelOverlay packet - pass + Currently, we store this data in the ParcelManager.packet_overlay dictionary + as parcel_overlay[sequence_id] = data (unparsed binary) + """ - ''' - // ParcelOverlay - // We send N packets per region to the viewer. - // N = 4, currently. At 256x256 meter regions, 4x4 meter parcel grid, - // there are 4096 parcel units per region. At N = 4, that's 1024 units - // per packet, allowing 8 bit bytes. - // sim -> viewer - // reliable - { - ParcelOverlay Low 196 Trusted Zerocoded - { - ParcelData Single - { SequenceID S32 } // 0...3, which piece of region - { Data Variable 2 } // packed bit-field, (grids*grids)/N - } - } - ''' + # 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 handle an incoming ParcelProperties packet. Parse and serialize the info into a Parcel() representation, then store it (or replace the stored version) """ - pass + RequestResult = packet.message_data.blocks['ParcelData'][0].get_variable('RequestResult').data + SequenceID = packet.message_data.blocks['ParcelData'][0].get_variable('SequenceID').data + SnapSelection= packet.message_data.blocks['ParcelData'][0].get_variable('SnapSelection').data + SelfCount = packet.message_data.blocks['ParcelData'][0].get_variable('SelfCount').data + OtherCount = packet.message_data.blocks['ParcelData'][0].get_variable('OtherCount').data + PublicCount = packet.message_data.blocks['ParcelData'][0].get_variable('PublicCount').data + LocalID = packet.message_data.blocks['ParcelData'][0].get_variable('LocalID').data + OwnerID = packet.message_data.blocks['ParcelData'][0].get_variable('OwnerID').data + IsGroupOwned = packet.message_data.blocks['ParcelData'][0].get_variable('IsGroupOwned').data + AuctionID = packet.message_data.blocks['ParcelData'][0].get_variable('AuctionID').data + ClaimDate = packet.message_data.blocks['ParcelData'][0].get_variable('ClaimDate').data + ClaimPrice = packet.message_data.blocks['ParcelData'][0].get_variable('ClaimPrice').data + RentPrice = packet.message_data.blocks['ParcelData'][0].get_variable('RentPrice').data + AABBMin = packet.message_data.blocks['ParcelData'][0].get_variable('AABBMin').data + AABBMax = packet.message_data.blocks['ParcelData'][0].get_variable('AABBMax').data + Bitmap = packet.message_data.blocks['ParcelData'][0].get_variable('Bitmap').data + Area = packet.message_data.blocks['ParcelData'][0].get_variable('Area').data + Status = packet.message_data.blocks['ParcelData'][0].get_variable('Status').data + SimWideMaxPrims = packet.message_data.blocks['ParcelData'][0].get_variable('SimWideMaxPrims').data + SimWideTotalPrims = packet.message_data.blocks['ParcelData'][0].get_variable('SimWideTotalPrims').data + MaxPrims = packet.message_data.blocks['ParcelData'][0].get_variable('MaxPrims').data + TotalPrims = packet.message_data.blocks['ParcelData'][0].get_variable('TotalPrims').data + OwnerPrims = packet.message_data.blocks['ParcelData'][0].get_variable('OwnerPrims').data + GroupPrims = packet.message_data.blocks['ParcelData'][0].get_variable('GroupPrims').data + OtherPrims = packet.message_data.blocks['ParcelData'][0].get_variable('OtherPrims').data + SelectedPrims = packet.message_data.blocks['ParcelData'][0].get_variable('SelectedPrims').data + ParcelPrimBonus = packet.message_data.blocks['ParcelData'][0].get_variable('ParcelPrimBonus').data + OtherCleanTime = packet.message_data.blocks['ParcelData'][0].get_variable('OtherCleanTime').data + ParcelFlags = packet.message_data.blocks['ParcelData'][0].get_variable('ParcelFlags').data + SalePrice = packet.message_data.blocks['ParcelData'][0].get_variable('SalePrice').data + Name = packet.message_data.blocks['ParcelData'][0].get_variable('Name').data + Desc = packet.message_data.blocks['ParcelData'][0].get_variable('Desc').data + MusicURL = packet.message_data.blocks['ParcelData'][0].get_variable('MusicURL').data + MediaURL = packet.message_data.blocks['ParcelData'][0].get_variable('MediaURL').data + MediaID = packet.message_data.blocks['ParcelData'][0].get_variable('MediaID').data + MediaAutoScale = packet.message_data.blocks['ParcelData'][0].get_variable('MediaAutoScale').data + GroupID = packet.message_data.blocks['ParcelData'][0].get_variable('GroupID').data + PassPrice = packet.message_data.blocks['ParcelData'][0].get_variable('PassPrice').data + PassHours = packet.message_data.blocks['ParcelData'][0].get_variable('PassHours').data + Category = packet.message_data.blocks['ParcelData'][0].get_variable('Category').data + AuthBuyerID = packet.message_data.blocks['ParcelData'][0].get_variable('AuthBuyerID').data + SnapshotID = packet.message_data.blocks['ParcelData'][0].get_variable('SnapshotID').data + UserLocation = packet.message_data.blocks['ParcelData'][0].get_variable('UserLocation').data + UserLookAt = packet.message_data.blocks['ParcelData'][0].get_variable('UserLookAt').data + LandingType = packet.message_data.blocks['ParcelData'][0].get_variable('LandingType').data + RegionPushOverride = packet.message_data.blocks['ParcelData'][0].get_variable('RegionPushOverride').data + RegionDenyAnonymous = packet.message_data.blocks['ParcelData'][0].get_variable('RegionDenyAnonymous').data + RegionDenyIdentified = packet.message_data.blocks['ParcelData'][0].get_variable('RegionDenyIdentified').data + RegionDenyTransacted = packet.message_data.blocks['ParcelData'][0].get_variable('RegionDenyTransacted').data + RegionDenyAgeUnverified = packet.message_data.blocks['AgeVerificationBlock'][0].get_variable('RegionDenyAgeUnverified').data - ''' - // ParcelProperties - // sequence id = -1 for parcels that you explicitly selected - // For agents, sequence id increments every time the agent transits into - // a new parcel. It is used to detect out-of-order agent parcel info updates. - // Bitmap = packed bit field, one bit per parcel grid, on if that grid is - // part of the selected parcel. - // sim -> viewer - // WARNING: This packet is potentially large. With max length name, - // description, music URL and media URL, it is 1526 + sizeof ( LLUUID ) bytes. - { - ParcelProperties High 23 Trusted Zerocoded - { - ParcelData Single - { RequestResult S32 } - { SequenceID S32 } - { SnapSelection BOOL } - { SelfCount S32 } - { OtherCount S32 } - { PublicCount S32 } - { LocalID S32 } - { OwnerID LLUUID } - { IsGroupOwned BOOL } - { AuctionID U32 } - { ClaimDate S32 } // time_t - { ClaimPrice S32 } - { RentPrice S32 } - { AABBMin LLVector3 } - { AABBMax LLVector3 } - { Bitmap Variable 2 } // packed bit-field - { Area S32 } - { Status U8 } // owned vs. pending - { SimWideMaxPrims S32 } - { SimWideTotalPrims S32 } - { MaxPrims S32 } - { TotalPrims S32 } - { OwnerPrims S32 } - { GroupPrims S32 } - { OtherPrims S32 } - { SelectedPrims S32 } - { ParcelPrimBonus F32 } + parcel = Parcel(self.region, self.agent, RequestResult = RequestResult, SequenceID = SequenceID, SnapSelection = SnapSelection, SelfCount = SelfCount, OtherCount = OtherCount, PublicCount = PublicCount, LocalID = LocalID, OwnerID = OwnerID, IsGroupOwned = IsGroupOwned, AuctionID = AuctionID, ClaimDate = ClaimDate, ClaimPrice = ClaimPrice, RentPrice = RentPrice, AABBMin = AABBMin, AABBMax = AABBMax, Bitmap = Bitmap, Area = Area, Status = Status, SimWideMaxPrims = SimWideMaxPrims, SimWideTotalPrims = SimWideTotalPrims, MaxPrims = MaxPrims, TotalPrims = TotalPrims, OwnerPrims = OwnerPrims, GroupPrims = GroupPrims, OtherPrims = OtherPrims, SelectedPrims = SelectedPrims, ParcelPrimBonus = ParcelPrimBonus, OtherCleanTime = OtherCleanTime, ParcelFlags = ParcelFlags, SalePrice = SalePrice, Name = Name, Desc = Desc, MusicURL = MusicURL, MediaURL = MediaURL, MediaID = MediaID, MediaAutoScale = MediaAutoScale, GroupID = GroupID, PassPrice = PassPrice, PassHours = PassHours, Category = Category, AuthBuyerID = AuthBuyerID, SnapshotID = SnapshotID, UserLocation = UserLocation, UserLookAt = UserLookAt, LandingType = LandingType, RegionPushOverride = RegionPushOverride, RegionDenyAnonymous = RegionDenyAnonymous, RegionDenyIdentified = RegionDenyIdentified, RegionDenyTransacted = RegionDenyTransacted, RegionDenyAgeUnverified = RegionDenyAgeUnverified, settings = self.settings) - { OtherCleanTime S32 } - - { ParcelFlags U32 } - { SalePrice S32 } - { Name Variable 1 } // string - { Desc Variable 1 } // string - { MusicURL Variable 1 } // string - { MediaURL Variable 1 } // string - { MediaID LLUUID } - { MediaAutoScale U8 } - { GroupID LLUUID } - { PassPrice S32 } - { PassHours F32 } - { Category U8 } - { AuthBuyerID LLUUID } - { SnapshotID LLUUID } - { UserLocation LLVector3 } - { UserLookAt LLVector3 } - { LandingType U8 } - { RegionPushOverride BOOL } - { RegionDenyAnonymous BOOL } - { RegionDenyIdentified BOOL } - { RegionDenyTransacted BOOL } - } - { - AgeVerificationBlock Single - { RegionDenyAgeUnverified BOOL } - } - } - ''' + self._store_parcel(parcel) def onParcelPropertiesUpdate(self, packet): - """ parse and handle an incoming ParcelPropertiesUpdate 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 """ - pass + parcel_update = {} - ''' - // ParcelPropertiesUpdate - // viewer -> sim - // reliable - { - ParcelPropertiesUpdate Low 198 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { LocalID S32 } - { Flags U32 } + 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 - { ParcelFlags U32 } - { SalePrice S32 } - { Name Variable 1 } // string - { Desc Variable 1 } // string - { MusicURL Variable 1 } // string - { MediaURL Variable 1 } // string - { MediaID LLUUID } - { MediaAutoScale U8 } - { GroupID LLUUID } - { PassPrice S32 } - { PassHours F32 } - { Category U8 } - { AuthBuyerID LLUUID } - { SnapshotID LLUUID } - { UserLocation LLVector3 } - { UserLookAt LLVector3 } - { LandingType U8 } - } - } - ''' + self._update_parcel_properties(parcel_update) + + def _store_parcel(self, new_parcel): + """ store a representation of a parcel """ + + # replace an existing list member, else, append + + index = [self.parcels.index(parcel) for parcel in self.parcels if parcel.LocalID == new_parcel.LocalID] + + if index != []: + + self.parcels[index[0]] = new_parcel + + if self.settings.LOG_VERBOSE: log(DEBUG, 'Updating a stored parcel: %s in region \'%s\'' % (new_parcel.LocalID, self.region.SimName)) + + else: + + self.parcels.append(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 request_estate_covenant(self, ): """ request the estate covenant (for the current estate)""" @@ -218,118 +264,152 @@ class ParcelManager(object): self.onEstateCovenantReply_received = self.packet_handler._register('EstateCovenantReply') self.onEstateCovenantReply_received.subscribe(self.onEstateCovenantReply) - pass - - ''' - // EstateCovenantRequest - // viewer -> sim - // reliable - { - EstateCovenantRequest Low 203 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - } - ''' + self.sendEstateCovenantRequest() def sendEstateCovenantRequest(self): """ send an EstateCovenantRequest packet """ - pass + packet = EstateCovenantRequestPacket() - ''' - // EstateCovenantRequest - // viewer -> sim - // reliable - { - EstateCovenantRequest Low 203 NotTrusted Unencoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - } - ''' + 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 """ - self.self.onEstateCovenantReply_received.unsubscribe(self.onEstateCovenantReply) + try: - pass + self.onEstateCovenantReply_received.unsubscribe(self.onEstateCovenantReply) - ''' - // EstateCovenantReply - // sim -> viewer - // reliable - { - EstateCovenantReply Low 204 Trusted Unencoded - { - Data Single - { CovenantID LLUUID } - { CovenantTimestamp U32 } - { EstateName Variable 1 } // string - { EstateOwnerID LLUUID } - } - } - ''' + except AttributeError: - def sendParcelPropertiesRequest(self, ): + 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 """ - pass + packet = ParcelPropertiesRequestPacket() - ''' - // ParcelPropertiesRequest - // SequenceID should be -1 or -2, and is echoed back in the - // parcel properties message. - // viewer -> sim - // reliable - { - ParcelPropertiesRequest Medium 11 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { SequenceID S32 } - { West F32 } - { South F32 } - { East F32 } - { North F32 } - { SnapSelection BOOL } - } - } - ''' + packet.AgentData['AgentID'] = self.agent.agent_id + packet.AgentData['SessionID'] = self.agent.session_id - def sendParcelPropertiesRequestByID(self, ): + 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 """ - pass + packet = ParcelPropertiesRequestByID() - ''' - // ParcelPropertiesRequestByID - // viewer -> sim - // reliable - { - ParcelPropertiesRequestByID Low 197 NotTrusted Zerocoded - { - AgentData Single - { AgentID LLUUID } - { SessionID LLUUID } - } - { - ParcelData Single - { SequenceID S32 } - { LocalID S32 } - } - } - - ''' + 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 parcelid """ + + 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): + """ request the properties of the parcel the agent currently inhabits """ + + self.SouthWest = self.agent.Position.X + self.NorthEast = self.agent.Position.Y + + self.sendParcelPropertiesRequest(-50000, self.SouthWest, self.SouthWest, self.NorthEast, self.NorthEast, False) + + def request_all_parcel_properties(self, delay = 1): + """ 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 """ + + # spawn a coroutine so this is non blocking + api.spawn(self.__request_all_parcel_properties, delay) + + def __request_all_parcel_properties(self, delay = 1): + """ request the properties of all of the parcels on the current region """ + + # ugh this is a wretched way to request parcel info, but it is what it is + for x in range(1,17): + for y in range(1,17): + + self.sendParcelPropertiesRequest(-50000, x * 4, x * 4, y * 4, y * 4, False) + + api.sleep(delay) def return_parcel_objects(self, ): """ return the specified objects for the specified parcel """ @@ -620,8 +700,8 @@ class ParcelManager(object): class Parcel(object): """ a representation of a parcel """ - def __init__(self, region, agent, settings = None): - """ initialize the parcel manager """ + 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 @@ -634,69 +714,81 @@ class Parcel(object): self.region = region self.agent = agent - ''' - { - ParcelProperties High 23 Trusted Zerocoded - { - ParcelData Single - { RequestResult S32 } - { SequenceID S32 } - { SnapSelection BOOL } - { SelfCount S32 } - { OtherCount S32 } - { PublicCount S32 } - { LocalID S32 } - { OwnerID LLUUID } - { IsGroupOwned BOOL } - { AuctionID U32 } - { ClaimDate S32 } // time_t - { ClaimPrice S32 } - { RentPrice S32 } - { AABBMin LLVector3 } - { AABBMax LLVector3 } - { Bitmap Variable 2 } // packed bit-field - { Area S32 } - { Status U8 } // owned vs. pending - { SimWideMaxPrims S32 } - { SimWideTotalPrims S32 } - { MaxPrims S32 } - { TotalPrims S32 } - { OwnerPrims S32 } - { GroupPrims S32 } - { OtherPrims S32 } - { SelectedPrims S32 } - { ParcelPrimBonus F32 } + # mapping values from the ParcelProperties message + # not all properties will ultimately live here, but for now + # mapping all of them will do - { OtherCleanTime S32 } + # 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 - { ParcelFlags U32 } - { SalePrice S32 } - { Name Variable 1 } // string - { Desc Variable 1 } // string - { MusicURL Variable 1 } // string - { MediaURL Variable 1 } // string - { MediaID LLUUID } - { MediaAutoScale U8 } - { GroupID LLUUID } - { PassPrice S32 } - { PassHours F32 } - { Category U8 } - { AuthBuyerID LLUUID } - { SnapshotID LLUUID } - { UserLocation LLVector3 } - { UserLookAt LLVector3 } - { LandingType U8 } - { RegionPushOverride BOOL } - { RegionDenyAnonymous BOOL } - { RegionDenyIdentified BOOL } - { RegionDenyTransacted BOOL } - } - { - AgeVerificationBlock Single - { RegionDenyAgeUnverified BOOL } - } - } - ''' + # 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: + + setattr(self, attribute, parcel_properties[attribute]) def return_objects(self, ): """ return the specified objects for this parcel """ diff --git a/pyogp/lib/base/region.py b/pyogp/lib/base/region.py index fbfee7f..1b707ae 100644 --- a/pyogp/lib/base/region.py +++ b/pyogp/lib/base/region.py @@ -20,6 +20,8 @@ from pyogp.lib.base.utilities.helpers import Helpers from pyogp.lib.base.event_queue import EventQueueClient, EventQueueHandler from pyogp.lib.base.objects import Objects from pyogp.lib.base.datatypes import * +from pyogp.lib.base.event_system import EventsHandler +from pyogp.lib.base.parcel import ParcelManager # messaging from pyogp.lib.base.message.udpdispatcher import UDPDispatcher @@ -55,7 +57,7 @@ class Region(object): """ - def __init__(self, global_x = 0, global_y = 0, seed_capability_url = None, udp_blacklist = None, sim_ip = None, sim_port = None, circuit_code = None, agent = None, settings = None, packet_handler = None, event_queue_handler = None, handle = None): + def __init__(self, global_x = 0, global_y = 0, seed_capability_url = None, udp_blacklist = None, sim_ip = None, sim_port = None, circuit_code = None, agent = None, settings = None, packet_handler = None, event_queue_handler = None, handle = None, events_handler = None): """ initialize a region """ # allow the settings to be passed in @@ -81,6 +83,16 @@ class Region(object): elif self.settings.HANDLE_EVENT_QUEUE_DATA: self.event_queue_handler = EventQueueHandler(settings = self.settings) + # allow the eventhandler to be passed in + # so that applications running multiple avatars + # may use the same eventhandler + + # otherwise, let's just use our own + if events_handler != None: + self.events_handler = events_handler + else: + self.events_handler = EventsHandler() + # initialize the init params self.global_x = int(global_x) self.global_y = int(global_y) @@ -118,10 +130,13 @@ class Region(object): self.packet_queue = [] if self.settings.ENABLE_OBJECT_TRACKING == True: - self.objects = Objects(agent = self.agent, region = self, settings = self.settings, packet_handler = self.packet_handler) + self.objects = Objects(agent = self.agent, region = self, settings = self.settings, packet_handler = self.packet_handler, events_handler = self.events_handler) else: self.objects = None + if self.settings.ENABLE_PARCEL_TRACKING: + self.parcel_manager = ParcelManager(region = self, agent = self.agent, packet_handler = self.packet_handler, events_handler = self.events_handler, settings = None) + # required packet handlers onPacketAck_received = self.packet_handler._register('PacketAck') onPacketAck_received.subscribe(self.helpers.null_packet_handler, self) @@ -500,29 +515,29 @@ class Region(object): self.sendRegionHandshakeReply() # propagate the incoming data - self.SimName = packet.message_data.blocks['RegionInfo'][0].get_variable('SimName') - self.SimAccess = packet.message_data.blocks['RegionInfo'][0].get_variable('SimAccess') - self.SimOwner = packet.message_data.blocks['RegionInfo'][0].get_variable('SimOwner') - self.IsEstateManager = packet.message_data.blocks['RegionInfo'][0].get_variable('IsEstateManager') - self.WaterHeight = packet.message_data.blocks['RegionInfo'][0].get_variable('WaterHeight') - self.BillableFactor = packet.message_data.blocks['RegionInfo'][0].get_variable('BillableFactor') - self.TerrainBase0 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainBase0') - self.TerrainBase1 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainBase1') - self.TerrainBase2 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainBase2') - self.TerrainStartHeight00 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight00') - self.TerrainStartHeight01 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight01') - self.TerrainStartHeight10 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight10') - self.TerrainStartHeight11 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight11') - self.TerrainHeightRange00 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange00') - self.TerrainHeightRange01 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange01') - self.TerrainHeightRange10 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange10') - self.TerrainHeightRange11 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange11') - self.CPUClassID = packet.message_data.blocks['RegionInfo3'][0].get_variable('CPUClassID') - self.CPURatio = packet.message_data.blocks['RegionInfo3'][0].get_variable('CPURatio') - self.ColoName = packet.message_data.blocks['RegionInfo3'][0].get_variable('ColoName') - self.ProductSKU = packet.message_data.blocks['RegionInfo3'][0].get_variable('ProductSKU') - self.ProductName = packet.message_data.blocks['RegionInfo3'][0].get_variable('ProductName') - self.RegionID = packet.message_data.blocks['RegionInfo2'][0].get_variable('RegionID') + self.SimName = packet.message_data.blocks['RegionInfo'][0].get_variable('SimName').data + self.SimAccess = packet.message_data.blocks['RegionInfo'][0].get_variable('SimAccess').data + self.SimOwner = packet.message_data.blocks['RegionInfo'][0].get_variable('SimOwner').data + self.IsEstateManager = packet.message_data.blocks['RegionInfo'][0].get_variable('IsEstateManager').data + self.WaterHeight = packet.message_data.blocks['RegionInfo'][0].get_variable('WaterHeight').data + self.BillableFactor = packet.message_data.blocks['RegionInfo'][0].get_variable('BillableFactor').data + self.TerrainBase0 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainBase0').data + self.TerrainBase1 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainBase1').data + self.TerrainBase2 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainBase2').data + self.TerrainStartHeight00 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight00').data + self.TerrainStartHeight01 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight01').data + self.TerrainStartHeight10 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight10').data + self.TerrainStartHeight11 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainStartHeight11').data + self.TerrainHeightRange00 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange00').data + self.TerrainHeightRange01 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange01').data + self.TerrainHeightRange10 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange10').data + self.TerrainHeightRange11 = packet.message_data.blocks['RegionInfo'][0].get_variable('TerrainHeightRange11').data + self.CPUClassID = packet.message_data.blocks['RegionInfo3'][0].get_variable('CPUClassID').data + self.CPURatio = packet.message_data.blocks['RegionInfo3'][0].get_variable('CPURatio').data + self.ColoName = packet.message_data.blocks['RegionInfo3'][0].get_variable('ColoName').data + self.ProductSKU = packet.message_data.blocks['RegionInfo3'][0].get_variable('ProductSKU').data + self.ProductName = packet.message_data.blocks['RegionInfo3'][0].get_variable('ProductName').data + self.RegionID = packet.message_data.blocks['RegionInfo2'][0].get_variable('RegionID').data # we are connected self.connected = True diff --git a/pyogp/lib/base/settings.py b/pyogp/lib/base/settings.py index 9a1e89b..d0abfef 100644 --- a/pyogp/lib/base/settings.py +++ b/pyogp/lib/base/settings.py @@ -85,6 +85,9 @@ class Settings(object): # allow connecting to multiple simulators self.MULTIPLE_SIM_CONNECTIONS = False + # enabled the tracking of region parcels + self.ENABLE_PARCEL_TRACKING = True + #~~~~~~~~~~~~~~~~~~~~~~ # Agent Domain specific #~~~~~~~~~~~~~~~~~~~~~~ diff --git a/pyogp/lib/base/utilities/enums.py b/pyogp/lib/base/utilities/enums.py index 0c2cd35..8121cc2 100644 --- a/pyogp/lib/base/utilities/enums.py +++ b/pyogp/lib/base/utilities/enums.py @@ -99,6 +99,42 @@ class ExtraParam(object): Light = 0x20 Sculpt = 0x30 +class ParcelFlags(object): + """ Parcel Flag constants """ + + AllowFly = 1 << 0 # Can start flying + AllowOtherScripts = 1 << 1 # Scripts by others can run. + ForSale = 1 << 2 # Can buy this land + ForSaleObjects = 1 << 7 # Can buy all objects on this land + AllowLandmarks = 1 << 3 + AllowTerraform = 1 << 4 + AllowDamage = 1 << 5 + CreateObjects = 1 << 6 + UseAccessGroup = 1 << 8 + UseAccessList = 1 << 9 + UseBanList = 1 << 10 + UsePassList = 1 << 11 + ShowDirectory = 1 << 12 + AllowDeedToGroup = 1 << 13 + ContributeWithDeed = 1 << 14 + SoundLocal = 1 << 15 # Hear sounds in this parcel only + SellParcelObjects = 1 << 16 # Objects on land are included as part of the land when the land is sold + AllowPublish = 1 << 17 # Allow publishing of parcel information on the web + MaturePublish = 1 << 18 # The information on this parcel is mature + URLWebPage = 1 << 19 # The "media URL" is an HTML page + URLRawHTML = 1 << 20 # The "media URL" is a raw HTML string like