root/projects/2008/pyogp/pyogp.lib.base/trunk/pyogp/lib/base/objects.py

Revision 2493, 55.4 kB (checked in by joshua.linden, 5 months ago)

* Use the PCode enum instead of magic numbers
* Watch out for "None" values when applying ObjectUpdates? to agent
* Add AgentDynamicsUpdate? app event
* Handle 48-, 32-, and 16-byte ObjectUpdates?
* Add basic sit/stand/fly methods to agent, with associated enums

Not reviewed

Line 
1 # standard python libs
2 from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG
3 import uuid
4 import re
5 from binascii import hexlify
6 import struct
7 import math
8
9 # related
10
11 # pyogp
12 from pyogp.lib.base import *
13 from pyogp.lib.base.datamanager import DataManager
14 from pyogp.lib.base.permissions import *
15
16 # pyogp message
17 from pyogp.lib.base.message.message_handler import MessageHandler
18 from pyogp.lib.base.message.packets import *
19 from pyogp.lib.base.datatypes import *
20
21 # pyogp utilities
22 from pyogp.lib.base.utilities.helpers import Helpers
23 from pyogp.lib.base.utilities.enums import *
24
25 # initialize logging
26 logger = getLogger('pyogp.lib.base.objects')
27 log = logger.log
28
29 class ObjectManager(DataManager):
30     """ is an Object Manager
31
32     Initialize the event queue client class
33     >>> objects = ObjectManager()
34
35     Sample implementations: region.py
36     Tests: tests/test_objects.py
37     """
38
39     def __init__(self, agent = None, region = None, settings = None, message_handler = None, events_handler = None):
40         """ set up the object manager """
41         super(ObjectManager, self).__init__(settings, agent)
42         self.region = region
43
44         # the object store consists of a list
45         # of Object() instances
46         self.object_store = []
47
48         # the avatar store consists of a list
49         # of Avatar() instances
50         self.avatar_store = []
51
52         # other useful things
53         self.helpers = Helpers()
54        
55         self.message_handler = message_handler
56        
57         if self.settings.LOG_VERBOSE: log(INFO, "Initializing object storage")
58
59     def enable_callbacks(self):
60         """enables the callback handlers for this ObjectManager"""
61         if self.settings.HANDLE_PACKETS:
62             # supply a MessageHandler if not given one
63             if self.message_handler == None:
64                 self.message_handler = MessageHandler()
65
66             onObjectUpdate_received = self.message_handler.register('ObjectUpdate')
67             onObjectUpdate_received.subscribe(self.onObjectUpdate)
68
69             onObjectUpdateCached_received = self.message_handler.register('ObjectUpdateCached')
70             onObjectUpdateCached_received.subscribe(self.onObjectUpdateCached)
71
72             onObjectUpdateCompressed_received= self.message_handler.register('ObjectUpdateCompressed')
73             onObjectUpdateCompressed_received.subscribe(self.onObjectUpdateCompressed)
74
75             onObjectProperties_received = self.message_handler.register('ObjectProperties')
76             onObjectProperties_received.subscribe(self.onObjectProperties)
77
78             onKillObject_received= self.message_handler.register('KillObject')
79             onKillObject_received.subscribe(self.onKillObject)
80
81             # uncomment these to view packets sent back to simulator
82             # onObjectName_sent = self.message_handler.register('ObjectName')
83             # onObjectName_sent.subscribe(self.helpers.log_packet, self)
84
85             # onDeRezObject_sent = self.message_handler.register('DeRezObject')
86             # onDeRezObject_sent.subscribe(self.helpers.log_packet, self)
87
88
89     def process_multiple_object_updates(self, objects):
90         """ process a list of object updates """
91
92         [self.process_object_update(_object) for _object in objects]
93
94     def process_object_update(self, _object):
95         """ append to or replace an object in self.objects """
96
97         # this is an avatar
98         if _object.PCode == PCode.Avatar:
99
100             self.store_avatar(_object)
101
102         # this is a Primitive
103         elif _object.PCode == PCode.Primitive:
104
105             self.store_object(_object)
106
107         else:
108
109             if self.settings.LOG_VERBOSE: log(DEBUG, "Not processing object update of type %s" % (PCode(PCode)))
110
111     def store_object(self, _object):
112
113         # replace an existing list member, else, append
114
115         index = [self.object_store.index(_object_) for _object_ in self.object_store if _object_.LocalID == _object.LocalID]
116
117         if index != []:
118
119             self.object_store[index[0]] = _object
120
121             #if self.settings.LOG_VERBOSE: log(DEBUG, 'Updating a stored object: %s in region \'%s\'' % (_object.FullID, self.region.SimName))
122
123         else:
124
125             self.object_store.append(_object)
126
127             #if self.settings.LOG_VERBOSE: log(DEBUG, 'Stored a new object: %s in region \'%s\'' % (_object.LocalID, self.region.SimName))
128
129     def store_avatar(self, _objectdata):
130         # if the object data pertains to us, update our data!
131         if str(_objectdata.FullID) == str(self.agent.agent_id):
132
133             if _objectdata.Position:
134                 self.agent.Position = _objectdata.Position
135             if _objectdata.FootCollisionPlane:
136                 self.agent.FootCollisionPlane = _objectdata.FootCollisionPlane
137             if _objectdata.Velocity:
138                 self.agent.Velocity = _objectdata.Velocity
139             if _objectdata.Acceleration:
140                 self.agent.Acceleration = _objectdata.Acceleration
141             if _objectdata.Rotation:
142                 self.agent.Rotation = _objectdata.Rotation
143             if _objectdata.AngularVelocity:
144                 self.agent.AngularVelocity = _objectdata.AngularVelocity
145
146             if self.settings.ENABLE_APPEARANCE_MANAGEMENT:
147                 self.agent.appearance.TextureEntry = _objectdata.TextureEntry
148                 self.agent.appearance.send_AgentSetAppearance()
149                
150             self.agent.sendDynamicsUpdate()
151            
152         index = [self.avatar_store.index(_avatar_) for _avatar_ in self.avatar_store if _avatar_.LocalID == _objectdata.LocalID]
153
154         if index != []:
155
156             self.avatar_store[index[0]] = _objectdata
157
158             #if self.settings.LOG_VERBOSE: log(DEBUG, 'Replacing a stored avatar: %s in region \'%s\'' % (_objectdata.LocalID, self.region.SimName))
159
160         else:
161
162             self.avatar_store.append(_objectdata)
163
164             #if self.settings.LOG_VERBOSE: log(DEBUG, 'Stored a new avatar: %s in region \'%s\'' % (_objectdata.LocalID, self.region.SimName))
165
166     def get_object_from_store(self, LocalID = None, FullID = None):
167         """ searches the store and returns object if stored, None otherwise """
168
169         _object = []
170
171         if LocalID != None:
172             _object = [_object for _object in self.object_store if _object.LocalID == LocalID]
173         elif FullID != None:
174             _object = [_object for _object in self.object_store if str(_object.FullID) == str(FullID)]
175
176         if _object == []:
177             return None
178         else:
179             return _object[0]
180
181     def get_avatar_from_store(self, LocalID = None, FullID = None):
182         """ searches the store and returns object if stored, None otherwise """
183
184         if LocalID != None:
185             _avatar = [_avatar for _avatar in self.avatar_store if _avatar.LocalID == LocalID]
186         elif FullID != None:
187             _avatar = [_avatar for _avatar in self.avatar_store if _avatar.FullID == FullID]
188
189         if _avatar == []:
190             return None
191         else:
192             return _avatar[0]
193
194     def my_objects(self):
195         """ returns a list of known objects where the calling client is the owner """
196
197         matches = [_object for _object in self.object_store if str(_object.OwnerID) == str(self.agent.agent_id)]
198
199         return matches
200
201     def find_objects_by_name(self, Name):
202         """ searches the store for known objects by name
203
204         returns a list of all such known objects
205         """
206
207         pattern = re.compile(Name)
208
209         matches = [_object for _object in self.object_store if pattern.match(_object.Name)]
210
211         return matches
212
213     def find_objects_within_radius(self, radius):
214         """ returns objects nearby. returns a list of objects """
215
216         if type(radius) != float:
217             radius = float(radius)
218
219         objects_nearby = []
220
221         for item in self.object_store:
222
223             if item.Position == None: continue
224
225             if math.sqrt(math.pow((item.Position.X - self.agent.Position.X),2) +  math.pow((item.Position.Y - self.agent.Position.Y),2) + math.pow((item.Position.Z - self.agent.Position.Z),2)) <= radius:
226                 objects_nearby.append(item)
227
228         return objects_nearby
229
230     def remove_object_from_store(self, ID = None):
231
232         victim = self.get_object_from_store(LocalID = ID)
233         if victim == None:
234             victim = self.get_avatar_from_store(LocalID = ID)
235
236         # if we do not know about this object, pass
237         if victim == None or victim == []:
238             return
239
240         # this is an avatar
241         if victim.PCode == PCode.Avatar:
242
243             self.kill_stored_avatar(ID)
244
245         # this is a Primitive
246         elif victim.PCode == PCode.Primitive:
247
248             self.kill_stored_object(ID)
249
250         else:
251
252             if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Not processing kill of unstored object type %s" % (PCode(PCode)))
253
254     def kill_stored_avatar(self, ID):
255
256         index = [self.avatar_store.index(_avatar_) for _avatar_ in self.avatar_store if _avatar_.LocalID == ID]
257
258         if index != []:
259             del self.avatar_store[index[0]]
260             if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Kill on object data for avatar tracked as local id %s" % (ID))
261
262     def kill_stored_object(self, ID):
263
264         index = [self.object_store.index(_object_) for _object_ in self.object_store if _object_.LocalID == ID]
265
266         if index != []:
267             del self.object_store[index[0]]
268             if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Kill on object data for object tracked as local id %s" % (ID))
269
270     def update_multiple_objects_properties(self, object_list):
271         """ update the attributes of objects """
272
273         #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Processing multiple object properties updates: %s" % (len(object_list)))
274
275         for object_properties in object_list:
276
277             self.update_object_properties(object_properties)
278
279     def update_object_properties(self, object_properties):
280         """ update the attributes of an object
281        
282         If the object is known, we update the properties.
283         If not, we create a new object
284         """
285
286         #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Processing object properties update for FullID: %s" % (object_properties['FullID']))
287
288         if object_properties.has_key('PCode'):
289             # this is an avatar
290             if object_properties['PCode'] == PCode.Avatar:
291
292                 #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Creating a new avatar and storing their attributes. LocalID = %s" % (object_properties['LocalID']))
293
294                 _object = Object()
295                 _object._update_properties(object_properties)
296
297                 self.store_avatar(_object)
298
299             else:
300
301                 self.update_prim_properties(object_properties)
302
303         else:
304
305                 self.update_prim_properties(object_properties)
306
307     def update_prim_properties(self, prim_properties):
308
309             _object = self.get_object_from_store(FullID = prim_properties['FullID'])
310
311             if _object == None:
312                 #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Creating a new object and storing it's attributes. LocalID = %s" % (object_properties['LocalID']))
313                 _object = Object()
314                 _object._update_properties(prim_properties)
315                 self.store_object(_object)
316             else:
317                 #if self.settings.LOG_VERBOSE and self.settings.ENABLE_OBJECT_LOGGING: log(DEBUG, "Updating an object's attributes. LocalID = %s" % (object_properties['LocalID']))
318                 _object._update_properties(prim_properties)
319
320     def request_object_update(self, ID = None, ID_list = None):
321         """ requests object updates from the simulator
322
323         accepts a tuple of (ID, CacheMissType), or a list of such tuples
324         """
325
326         packet = RequestMultipleObjectsPacket()
327         packet.AgentData['AgentID'] = self.agent.agent_id
328         packet.AgentData['SessionID'] = self.agent.session_id
329
330         if ID != None:
331
332             ObjectData = {}
333             ObjectData['CacheMissType'] = ID[1]
334             ObjectData['ID'] = ID[0]
335
336             packet.ObjectDataBlocks.append(ObjectData)
337
338         else:
339
340             for ID in ID_list:
341
342                 ObjectData = {}
343                 ObjectData['CacheMissType'] = ID[1]
344                 ObjectData['ID'] = ID[0]
345
346                 packet.ObjectDataBlocks.append(ObjectData)
347
348         # enqueue the message, send as reliable
349         self.region.enqueue_message(packet(), True)
350
351     def create_default_box(self, GroupID = UUID(), relative_position = (1, 0, 0)):
352         """ creates the default box, defaulting as 1m to the east, with an option GroupID to set the prim to"""
353
354         # self.agent.Position holds where we are. we need to add this tuple to the incoming tuple (vector to a vector)
355         location_to_rez_x = self.agent.Position.X + relative_position[0]
356         location_to_rez_y = self.agent.Position.Y + relative_position[1]
357         location_to_rez_z = self.agent.Position.Z + relative_position[2]
358
359         location_to_rez = (location_to_rez_x, location_to_rez_y, location_to_rez_z)
360
361         # not sure what RayTargetID is, send as uuid of zeros
362         RayTargetID = UUID()
363
364         self.object_add(GroupID = GroupID, PCode = PCode.Primitive, Material = 3, AddFlags = 2, PathCurve = 16, ProfileCurve = 1, PathBegin = 0, PathEnd = 0, PathScaleX = 100, PathScaleY = 100, PathShearX = 0, PathShearY = 0, PathTwist = 0, PathTwistBegin = 0, PathRadiusOffset = 0, PathTaperX = 0, PathTaperY = 0, PathRevolutions = 0, PathSkew = 0, ProfileBegin = 0, ProfileEnd = 0, ProfileHollow = 0, BypassRaycast = 1, RayStart = location_to_rez, RayEnd = location_to_rez, RayTargetID = RayTargetID, RayEndIsIntersection = 0, Scale = (0.5, 0.5, 0.5), Rotation = (0, 0, 0, 1), State = 0)
365
366     def object_add(self, PCode, Material, AddFlags, PathCurve, ProfileCurve, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathTwist, PathTwistBegin, PathRadiusOffset, PathTaperX, PathTaperY, PathRevolutions, PathSkew, ProfileBegin, ProfileEnd, ProfileHollow, BypassRaycast, RayStart, RayEnd, RayTargetID, RayEndIsIntersection, Scale, Rotation, State, GroupID = UUID()):
367         '''
368         ObjectAdd - create new object in the world
369         Simulator will assign ID and send message back to signal
370         object actually created.
371
372         AddFlags (see also ObjectUpdate)
373         0x01 - use physics
374         0x02 - create selected
375
376         GroupID defaults to (No group active)
377         '''
378
379         packet = ObjectAddPacket()
380
381         # build the AgentData block
382         packet.AgentData['AgentID'] = self.agent.agent_id
383         packet.AgentData['SessionID'] = self.agent.session_id
384         packet.AgentData['GroupID'] = GroupID
385
386         # build the ObjectData block (it's a Single)
387         packet.ObjectData['PCode'] = PCode
388         packet.ObjectData['Material'] = Material
389         packet.ObjectData['AddFlags'] = AddFlags
390         packet.ObjectData['PathCurve'] = PathCurve
391         packet.ObjectData['ProfileCurve'] = ProfileCurve
392         packet.ObjectData['PathBegin'] = PathBegin
393         packet.ObjectData['PathEnd'] = PathEnd
394         packet.ObjectData['PathScaleX'] = PathScaleX
395         packet.ObjectData['PathScaleY'] = PathScaleY
396         packet.ObjectData['PathShearX'] = PathShearX
397         packet.ObjectData['PathShearY'] = PathShearY
398         packet.ObjectData['PathTwist'] = PathTwist
399         packet.ObjectData['PathTwistBegin'] = PathTwistBegin
400         packet.ObjectData['PathRadiusOffset'] = PathRadiusOffset
401         packet.ObjectData['PathTaperX'] = PathTaperX
402         packet.ObjectData['PathTaperY'] = PathTaperY
403         packet.ObjectData['PathRevolutions'] = PathRevolutions
404         packet.ObjectData['PathSkew'] = PathSkew
405         packet.ObjectData['ProfileBegin'] = ProfileBegin
406         packet.ObjectData['ProfileEnd'] = ProfileEnd
407         packet.ObjectData['ProfileHollow'] = ProfileHollow
408         packet.ObjectData['BypassRaycast'] = BypassRaycast
409         packet.ObjectData['RayStart'] = RayStart
410         packet.ObjectData['RayEnd'] = RayEnd
411         packet.ObjectData['RayTargetID'] = RayTargetID
412         packet.ObjectData['RayEndIsIntersection'] = RayEndIsIntersection
413         packet.ObjectData['Scale'] = Scale
414         packet.ObjectData['Rotation'] = Rotation
415         packet.ObjectData['State'] = State
416
417         self.region.enqueue_message(packet(), True)
418
419     def onObjectUpdate(self, packet):
420         """ populates an Object instance and adds it to the ObjectManager() store """
421
422         REGION_SIZE = 256.0
423         MIN_HEIGHT = -REGION_SIZE
424         MAX_HEIGHT = 4096.0
425
426         object_list = []
427
428         # ToDo: handle these 2 variables properly
429         _RegionHandle = packet.blocks['RegionData'][0].get_variable('RegionHandle').data
430         _TimeDilation = packet.blocks['RegionData'][0].get_variable('TimeDilation').data
431
432         for ObjectData_block in packet.blocks['ObjectData']:
433
434             object_properties = {}
435
436             object_properties['LocalID'] = ObjectData_block.get_variable('ID').data
437             object_properties['State'] = ObjectData_block.get_variable('State').data
438             object_properties['FullID'] = ObjectData_block.get_variable('FullID').data
439             object_properties['CRC'] = ObjectData_block.get_variable('CRC').data
440             object_properties['PCode'] = ObjectData_block.get_variable('PCode').data
441             object_properties['Material'] = ObjectData_block.get_variable('Material').data
442             object_properties['ClickAction'] = ObjectData_block.get_variable('ClickAction').data
443             object_properties['Scale'] = ObjectData_block.get_variable('Scale').data
444             object_properties['ObjectData'] = ObjectData_block.get_variable('ObjectData').data
445             object_properties['ParentID'] = ObjectData_block.get_variable('ParentID').data
446             object_properties['UpdateFlags'] = ObjectData_block.get_variable('UpdateFlags').data
447             object_properties['PathCurve'] = ObjectData_block.get_variable('PathCurve').data
448             object_properties['ProfileCurve'] = ObjectData_block.get_variable('ProfileCurve').data
449             object_properties['PathBegin'] = ObjectData_block.get_variable('PathBegin').data
450             object_properties['PathEnd'] = ObjectData_block.get_variable('PathEnd').data
451             object_properties['PathScaleX'] = ObjectData_block.get_variable('PathScaleX').data
452             object_properties['PathScaleY'] = ObjectData_block.get_variable('PathScaleY').data
453             object_properties['PathShearX'] = ObjectData_block.get_variable('PathShearX').data
454             object_properties['PathShearY'] = ObjectData_block.get_variable('PathShearY').data
455             object_properties['PathTwist'] = ObjectData_block.get_variable('PathTwist').data
456             object_properties['PathTwistBegin'] = ObjectData_block.get_variable('PathTwistBegin').data
457             object_properties['PathRadiusOffset'] = ObjectData_block.get_variable('PathRadiusOffset').data
458             object_properties['PathTaperX'] = ObjectData_block.get_variable('PathTaperX').data
459             object_properties['PathTaperY'] = ObjectData_block.get_variable('PathTaperY').data
460             object_properties['PathRevolutions'] = ObjectData_block.get_variable('PathRevolutions').data
461             object_properties['PathSkew'] = ObjectData_block.get_variable('PathSkew').data
462             object_properties['ProfileBegin'] = ObjectData_block.get_variable('ProfileBegin').data
463             object_properties['ProfileEnd'] = ObjectData_block.get_variable('ProfileEnd').data
464             object_properties['ProfileHollow'] = ObjectData_block.get_variable('ProfileHollow').data
465             object_properties['TextureEntry'] = ObjectData_block.get_variable('TextureEntry').data
466             object_properties['TextureAnim'] = ObjectData_block.get_variable('TextureAnim').data
467             object_properties['NameValue'] = ObjectData_block.get_variable('NameValue').data
468             object_properties['Data'] = ObjectData_block.get_variable('Data').data
469             object_properties['Text'] = ObjectData_block.get_variable('Text').data
470             object_properties['TextColor'] = ObjectData_block.get_variable('TextColor').data
471             object_properties['MediaURL'] = ObjectData_block.get_variable('MediaURL').data
472             object_properties['PSBlock'] = ObjectData_block.get_variable('PSBlock').data
473             object_properties['ExtraParams'] = ObjectData_block.get_variable('ExtraParams').data
474             object_properties['Sound'] = ObjectData_block.get_variable('Sound').data
475             object_properties['OwnerID'] = ObjectData_block.get_variable('OwnerID').data
476             object_properties['Gain'] = ObjectData_block.get_variable('Gain').data
477             object_properties['Flags'] = ObjectData_block.get_variable('Flags').data
478             object_properties['Radius'] = ObjectData_block.get_variable('Radius').data
479             object_properties['JointType'] = ObjectData_block.get_variable('JointType').data
480             object_properties['JointPivot'] = ObjectData_block.get_variable('JointPivot').data
481             object_properties['JointAxisOrAnchor'] = ObjectData_block.get_variable('JointAxisOrAnchor').data
482
483             # deal with the data stored in _ObjectData
484             # see http://wiki.secondlife.com/wiki/ObjectUpdate#ObjectData_Format for details
485
486             object_properties['FootCollisionPlane'] = None
487             object_properties['Position'] = None
488             object_properties['Velocity'] = None
489             object_properties['Acceleration'] = None
490             object_properties['Rotation'] = None
491             object_properties['AngularVelocity'] = None
492
493             objdata = object_properties['ObjectData']
494             if len(objdata) == 76 or len(objdata) == 60:
495
496                 pos = 0
497
498                 if len(objdata) == 76:
499                     # Foot collision plane. LLVector4.
500                     # Angular velocity is ignored and set to 0. Falls through to 60 bytes parser.
501                     object_properties['FootCollisionPlane'] = Quaternion(objdata, pos)
502                     pos += 16
503
504                 # 32 bit precision update.
505                
506                 object_properties['Position'] = Vector3(objdata, pos+0)
507                 object_properties['Velocity'] = Vector3(objdata, pos+12)
508                 object_properties['Acceleration'] = Vector3(objdata, pos+24)
509                 object_properties['Rotation'] = Quaternion(objdata, pos+36, 3) # unpack from vector3
510                 object_properties['AngularVelocity'] = Vector3(objdata, pos+48)
511
512                 # *TODO: This is... weird
513                 #if len(objdata) == 76:
514                 #    object_properties['AngularVelocity'] = Vector3()
515
516
517             elif len(objdata) == 48 or len(objdata) == 32:
518
519                 pos = 0
520                 if len(objdata) == 48:
521                     # Foot collision plane. LLVector4
522                     object_properties['FootCollisionPlane'] = Quaternion(objdata, pos)
523                     pos += 16
524
525                 # 32 bit precision update.
526
527                 # Position. U16Vec3.
528                 # Velocity. U16Vec3.
529                 # Acceleration. U16Vec3.
530                 # Rotation. U16Rot(4xU16).
531                 # Angular velocity. LLVector3.
532
533                 object_properties['Position'] = Vector3(
534                     X=Helpers.packed_u16_to_float(objdata, pos+ 0, -0.5*REGION_SIZE, 1.5*REGION_SIZE),
535                     Y=Helpers.packed_u16_to_float(objdata, pos+ 2, -0.5*REGION_SIZE, 1.5*REGION_SIZE),
536                     Z=Helpers.packed_u16_to_float(objdata, pos+ 4, MIN_HEIGHT, MAX_HEIGHT))
537                 object_properties['Velocity'] = Vector3(
538                     X=Helpers.packed_u16_to_float(objdata, pos+ 6, -REGION_SIZE, REGION_SIZE),
539                     Y=Helpers.packed_u16_to_float(objdata, pos+ 8, -REGION_SIZE, REGION_SIZE),
540                     Z=Helpers.packed_u16_to_float(objdata, pos+10, -REGION_SIZE, REGION_SIZE))
541                 object_properties['Acceleration'] = Vector3(
542                     X=Helpers.packed_u16_to_float(objdata, pos+12, -REGION_SIZE, REGION_SIZE),
543                     Y=Helpers.packed_u16_to_float(objdata, pos+14, -REGION_SIZE, REGION_SIZE),
544                     Z=Helpers.packed_u16_to_float(objdata, pos+16, -REGION_SIZE, REGION_SIZE))
545                 object_properties['Rotation'] = Quaternion(
546                     X=Helpers.packed_u16_to_float(objdata, pos+18, -1.0, 1.0),
547                     Y=Helpers.packed_u16_to_float(objdata, pos+20, -1.0, 1.0),
548                     Z=Helpers.packed_u16_to_float(objdata, pos+22, -1.0, 1.0),
549                     W=Helpers.packed_u16_to_float(objdata, pos+24, -1.0, 1.0))
550                 object_properties['AngularVelocity'] = Vector3(
551                     X=Helpers.packed_u16_to_float(objdata, pos+26, -REGION_SIZE, REGION_SIZE),
552                     Y=Helpers.packed_u16_to_float(objdata, pos+28, -REGION_SIZE, REGION_SIZE),
553                     Z=Helpers.packed_u16_to_float(objdata, pos+30, -REGION_SIZE, REGION_SIZE))
554
555             elif len(objdata) == 16:
556
557                 # 8 bit precision update.
558
559                 # Position. U8Vec3.
560                 # Velocity. U8Vec3.
561                 # Acceleration. U8Vec3.
562                 # Rotation. U8Rot(4xU8).
563                 # Angular velocity. U8Vec3
564
565                 object_properties['Position'] = Vector3(
566                     X=Helpers.packed_u8_to_float(objdata,  0, -0.5*REGION_SIZE, 1.5*REGION_SIZE),
567                     Y=Helpers.packed_u8_to_float(objdata,  1, -0.5*REGION_SIZE, 1.5*REGION_SIZE),
568                     Z=Helpers.packed_u8_to_float(objdata,  2, MIN_HEIGHT, MAX_HEIGHT))
569                 object_properties['Velocity'] = Vector3(
570                     X=Helpers.packed_u8_to_float(objdata,  3, -REGION_SIZE, REGION_SIZE),
571                     Y=Helpers.packed_u8_to_float(objdata,  4, -REGION_SIZE, REGION_SIZE),
572                     Z=Helpers.packed_u8_to_float(objdata,  5, -REGION_SIZE, REGION_SIZE))
573                 object_properties['Acceleration'] = Vector3(
574                     X=Helpers.packed_u8_to_float(objdata,  6, -REGION_SIZE, REGION_SIZE),
575                     Y=Helpers.packed_u8_to_float(objdata,  7, -REGION_SIZE, REGION_SIZE),
576                     Z=Helpers.packed_u8_to_float(objdata,  8, -REGION_SIZE, REGION_SIZE))
577                 object_properties['Rotation'] = Quaternion(
578                     X=Helpers.packed_u8_to_float(objdata,  9, -1.0, 1.0),
579                     Y=Helpers.packed_u8_to_float(objdata, 10, -1.0, 1.0),
580                     Z=Helpers.packed_u8_to_float(objdata, 11, -1.0, 1.0),
581                     W=Helpers.packed_u8_to_float(objdata, 12, -1.0, 1.0))
582                 object_properties['AngularVelocity'] = Vector3(
583                     X=Helpers.packed_u8_to_float(objdata, 13, -REGION_SIZE, REGION_SIZE),
584                     Y=Helpers.packed_u8_to_float(objdata, 14, -REGION_SIZE, REGION_SIZE),
585                     Z=Helpers.packed_u8_to_float(objdata, 15, -REGION_SIZE, REGION_SIZE))
586
587             object_list.append(object_properties)
588
589         self.update_multiple_objects_properties(object_list)
590
591     def onObjectUpdateCached(self, packet):
592         """ borrowing from libomv, we'll request object data for all data coming in via ObjectUpdateCached"""
593
594         # ToDo: handle these 2 variables properly
595         _RegionHandle = packet.blocks['RegionData'][0].get_variable('RegionHandle').data
596         _TimeDilation = packet.blocks['RegionData'][0].get_variable('TimeDilation').data
597
598         _request_list = []
599
600         for ObjectData_block in packet.blocks['ObjectData']:
601
602             LocalID = ObjectData_block.get_variable('ID').data
603             _CRC = ObjectData_block.get_variable('CRC').data
604             _UpdateFlags = ObjectData_block.get_variable('UpdateFlags').data
605
606             # Objects.request_object_update() expects a tuple of (_ID, CacheMissType)
607
608             # see if we have the object stored already
609             _object = self.get_object_from_store(LocalID = LocalID)
610
611             if _object == None or _object == []:
612                 CacheMissType = 1
613             else:
614                 CacheMissType = 0
615
616             _request_list.append((LocalID, CacheMissType))
617
618         # ask the simulator for updates
619         self.request_object_update(ID_list = _request_list)
620
621     def onObjectUpdateCompressed(self, packet):
622
623         object_list = []
624
625         # ToDo: handle these 2 variables properly
626         _RegionHandle = packet.blocks['RegionData'][0].get_variable('RegionHandle').data
627         _TimeDilation = packet.blocks['RegionData'][0].get_variable('TimeDilation').data
628
629         for ObjectData_block in packet.blocks['ObjectData']:
630
631             object_properties = {}
632
633             object_properties['UpdateFlags'] = ObjectData_block.get_variable('UpdateFlags').data
634             object_properties['Data'] = ObjectData_block.get_variable('Data').data
635             _Data = object_properties['Data']
636
637             pos = 0         # position in the binary string
638             object_properties['FullID'] = UUID(bytes = _Data, offset = 0)        # LLUUID
639             pos += 16
640             object_properties['LocalID'] = struct.unpack("<I", _Data[pos:pos+4])[0]
641             pos += 4
642             object_properties['PCode'] = struct.unpack(">B", _Data[pos:pos+1])[0]
643             pos += 1
644
645             if object_properties['PCode'] != 9:         # if it is not a prim, stop.
646                 log(WARNING, 'Fix Me!! Skipping parsing of ObjectUpdateCompressed packet when it is not a prim.')
647                 # we ought to parse it and make sense of the data...
648                 continue
649
650             object_properties['State'] = struct.unpack(">B", _Data[pos:pos+1])[0]
651             pos += 1
652             object_properties['CRC'] = struct.unpack("<I", _Data[pos:pos+4])[0]
653             pos += 4
654             object_properties['Material'] = struct.unpack(">B", _Data[pos:pos+1])[0]
655             pos += 1
656             object_properties['ClickAction'] = struct.unpack(">B", _Data[pos:pos+1])[0]
657             pos += 1
658             object_properties['Scale'] = Vector3(_Data, pos)
659             pos += 12
660             object_properties['Position'] = Vector3(_Data, pos)
661             pos += 12
662             object_properties['Rotation'] = Vector3(_Data, pos)
663             pos += 12
664             object_properties['Flags'] = struct.unpack(">B", _Data[pos:pos+1])[0]
665             pos += 1
666             object_properties['OwnerID'] = UUID(bytes = _Data, offset = pos)
667             pos += 16
668
669             # Placeholder vars, to be populated via flags if present
670             object_properties['AngularVelocity'] = Vector3()
671             object_properties['ParentID'] = UUID()
672             object_properties['Text'] = ''
673             object_properties['TextColor'] = None
674             object_properties['MediaURL'] = ''
675             object_properties['Sound'] = UUID()
676             object_properties['Gain'] = 0
677             object_properties['Flags'] = 0
678             object_properties['Radius'] = 0
679             object_properties['NameValue'] = ''
680             object_properties['ExtraParams'] = None
681
682             if object_properties['Flags'] != 0:
683
684                 log(WARNING, "FixMe! Quiting parsing an ObjectUpdateCompressed packet with flags due to incomplete implemention. Storing a partial representation of an object with uuid of %s" % (_FullID))
685
686                 # the commented code is not working right, we need to figure out why!
687                 # ExtraParams in particular seemed troublesome
688
689                 '''
690                 print 'Flags: ', Flags
691
692                 if (Flags & CompressedUpdateFlags.contains_AngularVelocity) != 0:
693                     _AngularVelocity = Vector3(_Data, pos)
694                     pos += 12
695                     print 'AngularVelocity: ', _AngularVelocity
696                 else:
697                     _AngularVelocity = None
698
699                 if (Flags & CompressedUpdateFlags.contains_Parent) != 0:
700                     _ParentID = UUID(_Data, pos)
701                     pos += 16
702                     print 'ParentID: ', _ParentID
703                 else:
704                     _ParentID = None
705
706                 if (Flags & CompressedUpdateFlags.Tree) != 0:
707                     # skip it, only iterate the position
708                     pos += 1
709                     print 'Tree'
710
711                 if (Flags & CompressedUpdateFlags.ScratchPad) != 0:
712                     # skip it, only iterate the position
713                     size = struct.unpack(">B", _Data[pos:pos+1])[0]
714                     pos += 1
715                     pos += size
716                     print 'Scratchpad size'
717
718                 if (Flags & CompressedUpdateFlags.contains_Text) != 0:
719                     # skip it, only iterate the position
720                     _Text = ''
721                     while struct.unpack(">B", _Data[pos:pos+1])[0] != 0:
722                         pos += 1
723                     pos += 1
724                     _TextColor = struct.unpack("<I", _Data[pos:pos+4])[0]
725                     pos += 4
726                     print '_TextColor: ', _TextColor
727
728                 if (Flags & CompressedUpdateFlags.MediaURL) != 0:
729                     # skip it, only iterate the position
730                     _MediaURL = ''
731                     while struct.unpack(">B", _Data[pos:pos+1])[0] != 0:
732                         pos += 1
733                     pos += 1
734                     print '_MediaURL: ', _MediaURL
735
736                 if (Flags & CompressedUpdateFlags.contains_Particles) != 0:
737                     # skip it, only iterate the position
738                     ParticleData = _Data[pos:pos+86]
739                     pos += 86
740                     print 'Particles'
741
742                 # parse ExtraParams
743                 # ToDo: finish this up, for now we are just incrementing the position and not dealing with the data
744
745                 _Flexible = None
746                 _Light = None
747                 _Sculpt = None
748
749                 num_extra_params =  struct.unpack(">b", _Data[pos:pos+1])[0]
750                 print 'Number of extra params: ', num_extra_params
751                 pos += 1
752
753                 for i in range(num_extra_params):
754
755                     # ExtraParam type
756                     extraparam_type = struct.unpack("<H", _Data[pos:pos+2])[0]
757                     pos += 2
758
759                     datalength = struct.unpack("<I", _Data[pos:pos+4])[0]
760                     print 'ExtraParams type: %s length: %s' % (extraparam_type, datalength)
761                     pos += 4
762
763                     pos += int(datalength)
764
765                 # ToDo: Deal with extra parameters
766                 #log(WARNING, "Incomplete implementation in onObjectUpdateCompressed when flags are present. Skipping parsing this object...")
767                 #continue
768
769                 if (Flags & CompressedUpdateFlags.contains_Sound) != 0:
770                     # skip it, only iterate the position
771                     #_Sound = UUID(bytes = _Data[pos:pos+16])
772                     pos += 16
773                     print 'Sound'
774
775                     #_Gain = struct.unpack(">f", _Data[pos:pos+4])[0]
776                     pos += 4
777
778                     #_Flags = stuct.unpack(">B", _Data[pos:pos+1])[0]
779                     pos += 1
780
781                     #_Radius = struct.unpack(">f", _Data[pos:pos+4])[0]
782                     pos += 4
783
784                 if (Flags & CompressedUpdateFlags.contains_NameValues) != 0:
785                     # skip it, only iterate the position
786                     _NameValue = ''
787
788                     while _Data[pos:pos+1] != 0:
789                         #_NameValue += struct.unpack(">c", _Data[pos:pos+1])[0]
790                         pos += 1
791                     pos += 1
792                 '''
793
794                 object_properties['PathCurve'] = None
795                 object_properties['PathBegin'] = None
796                 object_properties['PathEnd'] = None
797                 object_properties['PathScaleX'] = None
798                 object_properties['PathScaleY'] = None
799                 object_properties['PathShearX'] = None
800                 object_properties['PathShearY'] = None
801                 object_properties['PathTwist'] = None
802                 object_properties['PathTwistBegin'] = None
803                 object_properties['PathRadiusOffset'] = None
804                 object_properties['PathTaperX'] = None
805                 object_properties['PathTaperY'] = None
806                 object_properties['PathRevolutions'] = None
807                 object_properties['PathSkew'] = None
808                 object_properties['ProfileCurve'] = None
809                 object_properties['ProfileBegin'] = None
810                 object_properties['ProfileEnd'] = None
811                 object_properties['ProfileHollow'] = None
812                 object_properties['TextureEntry'] = None
813                 object_properties['TextureAnim'] = None
814                 object_properties['TextureAnim'] = None
815
816             else:
817
818                 object_properties['PathCurve'] = struct.unpack(">B", _Data[pos:pos+1])[0]
819                 pos += 1
820                 object_properties['PathBegin'] = struct.unpack("<H", _Data[pos:pos+2])[0]
821                 pos += 2
822                 object_properties['PathEnd'] = struct.unpack("<H", _Data[pos:pos+2])[0]
823                 pos += 2
824                 object_properties['PathScaleX'] = struct.unpack(">B", _Data[pos:pos+1])[0]
825                 pos += 1
826                 object_properties['PathScaleY'] = struct.unpack(">B", _Data[pos:pos+1])[0]
827                 pos += 1
828                 object_properties['PathShearX'] = struct.unpack(">B", _Data[pos:pos+1])[0]
829                 pos += 1
830                 object_properties['PathShearY'] = struct.unpack(">B", _Data[pos:pos+1])[0]
831                 pos += 1
832                 object_properties['PathTwist'] = struct.unpack(">B", _Data[pos:pos+1])[0]
833                 pos += 1
834                 object_properties['PathTwistBegin'] = struct.unpack(">B", _Data[pos:pos+1])[0]
835                 pos += 1
836                 object_properties['PathRadiusOffset'] = struct.unpack(">B", _Data[pos:pos+1])[0]
837                 pos += 1
838                 object_properties['PathTaperX'] = struct.unpack(">B", _Data[pos:pos+1])[0]
839                 pos += 1
840                 object_properties['PathTaperY'] = struct.unpack(">B", _Data[pos:pos+1])[0]
841                 pos += 1
842                 object_properties['PathRevolutions'] = struct.unpack(">B", _Data[pos:pos+1])[0]
843                 pos += 1
844                 object_properties['PathSkew'] = struct.unpack(">B", _Data[pos:pos+1])[0]
845                 pos += 1
846                 object_properties['ProfileCurve'] = struct.unpack(">B", _Data[pos:pos+1])[0]
847                 pos += 1
848                 object_properties['ProfileBegin'] = struct.unpack(">B", _Data[pos:pos+1])[0]
849                 pos += 1
850                 object_properties['ProfileEnd'] = struct.unpack(">B", _Data[pos:pos+1])[0]
851                 pos += 1
852                 object_properties['ProfileHollow'] = struct.unpack(">B", _Data[pos:pos+1])[0]
853                 pos += 1
854
855                 # Texture handling
856                 size = struct.unpack("<H", _Data[pos:pos+2])[0]
857                 pos += 2
858                 object_properties['TextureEntry'] = _Data[pos:pos+size]
859                 pos += size
860
861                 if (object_properties['Flags'] & CompressedUpdateFlags.TextureAnim) != 0:
862                     object_properties['TextureAnim'] = struct.unpack("<H", _Data[pos:pos+2])[0]
863                     pos += 2
864                 else:
865                     object_properties['TextureAnim'] = None
866
867             object_list.append(object_properties)
868
869         self.update_multiple_objects_properties(object_list)
870
871     def onKillObject(self, packet):
872
873         _KillID = packet.blocks['ObjectData'][0].get_variable('ID').data
874
875         self.remove_object_from_store(_KillID)
876
877     def onObjectProperties(self, packet):
878
879         object_list = []
880
881         for ObjectData_block in packet.blocks['ObjectData']:
882
883             object_properties = {}
884
885             object_properties['FullID'] = ObjectData_block.get_variable('ObjectID').data
886             object_properties['CreatorID'] = ObjectData_block.get_variable('CreatorID').data
887             object_properties['OwnerID'] = ObjectData_block.get_variable('OwnerID').data
888             object_properties['GroupID'] = ObjectData_block.get_variable('GroupID').data
889             object_properties['CreationDate'] = ObjectData_block.get_variable('CreationDate').data
890             object_properties['BaseMask'] = ObjectData_block.get_variable('BaseMask').data
891             object_properties['OwnerMask'] = ObjectData_block.get_variable('OwnerMask').data
892             object_properties['GroupMask'] = ObjectData_block.get_variable('GroupMask').data
893             object_properties['EveryoneMask'] = ObjectData_block.get_variable('EveryoneMask').data
894             object_properties['NextOwnerMask'] = ObjectData_block.get_variable('NextOwnerMask').data
895             object_properties['OwnershipCost'] = ObjectData_block.get_variable('OwnershipCost').data
896             #object_properties['TaxRate'] = ObjectData_block.get_variable('TaxRate').data
897             object_properties['SaleType'] = ObjectData_block.get_variable('SaleType').data
898             object_properties['SalePrice'] = ObjectData_block.get_variable('SalePrice').data
899             object_properties['AggregatePerms'] = ObjectData_block.get_variable('AggregatePerms').data
900             object_properties['AggregatePermTextures'] = ObjectData_block.get_variable('AggregatePermTextures').data
901             object_properties['AggregatePermTexturesOwner'] = ObjectData_block.get_variable('AggregatePermTexturesOwner').data
902             object_properties['Category'] = ObjectData_block.get_variable('Category').data
903             object_properties['InventorySerial'] = ObjectData_block.get_variable('InventorySerial').data
904             object_properties['ItemID'] = ObjectData_block.get_variable('ItemID').data
905             object_properties['FolderID'] = ObjectData_block.get_variable('FolderID').data
906             object_properties['FromTaskID'] = ObjectData_block.get_variable('FromTaskID').data
907             object_properties['LastOwnerID'] = ObjectData_block.get_variable('LastOwnerID').data
908             object_properties['Name'] = ObjectData_block.get_variable('Name').data
909             object_properties['Description'] = ObjectData_block.get_variable('Description').data
910             object_properties['TouchName'] = ObjectData_block.get_variable('TouchName').data
911             object_properties['SitName'] = ObjectData_block.get_variable('SitName').data
912             object_properties['TextureID'] = ObjectData_block.get_variable('TextureID').data
913
914             object_list.append(object_properties)
915
916         self.update_multiple_objects_properties(object_list)
917
918 class Object(object):
919     """ represents an Object
920
921     Initialize the Object class instance
922     >>> object = Object()
923
924     Sample implementations: objects.py
925     Tests: tests/test_objects.py
926     """
927
928     def __init__(self, LocalID = None, State = None, FullID = None, CRC = None, PCode = None, Material = None, ClickAction = None, Scale = None, ObjectData = None, ParentID = None, UpdateFlags = None, PathCurve = None, ProfileCurve = None, PathBegin = None, PathEnd = None, PathScaleX = None, PathScaleY = None, PathShearX = None, PathShearY = None, PathTwist = None, PathTwistBegin = None, PathRadiusOffset = None, PathTaperX = None, PathTaperY = None, PathRevolutions = None, PathSkew = None, ProfileBegin = None, ProfileEnd = None, ProfileHollow = None, TextureEntry = None, TextureAnim = None, NameValue = None, Data = None, Text = None, TextColor = None, MediaURL = None, PSBlock = None, ExtraParams = None, Sound = None, OwnerID = None, Gain = None, Flags = None, Radius = None, JointType = None, JointPivot = None, JointAxisOrAnchor = None, FootCollisionPlane = None, Position = None, Velocity = None, Acceleration = None, Rotation = None, AngularVelocity = None):
929         """ set up the object attributes """
930
931         self.LocalID = LocalID                                 # U32
932         self.State = State                           # U8
933         self.FullID = FullID        # LLUUID
934         self.CRC = CRC                               # U32 // TEMPORARY HACK FOR JAMES
935         self.PCode = PCode                           # U8
936         self.Material = Material                     # U8
937         self.ClickAction = ClickAction               # U8
938         self.Scale = Scale                           # LLVector3
939         self.ObjectData = ObjectData                 # Variable 1
940         self.ParentID = ParentID                     # U32
941         self.UpdateFlags = UpdateFlags               # U32 // U32, see object_flags.h
942         self.PathCurve = PathCurve                   # U8
943         self.ProfileCurve = ProfileCurve             # U8
944         self.PathBegin = PathBegin                   # U16 // 0 to 1, quanta = 0.01
945         self.PathEnd = PathEnd                       # U16 // 0 to 1, quanta = 0.01
946         self.PathScaleX = PathScaleX                 # U8 // 0 to 1, quanta = 0.01
947         self.PathScaleY = PathScaleY                 # U8 // 0 to 1, quanta = 0.01
948         self.PathShearX = PathShearX                 # U8 // -.5 to .5, quanta = 0.01
949         self.PathShearY = PathShearY                 # U8 // -.5 to .5, quanta = 0.01
950         self.PathTwist = PathTwist                   # S8 // -1 to 1, quanta = 0.01
951         self.PathTwistBegin = PathTwistBegin         # S8 // -1 to 1, quanta = 0.01
952         self.PathRadiusOffset = PathRadiusOffset     # S8 // -1 to 1, quanta = 0.01
953         self.PathTaperX = PathTaperX                 # S8 // -1 to 1, quanta = 0.01
954         self.PathTaperY = PathTaperY                 # S8 // -1 to 1, quanta = 0.01
955         self.PathRevolutions = PathRevolutions       # U8 // 0 to 3, quanta = 0.015
956         self.PathSkew = PathSkew                     # S8 // -1 to 1, quanta = 0.01
957         self.ProfileBegin = ProfileBegin             # U16 // 0 to 1, quanta = 0.01
958         self.ProfileEnd = ProfileEnd                 # U16 // 0 to 1, quanta = 0.01
959         self.ProfileHollow = ProfileHollow           # U16 // 0 to 1, quanta = 0.01
960         self.TextureEntry = TextureEntry             # Variable 2
961         self.TextureAnim = TextureAnim               # Variable 1
962         self.NameValue = NameValue                   # Variable 2
963         self.Data = Data                             # Variable 2
964         self.Text = Text                             # Variable 1 // llSetText() hovering text
965         self.TextColor = TextColor                   # Fixed 4 // actually, a LLColor4U
966         self.MediaURL = MediaURL                     # Variable 1 // URL for web page, movie, etc.
967         self.PSBlock = PSBlock                       # Variable 1
968         self.ExtraParams = ExtraParams               # Variable 1
969         self.Sound = Sound                           # LLUUID
970         self.OwnerID = OwnerID                       # LLUUID // HACK object's owner id, only set if non-null sound, for muting
971         self.Gain = Gain                             # F32
972         self.Flags = Flags                           # U8
973         self.Radius = Radius                         # F32 // cutoff radius
974         self.JointType = JointType                   # U8
975         self.JointPivot = JointPivot                 # LLVector3
976         self.JointAxisOrAnchor = JointAxisOrAnchor   # LLVector3
977
978         # from ObjectUpdateCompressed
979         self.FootCollisionPlane = FootCollisionPlane
980         self.Position = Position
981         self.Velocity = Velocity
982         self.Acceleration = Acceleration
983         self.Rotation = Rotation
984         self.AngularVelocity = AngularVelocity
985
986         # from ObjectProperties
987         self.CreatorID = None
988         self.GroupID = None
989         self.CreationDate = None
990         self.BaseMask = None
991         self.OwnerMask = None
992         self.GroupMask = None
993         self.EveryoneMask = None
994         self.NextOwnerMask = None
995         self.OwnershipCost = None
996         # TaxRate
997         self.SaleType = None
998         self.SalePrice = None
999         self.AggregatePerms = None
1000         self.AggregatePermTextures = None
1001         self.AggregatePermTexturesOwner = None
1002         self.Category = None
1003         self.InventorySerial = None
1004         self.ItemID = None
1005         self.FolderID = None
1006         self.FromTaskID = None
1007         self.LastOwnerID = None
1008         self.Name = None
1009         self.Description = None
1010         self.TouchName = None
1011         self.SitName = None
1012         self.TextureID = None
1013
1014     def update_object_permissions(self, agent, Field, Set, Mask, Override = False):
1015         """ update permissions for a list of objects
1016
1017         This will update a specific bit to a specific value.
1018         """
1019
1020         packet = ObjectPermissionsPacket()
1021
1022         # build the AgentData block
1023         packet.AgentData['AgentID'] = agent.agent_id
1024         packet.AgentData['SessionID'] = agent.session_id
1025
1026         packet.HeaderData['Override'] = Override # BOOL, God-bit.
1027
1028         ObjectData = {}
1029         ObjectData['ObjectLocalID'] = self.LocalID
1030         ObjectData['Field'] = Field         # U32
1031         ObjectData['Set'] = Set             # U8
1032         ObjectData['Mask'] = Mask           # S32
1033
1034         packet.ObjectDataBlocks.append(ObjectData)
1035
1036         agent.region.enqueue_message(packet())
1037
1038     def set_object_full_permissions(self, agent):
1039         """
1040         Set Next Owner Permissions Copy, Modify, Transfer
1041         This is also called 'full permissions'.
1042         """
1043
1044         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy)
1045         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Modify)
1046         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer)
1047
1048     def set_object_copy_mod_permissions(self, agent):
1049         """
1050         Set Next Owner Permissions to Copy/Mod
1051         This is a common permission set for attachements.
1052         """
1053
1054         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy)
1055         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Modify)
1056         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Transfer)
1057
1058     def set_object_mod_transfer_permissions(self, agent):
1059         """
1060         Set Next Owner Permissions to Mod/Transfer
1061         This is a common permission set for clothing.
1062         """
1063
1064         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Copy)
1065         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Modify)
1066         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer)
1067
1068     def set_object_transfer_only_permissions(self, agent):
1069         """
1070         Set Next Owner Permissions to Transfer Only
1071         This is the most restrictive set of permissions allowed.
1072         """
1073
1074         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Copy)
1075         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Modify)
1076         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer)
1077
1078     def set_object_copy_transfer_permissions(self, agent):
1079         """
1080         Set Next Owner Permissions to Copy/Transfer
1081         """
1082
1083         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy)
1084         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Modify)
1085         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Transfer)
1086
1087     def set_object_copy_only_permissions(self, agent):
1088         """
1089         Set Next Owner Permissions to Copy Only
1090         """
1091
1092         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 1, PermissionsMask.Copy)
1093         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Modify)
1094         self.update_object_permissions(agent, PermissionsTarget.NextOwner, 0, PermissionsMask.Transfer)
1095
1096     def set_object_name(self, agent, Name):
1097         """ update the name of an object
1098
1099         """
1100
1101         packet = ObjectNamePacket()
1102
1103         # build the AgentData block
1104         packet.AgentData['AgentID'] = agent.agent_id
1105         packet.AgentData['SessionID'] = agent.session_id
1106
1107         ObjectData = {}
1108         ObjectData['LocalID'] = self.LocalID
1109         ObjectData['Name'] = Name
1110
1111         packet.ObjectDataBlocks.append(ObjectData)
1112
1113         agent.region.enqueue_message(packet())
1114
1115     def set_object_description(self, agent, Description):
1116         """ update the description of an objects
1117
1118         """
1119
1120         packet = ObjectDescriptionPacket()
1121
1122         # build the AgentData block
1123         packet.AgentData['AgentID'] = agent.agent_id
1124         packet.AgentData['SessionID'] = agent.session_id
1125
1126         ObjectData = {}
1127         ObjectData['LocalID'] = self.LocalID
1128         ObjectData['Description'] = Description
1129
1130         packet.ObjectDataBlocks.append(ObjectData)
1131
1132         agent.region.enqueue_message(packet())
1133
1134     def derez(self, agent, destination, destinationID, transactionID, GroupID):
1135         """ derez an object, specifying the destination """
1136
1137         packet = DeRezObjectPacket()
1138
1139         # build the AgentData block
1140         packet.AgentData['AgentID'] = agent.agent_id
1141         packet.AgentData['SessionID'] = agent.session_id
1142
1143         packet.AgentBlock['GroupID'] = GroupID
1144         packet.AgentBlock['Destination'] = destination
1145         packet.AgentBlock['DestinationID'] = destinationID
1146         packet.AgentBlock['TransactionID'] = transactionID
1147
1148         # fix me: faking these values
1149         packet.AgentBlock['PacketCount']   = 1
1150         packet.AgentBlock['PacketNumber']  = 0
1151
1152         ObjectData = {}
1153         ObjectData['ObjectLocalID'] = self.LocalID
1154
1155         packet.ObjectDataBlocks.append(ObjectData)
1156
1157         agent.region.enqueue_message(packet())
1158
1159     def take(self, agent):
1160         """ take object into inventory """
1161
1162         parent_folder = self.FolderID
1163
1164         # Check if we have inventory turned on
1165         if not(parent_folder and agent.settings.ENABLE_INVENTORY_MANAGEMENT):
1166             log(WARNING,"Inventory not available, please enable settings.ENABLE_INVENTORY_MANAGEMENT")
1167             return
1168
1169         if not(parent_folder):
1170             # locate Object folder
1171             objects_folder = [ folder for folder in agent.inventory.folders if folder.Name == 'Objects' ]
1172             if objects_folder:
1173                 parent_folder = objects_folder[0].FolderID
1174             else:
1175                 log(ERROR,"Unable to locate top-level Objects folder to take item into inventory.")
1176                 return
1177
1178         self.derez(agent, 4, parent_folder, uuid.uuid4(), agent.ActiveGroupID)
1179
1180     def select(self, agent):
1181         """ select an object
1182
1183         """
1184
1185         packet = ObjectSelectPacket()
1186
1187         # build the AgentData block
1188         packet.AgentData['AgentID'] = agent.agent_id
1189         packet.AgentData['SessionID'] = agent.session_id
1190
1191         ObjectData = {}
1192         ObjectData['ObjectLocalID'] = self.LocalID
1193
1194         packet.ObjectDataBlocks.append(ObjectData)
1195
1196         agent.region.enqueue_message(packet())
1197
1198     def deselect(self, agent):
1199         """ deselect an object
1200
1201         """
1202
1203         packet = ObjectDeselectPacket()
1204
1205         # build the AgentData block
1206         packet.AgentData['AgentID'] = agent.agent_id
1207         packet.AgentData['SessionID'] = agent.session_id
1208
1209         ObjectData = {}
1210         ObjectData['ObjectLocalID'] = self.LocalID
1211
1212         packet.ObjectDataBlocks.append(ObjectData)
1213
1214         agent.region.enqueue_message(packet())
1215
1216     def _update_properties(self, properties):
1217         """ takes a dictionary of attribute:value and makes it so """
1218
1219         for attribute in properties:
1220
1221             setattr(self, attribute, properties[attribute])
1222
1223 """
1224 Contributors can be viewed at:
1225 http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt
1226
1227 $LicenseInfo:firstyear=2008&license=apachev2$
1228
1229 Copyright 2009, Linden Research, Inc.
1230
1231 Licensed under the Apache License, Version 2.0 (the "License").
1232 You may obtain a copy of the License at:
1233     http://www.apache.org/licenses/LICENSE-2.0
1234 or in
1235     http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt
1236
1237 $/LicenseInfo$
1238 """
1239
Note: See TracBrowser for help on using the browser.