panda3d/direct/src/distributed/DoCollectionManager.py

325 lines
12 KiB
Python
Executable File

#hack:
BAD_DO_ID = BAD_ZONE_ID = 0xFFFFFFFF
BAD_CHANNEL_ID = 0xFFFFFFFFFFFFFFFF
class DoCollectionManager:
def __init__(self):
# Dict of {DistributedObject ids : DistributedObjects}
self.doId2do = {}
# (parentId, zoneId) to dict of doId->DistributedObjectAI
## self.zoneId2doIds={}
if self.hasOwnerView():
# Dict of {DistributedObject ids : DistributedObjects} for 'owner' views of objects
self.doId2ownerView = {}
# Dict of {
# parent DistributedObject id:
# { zoneIds : [child DistributedObject ids] }}
self.__doHierarchy = {}
def getDo(self, doId):
return self.doId2do.get(doId)
def getOwnerView(self, doId):
assert self.hasOwnerView()
return self.doId2ownerView.get(doId)
def getDoTable(self, ownerView):
if ownerView:
assert self.hasOwnerView()
return self.doId2ownerView
else:
return self.doId2do
def doFind(self, str):
"""
Returns list of distributed objects with matching str in value.
"""
for value in self.doId2do.values():
if `value`.find(str) >= 0:
return value
def doFindAll(self, str):
"""
Returns list of distributed objects with matching str in value.
"""
matches = []
for value in self.doId2do.values():
if `value`.find(str) >= 0:
matches.append(value)
return matches
def getDoHierarchy(self):
return self.__doHierarchy
if __debug__:
def printObjects(self):
format="%10s %10s %10s %30s %20s"
title=format%("parentId", "zoneId", "doId", "dclass", "name")
print title
print '-'*len(title)
for distObj in self.doId2do.values():
print format%(
distObj.__dict__.get("parentId"),
distObj.__dict__.get("zoneId"),
distObj.__dict__.get("doId"),
distObj.dclass.getName(),
distObj.__dict__.get("name"))
def getDoList(self, parentId, zoneId=None, classType=None):
"""
parentId is any distributed object id.
zoneId is a uint32, defaults to None (all zones). Try zone 2 if
you're not sure which zone to use (0 is a bad/null zone and
1 has had reserved use in the past as a no messages zone, while
2 has traditionally been a global, uber, misc stuff zone).
dclassType is a distributed class type filter, defaults
to None (no filter).
If dclassName is None then all objects in the zone are returned;
otherwise the list is filtered to only include objects of that type.
"""
return [self.doId2do.get(i)
for i in self.getDoIdList(parentId, zoneId, classType)]
def getDoIdList(self, parentId, zoneId=None, classType=None):
"""
parentId is any distributed object id.
zoneId is a uint32, defaults to None (all zones). Try zone 2 if
you're not sure which zone to use (0 is a bad/null zone and
1 has had reserved use in the past as a no messages zone, while
2 has traditionally been a global, uber, misc stuff zone).
dclassType is a distributed class type filter, defaults
to None (no filter).
If dclassName is None then all objects in the zone are returned;
otherwise the list is filtered to only include objects of that type.
"""
parent=self.__doHierarchy.get(parentId)
if parent is None:
return []
if zoneId is None:
r = []
for zone in parent.values():
for obj in zone:
r.append(obj)
else:
r = parent.get(zoneId, [])
if classType is not None:
a = []
for obj in r:
if isinstance(obj, classType):
a.append(obj)
r = a
return r
def countObjects(self, classType):
"""
Counts the number of objects of the given type in the
repository (for testing purposes)
"""
count = 0
for dobj in self.doId2do.values():
if isinstance(dobj, classType):
count += 1
return count
def getAllOfType(self, type):
# Returns a list of all DistributedObjects in the repository
# of a particular type.
result = []
for obj in self.doId2do.values():
if isinstance(obj, type):
result.append(obj)
return result
def findAnyOfType(self, type):
# Searches the repository for any object of the given type.
for obj in self.doId2do.values():
if isinstance(obj, type):
return obj
return None
#----------------------------------
def deleteDistributedObjects(self):
# Get rid of all the distributed objects
for doId in self.doId2do.keys():
# Look up the object
do = self.doId2do[doId]
self.deleteDistObject(do)
# Get rid of everything that manages distributed objects
self.deleteObjects()
# the zoneId2doIds table should be empty now
if len(self.__doHierarchy) > 0:
self.notify.warning(
'__doHierarchy table not empty: %s' % self.__doHierarchy)
self.__doHierarchy = {}
def handleObjectLocation(self, di):
# CLIENT_OBJECT_LOCATION
doId = di.getUint32()
parentId = di.getUint32()
zoneId = di.getUint32()
obj = self.doId2do.get(doId)
if obj is not None:
self.notify.debug(
"handleObjectLocation: doId: %s parentId: %s zoneId: %s"%
(doId, parentId, zoneId))
# Let the object finish the job
obj.setLocation(parentId, zoneId)
self.storeObjectLocation(doId, parentId, zoneId)
else:
self.notify.warning(
"handleObjectLocation: Asked to update non-existent obj: %s" % (doId))
def handleSetLocation(self, di):
# This was initially added because creating a distributed quest
# object would cause a message like this to be generated.
assert self.notify.debugStateCall(self)
parentId = di.getUint32()
zoneId = di.getUint32()
distObj = self.doId2do.get(self.getMsgChannel())
if distObj is not None:
distObj.setLocation(parentId, zoneId)
def storeObjectLocation(self, doId, parentId, zoneId):
obj = self.doId2do.get(doId)
if obj is not None:
oldParentId = obj.parentId
oldZoneId = obj.zoneId
if oldParentId != parentId:
# Remove old location
self.deleteDoIdFromLocation(doId, oldParentId, oldZoneId)
if oldParentId == parentId and oldZoneId == zoneId:
# object is already at that parent and zone
return
if (parentId is None) or (zoneId is None):
# Do not store null values
return
# Add to new location
parentZoneDict = self.__doHierarchy.setdefault(parentId, {})
zoneDoSet = parentZoneDict.setdefault(zoneId, set())
zoneDoSet.add(doId)
def deleteObjectLocation(self, distObj):
self.deleteDoIdFromLocation(
distObj.doId, distObj.parentId, distObj.zoneId)
def deleteDoIdFromLocation(self, doId, parentId, zoneId):
# Do not worry about null values
if (parentId is None) or (zoneId is None):
return
parentZoneDict = self.__doHierarchy.get(parentId)
if parentZoneDict is not None:
zoneDoSet = parentZoneDict.get(zoneId)
if zoneDoSet is not None:
if doId in zoneDoSet:
zoneDoSet.remove(doId)
if len(zoneDoSet) == 0:
del parentZoneDict[zoneId]
else:
self.notify.warning(
"deleteObjectLocation: objId: %s not found"%(doId,))
else:
self.notify.warning(
"deleteObjectLocation: zoneId: %s not found"%(zoneId,))
else:
self.notify.warning(
"deleteObjectLocation: parentId: %s not found"%(parentId,))
def addDOToTables(self, do, location=None, ownerView=False):
assert self.notify.debugStateCall(self)
#assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
if not ownerView:
if location is None:
location = (do.parentId, do.zoneId)
doTable = self.getDoTable(ownerView)
# make sure the object is not already present
if do.doId in doTable:
if ownerView:
tableName = 'doId2ownerView'
else:
tableName = 'doId2do'
self.notify.error('doId %s already in %s [%s stomping %s]' % (
do.doId, tableName, do.__class__.__name__,
doTable[do.doId].__class__.__name__))
doTable[do.doId]=do
if not ownerView:
if self.isValidLocationTuple(location):
self.storeObjectLocation(do.doId, location[0], location[1])
##assert do.doId not in self.zoneId2doIds.get(location,{})
##self.zoneId2doIds.setdefault(location, {})
##self.zoneId2doIds[location][do.doId]=do
def isValidLocationTuple(self, location):
return (location is not None
and location != (0xffffffff, 0xffffffff)
and location != (0, 0))
def removeDOFromTables(self, do):
assert self.notify.debugStateCall(self)
#assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
#assert do.doId in self.doId2do
self.deleteObjectLocation(do)
## location = do.getLocation()
## if location is not None:
## if location not in self.zoneId2doIds:
## self.notify.warning(
## 'dobj %s (%s) has invalid location: %s' %
## (do, do.doId, location))
## else:
## assert do.doId in self.zoneId2doIds[location]
## del self.zoneId2doIds[location][do.doId]
## if len(self.zoneId2doIds[location]) == 0:
## del self.zoneId2doIds[location]
if do.doId in self.doId2do:
del self.doId2do[do.doId]
def changeDOZoneInTables(self, do, newParentId, newZoneId, oldParentId, oldZoneId):
if 1:
self.storeObjectLocation(do.doId, newParentId, newZoneId)
else:
#assert not hasattr(do, "isQueryAllResponse") or not do.isQueryAllResponse
oldLocation = (oldParentId, oldZoneId)
newLocation = (newParentId, newZoneId)
# HACK: DistributedGuildMemberUD starts in -1,-1, which isnt ever put in the
# zoneId2doIds table
if self.isValidLocationTuple(oldLocation):
assert self.notify.debugStateCall(self)
assert oldLocation in self.zoneId2doIds
assert do.doId in self.zoneId2doIds[oldLocation]
assert do.doId not in self.zoneId2doIds.get(newLocation,{})
# remove from old zone
del(self.zoneId2doIds[oldLocation][do.doId])
if len(self.zoneId2doIds[oldLocation]) == 0:
del self.zoneId2doIds[oldLocation]
if self.isValidLocationTuple(newLocation):
# add to new zone
self.zoneId2doIds.setdefault(newLocation, {})
self.zoneId2doIds[newLocation][do.doId]=do
## def getObjectsInZone(self, parentId, zoneId):
## """ call this to get a dict of doId:distObj for a zone.
## Creates a shallow copy, so you can do whatever you want with the
## dict. """
## assert self.notify.debugStateCall(self)
## return copy.copy(self.zoneId2doIds.get((parentId, zoneId), {}))
def getObjectsOfClassInZone(self, parentId, zoneId, objClass):
"""
returns dict of doId:object for a zone, containing all objects
that inherit from 'class'. returned dict is safely mutable.
"""
assert self.notify.debugStateCall(self)
doDict = {}
for doId in self.getDoIdList(parentId, zoneId, objClass):
if isinstance(do, objClass):
doDict[doId] = self.doId2do.get(do)
return doDict