180 lines
7.3 KiB
Python
180 lines
7.3 KiB
Python
from panda3d.core import *
|
|
from otp.ai.AIBaseGlobal import *
|
|
from direct.distributed.ClockDelta import *
|
|
from direct.distributed import DistributedObjectAI
|
|
from . import Level
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from . import EntityCreatorAI
|
|
from direct.showbase.PythonUtil import Functor, weightedChoice
|
|
|
|
class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, Level.Level):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelAI')
|
|
|
|
def __init__(self, air, zoneId, entranceId, avIds):
|
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
|
Level.Level.__init__(self)
|
|
self.zoneId = zoneId
|
|
self.entranceId = entranceId
|
|
if len(avIds) <= 0 or len(avIds) > 4:
|
|
self.notify.warning('How do we have this many avIds? avIds: %s' % avIds)
|
|
self.avIdList = avIds
|
|
self.numPlayers = len(self.avIdList)
|
|
self.presentAvIds = list(self.avIdList)
|
|
self.notify.debug('expecting avatars: %s' % str(self.avIdList))
|
|
if __dev__:
|
|
self.modified = 0
|
|
|
|
def setLevelSpec(self, levelSpec):
|
|
self.levelSpec = levelSpec
|
|
|
|
def generate(self, levelSpec=None):
|
|
self.notify.debug('generate')
|
|
DistributedObjectAI.DistributedObjectAI.generate(self)
|
|
if levelSpec == None:
|
|
levelSpec = self.levelSpec
|
|
self.initializeLevel(levelSpec)
|
|
self.sendUpdate('setZoneIds', [self.zoneIds])
|
|
self.sendUpdate('setStartTimestamp', [self.startTimestamp])
|
|
if __dev__:
|
|
pass
|
|
return
|
|
|
|
def getLevelZoneId(self):
|
|
return self.zoneId
|
|
|
|
def getPlayerIds(self):
|
|
return self.avIdList
|
|
|
|
def getEntranceId(self):
|
|
return self.entranceId
|
|
|
|
def getBattleCreditMultiplier(self):
|
|
return 1.0
|
|
|
|
def delete(self, deAllocZone=True):
|
|
self.notify.debug('delete')
|
|
if __dev__:
|
|
self.removeAutosaveTask()
|
|
self.destroyLevel()
|
|
self.ignoreAll()
|
|
if deAllocZone:
|
|
self.air.deallocateZone(self.zoneId)
|
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
|
|
|
def initializeLevel(self, levelSpec):
|
|
self.startTime = globalClock.getRealTime()
|
|
self.startTimestamp = globalClockDelta.localToNetworkTime(self.startTime, bits=32)
|
|
lol = list(zip([1] * levelSpec.getNumScenarios(), list(range(levelSpec.getNumScenarios()))))
|
|
scenarioIndex = weightedChoice(lol)
|
|
Level.Level.initializeLevel(self, self.doId, levelSpec, scenarioIndex)
|
|
if __dev__:
|
|
self.accept(self.editMgrEntity.getSpecSaveEvent(), self.saveSpec)
|
|
for avId in self.avIdList:
|
|
self.acceptOnce(self.air.getAvatarExitEvent(avId), Functor(self.handleAvatarDisconnect, avId))
|
|
|
|
self.allToonsGoneBarrier = self.beginBarrier('allToonsGone', self.avIdList, 3 * 24 * 60 * 60, self.allToonsGone)
|
|
|
|
def handleAvatarDisconnect(self, avId):
|
|
try:
|
|
self.presentAvIds.remove(avId)
|
|
DistributedLevelAI.notify.warning('av %s has disconnected' % avId)
|
|
except:
|
|
DistributedLevelAI.notify.warning('got disconnect for av %s, not in list' % avId)
|
|
|
|
if not self.presentAvIds:
|
|
self.allToonsGone([])
|
|
|
|
def _levelControlsRequestDelete(self):
|
|
return True
|
|
|
|
def allToonsGone(self, toonsThatCleared):
|
|
DistributedLevelAI.notify.info('allToonsGone')
|
|
if hasattr(self, 'allToonsGoneBarrier'):
|
|
self.ignoreBarrier(self.allToonsGoneBarrier)
|
|
del self.allToonsGoneBarrier
|
|
for avId in self.avIdList:
|
|
self.ignore(self.air.getAvatarExitEvent(avId))
|
|
|
|
if self._levelControlsRequestDelete():
|
|
self.requestDelete()
|
|
|
|
def createEntityCreator(self):
|
|
return EntityCreatorAI.EntityCreatorAI(level=self)
|
|
|
|
def setOuch(self, penalty):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
av = self.air.doId2do.get(avId)
|
|
self.notify.debug('setOuch %s' % penalty)
|
|
if av and penalty > 0:
|
|
av.takeDamage(penalty)
|
|
if av.getHp() <= 0:
|
|
av.inventory.zeroInv()
|
|
av.d_setInventory(av.inventory.makeNetString())
|
|
|
|
def requestCurrentLevelSpec(self, specHash, entTypeRegHash):
|
|
senderId = self.air.getAvatarIdFromSender()
|
|
self.notify.info('av %s: specHash %s, entTypeRegHash %s' % (senderId, specHash, entTypeRegHash))
|
|
if not __dev__:
|
|
self.notify.info('client is in dev mode and we are not')
|
|
self.sendUpdateToAvatarId(senderId, 'setSpecDeny', [
|
|
'AI server is not running in dev mode. Set want-dev to false on your client or true on the AI.'])
|
|
return
|
|
srvHash = self.levelSpec.entTypeReg.getHashStr()
|
|
self.notify.info('srv entTypeRegHash %s' % srvHash)
|
|
if srvHash != entTypeRegHash:
|
|
self.sendUpdateToAvatarId(senderId, 'setSpecDeny', [
|
|
'EntityTypeRegistry hashes do not match! (server:%s, client:%s' % (srvHash, entTypeRegHash)])
|
|
return
|
|
if self.levelSpec.stringHash() != specHash:
|
|
self.notify.info('spec hashes do not match, sending our spec')
|
|
spec = self.levelSpec
|
|
useDisk = simbase.air._specByDisk
|
|
else:
|
|
self.notify.info('spec hashes match, sending null spec')
|
|
spec = None
|
|
useDisk = 0
|
|
specStr = repr(spec)
|
|
from direct.directutil import DistributedLargeBlobSenderAI
|
|
largeBlob = DistributedLargeBlobSenderAI.DistributedLargeBlobSenderAI(self.air, self.zoneId, senderId, specStr, useDisk=useDisk)
|
|
self.sendUpdateToAvatarId(senderId, 'setSpecSenderDoId', [largeBlob.doId])
|
|
return
|
|
|
|
if __dev__:
|
|
|
|
def setAttribChange(self, entId, attribName, value, username='SYSTEM'):
|
|
DistributedLevelAI.notify.info('setAttribChange(%s): %s, %s = %s' % (username, entId, attribName, repr(value)))
|
|
self.sendUpdate('setAttribChange', [
|
|
entId, attribName, repr(value), username])
|
|
self.levelSpec.setAttribChange(entId, attribName, value, username)
|
|
self.modified = 1
|
|
self.scheduleAutosave()
|
|
|
|
AutosavePeriod = simbase.config.GetFloat('level-autosave-period-minutes', 5)
|
|
|
|
def scheduleAutosave(self):
|
|
if hasattr(self, 'autosaveTask'):
|
|
return
|
|
self.autosaveTaskName = self.uniqueName('autosaveSpec')
|
|
self.autosaveTask = taskMgr.doMethodLater(DistributedLevelAI.AutosavePeriod * 60, self.autosaveSpec, self.autosaveTaskName)
|
|
|
|
def removeAutosaveTask(self):
|
|
if hasattr(self, 'autosaveTask'):
|
|
taskMgr.remove(self.autosaveTaskName)
|
|
del self.autosaveTask
|
|
|
|
def autosaveSpec(self, task=None):
|
|
self.removeAutosaveTask()
|
|
if self.modified:
|
|
DistributedLevelAI.notify.info('autosaving spec')
|
|
filename = self.levelSpec.getFilename()
|
|
filename = '%s.autosave' % filename
|
|
self.levelSpec.saveToDisk(filename, makeBackup=0)
|
|
|
|
def saveSpec(self, task=None):
|
|
DistributedLevelAI.notify.info('saving spec')
|
|
self.removeAutosaveTask()
|
|
if not self.modified:
|
|
DistributedLevelAI.notify.info('no changes to save')
|
|
return
|
|
self.levelSpec.saveToDisk()
|
|
self.modified = 0
|