def load_chunk(self, x, z): """ Retrieve a ``Chunk`` synchronously. This method does lots of automatic caching of chunks to ensure that disk I/O is kept to a minimum. """ if (x, z) in self.chunk_cache: return self.chunk_cache[x, z] elif (x, z) in self.dirty_chunk_cache: return self.dirty_chunk_cache[x, z] chunk = Chunk(x, z) self.serializer.load_chunk(chunk) if chunk.populated: self.chunk_cache[x, z] = chunk else: self.populate_chunk(chunk) chunk.populated = True chunk.dirty = True self.dirty_chunk_cache[x, z] = chunk self.postprocess_chunk(chunk) return chunk
def load_chunk(self, x, z): """ As the bot goes through the world, it would be helpful for it to keep records of chunks it has been to. """ if (x, z) in self.chunk_cache: return self.chunk_cache[x, z] elif (x, z) in self.dirty_chunk_cache: return self.dirty_chunk_cache[x, z] # chuck is not in memory, check if it is on the disk chunk = Chunk(x, z) first, second, filename = names_for_chunk(x, z) f = self.folder.child(first).child(second) if not f.exists(): f.makedirs() f = f.child(filename) if f.exists() and f.getsize(): chunk.load_from_tag(read_from_file(f.open("r"))) self.chunk_cache[x, z] = chunk return chunk print "attempting to load a chunk that is not available" raise Exception
def setUp(self): self.f = FallablesMockFactory() self.p = retrieve_plugins(IDigHook, parameters={"factory": self.f}) if "alpha_sand_gravel" not in self.p: raise unittest.SkipTest("Plugin not present") self.hook = self.p["alpha_sand_gravel"] self.c = Chunk(0, 0)
def test_ascend_one_up(self): """ ``ascend()`` moves players upwards. """ self.p.location.pos = self.p.location.pos._replace(y=16) c = Chunk(0, 0) c.set_block((0, 0, 0), 1) c.set_block((0, 1, 0), 1) self.p.chunks[0, 0] = c self.p.ascend(1) self.assertEqual(self.p.location.pos.y, 32)
def test_ascend_zero(self): """ ``ascend()`` can take a count of zero to ensure that the client is standing on solid ground. """ self.p.location.pos = self.p.location.pos._replace(y=16) c = Chunk(0, 0) c.set_block((0, 0, 0), 1) self.p.chunks[0, 0] = c self.p.ascend(0) self.assertEqual(self.p.location.pos.y, 16)
def test_ascend_zero_up(self): """ Even with a zero count, ``ascend()`` will move the player to the correct elevation. """ self.p.location.pos = self.p.location.pos._replace(y=16) c = Chunk(0, 0) c.set_block((0, 0, 0), 1) c.set_block((0, 1, 0), 1) self.p.chunks[0, 0] = c self.p.ascend(0) self.assertEqual(self.p.location.pos.y, 32)
def test_save_chunk_to_tag(self): chunk = Chunk(1, 2) tag = self.s._save_chunk_to_tag(chunk) self.assertTrue("xPos" in tag["Level"]) self.assertTrue("zPos" in tag["Level"]) self.assertEqual(tag["Level"]["xPos"].value, 1) self.assertEqual(tag["Level"]["zPos"].value, 2)
def test_load_chunk_first(self): """ Loading a non-existent chunk raises an SRE. """ self.assertRaises(SerializerReadException, self.s.load_chunk, Chunk(0, 0))
def test_single_block_damage_packet(self): chunk = Chunk(2, 1) chunk.populated = True chunk.set_block((2, 4, 8), 1) chunk.set_metadata((2, 4, 8), 2) packet = chunk.get_damage_packet() self.assertEqual(packet, '\x35\x00\x00\x00\x22\x04\x00\x00\x00\x18\x01\x02')
def empty_chunk(): before = time.time() for i in range(10): Chunk(i, i) after = time.time() return after - before
def sequential_seeded(p): before = time.time() for i in range(10): chunk = Chunk(i, i) p.populate(chunk, i) after = time.time() return after - before
def repeated_seeds(p): before = time.time() for i in range(10): chunk = Chunk(i, i) p.populate(chunk, 0) after = time.time() return after - before
def make_chunk(self, x, z, seed, generators): """ Create a chunk using the given parameters. """ generators = retrieve_sorted_plugins(ITerrainGenerator, generators) chunk = Chunk(x, z) for stage in generators: stage.populate(chunk, seed) chunk.regenerate() return { "blocks": chunk.blocks.tostring(), "metadata": chunk.metadata.tostring(), "skylight": chunk.skylight.tostring(), "blocklight": chunk.blocklight.tostring(), "heightmap": chunk.heightmap.tostring(), }
def pipeline(): generators = configuration.getlist("bravo", "generators") generators = retrieve_named_plugins(ITerrainGenerator, generators) before = time.time() for i in range(10): chunk = Chunk(i, i) for generator in generators: generator.populate(chunk, 0) after = time.time() return after - before
def load_chunk(self, x, z): name = name_for_anvil(x, z) fp = self.folder.child("region").child(name) region = Region(fp) chunk = Chunk(x, z) try: data = region.get_chunk(x, z) tag = NBTFile(buffer=StringIO(data)) self._load_chunk_from_tag(chunk, tag) except MissingChunk: raise SerializerReadException("No chunk %r in region" % chunk) except Exception, e: raise SerializerReadException("%r couldn't be loaded: %s" % (chunk, e))
def pipeline(): generators = beta["generators"] generators = retrieve_named_plugins(ITerrainGenerator, generators) before = time.time() for i in range(10): chunk = Chunk(i, i) for generator in generators: generator.populate(chunk, 0) after = time.time() return after - before
def full_chunk_test(): # compare two full chunks chunk1 = Chunk(0, 0) chunk2 = Chunk(0, 0) randomize_chunk(chunk1) packet1 = chunk1.save_to_packet() p, l = parse_packets(packet1) header, payload = p[0] chunk2.load_from_packet(payload) packet2 = chunk2.save_to_packet() compare_chunks(chunk1, chunk2)
def test_get_damage_packet_single(self): # Create a chunk. c = Chunk(0, 0) # Damage the block. c.populated = True c.set_block((0, 0, 0), 1) # Enable warning-to-error for DeprecationWarning, then see whether # retrieving damage causes a warning-to-error to be raised. (It # shouldn't.) warnings.simplefilter("error", DeprecationWarning) c.get_damage_packet() # ...And reset the warning filters. warnings.resetwarnings()
def load_chunk(self, x, z): """ Retrieve a ``Chunk`` synchronously. This method does lots of automatic caching of chunks to ensure that disk I/O is kept to a minimum. """ if (x, z) in self.chunk_cache: return self.chunk_cache[x, z] elif (x, z) in self.dirty_chunk_cache: return self.dirty_chunk_cache[x, z] chunk = Chunk(x, z) first, second, filename = names_for_chunk(x, z) f = self.folder.child(first).child(second) if not f.exists(): f.makedirs() f = f.child(filename) if f.exists() and f.getsize(): chunk.load_from_tag(read_from_file(f.open("r"))) if chunk.populated: self.chunk_cache[x, z] = chunk else: self.populate_chunk(chunk) chunk.populated = True chunk.dirty = True self.dirty_chunk_cache[x, z] = chunk # Apply the current season to the chunk. if self.season: self.season.transform(chunk) # Since this chunk hasn't been given to any player yet, there's no # conceivable way that any meaningful damage has been accumulated; # anybody loading any part of this chunk will want the entire thing. # Thus, it should start out undamaged. chunk.clear_damage() return chunk
def setUp(self): self.hook = Winter() self.c = Chunk(0, 0)
def request_chunk(self, x, z): """ Request a ``Chunk`` to be delivered later. :returns: ``Deferred`` that will be called with the ``Chunk`` """ if (x, z) in self.chunk_cache: returnValue(self.chunk_cache[x, z]) elif (x, z) in self.dirty_chunk_cache: returnValue(self.dirty_chunk_cache[x, z]) elif (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) chunk = Chunk(x, z) yield maybeDeferred(self.serializer.load_chunk, chunk) if chunk.populated: self.chunk_cache[x, z] = chunk self.postprocess_chunk(chunk) #self.factory.scan_chunk(chunk) returnValue(chunk) if self.async: from ampoule import deferToAMPProcess from bravo.remote import MakeChunk d = deferToAMPProcess(MakeChunk, x=x, z=z, seed=self.seed, generators=configuration.getlist(self.config_name, "generators") ) # Get chunk data into our chunk object. def fill_chunk(kwargs): chunk.blocks = fromstring(kwargs["blocks"], dtype=uint8).reshape(chunk.blocks.shape) chunk.heightmap = fromstring(kwargs["heightmap"], dtype=uint8).reshape(chunk.heightmap.shape) chunk.metadata = fromstring(kwargs["metadata"], dtype=uint8).reshape(chunk.metadata.shape) chunk.skylight = fromstring(kwargs["skylight"], dtype=uint8).reshape(chunk.skylight.shape) chunk.blocklight = fromstring(kwargs["blocklight"], dtype=uint8).reshape(chunk.blocklight.shape) return chunk d.addCallback(fill_chunk) else: # Populate the chunk the slow way. :c for stage in self.pipeline: stage.populate(chunk, self.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. #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.dirty_chunk_cache[x, z] = 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)
def setUp(self): self.c = Chunk(0, 0)
def setUp(self): self.chunk = Chunk(0, 0) self.p = bravo.plugin.retrieve_plugins(bravo.ibravo.ITerrainGenerator)
def repeated_seeds(i, p): chunk = Chunk(i, i) p.populate(chunk, 0)
def sequential_seeded(i, p): chunk = Chunk(i, i) p.populate(chunk, i)
def request_chunk(self, x, z): """ Request a ``Chunk`` to be delivered later. :returns: ``Deferred`` that will be called with the ``Chunk`` """ if (x, z) in self.chunk_cache: returnValue(self.chunk_cache[x, z]) elif (x, z) in self.dirty_chunk_cache: returnValue(self.dirty_chunk_cache[x, z]) elif (x, z) in self._pending_chunks: # Rig up another Deferred and wrap it up in a to-go box. retval = yield fork_deferred(self._pending_chunks[x, z]) returnValue(retval) chunk = Chunk(x, z) yield maybeDeferred(self.serializer.load_chunk, chunk) if chunk.populated: self.chunk_cache[x, z] = chunk self.postprocess_chunk(chunk) returnValue(chunk) if self. async: from ampoule import deferToAMPProcess from bravo.remote import MakeChunk d = deferToAMPProcess(MakeChunk, x=x, z=z, seed=self.seed, generators=configuration.getlist( self.config_name, "generators")) self._pending_chunks[x, z] = d # Get chunk data into our chunk object. def fill_chunk(kwargs): chunk.blocks = fromstring( kwargs["blocks"], dtype=uint8).reshape(chunk.blocks.shape) chunk.heightmap = fromstring(kwargs["heightmap"], dtype=uint8).reshape( chunk.heightmap.shape) chunk.metadata = fromstring(kwargs["metadata"], dtype=uint8).reshape( chunk.metadata.shape) chunk.skylight = fromstring(kwargs["skylight"], dtype=uint8).reshape( chunk.skylight.shape) chunk.blocklight = fromstring(kwargs["blocklight"], dtype=uint8).reshape( chunk.blocklight.shape) return chunk d.addCallback(fill_chunk) else: self.populate_chunk(chunk) d = succeed(chunk) self._pending_chunks[x, z] = d def pp(chunk): chunk.populated = True chunk.dirty = True self.postprocess_chunk(chunk) self.dirty_chunk_cache[x, z] = chunk del self._pending_chunks[x, z] return chunk # Set up callbacks. d.addCallback(pp) # Multiple people might be subscribed to this pending callback. We're # going to keep it for ourselves and fork off another Deferred for our # caller. retval = yield fork_deferred(d) returnValue(retval)
def request_chunk(self, x, z): """ Request a ``Chunk`` to be delivered later. :returns: Deferred that will be called with the Chunk """ if not async: return deferLater(reactor, 0.000001, self.factory.world.load_chunk, x, z) if (x, z) in self.chunk_cache: return succeed(self.chunk_cache[x, z]) elif (x, z) in self.dirty_chunk_cache: return succeed(self.dirty_chunk_cache[x, z]) elif (x, z) in self._pending_chunks: # Rig up another Deferred and wrap it up in a to-go box. d = Deferred() self._pending_chunks[x, z].chainDeferred(d) return d chunk = Chunk(x, z) first, second, filename = names_for_chunk(x, z) f = self.folder.child(first).child(second) if not f.exists(): f.makedirs() f = f.child(filename) if f.exists() and f.getsize(): chunk.load_from_tag(read_from_file(f.open("r"))) if chunk.populated: self.chunk_cache[x, z] = chunk return succeed(chunk) d = deferToAMPProcess(MakeChunk, x=x, z=z, seed=self.seed, generators=configuration.getlist("bravo", "generators")) self._pending_chunks[x, z] = d def pp(kwargs): chunk.blocks = fromstring(kwargs["blocks"], dtype=uint8).reshape(chunk.blocks.shape) chunk.heightmap = fromstring(kwargs["heightmap"], dtype=uint8).reshape(chunk.heightmap.shape) chunk.metadata = fromstring(kwargs["metadata"], dtype=uint8).reshape(chunk.metadata.shape) chunk.skylight = fromstring(kwargs["skylight"], dtype=uint8).reshape(chunk.skylight.shape) chunk.blocklight = fromstring(kwargs["blocklight"], dtype=uint8).reshape(chunk.blocklight.shape) chunk.populated = True chunk.dirty = True # Apply the current season to the chunk. if self.season: self.season.transform(chunk) # Since this chunk hasn't been given to any player yet, there's no # conceivable way that any meaningful damage has been accumulated; # anybody loading any part of this chunk will want the entire thing. # Thus, it should start out undamaged. chunk.clear_damage() self.dirty_chunk_cache[x, z] = chunk del self._pending_chunks[x, z] return chunk # Set up callbacks. d.addCallback(pp) # Multiple people might be subscribed to this pending callback. We're # going to keep it for ourselves and fork off another Deferred for our # caller. forked = Deferred() d.chainDeferred(forked) forked.addCallback(lambda none: chunk) return forked
def request_chunk(self, x, z): """ Request a ``Chunk`` to be delivered later. :returns: ``Deferred`` that will be called with the ``Chunk`` """ if (x, z) in self.chunk_cache: returnValue(self.chunk_cache[x, z]) elif (x, z) in self.dirty_chunk_cache: returnValue(self.dirty_chunk_cache[x, z]) elif (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) 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) if chunk.populated: self.chunk_cache[x, z] = 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.dirty_chunk_cache[x, z] = 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)
class TestAlphaSandGravelDig(unittest.TestCase): def setUp(self): self.f = FallablesMockFactory() self.p = retrieve_plugins(IDigHook, parameters={"factory": self.f}) if "alpha_sand_gravel" not in self.p: raise unittest.SkipTest("Plugin not present") self.hook = self.p["alpha_sand_gravel"] self.c = Chunk(0, 0) def test_trivial(self): pass def test_floating_sand(self): """ Sand placed in midair should fall down to the ground. """ self.c.set_block((0, 1, 0), blocks["sand"].slot) self.hook.dig_hook(self.c, 0, 0, 0, blocks["air"].slot) self.assertEqual(self.c.get_block((0, 1, 0)), blocks["air"].slot) self.assertEqual(self.c.get_block((0, 0, 0)), blocks["sand"].slot) def test_sand_on_snow(self): """ Sand placed on snow should replace the snow. Test for #298. """ self.c.set_block((0, 1, 0), blocks["sand"].slot) self.c.set_block((0, 0, 0), blocks["snow"].slot) self.hook.dig_hook(self.c, 0, 2, 0, blocks["snow"].slot) self.assertEqual(self.c.get_block((0, 1, 0)), blocks["air"].slot) self.assertEqual(self.c.get_block((0, 0, 0)), blocks["sand"].slot) def test_sand_on_water(self): """ Sand placed on water should replace the water. Test for #317. """ self.c.set_block((0, 1, 0), blocks["sand"].slot) self.c.set_block((0, 0, 0), blocks["spring"].slot) self.hook.dig_hook(self.c, 0, 2, 0, blocks["spring"].slot) self.assertEqual(self.c.get_block((0, 1, 0)), blocks["air"].slot) self.assertEqual(self.c.get_block((0, 0, 0)), blocks["sand"].slot)
class TestChunkBlocks(unittest.TestCase): def setUp(self): self.c = Chunk(0, 0) def test_trivial(self): pass def test_set_block(self): self.assertEqual(self.c.blocks[0], 0) self.c.set_block((0, 0, 0), 1) self.assertEqual(self.c.blocks[0], 1) def test_set_block_xyz_xzy(self): """ Test that set_block swizzles correctly. """ self.c.set_block((1, 0, 0), 1) self.c.set_block((0, 1, 0), 2) self.c.set_block((0, 0, 1), 3) self.assertEqual(self.c.blocks[2048], 1) self.assertEqual(self.c.blocks[1], 2) self.assertEqual(self.c.blocks[128], 3) def test_destroy(self): """ Test block destruction. """ self.c.set_block((0, 0, 0), 1) self.c.set_metadata((0, 0, 0), 1) self.c.destroy((0, 0, 0)) self.assertEqual(self.c.blocks[0], 0) self.assertEqual(self.c.metadata[0], 0) def test_sed(self): """ ``sed()`` should work. """ self.c.set_block((1, 1, 1), 1) self.c.set_block((2, 2, 2), 2) self.c.set_block((3, 3, 3), 3) self.c.sed(1, 3) self.assertEqual(self.c.get_block((1, 1, 1)), 3) self.assertEqual(self.c.get_block((2, 2, 2)), 2) self.assertEqual(self.c.get_block((3, 3, 3)), 3) def test_single_block_damage_packet(self): chunk = Chunk(2, 1) chunk.populated = True chunk.set_block((2, 4, 8), 1) chunk.set_metadata((2, 4, 8), 2) packet = chunk.get_damage_packet() self.assertEqual(packet, '\x35\x00\x00\x00\x22\x04\x00\x00\x00\x18\x01\x02') def test_set_block_correct_heightmap(self): """ Test heightmap update for a single column. """ self.c.populated = True self.assertEqual(self.c.heightmap[0], 0) self.c.set_block((0, 20, 0), 1) self.assertEqual(self.c.heightmap[0], 20) self.c.set_block((0, 10, 0), 1) self.assertEqual(self.c.heightmap[0], 20) self.c.set_block((0, 30, 0), 1) self.assertEqual(self.c.heightmap[0], 30) self.c.destroy((0, 10, 0)) self.assertEqual(self.c.heightmap[0], 30) self.c.destroy((0, 30, 0)) self.assertEqual(self.c.heightmap[0], 20)
class TestChunkBlocks(unittest.TestCase): def setUp(self): self.c = Chunk(0, 0) def test_trivial(self): pass def test_destroy(self): """ Test block destruction. """ self.c.set_block((0, 0, 0), 1) self.c.set_metadata((0, 0, 0), 1) self.c.destroy((0, 0, 0)) self.assertEqual(self.c.get_block((0, 0, 0)), 0) self.assertEqual(self.c.get_metadata((0, 0, 0)), 0) def test_sed(self): """ ``sed()`` should work. """ self.c.set_block((1, 1, 1), 1) self.c.set_block((2, 2, 2), 2) self.c.set_block((3, 3, 3), 3) self.c.sed(1, 3) self.assertEqual(self.c.get_block((1, 1, 1)), 3) self.assertEqual(self.c.get_block((2, 2, 2)), 2) self.assertEqual(self.c.get_block((3, 3, 3)), 3) def test_set_block_heightmap(self): """ Heightmaps work. """ self.c.populated = True self.c.set_block((0, 20, 0), 1) self.assertEqual(self.c.heightmap[0], 20) def test_set_block_heightmap_underneath(self): """ A block placed underneath the highest block will not alter the heightmap. """ self.c.populated = True self.c.set_block((0, 20, 0), 1) self.assertEqual(self.c.heightmap[0], 20) self.c.set_block((0, 10, 0), 1) self.assertEqual(self.c.heightmap[0], 20) def test_set_block_heightmap_destroyed(self): """ Upon destruction of the highest block, the heightmap will point at the next-highest block. """ self.c.populated = True self.c.set_block((0, 30, 0), 1) self.c.set_block((0, 10, 0), 1) self.c.destroy((0, 30, 0)) self.assertEqual(self.c.heightmap[0], 10)
def setUp(self): self.f = FallablesMockFactory() self.p = retrieve_plugins(IDigHook, factory=self.f) self.hook = self.p["alpha_sand_gravel"] self.c = Chunk(0, 0)
def request_chunk(self, x, z): """ Request a ``Chunk`` to be delivered later. :returns: Deferred that will be called with the Chunk """ if not async: return deferLater(reactor, 0.000001, self.factory.world.load_chunk, x, z) if (x, z) in self.chunk_cache: return succeed(self.chunk_cache[x, z]) elif (x, z) in self.dirty_chunk_cache: return succeed(self.dirty_chunk_cache[x, z]) elif (x, z) in self._pending_chunks: # Rig up another Deferred and wrap it up in a to-go box. return fork_deferred(self._pending_chunks[x, z]) chunk = Chunk(x, z) self.serializer.load_chunk(chunk) if chunk.populated: self.chunk_cache[x, z] = chunk return succeed(chunk) d = deferToAMPProcess(MakeChunk, x=x, z=z, seed=self.seed, generators=configuration.getlist( "bravo", "generators")) self._pending_chunks[x, z] = d def pp(kwargs): chunk.blocks = fromstring(kwargs["blocks"], dtype=uint8).reshape(chunk.blocks.shape) chunk.heightmap = fromstring(kwargs["heightmap"], dtype=uint8).reshape( chunk.heightmap.shape) chunk.metadata = fromstring( kwargs["metadata"], dtype=uint8).reshape(chunk.metadata.shape) chunk.skylight = fromstring( kwargs["skylight"], dtype=uint8).reshape(chunk.skylight.shape) chunk.blocklight = fromstring(kwargs["blocklight"], dtype=uint8).reshape( chunk.blocklight.shape) chunk.populated = True chunk.dirty = True self.postprocess_chunk(chunk) self.dirty_chunk_cache[x, z] = chunk del self._pending_chunks[x, z] return chunk # Set up callbacks. d.addCallback(pp) # Multiple people might be subscribed to this pending callback. We're # going to keep it for ourselves and fork off another Deferred for our # caller. return fork_deferred(d)
class TestGenerators(unittest.TestCase): def setUp(self): self.chunk = Chunk(0, 0) self.p = bravo.plugin.retrieve_plugins(bravo.ibravo.ITerrainGenerator) def test_trivial(self): pass def test_boring(self): if "boring" not in self.p: raise unittest.SkipTest("plugin not present") plugin = self.p["boring"] plugin.populate(self.chunk, 0) for x, z, y in iterchunk(): if y < CHUNK_HEIGHT // 2: self.assertEqual(self.chunk.get_block((x, y, z)), bravo.blocks.blocks["stone"].slot) else: self.assertEqual(self.chunk.get_block((x, y, z)), bravo.blocks.blocks["air"].slot) def test_beaches_range(self): if "beaches" not in self.p: raise unittest.SkipTest("plugin not present") plugin = self.p["beaches"] # Prepare chunk. for i in range(5): self.chunk.set_block((i, 61 + i, i), bravo.blocks.blocks["dirt"].slot) plugin.populate(self.chunk, 0) for i in range(5): self.assertEqual(self.chunk.get_block((i, 61 + i, i)), bravo.blocks.blocks["sand"].slot, "%d, %d, %d is wrong" % (i, 61 + i, i)) def test_beaches_immersed(self): """ Test that beaches still generate properly around pre-existing water tables. This test is meant to ensure that the order of beaches and watertable does not matter. """ if "beaches" not in self.p: raise unittest.SkipTest("plugin not present") plugin = self.p["beaches"] # Prepare chunk. for x, z, y in product(xrange(16), xrange(16), xrange(60, 64)): self.chunk.set_block((x, y, z), bravo.blocks.blocks["spring"].slot) for i in range(5): self.chunk.set_block((i, 61 + i, i), bravo.blocks.blocks["dirt"].slot) plugin.populate(self.chunk, 0) for i in range(5): self.assertEqual(self.chunk.get_block((i, 61 + i, i)), bravo.blocks.blocks["sand"].slot, "%d, %d, %d is wrong" % (i, 61 + i, i))
class TestLightmaps(unittest.TestCase): def setUp(self): self.c = Chunk(0, 0) def test_trivial(self): pass def test_boring_skylight_values(self): # Fill it as if we were the boring generator. self.c.blocks[:, :, 0].fill(1) self.c.regenerate() # Make sure that all of the blocks at the bottom of the ambient # lightmap are set to 15 (fully illuminated). # Note that skylight of a solid block is 0, the important value # is the skylight of the transluscent (usually air) block above it. reference = empty((16, 16)) reference.fill(15) assert_array_equal(self.c.skylight[:, :, 1], reference) def test_skylight_spread(self): # Fill it as if we were the boring generator. self.c.blocks[:, :, 0].fill(1) # Put a false floor up to block the light. self.c.blocks[1:15, 1:15, 3].fill(1) self.c.regenerate() # Put a gradient on the reference lightmap. reference = empty((16, 16)) reference.fill(15) top = 1 bottom = 15 glow = 14 while top < bottom: reference[top:bottom, top:bottom] = glow top += 1 bottom -= 1 glow -= 1 assert_array_equal(self.c.skylight[:, :, 1], reference) def test_skylight_arch(self): """ Indirect illumination should work. """ # Floor. self.c.blocks[:, :, 0].fill(1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. self.c.blocks[0:2, 0:3, 1:3].fill(1) self.c.blocks[1, 1, 1] = 0 # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.skylight[1, 1, 1], 14) def test_skylight_arch_leaves(self): """ Indirect illumination with dimming should work. """ # Floor. self.c.blocks[:, :, 0].fill(1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. self.c.blocks[0:2, 0:3, 1:3].fill(1) self.c.blocks[1, 1, 1] = 0 # Leaves in front of the spot should cause a dimming of 1. self.c.blocks[2, 1, 1] = 18 # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.skylight[1, 1, 1], 13) def test_skylight_arch_leaves_occluded(self): """ Indirect illumination with dimming through occluded blocks only should work. """ # Floor. self.c.blocks[:, :, 0].fill(1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. self.c.blocks[0:3, 0:3, 1:3].fill(1) self.c.blocks[1, 1, 1] = 0 # Leaves in front of the spot should cause a dimming of 1, but since # the leaves themselves are occluded, the total dimming should be 2. self.c.blocks[2, 1, 1] = 18 # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.skylight[1, 1, 1], 12) def test_incremental_solid(self): """ Regeneration isn't necessary to correctly light solid blocks. """ # Initialize tables and enable set_block(). self.c.regenerate() self.c.populated = True # Any solid block with no dimming works. I choose dirt. self.c.set_block((0, 0, 0), blocks["dirt"].slot) self.assertEqual(self.c.skylight[0, 0, 0], 0) def test_incremental_air(self): """ Regeneration isn't necessary to correctly light dug blocks, which leave behind air. """ # Any solid block with no dimming works. I choose dirt. self.c.blocks[0, 0, 0] = blocks["dirt"].slot # Initialize tables and enable set_block(). self.c.regenerate() self.c.populated = True self.c.set_block((0, 0, 0), blocks["air"].slot) self.assertEqual(self.c.skylight[0, 0, 0], 15)
class TestLightmaps(unittest.TestCase): def setUp(self): self.c = Chunk(0, 0) def test_trivial(self): pass def test_boring_skylight_values(self): # Fill it as if we were the boring generator. for x, z in XZ: self.c.set_block((x, 0, z), 1) self.c.regenerate() # Make sure that all of the blocks at the bottom of the ambient # lightmap are set to 15 (fully illuminated). # Note that skylight of a solid block is 0, the important value # is the skylight of the transluscent (usually air) block above it. for x, z in XZ: self.assertEqual(self.c.get_skylight((x, 0, z)), 0xf) test_boring_skylight_values.todo = "Skylight maths is still broken" def test_skylight_spread(self): # Fill it as if we were the boring generator. for x, z in XZ: self.c.set_block((x, 0, z), 1) # Put a false floor up to block the light. for x, z in product(xrange(1, 15), repeat=2): self.c.set_block((x, 2, z), 1) self.c.regenerate() # Test that a gradient emerges. for x, z in XZ: flipx = x if x > 7 else 15 - x flipz = z if z > 7 else 15 - z target = max(flipx, flipz) self.assertEqual(self.c.get_skylight((x, 1, z)), target, "%d, %d" % (x, z)) test_skylight_spread.todo = "Skylight maths is still broken" def test_skylight_arch(self): """ Indirect illumination should work. """ # Floor. for x, z in XZ: self.c.set_block((x, 0, z), 1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. for x, y, z in product(xrange(2), xrange(1, 3), xrange(3)): self.c.set_block((x, y, z), 1) self.c.set_block((1, 1, 1), 0) # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.get_skylight((1, 1, 1)), 14) test_skylight_arch.todo = "Skylight maths is still broken" def test_skylight_arch_leaves(self): """ Indirect illumination with dimming should work. """ # Floor. for x, z in XZ: self.c.set_block((x, 0, z), 1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. for x, y, z in product(xrange(2), xrange(1, 3), xrange(3)): self.c.set_block((x, y, z), 1) self.c.set_block((1, 1, 1), 0) # Leaves in front of the spot should cause a dimming of 1. self.c.set_block((2, 1, 1), 18) # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.get_skylight((1, 1, 1)), 13) test_skylight_arch_leaves.todo = "Skylight maths is still broken" def test_skylight_arch_leaves_occluded(self): """ Indirect illumination with dimming through occluded blocks only should work. """ # Floor. for x, z in XZ: self.c.set_block((x, 0, z), 1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. for x, y, z in product(xrange(3), xrange(1, 3), xrange(3)): self.c.set_block((x, y, z), 1) self.c.set_block((1, 1, 1), 0) # Leaves in front of the spot should cause a dimming of 1, but since # the leaves themselves are occluded, the total dimming should be 2. self.c.set_block((2, 1, 1), 18) # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.get_skylight((1, 1, 1)), 12) test_skylight_arch_leaves_occluded.todo = "Skylight maths is still broken" def test_incremental_solid(self): """ Regeneration isn't necessary to correctly light solid blocks. """ # Initialize tables and enable set_block(). self.c.regenerate() self.c.populated = True # Any solid block with no dimming works. I choose dirt. self.c.set_block((0, 0, 0), blocks["dirt"].slot) self.assertEqual(self.c.get_skylight((0, 0, 0)), 0) test_incremental_solid.todo = "Skylight maths is still broken" def test_incremental_air(self): """ Regeneration isn't necessary to correctly light dug blocks, which leave behind air. """ # Any solid block with no dimming works. I choose dirt. self.c.set_block((0, 0, 0), blocks["dirt"].slot) # Initialize tables and enable set_block(). self.c.regenerate() self.c.populated = True self.c.set_block((0, 0, 0), blocks["air"].slot) self.assertEqual(self.c.get_skylight((0, 0, 0)), 15)
class TestWinter(unittest.TestCase): def setUp(self): self.hook = Winter() self.c = Chunk(0, 0) def test_trivial(self): pass def test_spring_to_ice(self): self.c.set_block((0, 0, 0), blocks["spring"].slot) self.hook.transform(self.c) self.assertEqual(self.c.get_block((0, 0, 0)), blocks["ice"].slot) def test_snow_on_stone(self): self.c.set_block((0, 0, 0), blocks["stone"].slot) self.hook.transform(self.c) self.assertEqual(self.c.get_block((0, 1, 0)), blocks["snow"].slot) def test_no_snow_on_snow(self): """ Test whether snow is spawned on top of other snow. """ self.c.set_block((0, 0, 0), blocks["snow"].slot) self.hook.transform(self.c) self.assertNotEqual(self.c.get_block((0, 1, 0)), blocks["snow"].slot) def test_no_floating_snow(self): """ Test whether snow is spawned in the correct y-level over populated chunks. """ self.c.set_block((0, 0, 0), blocks["grass"].slot) self.c.populated = True self.c.dirty = False self.c.clear_damage() self.hook.transform(self.c) self.assertEqual(self.c.get_block((0, 1, 0)), blocks["snow"].slot) self.assertNotEqual(self.c.get_block((0, 2, 0)), blocks["snow"].slot) def test_bad_heightmap_floating_snow(self): """ Test whether snow is spawned in the correct y-level over populated chunks, if the heightmap is incorrect. """ self.c.set_block((0, 0, 0), blocks["grass"].slot) self.c.populated = True self.c.dirty = False self.c.clear_damage() self.c.heightmap[0 * 16 + 0] = 2 self.hook.transform(self.c) self.assertEqual(self.c.get_block((0, 1, 0)), blocks["snow"].slot) self.assertNotEqual(self.c.get_block((0, 2, 0)), blocks["snow"].slot) def test_top_of_world_snow(self): """ Blocks at the top of the world should not cause exceptions when snow is placed on them. """ self.c.set_block((0, 127, 0), blocks["stone"].slot) self.hook.transform(self.c)
class TestLightmaps(unittest.TestCase): def setUp(self): self.c = Chunk(0, 0) def test_trivial(self): pass def test_boring_skylight_values(self): # Fill it as if we were the boring generator. for x, z in product(xrange(16), repeat=2): self.c.set_block((x, 0, z), 1) self.c.regenerate() # Make sure that all of the blocks at the bottom of the ambient # lightmap are set to 15 (fully illuminated). # Note that skylight of a solid block is 0, the important value # is the skylight of the transluscent (usually air) block above it. for i in xrange(1, 32768, 128): self.assertEqual(self.c.skylight[i], 0xf) def test_skylight_spread(self): # Fill it as if we were the boring generator. for x, z in product(xrange(16), repeat=2): self.c.set_block((x, 0, z), 1) # Put a false floor up to block the light. for x, z in product(xrange(1, 15), repeat=2): self.c.set_block((x, 2, z), 1) self.c.regenerate() # Test that a gradient emerges. for x, z in product(xrange(16), repeat=2): flipx = x if x > 7 else 15 - x flipz = z if z > 7 else 15 - z target = max(flipx, flipz) self.assertEqual(self.c.skylight[(x * 16 + z) * 128 + 1], target, "%d, %d" % (x, z)) def test_skylight_arch(self): """ Indirect illumination should work. """ # Floor. for x, z in product(xrange(16), repeat=2): self.c.set_block((x, 0, z), 1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. for x, y, z in product(xrange(2), xrange(1, 3), xrange(3)): self.c.set_block((x, y, z), 1) self.c.set_block((1, 1, 1), 0) # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.skylight[(1 * 16 + 1) * 128 + 1], 14) def test_skylight_arch_leaves(self): """ Indirect illumination with dimming should work. """ # Floor. for x, z in product(xrange(16), repeat=2): self.c.set_block((x, 0, z), 1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. for x, y, z in product(xrange(2), xrange(1, 3), xrange(3)): self.c.set_block((x, y, z), 1) self.c.set_block((1, 1, 1), 0) # Leaves in front of the spot should cause a dimming of 1. self.c.set_block((2, 1, 1), 18) # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.skylight[(1 * 16 + 1) * 128 + 1], 13) def test_skylight_arch_leaves_occluded(self): """ Indirect illumination with dimming through occluded blocks only should work. """ # Floor. for x, z in product(xrange(16), repeat=2): self.c.set_block((x, 0, z), 1) # Arch of bedrock, with an empty spot in the middle, which will be our # indirect spot. for x, y, z in product(xrange(3), xrange(1, 3), xrange(3)): self.c.set_block((x, y, z), 1) self.c.set_block((1, 1, 1), 0) # Leaves in front of the spot should cause a dimming of 1, but since # the leaves themselves are occluded, the total dimming should be 2. self.c.set_block((2, 1, 1), 18) # Illuminate and make sure that our indirect spot has just a little # bit of illumination. self.c.regenerate() self.assertEqual(self.c.skylight[(1 * 16 + 1) * 128 + 1], 12) def test_incremental_solid(self): """ Regeneration isn't necessary to correctly light solid blocks. """ # Initialize tables and enable set_block(). self.c.regenerate() self.c.populated = True # Any solid block with no dimming works. I choose dirt. self.c.set_block((0, 0, 0), blocks["dirt"].slot) self.assertEqual(self.c.skylight[(0 * 16 + 0) * 128 + 0], 0) def test_incremental_air(self): """ Regeneration isn't necessary to correctly light dug blocks, which leave behind air. """ # Any solid block with no dimming works. I choose dirt. self.c.blocks[(0 * 16 + 0) * 128 + 0] = blocks["dirt"].slot # Initialize tables and enable set_block(). self.c.regenerate() self.c.populated = True self.c.set_block((0, 0, 0), blocks["air"].slot) self.assertEqual(self.c.skylight[(0 * 16 + 0) * 128 + 0], 15)
def empty_chunk(i): Chunk(i, i)