added LevelSpec data wrapper
This commit is contained in:
parent
5951f37a02
commit
5c113af66a
|
|
@ -64,11 +64,11 @@ class DistributedLevel(DistributedObject.DistributedObject,
|
|||
def levelAnnounceGenerate(self):
|
||||
pass
|
||||
|
||||
def initializeLevel(self, spec):
|
||||
"""subclass should call this as soon as it's located its spec data.
|
||||
def initializeLevel(self, levelSpec):
|
||||
"""subclass should call this as soon as it's located its level spec.
|
||||
Must be called after obj has been generated."""
|
||||
Level.Level.initializeLevel(self, self.doId,
|
||||
spec, self.scenarioIndex)
|
||||
levelSpec, self.scenarioIndex)
|
||||
|
||||
# all of the entities have been created now.
|
||||
# there should not be any pending reparents left at this point
|
||||
|
|
@ -287,7 +287,7 @@ class DistributedLevel(DistributedObject.DistributedObject,
|
|||
return
|
||||
|
||||
zoneEntId = self.zoneNum2entId[zoneNum]
|
||||
zoneSpec = self.entId2spec[zoneEntId]
|
||||
zoneSpec = self.levelSpec.getEntitySpec(zoneEntId)
|
||||
# use dicts to efficiently ensure that there are no duplicates
|
||||
visibleZoneNums = list2dict([zoneNum])
|
||||
visibleZoneNums.update(list2dict(zoneSpec['visibility']))
|
||||
|
|
@ -352,12 +352,12 @@ class DistributedLevel(DistributedObject.DistributedObject,
|
|||
if __debug__:
|
||||
# if someone has edited the level, we'll get the full up-to-date
|
||||
# spec in this message
|
||||
def setSpecOverride(self, specStr):
|
||||
if self.spec is not None:
|
||||
def setLevelSpecOverride(self, specStr):
|
||||
if self.levelSpec is not None:
|
||||
return
|
||||
|
||||
try:
|
||||
self.spec = eval(specStr)
|
||||
self.levelSpec = eval(specStr)
|
||||
except Exception, e:
|
||||
print ('Exception in %s(%s):\n\t%s' %
|
||||
(lineInfo()[2], specStr, e))
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
|
|||
Level.Level.__init__(self)
|
||||
self.uberZoneId = zoneId
|
||||
|
||||
def generate(self, spec):
|
||||
def generate(self, levelSpec):
|
||||
self.notify.debug('generate')
|
||||
DistributedObjectAI.DistributedObjectAI.generate(self)
|
||||
|
||||
self.initializeLevel(spec)
|
||||
self.initializeLevel(levelSpec)
|
||||
|
||||
self.sendUpdate('setZoneIds', [self.zoneIds])
|
||||
self.sendUpdate('setStartTimestamp', [self.startTimestamp])
|
||||
|
|
@ -32,19 +32,21 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
|
|||
self.destroyLevel()
|
||||
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||
|
||||
def initializeLevel(self, spec):
|
||||
def initializeLevel(self, levelSpec):
|
||||
# record the level's start time so that we can sync the clients
|
||||
self.startTime = globalClock.getRealTime()
|
||||
self.startTimestamp = globalClockDelta.localToNetworkTime(
|
||||
self.startTime, bits=32)
|
||||
|
||||
# choose a scenario
|
||||
wc = WeightedChoice.WeightedChoice(spec['scenarios'], 1)
|
||||
scenario = wc.choose()
|
||||
scenarioIndex = spec['scenarios'].index(scenario)
|
||||
# make list of lists: [(weight, scenarioIndex), ...]
|
||||
lol = zip(levelSpec.getScenarioWeights(),
|
||||
range(levelSpec.getNumScenarios()))
|
||||
wc = WeightedChoice.WeightedChoice(lol)
|
||||
scenarioIndex = wc.choose()[1]
|
||||
|
||||
Level.Level.initializeLevel(self, self.doId,
|
||||
spec, scenarioIndex)
|
||||
levelSpec, scenarioIndex)
|
||||
|
||||
def createEntityCreator(self):
|
||||
"""Create the object that will be used to create Entities.
|
||||
|
|
@ -56,7 +58,7 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
|
|||
# this func is called before the entity has been created; look
|
||||
# into the spec data, since we can't yet get a handle on the
|
||||
# object itself at this point
|
||||
spec = self.entId2spec[entId]
|
||||
spec = self.levelSpec.getEntitySpec(entId)
|
||||
type = spec['type']
|
||||
if type == 'zone':
|
||||
if not hasattr(self, 'zoneNum2zoneId'):
|
||||
|
|
@ -85,11 +87,11 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
|
|||
|
||||
# send a copy of the entire spec for any new users that
|
||||
# might come in
|
||||
##self.sendUpdate('setSpecOverride', [repr(self.spec)])
|
||||
##self.sendUpdate('setSpecOverride', [repr(self.levelSpec)])
|
||||
|
||||
def getCurrentSpec(self):
|
||||
def getCurrentLevelSpec(self):
|
||||
"""returns the complete, current spec, including any edits"""
|
||||
return self.spec
|
||||
return self.levelSpec
|
||||
|
||||
"""
|
||||
def getSpecOverride(self):
|
||||
|
|
|
|||
|
|
@ -34,31 +34,22 @@ class Level:
|
|||
UberZoneEntId = 0
|
||||
|
||||
def __init__(self):
|
||||
self.spec = None
|
||||
self.levelSpec = None
|
||||
|
||||
def initializeLevel(self, levelId, spec, scenarioIndex):
|
||||
def initializeLevel(self, levelId, levelSpec, scenarioIndex):
|
||||
""" subclass should call this as soon as it has located
|
||||
its spec data """
|
||||
self.levelId = levelId
|
||||
self.spec = spec
|
||||
self.levelSpec = levelSpec
|
||||
self.scenarioIndex = scenarioIndex
|
||||
|
||||
# create a complete set of global and scenario-specific entity specs
|
||||
globalEntities = self.spec['globalEntities']
|
||||
scenarioEntities = self.spec['scenarios'][self.scenarioIndex][0]
|
||||
entId2spec = {}
|
||||
entId2spec.update(globalEntities)
|
||||
entId2spec.update(scenarioEntities)
|
||||
self.entId2spec = entId2spec
|
||||
self.levelSpec.setScenario(self.scenarioIndex)
|
||||
|
||||
# create some handy tables
|
||||
|
||||
# entity type -> list of entIds
|
||||
self.entType2ids = {}
|
||||
for entId, spec in self.entId2spec.items():
|
||||
entType = spec['type']
|
||||
self.entType2ids.setdefault(entType, [])
|
||||
self.entType2ids[entType].append(entId)
|
||||
self.entType2ids = self.levelSpec.getEntType2ids(
|
||||
self.levelSpec.getAllEntIds())
|
||||
|
||||
# there should be one and only one levelMgr
|
||||
assert len(self.entType2ids['levelMgr']) == 1
|
||||
|
|
@ -83,10 +74,8 @@ class Level:
|
|||
del self.createdEntities
|
||||
if hasattr(self, 'entities'):
|
||||
del self.entities
|
||||
if hasattr(self, 'entId2spec'):
|
||||
del self.entId2spec
|
||||
if hasattr(self, 'spec'):
|
||||
del self.spec
|
||||
if hasattr(self, 'levelSpec'):
|
||||
del self.levelSpec
|
||||
|
||||
def createEntityCreator(self):
|
||||
self.notify.error(
|
||||
|
|
@ -131,7 +120,7 @@ class Level:
|
|||
|
||||
def createEntity(self, entId):
|
||||
assert not self.entities.has_key(entId)
|
||||
spec = self.entId2spec[entId]
|
||||
spec = self.levelSpec.getEntitySpec(entId)
|
||||
self.notify.debug('creating %s %s' % (spec['type'], entId))
|
||||
entity = self.entityCreator.createEntity(entId)
|
||||
# NOTE: the entity is not considered to really be created until
|
||||
|
|
@ -156,7 +145,7 @@ class Level:
|
|||
entities; this is called directly by Entity.
|
||||
"""
|
||||
entId = entity.entId
|
||||
spec = self.entId2spec[entId]
|
||||
spec = self.levelSpec.getEntitySpec(entId)
|
||||
# on initialization, set items directly on entity
|
||||
for key,value in spec.items():
|
||||
if key in ('type', 'name', 'comment',):
|
||||
|
|
@ -170,7 +159,7 @@ class Level:
|
|||
return self.entities.get(entId)
|
||||
|
||||
def getEntityType(self, entId):
|
||||
return self.entId2spec[entId]['type']
|
||||
return self.levelSpec.getEntityType(entId)
|
||||
|
||||
def getZoneId(self, dummy=None, zoneNum=None, entId=None):
|
||||
"""look up network zoneId by zoneNum or entId"""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
"""LevelSpec module: contains the LevelSpec class"""
|
||||
|
||||
from PythonUtil import list2dict
|
||||
import string
|
||||
|
||||
class LevelSpec:
|
||||
"""contains spec data for a level, is responsible for handing the data
|
||||
out upon request, as well as recording changes made during editing, and
|
||||
saving out modified spec data"""
|
||||
def __init__(self, specDict, scenario=0):
|
||||
self.specDict = specDict
|
||||
|
||||
# this maps an entId to the dict that holds its spec;
|
||||
# entities are either in the global dict or a scenario dict
|
||||
# update the map of entId to spec dict
|
||||
self.entId2specDict = {}
|
||||
self.entId2specDict.update(
|
||||
list2dict(self.getGlobalEntIds(),
|
||||
value=self.privGetGlobalEntityDict()))
|
||||
for i in range(self.getNumScenarios()):
|
||||
self.entId2specDict.update(
|
||||
list2dict(self.getScenarioEntIds(i),
|
||||
value=self.privGetScenarioEntityDict(i)))
|
||||
|
||||
self.setScenario(scenario)
|
||||
|
||||
def getNumScenarios(self):
|
||||
return len(self.specDict['scenarios'])
|
||||
|
||||
def getScenarioWeights(self):
|
||||
weights = []
|
||||
for entry in self.specDict['scenarios']:
|
||||
weights.append(entry[1])
|
||||
return weights
|
||||
|
||||
def setScenario(self, scenario):
|
||||
assert scenario in range(0, self.getNumScenarios())
|
||||
self.scenario = scenario
|
||||
|
||||
def getScenario(self):
|
||||
return self.scenario
|
||||
|
||||
def getGlobalEntIds(self):
|
||||
return self.privGetGlobalEntityDict().keys()
|
||||
|
||||
def getScenarioEntIds(self, scenario=None):
|
||||
if scenario is None:
|
||||
scenario = self.scenario
|
||||
return self.privGetScenarioEntityDict(scenario).keys()
|
||||
|
||||
def getAllEntIds(self):
|
||||
return self.getGlobalEntIds() + self.getScenarioEntIds()
|
||||
|
||||
def getEntitySpec(self, entId):
|
||||
assert entId in self.entId2specDict
|
||||
specDict = self.entId2specDict[entId]
|
||||
return specDict[entId]
|
||||
|
||||
def getEntityType(self, entId):
|
||||
return self.getEntitySpec(entId)['type']
|
||||
|
||||
def getEntType2ids(self, entIds):
|
||||
"""given list of entIds, return dict of entType 2 entIds"""
|
||||
entType2ids = {}
|
||||
for entId in entIds:
|
||||
type = self.getEntityType(entId)
|
||||
entType2ids.setdefault(type, [])
|
||||
entType2ids[type].append(entId)
|
||||
return entType2ids
|
||||
|
||||
# private support functions to abstract dict structure
|
||||
def privGetGlobalEntityDict(self):
|
||||
return self.specDict['globalEntities']
|
||||
|
||||
def privGetScenarioEntityDict(self, scenario):
|
||||
return self.specDict['scenarios'][scenario][0]
|
||||
|
||||
if __debug__:
|
||||
def setAttribChange(self, entId, attrib, value):
|
||||
pass
|
||||
|
||||
def getSpecImportsModuleName(self):
|
||||
# name of module that should be imported by spec py file
|
||||
return 'SpecImports'
|
||||
|
||||
def getPrettyString(self):
|
||||
"""Returns a string that contains the spec data, nicely formatted.
|
||||
This should be used when writing the spec out to file."""
|
||||
import pprint
|
||||
|
||||
tabWidth = 4
|
||||
tab = ' ' * tabWidth
|
||||
# structure names
|
||||
globalEntitiesName = 'GlobalEntities'
|
||||
scenarioEntitiesName = 'Scenario%s'
|
||||
scenarioWeightName = 'Scenarios'
|
||||
topLevelName = 'levelSpec'
|
||||
def getPrettyEntityDictStr(name, dict, tabs=0):
|
||||
def t(n):
|
||||
return (tabs+n)*tab
|
||||
def sortList(lst, firstElements=[]):
|
||||
"""sort list; elements in firstElements will be put
|
||||
first, in the order that they appear in firstElements;
|
||||
rest of elements will follow, sorted"""
|
||||
elements = list(lst)
|
||||
# put elements in order
|
||||
result = []
|
||||
for el in firstElements:
|
||||
if el in elements:
|
||||
result.append(el)
|
||||
elements.remove(el)
|
||||
elements.sort()
|
||||
result.extend(elements)
|
||||
return result
|
||||
|
||||
firstTypes = ('levelMgr', 'zone',)
|
||||
firstAttribs = ('type', 'name', 'comment', 'parent',
|
||||
'pos', 'x', 'y', 'z',
|
||||
'hpr', 'h', 'p', 'r',
|
||||
'scale', 'sx', 'sy', 'sz',
|
||||
'color',
|
||||
'model',
|
||||
)
|
||||
str = t(0)+'%s = {\n' % name
|
||||
# get list of types
|
||||
entIds = dict.keys()
|
||||
entType2ids = self.getEntType2ids(entIds)
|
||||
# put types in order
|
||||
types = sortList(entType2ids.keys(), firstTypes)
|
||||
for type in types:
|
||||
str += t(1)+'# %s\n' % string.upper(type)
|
||||
entIds = entType2ids[type]
|
||||
entIds.sort()
|
||||
for entId in entIds:
|
||||
str += t(1)+'%s: {\n' % entId
|
||||
spec = dict[entId]
|
||||
attribs = sortList(spec.keys(), firstAttribs)
|
||||
for attrib in attribs:
|
||||
str += t(2)+"'%s': %s,\n" % (attrib,
|
||||
repr(spec[attrib]))
|
||||
str += t(2)+'},\n'
|
||||
|
||||
str += t(1)+'}\n'
|
||||
return str
|
||||
def getPrettyScenarioWeightTableStr(tabs=0, self=self):
|
||||
def t(n):
|
||||
return (tabs+n)*tab
|
||||
str = t(0)+'%s = [\n' % scenarioWeightName
|
||||
for i in range(self.getNumScenarios()):
|
||||
str += t(1)+'[%s, %s],\n' % (scenarioEntitiesName % i,
|
||||
self.getScenarioWeights()[i])
|
||||
str += t(1)+']\n'
|
||||
return str
|
||||
def getPrettyTopLevelDictStr(tabs=0):
|
||||
def t(n):
|
||||
return (tabs+n)*tab
|
||||
str = t(0)+'%s = {\n' % topLevelName
|
||||
str += t(1)+"'globalEntities': %s,\n" % globalEntitiesName
|
||||
str += t(1)+"'scenarios': %s,\n" % scenarioWeightName
|
||||
str += t(1)+'}\n'
|
||||
return str
|
||||
|
||||
str = 'from %s import *\n' % self.getSpecImportsModuleName()
|
||||
str += '\n'
|
||||
|
||||
# add the global entities
|
||||
str += getPrettyEntityDictStr('GlobalEntities',
|
||||
self.privGetGlobalEntityDict())
|
||||
str += '\n'
|
||||
|
||||
# add the scenario entities
|
||||
numScenarios = self.getNumScenarios()
|
||||
for i in range(numScenarios):
|
||||
str += getPrettyEntityDictStr('Scenario%s' % i,
|
||||
self.privGetScenarioEntityDict(i))
|
||||
str += '\n'
|
||||
|
||||
# add the scenario weight table
|
||||
str += getPrettyScenarioWeightTableStr()
|
||||
str += '\n'
|
||||
|
||||
# add the top-level table
|
||||
str += getPrettyTopLevelDictStr()
|
||||
|
||||
self.testPrettyString(prettyString=str)
|
||||
|
||||
return str
|
||||
|
||||
def testPrettyString(self, prettyString=None):
|
||||
# execute the pretty output in our local scope
|
||||
if prettyString is None:
|
||||
prettyString=self.getPrettyString()
|
||||
exec(prettyString)
|
||||
assert levelSpec == self.specDict, (
|
||||
'LevelSpec pretty string does not match spec data.\n'
|
||||
'pretty=%s\n'
|
||||
'specData=%s' %
|
||||
(levelSpec, self.specDict)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return 'LevelSpec(%s, scenario=%s)' % (repr(self.specDict),
|
||||
self.scenario)
|
||||
Loading…
Reference in New Issue