oldschool-toontown/otp/level/DistributedLevelAI.py

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