def enable_cache(self, size): """ Set the permanent cache size. Changing the size of the cache sets off a series of events which will empty or fill the cache to make it the proper size. For reference, 3 is a large-enough size to completely satisfy the Notchian client's login demands. 10 is enough to completely fill the Notchian client's chunk buffer. :param int size: The taxicab radius of the cache, in chunks """ log.msg("Setting cache size to %d..." % size) self.permanent_cache = set() def assign(chunk): self.permanent_cache.add(chunk) x = self.spawn[0] // 16 z = self.spawn[2] // 16 rx = xrange(x - size, x + size) rz = xrange(z - size, z + size) d = coiterate( self.request_chunk(x, z).addCallback(assign) for x, z in product(rx, rz)) d.addCallback(lambda chaff: log.msg("Cache size is now %d" % size))
def dig_hook(self, factory, chunk, x, y, z, block): """ Check for neighboring water that might want to spread. Also check to see whether we are, for example, dug ice that has turned back into water. """ x += chunk.x * 16 z += chunk.z * 16 # Check for sponges first, since they will mark the entirety of the # area. if block == self.sponge: for coords in product( xrange(x - 3, x + 4), xrange(max(y - 3, 0), min(y + 4, 128)), xrange(z - 3, z + 4), ): self.pending[factory].add(coords) else: for (dx, dy, dz) in ( ( 0, 0, 0), ( 0, 0, 1), ( 0, 0, -1), ( 0, 1, 0), ( 1, 0, 0), (-1, 0, 0)): coords = x + dx, y + dy, z + dz if factory.world.get_block(coords) in (self.spring, self.fluid): self.pending[factory].add(coords) if any(self.pending.itervalues()) and not self.loop.running: self.loop.start(self.step)
def keys_near(self, key, radius): """ Get all bucket keys "near" this key. This method may return a generator. """ minx, innerx = divmod(key[0], 16) minz, innerz = divmod(key[1], 16) minx = int(minx) minz = int(minz) # Adjust for range() purposes. maxx = minx + 1 maxz = minz + 1 # Adjust for leakiness. if innerx <= radius: minx -= 1 if innerz <= radius: minz -= 1 if innerx + radius >= 16: maxx += 1 if innerz + radius >= 16: maxz += 1 # Expand as needed. expand = int(radius // 16) minx -= expand minz -= expand maxx += expand maxz += expand return product(xrange(minx, maxx), xrange(minz, maxz))
def build_hook(self, factory, player, builddata): """ Remove water around a placed sponge. Remember that we are post-build here, so coordinates have already been adjusted. """ if builddata.block.slot != blocks["sponge"].slot: return True, builddata fluids = set([blocks["spring"].slot, blocks["water"].slot, blocks["ice"].slot]) # Minimum offsets. minx = builddata.x - 2 miny = max(builddata.y - 2, 0) minz = builddata.z - 2 # Maximum offsets. Remember to +1 for range(). maxx = builddata.x + 3 maxy = min(builddata.y + 3, 128) maxz = builddata.z + 3 for coords in product(xrange(minx, maxx), xrange(miny, maxy), xrange(minz, maxz)): if coords == (builddata.x, builddata.y, builddata.z): continue if factory.world.get_block(coords) in fluids: factory.world.set_block(coords, blocks["air"].slot) factory.world.set_metadata(coords, 0x0) return True, builddata
def populate(self, chunk, seed): """ Make smooth waves of stone. """ reseed(seed) # And into one end he plugged the whole of reality as extrapolated # from a piece of fairy cake, and into the other end he plugged his # wife: so that when he turned it on she saw in one instant the whole # infinity of creation and herself in relation to it. factor = 1 / 256 for x, z in product(xrange(16), repeat=2): magx = (chunk.x * 16 + x) * factor magz = (chunk.z * 16 + z) * factor height = octaves2(magx, magz, 6) # Normalize around 70. Normalization is scaled according to a # rotated cosine. #scale = rotated_cosine(magx, magz, seed, 16 * 10) height *= 15 height = int(height + 70) column = chunk.get_column(x, z) column[:height + 1].fill([blocks["stone"].slot])
def build_hook(self, factory, player, builddata): """ Remove water around a placed sponge. Remember that we are post-build here, so coordinates have already been adjusted. """ if builddata.block.slot != blocks["sponge"].slot: return True, builddata fluids = set( [blocks["spring"].slot, blocks["water"].slot, blocks["ice"].slot]) # Minimum offsets. minx = builddata.x - 2 miny = max(builddata.y - 2, 0) minz = builddata.z - 2 # Maximum offsets. Remember to +1 for range(). maxx = builddata.x + 3 maxy = min(builddata.y + 3, 128) maxz = builddata.z + 3 for coords in product(xrange(minx, maxx), xrange(miny, maxy), xrange(minz, maxz)): if coords == (builddata.x, builddata.y, builddata.z): continue if factory.world.get_block(coords) in fluids: factory.world.set_block(coords, blocks["air"].slot) factory.world.set_metadata(coords, 0x0) return True, builddata
def enable_cache(self, size): """ Set the permanent cache size. Changing the size of the cache sets off a series of events which will empty or fill the cache to make it the proper size. For reference, 3 is a large-enough size to completely satisfy the Notchian client's login demands. 10 is enough to completely fill the Notchian client's chunk buffer. :param int size: The taxicab radius of the cache, in chunks """ log.msg("Setting cache size to %d..." % size) self.permanent_cache = set() def assign(chunk): self.permanent_cache.add(chunk) rx = xrange(self.spawn[0] - size, self.spawn[0] + size) rz = xrange(self.spawn[2] - size, self.spawn[2] + size) d = coiterate(self.request_chunk(x, z).addCallback(assign) for x, z in product(rx, rz)) d.addCallback(lambda chaff: log.msg("Cache size is now %d" % size))
def reduce_recipe(self): """ Reduce a crafting table according to a recipe. This function returns None; the crafting table is modified in-place. This function assumes that the recipe already fits the crafting table and will not do additional checks to verify this assumption. """ crafting = self.crafting_table offset = self.recipe_offset dims = self.recipe.dimensions indices = product(xrange(offset[0], offset[0] + dims[1]), xrange(offset[1], offset[1] + dims[0])) for index, slot in zip(indices, self.recipe.recipe): if slot is not None: scount = slot[1] tblock, tdamage, tcount = crafting[index] tcount -= scount if tcount: crafting[index] = tblock, tdamage, tcount else: crafting[index] = None
def regenerate_blocklight(self): lightmap = zeros((16, 16, 128), dtype=uint32) for x, y, z in product(xrange(16), xrange(128), xrange(16)): block = self.blocks[x, z, y] if block in glowing_blocks: composite_glow(lightmap, glowing_blocks[block], x, y, z) self.blocklight = cast[uint8](lightmap.clip(0, 15))
def enable_cache(self): """ Start up a rudimentary permanent cache. """ self.permanent_cache = set() def assign(chunk): self.permanent_cache.add(chunk) rx = xrange(self.spawn[0] - 3, self.spawn[0] + 3) rz = xrange(self.spawn[2] - 3, self.spawn[2] + 3) d = coiterate(assign(self.load_chunk(x, z)) for x, z in product(rx, rz)) d.addCallback(lambda chaff: log.msg("Cache is warmed up!"))
def populate(self, chunk, seed): """ Turn the top few layers of stone into dirt. """ chunk.regenerate_heightmap() for x, z in product(xrange(16), repeat=2): y = chunk.heightmap[x, z] if chunk.get_block((x, y, z)) == blocks["stone"].slot: column = chunk.get_column(x, z) bottom = max(y - 3, 0) column[bottom:y + 1].fill(blocks["dirt"].slot)
def regenerate_heightmap(self): """ Regenerate the height map. The height map is merely the position of the tallest block in any xz-column. """ for x, z in product(xrange(16), repeat=2): for y in range(127, -1, -1): if self.blocks[x, z, y]: break self.heightmap[x, z] = y
def populate(self, chunk, seed): """ Find the top dirt block in each y-level and turn it into grass. """ chunk.regenerate_heightmap() for x, z in product(xrange(16), repeat=2): y = chunk.heightmap[x, z] if (chunk.get_block((x, y, z)) == blocks["dirt"].slot and (y == 127 or chunk.get_block((x, y + 1, z)) == blocks["air"].slot)): chunk.set_block((x, y, z), blocks["grass"].slot)
def populate(self, chunk, seed): """ Find the top dirt block in each y-level and turn it into grass. """ chunk.regenerate_heightmap() for x, z in product(xrange(16), repeat=2): y = chunk.heightmap[x, z] if (chunk.get_block((x, y, z)) == blocks["dirt"].slot and (y == 127 or chunk.get_block( (x, y + 1, z)) == blocks["air"].slot)): chunk.blocks[x, z, y] = blocks["grass"].slot
def transform(self, chunk): chunk.sed(blocks["spring"].slot, blocks["ice"].slot) # Make sure that the heightmap is valid so that we don't spawn # floating snow. chunk.regenerate_heightmap() # Lay snow over anything not already snowed and not snow-resistant. for x, z in product(xrange(16), xrange(16)): height = chunk.height_at(x, z) top_block = chunk.get_block((x, height, z)) if top_block != blocks["snow"].slot: if top_block not in snow_resistant: chunk.set_block((x, height + 1, z), blocks["snow"].slot)
def populate(self, chunk, seed): """ Make smooth waves of stone, then compare to current landscape """ factor = 1 / 256 for x, z in product(xrange(16), repeat=2): magx = ((chunk.x + 32) * 16 + x) * factor magz = ((chunk.z + 32) * 16 + z) * factor height = octaves2(magx, magz, 6) height *= 15 height = int(height + 70) if -6 < chunk.heightmap[x,z] - height < 3 and chunk.heightmap[x,z] > 63 and height > 63: column = chunk.get_column(x, z) column[:].fill(blocks["air"].slot) column[:height-3].fill(blocks["stone"].slot)
def populate(self, chunk, seed): """ Find water level and if the chunk at water level or water level minus 1 should be dirt, make it sand. """ chunk.regenerate_heightmap() for x, z in product(xrange(16), repeat=2): y = chunk.heightmap[x, z] if (60 <= y <= 64 and (chunk.get_block((x, y + 1, z)) in self.above) and (chunk.get_block((x, y, z)) in self.replace)): chunk.set_block((x, y, z), blocks["sand"].slot)
def transform(self, chunk): chunk.sed(blocks["spring"].slot, blocks["ice"].slot) # Lay snow over anything not already snowed and not snow-resistant. for x, z in product(xrange(16), xrange(16)): column = chunk.get_column(x, z) # First is above second. for first, second in pairwise(enumerate(reversed(column))): if second[1] not in (blocks["snow"].slot, blocks["air"].slot): # Solid ground! Is it snowable? if second[1] not in snow_resistant: # Yay! y = len(column) - 1 - first[0] chunk.set_block((x, y, z), blocks["snow"].slot) break
def populate(self, chunk, seed): """ Make smooth waves of stone, then compare to current landscape """ factor = 1 / 256 for x, z in product(xrange(16), repeat=2): magx = ((chunk.x + 32) * 16 + x) * factor magz = ((chunk.z + 32) * 16 + z) * factor height = octaves2(magx, magz, 6) height *= 15 height = int(height + 70) if -6 < chunk.heightmap[x, z] - height < 3 and chunk.heightmap[ x, z] > 63 and height > 63: column = chunk.get_column(x, z) column[:].fill(blocks["air"].slot) column[:height - 3].fill(blocks["stone"].slot)
def populate(self, chunk, seed): """ Eat moar stone """ factor = 1 / 256 for x, z in product(xrange(16), repeat=2): magx = ((chunk.x+16) * 16 + x) * factor magz = ((chunk.z+16) * 16 + z) * factor height = octaves2(magx, magz, 6) height *= 15 height = int(height + 70) column = chunk.get_column(x, z) if abs(chunk.heightmap[x,z] - height) < 10: column[:].fill(blocks["air"].slot) else: column[:height-30+randint(-15,10)].fill(blocks["air"].slot)
def send_initial_chunk_and_location(self): bigx, smallx, bigz, smallz = split_coords(self.location.x, self.location.z) # Spawn the 25 chunks in a square around the spawn, *before* spawning # the player. Otherwise, there's a funky Beta 1.2 bug which causes the # player to not be able to move. d = cooperate( self.enable_chunk(i, j) for i, j in product(xrange(bigx - 3, bigx + 3), xrange(bigz - 3, bigz + 3))).whenDone() # Don't dare send more chunks beyond the initial one until we've # spawned. d.addCallback(lambda none: self.update_location()) d.addCallback(lambda none: self.position_changed()) d.addCallback(lambda none: self.update_chunks())
def regenerate_skylight(self): """ Regenerate the ambient light map. Each block's individual light comes from two sources. The ambient light comes from the sky. The height map must be valid for this method to produce valid results. """ lightmap = zeros((16, 16, 128), dtype=uint32) for x, z in product(xrange(16), repeat=2): y = self.heightmap[x, z] composite_glow(lightmap, 14, x, y, z) self.skylight = cast[uint8](lightmap.clip(0, 15))
def populate(self, chunk, seed): """ Eat moar stone """ factor = 1 / 256 for x, z in product(xrange(16), repeat=2): magx = ((chunk.x + 16) * 16 + x) * factor magz = ((chunk.z + 16) * 16 + z) * factor height = octaves2(magx, magz, 6) height *= 15 height = int(height + 70) column = chunk.get_column(x, z) if abs(chunk.heightmap[x, z] - height) < 10: column[:].fill(blocks["air"].slot) else: column[:height - 30 + randint(-15, 10)].fill( blocks["air"].slot)
def send_initial_chunk_and_location(self): bigx, smallx, bigz, smallz = split_coords(self.player.location.x, self.player.location.z) # Spawn the 25 chunks in a square around the spawn, *before* spawning # the player. Otherwise, there's a funky Beta 1.2 bug which causes the # player to not be able to move. d = cooperate( self.enable_chunk(i, j) for i, j in product( xrange(bigx - 3, bigx + 3), xrange(bigz - 3, bigz + 3) ) ).whenDone() # Don't dare send more chunks beyond the initial one until we've # spawned. d.addCallback(lambda none: self.update_location()) d.addCallback(lambda none: self.update_chunks())
def populate(self, chunk, seed): """ Find blocks within a height range and turn them into sand if they are dirt and underwater or exposed to air. If the height range is near the water table level, this creates fairly good beaches. """ chunk.regenerate_heightmap() for x, z in product(xrange(16), repeat=2): y = chunk.heightmap[x, z] while y > 60 and chunk.get_block((x, y, z)) in self.above: y -= 1 if not 60 < y < 66: continue if chunk.get_block((x, y, z)) in self.replace: chunk.blocks[x, z, y] = blocks["sand"].slot
def check_recipes(self): """ See if the crafting table matches any recipes. :returns: the recipe and offset, or None if no matches could be made """ # This isn't perfect, unfortunately, but correctness trumps algorithmic # perfection. (For now.) crafting = self.crafting_table for recipe in retrieve_plugins(IRecipe).itervalues(): dims = recipe.dimensions for x, y in crafting.iterkeys(): if (x + dims[1] > self.crafting_stride or y + dims[0] > self.crafting_stride): continue indices = product(xrange(x, x + dims[1]), xrange(y, y + dims[0])) matches_needed = dims[0] * dims[1] for index, slot in zip(indices, recipe.recipe): if crafting[index] is None and slot is None: matches_needed -= 1 elif crafting[index] is not None and slot is not None: cprimary, csecondary, ccount = crafting[index] skey, scount = slot if ((cprimary, csecondary) == skey and ccount >= scount): matches_needed -= 1 if matches_needed == 0: # Jackpot! self.recipe = recipe self.recipe_offset = (x, y) return self.recipe = None
def populate(self, chunk, seed): """ Make smooth islands of stone. """ reseed(seed) factor = 1 / 256 for x, z in product(xrange(16), repeat=2): column = chunk.get_column(x, z) magx = (chunk.x * 16 + x) * factor magz = (chunk.z * 16 + z) * factor samples = array([octaves3(magx, magz, y * factor, 6) for y in xrange(column.size)]) column = where(samples > 0, blocks["dirt"].slot, column) column = where(samples > 0.1, blocks["stone"].slot, column) chunk.set_column(x, z, column)
def populate(self, chunk, seed): reseed(seed) xzfactor = 1 / 16 yfactor = 1 / 32 for x, z in product(xrange(16), repeat=2): for y in range(chunk.heightmap[x, z] + 1): magx = (chunk.x * 16 + x) * xzfactor magz = (chunk.z * 16 + z) * xzfactor magy = y * yfactor sample = octaves3(magx, magz, magy, 3) if sample > 0.9999: # Figure out what to place here. old = chunk.get_block((x, y, z)) if old == blocks["sand"].slot: # Sand becomes clay. chunk.set_block((x, y, z), blocks["clay"].slot) elif old == blocks["dirt"].slot: # Dirt becomes gravel. chunk.set_block((x, y, z), blocks["gravel"].slot) elif old == blocks["stone"].slot: # Stone becomes one of the ores. if y < 12: chunk.set_block((x, y, z), blocks["diamond-ore"].slot) elif y < 24: chunk.set_block((x, y, z), blocks["gold-ore"].slot) elif y < 36: chunk.set_block((x, y, z), blocks["redstone-ore"].slot) elif y < 48: chunk.set_block((x, y, z), blocks["iron-ore"].slot) else: chunk.set_block((x, y, z), blocks["coal-ore"].slot)
def populate(self, chunk, seed): reseed(seed) xzfactor = 1 / 16 yfactor = 1 / 32 for x, z in product(xrange(16), repeat=2): for y in range(chunk.heightmap[x, z] + 1): magx = (chunk.x * 16 + x) * xzfactor magz = (chunk.z * 16 + z) * xzfactor magy = y * yfactor sample = octaves3(magx, magz, magy, 3) if sample > 0.9999: # Figure out what to place here. old = chunk.blocks[x, z, y] new = None if old == blocks["sand"].slot: # Sand becomes clay. new = blocks["clay"].slot elif old == blocks["dirt"].slot: # Dirt becomes gravel. new = blocks["gravel"].slot elif old == blocks["stone"].slot: # Stone becomes one of the ores. if y < 12: new = blocks["diamond-ore"].slot elif y < 24: new = blocks["gold-ore"].slot elif y < 36: new = blocks["redstone-ore"].slot elif y < 48: new = blocks["iron-ore"].slot else: new = blocks["coal-ore"].slot if new: chunk.blocks[x, z, y] = new
def entities_near(self, radius): """ Obtain the entities within a radius of this player. Radius is measured in blocks. """ chunk_radius = int(radius // 16 + 1) chunkx, chaff, chunkz, chaff = split_coords(self.location.x, self.location.z) minx = chunkx - chunk_radius maxx = chunkx + chunk_radius + 1 minz = chunkz - chunk_radius maxz = chunkz + chunk_radius + 1 for x, z in product(xrange(minx, maxx), xrange(minz, maxz)): chunk = self.chunks[x, z] yieldables = [entity for entity in chunk.entities if self.location.distance(entity.location) <= radius] for i in yieldables: yield i
def populate(self, chunk, seed): """ Make smooth islands of stone. """ reseed(seed) factor = 1 / 256 for x, z in product(xrange(16), repeat=2): column = chunk.get_column(x, z) magx = (chunk.x * 16 + x) * factor magz = (chunk.z * 16 + z) * factor samples = array([ octaves3(magx, magz, y * factor, 6) for y in xrange(column.size) ]) column = where(samples > 0, blocks["dirt"].slot, column) column = where(samples > 0.1, blocks["stone"].slot, column) chunk.set_column(x, z, column)
def regenerate_skylight(self): """ Regenerate the ambient light map. Each block's individual light comes from two sources. The ambient light comes from the sky. The height map must be valid for this method to produce valid results. """ lightmap = zeros((16, 16, 128), dtype=uint8) for x, z in product(xrange(16), repeat=2): light = 15 for y in range(127, -1, -1): dim = blocks[self.blocks[x, z, y]].dim light -= dim if light <= 0: break lightmap[x, z, y] = light self.skylight = lightmap.clip(0, 15)
def entities_near(self, radius): """ Obtain the entities within a radius of this player. Radius is measured in blocks. """ chunk_radius = int(radius // 16 + 1) chunkx, chaff, chunkz, chaff = split_coords(self.location.x, self.location.z) minx = chunkx - chunk_radius maxx = chunkx + chunk_radius + 1 minz = chunkz - chunk_radius maxz = chunkz + chunk_radius + 1 for x, z in product(xrange(minx, maxx), xrange(minz, maxz)): chunk = self.chunks[x, z] yieldables = [ entity for entity in chunk.entities if self.location.distance(entity.location) <= radius ] for i in yieldables: yield i
def apply_recipe(self): """ Return the crafted output of an applied recipe. This function assumes that the recipe already fits the crafting table and will not do additional checks to verify this assumption. """ crafting = self.crafting_table offset = self.recipe_offset dims = self.recipe.dimensions indices = product(xrange(offset[0], offset[0] + dims[1]), xrange(offset[1], offset[1] + dims[0])) count = [] for index, slot in zip(indices, self.recipe.recipe): if slot is not None and crafting[index] is not None: scount = slot[1] tcount = crafting[index][2] count.append(tcount // scount) counted = min(count) if counted > 0: return self.recipe.provides[0], self.recipe.provides[1] * counted
def regenerate_skylight(self): """ Regenerate the ambient light map. Each block's individual light comes from two sources. The ambient light comes from the sky. The height map must be valid for this method to produce valid results. """ lightmap = zeros((16, 16, 128), dtype=uint8) for x, z in product(xrange(16), repeat=2): # The maximum lighting value, unsurprisingly, is 0xf, which is the # biggest possible value for a nibble. light = 0xf # Apparently, skylights start at the block *above* the block on # which the light is incident? height = self.heightmap[x, z] + 1 # The topmost block, regardless of type, is set to maximum # lighting, as are all the blocks above it. lightmap[x, z, height:] = light # Dim the light going throught the remaining blocks, until there # is no more light left. for y in range(height, -1, -1): dim = blocks[self.blocks[x, z, y]].dim light -= dim if light <= 0: break lightmap[x, z, y] = light self.skylight = lightmap.clip(0, 15)
os.makedirs(target) print "Making map of %dx%d chunks in %s" % (size, size, target) print "Using pipeline: %s" % ", ".join(plugin.name for plugin in pipeline) world = World(target) world.pipeline = pipeline world.season = None counts = [1, 2, 4, 5, 8] count = 0 total = size ** 2 cpu = 0 before = time.time() for i, j in product(xrange(size), repeat=2): start = time.time() chunk = world.load_chunk(i, j) cpu += (time.time() - start) world.save_chunk(chunk) count += 1 if count >= counts[0]: print "Status: %d/%d (%.2f%%)" % (count, total, count * 100 / total) counts.append(counts.pop(0) * 10) taken = time.time() - before print "Finished!" print "Took %.2f seconds to generate (%dms/chunk)" % (taken, taken * 1000 / size) print "Spent %.2f seconds on CPU (%dms/chunk)" % (cpu, cpu * 1000 / size)
x, y, w, h = (float(i) for i in arguments) image = Image.new("L", (WIDTH, HEIGHT)) pbo = image.load() counts = [1, 2, 4, 5, 8] count = 0 total = WIDTH * HEIGHT print "Seed: %d" % options.seed print "Coords: %f, %f" % (x, y) print "Window: %fx%f" % (w, h) print "Octaves: %d" % options.octaves print "Offsets: %f, %f" % (xoffset, yoffset) for i, j in product(xrange(WIDTH), xrange(HEIGHT)): count += 1 if count >= counts[0]: print "Status: %d/%d (%.2f%%)" % (count, total, count * 100 / total) counts.append(counts.pop(0) * 10) # Get our scaled coords xcoord = x + w * i / WIDTH ycoord = y + h * j / HEIGHT # Get noise and scale from [-1, 1] to [0, 255] if xoffset or yoffset: noise = offset2(xcoord, ycoord, xoffset, yoffset, options.octaves) if options.octaves > 1: noise = octaves2(xcoord, ycoord, options.octaves) else:
from numpy import int8, uint8, uint32 from numpy import cast, empty, where, zeros from bravo.blocks import blocks, glowing_blocks from bravo.compat import product from bravo.packets import make_packet from bravo.utilities import pack_nibbles # Set up glow tables. # These tables provide glow maps for illuminated points. glow = [None] * 15 for i in range(15): dim = 2 * i + 1 glow[i] = zeros((dim, dim, dim), dtype=int8) for x, y, z in product(xrange(dim), repeat=3): distance = abs(x - i) + abs(y - i) + abs(z - i) glow[i][ x, y, z] = i + 1 - distance glow[i] = cast[uint8](glow[i].clip(0, 15)) def composite_glow(target, strength, x, y, z): """ Composite a light source onto a lightmap. The exact operation is not quite unlike an add. """ ambient = glow[strength] xbound, zbound, ybound = target.shape sx = x - strength
def process(self): for factory in self.pending: w = factory.world new = set() for x, y, z in self.pending[factory]: # Neighbors on the xz-level. neighbors = ((x - 1, y, z), (x + 1, y, z), (x, y, z - 1), (x, y, z + 1)) # Our downstairs pal. below = (x, y - 1, z) block = w.get_block((x, y, z)) if block == self.sponge: # Track this sponge. self.sponges[factory][x, y, z] = True # Destroy the water! Destroy! for coords in product( xrange(x - 2, x + 3), xrange(max(y - 2, 0), min(y + 3, 128)), xrange(z - 2, z + 3), ): target = w.get_block(coords) if target == self.spring: if (coords[0], coords[2]) in self.springs[factory]: del self.springs[factory][coords[0], coords[2]] w.destroy(coords) elif target == self.fluid: w.destroy(coords) # And now mark our surroundings so that they can be # updated appropriately. for coords in product( xrange(x - 3, x + 4), xrange(max(y - 3, 0), min(y + 4, 128)), xrange(z - 3, z + 4), ): if coords != (x, y, z): new.add(coords) if block == self.spring: # Double-check that we weren't placed inside a sponge. # That's just wrong. if any(self.sponges[factory].iteritemsnear((x, y, z), 2)): w.destroy((x, y, z)) continue # Track this spring. self.springs[factory][x, z] = y # Spawn water from springs. for coords in neighbors: if (w.get_block(coords) in self.whitespace and not any(self.sponges[factory].iteritemsnear(coords, 2))): w.set_block(coords, self.fluid) w.set_metadata(coords, 0x0) new.add(coords) # Is this water falling down to the next y-level? if (y > 0 and w.get_block(below) in self.whitespace and not any(self.sponges[factory].iteritemsnear(below, 2))): w.set_block(below, self.fluid) w.set_metadata(below, FALLING) new.add(below) elif block == self.fluid: # Double-check that we weren't placed inside a sponge. if any(self.sponges[factory].iteritemsnear((x, y, z), 2)): w.destroy((x, y, z)) continue # First, figure out whether or not we should be spreading. # Let's see if there are any springs nearby which are # above us and thus able to fuel us. if not any(springy >= y for springy in self.springs[factory].iterkeysnear( (x, z), self.levels + 1 ) ): # Oh noes, we're drying up! We should mark our # neighbors and dry ourselves up. new.update(neighbors) new.add(below) w.destroy((x, y, z)) continue metadata = w.get_metadata((x, y, z)) newmd = self.levels + 1 for coords in neighbors: jones = w.get_block(coords) if jones == self.spring: newmd = 0 new.update(neighbors) break elif jones == self.fluid: jonesmd = w.get_metadata(coords) & ~FALLING if jonesmd + 1 < newmd: newmd = jonesmd + 1 if newmd > self.levels: # We should dry up. new.update(neighbors) new.add(below) w.destroy((x, y, z)) continue # Mark any neighbors which should adjust themselves. This # will only mark lower water levels than ourselves, and # only if they are definitely too low. for coords in neighbors: if w.get_metadata(coords) & ~FALLING > newmd + 1: new.add(coords) # Now, it's time to extend water. Remember, either the # water flows downward to the next y-level, or it # flows out across the xz-level, but *not* both. # Fall down to the next y-level, if possible. if (y > 0 and w.get_block(below) in self.whitespace and not any(self.sponges[factory].iteritemsnear(below, 2))): w.set_block(below, self.fluid) w.set_metadata(below, newmd | FALLING) new.add(below) continue # Clamp our newmd and assign. Also, set ourselves again; # we changed this time and we might change again. w.set_metadata((x, y, z), newmd) # Otherwise, just fill our neighbors with water, where # applicable, and mark them. if newmd < self.levels: newmd += 1 for coords in neighbors: if (w.get_block(coords) in self.whitespace and not any(self.sponges[factory].iteritemsnear(coords, 2))): w.set_block(coords, self.fluid) w.set_metadata(coords, newmd) new.add(coords) else: # Hm, why would a pending block not be any of the things # we care about? Maybe it used to be a spring or # something? if (x, z) in self.springs[factory]: # Destroyed spring. Add neighbors and below to blocks # to update. del self.springs[factory][x, z] new.update(neighbors) new.add(below) elif (x, y, z) in self.sponges[factory]: # The evil sponge tyrant is gone. Flow, minions, flow! for coords in product( xrange(x - 3, x + 4), xrange(max(y - 3, 0), min(y + 4, 128)), xrange(z - 3, z + 4), ): if coords != (x, y, z): new.add(coords) # Flush affected chunks. to_flush = defaultdict(set) for x, y, z in chain(self.pending[factory], new): to_flush[factory].add(factory.world.load_chunk(x // 16, z // 16)) for factory, chunks in to_flush.iteritems(): for chunk in chunks: factory.flush_chunk(chunk) self.pending[factory] = new # Prune and turn off the loop if appropriate. for dd in (self.pending, self.springs, self.sponges): for factory in dd.keys(): if not dd[factory]: del dd[factory] if not self.pending and self.loop.running: self.loop.stop()
from bravo.blocks import blocks, items from bravo.compat import namedtuple, product from bravo.config import configuration from bravo.entity import Sign from bravo.factories.infini import InfiniClientFactory from bravo.ibravo import IChatCommand, IBuildHook, IDigHook from bravo.inventory import Workbench, sync_inventories from bravo.packets import parse_packets, make_packet, make_error_packet from bravo.plugin import retrieve_plugins, retrieve_named_plugins from bravo.utilities import split_coords (STATE_UNAUTHENTICATED, STATE_CHALLENGED, STATE_AUTHENTICATED) = range(3) circle = [(i, j) for i, j in sorted( product(xrange(-10, 10), xrange(-10, 10)), key=lambda t: t[0]**2 + t[1]**2) if i**2 + j**2 <= 100 ] """ A list of points in a filled circle of radius 10, sorted according to distance from the center. """ BuildData = namedtuple("BuildData", "block, metadata, x, y, z, face") class BetaServerProtocol(Protocol): """ The Minecraft Alpha/Beta server protocol. This class is mostly designed to be a skeleton for featureful clients. It
from numpy import cast, empty, where, zeros from bravo.blocks import glowing_blocks from bravo.compat import product from bravo.entity import tile_entities from bravo.packets import make_packet from bravo.serialize import ChunkSerializer from bravo.utilities import pack_nibbles # Set up glow tables. # These tables provide glow maps for illuminated points. glow = [None] * 15 for i in range(15): dim = 2 * i + 1 glow[i] = zeros((dim, dim, dim), dtype=int8) for x, y, z in product(xrange(dim), repeat=3): distance = abs(x - i) + abs(y - i) + abs(z - i) glow[i][x, y, z] = i + 1 - distance glow[i] = cast[uint8](glow[i].clip(0, 15)) def composite_glow(target, strength, x, y, z): """ Composite a light source onto a lightmap. The exact operation is not quite unlike an add. """ ambient = glow[strength] xbound, zbound, ybound = target.shape
os.makedirs(target) print "Making map of %dx%d chunks in %s" % (size, size, target) print "Using pipeline: %s" % ", ".join(plugin.name for plugin in pipeline) world = World(target) world.pipeline = pipeline world.season = None counts = [1, 2, 4, 5, 8] count = 0 total = size**2 cpu = 0 before = time.time() for i, j in product(xrange(size), repeat=2): start = time.time() chunk = world.load_chunk(i, j) cpu += (time.time() - start) world.save_chunk(chunk) count += 1 if count >= counts[0]: print "Status: %d/%d (%.2f%%)" % (count, total, count * 100 / total) counts.append(counts.pop(0) * 10) taken = time.time() - before print "Finished!" print "Took %.2f seconds to generate (%dms/chunk)" % (taken, taken * 1000 / size) print "Spent %.2f seconds on CPU (%dms/chunk)" % (cpu, cpu * 1000 / size)
from bravo.blocks import blocks, items from bravo.compat import namedtuple, product from bravo.config import configuration from bravo.entity import Sign from bravo.factories.infini import InfiniClientFactory from bravo.ibravo import IChatCommand, IBuildHook, IDigHook, ISignHook from bravo.inventory import Workbench, sync_inventories from bravo.location import Location from bravo.packets import parse_packets, make_packet, make_error_packet from bravo.plugin import retrieve_plugins, retrieve_sorted_plugins from bravo.utilities import split_coords (STATE_UNAUTHENTICATED, STATE_CHALLENGED, STATE_AUTHENTICATED) = range(3) circle = [(i, j) for i, j in product(xrange(-10, 10), xrange(-10, 10)) if i**2 + j**2 <= 100] """ A list of points in a filled circle of radius 10. """ BuildData = namedtuple("BuildData", "block, metadata, x, y, z, face") class BetaServerProtocol(Protocol): """ The Minecraft Alpha/Beta server protocol. This class is mostly designed to be a skeleton for featureful clients. It tries hard to not step on the toes of potential subclasses. """