<commit_before>"""DistributedLevel.py: contains the DistributedLevel class"""

from ClockDelta import *
from PythonUtil import Functor, sameElements, list2dict, uniqueElements
import ToontownGlobals
import DistributedObject
import Level
import LevelConstants
import DirectNotifyGlobal
import EntityCreator
import OnscreenText
import Task
import LevelUtil

class DistributedLevel(DistributedObject.DistributedObject,
                       Level.Level):
    """DistributedLevel"""
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevel')

    WantVisibility = config.GetBool('level-visibility', 1)
    HideZones = config.GetBool('level-hidezones', 1)

    # TODO: move level-model stuff to LevelMgr or FactoryLevelMgr?
    FloorCollPrefix = 'zoneFloor'

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        Level.Level.__init__(self)
        self.lastToonZone = 0
        self.titleColor = (1,1,1,1)
        self.titleText = OnscreenText.OnscreenText(
            "",
            fg = self.titleColor,
            shadow = (0,0,0,1),
            font = ToontownGlobals.getSuitFont(),
            pos = (0,-0.5),
            scale = 0.16,
            drawOrder = 0,
            mayChange = 1,
            )

        self.smallTitleText = OnscreenText.OnscreenText(
            "",
            fg = self.titleColor,
            font = ToontownGlobals.getSuitFont(),
            pos = (0.65,0.9),
            scale = 0.08,
            drawOrder = 0,
            mayChange = 1,
            bg = (.5,.5,.5,.5),
            align = TextNode.ARight,
            )
        self.zonesEnteredList = []

    def generate(self):
        DistributedLevel.notify.debug('generate')
        DistributedObject.DistributedObject.generate(self)

        # this dict stores entity reparents if the parent hasn't been
        # created yet
        self.parent2ChildIds = {}

        # if the AI sends us a full spec, it will be put here
        self.curSpec = None

        # Most (if not all) of the timed entities of levels
        # run on looping intervals that are started once based on
        # the level's start time.
        # This sync request is *NOT* guaranteed to finish by the time
        # the entities get created.
        # We should listen for any and all time-sync events and re-sync
        # all our entities at that time.
        toonbase.tcr.timeManager.synchronize('DistributedLevel.generate')

        # add factory menu to SpeedChat
        toonbase.localToon.chatMgr.chatInputSpeedChat.addFactoryMenu()

    # the real required fields
    def setLevelZoneId(self, zoneId):
        # this is the zone that the level is in; we should listen to this
        # zone the entire time we're in here
        self.levelZone = zoneId

    def setPlayerIds(self, avIdList):
        self.avIdList = avIdList
        assert toonbase.localToon.doId in self.avIdList

    def setEntranceId(self, entranceId):
        self.entranceId = entranceId

    def getEntranceId(self):
        return self.entranceId

    # "required" fields (these ought to be required fields, but
    # the AI level obj doesn't know the data values until it has been
    # generated.)
    def setZoneIds(self, zoneIds):
        DistributedLevel.notify.debug('setZoneIds: %s' % zoneIds)
        self.zoneIds = zoneIds

    def setStartTimestamp(self, timestamp):
        DistributedLevel.notify.debug('setStartTimestamp: %s' % timestamp)
        self.startTime = globalClockDelta.networkToLocalTime(timestamp,bits=32)

    def setScenarioIndex(self, scenarioIndex):
        self.scenarioIndex = scenarioIndex

        # ugly hack: we treat a few DC fields as if they were required,
        # and use 'levelAnnounceGenerate()' in place of regular old
        # 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, give the server the opportunity to send us
            # a full spec
            self.candidateSpec = levelSpec
            self.sendUpdate('requestCurrentLevelSpec',
                            [hash(levelSpec),
                             hash(levelSpec.entTypeReg)])
        else:
            self.privGotSpec(levelSpec)

    if __debug__:
        def setSpecDeny(self, reason):
            DistributedLevel.notify.error(reason)
            
        def setSpecSenderDoId(self, 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
                spec = eval(specBlob)
                if spec is None:
                    spec = self.candidateSpec
                del self.candidateSpec
                self.privGotSpec(spec)

            if blobSender.isComplete():
                setSpecBlob(blobSender.getBlob())
            else:
                evtName = self.uniqueName('specDone')
                blobSender.setDoneEvent(evtName)
                self.acceptOnce(evtName, setSpecBlob)

    def privGotSpec(self, levelSpec):
        Level.Level.initializeLevel(self, self.doId, levelSpec,
                                    self.scenarioIndex)

        # all of the local entities have been created now.
        # TODO: have any of the distributed entities been created at this point?

        # there should not be any pending reparents left at this point
        # TODO: is it possible for a local entity to be parented to a
        # distributed entity? I think so!
        assert len(self.parent2ChildIds) == 0
        # make sure the zoneNums from the model match the zoneNums from
        # the zone entities
        modelZoneNums = self.zoneNums
        entityZoneNums = self.zoneNum2entId.keys()
        if not sameElements(modelZoneNums, entityZoneNums):
            DistributedLevel.notify.error(
                'model zone nums (%s) do not match entity zone nums (%s)\n'
                'use SpecUtil.updateSpec' %
                (modelZoneNums, entityZoneNums))

        # load stuff
        self.initVisibility()

        self.placeLocalToon()

    def placeLocalToon(self):
        # the entrancePoint entities register themselves with us
        if self.entranceId not in self.entranceId2entity:
            self.notify.warning('unknown entranceId %s' % self.entranceId)
            toonbase.localToon.setPos(0,0,0)
        else:
            epEnt = self.entranceId2entity[self.entranceId]
            epEnt.placeToon(toonbase.localToon,
                            self.avIdList.index(toonbase.localToon.doId),
                            len(self.avIdList))
<commit_msg>closer to a clean factory entrance sequence<commit_after>"""DistributedLevel.py: contains the DistributedLevel class"""

from ClockDelta import *
from PythonUtil import Functor, sameElements, list2dict, uniqueElements
import ToontownGlobals
import DistributedObject
import Level
import LevelConstants
import DirectNotifyGlobal
import EntityCreator
import OnscreenText
import Task
import LevelUtil

class DistributedLevel(DistributedObject.DistributedObject,
                       Level.Level):
    """DistributedLevel"""
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevel')

    WantVisibility = config.GetBool('level-visibility', 1)
    HideZones = config.GetBool('level-hidezones', 1)

    # TODO: move level-model stuff to LevelMgr or FactoryLevelMgr?
    FloorCollPrefix = 'zoneFloor'

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        Level.Level.__init__(self)
        self.lastToonZone = 0
        self.titleColor = (1,1,1,1)
        self.titleText = OnscreenText.OnscreenText(
            "",
            fg = self.titleColor,
            shadow = (0,0,0,1),
            font = ToontownGlobals.getSuitFont(),
            pos = (0,-0.5),
            scale = 0.16,
            drawOrder = 0,
            mayChange = 1,
            )

        self.smallTitleText = OnscreenText.OnscreenText(
            "",
            fg = self.titleColor,
            font = ToontownGlobals.getSuitFont(),
            pos = (0.65,0.9),
            scale = 0.08,
            drawOrder = 0,
            mayChange = 1,
            bg = (.5,.5,.5,.5),
            align = TextNode.ARight,
            )
        self.zonesEnteredList = []

    def generate(self):
        DistributedLevel.notify.debug('generate')
        DistributedObject.DistributedObject.generate(self)

        # this dict stores entity reparents if the parent hasn't been
        # created yet
        self.parent2ChildIds = {}

        # if the AI sends us a full spec, it will be put here
        self.curSpec = None

        # Most (if not all) of the timed entities of levels
        # run on looping intervals that are started once based on
        # the level's start time.
        # This sync request is *NOT* guaranteed to finish by the time
        # the entities get created.
        # We should listen for any and all time-sync events and re-sync
        # all our entities at that time.
        toonbase.tcr.timeManager.synchronize('DistributedLevel.generate')

        # add factory menu to SpeedChat
        toonbase.localToon.chatMgr.chatInputSpeedChat.addFactoryMenu()

    # the real required fields
    def setLevelZoneId(self, zoneId):
        # this is the zone that the level is in; we should listen to this
        # zone the entire time we're in here
        self.levelZone = zoneId

    def setPlayerIds(self, avIdList):
        self.avIdList = avIdList
        assert toonbase.localToon.doId in self.avIdList

    def setEntranceId(self, entranceId):
        self.entranceId = entranceId

    def getEntranceId(self):
        return self.entranceId

    # "required" fields (these ought to be required fields, but
    # the AI level obj doesn't know the data values until it has been
    # generated.)
    def setZoneIds(self, zoneIds):
        DistributedLevel.notify.debug('setZoneIds: %s' % zoneIds)
        self.zoneIds = zoneIds

    def setStartTimestamp(self, timestamp):
        DistributedLevel.notify.debug('setStartTimestamp: %s' % timestamp)
        self.startTime = globalClockDelta.networkToLocalTime(timestamp,bits=32)

    def setScenarioIndex(self, scenarioIndex):
        self.scenarioIndex = scenarioIndex

        # ugly hack: we treat a few DC fields as if they were required,
        # and use 'levelAnnounceGenerate()' in place of regular old
        # 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, give the server the opportunity to send us
            # a full spec
            self.candidateSpec = levelSpec
            self.sendUpdate('requestCurrentLevelSpec',
                            [hash(levelSpec),
                             hash(levelSpec.entTypeReg)])
        else:
            self.privGotSpec(levelSpec)

    if __debug__:
        def setSpecDeny(self, reason):
            DistributedLevel.notify.error(reason)
            
        def setSpecSenderDoId(self, 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
                spec = eval(specBlob)
                if spec is None:
                    spec = self.candidateSpec
                del self.candidateSpec
                self.privGotSpec(spec)

            if blobSender.isComplete():
                setSpecBlob(blobSender.getBlob())
            else:
                evtName = self.uniqueName('specDone')
                blobSender.setDoneEvent(evtName)
                self.acceptOnce(evtName, setSpecBlob)

    def privGotSpec(self, levelSpec):
        Level.Level.initializeLevel(self, self.doId, levelSpec,
                                    self.scenarioIndex)

        # all of the local entities have been created now.
        # TODO: have any of the distributed entities been created at this point?

        # there should not be any pending reparents left at this point
        # TODO: is it possible for a local entity to be parented to a
        # distributed entity? I think so!
        assert len(self.parent2ChildIds) == 0
        # make sure the zoneNums from the model match the zoneNums from
        # the zone entities
        modelZoneNums = self.zoneNums
        entityZoneNums = self.zoneNum2entId.keys()
        if not sameElements(modelZoneNums, entityZoneNums):
            DistributedLevel.notify.error(
                'model zone nums (%s) do not match entity zone nums (%s)\n'
                'use SpecUtil.updateSpec' %
                (modelZoneNums, entityZoneNums))

        # load stuff
        self.initVisibility()
        self.placeLocalToon()

    def placeLocalToon(self):
        # the entrancePoint entities register themselves with us
        if self.entranceId not in self.entranceId2entity:
            self.notify.warning('unknown entranceId %s' % self.entranceId)
            toonbase.localToon.setPos(0,0,0)
        else:
            epEnt = self.entranceId2entity[self.entranceId]
            epEnt.placeToon(toonbase.localToon,
                            self.avIdList.index(toonbase.localToon.doId),
                            len(self.avIdList))
            # kickstart the visibility
            firstZoneEnt = self.getEntity(epEnt.getZoneEntId())
            self.enterZone(firstZoneEnt.getZoneNum())

{'fraction_non_alphanumeric': 0.07706019470725353, 'fraction_numerical': 0.006307418072123955, 'mean_word_length': 2.160095320623917, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6184267', 'n_tokens_mistral': 4032, 'n_tokens_neox': 3894, 'n_words': 1128}