From c27e85f89094a08aeb12e8e2c9c44aaa4d5eb36a Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Tue, 7 Oct 2003 02:26:54 +0000 Subject: [PATCH] levelAI only sends spec if hash is different, spec is saved by AI --- direct/src/level/DistributedLevel.py | 39 ++++++++++---------- direct/src/level/DistributedLevelAI.py | 32 ++++++++++++++--- direct/src/level/LevelSpec.py | 50 +++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 23 deletions(-) diff --git a/direct/src/level/DistributedLevel.py b/direct/src/level/DistributedLevel.py index 6827c79b9c..dea76124b9 100755 --- a/direct/src/level/DistributedLevel.py +++ b/direct/src/level/DistributedLevel.py @@ -98,24 +98,37 @@ class DistributedLevel(DistributedObject.DistributedObject, # announceGenerate(). Note that we have to call # gotAllRequired() in the last 'faux-required' DC update # handler. If you add another field, move this to the last one. + self.privGotAllRequired() + + def privGotAllRequired(self): + self.levelAnnounceGenerate() + def levelAnnounceGenerate(self): + pass + + 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.""" if __debug__: - # if we're in debug, ask the server if it wants to send us + # if we're in debug, give the server the opportunity to send us # a full spec - self.sendUpdate('requestCurrentLevelSpec', []) + self.candidateSpec = levelSpec + self.sendUpdate('requestCurrentLevelSpec', [hash(levelSpec)]) else: - self.gotAllRequired() + self.privGotSpec(levelSpec) if __debug__: def setSpecSenderDoId(self, doId): - DistributedLevel.notify.debug( - 'setSpecSenderDoId: %s' % doId) + DistributedLevel.notify.debug('setSpecSenderDoId: %s' % doId) blobSender = toonbase.tcr.doId2do[doId] def setSpecBlob(specBlob, blobSender=blobSender, self=self): blobSender.sendAck() from LevelSpec import LevelSpec - self.curSpec = eval(specBlob) - self.gotAllRequired() + spec = eval(specBlob) + if spec is None: + spec = self.candidateSpec + del self.candidateSpec + self.privGotSpec(spec) if blobSender.isComplete(): setSpecBlob(blobSender.getBlob()) @@ -124,17 +137,7 @@ class DistributedLevel(DistributedObject.DistributedObject, blobSender.setDoneEvent(evtName) self.acceptOnce(evtName, setSpecBlob) - def gotAllRequired(self): - self.levelAnnounceGenerate() - def levelAnnounceGenerate(self): - pass - - 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.""" - # if the AI sent us a full spec, use it instead - if self.curSpec is not None: - levelSpec = self.curSpec + def privGotSpec(self, levelSpec): Level.Level.initializeLevel(self, self.doId, levelSpec, self.scenarioIndex) diff --git a/direct/src/level/DistributedLevelAI.py b/direct/src/level/DistributedLevelAI.py index 6f0a78f4ff..64a2d0387c 100755 --- a/direct/src/level/DistributedLevelAI.py +++ b/direct/src/level/DistributedLevelAI.py @@ -18,7 +18,7 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, Level.Level.__init__(self) # this is one of the required fields self.zoneId = zoneId - self.hasBeenEdited = 0 + self.modified = 0 def generate(self, levelSpec): self.notify.debug('generate') @@ -38,6 +38,8 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, def delete(self): self.notify.debug('delete') + if __debug__: + self.saveSpec() self.destroyLevel() DistributedObjectAI.DistributedObjectAI.delete(self) @@ -91,11 +93,33 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, # send a copy to the client-side level obj self.sendUpdate('setAttribChange', [entId, attribName, valueStr]) - self.hasBeenEdited = 1 - def requestCurrentLevelSpec(self): + self.modified = 1 + self.scheduleSave() + + SavePeriod = simbase.config.GetFloat('factory-save-period', 10) + + def scheduleSave(self): + if hasattr(self, 'saveTask'): + return + self.saveTask = taskMgr.doMethodLater( + DistributedLevelAI.SavePeriod, + self.saveSpec, + self.uniqueName('saveSpec')) + + def saveSpec(self, task=None): + DistributedLevelAI.notify.info('saving spec') + if hasattr(self, 'saveTask'): + del self.saveTask + if self.modified: + self.levelSpec.saveToDisk() + self.modified = 0 + + def requestCurrentLevelSpec(self, specHash): senderId = self.air.msgSender - spec = self.levelSpec + spec = None + if hash(self.levelSpec) != specHash: + spec = self.levelSpec specStr = repr(spec) import DistributedLargeBlobSenderAI diff --git a/direct/src/level/LevelSpec.py b/direct/src/level/LevelSpec.py index 6ded3b8cfa..194793e93b 100755 --- a/direct/src/level/LevelSpec.py +++ b/direct/src/level/LevelSpec.py @@ -10,8 +10,9 @@ class LevelSpec: saving out modified spec data""" notify = DirectNotifyGlobal.directNotify.newCategory("LevelSpec") - def __init__(self, specDict, scenario=0, entTypeReg=None): + def __init__(self, specDict, scenario=0, filename=None, entTypeReg=None): self.specDict = specDict + self.filename = filename self.entTypeReg = entTypeReg # this maps an entId to the dict that holds its spec; @@ -141,6 +142,50 @@ class LevelSpec: # name of module that should be imported by spec py file return 'SpecImports' + def saveToDisk(self, filename=None): + """returns zero on failure""" + import os + + if filename is None: + filename = self.filename + + # create a backup + try: + # does the file exist? + exists = 0 + try: + os.stat(filename) + exists = 1 + except OSError: + pass + if exists: + def getBackupFilename(num, filename=filename): + return '%s.%03i' % (filename, num) + numBackups = 50 + try: + os.unlink(getBackupFilename(numBackups-1)) + except OSError: + pass + for i in range(numBackups-1,0,-1): + try: + os.rename(getBackupFilename(i-1), + getBackupFilename(i)) + except OSError: + pass + os.rename(filename, getBackupFilename(0)) + except OSError, e: + LevelSpec.notify.warning('error during backup: %s' % str(e)) + + retval = 1 + # wb to create a UNIX-format file + f = file(filename, 'wb') + try: + f.write(self.getPrettyString()) + except IOError: + retval = 0 + f.close() + return retval + def getPrettyString(self): """Returns a string that contains the spec data, nicely formatted. This should be used when writing the spec out to file.""" @@ -256,6 +301,9 @@ class LevelSpec: (levelSpec, self.specDict) ) + def __hash__(self): + return hash(repr(self)) + def __repr__(self): return 'LevelSpec(%s, scenario=%s)' % (repr(self.specDict), self.scenario)