def request_chunk(self, x, z): """ Request a ``Chunk`` to be delivered later. :returns: ``Deferred`` that will be called with the ``Chunk`` """ # First, try the cache. cached = self._cache.get((x, z)) if cached is not None: returnValue(cached) # Is it pending? if (x, z) in self._pending_chunks: # Rig up another Deferred and wrap it up in a to-go box. retval = yield self._pending_chunks[x, z].deferred() returnValue(retval) # Create a new chunk object, since the cache turned up empty. try: chunk = yield maybeDeferred(self.serializer.load_chunk, x, z) except SerializerReadException: # Looks like the chunk wasn't already on disk. Guess we're gonna # need to keep going. chunk = Chunk(x, z) # Add in our magic dirtiness hook so that the cache can be aware of # chunks who have been...naughty. chunk.dirtied = self._cache.dirtied if chunk.dirty: # The chunk was already dirty!? Oh, naughty indeed! self._cache.dirtied(chunk) if chunk.populated: self._cache.put(chunk) self.postprocess_chunk(chunk) if self.factory: self.factory.scan_chunk(chunk) returnValue(chunk) if self. async: from ampoule import deferToAMPProcess from bravo.remote import MakeChunk generators = [plugin.name for plugin in self.pipeline] d = deferToAMPProcess(MakeChunk, x=x, z=z, seed=self.level.seed, generators=generators) # Get chunk data into our chunk object. def fill_chunk(kwargs): chunk.blocks = array("B") chunk.blocks.fromstring(kwargs["blocks"]) chunk.heightmap = array("B") chunk.heightmap.fromstring(kwargs["heightmap"]) chunk.metadata = array("B") chunk.metadata.fromstring(kwargs["metadata"]) chunk.skylight = array("B") chunk.skylight.fromstring(kwargs["skylight"]) chunk.blocklight = array("B") chunk.blocklight.fromstring(kwargs["blocklight"]) return chunk d.addCallback(fill_chunk) else: # Populate the chunk the slow way. :c for stage in self.pipeline: stage.populate(chunk, self.level.seed) chunk.regenerate() d = succeed(chunk) # Set up our event and generate our return-value Deferred. It has to # be done early becaues PendingEvents only fire exactly once and it # might fire immediately in certain cases. pe = PendingEvent() # This one is for our return value. retval = pe.deferred() # This one is for scanning the chunk for automatons. if self.factory: pe.deferred().addCallback(self.factory.scan_chunk) self._pending_chunks[x, z] = pe def pp(chunk): chunk.populated = True chunk.dirty = True self.postprocess_chunk(chunk) self._cache.dirtied(chunk) del self._pending_chunks[x, z] return chunk # Set up callbacks. d.addCallback(pp) d.chainDeferred(pe) # Because multiple people might be attached to this callback, we're # going to do something magical here. We will yield a forked version # of our Deferred. This means that we will wait right here, for a # long, long time, before actually returning with the chunk, *but*, # when we actually finish, we'll be ready to return the chunk # immediately. Our caller cannot possibly care because they only see a # Deferred either way. retval = yield retval returnValue(retval)