From cd6d482c58f8cf5eb9585c36cdbe21e92dfb6ba4 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 06:44:36 -0600 Subject: [PATCH 01/60] Add setting to change north direction --- sample.settings.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sample.settings.py b/sample.settings.py index 3b6aed2..e31cd1f 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -152,6 +152,13 @@ verbose = 1 if "web_assets_hook" in locals(): skipjs = True +################################################################################ +### north_direction +## Make north point somewhere else! +## default: upper-right +## Type: string +## Example: +north_direction = "upper-right" From 954fb18a4c9263dd52d54210721f85bf5ee00e0b Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 06:47:58 -0600 Subject: [PATCH 02/60] Parse commandline for changing north direction --- overviewer.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/overviewer.py b/overviewer.py index 1bf6790..6dfbd68 100755 --- a/overviewer.py +++ b/overviewer.py @@ -109,6 +109,7 @@ def main(): parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js") parser.add_option("--no-signs", dest="nosigns", action="store_true", help="Don't output signs to markers.js") parser.add_option("--display-config", dest="display_config", action="store_true", help="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True) + parser.add_option("--north-direction", dest="north_direction", help="Specifies which corner of the screen North will point to.", type="string", default="upper-right") #parser.add_option("--write-config", dest="write_config", action="store_true", help="Writes out a sample config file", commandLineOnly=True) options, args = parser.parse_args() @@ -209,6 +210,11 @@ def main(): optimizeimages.check_programs(optimizeimg) else: optimizeimg = None + + if options.north_direction: + north_direction = options.north_direction + else: + north_direction = 'upper-right' logging.getLogger().setLevel( logging.getLogger().level + 10*options.quiet) @@ -223,7 +229,8 @@ def main(): logging.info("Notice: Not using biome data for tinting") # First do world-level preprocessing - w = world.World(worlddir, useBiomeData=useBiomeData, regionlist=regionlist) + w = world.World(worlddir, useBiomeData=useBiomeData, regionlist=regionlist, + north_direction=north_direction) w.go(options.procs) logging.info("Rending the following tilesets: %s", ",".join(options.rendermode)) @@ -233,7 +240,7 @@ def main(): # create the quadtrees # TODO chunklist q = [] - qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender} + qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender, 'north_direction' : north_direction} for rendermode in options.rendermode: if rendermode == 'normal': qtree = quadtree.QuadtreeGen(w, destdir, rendermode=rendermode, tiledir='tiles', **qtree_args) From 7380ed08de0f4fb77474e74578abdc210bb849d1 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 06:49:37 -0600 Subject: [PATCH 03/60] Make chunk to tile coords aware of new north --- world.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/world.py b/world.py index 1344d10..abc995d 100644 --- a/world.py +++ b/world.py @@ -69,9 +69,11 @@ class World(object): mincol = maxcol = minrow = maxrow = 0 - def __init__(self, worlddir, useBiomeData=False,regionlist=None): + def __init__(self, worlddir, useBiomeData=False,regionlist=None, + north_direction="upper-right"): self.worlddir = worlddir self.useBiomeData = useBiomeData + self.north_direction = north_direction #find region files, or load the region list #this also caches all the region file header info @@ -182,17 +184,27 @@ class World(object): in the chunk coordinate system, and figures out the row and column in the image each one should be. Returns (col, row).""" - # columns are determined by the sum of the chunk coords, rows are the - # difference (TODO: be able to change direction of north) # change this function, and you MUST change unconvert_coords - return (chunkx + chunky, chunky - chunkx) + if self.north_direction == 'lower-left': + return (chunkx + chunky, chunky - chunkx) + elif self.north_direction == 'lower-right': + return (chunkx + chunky, chunkx - chunky) + elif self.north_direction == 'upper-left': + return (chunkx - chunky, chunkx + chunky) + elif self.north_direction == 'upper-right': + return (chunky - chunkx, chunkx + chunky) def unconvert_coords(self, col, row): """Undoes what convert_coords does. Returns (chunkx, chunky).""" - # col + row = chunky + chunky => (col + row)/2 = chunky - # col - row = chunkx + chunkx => (col - row)/2 = chunkx - return ((col - row) / 2, (col + row) / 2) + if self.north_direction == 'lower-left': + return ((col - row) / 2, (row + col) / 2) + if self.north_direction == 'lower-right': + return ((col + row) / 2, (col - row) / 2) + if self.north_direction == 'upper-left': + return ((col + row) / 2, (row - col) / 2) + if self.north_direction == 'upper-right': + return ((row - col) / 2, (col + row) / 2) def findTrueSpawn(self): """Adds the true spawn location to self.POI. The spawn Y coordinate From f2882512e7dd5a43dc942237af10d28c9f5960fd Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:01:17 -0600 Subject: [PATCH 04/60] Make quadtree flip axis on some north directions This should not be handled here, but maybe in nbt? This was quick to find and modify, that's all. --- quadtree.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/quadtree.py b/quadtree.py index fc00b01..9ccb04f 100644 --- a/quadtree.py +++ b/quadtree.py @@ -49,7 +49,7 @@ def iterate_base4(d): return itertools.product(xrange(4), repeat=d) class QuadtreeGen(object): - def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal"): + def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='upper-right'): """Generates a quadtree from the world given into the given dest directory @@ -66,6 +66,7 @@ class QuadtreeGen(object): self.optimizeimg = optimizeimg self.bgcolor = bgcolor self.rendermode = rendermode + self.north_direction = north_direction # force png renderformat if we're using an overlay mode if 'overlay' in get_render_mode_inheritance(rendermode): @@ -239,6 +240,10 @@ class QuadtreeGen(object): continue chunkx, chunky = unconvert_coords(col, row) + if self.north_direction == 'upper-right': + chunky = -chunky + elif self.north_direction == 'lower-right': + chunkx = -chunkx regionx_ = chunkx//32 regiony_ = chunky//32 @@ -468,7 +473,7 @@ class QuadtreeGen(object): # draw the chunk! try: - a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue) + a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue, self.north_direction) a.chunk_render(tileimg, xpos, ypos, None) except chunk.ChunkCorrupt: # an error was already printed From f0ea678cb623f78642d7ca34b601386bb0733ca2 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:03:46 -0600 Subject: [PATCH 05/60] Google maps js needs to know north-direction --- googlemap.py | 3 +++ web_assets/overviewerConfig.js | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/googlemap.py b/googlemap.py index 48fede1..07e082d 100644 --- a/googlemap.py +++ b/googlemap.py @@ -71,6 +71,7 @@ class MapGen(object): self.web_assets_hook = configInfo.get('web_assets_hook', None) self.web_assets_path = configInfo.get('web_assets_path', None) self.bg_color = configInfo.get('bg_color') + self.north_direction = configInfo.get('north_direction', 'upper-right') if not len(quadtrees) > 0: raise ValueError("there must be at least one quadtree to work on") @@ -109,6 +110,8 @@ class MapGen(object): "{minzoom}", str(0)) config = config.replace( "{maxzoom}", str(zoomlevel)) + config = config.replace( + "{north_direction}", self.north_direction) config = config.replace("{spawn_coords}", json.dumps(list(self.world.spawn))) diff --git a/web_assets/overviewerConfig.js b/web_assets/overviewerConfig.js index ce38479..22097d0 100644 --- a/web_assets/overviewerConfig.js +++ b/web_assets/overviewerConfig.js @@ -79,7 +79,11 @@ var overviewerConfig = { * Set to true to turn on debug mode, which adds a grid to the map along * with co-ordinates and a bunch of console output. */ - 'debug': false + 'debug': false, + /** + * Set which way north points. + */ + 'north_direction': '{north_direction}' }, /** * Group definitions for objects that are partially selectable (signs and From f306a2ecffbac51503a11efd4cd3292421994f17 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:06:20 -0600 Subject: [PATCH 06/60] Google maps JS handle north_direction Modifies coords conversion to handle configurable north direction --- web_assets/overviewer.js | 48 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index ea87a19..10ec81a 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -492,6 +492,22 @@ var overviewer = { var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * Math.pow(2, overviewerConfig.map.maxZoom)); + if(overviewerConfig.map.north_direction == 'lower-left'){ + x = x; + y = y; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + x = -x-1; + y = -y-1; + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + temp = x; + x = -y-1; + y = temp-16; + } else if(overviewerConfig.map.north_direction == 'lower-right'){ + temp = x; + x = y; + y = -temp-1+16; + } + // This information about where the center column is may change with // a different drawing implementation -- check it again after any // drawing overhauls! @@ -500,8 +516,14 @@ var overviewer = { // so the Y coordinate is at 0.5, and the X is at 0.5 - // ((tileSize / 2) / (tileSize * 2^maxZoom)) // or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) - var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - var lat = 0.5; + lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + lat = 0.5; + if(overviewerConfig.map.north_direction == 'upper-right'){ + lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } + // the following metrics mimic those in ChunkRenderer.chunk_render // in "chunk.py" or, equivalently, chunk_render in src/iterate.c @@ -549,6 +571,12 @@ var overviewer = { lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); lat -= 0.5; + if(overviewerConfig.map.north_direction == 'upper-right'){ + lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } + // I'll admit, I plugged this into Wolfram Alpha: // a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) // And I don't know the math behind solving for for X and Z given @@ -563,6 +591,22 @@ var overviewer = { point.x += 64; point.z -= 64; + if(overviewerConfig.map.north_direction == 'lower-left'){ + point.x = point.x; + point.z = point.z; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + point.x = -point.x; + point.z = -point.z; + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + temp = point.z; + point.z = -point.x; + point.x = temp+16; + } else if(overviewerConfig.map.north_direction == 'lower-right'){ + temp = point.z; + point.z = point.x; + point.x = -temp+16; + } + return point; }, /** From 722b814873c6f594244e558ff71da688f8a6fdd1 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:18:35 -0600 Subject: [PATCH 07/60] Rotate each chunk based on north direction --- chunk.py | 66 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/chunk.py b/chunk.py index f8aa1f6..4ba13c8 100644 --- a/chunk.py +++ b/chunk.py @@ -56,6 +56,7 @@ def get_lvldata(world, filename, x, y, retries=2): try: d = world.load_from_region(filename, x, y) + #TODO should probably do rotation from here except Exception, e: if retries > 0: # wait a little bit, and try again (up to `retries` times) @@ -70,21 +71,21 @@ def get_lvldata(world, filename, x, y, retries=2): if not d: raise NoSuchChunk(x,y) return d -def get_blockarray(level): +def get_blockarray(level, north_direction): """Takes the level struct as returned from get_lvldata, and returns the Block array, which just contains all the block ids""" - return numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) + return numpy.rot90(numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), get_north_rotations(north_direction)) -def get_blockarray_fromfile(filename): +def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" d = nbt.load_from_region(filename, x, y) level = d[1]['Level'] - return get_blockarray(level) + return get_blockarray(level, north_direction) -def get_skylight_array(level): +def get_skylight_array(level, north_direction): """Returns the skylight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" - skylight = numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)) + skylight = numpy.rot90(numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) # this array is 2 blocks per byte, so expand it skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits @@ -93,26 +94,36 @@ def get_skylight_array(level): skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4 return skylight_expanded -def get_blocklight_array(level): +def get_blocklight_array(level, north_direction): """Returns the blocklight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" # expand just like get_skylight_array() - blocklight = numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)) + blocklight = numpy.rot90(numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) blocklight_expanded[:,:,::2] = blocklight & 0x0F blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4 return blocklight_expanded -def get_blockdata_array(level): +def get_blockdata_array(level, north_direction): """Returns the ancillary data from the 'Data' byte array. Data is packed in a similar manner to skylight data""" - return numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)) + return numpy.rot90(numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) def get_tileentity_data(level): """Returns the TileEntities TAG_List from chunk dat file""" data = level['TileEntities'] return data +def get_north_rotations(north_direction): + if north_direction == "upper-left": + return 1 + elif north_direction == "upper-right": + return 2 + elif north_direction == "lower-right": + return 3 + elif north_direction == "lower-left": + return 0 + # This set holds blocks ids that can be seen through, for occlusion calculations transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, @@ -138,13 +149,14 @@ class NoSuchChunk(Exception): pass class ChunkRenderer(object): - def __init__(self, chunkcoords, worldobj, rendermode, queue): + def __init__(self, chunkcoords, worldobj, rendermode, queue, north_direction): """Make a new chunk renderer for the given chunk coordinates. chunkcoors should be a tuple: (chunkX, chunkY) cachedir is a directory to save the resulting chunk images to """ self.queue = queue + self.north_direction = north_direction self.regionfile = worldobj.get_region_path(*chunkcoords) #if not os.path.exists(self.regionfile): @@ -181,21 +193,21 @@ class ChunkRenderer(object): def _load_blocks(self): """Loads and returns the block array""" if not hasattr(self, "_blocks"): - self._blocks = get_blockarray(self._load_level()) + self._blocks = get_blockarray(self._load_level(), self.north_direction) return self._blocks blocks = property(_load_blocks) def _load_skylight(self): """Loads and returns skylight array""" if not hasattr(self, "_skylight"): - self._skylight = get_skylight_array(self.level) + self._skylight = get_skylight_array(self.level, self.north_direction) return self._skylight skylight = property(_load_skylight) def _load_blocklight(self): """Loads and returns blocklight array""" if not hasattr(self, "_blocklight"): - self._blocklight = get_blocklight_array(self.level) + self._blocklight = get_blocklight_array(self.level, self.north_direction) return self._blocklight blocklight = property(_load_blocklight) @@ -204,9 +216,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX - 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX - 1, self.chunkY) - self._left_skylight = get_skylight_array(chunk_data) - self._left_blocklight = get_blocklight_array(chunk_data) - self._left_blocks = get_blockarray(chunk_data) + self._left_skylight = get_skylight_array(chunk_data, self.north_direction) + self._left_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._left_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._left_skylight = None self._left_blocklight = None @@ -238,9 +250,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY + 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY + 1) - self._right_skylight = get_skylight_array(chunk_data) - self._right_blocklight = get_blocklight_array(chunk_data) - self._right_blocks = get_blockarray(chunk_data) + self._right_skylight = get_skylight_array(chunk_data, self.north_direction) + self._right_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._right_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._right_skylight = None self._right_blocklight = None @@ -272,9 +284,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX + 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX + 1, self.chunkY) - self._up_right_skylight = get_skylight_array(chunk_data) - self._up_right_blocklight = get_blocklight_array(chunk_data) - self._up_right_blocks = get_blockarray(chunk_data) + self._up_right_skylight = get_skylight_array(chunk_data, self.north_direction) + self._up_right_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._up_right_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._up_right_skylight = None self._up_right_blocklight = None @@ -299,9 +311,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY - 1) - self._up_left_skylight = get_skylight_array(chunk_data) - self._up_left_blocklight = get_blocklight_array(chunk_data) - self._up_left_blocks = get_blockarray(chunk_data) + self._up_left_skylight = get_skylight_array(chunk_data, self.north_direction) + self._up_left_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._up_left_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._up_left_skylight = None self._up_left_blocklight = None @@ -391,7 +403,7 @@ class ChunkRenderer(object): rendered, and blocks are drawn with a color tint depending on their depth.""" - blockData = get_blockdata_array(self.level) + blockData = get_blockdata_array(self.level, self.north_direction) blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits blockData_expanded[:,:,::2] = blockData & 0x0F From e81db44ea991c240de27a1f6fead74d63a891ca6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:42:50 -0600 Subject: [PATCH 08/60] Fix bad coords for upper-left and -right north --- web_assets/overviewer.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 10ec81a..08f85b2 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -516,11 +516,11 @@ var overviewer = { // so the Y coordinate is at 0.5, and the X is at 0.5 - // ((tileSize / 2) / (tileSize * 2^maxZoom)) // or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) - lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); lat = 0.5; - if(overviewerConfig.map.north_direction == 'upper-right'){ - lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'lower-left' || + overviewerConfig.map.north_direction == 'lower-right'){ + lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else{ lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); } @@ -568,12 +568,12 @@ var overviewer = { // Revert base positioning // See equivalent code in fromWorldToLatLng() - lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); lat -= 0.5; - if(overviewerConfig.map.north_direction == 'upper-right'){ - lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'lower-left' || + overviewerConfig.map.north_direction == 'lower-right'){ + lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else{ lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); } From e732eb6450c2de8dfe847a837d14fb44eb8c13ff Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 21:05:45 -0600 Subject: [PATCH 09/60] A smarter (?) attempt at rotating everything. --- chunk.py | 5 ++--- nbt.py | 29 ++++++++++++++++++++++------- quadtree.py | 4 ---- world.py | 25 +++++++------------------ 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/chunk.py b/chunk.py index 4ba13c8..0921e54 100644 --- a/chunk.py +++ b/chunk.py @@ -55,8 +55,7 @@ def get_lvldata(world, filename, x, y, retries=2): raise NoSuchChunk try: - d = world.load_from_region(filename, x, y) - #TODO should probably do rotation from here + d = world.load_from_region(filename, x, y) except Exception, e: if retries > 0: # wait a little bit, and try again (up to `retries` times) @@ -78,7 +77,7 @@ def get_blockarray(level, north_direction): def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" - d = nbt.load_from_region(filename, x, y) + d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] return get_blockarray(level, north_direction) diff --git a/nbt.py b/nbt.py index aa30700..7a095e8 100644 --- a/nbt.py +++ b/nbt.py @@ -17,6 +17,7 @@ import gzip, zlib import struct import StringIO import os +import numpy # decorator to handle filename or object as first parameter def _file_loader(func): @@ -34,15 +35,15 @@ def _file_loader(func): def load(fileobj): return NBTFileReader(fileobj).read_all() -def load_from_region(filename, x, y): - nbt = load_region(filename).load_chunk(x, y) +def load_from_region(filename, x, y, north_direction): + nbt = load_region(filename, north_direction).load_chunk(x, y) if nbt is None: return None ## return none. I think this is who we should indicate missing chunks #raise IOError("No such chunk in region: (%i, %i)" % (x, y)) return nbt.read_all() -def load_region(filename): - return MCRFileReader(filename) +def load_region(filename, north_direction): + return MCRFileReader(filename, north_direction) # compile the unpacker's into a classes @@ -199,13 +200,25 @@ class MCRFileReader(object): chunks (as instances of NBTFileReader), getting chunk timestamps, and for listing chunks contained in the file.""" - def __init__(self, filename): + def __init__(self, filename, north_direction): self._file = None self._filename = filename + self.north_direction = north_direction # cache used when the entire header tables are read in get_chunks() self._locations = None self._timestamps = None self._chunks = None + + def get_north_rotations(self): + #Upper-left and lower-right are swapped from chunk.py rots + if self.north_direction == "upper-left": + return 3 + elif self.north_direction == "upper-right": + return 2 + elif self.north_direction == "lower-right": + return 1 + elif self.north_direction == "lower-left": + return 0 def _read_24bit_int(self): """Read in a 24-bit, big-endian int, used in the chunk @@ -328,11 +341,13 @@ class MCRFileReader(object): locations_append = self._locations.append for _ in xrange(32*32): locations_append(self._read_chunk_location()) + self._locations = numpy.reshape(numpy.rot90(numpy.reshape(self._locations, (32,32)),self.get_north_rotations()), -1) # read chunk timestamp table timestamp_append = self._timestamps.append for _ in xrange(32*32): timestamp_append(self._read_chunk_timestamp()) + self._timestamps = numpy.reshape(numpy.rot90(numpy.reshape(self._timestamps, (32,32)),self.get_north_rotations()), -1) if closeFile: self.closefile() @@ -344,10 +359,10 @@ class MCRFileReader(object): load_chunk(), this will wrap x and y into the range [0, 31]. """ x = x % 32 - y = y % 32 + y = y % 32 if self._timestamps is None: self.get_chunk_info() - return self._timestamps[x + y * 32] + return self._timestamps[x + y * 32] def chunkExists(self, x, y): """Determines if a chunk exists without triggering loading of the backend data""" diff --git a/quadtree.py b/quadtree.py index 9ccb04f..fe5002f 100644 --- a/quadtree.py +++ b/quadtree.py @@ -240,10 +240,6 @@ class QuadtreeGen(object): continue chunkx, chunky = unconvert_coords(col, row) - if self.north_direction == 'upper-right': - chunky = -chunky - elif self.north_direction == 'lower-right': - chunkx = -chunkx regionx_ = chunkx//32 regiony_ = chunky//32 diff --git a/world.py b/world.py index abc995d..a53b823 100644 --- a/world.py +++ b/world.py @@ -169,7 +169,7 @@ class World(object): if self.regions.get(filename) is not None: self.regions[filename][0].closefile() chunkcache = {} - mcr = nbt.MCRFileReader(filename) + mcr = nbt.MCRFileReader(filename, self.north_direction) self.regions[filename] = (mcr,os.path.getmtime(filename),chunkcache) return mcr @@ -185,26 +185,12 @@ class World(object): in the image each one should be. Returns (col, row).""" # change this function, and you MUST change unconvert_coords - if self.north_direction == 'lower-left': - return (chunkx + chunky, chunky - chunkx) - elif self.north_direction == 'lower-right': - return (chunkx + chunky, chunkx - chunky) - elif self.north_direction == 'upper-left': - return (chunkx - chunky, chunkx + chunky) - elif self.north_direction == 'upper-right': - return (chunky - chunkx, chunkx + chunky) + return (chunkx + chunky, chunky - chunkx) def unconvert_coords(self, col, row): """Undoes what convert_coords does. Returns (chunkx, chunky).""" - if self.north_direction == 'lower-left': - return ((col - row) / 2, (row + col) / 2) - if self.north_direction == 'lower-right': - return ((col + row) / 2, (col - row) / 2) - if self.north_direction == 'upper-left': - return ((col + row) / 2, (row - col) / 2) - if self.north_direction == 'upper-right': - return ((row - col) / 2, (col + row) / 2) + return ((col - row) / 2, (row + col) / 2) def findTrueSpawn(self): """Adds the true spawn location to self.POI. The spawn Y coordinate @@ -225,7 +211,10 @@ class World(object): chunkFile = self.get_region_path(chunkX, chunkY) if chunkFile is not None: - data = nbt.load_from_region(chunkFile, chunkX, chunkY)[1] + #TODO I broke it + + #data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1] + data = None if data is not None: level = data['Level'] blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) From 6124ffd0edcae99c47bfaedc36bfb4781fcded2f Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 21:06:09 -0600 Subject: [PATCH 10/60] Early work on rotating stubborn textures --- rendernode.py | 3 ++- textures.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/rendernode.py b/rendernode.py index 6f6c76e..10db6c6 100644 --- a/rendernode.py +++ b/rendernode.py @@ -65,7 +65,8 @@ def pool_initializer(rendernode): # make sure textures are generated for this process # and initialize c_overviewer - textures.generate(path=rendernode.options.get('textures_path', None)) + textures.generate(north_direction=rendernode.options.get('north_direction', None), + path=rendernode.options.get('textures_path', None)) c_overviewer.init_chunk_render() # load biome data in each process, if needed diff --git a/textures.py b/textures.py index 5e2db3b..61ea8af 100644 --- a/textures.py +++ b/textures.py @@ -500,7 +500,7 @@ def generate_texture_tuple(img, blockid): blockmap list and specialblockmap dictionary.""" return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) -def generate_special_texture(blockID, data): +def generate_special_texture(blockID, data, north_direction): """Generates a special texture, such as a correctly facing minecraft track""" #print "%s has ancillary data: %X" %(blockID, data) # TODO ladders, stairs, levers, buttons, and signs @@ -594,6 +594,11 @@ def generate_special_texture(blockID, data): if blockID == 26: # bed + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0x8 | 0x2 + elif (data & 0x3) == 1: data = data & 0x8 | 0x3 + elif (data & 0x3) == 2: data = data & 0x8 | 0x0 + elif (data & 0x3) == 3: data = data & 0x8 | 0x1 increment = 5 left_face = None right_face = None @@ -708,6 +713,11 @@ def generate_special_texture(blockID, data): if blockID in (50,75,76): # torch, off redstone torch, on redstone torch + if north_direction == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 # choose the proper texture if blockID == 50: # torch @@ -777,6 +787,11 @@ def generate_special_texture(blockID, data): if blockID in (53,67): # wooden and cobblestone stairs. + if north_direction == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 if blockID == 53: # wooden texture = terrain_images[4] @@ -988,6 +1003,11 @@ def generate_special_texture(blockID, data): if blockID in (61, 62, 23): #furnace and burning furnace + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 top = terrain_images[62] side = terrain_images[45] @@ -1018,6 +1038,8 @@ def generate_special_texture(blockID, data): # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + if north_direction == 'upper-right': + data = (data + 8) % 16 # If the signpost is looking directly to the image, draw some # random dots, they will look as text. if data in (0,1,2,3,4,5,15): @@ -1070,6 +1092,11 @@ def generate_special_texture(blockID, data): # mask out the high bits to figure out the orientation img = Image.new("RGBA", (24,24), (38,92,255,0)) + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0xc | 0x2 + elif (data & 0x3) == 1: data = data & 0xc | 0x3 + elif (data & 0x3) == 2: data = data & 0xc | 0x0 + elif (data & 0x3) == 3: data = data & 0xc | 0x1 if (data & 0x03) == 0: if not swung: tex = transform_image_side(raw_door) @@ -1108,6 +1135,11 @@ def generate_special_texture(blockID, data): if blockID == 65: # ladder + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 img = Image.new("RGBA", (24,24), (38,92,255,0)) raw_texture = terrain_images[83] #print "ladder is facing: %d" % data @@ -1154,6 +1186,17 @@ def generate_special_texture(blockID, data): raw_straight = terrain_images[128] raw_corner = terrain_images[112] + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + + elif data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 7 + ## use transform_image to scale and shear if data == 0: track = transform_image(raw_straight, blockID) @@ -1202,6 +1245,11 @@ def generate_special_texture(blockID, data): if blockID == 68: # wall sign + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 texture = terrain_images[4].copy() # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) @@ -1332,6 +1380,11 @@ def generate_special_texture(blockID, data): if blockID in (86,91): # pumpkins, jack-o-lantern + if north_direction == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 top = terrain_images[102] frontID = 119 if blockID == 86 else 120 front = terrain_images[frontID] @@ -1393,6 +1446,11 @@ def generate_special_texture(blockID, data): if blockID in (93, 94): # redstone repeaters, ON and OFF # NOTE: this function uses the redstone torches generated above, # this must run after the function of the torches. + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0xc | 0x2 + elif (data & 0x3) == 1: data = data & 0xc | 0x3 + elif (data & 0x3) == 2: data = data & 0xc | 0x0 + elif (data & 0x3) == 3: data = data & 0xc | 0x1 top = terrain_images[131] if blockID == 93 else terrain_images[147] side = terrain_images[5] @@ -1509,6 +1567,11 @@ def generate_special_texture(blockID, data): if blockID == 96: # trapdoor + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0x4 | 0x1 + elif (data & 0x3) == 1: data = data & 0x4 | 0x0 + elif (data & 0x3) == 2: data = data & 0x4 | 0x3 + elif (data & 0x3) == 3: data = data & 0x4 | 0x2 texture = terrain_images[84] if data & 0x4 == 0x4: # opened trapdoor if data & 0x3 == 0: # west @@ -1664,7 +1727,7 @@ biome_tall_fern_texture = None biome_leaf_texture = None specialblockmap = None -def generate(path=None): +def generate(north_direction, path=None): global _find_file_local_path _find_file_local_path = path @@ -1689,4 +1752,4 @@ def generate(path=None): specialblockmap = {} for blockID in special_blocks: for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) + specialblockmap[(blockID, data)] = generate_special_texture(blockID, data, north_direction) From 1b06da4e737ed3f9036ec3ff18bc0a776df7e0aa Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 10:44:55 -0600 Subject: [PATCH 11/60] Rotate region coords based on configured north --- world.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/world.py b/world.py index a53b823..b5ce5a0 100644 --- a/world.py +++ b/world.py @@ -189,7 +189,6 @@ class World(object): def unconvert_coords(self, col, row): """Undoes what convert_coords does. Returns (chunkx, chunky).""" - return ((col - row) / 2, (row + col) / 2) def findTrueSpawn(self): @@ -285,6 +284,7 @@ class World(object): world. Returns (regionx, regiony, filename)""" + join = os.path.join if regionlist is not None: for path in regionlist: @@ -293,7 +293,20 @@ class World(object): if f.startswith("r.") and f.endswith(".mcr"): p = f.split(".") logging.debug("Using path %s from regionlist", f) - yield (int(p[1]), int(p[2]), join(self.worlddir, 'region', f)) + x = int(p[1]) + y = int(p[2]) + if self.north_direction == 'upper-right': + x = -x + y = -y + elif self.north_direction == 'upper-left': + temp = x + x = -y + y = temp + elif self.north_direction == 'lower-right': + temp = x + x = y + y = -temp + yield (x, y, join(self.worlddir, 'region', f)) else: logging.warning("Ignore path '%s' in regionlist", f) @@ -301,7 +314,20 @@ class World(object): for path in glob(os.path.join(self.worlddir, 'region') + "/r.*.*.mcr"): dirpath, f = os.path.split(path) p = f.split(".") - yield (int(p[1]), int(p[2]), join(dirpath, f)) + x = int(p[1]) + y = int(p[2]) + if self.north_direction == 'upper-right': + x = -x + y = -y + elif self.north_direction == 'upper-left': + temp = x + x = -y + y = temp + elif self.north_direction == 'lower-right': + temp = x + x = y + y = -temp + yield (x, y, join(dirpath, f)) def get_save_dir(): """Returns the path to the local saves directory From d32cc2aa1089d30fdf6201b1eb0a1721335e6e38 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 12:21:47 -0600 Subject: [PATCH 12/60] Update coord conversion on map to handle new rotation option --- web_assets/overviewer.js | 45 +++++++++++++--------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 08f85b2..424cdc4 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -492,20 +492,17 @@ var overviewer = { var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * Math.pow(2, overviewerConfig.map.maxZoom)); - if(overviewerConfig.map.north_direction == 'lower-left'){ - x = x; - y = y; - } else if(overviewerConfig.map.north_direction == 'upper-right'){ - x = -x-1; - y = -y-1; + if(overviewerConfig.map.north_direction == 'upper-right'){ + x = -x-1+512; + y = -y-1+512; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = x; - x = -y-1; - y = temp-16; + x = -y-1+512; + y = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = x; x = y; - y = -temp-1+16; + y = -temp-1+512; } // This information about where the center column is may change with @@ -517,13 +514,7 @@ var overviewer = { // ((tileSize / 2) / (tileSize * 2^maxZoom)) // or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) lat = 0.5; - if(overviewerConfig.map.north_direction == 'lower-left' || - overviewerConfig.map.north_direction == 'lower-right'){ - lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else{ - lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } - + lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); // the following metrics mimic those in ChunkRenderer.chunk_render // in "chunk.py" or, equivalently, chunk_render in src/iterate.c @@ -570,12 +561,7 @@ var overviewer = { // See equivalent code in fromWorldToLatLng() lat -= 0.5; - if(overviewerConfig.map.north_direction == 'lower-left' || - overviewerConfig.map.north_direction == 'lower-right'){ - lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else{ - lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } + lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); // I'll admit, I plugged this into Wolfram Alpha: // a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) @@ -591,20 +577,17 @@ var overviewer = { point.x += 64; point.z -= 64; - if(overviewerConfig.map.north_direction == 'lower-left'){ - point.x = point.x; - point.z = point.z; - } else if(overviewerConfig.map.north_direction == 'upper-right'){ - point.x = -point.x; - point.z = -point.z; + if(overviewerConfig.map.north_direction == 'upper-right'){ + point.x = -point.x+512; + point.z = -point.z+512; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = point.z; - point.z = -point.x; - point.x = temp+16; + point.z = -point.x+512; + point.x = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = point.z; point.z = point.x; - point.x = -temp+16; + point.x = -temp+512; } return point; From 3f3ca1979f7b1134b37238140296133ae89b0e91 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 15:47:45 -0600 Subject: [PATCH 13/60] Move special texture rotation to it's own function --- textures.py | 156 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 67 deletions(-) diff --git a/textures.py b/textures.py index 61ea8af..97f8b51 100644 --- a/textures.py +++ b/textures.py @@ -505,7 +505,9 @@ def generate_special_texture(blockID, data, north_direction): #print "%s has ancillary data: %X" %(blockID, data) # TODO ladders, stairs, levers, buttons, and signs # all need to behandled here (and in chunkpy) - + + data = convert_data(blockID, data, north_direction) + if blockID == 2: # grass # data & 0x10 means SNOW sides side_img = terrain_images[3] @@ -594,11 +596,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 26: # bed - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0x8 | 0x2 - elif (data & 0x3) == 1: data = data & 0x8 | 0x3 - elif (data & 0x3) == 2: data = data & 0x8 | 0x0 - elif (data & 0x3) == 3: data = data & 0x8 | 0x1 increment = 5 left_face = None right_face = None @@ -713,12 +710,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (50,75,76): # torch, off redstone torch, on redstone torch - if north_direction == 'upper-right': - if data == 1: data = 2 - elif data == 2: data = 1 - elif data == 3: data = 4 - elif data == 4: data = 3 - # choose the proper texture if blockID == 50: # torch small = terrain_images[80] @@ -787,11 +778,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (53,67): # wooden and cobblestone stairs. - if north_direction == 'upper-right': - if data == 0: data = 1 - elif data == 1: data = 0 - elif data == 2: data = 3 - elif data == 3: data = 2 if blockID == 53: # wooden texture = terrain_images[4] @@ -1003,11 +989,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (61, 62, 23): #furnace and burning furnace - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 top = terrain_images[62] side = terrain_images[45] @@ -1033,13 +1014,10 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 63: # singposts - texture = terrain_images[4].copy() # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - if north_direction == 'upper-right': - data = (data + 8) % 16 # If the signpost is looking directly to the image, draw some # random dots, they will look as text. if data in (0,1,2,3,4,5,15): @@ -1092,11 +1070,6 @@ def generate_special_texture(blockID, data, north_direction): # mask out the high bits to figure out the orientation img = Image.new("RGBA", (24,24), (38,92,255,0)) - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0xc | 0x2 - elif (data & 0x3) == 1: data = data & 0xc | 0x3 - elif (data & 0x3) == 2: data = data & 0xc | 0x0 - elif (data & 0x3) == 3: data = data & 0xc | 0x1 if (data & 0x03) == 0: if not swung: tex = transform_image_side(raw_door) @@ -1135,11 +1108,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 65: # ladder - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 img = Image.new("RGBA", (24,24), (38,92,255,0)) raw_texture = terrain_images[83] #print "ladder is facing: %d" % data @@ -1186,17 +1154,6 @@ def generate_special_texture(blockID, data, north_direction): raw_straight = terrain_images[128] raw_corner = terrain_images[112] - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - - elif data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 7 - ## use transform_image to scale and shear if data == 0: track = transform_image(raw_straight, blockID) @@ -1245,11 +1202,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 68: # wall sign - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 texture = terrain_images[4].copy() # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) @@ -1380,11 +1332,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (86,91): # pumpkins, jack-o-lantern - if north_direction == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 top = terrain_images[102] frontID = 119 if blockID == 86 else 120 front = terrain_images[frontID] @@ -1446,11 +1393,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (93, 94): # redstone repeaters, ON and OFF # NOTE: this function uses the redstone torches generated above, # this must run after the function of the torches. - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0xc | 0x2 - elif (data & 0x3) == 1: data = data & 0xc | 0x3 - elif (data & 0x3) == 2: data = data & 0xc | 0x0 - elif (data & 0x3) == 3: data = data & 0xc | 0x1 top = terrain_images[131] if blockID == 93 else terrain_images[147] side = terrain_images[5] @@ -1567,11 +1509,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 96: # trapdoor - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0x4 | 0x1 - elif (data & 0x3) == 1: data = data & 0x4 | 0x0 - elif (data & 0x3) == 2: data = data & 0x4 | 0x3 - elif (data & 0x3) == 3: data = data & 0x4 | 0x2 texture = terrain_images[84] if data & 0x4 == 0x4: # opened trapdoor if data & 0x3 == 0: # west @@ -1588,9 +1525,94 @@ def generate_special_texture(blockID, data, north_direction): return generate_texture_tuple(img, blockID) - return None +def convert_data(blockID, data, north_direction): + if blockID == 26: # bed + if north_direction == 'upper-right': + #Masked to not clobber block head/foot info + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + if blockID in (27, 28, 66): # minetrack: + if north_direction == 'upper-right': + #Masked to not clobber powered rail on/off info + #Ascending + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + if blockID == 66: #normal minetrack only + if north_direction == 'upper-right': + #Corners + if data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 7 + if blockID in (50, 75, 76): # torch, off/on redstone torch + if north_direction == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 + if blockID in (53,67): # wooden and cobblestone stairs. + if north_direction == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + if blockID in (61, 62, 23): # furnace and burning furnace + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + if blockID == 63: # signposts + if north_direction == 'upper-right': + data = (data + 8) % 16 + if blockID in (64,71): # wooden door, or iron door + if north_direction == 'upper-right': + #Masked to not clobber block top/bottom & swung info + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + if blockID == 65: # ladder + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + if blockID == 68: # wall sign + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + if blockID in (86,91): # pumpkins, jack-o-lantern + if north_direction == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + if blockID in (93, 94): # redstone repeaters, ON and OFF + if north_direction == 'upper-right': + #Masked to not clobber delay info + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + if blockID == 96: # trapdoor + if north_direction == 'upper-right': + #Masked to not clobber opened/closed info + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + return data + def tintTexture(im, c): # apparently converting to grayscale drops the alpha channel? i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c) From d0f98223e4631b8392301c6c4054e2074af27508 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:01:46 -0600 Subject: [PATCH 14/60] Enhance north-direction commandline option --- overviewer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/overviewer.py b/overviewer.py index 74c5414..79550fe 100755 --- a/overviewer.py +++ b/overviewer.py @@ -96,6 +96,7 @@ def main(): cpus = 1 avail_rendermodes = c_overviewer.get_render_modes() + avail_north_dirs = ['lower-left', 'upper-left', 'upper-right', 'lower-right'] parser = ConfigOptionParser(usage=helptext, config="settings.py") parser.add_option("-V", "--version", dest="version", help="Displays version information and then exits", action="store_true") @@ -118,7 +119,7 @@ def main(): parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js") parser.add_option("--no-signs", dest="nosigns", action="store_true", help="Don't output signs to markers.js") parser.add_option("--display-config", dest="display_config", action="store_true", help="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True) - parser.add_option("--north-direction", dest="north_direction", help="Specifies which corner of the screen North will point to.", type="string", default="upper-right") + parser.add_option("--north-direction", dest="north_direction", help="Specifies which corner of the screen north will point to. Valid options are: " + ", ".join(avail_north_dirs) + ".", type="choice", default=avail_north_dirs[0], choices=avail_north_dirs) #parser.add_option("--write-config", dest="write_config", action="store_true", help="Writes out a sample config file", commandLineOnly=True) options, args = parser.parse_args() From 1fdd143a983453c609c7b36954510efa518a1939 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:03:28 -0600 Subject: [PATCH 15/60] Explain available north-directions in sample settings --- sample.settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sample.settings.py b/sample.settings.py index e31cd1f..01fcf2b 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -155,6 +155,7 @@ if "web_assets_hook" in locals(): ################################################################################ ### north_direction ## Make north point somewhere else! +## Valid options are 'lower-left', 'upper-left', 'upper-right', 'upper-left' ## default: upper-right ## Type: string ## Example: From eebd0a27409e739c21cb8ee3bb36f92671ce1bfb Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:17:07 -0600 Subject: [PATCH 16/60] Add info on north-direction to readme --- README.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.rst b/README.rst index 942e4cb..5a8ee1a 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,28 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +Additional Features +------------------- +A configurable north direction! I'm not a big fan of north point to the bottom +left corner of the screen, and I bet you aren't either. So here you go. Either +use the commandline option --north-direction, or in the settings file use +north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', +and 'lower-right'. + +Additional Bugs +--------------- +The upper-right direction seems to exacerbate an existing bug that will +not let you click on a sign in some cases. The upper-right direction seems to +mostly always trigger this bug. Still investigating. And banging my head on +my desk. + +Some blocks don't rotate properly in the upper-left and lower-right directions. + +Upstream +-------- +Once I've squashed some bugs and cleaned up the code a bit, I will look to +getting this included in the official Minecraft Overviewer. + Features ======== From caef1afaf0a64a8f0e7ad3f4c2d4431b1fe843de Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:17:07 -0600 Subject: [PATCH 17/60] Add info on north-direction to readme --- README.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.rst b/README.rst index 942e4cb..6d2c400 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,25 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +###Additional Features +A configurable north direction! I'm not a big fan of north point to the bottom +left corner of the screen, and I bet you aren't either. So here you go. Either +use the commandline option --north-direction, or in the settings file use +north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', +and 'lower-right'. + +###Additional Bugs +The upper-right direction seems to exacerbate an existing bug that will +not let you click on a sign in some cases. The upper-right direction seems to +mostly always trigger this bug. Still investigating. And banging my head on +my desk. + +Some blocks don't rotate properly in the upper-left and lower-right directions. + +###Upstream +Once I've squashed some bugs and cleaned up the code a bit, I will look to +getting this included in the official Minecraft Overviewer. + Features ======== From afe25eed219068f9fb0f269a0bd784fef4503bb6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:23:17 -0600 Subject: [PATCH 18/60] Readme modification again. --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 6d2c400..d38be21 100644 --- a/README.rst +++ b/README.rst @@ -22,14 +22,14 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -###Additional Features +####Additional Features A configurable north direction! I'm not a big fan of north point to the bottom left corner of the screen, and I bet you aren't either. So here you go. Either use the commandline option --north-direction, or in the settings file use north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', and 'lower-right'. -###Additional Bugs +####Additional Bugs The upper-right direction seems to exacerbate an existing bug that will not let you click on a sign in some cases. The upper-right direction seems to mostly always trigger this bug. Still investigating. And banging my head on @@ -37,7 +37,7 @@ my desk. Some blocks don't rotate properly in the upper-left and lower-right directions. -###Upstream +####Upstream Once I've squashed some bugs and cleaned up the code a bit, I will look to getting this included in the official Minecraft Overviewer. From 3a6194a6f1971bafd251e760e5bba4391d964238 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:24:23 -0600 Subject: [PATCH 19/60] I give up. Markdown is apparently not for me. --- README.rst | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/README.rst b/README.rst index d38be21..942e4cb 100644 --- a/README.rst +++ b/README.rst @@ -22,25 +22,6 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -####Additional Features -A configurable north direction! I'm not a big fan of north point to the bottom -left corner of the screen, and I bet you aren't either. So here you go. Either -use the commandline option --north-direction, or in the settings file use -north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', -and 'lower-right'. - -####Additional Bugs -The upper-right direction seems to exacerbate an existing bug that will -not let you click on a sign in some cases. The upper-right direction seems to -mostly always trigger this bug. Still investigating. And banging my head on -my desk. - -Some blocks don't rotate properly in the upper-left and lower-right directions. - -####Upstream -Once I've squashed some bugs and cleaned up the code a bit, I will look to -getting this included in the official Minecraft Overviewer. - Features ======== From 110bd10569d142edad9a32a595d1b9ed46b3f8e6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 21:42:24 -0600 Subject: [PATCH 20/60] Handle more regions This is much, much slower than numpy would do it. I should probably do this right sometime, but right now I just want it to work. --- nbt.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/nbt.py b/nbt.py index 7a095e8..d5c8e96 100644 --- a/nbt.py +++ b/nbt.py @@ -339,9 +339,21 @@ class MCRFileReader(object): # read chunk location table locations_append = self._locations.append - for _ in xrange(32*32): + locations_index = [] + for x in xrange(32*32): locations_append(self._read_chunk_location()) - self._locations = numpy.reshape(numpy.rot90(numpy.reshape(self._locations, (32,32)),self.get_north_rotations()), -1) + locations_index.append(x) + try: + temp = numpy.reshape(self._locations, (32, 32)) + temp = numpy.rot90(temp, self.get_north_rotations()) + self._locations = numpy.reshape(temp, -1) + except ValueError: + temp = numpy.reshape(locations_index, (32, 32)) + temp = numpy.rot90(temp, self.get_north_rotations()) + temp = numpy.reshape(temp, -1) + ttemp = self._locations[:] + for i, e in enumerate(temp): + self._locations[i] = ttemp[e] # read chunk timestamp table timestamp_append = self._timestamps.append From acfdc5ed4bbbde0490dd43a7ff40805a9949e885 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Tue, 12 Jul 2011 11:51:38 -0600 Subject: [PATCH 21/60] Update readme with information on changes --- README.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.rst b/README.rst index 942e4cb..c966cc7 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,36 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +### Additional Features + +A configurable north direction! I'm not a big fan of north pointing to the +bottom left corner of the screen, and I bet you aren't either. So here you go! +Either use the commandline option --north-direction, or use the option +north_direction in the settings file. Valid options are 'lower-left', +'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', +myself. + +### Additional Bugs + +With very small testing maps, the upper-right direction seems to exacerbate an +existing bug that will not let you click on a sign in some cases. This is a +bang-head-on-desk sort of thing, I should be able to find it but I cannot. +Somehow it does seem to work fine on my actual SMP map, but there is a good +chance that is just luck. + +Some blocks don't rotate properly in the upper-left and lower-right directions. +This is an easy fix, it just takes time. And besides, the upper-right is what +you are really looking for, anyway. Probably. + +It's slow! One of the rotation methods is very, very inefficient. It needs to be +rewritten, but I don't seem to be smart enough to do it right now. I must +investigate! No hard numbers but I will run a benchmark soon. + +### Upstream + +Once I've squashed some bugs and cleaned up the code a bit, I will see about +getting this included in the official Minecraft Overviewer. + Features ======== From 679b27aba9135376b91b64cd6b7a5d886c810bbb Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Tue, 12 Jul 2011 10:56:15 -0700 Subject: [PATCH 22/60] Edited README.rst via GitHub --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index c966cc7..fb25d4a 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,8 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +## Configurable North changes + ### Additional Features A configurable north direction! I'm not a big fan of north pointing to the From 56a5f42cd00fc5ad2dda56933b44c0cbf79635d0 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 13:27:18 -0600 Subject: [PATCH 23/60] Add rough benchmark to Readme --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index fb25d4a..050ea06 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,8 @@ you are really looking for, anyway. Probably. It's slow! One of the rotation methods is very, very inefficient. It needs to be rewritten, but I don't seem to be smart enough to do it right now. I must -investigate! No hard numbers but I will run a benchmark soon. +investigate! On my SMP map the initial render took 30% longer vs the original. +Be warned. ### Upstream From e26059be383ec36d1cbba71b2cf0927dc9374014 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:01:04 -0600 Subject: [PATCH 24/60] Add special texture rotation for upper-left and lower-right --- textures.py | 172 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 22 deletions(-) diff --git a/textures.py b/textures.py index 97f8b51..2713761 100644 --- a/textures.py +++ b/textures.py @@ -1529,87 +1529,215 @@ def generate_special_texture(blockID, data, north_direction): def convert_data(blockID, data, north_direction): if blockID == 26: # bed - if north_direction == 'upper-right': - #Masked to not clobber block head/foot info + #Masked to not clobber block head/foot info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 4 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID in (27, 28, 66): # minetrack: - if north_direction == 'upper-right': - #Masked to not clobber powered rail on/off info - #Ascending + #Masked to not clobber powered rail on/off info + #Ascending and flat straight + if north_direction == 'upper-left': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north_direction == 'upper-right': if (data & 0b0111) == 2: data = data & 0b1000 | 3 elif (data & 0b0111) == 3: data = data & 0b1000 | 2 elif (data & 0b0111) == 4: data = data & 0b1000 | 5 elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - if blockID == 66: #normal minetrack only - if north_direction == 'upper-right': - #Corners + elif north_direction == 'lower-right': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + if blockID == 66: # normal minetrack only + #Corners + if north_direction == 'upper-left': + if data == 6: data = 7 + elif data == 7: data = 8 + elif data == 8: data = 6 + elif data == 9: data = 9 + elif north_direction == 'upper-right': if data == 6: data = 8 elif data == 7: data = 9 elif data == 8: data = 6 elif data == 9: data = 7 + elif north_direction == 'lower-right': + if data == 6: data = 9 + elif data == 7: data = 6 + elif data == 8: data = 8 + elif data == 9: data = 7 if blockID in (50, 75, 76): # torch, off/on redstone torch - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + elif north_direction == 'upper-right': if data == 1: data = 2 elif data == 2: data = 1 elif data == 3: data = 4 elif data == 4: data = 3 + elif north_direction == 'lower-right': + if data == 1: data = 4 + elif data == 2: data = 3 + elif data == 3: data = 1 + elif data == 4: data = 2 if blockID in (53,67): # wooden and cobblestone stairs. - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north_direction == 'upper-right': if data == 0: data = 1 elif data == 1: data = 0 elif data == 2: data = 3 elif data == 3: data = 2 + elif north_direction == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 if blockID in (61, 62, 23): # furnace and burning furnace - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north_direction == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 + elif north_direction == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 if blockID == 63: # signposts - if north_direction == 'upper-right': + if north_direction == 'upper-left': + data = (data + 4) % 16 + elif north_direction == 'upper-right': data = (data + 8) % 16 - if blockID in (64,71): # wooden door, or iron door - if north_direction == 'upper-right': - #Masked to not clobber block top/bottom & swung info + elif north_direction == 'lower-right': + data = (data + 12) % 16 + if blockID in (64,71): # wooden/iron door + #Masked to not clobber block top/bottom & swung info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 65: # ladder - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north_direction == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 + elif north_direction == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 if blockID == 68: # wall sign - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north_direction == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 + elif north_direction == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 if blockID in (86,91): # pumpkins, jack-o-lantern - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north_direction == 'upper-right': if data == 0: data = 2 elif data == 1: data = 3 elif data == 2: data = 0 elif data == 3: data = 1 + elif north_direction == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 if blockID in (93, 94): # redstone repeaters, ON and OFF - if north_direction == 'upper-right': - #Masked to not clobber delay info + #Masked to not clobber delay info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 96: # trapdoor - if north_direction == 'upper-right': - #Masked to not clobber opened/closed info + #Masked to not clobber opened/closed info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 return data From 0ffb13eafca5c85494dad8b6151e7f5bf620f123 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:10:09 -0600 Subject: [PATCH 25/60] Remove one 'Additional Bug' from README --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 050ea06..4f066e0 100644 --- a/README.rst +++ b/README.rst @@ -22,9 +22,11 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -## Configurable North changes +Configurable North changes +========================== -### Additional Features +Additional Features +------------------- A configurable north direction! I'm not a big fan of north pointing to the bottom left corner of the screen, and I bet you aren't either. So here you go! @@ -33,7 +35,8 @@ north_direction in the settings file. Valid options are 'lower-left', 'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', myself. -### Additional Bugs +Additional Bugs +--------------- With very small testing maps, the upper-right direction seems to exacerbate an existing bug that will not let you click on a sign in some cases. This is a @@ -41,16 +44,13 @@ bang-head-on-desk sort of thing, I should be able to find it but I cannot. Somehow it does seem to work fine on my actual SMP map, but there is a good chance that is just luck. -Some blocks don't rotate properly in the upper-left and lower-right directions. -This is an easy fix, it just takes time. And besides, the upper-right is what -you are really looking for, anyway. Probably. - It's slow! One of the rotation methods is very, very inefficient. It needs to be rewritten, but I don't seem to be smart enough to do it right now. I must investigate! On my SMP map the initial render took 30% longer vs the original. Be warned. -### Upstream +Upstream +-------- Once I've squashed some bugs and cleaned up the code a bit, I will see about getting this included in the official Minecraft Overviewer. From 6e3a732eb855977c6ed7ebf4bfe1808dae5625c3 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:10:50 -0600 Subject: [PATCH 26/60] Rotate compass rose when rotating map --- .../{compass.png => compass_lower-left.png} | Bin web_assets/compass_lower-right.png | Bin 0 -> 7067 bytes web_assets/compass_upper-left.png | Bin 0 -> 7086 bytes web_assets/compass_upper-right.png | Bin 0 -> 7075 bytes web_assets/overviewerConfig.js | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename web_assets/{compass.png => compass_lower-left.png} (100%) create mode 100644 web_assets/compass_lower-right.png create mode 100644 web_assets/compass_upper-left.png create mode 100644 web_assets/compass_upper-right.png diff --git a/web_assets/compass.png b/web_assets/compass_lower-left.png similarity index 100% rename from web_assets/compass.png rename to web_assets/compass_lower-left.png diff --git a/web_assets/compass_lower-right.png b/web_assets/compass_lower-right.png new file mode 100644 index 0000000000000000000000000000000000000000..8990e7ee563b66f6baa6e134e5b4286f1a9897d7 GIT binary patch literal 7067 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 TSuMrDz`)??>gTe~DWM4fGX%S; literal 0 HcmV?d00001 diff --git a/web_assets/compass_upper-left.png b/web_assets/compass_upper-left.png new file mode 100644 index 0000000000000000000000000000000000000000..60e12d7abb3ae90b26c65a050a27f3eb47677fb5 GIT binary patch literal 7086 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 literal 0 HcmV?d00001 diff --git a/web_assets/overviewerConfig.js b/web_assets/overviewerConfig.js index 22097d0..6a39735 100644 --- a/web_assets/overviewerConfig.js +++ b/web_assets/overviewerConfig.js @@ -14,7 +14,7 @@ var overviewerConfig = { 'image': { 'defaultMarker': 'signpost.png', 'signMarker': 'signpost_icon.png', - 'compass': 'compass.png', + 'compass': 'compass_{north_direction}.png', 'spawnMarker': 'http://google-maps-icons.googlecode.com/files/home.png', 'queryMarker': 'http://google-maps-icons.googlecode.com/files/regroup.png' }, From 32abe88efd0f6c59a3ac7b785cee45fb6863d46f Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:29:08 -0600 Subject: [PATCH 27/60] Show actual default as default for north-direction --- sample.settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample.settings.py b/sample.settings.py index 01fcf2b..cccdac8 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -156,7 +156,7 @@ if "web_assets_hook" in locals(): ### north_direction ## Make north point somewhere else! ## Valid options are 'lower-left', 'upper-left', 'upper-right', 'upper-left' -## default: upper-right +## default: lower-left ## Type: string ## Example: north_direction = "upper-right" From 284199c557c6dd859ac3314bea4e41bf90a29476 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:39:58 -0600 Subject: [PATCH 28/60] More info in README --- README.rst | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 4f066e0..1535941 100644 --- a/README.rst +++ b/README.rst @@ -33,27 +33,31 @@ bottom left corner of the screen, and I bet you aren't either. So here you go! Either use the commandline option --north-direction, or use the option north_direction in the settings file. Valid options are 'lower-left', 'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', -myself. +myself. Defaults to 'lower-left' to match official behavior. Additional Bugs --------------- -With very small testing maps, the upper-right direction seems to exacerbate an +Sometimes the upper- and lower-right direction seems to exacerbate an existing bug that will not let you click on a sign in some cases. This is a -bang-head-on-desk sort of thing, I should be able to find it but I cannot. -Somehow it does seem to work fine on my actual SMP map, but there is a good -chance that is just luck. +bang-head-on-desk sort of thing, I should be able to find it but I just haven't +yet. It does seem to work fine on my actual SMP map, for what that's worth. It's slow! One of the rotation methods is very, very inefficient. It needs to be rewritten, but I don't seem to be smart enough to do it right now. I must -investigate! On my SMP map the initial render took 30% longer vs the original. -Be warned. +investigate! On my SMP map the initial render took 30% longer vs the official +Minecraft Overviewer. Totally worth it, though. + +So far only blocks where direction is important (minecart tracks, doors, signs, +etc) rotate. Blocks like grass, stone, gravel, etc do not, however for the most +part it isn't noticeable because they look very similar no matter which +direction we are viewing them from. Upstream -------- -Once I've squashed some bugs and cleaned up the code a bit, I will see about -getting this included in the official Minecraft Overviewer. +Once I've squashed the additional bugs and cleaned up the code a bit, I will +see about getting this included in the official Minecraft Overviewer. Features ======== From 59594ee44225305ba6c54ef92ac79599253c6149 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Thu, 14 Jul 2011 16:36:10 -0600 Subject: [PATCH 29/60] Clean up region rotation a bit. --- nbt.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/nbt.py b/nbt.py index d5c8e96..994b0e8 100644 --- a/nbt.py +++ b/nbt.py @@ -331,35 +331,24 @@ class MCRFileReader(object): self.openfile() self._chunks = None - self._locations = [] + self._locations = [0]*32*32 self._timestamps = [] # go to the beginning of the file self._file.seek(0) # read chunk location table - locations_append = self._locations.append - locations_index = [] - for x in xrange(32*32): - locations_append(self._read_chunk_location()) - locations_index.append(x) - try: - temp = numpy.reshape(self._locations, (32, 32)) - temp = numpy.rot90(temp, self.get_north_rotations()) - self._locations = numpy.reshape(temp, -1) - except ValueError: - temp = numpy.reshape(locations_index, (32, 32)) - temp = numpy.rot90(temp, self.get_north_rotations()) - temp = numpy.reshape(temp, -1) - ttemp = self._locations[:] - for i, e in enumerate(temp): - self._locations[i] = ttemp[e] + locations_index = numpy.reshape(numpy.rot90(numpy.reshape(range(32*32), + (32, 32)), self.get_north_rotations()), -1) + for i in locations_index: + self._locations[i] = self._read_chunk_location() # read chunk timestamp table timestamp_append = self._timestamps.append for _ in xrange(32*32): timestamp_append(self._read_chunk_timestamp()) - self._timestamps = numpy.reshape(numpy.rot90(numpy.reshape(self._timestamps, (32,32)),self.get_north_rotations()), -1) + self._timestamps = numpy.reshape(numpy.rot90(numpy.reshape( + self._timestamps, (32,32)),self.get_north_rotations()), -1) if closeFile: self.closefile() From 077552b53b0200a7805b6052cba053691bd050e8 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 17 Jul 2011 08:47:57 -0600 Subject: [PATCH 30/60] Cache rotated chunk data This results in a small performance gain for rotations, but still noticeably slower than with no rotation. --- chunk.py | 64 ++++++++++++++++++++++------------------------------- nbt.py | 5 ++--- quadtree.py | 2 +- world.py | 22 ++++++++++++++++++ 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/chunk.py b/chunk.py index 0921e54..6f8ccba 100644 --- a/chunk.py +++ b/chunk.py @@ -70,21 +70,22 @@ def get_lvldata(world, filename, x, y, retries=2): if not d: raise NoSuchChunk(x,y) return d -def get_blockarray(level, north_direction): +def get_blockarray(level): """Takes the level struct as returned from get_lvldata, and returns the Block array, which just contains all the block ids""" - return numpy.rot90(numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), get_north_rotations(north_direction)) + return level['Blocks'] def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" + #TODO Update this for configurable-north d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] - return get_blockarray(level, north_direction) + return get_blockarray(level) -def get_skylight_array(level, north_direction): +def get_skylight_array(level): """Returns the skylight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" - skylight = numpy.rot90(numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) + skylight = level['SkyLight'] # this array is 2 blocks per byte, so expand it skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits @@ -93,36 +94,26 @@ def get_skylight_array(level, north_direction): skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4 return skylight_expanded -def get_blocklight_array(level, north_direction): +def get_blocklight_array(level): """Returns the blocklight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" # expand just like get_skylight_array() - blocklight = numpy.rot90(numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) + blocklight = level['BlockLight'] blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) blocklight_expanded[:,:,::2] = blocklight & 0x0F blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4 return blocklight_expanded -def get_blockdata_array(level, north_direction): +def get_blockdata_array(level): """Returns the ancillary data from the 'Data' byte array. Data is packed in a similar manner to skylight data""" - return numpy.rot90(numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) + return level['Data'] def get_tileentity_data(level): """Returns the TileEntities TAG_List from chunk dat file""" data = level['TileEntities'] return data -def get_north_rotations(north_direction): - if north_direction == "upper-left": - return 1 - elif north_direction == "upper-right": - return 2 - elif north_direction == "lower-right": - return 3 - elif north_direction == "lower-left": - return 0 - # This set holds blocks ids that can be seen through, for occlusion calculations transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, @@ -148,14 +139,13 @@ class NoSuchChunk(Exception): pass class ChunkRenderer(object): - def __init__(self, chunkcoords, worldobj, rendermode, queue, north_direction): + def __init__(self, chunkcoords, worldobj, rendermode, queue): """Make a new chunk renderer for the given chunk coordinates. chunkcoors should be a tuple: (chunkX, chunkY) cachedir is a directory to save the resulting chunk images to """ self.queue = queue - self.north_direction = north_direction self.regionfile = worldobj.get_region_path(*chunkcoords) #if not os.path.exists(self.regionfile): @@ -192,21 +182,21 @@ class ChunkRenderer(object): def _load_blocks(self): """Loads and returns the block array""" if not hasattr(self, "_blocks"): - self._blocks = get_blockarray(self._load_level(), self.north_direction) + self._blocks = get_blockarray(self._load_level()) return self._blocks blocks = property(_load_blocks) def _load_skylight(self): """Loads and returns skylight array""" if not hasattr(self, "_skylight"): - self._skylight = get_skylight_array(self.level, self.north_direction) + self._skylight = get_skylight_array(self.level) return self._skylight skylight = property(_load_skylight) def _load_blocklight(self): """Loads and returns blocklight array""" if not hasattr(self, "_blocklight"): - self._blocklight = get_blocklight_array(self.level, self.north_direction) + self._blocklight = get_blocklight_array(self.level) return self._blocklight blocklight = property(_load_blocklight) @@ -215,9 +205,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX - 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX - 1, self.chunkY) - self._left_skylight = get_skylight_array(chunk_data, self.north_direction) - self._left_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._left_blocks = get_blockarray(chunk_data, self.north_direction) + self._left_skylight = get_skylight_array(chunk_data) + self._left_blocklight = get_blocklight_array(chunk_data) + self._left_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._left_skylight = None self._left_blocklight = None @@ -249,9 +239,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY + 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY + 1) - self._right_skylight = get_skylight_array(chunk_data, self.north_direction) - self._right_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._right_blocks = get_blockarray(chunk_data, self.north_direction) + self._right_skylight = get_skylight_array(chunk_data) + self._right_blocklight = get_blocklight_array(chunk_data) + self._right_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._right_skylight = None self._right_blocklight = None @@ -283,9 +273,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX + 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX + 1, self.chunkY) - self._up_right_skylight = get_skylight_array(chunk_data, self.north_direction) - self._up_right_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._up_right_blocks = get_blockarray(chunk_data, self.north_direction) + self._up_right_skylight = get_skylight_array(chunk_data) + self._up_right_blocklight = get_blocklight_array(chunk_data) + self._up_right_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._up_right_skylight = None self._up_right_blocklight = None @@ -310,9 +300,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY - 1) - self._up_left_skylight = get_skylight_array(chunk_data, self.north_direction) - self._up_left_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._up_left_blocks = get_blockarray(chunk_data, self.north_direction) + self._up_left_skylight = get_skylight_array(chunk_data) + self._up_left_blocklight = get_blocklight_array(chunk_data) + self._up_left_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._up_left_skylight = None self._up_left_blocklight = None @@ -402,7 +392,7 @@ class ChunkRenderer(object): rendered, and blocks are drawn with a color tint depending on their depth.""" - blockData = get_blockdata_array(self.level, self.north_direction) + blockData = get_blockdata_array(self.level) blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits blockData_expanded[:,:,::2] = blockData & 0x0F diff --git a/nbt.py b/nbt.py index 994b0e8..b027026 100644 --- a/nbt.py +++ b/nbt.py @@ -210,13 +210,12 @@ class MCRFileReader(object): self._chunks = None def get_north_rotations(self): - #Upper-left and lower-right are swapped from chunk.py rots if self.north_direction == "upper-left": - return 3 + return 1 elif self.north_direction == "upper-right": return 2 elif self.north_direction == "lower-right": - return 1 + return 3 elif self.north_direction == "lower-left": return 0 diff --git a/quadtree.py b/quadtree.py index fe5002f..ceb543e 100644 --- a/quadtree.py +++ b/quadtree.py @@ -469,7 +469,7 @@ class QuadtreeGen(object): # draw the chunk! try: - a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue, self.north_direction) + a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue) a.chunk_render(tileimg, xpos, ypos, None) except chunk.ChunkCorrupt: # an error was already printed diff --git a/world.py b/world.py index b5ce5a0..aa1509d 100644 --- a/world.py +++ b/world.py @@ -153,6 +153,18 @@ class World(object): data = nbt.read_all() level = data[1]['Level'] chunk_data = level + chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( + level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), + self._get_north_rotations()) + chunk_data['Data'] = numpy.rot90(numpy.frombuffer( + level['Data'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( + level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( + level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) #chunk_data = {} #chunk_data['skylight'] = chunk.get_skylight_array(level) #chunk_data['blocklight'] = chunk.get_blocklight_array(level) @@ -276,6 +288,16 @@ class World(object): self.findTrueSpawn() + def _get_north_rotations(self): + if self.north_direction == "upper-left": + return 1 + elif self.north_direction == "upper-right": + return 2 + elif self.north_direction == "lower-right": + return 3 + elif self.north_direction == "lower-left": + return 0 + def _iterate_regionfiles(self,regionlist=None): """Returns an iterator of all of the region files, along with their coordinates From b6a2447b9cfbaa43cb246b8c187e236b8d8520e6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 23 Jul 2011 07:51:55 -0600 Subject: [PATCH 31/60] Fix trapdoor rotations --- textures.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/textures.py b/textures.py index 2713761..e126c9e 100644 --- a/textures.py +++ b/textures.py @@ -1724,8 +1724,8 @@ def convert_data(blockID, data, north_direction): if blockID == 96: # trapdoor #Masked to not clobber opened/closed info if north_direction == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 elif north_direction == 'upper-right': @@ -1734,8 +1734,8 @@ def convert_data(blockID, data, north_direction): elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 elif north_direction == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 From 5afdff09cc921ba94f989c951e36b7710452c0bb Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 23 Jul 2011 11:23:14 -0600 Subject: [PATCH 32/60] Really make lower-left the default everywhere --- googlemap.py | 2 +- overviewer.py | 2 +- quadtree.py | 2 +- world.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/googlemap.py b/googlemap.py index 07e082d..a6c6724 100644 --- a/googlemap.py +++ b/googlemap.py @@ -71,7 +71,7 @@ class MapGen(object): self.web_assets_hook = configInfo.get('web_assets_hook', None) self.web_assets_path = configInfo.get('web_assets_path', None) self.bg_color = configInfo.get('bg_color') - self.north_direction = configInfo.get('north_direction', 'upper-right') + self.north_direction = configInfo.get('north_direction', 'lower-left') if not len(quadtrees) > 0: raise ValueError("there must be at least one quadtree to work on") diff --git a/overviewer.py b/overviewer.py index 79550fe..371793b 100755 --- a/overviewer.py +++ b/overviewer.py @@ -224,7 +224,7 @@ def main(): if options.north_direction: north_direction = options.north_direction else: - north_direction = 'upper-right' + north_direction = 'lower-left' logging.getLogger().setLevel( logging.getLogger().level + 10*options.quiet) diff --git a/quadtree.py b/quadtree.py index ceb543e..4f46879 100644 --- a/quadtree.py +++ b/quadtree.py @@ -49,7 +49,7 @@ def iterate_base4(d): return itertools.product(xrange(4), repeat=d) class QuadtreeGen(object): - def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='upper-right'): + def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='lower-left'): """Generates a quadtree from the world given into the given dest directory diff --git a/world.py b/world.py index aa1509d..0df5c66 100644 --- a/world.py +++ b/world.py @@ -70,7 +70,7 @@ class World(object): mincol = maxcol = minrow = maxrow = 0 def __init__(self, worlddir, useBiomeData=False,regionlist=None, - north_direction="upper-right"): + north_direction="lower-left"): self.worlddir = worlddir self.useBiomeData = useBiomeData self.north_direction = north_direction From 5728bc4051e17f23d316180bed30197b27c293ea Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 23 Jul 2011 11:25:17 -0600 Subject: [PATCH 33/60] Rotate biome data --- textures.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/textures.py b/textures.py index e126c9e..7ce64ea 100644 --- a/textures.py +++ b/textures.py @@ -1784,8 +1784,25 @@ def getBiomeData(worlddir, chunkX, chunkY): ''' global currentBiomeFile, currentBiomeData + biomeX = chunkX // 32 + biomeY = chunkY // 32 + rots = 0 + if _north == 'upper-right': + biomeX = -biomeX + biomeY = -biomeY + rots = 2 + elif _north == 'lower-right': + temp = biomeX + biomeX = -biomeY + biomeY = temp + rots = 1 + elif _north == 'upper-left': + temp = biomeX + biomeX = biomeY + biomeY = -temp + rots = 3 - biomeFile = "b.%d.%d.biome" % (chunkX // 32, chunkY // 32) + biomeFile = "b.%d.%d.biome" % (biomeX, biomeY) if biomeFile == currentBiomeFile: return currentBiomeData @@ -1795,7 +1812,9 @@ def getBiomeData(worlddir, chunkX, chunkY): # make sure the file size is correct if not len(rawdata) == 512 * 512 * 2: raise Exception("Biome file %s is not valid." % (biomeFile,)) - data = numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2")) + data = numpy.reshape(numpy.rot90(numpy.reshape( + numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2")), + (512,512)),rots), -1) except IOError: data = None pass # no biome data @@ -1878,6 +1897,8 @@ biome_leaf_texture = None specialblockmap = None def generate(north_direction, path=None): + global _north + _north = north_direction global _find_file_local_path _find_file_local_path = path From 4ac5dfc2a8efc69f87a2034bcf7e7721afa801a7 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 14:37:04 -0600 Subject: [PATCH 34/60] Fix regions being shifted by one during rotation This fixes exaggerated tile counts in progress updates, reduces the calls to rendernode (for a small performance increase), and shifts the problem of unclickable signs in UR orientation further away. --- textures.py | 8 ++++---- web_assets/overviewer.js | 16 ++++++++-------- world.py | 16 ++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/textures.py b/textures.py index 7ce64ea..8662ad1 100644 --- a/textures.py +++ b/textures.py @@ -1788,18 +1788,18 @@ def getBiomeData(worlddir, chunkX, chunkY): biomeY = chunkY // 32 rots = 0 if _north == 'upper-right': - biomeX = -biomeX - biomeY = -biomeY + biomeX = -biomeX-1 + biomeY = -biomeY-1 rots = 2 elif _north == 'lower-right': temp = biomeX - biomeX = -biomeY + biomeX = -biomeY-1 biomeY = temp rots = 1 elif _north == 'upper-left': temp = biomeX biomeX = biomeY - biomeY = -temp + biomeY = -temp-1 rots = 3 biomeFile = "b.%d.%d.biome" % (biomeX, biomeY) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 424cdc4..aca2d8a 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -493,16 +493,16 @@ var overviewer = { Math.pow(2, overviewerConfig.map.maxZoom)); if(overviewerConfig.map.north_direction == 'upper-right'){ - x = -x-1+512; - y = -y-1+512; + x = -x-1; + y = -y-1; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = x; - x = -y-1+512; + x = -y-1; y = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = x; x = y; - y = -temp-1+512; + y = -temp-1; } // This information about where the center column is may change with @@ -578,16 +578,16 @@ var overviewer = { point.z -= 64; if(overviewerConfig.map.north_direction == 'upper-right'){ - point.x = -point.x+512; - point.z = -point.z+512; + point.x = -point.x; + point.z = -point.z; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = point.z; - point.z = -point.x+512; + point.z = -point.x; point.x = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = point.z; point.z = point.x; - point.x = -temp+512; + point.x = -temp; } return point; diff --git a/world.py b/world.py index 0df5c66..fe35379 100644 --- a/world.py +++ b/world.py @@ -318,16 +318,16 @@ class World(object): x = int(p[1]) y = int(p[2]) if self.north_direction == 'upper-right': - x = -x - y = -y + x = -x-1 + y = -y-1 elif self.north_direction == 'upper-left': temp = x - x = -y + x = -y-1 y = temp elif self.north_direction == 'lower-right': temp = x x = y - y = -temp + y = -temp-1 yield (x, y, join(self.worlddir, 'region', f)) else: logging.warning("Ignore path '%s' in regionlist", f) @@ -339,16 +339,16 @@ class World(object): x = int(p[1]) y = int(p[2]) if self.north_direction == 'upper-right': - x = -x - y = -y + x = -x-1 + y = -y-1 elif self.north_direction == 'upper-left': temp = x - x = -y + x = -y-1 y = temp elif self.north_direction == 'lower-right': temp = x x = y - y = -temp + y = -temp-1 yield (x, y, join(dirpath, f)) def get_save_dir(): From 80664ba4f1ead73fbf88de697407ece50c0f363d Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 15:54:38 -0600 Subject: [PATCH 35/60] Update get_blockarray_fromfile for rotation --- chunk.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/chunk.py b/chunk.py index 6f8ccba..908df1f 100644 --- a/chunk.py +++ b/chunk.py @@ -77,10 +77,22 @@ def get_blockarray(level): def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" - #TODO Update this for configurable-north d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] - return get_blockarray(level) + chunk_data = level + chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( + level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), + self._get_north_rotations()) + chunk_data['Data'] = numpy.rot90(numpy.frombuffer( + level['Data'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( + level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( + level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + return get_blockarray(chunk_data) def get_skylight_array(level): """Returns the skylight array. This is 4 bits per block, but it is From 4725b4b12552a8a8d2f711e7dc8fd963d586bc55 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 15:55:51 -0600 Subject: [PATCH 36/60] Fix findTrueSpawn with rotations --- world.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/world.py b/world.py index fe35379..1d580d8 100644 --- a/world.py +++ b/world.py @@ -210,9 +210,20 @@ class World(object): ## read spawn info from level.dat data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1] - spawnX = data['Data']['SpawnX'] + disp_spawnX = spawnX = data['Data']['SpawnX'] spawnY = data['Data']['SpawnY'] - spawnZ = data['Data']['SpawnZ'] + disp_spawnZ = spawnZ = data['Data']['SpawnZ'] + if self.north_direction == 'upper-right': + spawnX = -spawnX + spawnZ = -spawnZ + elif self.north_direction == 'upper-left': + temp = spawnX + spawnX = -spawnZ + spawnZ = temp + elif self.north_direction == 'lower-right': + temp = spawnX + spawnX = spawnZ + spawnZ = -temp ## The chunk that holds the spawn location chunkX = spawnX/16 @@ -222,10 +233,7 @@ class World(object): chunkFile = self.get_region_path(chunkX, chunkY) if chunkFile is not None: - #TODO I broke it - - #data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1] - data = None + data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1] if data is not None: level = data['Level'] blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) @@ -240,9 +248,9 @@ class World(object): if spawnY == 128: break - self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ, + self.POI.append( dict(x=disp_spawnX, y=spawnY, z=disp_spawnZ, msg="Spawn", type="spawn", chunk=(chunkX, chunkY))) - self.spawn = (spawnX, spawnY, spawnZ) + self.spawn = (disp_spawnX, spawnY, disp_spawnZ) def go(self, procs): """Scan the world directory, to fill in From 0035a367a8970b1c9cb58c896e230991db65d8f8 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 22:15:55 -0600 Subject: [PATCH 37/60] Only rotate Block data when asking for block data --- overviewer_core/chunk.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index b6116dd..3920a13 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -83,15 +83,6 @@ def get_blockarray_fromfile(filename, north_direction): chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), self._get_north_rotations()) - chunk_data['Data'] = numpy.rot90(numpy.frombuffer( - level['Data'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( - level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( - level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) return get_blockarray(chunk_data) def get_skylight_array(level): From 9af931b4a3d91364f7afa6161cae8cab6da04366 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 22:47:25 -0600 Subject: [PATCH 38/60] Super performance fixer! Rotations no longer have any significant performance hit. Allocate rotated array once during rotation instead of during every chunk render. --- README.rst | 5 ----- overviewer_core/world.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 1535941..bf3e700 100644 --- a/README.rst +++ b/README.rst @@ -43,11 +43,6 @@ existing bug that will not let you click on a sign in some cases. This is a bang-head-on-desk sort of thing, I should be able to find it but I just haven't yet. It does seem to work fine on my actual SMP map, for what that's worth. -It's slow! One of the rotation methods is very, very inefficient. It needs to be -rewritten, but I don't seem to be smart enough to do it right now. I must -investigate! On my SMP map the initial render took 30% longer vs the official -Minecraft Overviewer. Totally worth it, though. - So far only blocks where direction is important (minecart tracks, doors, signs, etc) rotate. Blocks like grass, stone, gravel, etc do not, however for the most part it isn't noticeable because they look very similar no matter which diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 6ca3d86..2bba9dd 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -153,18 +153,18 @@ class World(object): data = nbt.read_all() level = data[1]['Level'] chunk_data = level - chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( + chunk_data['Blocks'] = numpy.array(numpy.rot90(numpy.frombuffer( level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), - self._get_north_rotations()) - chunk_data['Data'] = numpy.rot90(numpy.frombuffer( + self._get_north_rotations())) + chunk_data['Data'] = numpy.array(numpy.rot90(numpy.frombuffer( level['Data'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( + self._get_north_rotations())) + chunk_data['SkyLight'] = numpy.array(numpy.rot90(numpy.frombuffer( level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( + self._get_north_rotations())) + chunk_data['BlockLight'] = numpy.array(numpy.rot90(numpy.frombuffer( level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) + self._get_north_rotations())) #chunk_data = {} #chunk_data['skylight'] = chunk.get_skylight_array(level) #chunk_data['blocklight'] = chunk.get_blocklight_array(level) From 1bc0619cd3ead404d7ce8bef7f8d2bd297c4b83d Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 23:46:08 -0600 Subject: [PATCH 39/60] Fix potential bed bug. --- overviewer_core/textures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index cd9439a..2cf3658 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1687,7 +1687,7 @@ def convert_data(blockID, data, north_direction): if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 4 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 From ebbc41abed172ea65e15c1c2909fd79b43cb382b Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 23:46:53 -0600 Subject: [PATCH 40/60] Rotate pistons for north-direction --- overviewer_core/textures.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 2cf3658..b728b2d 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1698,6 +1698,23 @@ def convert_data(blockID, data, north_direction): elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + if blockID in (29, 33, 34): # sticky piston, piston, piston extension + #Masked to not clobber block head/foot info + if north_direction == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north_direction == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north_direction == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 if blockID in (27, 28, 66): # minetrack: #Masked to not clobber powered rail on/off info #Ascending and flat straight From 34e0cf0e20e3d98225f3730f70bcad33b6717f11 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Thu, 4 Aug 2011 10:33:07 -0600 Subject: [PATCH 41/60] Set default north_direction for get_blockarray_fromfile --- overviewer_core/chunk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 3920a13..8af6757 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -75,7 +75,7 @@ def get_blockarray(level): Block array, which just contains all the block ids""" return level['Blocks'] -def get_blockarray_fromfile(filename, north_direction): +def get_blockarray_fromfile(filename, north_direction='lower-left'): """Same as get_blockarray except takes a filename. This is a shortcut""" d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] From 4ee15e8f449deb4ce7bb8a793111f6b8630c6625 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Thu, 4 Aug 2011 12:49:49 -0600 Subject: [PATCH 42/60] Code cleanup to simplify upstream merge --- README.rst | 40 ++----- overviewer.py | 2 +- overviewer_core/chunk.py | 2 +- overviewer_core/data/web_assets/overviewer.js | 16 +-- overviewer_core/nbt.py | 12 +- overviewer_core/quadtree.py | 3 +- overviewer_core/rendernode.py | 4 +- overviewer_core/textures.py | 104 +++++++++--------- overviewer_core/world.py | 34 +++--- 9 files changed, 96 insertions(+), 121 deletions(-) diff --git a/README.rst b/README.rst index bf3e700..90282f5 100644 --- a/README.rst +++ b/README.rst @@ -22,38 +22,6 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -Configurable North changes -========================== - -Additional Features -------------------- - -A configurable north direction! I'm not a big fan of north pointing to the -bottom left corner of the screen, and I bet you aren't either. So here you go! -Either use the commandline option --north-direction, or use the option -north_direction in the settings file. Valid options are 'lower-left', -'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', -myself. Defaults to 'lower-left' to match official behavior. - -Additional Bugs ---------------- - -Sometimes the upper- and lower-right direction seems to exacerbate an -existing bug that will not let you click on a sign in some cases. This is a -bang-head-on-desk sort of thing, I should be able to find it but I just haven't -yet. It does seem to work fine on my actual SMP map, for what that's worth. - -So far only blocks where direction is important (minecart tracks, doors, signs, -etc) rotate. Blocks like grass, stone, gravel, etc do not, however for the most -part it isn't noticeable because they look very similar no matter which -direction we are viewing them from. - -Upstream --------- - -Once I've squashed the additional bugs and cleaned up the code a bit, I will -see about getting this included in the official Minecraft Overviewer. - Features ======== @@ -227,6 +195,10 @@ Options --list-rendermodes List the available render modes, and a short description of each. +--north-direction=NORTH_DIRECTION + Specifies which corner of the screen north will point to. + Valid options are: lower-left, upper-left, upper-right, lower-right. + --settings=PATH Use this option to load settings from a file. The format of this file is given below. @@ -296,6 +268,10 @@ textures_path source. Overviewer looks in here for terrain.png and other textures before it looks anywhere else. +north_direction + Specifies which corner of the screen north will point to. + Valid options are: lower-left, upper-left, upper-right, lower-right. + Viewing the Results ------------------- Within the output directory you will find two things: an index.html file, and a diff --git a/overviewer.py b/overviewer.py index 80f4b1e..27b1ff1 100755 --- a/overviewer.py +++ b/overviewer.py @@ -255,7 +255,7 @@ def main(): # create the quadtrees # TODO chunklist q = [] - qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender, 'north_direction' : north_direction} + qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender} for rendermode in options.rendermode: if rendermode == 'normal': qtree = quadtree.QuadtreeGen(w, destdir, rendermode=rendermode, tiledir='tiles', **qtree_args) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 8af6757..fcba9bd 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -55,7 +55,7 @@ def get_lvldata(world, filename, x, y, retries=2): raise NoSuchChunk try: - d = world.load_from_region(filename, x, y) + d = world.load_from_region(filename, x, y) except Exception, e: if retries > 0: # wait a little bit, and try again (up to `retries` times) diff --git a/overviewer_core/data/web_assets/overviewer.js b/overviewer_core/data/web_assets/overviewer.js index eab2dc8..b3f84e3 100644 --- a/overviewer_core/data/web_assets/overviewer.js +++ b/overviewer_core/data/web_assets/overviewer.js @@ -536,13 +536,13 @@ var overviewer = { var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * Math.pow(2, overviewerConfig.map.zoomLevels)); - if(overviewerConfig.map.north_direction == 'upper-right'){ - x = -x-1; - y = -y-1; - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'upper-left'){ temp = x; x = -y-1; y = temp; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + x = -x-1; + y = -y-1; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = x; x = y; @@ -620,13 +620,13 @@ var overviewer = { point.x += 64; point.z -= 64; - if(overviewerConfig.map.north_direction == 'upper-right'){ - point.x = -point.x; - point.z = -point.z; - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'upper-left'){ temp = point.z; point.z = -point.x; point.x = temp; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + point.x = -point.x; + point.z = -point.z; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = point.z; point.z = point.x; diff --git a/overviewer_core/nbt.py b/overviewer_core/nbt.py index b027026..41ba4c5 100644 --- a/overviewer_core/nbt.py +++ b/overviewer_core/nbt.py @@ -210,13 +210,13 @@ class MCRFileReader(object): self._chunks = None def get_north_rotations(self): - if self.north_direction == "upper-left": + if self.north_direction == 'upper-left': return 1 - elif self.north_direction == "upper-right": + elif self.north_direction == 'upper-right': return 2 - elif self.north_direction == "lower-right": + elif self.north_direction == 'lower-right': return 3 - elif self.north_direction == "lower-left": + elif self.north_direction == 'lower-left': return 0 def _read_24bit_int(self): @@ -359,10 +359,10 @@ class MCRFileReader(object): load_chunk(), this will wrap x and y into the range [0, 31]. """ x = x % 32 - y = y % 32 + y = y % 32 if self._timestamps is None: self.get_chunk_info() - return self._timestamps[x + y * 32] + return self._timestamps[x + y * 32] def chunkExists(self, x, y): """Determines if a chunk exists without triggering loading of the backend data""" diff --git a/overviewer_core/quadtree.py b/overviewer_core/quadtree.py index 96ddb01..2e77576 100644 --- a/overviewer_core/quadtree.py +++ b/overviewer_core/quadtree.py @@ -49,7 +49,7 @@ def iterate_base4(d): return itertools.product(xrange(4), repeat=d) class QuadtreeGen(object): - def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='lower-left'): + def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal"): """Generates a quadtree from the world given into the given dest directory @@ -66,7 +66,6 @@ class QuadtreeGen(object): self.optimizeimg = optimizeimg self.bgcolor = bgcolor self.rendermode = rendermode - self.north_direction = north_direction # force png renderformat if we're using an overlay mode if 'overlay' in get_render_mode_inheritance(rendermode): diff --git a/overviewer_core/rendernode.py b/overviewer_core/rendernode.py index 4303e12..016e287 100644 --- a/overviewer_core/rendernode.py +++ b/overviewer_core/rendernode.py @@ -65,8 +65,8 @@ def pool_initializer(rendernode): # make sure textures are generated for this process # and initialize c_overviewer - textures.generate(north_direction=rendernode.options.get('north_direction', None), - path=rendernode.options.get('textures_path', None)) + textures.generate(path=rendernode.options.get('textures_path', None), + north_direction=rendernode.options.get('north_direction', None)) c_overviewer.init_chunk_render() # load biome data in each process, if needed diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index b728b2d..3025754 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -511,10 +511,10 @@ def generate_texture_tuple(img, blockid): blockmap list and specialblockmap dictionary.""" return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) -def generate_special_texture(blockID, data, north_direction): +def generate_special_texture(blockID, data): """Generates a special texture, such as a correctly facing minecraft track""" - data = convert_data(blockID, data, north_direction) + data = convert_data(blockID, data) # blocks need to be handled here (and in chunk.py) @@ -1680,37 +1680,37 @@ def generate_special_texture(blockID, data, north_direction): return None -def convert_data(blockID, data, north_direction): +def convert_data(blockID, data): if blockID == 26: # bed #Masked to not clobber block head/foot info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID in (29, 33, 34): # sticky piston, piston, piston extension #Masked to not clobber block head/foot info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0111) == 2: data = data & 0b1000 | 5 elif (data & 0b0111) == 3: data = data & 0b1000 | 4 elif (data & 0b0111) == 4: data = data & 0b1000 | 2 elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0111) == 2: data = data & 0b1000 | 3 elif (data & 0b0111) == 3: data = data & 0b1000 | 2 elif (data & 0b0111) == 4: data = data & 0b1000 | 5 elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0111) == 2: data = data & 0b1000 | 4 elif (data & 0b0111) == 3: data = data & 0b1000 | 5 elif (data & 0b0111) == 4: data = data & 0b1000 | 3 @@ -1718,19 +1718,19 @@ def convert_data(blockID, data, north_direction): if blockID in (27, 28, 66): # minetrack: #Masked to not clobber powered rail on/off info #Ascending and flat straight - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0111) == 0: data = data & 0b1000 | 1 elif (data & 0b0111) == 1: data = data & 0b1000 | 0 elif (data & 0b0111) == 2: data = data & 0b1000 | 5 elif (data & 0b0111) == 3: data = data & 0b1000 | 4 elif (data & 0b0111) == 4: data = data & 0b1000 | 2 elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0111) == 2: data = data & 0b1000 | 3 elif (data & 0b0111) == 3: data = data & 0b1000 | 2 elif (data & 0b0111) == 4: data = data & 0b1000 | 5 elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0111) == 0: data = data & 0b1000 | 1 elif (data & 0b0111) == 1: data = data & 0b1000 | 0 elif (data & 0b0111) == 2: data = data & 0b1000 | 4 @@ -1739,171 +1739,171 @@ def convert_data(blockID, data, north_direction): elif (data & 0b0111) == 5: data = data & 0b1000 | 2 if blockID == 66: # normal minetrack only #Corners - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 6: data = 7 elif data == 7: data = 8 elif data == 8: data = 6 elif data == 9: data = 9 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 6: data = 8 elif data == 7: data = 9 elif data == 8: data = 6 elif data == 9: data = 7 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 6: data = 9 elif data == 7: data = 6 elif data == 8: data = 8 elif data == 9: data = 7 if blockID in (50, 75, 76): # torch, off/on redstone torch - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 1: data = 3 elif data == 2: data = 4 elif data == 3: data = 2 elif data == 4: data = 1 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 1: data = 2 elif data == 2: data = 1 elif data == 3: data = 4 elif data == 4: data = 3 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 1: data = 4 elif data == 2: data = 3 elif data == 3: data = 1 elif data == 4: data = 2 if blockID in (53,67): # wooden and cobblestone stairs. - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 0: data = 2 elif data == 1: data = 3 elif data == 2: data = 1 elif data == 3: data = 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 0: data = 1 elif data == 1: data = 0 elif data == 2: data = 3 elif data == 3: data = 2 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 0: data = 3 elif data == 1: data = 2 elif data == 2: data = 0 elif data == 3: data = 1 if blockID in (61, 62, 23): # furnace and burning furnace - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 2: data = 5 elif data == 3: data = 4 elif data == 4: data = 2 elif data == 5: data = 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 2: data = 4 elif data == 3: data = 5 elif data == 4: data = 3 elif data == 5: data = 2 if blockID == 63: # signposts - if north_direction == 'upper-left': + if _north == 'upper-left': data = (data + 4) % 16 - elif north_direction == 'upper-right': + elif _north == 'upper-right': data = (data + 8) % 16 - elif north_direction == 'lower-right': + elif _north == 'lower-right': data = (data + 12) % 16 if blockID in (64,71): # wooden/iron door #Masked to not clobber block top/bottom & swung info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 65: # ladder - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 2: data = 5 elif data == 3: data = 4 elif data == 4: data = 2 elif data == 5: data = 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 2: data = 4 elif data == 3: data = 5 elif data == 4: data = 3 elif data == 5: data = 2 if blockID == 68: # wall sign - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 2: data = 5 elif data == 3: data = 4 elif data == 4: data = 2 elif data == 5: data = 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 2: data = 4 elif data == 3: data = 5 elif data == 4: data = 3 elif data == 5: data = 2 if blockID in (86,91): # pumpkins, jack-o-lantern - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 0: data = 1 elif data == 1: data = 2 elif data == 2: data = 3 elif data == 3: data = 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 0: data = 2 elif data == 1: data = 3 elif data == 2: data = 0 elif data == 3: data = 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 0: data = 3 elif data == 1: data = 0 elif data == 2: data = 1 elif data == 3: data = 2 if blockID in (93, 94): # redstone repeaters, ON and OFF #Masked to not clobber delay info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 96: # trapdoor #Masked to not clobber opened/closed info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 @@ -1957,7 +1957,12 @@ def getBiomeData(worlddir, chunkX, chunkY): biomeX = chunkX // 32 biomeY = chunkY // 32 rots = 0 - if _north == 'upper-right': + if _north == 'upper-left': + temp = biomeX + biomeX = biomeY + biomeY = -temp-1 + rots = 3 + elif _north == 'upper-right': biomeX = -biomeX-1 biomeY = -biomeY-1 rots = 2 @@ -1966,11 +1971,6 @@ def getBiomeData(worlddir, chunkX, chunkY): biomeX = -biomeY-1 biomeY = temp rots = 1 - elif _north == 'upper-left': - temp = biomeX - biomeX = biomeY - biomeY = -temp-1 - rots = 3 biomeFile = "b.%d.%d.biome" % (biomeX, biomeY) if biomeFile == currentBiomeFile: @@ -2103,7 +2103,7 @@ def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower specialblockmap = {} for blockID in special_blocks: for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data, north_direction) + specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) if texture_size != 24: # rescale biome textures. diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 2bba9dd..2c5ac0e 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -218,13 +218,13 @@ class World(object): disp_spawnX = spawnX = data['Data']['SpawnX'] spawnY = data['Data']['SpawnY'] disp_spawnZ = spawnZ = data['Data']['SpawnZ'] - if self.north_direction == 'upper-right': - spawnX = -spawnX - spawnZ = -spawnZ - elif self.north_direction == 'upper-left': + if self.north_direction == 'upper-left': temp = spawnX spawnX = -spawnZ spawnZ = temp + elif self.north_direction == 'upper-right': + spawnX = -spawnX + spawnZ = -spawnZ elif self.north_direction == 'lower-right': temp = spawnX spawnX = spawnZ @@ -304,13 +304,13 @@ class World(object): self.findTrueSpawn() def _get_north_rotations(self): - if self.north_direction == "upper-left": + if self.north_direction == 'upper-left': return 1 - elif self.north_direction == "upper-right": + elif self.north_direction == 'upper-right': return 2 - elif self.north_direction == "lower-right": + elif self.north_direction == 'lower-right': return 3 - elif self.north_direction == "lower-left": + elif self.north_direction == 'lower-left': return 0 def _iterate_regionfiles(self,regionlist=None): @@ -331,18 +331,18 @@ class World(object): logging.debug("Using path %s from regionlist", f) x = int(p[1]) y = int(p[2]) - if self.north_direction == 'upper-right': - x = -x-1 - y = -y-1 - elif self.north_direction == 'upper-left': + if self.north_direction == 'upper-left': temp = x x = -y-1 y = temp + elif self.north_direction == 'upper-right': + x = -x-1 + y = -y-1 elif self.north_direction == 'lower-right': temp = x x = y y = -temp-1 - yield (x, y, join(self.worlddir, 'region', f)) + yield (x, y, join(self.worlddir, 'region', f)) else: logging.warning("Ignore path '%s' in regionlist", f) @@ -352,13 +352,13 @@ class World(object): p = f.split(".") x = int(p[1]) y = int(p[2]) - if self.north_direction == 'upper-right': - x = -x-1 - y = -y-1 - elif self.north_direction == 'upper-left': + if self.north_direction == 'upper-left': temp = x x = -y-1 y = temp + elif self.north_direction == 'upper-right': + x = -x-1 + y = -y-1 elif self.north_direction == 'lower-right': temp = x x = y From 77eb608ccb69b53583af2edde187709dab4b5dd1 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 13 Aug 2011 10:50:50 -0600 Subject: [PATCH 43/60] Make get_blockarray_fromfile rotationally self-sufficient --- overviewer_core/chunk.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index fcba9bd..a15c79b 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -80,9 +80,17 @@ def get_blockarray_fromfile(filename, north_direction='lower-left'): d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] chunk_data = level + rots = 0 + if self.north_direction == 'upper-left': + rots = 1 + elif self.north_direction == 'upper-right': + rots = 2 + elif self.north_direction == 'lower-right': + rots = 3 + chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), - self._get_north_rotations()) + rots) return get_blockarray(chunk_data) def get_skylight_array(level): From 02d2ae0e6a1be2c30ee5c50517133977555591a2 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 13 Aug 2011 10:51:43 -0600 Subject: [PATCH 44/60] Fix tile updates for upper-left and lower-right rotations --- overviewer_core/nbt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/overviewer_core/nbt.py b/overviewer_core/nbt.py index 41ba4c5..895eca9 100644 --- a/overviewer_core/nbt.py +++ b/overviewer_core/nbt.py @@ -211,11 +211,11 @@ class MCRFileReader(object): def get_north_rotations(self): if self.north_direction == 'upper-left': - return 1 + return 3 elif self.north_direction == 'upper-right': return 2 elif self.north_direction == 'lower-right': - return 3 + return 1 elif self.north_direction == 'lower-left': return 0 @@ -338,7 +338,7 @@ class MCRFileReader(object): # read chunk location table locations_index = numpy.reshape(numpy.rot90(numpy.reshape(range(32*32), - (32, 32)), self.get_north_rotations()), -1) + (32, 32)), -self.get_north_rotations()), -1) for i in locations_index: self._locations[i] = self._read_chunk_location() From 79680203a514446327ce1ba61023b30fd4864bcd Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 16 Aug 2011 15:02:09 -0400 Subject: [PATCH 45/60] fixed forcerender not being used while rendering innertiles --- overviewer_core/quadtree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/overviewer_core/quadtree.py b/overviewer_core/quadtree.py index 2e77576..a5d843f 100644 --- a/overviewer_core/quadtree.py +++ b/overviewer_core/quadtree.py @@ -302,7 +302,7 @@ class QuadtreeGen(object): tile_mtime = None #check mtimes on each part of the quad, this also checks if they exist - needs_rerender = tile_mtime is None + needs_rerender = (tile_mtime is None) or self.forcerender quadPath_filtered = [] for path in quadPath: try: @@ -318,7 +318,7 @@ class QuadtreeGen(object): if tile_mtime is not None: os.unlink(imgpath) return - # quit now if we don't need rerender + # quit now if we don't need rerender if not needs_rerender: return #logging.debug("writing out innertile {0}".format(imgpath)) From 3e2828a3d924742c2ae2be41315082d8064b7b67 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 16 Aug 2011 15:07:21 -0400 Subject: [PATCH 46/60] fixed bug when overviewer.dat exists in world dir, but output dir does not exist --- overviewer_core/world.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index a18b86a..ad98328 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -119,6 +119,11 @@ class World(object): logging.warning("Moving overviewer.dat to OutputDir") import shutil try: + # make sure destination dir actually exists + try: + os.mkdir(self.outputdir) + except OSError: # already exists, or failed + pass shutil.move(self.pickleFile, self.outputdir) logging.info("overviewer.dat moved") except BaseException as ex: From 8ab2dea9060767ba3cf9338531f586f4bc7063ef Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Tue, 16 Aug 2011 21:27:19 -0400 Subject: [PATCH 47/60] Py2exe executables now have a valid version number and description --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index e105bf6..f1a89d8 100755 --- a/setup.py +++ b/setup.py @@ -91,6 +91,10 @@ def recursive_package_data(src, package_dir='overviewer_core'): # if py2exe is not None: + setup_kwargs['comments'] = "http://overviewer.org" + # py2exe likes a very particular type of version number: + setup_kwargs['version'] = util.findGitVersion().replace("-",".") + setup_kwargs['console'] = ['overviewer.py'] setup_kwargs['data_files'] = [('', doc_files)] setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/textures', 'textures') From 3e1ae9dd1721faa7f70f1c3685055ed34d412871 Mon Sep 17 00:00:00 2001 From: Thomas Lake Date: Wed, 17 Aug 2011 17:49:22 -0400 Subject: [PATCH 48/60] Store north-direction in overviewer.dat Check stored value at render time and abort if direction has changed and --forcerender not specified. Addresses concerns from pull request #458 --- overviewer.py | 9 ++++++++- overviewer_core/googlemap.py | 1 + overviewer_core/world.py | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/overviewer.py b/overviewer.py index 3a191b5..3ae3494 100755 --- a/overviewer.py +++ b/overviewer.py @@ -117,7 +117,7 @@ def main(): parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, helptext="Print more output. You can specify this option multiple times.") parser.add_option("--skip-js", dest="skipjs", action="store_true", helptext="Don't output marker.js or regions.js") parser.add_option("--no-signs", dest="nosigns", action="store_true", helptext="Don't output signs to markers.js") - parser.add_option("--north-direction", dest="north_direction", helptext="Specifies which corner of the screen north will point to. Valid options are: " + ", ".join(avail_north_dirs) + ".", type="choice", default=avail_north_dirs[0], choices=avail_north_dirs) + parser.add_option("--north-direction", dest="north_direction", action="store", helptext="Specifies which corner of the screen north will point to. Valid options are: " + ", ".join(avail_north_dirs) + ".", type="choice", default=avail_north_dirs[0], choices=avail_north_dirs) parser.add_option("--display-config", dest="display_config", action="store_true", helptext="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True) #parser.add_option("--write-config", dest="write_config", action="store_true", helptext="Writes out a sample config file", commandLineOnly=True) @@ -241,6 +241,13 @@ def main(): # First do world-level preprocessing w = world.World(worlddir, destdir, useBiomeData=useBiomeData, regionlist=regionlist, north_direction=north_direction) + if not (w.persistentData['north_direction'] == north_direction) and not options.forcerender: + logging.error("Conflicting north-direction setting!") + logging.error("Overviewer.dat gives previous north-direction as "+w.persistentData['north_direction']) + logging.error("Requested north-direction was "+north_direction) + logging.error("To change north-direction of an existing render, --forcerender must be specified") + sys.exit(1) + w.go(options.procs) logging.info("Rending the following tilesets: %s", ",".join(options.rendermode)) diff --git a/overviewer_core/googlemap.py b/overviewer_core/googlemap.py index 82b4869..9a4e95e 100644 --- a/overviewer_core/googlemap.py +++ b/overviewer_core/googlemap.py @@ -187,6 +187,7 @@ class MapGen(object): # save persistent data self.world.persistentData['POI'] = self.world.POI + self.world.persistentData['north_direction'] = self.world.north_direction with open(self.world.pickleFile,"wb") as f: cPickle.dump(self.world.persistentData,f) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 59b77c5..2f5e7f7 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -135,9 +135,11 @@ class World(object): if os.path.exists(self.pickleFile): with open(self.pickleFile,"rb") as p: self.persistentData = cPickle.load(p) + if not self.persistentData.get('north_direction', False): + self.persistentData['north_direction']=='lower-left' else: # some defaults - self.persistentData = dict(POI=[]) + self.persistentData = dict(POI=[], north_direction=self.north_direction) def get_region_path(self, chunkX, chunkY): From 60649c85fd23af17bb5f3018b1e9a176174ca55a Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Wed, 17 Aug 2011 18:22:50 -0400 Subject: [PATCH 49/60] now defaults to the map's current north direction unless otherwise specified --- README.rst | 3 +++ overviewer.py | 11 +++++--- overviewer_core/world.py | 58 ++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/README.rst b/README.rst index 90282f5..e8dede7 100644 --- a/README.rst +++ b/README.rst @@ -198,6 +198,9 @@ Options --north-direction=NORTH_DIRECTION Specifies which corner of the screen north will point to. Valid options are: lower-left, upper-left, upper-right, lower-right. + If you do not specify this option, it will default to whatever direction + the existing map uses. For new maps, it defaults to lower-left for + historical reasons. --settings=PATH Use this option to load settings from a file. The format of this file is diff --git a/overviewer.py b/overviewer.py index 3ae3494..de0cc57 100755 --- a/overviewer.py +++ b/overviewer.py @@ -96,7 +96,7 @@ def main(): cpus = 1 avail_rendermodes = c_overviewer.get_render_modes() - avail_north_dirs = ['lower-left', 'upper-left', 'upper-right', 'lower-right'] + avail_north_dirs = ['lower-left', 'upper-left', 'upper-right', 'lower-right', 'auto'] parser = ConfigOptionParser(usage=helptext, config="settings.py") parser.add_option("-V", "--version", dest="version", helptext="Displays version information and then exits", action="store_true") @@ -117,7 +117,7 @@ def main(): parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, helptext="Print more output. You can specify this option multiple times.") parser.add_option("--skip-js", dest="skipjs", action="store_true", helptext="Don't output marker.js or regions.js") parser.add_option("--no-signs", dest="nosigns", action="store_true", helptext="Don't output signs to markers.js") - parser.add_option("--north-direction", dest="north_direction", action="store", helptext="Specifies which corner of the screen north will point to. Valid options are: " + ", ".join(avail_north_dirs) + ".", type="choice", default=avail_north_dirs[0], choices=avail_north_dirs) + parser.add_option("--north-direction", dest="north_direction", action="store", helptext="Specifies which corner of the screen north will point to. Defaults to whatever the current map uses, or lower-left for new maps. Valid options are: " + ", ".join(avail_north_dirs) + ".", type="choice", default="auto", choices=avail_north_dirs) parser.add_option("--display-config", dest="display_config", action="store_true", helptext="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True) #parser.add_option("--write-config", dest="write_config", action="store_true", helptext="Writes out a sample config file", commandLineOnly=True) @@ -225,7 +225,7 @@ def main(): if options.north_direction: north_direction = options.north_direction else: - north_direction = 'lower-left' + north_direction = 'auto' logging.getLogger().setLevel( logging.getLogger().level + 10*options.quiet) @@ -241,7 +241,10 @@ def main(): # First do world-level preprocessing w = world.World(worlddir, destdir, useBiomeData=useBiomeData, regionlist=regionlist, north_direction=north_direction) - if not (w.persistentData['north_direction'] == north_direction) and not options.forcerender: + if north_direction == 'auto': + north_direction = w.persistentData['north_direction'] + options.north_direction = north_direction + elif w.persistentData['north_direction'] != north_direction and not options.forcerender: logging.error("Conflicting north-direction setting!") logging.error("Overviewer.dat gives previous north-direction as "+w.persistentData['north_direction']) logging.error("Requested north-direction was "+north_direction) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 2f5e7f7..0845b0a 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -69,33 +69,11 @@ class World(object): mincol = maxcol = minrow = maxrow = 0 - def __init__(self, worlddir, outputdir, useBiomeData=False, regionlist=None, north_direction="lower-left"): + def __init__(self, worlddir, outputdir, useBiomeData=False, regionlist=None, north_direction="auto"): self.worlddir = worlddir self.outputdir = outputdir self.useBiomeData = useBiomeData self.north_direction = north_direction - - #find region files, or load the region list - #this also caches all the region file header info - logging.info("Scanning regions") - regionfiles = {} - self.regions = {} - if regionlist: - self.regionlist = map(os.path.abspath, regionlist) # a list of paths - else: - self.regionlist = None - for x, y, regionfile in self._iterate_regionfiles(regionlist): - mcr = self.reload_region(regionfile) - mcr.get_chunk_info() - regionfiles[(x,y)] = (x,y,regionfile,mcr) - self.regionfiles = regionfiles - # set the number of region file handles we will permit open at any time before we start closing them -# self.regionlimit = 1000 - # the max number of chunks we will keep before removing them (includes emptry chunks) - self.chunklimit = 1024 - self.chunkcount = 0 - self.empty_chunk = [None,None] - logging.debug("Done scanning regions") # figure out chunk format is in use # if not mcregion, error out early @@ -136,10 +114,38 @@ class World(object): with open(self.pickleFile,"rb") as p: self.persistentData = cPickle.load(p) if not self.persistentData.get('north_direction', False): - self.persistentData['north_direction']=='lower-left' + # this is a pre-configurable-north map, so add the north_direction key + self.persistentData['north_direction'] = 'lower-left' else: - # some defaults - self.persistentData = dict(POI=[], north_direction=self.north_direction) + # some defaults, presumably a new map + self.persistentData = dict(POI=[], north_direction='lower-left') + + # handle 'auto' north + if self.north_direction == 'auto': + self.north_direction = self.persistentData['north_direction'] + north_direction = self.north_direction + + #find region files, or load the region list + #this also caches all the region file header info + logging.info("Scanning regions") + regionfiles = {} + self.regions = {} + if regionlist: + self.regionlist = map(os.path.abspath, regionlist) # a list of paths + else: + self.regionlist = None + for x, y, regionfile in self._iterate_regionfiles(regionlist): + mcr = self.reload_region(regionfile) + mcr.get_chunk_info() + regionfiles[(x,y)] = (x,y,regionfile,mcr) + self.regionfiles = regionfiles + # set the number of region file handles we will permit open at any time before we start closing them +# self.regionlimit = 1000 + # the max number of chunks we will keep before removing them (includes emptry chunks) + self.chunklimit = 1024 + self.chunkcount = 0 + self.empty_chunk = [None,None] + logging.debug("Done scanning regions") def get_region_path(self, chunkX, chunkY): From 42cb4b77562922d39af5d2eb2c4fde4f1430a008 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Wed, 17 Aug 2011 18:29:45 -0400 Subject: [PATCH 50/60] updated CONTRIBUTORS.rst with tswsl1989 and rmrector --- CONTRIBUTORS.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 174ebe7..21afa74 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -52,9 +52,11 @@ feature. * Ryan Hitchman * Jenny * Michael Jensen - * Maciej MaƂecki + * Thomas Lake + * Maciej Malecki * Ryan McCue * Morlok8k + * Ryan Rector * Gregory Short * Sam Steele * timwolla From 01bba80c543974aa125d717caf32c57736805154 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Wed, 17 Aug 2011 20:04:25 -0400 Subject: [PATCH 51/60] If overviewer.dat doesn't exist, don't give a north-direction warning --- overviewer.py | 2 +- overviewer_core/world.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/overviewer.py b/overviewer.py index de0cc57..6743293 100755 --- a/overviewer.py +++ b/overviewer.py @@ -244,7 +244,7 @@ def main(): if north_direction == 'auto': north_direction = w.persistentData['north_direction'] options.north_direction = north_direction - elif w.persistentData['north_direction'] != north_direction and not options.forcerender: + elif w.persistentData['north_direction'] != north_direction and not options.forcerender and not w.persistentDataIsNew: logging.error("Conflicting north-direction setting!") logging.error("Overviewer.dat gives previous north-direction as "+w.persistentData['north_direction']) logging.error("Requested north-direction was "+north_direction) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 0845b0a..c52008d 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -111,6 +111,7 @@ class World(object): self.pickleFile = os.path.join(self.outputdir, "overviewer.dat") if os.path.exists(self.pickleFile): + self.persistentDataIsNew = False; with open(self.pickleFile,"rb") as p: self.persistentData = cPickle.load(p) if not self.persistentData.get('north_direction', False): @@ -119,6 +120,7 @@ class World(object): else: # some defaults, presumably a new map self.persistentData = dict(POI=[], north_direction='lower-left') + self.persistentDataIsNew = True # indicates that the values in persistentData are new defaults, and it's OK to override them # handle 'auto' north if self.north_direction == 'auto': From 5c37bf1801646501326bb2b75b04fd08987fd872 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 18 Aug 2011 11:24:06 -0400 Subject: [PATCH 52/60] having both a world/overviewer.dat and a output/overviewer.dat now warns about the world version old behaviour was to replace the output dat with the world dat --- overviewer_core/world.py | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index c52008d..6c4c6f3 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -92,24 +92,30 @@ class World(object): # that may be needed between runs. # Currently only holds into about POIs (more more details, see quadtree) - self.pickleFile = os.path.join(self.worlddir, "overviewer.dat") - if os.path.exists(self.pickleFile): - logging.warning("overviewer.dat detected in WorldDir - this is no longer the correct location") - logging.warning("Moving overviewer.dat to OutputDir") - import shutil - try: - # make sure destination dir actually exists - try: - os.mkdir(self.outputdir) - except OSError: # already exists, or failed - pass - shutil.move(self.pickleFile, self.outputdir) - logging.info("overviewer.dat moved") - except BaseException as ex: - logging.error("Unable to move overviewer.dat") - logging.debug(ex.str()) - + self.oldPickleFile = os.path.join(self.worlddir, "overviewer.dat") self.pickleFile = os.path.join(self.outputdir, "overviewer.dat") + + if os.path.exists(self.oldPickleFile): + logging.warning("overviewer.dat detected in WorldDir - this is no longer the correct location") + if os.path.exists(self.pickleFile): + # new file exists, so make a note of it + logging.warning("you should delete the `overviewer.dat' file in your world directory") + else: + # new file does not exist, so move the old one + logging.warning("Moving overviewer.dat to OutputDir") + import shutil + try: + # make sure destination dir actually exists + try: + os.mkdir(self.outputdir) + except OSError: # already exists, or failed + pass + shutil.move(self.oldPickleFile, self.pickleFile) + logging.info("overviewer.dat moved") + except BaseException as ex: + logging.error("Unable to move overviewer.dat") + logging.debug(ex.str()) + if os.path.exists(self.pickleFile): self.persistentDataIsNew = False; with open(self.pickleFile,"rb") as p: From d19ee7d280041aeda51fbabd348cb5494b8d26ff Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 18 Aug 2011 11:34:16 -0400 Subject: [PATCH 53/60] --skip-js no longer skips writing overviewer.dat --- overviewer_core/googlemap.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/overviewer_core/googlemap.py b/overviewer_core/googlemap.py index 9a4e95e..50ff422 100644 --- a/overviewer_core/googlemap.py +++ b/overviewer_core/googlemap.py @@ -160,9 +160,6 @@ class MapGen(object): def finalize(self): - if self.skipjs: - return - # since we will only discover PointsOfInterest in chunks that need to be # [re]rendered, POIs like signs in unchanged chunks will not be listed # in self.world.POI. To make sure we don't remove these from markers.js @@ -175,6 +172,17 @@ class MapGen(object): else: markers = self.world.POI + # save persistent data + self.world.persistentData['POI'] = self.world.POI + self.world.persistentData['north_direction'] = self.world.north_direction + with open(self.world.pickleFile,"wb") as f: + cPickle.dump(self.world.persistentData,f) + + + # the rest of the function is javascript stuff + if self.skipjs: + return + # write out the default marker table with open(os.path.join(self.destdir, "markers.js"), 'w') as output: output.write("overviewer.collections.markerDatas.push([\n") @@ -185,12 +193,6 @@ class MapGen(object): output.write("\n") output.write("]);\n") - # save persistent data - self.world.persistentData['POI'] = self.world.POI - self.world.persistentData['north_direction'] = self.world.north_direction - with open(self.world.pickleFile,"wb") as f: - cPickle.dump(self.world.persistentData,f) - # write out the default (empty, but documented) region table with open(os.path.join(self.destdir, "regions.js"), 'w') as output: output.write('overviewer.collections.regionDatas.push([\n') From 320f7a178c1a1c3694acc37c98206428f17f0698 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 18 Aug 2011 20:40:36 -0400 Subject: [PATCH 54/60] Fixed lighting issue If more than one half-step or stair is stacked on top of another, the bottom steps/stairs will be black in a lighting render. This is now fixed --- overviewer_core/src/overviewer.h | 2 +- overviewer_core/src/rendermode-lighting.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index e7057e7..4a273bf 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 8 +#define OVERVIEWER_EXTENSION_VERSION 9 /* Python PIL, and numpy headers */ #include diff --git a/overviewer_core/src/rendermode-lighting.c b/overviewer_core/src/rendermode-lighting.c index 057331a..2c8b9b1 100644 --- a/overviewer_core/src/rendermode-lighting.c +++ b/overviewer_core/src/rendermode-lighting.c @@ -185,9 +185,14 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state, /* stairs and half-blocks take the skylevel from the upper block if it's transparent */ if (local_z != 127) { - upper_block = getArrayByte3D(blocks, local_x, local_y, local_z + 1); + int upper_counter = 0; + /* but if the upper_block is one of these special half-steps, we need to look at *its* upper_block */ + do { + upper_counter++; + upper_block = getArrayByte3D(blocks, local_x, local_y, local_z + upper_counter); + } while ((upper_block == 44 || upper_block == 54 || upper_block == 67) && local_z < 127); if (is_transparent(upper_block)) { - skylevel = getArrayByte3D(skylight, local_x, local_y, local_z + 1); + skylevel = getArrayByte3D(skylight, local_x, local_y, local_z + upper_counter); } } else { upper_block = 0; @@ -195,7 +200,7 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state, } /* the block has a bad blocklevel, estimate it from neigborhood - /* use given coordinates, no local ones! */ + * use given coordinates, no local ones! */ blocklevel = estimate_blocklevel(self, state, x, y, z, NULL); } From 5ae1fc4b640986e4f0dd5b4173ef30e8383776b2 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Fri, 19 Aug 2011 09:20:49 -0400 Subject: [PATCH 55/60] Very basic player.dat inspection script --- contrib/playerInspect.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 contrib/playerInspect.py diff --git a/contrib/playerInspect.py b/contrib/playerInspect.py new file mode 100644 index 0000000..0c3ff86 --- /dev/null +++ b/contrib/playerInspect.py @@ -0,0 +1,16 @@ +import sys + +sys.path.append(".") + +from overviewer_core.nbt import load + +print "Inspecting %s" % sys.argv[1] + +data = load(sys.argv[1])[1] + + +print "Position: %r" % data['Pos'] +print "Health: %s" % data['Health'] +print "Inventory: %d items" % len(data['Inventory']) +for item in data['Inventory']: + print " %r" % item From 88e889301a8cd4c09bf15dc535bfded88634381c Mon Sep 17 00:00:00 2001 From: asmodai Date: Fri, 19 Aug 2011 11:22:45 -0400 Subject: [PATCH 56/60] Added new items.py file with list of itemID and names --- contrib/playerInspect.py | 4 +- overviewer_core/items.py | 211 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 overviewer_core/items.py diff --git a/contrib/playerInspect.py b/contrib/playerInspect.py index 0c3ff86..c520788 100644 --- a/contrib/playerInspect.py +++ b/contrib/playerInspect.py @@ -3,6 +3,7 @@ import sys sys.path.append(".") from overviewer_core.nbt import load +from overviewer_core import items print "Inspecting %s" % sys.argv[1] @@ -13,4 +14,5 @@ print "Position: %r" % data['Pos'] print "Health: %s" % data['Health'] print "Inventory: %d items" % len(data['Inventory']) for item in data['Inventory']: - print " %r" % item + print " %-3d %s" % (item['Count'], items.id2item(item['id'])) + diff --git a/overviewer_core/items.py b/overviewer_core/items.py new file mode 100644 index 0000000..9b01aed --- /dev/null +++ b/overviewer_core/items.py @@ -0,0 +1,211 @@ +items = { + 0: 'Air', + 1: 'Stone', + 2: 'Grass', + 3: 'Dirt', + 4: 'Cobblestone', + 5: 'Wooden plank', + 6: 'Sapling', + 7: 'Bedrock', + 8: 'Water', + 9: 'Stationary', + 10: 'Lava', + 11: 'Stationary', + 12: 'Sand', + 13: 'Gravel', + 14: 'Gold ore', + 15: 'Iron ore', + 16: 'Coal ore', + 17: 'Wood', + 18: 'Leaves', + 19: 'Sponge', + 20: 'Glass', + 21: 'Lapis lazuli ore', + 22: 'Lapis lazuli block', + 23: 'Dispenser', + 24: 'Sandstone', + 25: 'Note block', + 26: 'Bed', + 27: 'Powered rail', + 28: 'Detector rail', + 29: 'Sticky piston', + 30: 'Cobweb', + 31: 'Tall grass', + 32: 'Dead shrubs', + 33: 'Piston', + 34: 'Piston extension', + 35: 'Wool', + 36: 'Block moved by piston', + 37: 'Dandelion', + 38: 'Rose', + 39: 'Brown mushroom', + 40: 'Red mushroom', + 41: 'Block of gold', + 42: 'Block of iron', + 43: 'Double slabs', + 44: 'Slabs', + 45: 'Brick block', + 46: 'TNT', + 47: 'Bookshelf', + 48: 'Moss stone', + 49: 'Obsidian', + 50: 'Torch', + 51: 'Fire', + 52: 'Monster spawner', + 53: 'Wooden stairs', + 54: 'Chest', + 55: 'Redstone wire', + 56: 'Diamond ore', + 57: 'Block of diamond', + 58: 'Crafting table', + 59: 'Seeds', + 60: 'Farmland', + 61: 'Furnace', + 62: 'Burning furnace', + 63: 'Sign', + 64: 'Wooden door', + 65: 'Ladders', + 66: 'Rails', + 67: 'Cobblestone stairs', + 68: 'Wall sign', + 69: 'Lever', + 70: 'Stone pressure plate', + 71: 'Iron door', + 72: 'Wooden pressure plate', + 73: 'Redstone ore', + 74: 'Glowing redstone ore', + 75: 'Redstone torch (off)', + 76: 'Redstone torch (on)', + 77: 'Stone button', + 78: 'Snow', + 79: 'Ice', + 80: 'Snow block', + 81: 'Cactus', + 82: 'Clay block', + 83: 'Sugar cane', + 84: 'Jukebox', + 85: 'Fence', + 86: 'Pumpkin', + 87: 'Netherrack', + 88: 'Soul sand', + 89: 'Glowstone block', + 90: 'Portal', + 91: 'Jack-O-Lantern', + 92: 'Cake', + 93: 'Redstone repeater (off)', + 94: 'Redstone repeater (on)', + 95: 'Locked', + 96: 'Trapdoor', + 256: 'Iron shovel', + 257: 'Iron pickaxe', + 258: 'Iron axe', + 259: 'Flint and steel', + 260: 'Red apple', + 261: 'Bow', + 262: 'Arrow', + 263: 'Coal', + 264: 'Diamond', + 265: 'Iron ingot', + 266: 'Gold ingot', + 267: 'Iron sword', + 268: 'Wooden sword', + 269: 'Wooden shovel', + 270: 'Wooden pickaxe', + 271: 'Wooden axe', + 272: 'Stone sword', + 273: 'Stone shovel', + 274: 'Stone pickaxe', + 275: 'Stone axe', + 276: 'Diamond sword', + 277: 'Diamond shovel', + 278: 'Diamond pickaxe', + 279: 'Diamond axe', + 280: 'Stick', + 281: 'Bowl', + 282: 'Mushroom soup', + 283: 'Gold sword', + 284: 'Gold shovel', + 285: 'Gold pickaxe', + 286: 'Gold axe', + 287: 'String', + 288: 'Feather', + 289: 'Gunpowder', + 290: 'Wooden hoe', + 291: 'Stone hoe', + 292: 'Iron hoe', + 293: 'Diamond hoe', + 294: 'Gold hoe', + 295: 'Seeds', + 296: 'Wheat', + 297: 'Bread', + 298: 'Leather cap', + 299: 'Leather tunic', + 300: 'Leather pants', + 301: 'Leather boots', + 302: 'Chain helmet', + 303: 'Chain chestplate', + 304: 'Chain leggings', + 305: 'Chain boots', + 306: 'Iron helmet', + 307: 'Iron chestplate', + 308: 'Iron leggings', + 309: 'Iron boots', + 310: 'Diamond helmet', + 311: 'Diamond chestplate', + 312: 'Diamond leggings', + 313: 'Diamond boots', + 314: 'Gold helmet', + 315: 'Gold chestplate', + 316: 'Gold leggings', + 317: 'Gold boots', + 318: 'Flint', + 319: 'Raw porkchop', + 320: 'Cooked porkchop', + 321: 'Paintings', + 322: 'Golden apple', + 323: 'Sign', + 324: 'Wooden door', + 325: 'Bucket', + 326: 'Water bucket', + 327: 'Lava bucket', + 328: 'Minecart', + 329: 'Saddle', + 330: 'Iron door', + 331: 'Redstone', + 332: 'Snowball', + 333: 'Boat', + 334: 'Leather', + 335: 'Milk', + 336: 'Clay brick', + 337: 'Clay', + 338: 'Sugar cane', + 339: 'Paper', + 340: 'Book', + 341: 'Slimeball', + 342: 'Minecart with chest', + 343: 'Minecart with furnace', + 344: 'Egg', + 345: 'Compass', + 346: 'Fishing rod', + 347: 'Clock', + 348: 'Glowstone dust', + 349: 'Raw fish', + 350: 'Cooked fish', + 351: 'Dye', + 352: 'Bone', + 353: 'Sugar', + 354: 'Cake', + 355: 'Bed', + 356: 'Redstone repeater', + 357: 'Cookie', + 358: 'Map', + 359: 'Shears', + 2256: 'Gold music disc', + 2257: 'Green music disc' +} + +def id2item(item_id): + if item_id in items: + return items[item_id] + else: + return item_id From 85e8a1e8895e5d0b908a395a858d55b5bc1127a0 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 22 Aug 2011 23:20:17 -0400 Subject: [PATCH 57/60] added more stern warnings about sample.settings.py and the --zoom option --- README.rst | 4 ++-- sample.settings.py | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index e8dede7..0548053 100644 --- a/README.rst +++ b/README.rst @@ -235,8 +235,8 @@ zoom=ZOOM This is equivalent to setting the dimensions of the highest zoom level. It does not actually change how the map is rendered, but rather *how much of - the map is rendered.* (Calling this option "zoom" may be a bit misleading, - I know) + the map is rendered.* Setting this option too low *will crop your map.* + (Calling this option "zoom" may be a bit misleading, I know) To be precise, it sets the width and height of the highest zoom level, in tiles. A zoom level of z means the highest zoom level of your map will be diff --git a/sample.settings.py b/sample.settings.py index cccdac8..f5761ea 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -33,7 +33,9 @@ if procs < 1: procs = 1 ## Sets the zoom level manually instead of calculating it. This can be useful ## if you have outlier chunks that make your world too big. This value will ## make the highest zoom level contain (2**ZOOM)^2 tiles -## Normally you should not need to set this variable. +## ***Normally you should not need to set this variable.*** +## ***Setting it too low will crop your map!*** +## Seriously, check the README before using this. ## Default: Automatically calculated from your world ## Type: integer ## Example: @@ -163,7 +165,8 @@ north_direction = "upper-right" -### As a reminder, don't use this file verbatim, it should only be used as -### a guide. +### As a reminder, *don't use this file verbatim*, it should only be used as +### a guide. Be sure to read what each option does before you set it. +### See the README for more details. import sys sys.exit("This sample-settings file shouldn't be used directly!") From 8d398864e337d6af1c6d30beaa22930c4e97be94 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 23 Aug 2011 21:37:10 +0200 Subject: [PATCH 58/60] Fix bad handle of both alpha layers being zero in composite.c --- overviewer_core/src/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/src/composite.c b/overviewer_core/src/composite.c index 432ece8..d83701c 100644 --- a/overviewer_core/src/composite.c +++ b/overviewer_core/src/composite.c @@ -194,7 +194,7 @@ alpha_over_full(PyObject *dest, PyObject *src, PyObject *mask, float overall_alp } /* special cases */ - if (in_alpha == 255 || *outmask == 0) { + if (in_alpha == 255 || (*outmask == 0 && in_alpha > 0)) { *outmask = in_alpha; *out = *in; From 03f97340394612a88b55ddf2c09113851e685a55 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 23 Aug 2011 22:10:20 +0200 Subject: [PATCH 59/60] Bump overviewer extension version --- overviewer_core/src/overviewer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 4a273bf..9dd2526 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 9 +#define OVERVIEWER_EXTENSION_VERSION 10 /* Python PIL, and numpy headers */ #include From 240fe128cb28dc134e769d45e8a543988e060575 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Tue, 23 Aug 2011 20:35:19 -0400 Subject: [PATCH 60/60] Attempt to detect if the user has forgotten to escape spaces in their paths --- overviewer.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/overviewer.py b/overviewer.py index 6743293..b0103b7 100755 --- a/overviewer.py +++ b/overviewer.py @@ -154,6 +154,17 @@ def main(): sys.exit(1) worlddir = args[0] + if len(args) > 2: + # it's possible the user has a space in one of their paths but didn't properly escape it + # attempt to detect this case + for start in range(len(args)): + if not os.path.exists(args[start]): + for end in range(start+1, len(args)+1): + if os.path.exists(" ".join(args[start:end])): + logging.warning("It looks like you meant to specify \"%s\" as your world dir or your output\n\ +dir but you forgot to put quotes around the directory, since it contains spaces." % " ".join(args[start:end])) + sys.exit(1) + if not os.path.exists(worlddir): # world given is either world number, or name worlds = world.get_worlds()