From 6a1ee964bf03dd961584d30d0987515446d5fed1 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sun, 6 Mar 2011 01:57:05 -0500 Subject: [PATCH 1/5] inlined 'iterate_chunkblocks' (backported from Xon's code) From the discussion in the massive Issue #271 discussion: https://github.com/brownan/Minecraft-Overviewer/issues/#issue/271 --- chunk.py | 380 +++++++++++++++++++++++++++---------------------------- 1 file changed, 186 insertions(+), 194 deletions(-) diff --git a/chunk.py b/chunk.py index ed3a3ad..8b74041 100644 --- a/chunk.py +++ b/chunk.py @@ -97,21 +97,6 @@ def get_tileentity_data(level): data = level['TileEntities'] return data -def iterate_chunkblocks(xoff,yoff): - """Iterates over the 16x16x128 blocks of a chunk in rendering order. - Yields (x,y,z,imgx,imgy) - x,y,z is the block coordinate in the chunk - imgx,imgy is the image offset in the chunk image where that block should go - """ - for x in xrange(15,-1,-1): - for y in xrange(16): - imgx = xoff + x*12 + y*12 - imgy = yoff - x*6 + y*6 + 128*12 + 16*12//2 - for z in xrange(128): - yield x,y,z,imgx,imgy - imgy -= 12 - - # This set holds blocks ids that can be seen through, for occlusion calculations transparent_blocks = set([0, 6, 8, 9, 18, 20, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85]) @@ -689,197 +674,204 @@ class ChunkRenderer(object): if not img: img = Image.new("RGBA", (384, 1728), (38,92,255,0)) - for x,y,z,imgx,imgy in iterate_chunkblocks(xoff,yoff): - blockid = blocks[x,y,z] + for x in xrange(15,-1,-1): + for y in xrange(16): + imgx = xoff + x*12 + y*12 + imgy = yoff - x*6 + y*6 + 128*12 + 16*12//2 + imgy += 12 + for z in xrange(128): + imgy -= 12 - # the following blocks don't have textures that can be pre-computed from the blockid - # alone. additional data is required. - # TODO torches, redstone torches, crops, ladders, stairs, - # levers, doors, buttons, and signs all need to be handled here (and in textures.py) + blockid = blocks[x,y,z] - ## minecart track, crops, ladder, doors, etc. - if blockid in textures.special_blocks: - # also handle furnaces here, since one side has a different texture than the other - ancilData = blockData_expanded[x,y,z] - try: - if blockid in pseudo_ancildata_blocks: - - pseudo_ancilData = self.generate_pseudo_ancildata(x,y,z,blockid) - ancilData = pseudo_ancilData - t = textures.specialblockmap[(blockid, ancilData)] - except KeyError: - t = None + # the following blocks don't have textures that can be pre-computed from the blockid + # alone. additional data is required. + # TODO torches, redstone torches, crops, ladders, stairs, + # levers, doors, buttons, and signs all need to be handled here (and in textures.py) - else: - t = textures.blockmap[blockid] + ## minecart track, crops, ladder, doors, etc. + if blockid in textures.special_blocks: + # also handle furnaces here, since one side has a different texture than the other + ancilData = blockData_expanded[x,y,z] + try: + if blockid in pseudo_ancildata_blocks: - if not t: - continue - - if self.world.useBiomeData: - # 16 : number of blocks in a chunk (in one direction) - # 32 : number of chunks in a region (and biome file) in one direction - # so 16 * 32 == 512 : number of blocks in biome file, in one direction - if blockid == 2: #grass - index = biomeColorData[ ((startY*16)+y) * 512 + (startX*16) + x] - c = textures.grasscolor[index] + pseudo_ancilData = self.generate_pseudo_ancildata(x,y,z,blockid) + ancilData = pseudo_ancilData + t = textures.specialblockmap[(blockid, ancilData)] + except KeyError: + t = None - # only tint the top texture - t = textures.prepareGrassTexture(c) - elif blockid == 18: # leaves - index = biomeColorData[ ((startY*16)+y) * 512 + (startX*16) + x] - c = textures.foliagecolor[index] - - t = textures.prepareLeafTexture(c) - - - - - # Check if this block is occluded - if cave and ( - x == 0 and y != 15 and z != 127 - ): - # If it's on the x face, only render if there's a - # transparent block in the y+1 direction OR the z-1 - # direction - if ( - blocks[x,y+1,z] not in transparent_blocks and - blocks[x,y,z+1] not in transparent_blocks - ): - continue - elif cave and ( - y == 15 and x != 0 and z != 127 - ): - # If it's on the facing y face, only render if there's - # a transparent block in the x-1 direction OR the z-1 - # direction - if ( - blocks[x-1,y,z] not in transparent_blocks and - blocks[x,y,z+1] not in transparent_blocks - ): - continue - elif cave and ( - y == 15 and x == 0 and z != 127 - ): - # If it's on the facing edge, only render if what's - # above it is transparent - if ( - blocks[x,y,z+1] not in transparent_blocks - ): - continue - elif (left_blocks == None and right_blocks == None): - # Normal block or not cave mode, check sides for - # transparentcy or render if it's a border chunk. - - if ( - x != 0 and y != 15 and z != 127 and - blocks[x-1,y,z] not in transparent_blocks and - blocks[x,y+1,z] not in transparent_blocks and - blocks[x,y,z+1] not in transparent_blocks - ): - continue - - elif (left_blocks != None and right_blocks == None): - - if ( - # If it has the left face covered check for - # transparent blocks in left face - y != 15 and z != 127 and - (left_blocks[15,y,z] if x == 0 else blocks[x - 1,y,z]) not in transparent_blocks and - blocks[x,y+1,z] not in transparent_blocks and - blocks[x,y,z+1] not in transparent_blocks - ): - continue - - elif (left_blocks == None and right_blocks != None): - - if ( - # If it has the right face covered check for - # transparent blocks in right face - x != 0 and z != 127 and - blocks[x-1,y,z] not in transparent_blocks and - (right_blocks[x,0,z] if y == 15 else blocks[x,y + 1,z]) not in transparent_blocks and - blocks[x,y,z+1] not in transparent_blocks - ): - continue - - elif ( - # If it's a interior chunk check for transparent blocks - # in the adjacent chunks. - z != 127 and - (left_blocks[15,y,z] if x == 0 else blocks[x - 1,y,z]) not in transparent_blocks and - (right_blocks[x,0,z] if y == 15 else blocks[x,y + 1,z]) not in transparent_blocks and - blocks[x,y,z+1] not in transparent_blocks - # Don't render if all sides aren't transparent - ): - continue - - # Draw the actual block on the image. For cave images, - # tint the block with a color proportional to its depth - if cave: - # no lighting for cave -- depth is probably more useful - composite.alpha_over(img, Image.blend(t[0],depth_colors[z],0.3), (imgx, imgy), t[1]) - else: - if not self.world.lighting: - # no lighting at all - composite.alpha_over(img, t[0], (imgx, imgy), t[1]) - elif blockid in transparent_blocks: - # transparent means draw the whole - # block shaded with the current - # block's light - black_coeff, _ = self.get_lighting_coefficient(x, y, z) - if self.world.spawn and black_coeff > 0.8 and blockid in solid_blocks and not ( - blockid in nospawn_blocks or ( - z != 127 and (blocks[x,y,z+1] in solid_blocks or blocks[x,y,z+1] in fluid_blocks) - ) - ): - composite.alpha_over(img, Image.blend(t[0], red_color, black_coeff), (imgx, imgy), t[1]) else: - composite.alpha_over(img, Image.blend(t[0], black_color, black_coeff), (imgx, imgy), t[1]) - else: - # draw each face lit appropriately, - # but first just draw the block - composite.alpha_over(img, t[0], (imgx, imgy), t[1]) - - # top face - black_coeff, face_occlude = self.get_lighting_coefficient(x, y, z + 1) - # Use red instead of black for spawnable blocks - if self.world.spawn and black_coeff > 0.8 and blockid in solid_blocks and not ( - blockid in nospawn_blocks or ( - z != 127 and (blocks[x,y,z+1] in solid_blocks or blocks[x,y,z+1] in fluid_blocks) - ) + t = textures.blockmap[blockid] + + if not t: + continue + + if self.world.useBiomeData: + # 16 : number of blocks in a chunk (in one direction) + # 32 : number of chunks in a region (and biome file) in one direction + # so 16 * 32 == 512 : number of blocks in biome file, in one direction + if blockid == 2: #grass + index = biomeColorData[ ((startY*16)+y) * 512 + (startX*16) + x] + c = textures.grasscolor[index] + + # only tint the top texture + t = textures.prepareGrassTexture(c) + elif blockid == 18: # leaves + index = biomeColorData[ ((startY*16)+y) * 512 + (startX*16) + x] + c = textures.foliagecolor[index] + + t = textures.prepareLeafTexture(c) + + + + + # Check if this block is occluded + if cave and ( + x == 0 and y != 15 and z != 127 ): - over_color = red_color + # If it's on the x face, only render if there's a + # transparent block in the y+1 direction OR the z-1 + # direction + if ( + blocks[x,y+1,z] not in transparent_blocks and + blocks[x,y,z+1] not in transparent_blocks + ): + continue + elif cave and ( + y == 15 and x != 0 and z != 127 + ): + # If it's on the facing y face, only render if there's + # a transparent block in the x-1 direction OR the z-1 + # direction + if ( + blocks[x-1,y,z] not in transparent_blocks and + blocks[x,y,z+1] not in transparent_blocks + ): + continue + elif cave and ( + y == 15 and x == 0 and z != 127 + ): + # If it's on the facing edge, only render if what's + # above it is transparent + if ( + blocks[x,y,z+1] not in transparent_blocks + ): + continue + elif (left_blocks == None and right_blocks == None): + # Normal block or not cave mode, check sides for + # transparentcy or render if it's a border chunk. + + if ( + x != 0 and y != 15 and z != 127 and + blocks[x-1,y,z] not in transparent_blocks and + blocks[x,y+1,z] not in transparent_blocks and + blocks[x,y,z+1] not in transparent_blocks + ): + continue + + elif (left_blocks != None and right_blocks == None): + + if ( + # If it has the left face covered check for + # transparent blocks in left face + y != 15 and z != 127 and + (left_blocks[15,y,z] if x == 0 else blocks[x - 1,y,z]) not in transparent_blocks and + blocks[x,y+1,z] not in transparent_blocks and + blocks[x,y,z+1] not in transparent_blocks + ): + continue + + elif (left_blocks == None and right_blocks != None): + + if ( + # If it has the right face covered check for + # transparent blocks in right face + x != 0 and z != 127 and + blocks[x-1,y,z] not in transparent_blocks and + (right_blocks[x,0,z] if y == 15 else blocks[x,y + 1,z]) not in transparent_blocks and + blocks[x,y,z+1] not in transparent_blocks + ): + continue + + elif ( + # If it's a interior chunk check for transparent blocks + # in the adjacent chunks. + z != 127 and + (left_blocks[15,y,z] if x == 0 else blocks[x - 1,y,z]) not in transparent_blocks and + (right_blocks[x,0,z] if y == 15 else blocks[x,y + 1,z]) not in transparent_blocks and + blocks[x,y,z+1] not in transparent_blocks + # Don't render if all sides aren't transparent + ): + continue + + # Draw the actual block on the image. For cave images, + # tint the block with a color proportional to its depth + if cave: + # no lighting for cave -- depth is probably more useful + composite.alpha_over(img, Image.blend(t[0],depth_colors[z],0.3), (imgx, imgy), t[1]) else: - over_color = black_color + if not self.world.lighting: + # no lighting at all + composite.alpha_over(img, t[0], (imgx, imgy), t[1]) + elif blockid in transparent_blocks: + # transparent means draw the whole + # block shaded with the current + # block's light + black_coeff, _ = self.get_lighting_coefficient(x, y, z) + if self.world.spawn and black_coeff > 0.8 and blockid in solid_blocks and not ( + blockid in nospawn_blocks or ( + z != 127 and (blocks[x,y,z+1] in solid_blocks or blocks[x,y,z+1] in fluid_blocks) + ) + ): + composite.alpha_over(img, Image.blend(t[0], red_color, black_coeff), (imgx, imgy), t[1]) + else: + composite.alpha_over(img, Image.blend(t[0], black_color, black_coeff), (imgx, imgy), t[1]) + else: + # draw each face lit appropriately, + # but first just draw the block + composite.alpha_over(img, t[0], (imgx, imgy), t[1]) - if not face_occlude: - composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[0]).enhance(black_coeff)) - - # left face - black_coeff, face_occlude = self.get_lighting_coefficient(x - 1, y, z) - if not face_occlude: - composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[1]).enhance(black_coeff)) + # top face + black_coeff, face_occlude = self.get_lighting_coefficient(x, y, z + 1) + # Use red instead of black for spawnable blocks + if self.world.spawn and black_coeff > 0.8 and blockid in solid_blocks and not ( + blockid in nospawn_blocks or ( + z != 127 and (blocks[x,y,z+1] in solid_blocks or blocks[x,y,z+1] in fluid_blocks) + ) + ): + over_color = red_color + else: + over_color = black_color - # right face - black_coeff, face_occlude = self.get_lighting_coefficient(x, y + 1, z) - if not face_occlude: - composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[2]).enhance(black_coeff)) + if not face_occlude: + composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[0]).enhance(black_coeff)) - # Draw edge lines - if blockid in (44,): # step block - increment = 6 - elif blockid in (78,): # snow - increment = 9 - else: - increment = 0 + # left face + black_coeff, face_occlude = self.get_lighting_coefficient(x - 1, y, z) + if not face_occlude: + composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[1]).enhance(black_coeff)) - if blockid not in transparent_blocks or blockid in (78,): #special case snow so the outline is still drawn - draw = ImageDraw.Draw(img) - if x != 15 and blocks[x+1,y,z] == 0: - draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) - if y != 0 and blocks[x,y-1,z] == 0: - draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) + # right face + black_coeff, face_occlude = self.get_lighting_coefficient(x, y + 1, z) + if not face_occlude: + composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[2]).enhance(black_coeff)) + + # Draw edge lines + if blockid in (44,): # step block + increment = 6 + elif blockid in (78,): # snow + increment = 9 + else: + increment = 0 + + if blockid not in transparent_blocks or blockid in (78,): #special case snow so the outline is still drawn + draw = ImageDraw.Draw(img) + if x != 15 and blocks[x+1,y,z] == 0: + draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) + if y != 0 and blocks[x,y-1,z] == 0: + draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) for entity in tileEntities: From 8b6b5df8ef9a0b15718ca9415dec35eaea456e43 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 4 Mar 2011 23:21:23 -0500 Subject: [PATCH 2/5] handle get_worlds() returning None correctly Exposed by Issue #287: https://github.com/brownan/Minecraft-Overviewer/issues/287 --- gmap.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gmap.py b/gmap.py index 11f29ee..b5bd213 100755 --- a/gmap.py +++ b/gmap.py @@ -71,6 +71,13 @@ def main(): if not os.path.exists(worlddir): # world given is either world number, or name worlds = world.get_worlds() + + # if there are no worlds found at all, exit now + if not worlds: + print "Invalid world path" + parser.print_help() + sys.exit(1) + try: worldnum = int(worlddir) worlddir = worlds[worldnum]['path'] From 5cb599fabb31e053da0b6b4034d22d8c6aced519 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sun, 6 Mar 2011 15:35:40 -0500 Subject: [PATCH 3/5] added a more informative error if the map is too big for Overviewer to handle From Issue #290: https://github.com/brownan/Minecraft-Overviewer/issues/290 --- quadtree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quadtree.py b/quadtree.py index 15c428b..eefc7de 100644 --- a/quadtree.py +++ b/quadtree.py @@ -114,7 +114,7 @@ class QuadtreeGen(object): yradius >= worldobj.maxrow and -yradius <= worldobj.minrow: break else: - raise ValueError("Your map is waaaay too big!") + raise ValueError("Your map is waaaay too big! Use the '-z' or '--zoom' options.") self.p = p else: From a5612a1fa593c1086af899c8d3bf4d025be36245 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sun, 6 Mar 2011 15:48:48 -0500 Subject: [PATCH 4/5] moved corrupt chunk handling to get_lvldata so it applies to all chunk loading brought up in Issue #286: https://github.com/brownan/Minecraft-Overviewer/issues/286 --- chunk.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/chunk.py b/chunk.py index 8b74041..e8ab061 100644 --- a/chunk.py +++ b/chunk.py @@ -49,8 +49,13 @@ image def get_lvldata(filename, x, y): """Takes a filename and chunkcoords and returns the Level struct, which contains all the level info""" - - d = nbt.load_from_region(filename, x, y) + + try: + d = nbt.load_from_region(filename, x, y) + except Exception, e: + logging.warning("Error opening chunk (%i, %i) in %s. It may be corrupt. %s", x, y, filename, e) + raise ChunkCorrupt(str(e)) + if not d: raise NoSuchChunk(x,y) return d[1]['Level'] @@ -253,9 +258,6 @@ class ChunkRenderer(object): except NoSuchChunk, e: #logging.debug("Skipping non-existant chunk") raise - except Exception, e: - logging.warning("Error opening chunk file %s. It may be corrupt. %s", self.regionfile, e) - raise ChunkCorrupt(str(e)) return self._level level = property(_load_level) From 9c7d6a184dd2ff2ccf9b83669235adfbc65be53c Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Sun, 6 Mar 2011 17:34:26 -0500 Subject: [PATCH 5/5] Print error messages after print_help --- chunk.py | 2 +- gmap.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chunk.py b/chunk.py index e8ab061..48d80bb 100644 --- a/chunk.py +++ b/chunk.py @@ -256,7 +256,7 @@ class ChunkRenderer(object): try: self._level = get_lvldata(self.regionfile, self.chunkX, self.chunkY) except NoSuchChunk, e: - #logging.debug("Skipping non-existant chunk") + logging.debug("Skipping non-existant chunk") raise return self._level level = property(_load_level) diff --git a/gmap.py b/gmap.py index b5bd213..65545ad 100755 --- a/gmap.py +++ b/gmap.py @@ -74,8 +74,8 @@ def main(): # if there are no worlds found at all, exit now if not worlds: - print "Invalid world path" parser.print_help() + print "\nInvalid world path" sys.exit(1) try: @@ -87,13 +87,13 @@ def main(): worlddir = worlds[worlddir]['path'] except KeyError: # it's not a number, name, or path - print "Invalid world name or path" parser.print_help() + print "Invalid world name or path" sys.exit(1) except KeyError: # it was an invalid number - print "Invalid world number" parser.print_help() + print "Invalid world number" sys.exit(1) if not options.cachedir: